RE: [PATCH 2/6] clk: renesas: rzg2l: Add PLL7 DSI clock support for RZ/G3L

Biju Das <[email protected]>
Newsgroups org.kernel.vger.linux-clk,org.kernel.vger.linux-kernel,org.kernel.vger.linux-renesas-soc
Message-ID <TY3PR01MB11346D51CFA3114C1F336D9B286CC2@TY3PR01MB11346.jpnprd01.prod.outlook.com>
Hi Geert,

Thanks for the feedback.

> -----Original Message-----
> From: Geert Uytterhoeven <[email protected]>
> Sent: 10 July 2026 16:39
> Subject: Re: [PATCH 2/6] clk: renesas: rzg2l: Add PLL7 DSI clock support for RZ/G3L
> 
> Hi Biju,
> 
> On Fri, 19 Jun 2026 at 18:40, Biju <[email protected]> wrote:
> > From: Biju Das <[email protected]>
> >
> > Add a new fractional PLL clock type (CLK_TYPE_G3L_PLLDSI) for the
> > RZ/G3L SoC's PLL7, which drives the DSI interface and requires a
> > dedicated parameter calculation and programming sequence distinct from
> > other PLLs in the RZ/G2L family.
> >
> > PLL7 output frequency is determined by the formula:
> >
> >   Ffdco = (NIR + NFR / 4096) * (Fosc / MR)
> >   Ffout = Ffdco / (1 << PR)
> >
> > where:
> >   - Fosc = 24 MHz (oscillator input)
> >   - PR in [0, 4]   (post divider, power-of-two)
> >   - MR in [1, 12]  (input pre-divider)
> >   - NIR in [56, 375], NFR in [0, 4095] (integer and fractional parts)
> >
> > The FDCO must fall within one of two valid ranges: 900–2000 MHz
> > (rangesel=0) or 2000–3000 MHz (rangesel=1).
> >
> > The parameter search in rzg3l_dsi_get_pll_parameters_values() iterates
> > over all valid (MR, PR) combinations, filtering by the required FPFD
> > range of 8–16 MHz, then delegates to
> > rzg3l_dsi_compute_pll_parameters() to find the NIR/NFR pair that best
> > approximates the requested rate. An exact match returns immediately;
> > otherwise the combination with the smallest absolute frequency error is used.
> >
> > Computed parameters are cached in pll7_dsi_params within pll_clk to
> > avoid redundant recalculation in determine_rate() when the requested
> > rate has not changed.
> >
> > Signed-off-by: Biju Das <[email protected]>
> 
> Thanks for your patch!
> 
> 
> > --- a/drivers/clk/renesas/rzg2l-cpg.c
> > +++ b/drivers/clk/renesas/rzg2l-cpg.c
> > @@ -67,6 +67,10 @@
> >  #define CPG_PLL_MON_OFFSET(x)          (CPG_PLL_STBY_OFFSET(x) + 0xc)
> >  #define CPG_PLL_MON_LOCK               BIT(4)
> >  #define CPG_PLL_MON_RESETB             BIT(0)
> > +#define CPG_PLL_CLK1_VAL(p, m, ni, nf, sel)    (FIELD_PREP(GENMASK(28, 26), p)  | \
> > +                                                FIELD_PREP(GENMASK(25, 22), m)  | \
> > +                                                FIELD_PREP(GENMASK(21, 13), ni) | \
> > +
> > +FIELD_PREP(GENMASK(12, 1), nf)  | (sel))
> 
> What about dropping CPG_PLL_CLK1_VAL(), defining
> 
>     #define CPG_PLL_CLK1_DIV_NF    GENMASK(12, 1)
>     #define CPG_PLL_CLK1_DIV_NI    GENMASK(21, 13)
>     ...
> 
> and using FIELD_PREP(CPG_PLL_CLK1_DIV_NF, ...) | ..." at all callsites?

OK.

> 
> >
> >  #define RZG3L_SDIV_DIV_DSI_A_WEN       BIT(16)
> >  #define RZG3L_SDIV_DIV_DSI_B_WEN       BIT(20)
> > @@ -96,6 +100,26 @@
> >  #define PLL5_HSCLK_MIN         10000000
> >  #define PLL5_HSCLK_MAX         187500000
> >
> > +#define RZG3L_OSC_CLK                  (24 * MEGA)
> 
> This is the clock rate of the external crystal.
> As this must be 24 MHz on RZ/G3L, I guess it is OK to use a define for that.
> 
> > +#define RZG3L_PLL7_FDCO_RANGE_0_MIN    (900 * MEGA)
> > +#define RZG3L_PLL7_FDCO_RANGE_0_MAX    (2000 * MEGA)
> > +#define RZG3L_PLL7_FDCO_RANGE_1_MIN    (2000 * MEGA)
> > +#define RZG3L_PLL7_FDCO_RANGE_1_MAX    (3000ULL * MEGA)
> 
> I didn't check the ranges yet.
> Do you need the 87 MHz upper limit of LVDS somewhere?

