* [PATCH 0/2] Renesas RZ/G3E DSI/LVDS clk improvements
@ 2026-10-29 14:25 Tommaso Merciai
2026-10-29 14:25 ` [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent Tommaso Merciai
2026-10-29 14:25 ` [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider Tommaso Merciai
0 siblings, 2 replies; 6+ messages in thread
From: Tommaso Merciai @ 2026-10-29 14:25 UTC (permalink / raw)
To: Geert Uytterhoeven, Stephen Boyd, Brian Masney, Jerome Brunet
Cc: linux-renesas-soc, linux-clk, linux-kernel, Tommaso Merciai,
biju.das.jz, tomm.merciai
Dear All,
This patch series improves the handling of the PLLDSI mux on the RZ/G3E
SoC, which selects between the LVDS path, a fixed division by 7, and the
DSI/RGB path, a programmable divider.
PATCH 1/2: Forward PLLDSI mux rate requests to the parent via
clk_mux_ops.determine_rate(). Drop struct rzv2h_plldsi_mux_clk.
PATCH 2/2: Moves the 4/7 duty cycle of the LVDS path from the mux to the
divider that produces it, similar to the work done on RZ/G3L [1] .
[1] https://lore.kernel.org/all/20260811182729.360959-1-biju.das.jz@bp.renesas.com/
Kind regards,
Tommaso
Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
---
Tommaso Merciai (2):
clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent
clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider
drivers/clk/renesas/r9a09g047-cpg.c | 4 +-
drivers/clk/renesas/rzv2h-cpg.c | 195 ++++++++++++++++++------------------
drivers/clk/renesas/rzv2h-cpg.h | 6 +-
3 files changed, 107 insertions(+), 98 deletions(-)
---
base-commit: b427a7529bc420a22cb22ea09cc788e925804e8e
change-id: 20261029-tm-ssgrlpcsrc-2083-v5-clk-duty-fixes-c009586b76c8
Best regards,
--
Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent 2026-10-29 14:25 [PATCH 0/2] Renesas RZ/G3E DSI/LVDS clk improvements Tommaso Merciai @ 2026-10-29 14:25 ` Tommaso Merciai 2026-11-02 16:03 ` Geert Uytterhoeven 2026-10-29 14:25 ` [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider Tommaso Merciai 1 sibling, 1 reply; 6+ messages in thread From: Tommaso Merciai @ 2026-10-29 14:25 UTC (permalink / raw) To: Geert Uytterhoeven, Stephen Boyd, Brian Masney, Jerome Brunet Cc: linux-renesas-soc, linux-clk, linux-kernel, Tommaso Merciai, biju.das.jz, tomm.merciai The PLLDSI mux determines rates itself, and on the LVDS path it reads the PLL instance from a parent that is not the PLL, addressing the per instance parameters past their end. Forward both inputs to clk_mux_ops.determine_rate() and let the parents carry the request up to the PLL. The parent encodes the output type and is selected through .set_duty_cycle(), hence CLK_SET_RATE_NO_REPARENT. The mux wrapper is left unused, so register a plain struct clk_mux. Fixes: 3afccee4a3b8 ("clk: renesas: rzv2h: Add PLLDSI clk mux support") Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> --- drivers/clk/renesas/rzv2h-cpg.c | 75 +++++------------------------------------ drivers/clk/renesas/rzv2h-cpg.h | 2 +- 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index 7ce94680c79a..12a5f058dd71 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -219,20 +219,6 @@ struct rzv2h_plldsi_div_clk { #define RZV2H_MAX_DIV_TABLES (16) -/** - * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock - * - * @priv: CPG private data - * @mux: mux clk - */ -struct rzv2h_plldsi_mux_clk { - struct rzv2h_cpg_priv *priv; - struct clk_mux mux; -}; - -#define to_plldsi_clk_mux(_mux) \ - container_of(_mux, struct rzv2h_plldsi_mux_clk, mux) - static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -474,52 +460,10 @@ static int rzv2h_cpg_plldsi_smux_set_parent(struct clk_hw *hw, u8 index) return clk_mux_ops.set_parent(hw, index); } -static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv *priv, - struct pll_clk *pll_clk, - struct clk_rate_request *req) -{ - struct rzv2h_pll_div_pars *dsi_params; - struct rzv2h_pll_dsi_info *dsi_info; - u8 lvds_table[] = { 7 }; - u64 rate_millihz; - - dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; - dsi_params = &dsi_info->pll_dsi_parameters; - - rate_millihz = mul_u32_u32(req->rate, MILLI); - if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, - lvds_table, ARRAY_SIZE(lvds_table), rate_millihz)) { - dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", - req->rate); - return -EINVAL; - } - - req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); - req->best_parent_rate = req->rate; - dsi_info->req_pll_dsi_rate = req->best_parent_rate * dsi_params->div.divider_value; - - return 0; -} - static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct clk_mux *mux = to_clk_mux(hw); - struct rzv2h_plldsi_mux_clk *dsi_mux = to_plldsi_clk_mux(mux); - struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); - struct rzv2h_cpg_priv *priv = dsi_mux->priv; - - /* - * For LVDS output (parent index 0), calculate PLL parameters with - * fixed divider value of 7. For DSI/RGB output (parent index 1) skip - * PLL calculation here as it's handled by determine_rate of the - * divider (up one level). - */ - if (!clk_mux_ops.get_parent(hw)) - return rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, req); - - req->best_parent_rate = req->rate; - return 0; + return clk_mux_ops.determine_rate(hw, req); } static int rzv2h_cpg_plldsi_smux_get_duty_cycle(struct clk_hw *hw, @@ -581,8 +525,8 @@ static struct clk_hw * __init rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core, struct rzv2h_cpg_priv *priv) { - struct rzv2h_plldsi_mux_clk *clk_hw_data; struct clk_init_data init = {}; + struct clk_mux *clk_hw_data; struct clk_hw *clk_hw; struct smuxed smux; int ret; @@ -598,22 +542,19 @@ rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core, if (!clk_hw_data) return ERR_PTR(-ENOMEM); - clk_hw_data->priv = priv; - init.name = core->name; init.ops = &rzv2h_cpg_plldsi_smux_ops; init.flags = core->flag; init.parent_names = core->parent_names; init.num_parents = core->num_parents; - clk_hw_data->mux.reg = priv->base + smux.offset; + clk_hw_data->reg = priv->base + smux.offset; + clk_hw_data->shift = smux.shift; + clk_hw_data->mask = clk_div_mask(smux.width); + clk_hw_data->flags = core->mux_flags; + clk_hw_data->lock = &priv->rmw_lock; - clk_hw_data->mux.shift = smux.shift; - clk_hw_data->mux.mask = clk_div_mask(smux.width); - clk_hw_data->mux.flags = core->mux_flags; - clk_hw_data->mux.lock = &priv->rmw_lock; - - clk_hw = &clk_hw_data->mux.hw; + clk_hw = &clk_hw_data->hw; clk_hw->init = &init; ret = devm_clk_hw_register(priv->dev, clk_hw); diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index dec0f7b621d6..4492f4e7b9ae 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -251,7 +251,7 @@ enum clk_types { .cfg.smux = _smux_packed, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .flag = CLK_SET_RATE_PARENT, \ + .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \ .mux_flags = CLK_MUX_HIWORD_MASK) /** -- 2.54.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent 2026-10-29 14:25 ` [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent Tommaso Merciai @ 2026-11-02 16:03 ` Geert Uytterhoeven 0 siblings, 0 replies; 6+ messages in thread From: Geert Uytterhoeven @ 2026-11-02 16:03 UTC (permalink / raw) To: Tommaso Merciai Cc: Stephen Boyd, Brian Masney, Jerome Brunet, linux-renesas-soc, linux-clk, linux-kernel, biju.das.jz, tomm.merciai On Thu, 29 Oct 2026 at 15:26, Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> wrote: > The PLLDSI mux determines rates itself, and on the LVDS path it reads the > PLL instance from a parent that is not the PLL, addressing the per instance > parameters past their end. > > Forward both inputs to clk_mux_ops.determine_rate() and let the parents > carry the request up to the PLL. The parent encodes the output type and is > selected through .set_duty_cycle(), hence CLK_SET_RATE_NO_REPARENT. > > The mux wrapper is left unused, so register a plain struct clk_mux. > > Fixes: 3afccee4a3b8 ("clk: renesas: rzv2h: Add PLLDSI clk mux support") > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> i.e. will queue in renesas-clk for v7.5. Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider 2026-10-29 14:25 [PATCH 0/2] Renesas RZ/G3E DSI/LVDS clk improvements Tommaso Merciai 2026-10-29 14:25 ` [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent Tommaso Merciai @ 2026-10-29 14:25 ` Tommaso Merciai 2026-11-02 16:16 ` Geert Uytterhoeven 1 sibling, 1 reply; 6+ messages in thread From: Tommaso Merciai @ 2026-10-29 14:25 UTC (permalink / raw) To: Geert Uytterhoeven, Stephen Boyd, Brian Masney, Jerome Brunet Cc: linux-renesas-soc, linux-clk, linux-kernel, Tommaso Merciai, biju.das.jz, tomm.merciai The 4/7 duty cycle of the LVDS path comes from the division by 7, not from the PLLDSI mux, as an odd divider cannot produce a 50% duty cycle. Report it from a dedicated LVDS divider clock type and let the core walk up to the selected parent through CLK_DUTY_CYCLE_PARENT. The reported values do not change, the mux keeps selecting the path, and its duty cycle constants go away. Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> --- drivers/clk/renesas/r9a09g047-cpg.c | 4 +- drivers/clk/renesas/rzv2h-cpg.c | 120 +++++++++++++++++++++++++++--------- drivers/clk/renesas/rzv2h-cpg.h | 6 +- 3 files changed, 99 insertions(+), 31 deletions(-) diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index f576dd441a11..cecc6d38d8f0 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -226,8 +226,8 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { CSDIV1_DIVCTL2, dtable_2_16_plldsi), DEF_PLLDSI_DIV(".plldsi1_csdiv", CLK_PLLDSI1_CSDIV, CLK_PLLDSI1, CSDIV1_DIVCTL3, dtable_2_16_plldsi), - DEF_FIXED(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 1, 7), - DEF_FIXED(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 1, 7), + DEF_PLLDSI_DIV_LVDS(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 7), + DEF_PLLDSI_DIV_LVDS(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 7), DEF_PLLDSI_SMUX(".smux2_dsi0_clk", CLK_SMUX2_DSI0_CLK, SSEL3_SELCTL0, smux2_dsi0_clk), DEF_PLLDSI_SMUX(".smux2_dsi1_clk", CLK_SMUX2_DSI1_CLK, diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index 12a5f058dd71..7ca2447fd811 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -75,11 +75,6 @@ /* On RZ/G3E SoC we have two DSI PLLs */ #define MAX_CPG_DSI_PLL 2 -#define CPG_PLLDSI_SMUX_LVDS_DUTY_NUM 4 -#define CPG_PLLDSI_SMUX_LVDS_DUTY_DEN 7 -#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM 1 -#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN 2 - /** * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters * @@ -466,26 +461,6 @@ static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw, return clk_mux_ops.determine_rate(hw, req); } -static int rzv2h_cpg_plldsi_smux_get_duty_cycle(struct clk_hw *hw, - struct clk_duty *duty) -{ - u8 parent = clk_mux_ops.get_parent(hw); - - /* - * CDIV7_DSIx_CLK - LVDS path (div7) - duty 4/7. - * CSDIV_DSIx - DSI/RGB path (csdiv) - duty 1/2. - */ - if (parent == 0) { - duty->num = CPG_PLLDSI_SMUX_LVDS_DUTY_NUM; - duty->den = CPG_PLLDSI_SMUX_LVDS_DUTY_DEN; - } else { - duty->num = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM; - duty->den = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN; - } - - return 0; -} - static int rzv2h_cpg_plldsi_smux_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) { @@ -497,8 +472,7 @@ static int rzv2h_cpg_plldsi_smux_set_duty_cycle(struct clk_hw *hw, * - If duty > 50% (num/den > 1/2), select LVDS path (parent 0) * - Otherwise, select DSI/RGB path (parent 1) */ - if (duty->num * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN > - duty->den * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM) + if (duty->num * 2 > duty->den) parent_idx = 0; else parent_idx = 1; @@ -517,7 +491,6 @@ static const struct clk_ops rzv2h_cpg_plldsi_smux_ops = { .determine_rate = rzv2h_cpg_plldsi_smux_determine_rate, .get_parent = rzv2h_cpg_plldsi_smux_get_parent, .set_parent = rzv2h_cpg_plldsi_smux_set_parent, - .get_duty_cycle = rzv2h_cpg_plldsi_smux_get_duty_cycle, .set_duty_cycle = rzv2h_cpg_plldsi_smux_set_duty_cycle, }; @@ -890,6 +863,94 @@ rzv2h_cpg_fixed_mod_status_clk_register(const struct cpg_core_clk *core, return &clk_hw_data->fix.hw; } +static int rzv2h_cpg_plldsi_div_lvds_get_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + /* + * An odd divider cannot generate a 50% duty cycle: the output stays + * high for (div + 1) / 2 input clock cycles out of div, e.g. a divider + * of 7 gives a duty cycle of 4/7. + */ + duty->num = DIV_ROUND_UP(fix->div, 2); + duty->den = fix->div; + + return 0; +} + +static int rzv2h_cpg_plldsi_div_lvds_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_fixed_factor_ops.determine_rate(hw, req); +} + +static int rzv2h_cpg_plldsi_div_lvds_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + return clk_fixed_factor_ops.set_rate(hw, rate, parent_rate); +} + +static unsigned long rzv2h_cpg_plldsi_div_lvds_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_fixed_factor_ops.recalc_rate(hw, parent_rate); +} + +static unsigned long +rzv2h_cpg_plldsi_div_lvds_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_accuracy) +{ + return clk_fixed_factor_ops.recalc_accuracy(hw, parent_accuracy); +} + +static const struct clk_ops rzv2h_cpg_plldsi_div_lvds_ops = { + .determine_rate = rzv2h_cpg_plldsi_div_lvds_determine_rate, + .set_rate = rzv2h_cpg_plldsi_div_lvds_set_rate, + .recalc_rate = rzv2h_cpg_plldsi_div_lvds_recalc_rate, + .recalc_accuracy = rzv2h_cpg_plldsi_div_lvds_recalc_accuracy, + .get_duty_cycle = rzv2h_cpg_plldsi_div_lvds_get_duty_cycle, +}; + +static struct clk_hw * __init +rzv2h_cpg_plldsi_div_lvds_clk_register(const struct cpg_core_clk *core, + struct rzv2h_cpg_priv *priv) +{ + struct clk_init_data init = { }; + struct clk_fixed_factor *fix; + const struct clk_hw *parent; + const char *parent_name; + int ret; + + WARN_DEBUG(core->parent >= priv->num_core_clks); + parent = priv->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + parent_name = clk_hw_get_name(parent); + + fix = devm_kzalloc(priv->dev, sizeof(*fix), GFP_KERNEL); + if (!fix) + return ERR_PTR(-ENOMEM); + + init.name = core->name; + init.ops = &rzv2h_cpg_plldsi_div_lvds_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + + fix->hw.init = &init; + fix->mult = core->mult; + fix->div = core->div; + + ret = devm_clk_hw_register(priv->dev, &fix->hw); + if (ret) + return ERR_PTR(ret); + + return &fix->hw; +} + static struct clk_hw *rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, void *data) @@ -983,6 +1044,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, } clk_hw = rzv2h_cpg_fixed_mod_status_clk_register(core, priv); break; + case CLK_TYPE_PLLDSI_DIV_LVDS: + clk_hw = rzv2h_cpg_plldsi_div_lvds_clk_register(core, priv); + break; case CLK_TYPE_PLL: clk_hw = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_pll_ops); break; diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index 4492f4e7b9ae..fa6caf693a72 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -207,6 +207,7 @@ enum clk_types { CLK_TYPE_SMUX, /* Static Mux */ CLK_TYPE_PLLDSI, /* PLLDSI */ CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ + CLK_TYPE_PLLDSI_DIV_LVDS, /* PLLDSI LVDS divider */ CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */ }; @@ -246,12 +247,15 @@ enum clk_types { .dtable = _dtable, \ .parent = _parent, \ .flag = CLK_SET_RATE_PARENT) +#define DEF_PLLDSI_DIV_LVDS(_name, _id, _parent, _div) \ + DEF_BASE(_name, _id, CLK_TYPE_PLLDSI_DIV_LVDS, _parent, .div = _div, .mult = 1) #define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \ DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \ .cfg.smux = _smux_packed, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \ + .flag = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT | \ + CLK_SET_RATE_NO_REPARENT, \ .mux_flags = CLK_MUX_HIWORD_MASK) /** -- 2.54.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider 2026-10-29 14:25 ` [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider Tommaso Merciai @ 2026-11-02 16:16 ` Geert Uytterhoeven 2026-11-04 16:05 ` Tommaso Merciai 0 siblings, 1 reply; 6+ messages in thread From: Geert Uytterhoeven @ 2026-11-02 16:16 UTC (permalink / raw) To: Tommaso Merciai Cc: Stephen Boyd, Brian Masney, Jerome Brunet, linux-renesas-soc, linux-clk, linux-kernel, biju.das.jz, tomm.merciai Hi Tommaso, Thanks for your patch! On Thu, 29 Oct 2026 at 15:26, Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> wrote: > The 4/7 duty cycle of the LVDS path comes from the division by 7, not from > the PLLDSI mux, as an odd divider cannot produce a 50% duty cycle. Note that the latter is not true in general: if an odd divider looks at both edges of the parent clock signal, the output will have a 50% duty cycle (assumed the parent has a 50% duty cycle). If an odd divider looks only at rising or falling edges, the output will indeed have a non-50% duty cycle. > Report it from a dedicated LVDS divider clock type and let the core walk up > to the selected parent through CLK_DUTY_CYCLE_PARENT. The reported values > do not change, the mux keeps selecting the path, and its duty cycle > constants go away. > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> i.e. will queue in renesas-clk for v7.5. > --- a/drivers/clk/renesas/rzv2h-cpg.c > +++ b/drivers/clk/renesas/rzv2h-cpg.c > @@ -890,6 +863,94 @@ rzv2h_cpg_fixed_mod_status_clk_register(const struct cpg_core_clk *core, > return &clk_hw_data->fix.hw; > } > > +static int rzv2h_cpg_plldsi_div_lvds_get_duty_cycle(struct clk_hw *hw, > + struct clk_duty *duty) > +{ > + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); > + > + /* > + * An odd divider cannot generate a 50% duty cycle: the output stays > + * high for (div + 1) / 2 input clock cycles out of div, e.g. a divider > + * of 7 gives a duty cycle of 4/7. > + */ > + duty->num = DIV_ROUND_UP(fix->div, 2); > + duty->den = fix->div; > + > + return 0; > +} > + > +static int rzv2h_cpg_plldsi_div_lvds_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + return clk_fixed_factor_ops.determine_rate(hw, req); > +} > + > +static int rzv2h_cpg_plldsi_div_lvds_set_rate(struct clk_hw *hw, > + unsigned long rate, > + unsigned long parent_rate) > +{ > + return clk_fixed_factor_ops.set_rate(hw, rate, parent_rate); > +} > + > +static unsigned long rzv2h_cpg_plldsi_div_lvds_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + return clk_fixed_factor_ops.recalc_rate(hw, parent_rate); > +} > + > +static unsigned long > +rzv2h_cpg_plldsi_div_lvds_recalc_accuracy(struct clk_hw *hw, > + unsigned long parent_accuracy) > +{ > + return clk_fixed_factor_ops.recalc_accuracy(hw, parent_accuracy); > +} > + > +static const struct clk_ops rzv2h_cpg_plldsi_div_lvds_ops = { > + .determine_rate = rzv2h_cpg_plldsi_div_lvds_determine_rate, > + .set_rate = rzv2h_cpg_plldsi_div_lvds_set_rate, > + .recalc_rate = rzv2h_cpg_plldsi_div_lvds_recalc_rate, > + .recalc_accuracy = rzv2h_cpg_plldsi_div_lvds_recalc_accuracy, > + .get_duty_cycle = rzv2h_cpg_plldsi_div_lvds_get_duty_cycle, > +}; > + > +static struct clk_hw * __init > +rzv2h_cpg_plldsi_div_lvds_clk_register(const struct cpg_core_clk *core, > + struct rzv2h_cpg_priv *priv) > +{ > + struct clk_init_data init = { }; > + struct clk_fixed_factor *fix; > + const struct clk_hw *parent; > + const char *parent_name; > + int ret; > + > + WARN_DEBUG(core->parent >= priv->num_core_clks); > + parent = priv->clks[core->parent]; > + if (IS_ERR(parent)) > + return ERR_CAST(parent); > + > + parent_name = clk_hw_get_name(parent); > + > + fix = devm_kzalloc(priv->dev, sizeof(*fix), GFP_KERNEL); > + if (!fix) > + return ERR_PTR(-ENOMEM); > + > + init.name = core->name; > + init.ops = &rzv2h_cpg_plldsi_div_lvds_ops; > + init.flags = CLK_SET_RATE_PARENT; > + init.parent_names = &parent_name; > + init.num_parents = 1; > + > + fix->hw.init = &init; > + fix->mult = core->mult; > + fix->div = core->div; > + > + ret = devm_clk_hw_register(priv->dev, &fix->hw); > + if (ret) > + return ERR_PTR(ret); > + > + return &fix->hw; > +} > + > static struct clk_hw > *rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, > void *data) Since there are now two copies of this code (see rzg3l_cpg_lvds_div_*), perhaps the time is ripe to add odd duty cycle support to clk_fixed_factor? As duty->num can be rounded up or down, I am envisioning two new mutually-exclusive fixflags parameters, e.g.: CLK_FIXED_FACTOR_ODD_DUTY_DOWN CLK_FIXED_FACTOR_ODD_DUTY_UP What do you think? Thanks! Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider 2026-11-02 16:16 ` Geert Uytterhoeven @ 2026-11-04 16:05 ` Tommaso Merciai 0 siblings, 0 replies; 6+ messages in thread From: Tommaso Merciai @ 2026-11-04 16:05 UTC (permalink / raw) To: Geert Uytterhoeven Cc: Stephen Boyd, Brian Masney, Jerome Brunet, linux-renesas-soc, linux-clk, linux-kernel, biju.das.jz, tomm.merciai Hi Geert, On Mon, Nov 02, 2026 at 05:16:09PM +0100, Geert Uytterhoeven wrote: > Hi Tommaso, > > Thanks for your patch! > > On Thu, 29 Oct 2026 at 15:26, Tommaso Merciai > <tommaso.merciai.xr@bp.renesas.com> wrote: > > The 4/7 duty cycle of the LVDS path comes from the division by 7, not from > > the PLLDSI mux, as an odd divider cannot produce a 50% duty cycle. > > Note that the latter is not true in general: if an odd divider looks > at both edges of the parent clock signal, the output will have a 50% > duty cycle (assumed the parent has a 50% duty cycle). > If an odd divider looks only at rising or falling edges, the output > will indeed have a non-50% duty cycle. Thanks for sharing. > > > Report it from a dedicated LVDS divider clock type and let the core walk up > > to the selected parent through CLK_DUTY_CYCLE_PARENT. The reported values > > do not change, the mux keeps selecting the path, and its duty cycle > > constants go away. > > > > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com> > > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> > i.e. will queue in renesas-clk for v7.5. > > > --- a/drivers/clk/renesas/rzv2h-cpg.c > > +++ b/drivers/clk/renesas/rzv2h-cpg.c > > > @@ -890,6 +863,94 @@ rzv2h_cpg_fixed_mod_status_clk_register(const struct cpg_core_clk *core, > > return &clk_hw_data->fix.hw; > > } > > > > +static int rzv2h_cpg_plldsi_div_lvds_get_duty_cycle(struct clk_hw *hw, > > + struct clk_duty *duty) > > +{ > > + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); > > + > > + /* > > + * An odd divider cannot generate a 50% duty cycle: the output stays > > + * high for (div + 1) / 2 input clock cycles out of div, e.g. a divider > > + * of 7 gives a duty cycle of 4/7. > > + */ > > + duty->num = DIV_ROUND_UP(fix->div, 2); > > + duty->den = fix->div; > > + > > + return 0; > > +} > > + > > +static int rzv2h_cpg_plldsi_div_lvds_determine_rate(struct clk_hw *hw, > > + struct clk_rate_request *req) > > +{ > > + return clk_fixed_factor_ops.determine_rate(hw, req); > > +} > > + > > +static int rzv2h_cpg_plldsi_div_lvds_set_rate(struct clk_hw *hw, > > + unsigned long rate, > > + unsigned long parent_rate) > > +{ > > + return clk_fixed_factor_ops.set_rate(hw, rate, parent_rate); > > +} > > + > > +static unsigned long rzv2h_cpg_plldsi_div_lvds_recalc_rate(struct clk_hw *hw, > > + unsigned long parent_rate) > > +{ > > + return clk_fixed_factor_ops.recalc_rate(hw, parent_rate); > > +} > > + > > +static unsigned long > > +rzv2h_cpg_plldsi_div_lvds_recalc_accuracy(struct clk_hw *hw, > > + unsigned long parent_accuracy) > > +{ > > + return clk_fixed_factor_ops.recalc_accuracy(hw, parent_accuracy); > > +} > > + > > +static const struct clk_ops rzv2h_cpg_plldsi_div_lvds_ops = { > > + .determine_rate = rzv2h_cpg_plldsi_div_lvds_determine_rate, > > + .set_rate = rzv2h_cpg_plldsi_div_lvds_set_rate, > > + .recalc_rate = rzv2h_cpg_plldsi_div_lvds_recalc_rate, > > + .recalc_accuracy = rzv2h_cpg_plldsi_div_lvds_recalc_accuracy, > > + .get_duty_cycle = rzv2h_cpg_plldsi_div_lvds_get_duty_cycle, > > +}; > > + > > +static struct clk_hw * __init > > +rzv2h_cpg_plldsi_div_lvds_clk_register(const struct cpg_core_clk *core, > > + struct rzv2h_cpg_priv *priv) > > +{ > > + struct clk_init_data init = { }; > > + struct clk_fixed_factor *fix; > > + const struct clk_hw *parent; > > + const char *parent_name; > > + int ret; > > + > > + WARN_DEBUG(core->parent >= priv->num_core_clks); > > + parent = priv->clks[core->parent]; > > + if (IS_ERR(parent)) > > + return ERR_CAST(parent); > > + > > + parent_name = clk_hw_get_name(parent); > > + > > + fix = devm_kzalloc(priv->dev, sizeof(*fix), GFP_KERNEL); > > + if (!fix) > > + return ERR_PTR(-ENOMEM); > > + > > + init.name = core->name; > > + init.ops = &rzv2h_cpg_plldsi_div_lvds_ops; > > + init.flags = CLK_SET_RATE_PARENT; > > + init.parent_names = &parent_name; > > + init.num_parents = 1; > > + > > + fix->hw.init = &init; > > + fix->mult = core->mult; > > + fix->div = core->div; > > + > > + ret = devm_clk_hw_register(priv->dev, &fix->hw); > > + if (ret) > > + return ERR_PTR(ret); > > + > > + return &fix->hw; > > +} > > + > > static struct clk_hw > > *rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, > > void *data) > > Since there are now two copies of this code (see rzg3l_cpg_lvds_div_*), > perhaps the time is ripe to add odd duty cycle support to > clk_fixed_factor? As duty->num can be rounded up or down, I am > envisioning two new mutually-exclusive fixflags parameters, e.g.: > > CLK_FIXED_FACTOR_ODD_DUTY_DOWN > CLK_FIXED_FACTOR_ODD_DUTY_UP > > What do you think? Agreed, thanks. I can send send a separate series, adding both flags into: - drivers/clk/clk-fixed-factor.c In thi way later we can converts: - drivers/clk/renesas/rzv2h-cpg.c - drivers/clk/renesas/rzg2l-cpg.c To the newly added CLK_FIXED_FACTOR_ODD_DUTY_UP. Not sure about CLK_FIXED_FACTOR_ODD_DUTY_DOWN which has nos users for now. Please correct me if I'm wrong. Kind regards, Tommaso > Thanks! > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > 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 ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-04 16:05 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-29 14:25 [PATCH 0/2] Renesas RZ/G3E DSI/LVDS clk improvements Tommaso Merciai 2026-10-29 14:25 ` [PATCH 1/2] clk: renesas: rzv2h: Forward PLLDSI mux rate requests to the parent Tommaso Merciai 2026-11-02 16:03 ` Geert Uytterhoeven 2026-10-29 14:25 ` [PATCH 2/2] clk: renesas: rzv2h: Report the DSI duty cycle from the LVDS divider Tommaso Merciai 2026-11-02 16:16 ` Geert Uytterhoeven 2026-11-04 16:05 ` Tommaso Merciai
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®