Linux clock framework development
 help / color / mirror / Atom feed
From: "Yu-Chun Lin [林祐君]" <eleanor.lin@realtek.com>
To: Jerome Brunet <jbrunet@baylibre.com>
Cc: "jbrunet+clk@baylibre.com" <jbrunet+clk@baylibre.com>,
	"sboyd@kernel.org" <sboyd@kernel.org>,
	"robh@kernel.org" <robh@kernel.org>,
	"krzk+dt@kernel.org" <krzk+dt@kernel.org>,
	"conor+dt@kernel.org" <conor+dt@kernel.org>,
	"p.zabel@pengutronix.de" <p.zabel@pengutronix.de>,
	"Edgar Lee [李承諭]" <cylee12@realtek.com>,
	"afaerber@suse.com" <afaerber@suse.com>,
	"Jyan Chou [周芷安]" <jyanchou@realtek.com>,
	"bmasney+clk@redhat.com" <bmasney+clk@redhat.com>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"linux-clk@vger.kernel.org" <linux-clk@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-realtek-soc@lists.infradead.org"
	<linux-realtek-soc@lists.infradead.org>,
	"James Tai [戴志峰]" <james.tai@realtek.com>,
	"CY_Huang[黃鉦晏]" <cy.huang@realtek.com>,
	"Stanley Chang[昌育德]" <stanley_chang@realtek.com>,
	"Brian Masney" <bmasney@redhat.com>
Subject: RE: [PATCH v15 05/11] clk: realtek: Add support for phase locked loops (PLLs)
Date: Sat, 17 Oct 2026 08:45:12 +0000	[thread overview]
Message-ID: <a53104475f414e12a69c3a942ad249b1@realtek.com> (raw)
In-Reply-To: <179157057524.266328.7662236812996349329.b4-review@b4>

Hi Jerome,

