* [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
* [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 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
* 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®