mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®