Not here. I believe 87MHz upper limit is to be taken care in LVDS bridge driver(mode_valid),
same case for DSI and DPI. 

> 
> > +#define RZG3L_PLL7_PR_MIN              (0)
> > +#define RZG3L_PLL7_PR_MAX              (4)
> > +#define RZG3L_PLL7_MR_MIN              (0)
> > +#define RZG3L_PLL7_MR_MAX              (11)
> > +#define RZG3L_PLL7_NIR_MIN             (55)
> > +#define RZG3L_PLL7_NIR_MAX             (374)
> > +#define RZG3L_PLL7_NFR_MIN             (0)
> > +#define RZG3L_PLL7_NFR_MAX             (4095)
> > +#define RZG3L_PLL7_NR_MIN              (56250)  /* Multiplied value 56.25 * 1000 */
> > +#define RZG3L_PLL7_NR_MAX              (375000) /* Multiplied value 375 * 1000 */
> > +#define RZG3L_PLL7_MULT_MIN            (293)    /* Multiplied value 0.293 * 1000 */
> > +#define RZG3L_PLL7_MULT_MAX            (375000) /* Multiplied value 375 * 1000 */
> > +#define RZG3L_PLL7_FSTD_DIV_MR_MIN     (8 * MEGA)
> > +#define RZG3L_PLL7_FSTD_DIV_MR_MAX     (16 * MEGA)
> > +
> >  /**
> >   * struct clk_hw_data - clock hardware data
> >   * @hw: clock hw
> 
> > @@ -1368,6 +1402,196 @@ static const struct clk_ops rzg3l_cpg_pll_ops = {
> >         .recalc_rate = rzg3s_cpg_pll_clk_recalc_rate,  };
> >
> > +static inline bool
> > +rzg3l_dsi_compute_pll_parameters(struct rzg3l_plldsi_parameters *pars,
> > +                                struct rzg3l_plldsi_parameters *p,
> > +                                struct rzg3l_plldsi_parameters *best,
> > +                                u64 freq_millihz, u32 fpfd, u32 pr) {
> > +       for (p->nir = RZG3L_PLL7_NIR_MIN; p->nir <= RZG3L_PLL7_NIR_MAX; p->nir++) {
> > +               u64 output_nir, output_nfr_range;
> > +               s64 nfr, output_nfr;
> > +               u64 fdco, output;
> > +               u64 nr_div_mr_pr;
> > +
> > +               /*
> > +                * The frequency generated by the PLL is calculated as follows:
> > +                *
> > +                * With:
> > +                * Freq = Ffout = Ffdco / pr
> > +                * input frequency(fstd) = 24MHz
> > +                * fpfd = fstd / mr
> > +                * nr = nir + nfr / 4096
> > +                * Ffdco = nr * fpfd
> > +                * Ffdco = (nir + (nfr / 4096)) * fpfd
> > +                *
> > +                * Freq can also be rewritten as:
> > +                * Freq = Ffdco / pr
> > +                *      = (nir * fpfd) / pr + ((nfr / 4096) * fpfd) / pr
> > +                *      = output_nir + output_nfr
> > +                *
> > +                * Every parameter has been determined at this point, but nfr.
> > +                * Considering that:
> > +                * 0 <= nfr <= 4095
> > +                * Then:
> > +                * 0 <= (nfr / 4096) < 1
> > +                * Therefore:
> > +                * 0 <= output_nfr < fpfd / pr
> > +                */
> > +
> > +               /* Compute output nir component (in mHz) */
> > +               output_nir = DIV_ROUND_CLOSEST_ULL((p->nir + 1) * 1ULL
> > + * fpfd * MILLI, pr);
> 
> The multiplication with 1ULL to force 64-bit looks a bit odd.
> As p->nir is small, perhaps mul_u32_u32((p->nir + 1) * MILLI, fpfd)?

Agreed.

> 
> > +               /* Compute range for output nfr (in mHz) */
> > +               output_nfr_range = DIV_ROUND_CLOSEST_ULL(fpfd * 1ULL *
> > + MILLI, pr);
> 
> mul_u32_u32(fpfd, MILLI)

Agreed.

> 
> > +               /* No point in continuing if we can't achieve the desired frequency */
> > +               if (freq_millihz < output_nir  || freq_millihz >= (output_nir + output_nfr_range))
> > +                       continue;
> > +
> > +               /*
> > +                * Compute the nfr component
> > +                *
> > +                * Since:
> > +                * Freq = output_nir + output_nfr
> > +                * Then:
> > +                * output_nfr = Freq - output_nir
> > +                *            = ((nfr / 4096) * fpfd) / pr
> > +                * Therefore:
> > +                * nfr = (output_nfr * 4096 * pr) / fpfd
> > +                */
> > +               output_nfr = freq_millihz - output_nir;
> > +               nfr = div64_s64(output_nfr * 4096ULL * pr, fpfd);
> 
> No need for the ULL
> fpfd is u32, so div_s64 is overkill, and div_s64 is sufficient.