> > +static inline struct rtk_clk_regmap_pll *to_rtk_clk_regmap_pll(struct
> > +clk_hw *hw) {
> > +     struct rtk_clk_regmap *clkr = to_rtk_clk_regmap(hw);
> > +
> > +     return container_of(clkr, struct rtk_clk_regmap_pll, clkr); }
> > +
> > +static int wait_freq_ready(struct rtk_clk_regmap_pll *clkp) {
> > +     u32 pollval;
> > +
> > +     /* reg == 0 means not configured.
> > +      * Register offset 0 is never a valid address on Realtek SoCs.
> 
> Fix the comment style please
> 

Ack.

> > +      */
> > +     if (!clkp->freq_ready_reg)
> > +             return 0;
> 
> Do you need to protect against your own driver providing bad offset ?
> 

Will change the comment to

/*
 * Optional register: return success if not provided.
 */

> > +
> > +     return regmap_read_poll_timeout_atomic(clkp->clkr.regmap,
> clkp->freq_ready_reg, pollval,
> > +             (pollval & clkp->freq_ready_mask) ==
> > +clkp->freq_ready_val, 1, TIMEOUT); }
> > +
> > +static bool is_power_on(struct rtk_clk_regmap_pll *clkp) {
> > +     u32 val;
> > +
> > +     /* reg == 0 means not configured (assume always on).
> > +      * Register offset 0 is never a valid address on Realtek SoCs.
> > +      */
> 
> You've already said this. It would be more helpful to tell us that PLL that does
> not provider a power reg is considered to be always powered, if that is the case
> 

Will change the comment to 

/*
 * If the PLL does not provide a power register, it is considered
 * to be always powered.
 */


> > +     if (!clkp->power_reg)
> > +             return true;
> > +
> > +     if (regmap_read(clkp->clkr.regmap, clkp->power_reg, &val))
> > +             return false;
> > +
> > +     return (val & clkp->power_mask) == clkp->power_val_on; }
> > +
> > +static void rtk_clk_regmap_pll_disable(struct clk_hw *hw) {
> > +     struct rtk_clk_regmap_pll *clkp = to_rtk_clk_regmap_pll(hw);
> > +     unsigned long flags;
> > +
> > +     if (!clkp->seq_power_off)
> > +             return;
> > +
> > +     spin_lock_irqsave(&clkp->lock, flags);
> 
> What do you protect with this ?
> regmap does provide some locking already and CCF too. Do you expect
> something to poke the registers you are writing ?
> 

This spinlock was added based on Brian's feedback [1] to prevent a race
condition between '.set_rate()' and '.enable()'/'.disable()'.

Since our '.set_rate()' involves a multi-step register sequence and a
polling loop, an interleaved '.enable()' or '.disable()' operation could
disrupt the hardware state. Thus, we need this shared spinlock to serialize
them.

[1] https://lore.kernel.org/lkml/ac_SX1UJRqiBH2iM@redhat.com/


> > +
> > +     regmap_multi_reg_write(clkp->clkr.regmap, clkp->seq_power_off,
> > +                            clkp->num_seq_power_off);
> > +
> > +     spin_unlock_irqrestore(&clkp->lock, flags); }
> > +
> > +static int rtk_clk_regmap_pll_is_enabled(struct clk_hw *hw) {
> > +     struct rtk_clk_regmap_pll *clkp = to_rtk_clk_regmap_pll(hw);
> > +     unsigned long flags;
> > +     int ret;
> > +
> > +     spin_lock_irqsave(&clkp->lock, flags);
> > +
> > +     ret = is_power_on(clkp);
> > +
> > +     spin_unlock_irqrestore(&clkp->lock, flags);
> > +
> > +     return ret;
> > +}
> > +
> > +static int rtk_clk_regmap_pll_determine_rate(struct clk_hw *hw,
> > +                                          struct clk_rate_request
> > +*req) {
> > +     struct rtk_clk_regmap_pll *clkp = to_rtk_clk_regmap_pll(hw);
> > +     const struct rtk_freq_table *ftblv = NULL;
> > +
> > +     ftblv = ftbl_find_by_rate(clkp->freq_tbl, req->rate);
> 
> Doesn't this completly disregard the parent rate ?
> 

Yes, the lookup works on the programmed code, not on the parent rate.
In our hardware, these PLLs are hardwired to a single 27MHz crystal
("osc27m") without any mux.
Therefore, the "parent rate" is a fixed property of the hardware rather
than dynamically selectable source, which is why the code operates on
predefined values.

> I understand that SoC vendor may expect a single frequency as input but it it is
> pretty fragile to do so.
> 

Agreed. will add an explicit check against the expected parent rate. This
ensures that if a board declares a different oscillator, it will fail safely
and explicitly, rather than silently programming unvalidated values.

> A PLL is usually some sort of (fractional) multiplier.
> It is way better to provide the table of multiplier you support, along with their
> sequence and properly doing the calculation here.
> 
> > +
> > +     if (ftblv && ftblv->rate >= req->min_rate) {
> > +             req->rate = ftblv->rate;
> > +             return 0;
> > +     }
> > +
> > +     /* floor result is below min_rate; find the smallest rate >= min_rate
> */
> > +     ftblv = ftbl_find_ceil_by_rate(clkp->freq_tbl, req->min_rate);
> > +     if (!ftblv || ftblv->rate > req->max_rate)
> > +             return -EINVAL;
> 
> This is not how determine_rate() works. You just round to the closest rate the
> PLL supports. You are free to round as you wish (bellow or closest)
> 
> Just test the different multipliers you support and return what is best compared
> to the request. There is no reason to return EINVAL for this.
> 

Will drop the complex floor-then-ceil search and do a plain closest match
within the '[min_rate, max_rate]' boundaries.

Regarding the multiplier table:
We prefer to keep this as a predefined list rather than deriving a new point
from an arbitrary rate. This is because these rows in 'freq_tbl[]' are the
exact operating points where the surrounding sequences (power, SSC, ready
flow) were validated.

We hope this design choice makes sense to you.

Therefore, the updated logic will look like this:

static int rtk_clk_regmap_pll_determine_rate(struct clk_hw *hw,
                                             struct clk_rate_request *req)
{
        struct rtk_clk_regmap_pll *clkp = to_rtk_clk_regmap_pll(hw);
        const struct rtk_freq_table *ftblv = NULL;

        if (clkp->expected_parent_rate &&
            req->best_parent_rate != clkp->expected_parent_rate)
                return -EINVAL;

        ftblv = ftbl_find_closest_by_rate(clkp->freq_tbl, req->rate,
                                          req->min_rate, req->max_rate);
        if (!ftblv)
                return -EINVAL;

        req->rate = ftblv->rate;

        return 0;
}

> > +
> > +     req->rate = ftblv->rate;
> > +
> > +     return 0;
> > +}
> > +

