[PATCH v2 5/9] clk: renesas: rzg2l: Add support for LVDS fixed-factor divider with 4:7 duty cycle

From: Biju

Date: Tue Aug 11 2026 - 14:32:42 EST


From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>

Add a new CLK_TYPE_G3L_LVDS_DIV clock type and register function for
a fixed-factor divider used by the LVDS clock, which cannot produce a
50% duty cycle for odd divider values. Implement a custom
get_duty_cycle() callback that reports the actual (div + 1) / 2 : div
duty cycle, and add the DEF_G3L_LVDS_DIV() macro to define such
clocks.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v2:
* New patch.
---
drivers/clk/renesas/rzg2l-cpg.c | 87 +++++++++++++++++++++++++++++++++
drivers/clk/renesas/rzg2l-cpg.h | 6 +++
2 files changed, 93 insertions(+)

diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 2d80775c60c2..d3d4e618466c 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -864,6 +864,90 @@ rzg2l_cpg_dsi_div_clk_register(const struct cpg_core_clk *core,
return clk_hw->clk;
}

+static int rzg3l_cpg_lvds_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ return clk_fixed_factor_ops.determine_rate(hw, req);
+}
+
+static int rzg3l_cpg_lvds_div_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 rzg3l_cpg_lvds_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return clk_fixed_factor_ops.recalc_rate(hw, parent_rate);
+}
+
+static unsigned long rzg3l_cpg_lvds_div_recalc_accuracy(struct clk_hw *hw,
+ unsigned long parent_accuracy)
+{
+ return clk_fixed_factor_ops.recalc_accuracy(hw, parent_accuracy);
+}
+
+static int rzg3l_cpg_lvds_div_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 = (fix->div + 1) / 2;
+ duty->den = fix->div;
+
+ return 0;
+}
+
+static const struct clk_ops rzg3l_cpg_lvds_div_ops = {
+ .determine_rate = rzg3l_cpg_lvds_div_determine_rate,
+ .set_rate = rzg3l_cpg_lvds_div_set_rate,
+ .recalc_rate = rzg3l_cpg_lvds_div_recalc_rate,
+ .recalc_accuracy = rzg3l_cpg_lvds_div_recalc_accuracy,
+ .get_duty_cycle = rzg3l_cpg_lvds_div_get_duty_cycle,
+};
+
+static struct clk * __init
+rzg3l_cpg_lvds_div_clk_register(const struct cpg_core_clk *core,
+ struct rzg2l_cpg_priv *priv)
+{
+ struct clk_fixed_factor *ff;
+ struct clk_init_data init;
+ const struct clk *parent;
+ const char *parent_name;
+ int ret;
+
+ parent = priv->clks[core->parent];
+ if (IS_ERR(parent))
+ return ERR_CAST(parent);
+
+ ff = devm_kzalloc(priv->dev, sizeof(*ff), GFP_KERNEL);
+ if (!ff)
+ return ERR_PTR(-ENOMEM);
+
+ parent_name = __clk_get_name(parent);
+ init.name = core->name;
+ init.ops = &rzg3l_cpg_lvds_div_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ ff->hw.init = &init;
+ ff->mult = 1;
+ ff->div = core->conf;
+
+ ret = devm_clk_hw_register(priv->dev, &ff->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return ff->hw.clk;
+}
+
struct g3l_dsi_div_hw_data {
struct clk_hw hw;
struct rzg2l_cpg_priv *priv;
@@ -1715,6 +1799,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
case CLK_TYPE_G3L_DSI_DIV:
clk = rzg3l_cpg_dsi_div_clk_register(core, priv);
break;
+ case CLK_TYPE_G3L_LVDS_DIV:
+ clk = rzg3l_cpg_lvds_div_clk_register(core, priv);
+ break;
case CLK_TYPE_G3L_PLLDSI:
clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg3l_cpg_plldsi_ops);
break;
diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h
index ebf6b744dac4..22cca4795301 100644
--- a/drivers/clk/renesas/rzg2l-cpg.h
+++ b/drivers/clk/renesas/rzg2l-cpg.h
@@ -148,6 +148,9 @@ enum clk_types {

/* Clock for G3L DSI divider */
CLK_TYPE_G3L_DSI_DIV,
+
+ /* Clock for G3L LVDS divider */
+ CLK_TYPE_G3L_LVDS_DIV,
};

#define DEF_TYPE(_name, _id, _type...) \
@@ -217,6 +220,9 @@ enum clk_types {
#define DEF_G3L_DSI_DIV(_name, _id, _parent, _conf) \
DEF_TYPE(_name, _id, CLK_TYPE_G3L_DSI_DIV, .parent = _parent, .conf = _conf, \
.core_flags = CLK_SET_RATE_PARENT)
+#define DEF_G3L_LVDS_DIV(_name, _id, _parent, _conf) \
+ DEF_TYPE(_name, _id, CLK_TYPE_G3L_LVDS_DIV, .parent = _parent, .conf = _conf, \
+ .core_flags = CLK_SET_RATE_PARENT)
#define DEF_G3L_PLLDSI(_name, _id, _parent, _conf) \
DEF_TYPE(_name, _id, CLK_TYPE_G3L_PLLDSI, .parent = _parent, .conf = _conf)

--
2.43.0