Agreed will use div_s64.

> 
> > +               nfr = DIV_S64_ROUND_CLOSEST(nfr, 1000);
> > +
> > +               /* Validate nfr value within allowed limits */
> > +               if (nfr < RZG3L_PLL7_NFR_MIN || nfr > RZG3L_PLL7_NFR_MAX)
> > +                       continue;
> > +
> > +               p->nfr = nfr;
> > +
> > +               /* Compute (Ffdco * 4096) */
> > +               fdco = (((p->nir + 1) * 4096ULL) + p->nfr) * fpfd;
> 
> mul_u32_u32(p->nir + 1, 4096)

Ok.

> 
> > +               if (fdco < (RZG3L_PLL7_FDCO_RANGE_0_MIN * 4096ULL) ||
> > +                   fdco > (RZG3L_PLL7_FDCO_RANGE_1_MAX * 4096ULL))
> > +                       continue;
> > +
> > +               if (fdco <= (RZG3L_PLL7_FDCO_RANGE_0_MAX * 4096ULL))
> > +                       p->rangesel = 0;
> > +               else
> > +                       p->rangesel = 1;
> > +
> > +               /* compute the nr and magnify by 1000 */
> > +               output = mul_u32_u32((p->nir + 1), 4096);
> 
> Please move this up, and use it in the calculation of fdco above.

Agreed.

> 
> > +               output += p->nfr;
> > +               output *= 1000;
> > +               nr_div_mr_pr = output / 4096;
> 
> Open-coded 64-by-32 division, please use div_u64() instead.
> Or >> 12.

Ok will use div_u64().
> 
> mul_u64_add_u64_div_u64 (multiply, add, and divide) and mul_u64_u32_shr (multiply and shift) might also
> be handy, somewhere...

If you agree, will do this optimization later.

> 
> > +               if (nr_div_mr_pr < RZG3L_PLL7_NR_MIN || nr_div_mr_pr > RZG3L_PLL7_NR_MAX)
> > +                       continue;
> > +
> > +               /* compute the magnified multipier = nr(magnified)/(mr *pr)  */
> > +               nr_div_mr_pr /= (p->mr + 1) * pr;
> 
> Open-coded 64-by-32 division, please use div_u64() instead.

OK. Will use div_u64().
> 
> > +               if (nr_div_mr_pr < RZG3L_PLL7_MULT_MIN || nr_div_mr_pr > RZG3L_PLL7_MULT_MAX)
> > +                       continue;
> > +
> > +               output *= RZG3L_OSC_CLK;
> > +               output /= (p->mr + 1) * pr * 4096;
> 
> Open-coded 64-by-32 division. Or perhaps even 64-by-64?

OK. Will use div_u64().
> 
> > +
> > +               p->error_millihz = freq_millihz - output;
> > +               p->freq_millihz = output;
> > +
> > +               /* If an exact match is found, return immediately */
> > +               if (p->error_millihz == 0) {
> > +                       *pars = *p;
> > +                       return true;
> > +               }
> > +
> > +               /* Update best match if error is smaller */
> > +               if (abs(p->error_millihz) < abs(best->error_millihz))
> > +                       *best = *p;
> > +       }
> > +
> > +       return false;
> > +}
> > +
> 
> > +static const struct clk_ops rzg3l_cpg_plldsi_ops = {
> > +       .recalc_rate = rzg3s_cpg_pll_clk_recalc_rate,
> > +       .determine_rate = rzg3l_cpg_plldsi_determine_rate,
> > +       .set_rate = rzg3l_cpg_plldsi_set_rate,
> > +       .is_enabled = rzg3l_cpg_pll_clk_is_enabled,
> > +       .enable = rzg3l_cpg_pll_clk_enable, };
> 
> Basically this is rzg3l_cpg_pll_ops with .determine_rate() and
> .set_rate() added.  Why can't you just add them to the existing rzg3l_cpg_pll_ops, and use that for all
> PLLs? PLL7 doesn't seem that different to me.

As per Figure 4.4-6 Block Diagram of the Deformed Clock System (5),
Only PLL7 is variable one.

PLL registers (1, 4, 6, 7), PLL1-CA55, PLL4-DDR, PLL6- GBETH are fixed frequency PLL's and
I believe we need to use dividers for changing the rate for this PLL's. where as for PLL7, 
as the divider is fixed (eg: DSI: for a given bpp and num_lanes fixed. LVDS: 1/7 divider,
same case for DPI). So, we need to generate variable frequencies at run time using PLL7
registers.

Cheers,
Biju
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.