[...]

> > +EXPORT_SYMBOL_NS_GPL(rtk_clk_pll_ops, "CLK_REALTEK");
> > +
> > +const struct clk_ops rtk_clk_pll_ro_ops = {
> > +     .recalc_rate = rtk_clk_regmap_pll_recalc_rate,
> 
> is_enabled ?

Ack. Will add.

Best Regards,
Yu-Chun
> 
> --
> Jerome

  reply	other threads:[~2026-10-17  8:48 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02  7:43 [PATCH v15 00/11] clk / reset: realtek: Add RTD1625 clock and reset support Yu-Chun Lin
2026-10-02  7:43 ` [PATCH v15 01/11] dt-bindings: clock: Add Realtek RTD1625 Clock & Reset Controller Yu-Chun Lin
2026-10-02  7:43 ` [PATCH v15 02/11] reset: Add Realtek basic reset support Yu-Chun Lin
2026-10-02  7:43 ` [PATCH v15 03/11] reset: realtek: Add RTD1625 reset controller driver Yu-Chun Lin
2026-10-02  7:43 ` [PATCH v15 04/11] clk: realtek: Introduce common probe() Yu-Chun Lin
2026-10-02  9:14   ` sashiko-bot
2026-10-02  7:43 ` [PATCH v15 05/11] clk: realtek: Add support for phase locked loops (PLLs) Yu-Chun Lin
2026-10-09 18:29   ` Jerome Brunet
2026-10-17  8:45     ` Yu-Chun Lin [林祐君] [this message]
2026-10-02  7:43 ` [PATCH v15 06/11] clk: realtek: Add support for gate clock Yu-Chun Lin
2026-10-09 18:29   ` Jerome Brunet
2026-10-17  9:05     ` Yu-Chun Lin [林祐君]
2026-10-02  7:43 ` [PATCH v15 07/11] clk: realtek: Add support for mux clock Yu-Chun Lin
2026-10-02  7:43 ` [PATCH v15 08/11] clk: realtek: Add support for MMC-tuned PLL clocks Yu-Chun Lin
2026-10-09 18:29   ` Jerome Brunet
2026-10-17  9:28     ` Yu-Chun Lin [林祐君]
2026-10-02  7:43 ` [PATCH v15 09/11] clk: realtek: Add RTD1625-CRT clock controller driver Yu-Chun Lin
2026-10-02  9:14   ` sashiko-bot
2026-10-09 18:29   ` Jerome Brunet
2026-10-17  9:08     ` Yu-Chun Lin [林祐君]
2026-10-02  7:43 ` [PATCH v15 10/11] clk: realtek: Add RTD1625-ISO " Yu-Chun Lin
2026-10-09 18:29   ` Jerome Brunet
2026-10-17  9:10     ` Yu-Chun Lin [林祐君]
2026-10-02  7:43 ` [PATCH v15 11/11] arm64: dts: realtek: Add clock support for RTD1625 Yu-Chun Lin
2026-10-09 18:29 ` [PATCH v15 00/11] clk / reset: realtek: Add RTD1625 clock and reset support Jerome Brunet
2026-10-17  8:37   ` Yu-Chun Lin [林祐君]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a53104475f414e12a69c3a942ad249b1@realtek.com \
    --to=eleanor.lin@realtek.com \
    --cc=afaerber@suse.com \
    --cc=bmasney+clk@redhat.com \
    --cc=bmasney@redhat.com \
    --cc=conor+dt@kernel.org \
    --cc=cy.huang@realtek.com \
    --cc=cylee12@realtek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=james.tai@realtek.com \
    --cc=jbrunet+clk@baylibre.com \
    --cc=jbrunet@baylibre.com \
    --cc=jyanchou@realtek.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-realtek-soc@lists.infradead.org \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=stanley_chang@realtek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox