Re: [PATCH 1/6] clk: renesas: rzg2l: Add DSI divider clock support for RZ/G3L
Geert Uytterhoeven <[email protected]>
| Newsgroups | org.kernel.vger.linux-renesas-soc,org.kernel.vger.linux-clk,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAMuHMdU3hKHP-dSVrX7xzQS8oozeJo_2na9Od3vRuPbKDGLt=Q@mail.gmail.com> |
Hi Biju, On Fri, 19 Jun 2026 at 18:40, Biju <[email protected]> wrote: > From: Biju Das <[email protected]> > > Add a new DSI divider clock type (CLK_TYPE_G3L_PLLDSI_DIV) for the RZ/G3L > SoC, which requires a different divider implementation than the existing > RZ/G2L DSI divider clock. > > The RZ/G3L DSI divider uses two cascaded dividers, DIV_DSI_A and > DIV_DSI_B, where the effective divider is: > > rate = parent_rate / ((1 << div_a) * (div_b + 1)) > > DIV_DSI_A is a power-of-two divider with values in the range [0, 5], > and DIV_DSI_B is a linear divider with values in the range [1, 16]. > > Introduce the g3l_dsi_div_hw_data structure, rzg3l_cpg_dsi_div_ops, and > rzg3l_cpg_dsi_div_clk_register() to implement the new clock type, and add > the DEF_G3L_PLLDSI_DIV() macro for use in clock table definitions. > > 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 > @@ -834,6 +837,134 @@ rzg2l_cpg_dsi_div_clk_register(const struct cpg_core_clk *core, > return clk_hw->clk; > } > > +struct g3l_dsi_div_hw_data { > + struct clk_hw hw; > + struct rzg2l_cpg_priv *priv; > + unsigned long rate; > + u32 off; > + u8 div_a; > + u8 div_b; > +}; > + > +#define to_g3l_dsi_div_hw_data(_hw) container_of(_hw, struct g3l_dsi_div_hw_data, hw) > + > +static unsigned long rzg3l_cpg_dsi_div_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); > + struct rzg2l_cpg_priv *priv = dsi_div->priv; > + int div_a, div_b, val; > + > + val = readl(priv->base + dsi_div->off); > + div_a = FIELD_GET(GENMASK(2, 0), val); > + div_b = FIELD_GET(GENMASK(7, 4), val); Please add and use #define DIV_DSI_A_SET GENMASK(2, 0) #define DIV_DSI_B_SET GENMASK(7, 4) (after having seen G3L_SDIV_DSI_C_SET, I kept on looking for similar A_SET and B_SET use). > + > + return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, (1 << div_a) * (div_b + 1)); You can simplify the divider to "(div_b + 1) << div_a". > +} > + > +static int rzg3l_cpg_dsi_div_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); > + struct rzg2l_cpg_priv *priv = dsi_div->priv; > + u32 divider = dsi_div_ab_desired; unsigned int > + bool divider_found = false; > + unsigned int div_a, div_b; Perhaps move the declarations of the loop counters inside the for()-statements? > + > + if (dsi_div_target) { > + /* Calculate the DIV_DSI_A and DIV_DSI_B */ Please drop "the". > + for (div_a = 5; div_a >= 0 && !divider_found; div_a--) { div_a is unsigned, so >= is always true > + for (div_b = 0; div_b < 16; div_b++) { > + divider = (1 << div_a) * (div_b + 1); (div_b + 1) << div_a > + if (divider == dsi_div_ab_desired) { divider is 1..512, while dsi_div_ab_desired is only u8. > + dsi_div->div_a = div_a; > + dsi_div->div_b = div_b; > + divider_found = true; > + break; > + } > + } > + } > + } else { > + dsi_div->div_b = 0; > + /* Calculate the DIV_DSI_A */ Please drop "the" > + for (div_a = 5; div_a >= 0 && !divider_found; div_a--) { > + divider = (1 << div_a); > + if (divider == dsi_div_ab_desired) { > + dsi_div->div_a = div_a; > + divider_found = true; > + break; > + } > + } > + } > + > + if (!divider_found) { > + dev_err(priv->dev, "failed dsi div for: %u\n", divider); > + return -EINVAL; > + } > + > + req->best_parent_rate = req->rate * divider; > + > + return 0; > +} > + > +static int rzg3l_cpg_dsi_div_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); > + struct rzg2l_cpg_priv *priv = dsi_div->priv; > + > + writel(RZG3L_SDIV_DIV_DSI_A_WEN | RZG3L_SDIV_DIV_DSI_B_WEN | > + (dsi_div->div_a << 0) | (dsi_div->div_b << 4), FIELD_PREP(DIV_DSI_A_SET, dsi_div->div_a) | FIELD_PREP(DIV_DSI_B_SET, dsi_div->div_b) > + priv->base + dsi_div->off); > + > + return 0; > +} Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds