[PATCH v5 4/9] clk: renesas: rzg2l-cpg: Re-enable critical module clocks during resume

Biju posted 9 patches 2 weeks, 5 days ago
There is a newer version of this series
[PATCH v5 4/9] clk: renesas: rzg2l-cpg: Re-enable critical module clocks during resume
Posted by Biju 2 weeks, 5 days ago
From: Biju Das <biju.das.jz@bp.renesas.com>

After a suspend/resume cycle, critical module clocks may be left disabled
as the hardware state is not automatically restored. Unlike regular clocks
which are re-enabled by their respective drivers, critical clocks
(CLK_IS_CRITICAL) have no owning driver to restore them, so the CPG driver
must take responsibility for re-enabling them on resume.

Introduce struct rzg2l_crit_clk_hw to track critical module clock hardware
entries in a singly-linked list anchored at crit_clk_hw_head in
rzg2l_cpg_priv. Populate the list during module clock registration by
checking for the CLK_IS_CRITICAL flag after clk_hw_register() succeeds.

On resume, walk the list and re-enable any critical module clock that is
found to be disabled, before deasserting critical resets, ensuring the
correct clock-before-reset restore ordering.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
v4->v5:
 * No change
v4:
 * Moved this patch from [1] as it is boot-dependent
 [1] https://lore.kernel.org/all/20260306134228.871815-1-biju.das.jz@bp.renesas.com/
---
 drivers/clk/renesas/rzg2l-cpg.c | 41 +++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index 8165c163143a..c2d31b93f62b 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -130,6 +130,12 @@ struct div_hw_data {
 	u32 width;
 };
 
+/* Critical clk list  */
+struct rzg2l_crit_clk_hw {
+	struct clk_hw *hw;
+	struct rzg2l_crit_clk_hw *next;
+};
+
 #define to_div_hw_data(_hw)	container_of(_hw, struct div_hw_data, hw_data)
 
 struct rzg2l_pll5_param {
@@ -168,6 +174,7 @@ struct rzg2l_pll5_mux_dsi_div_param {
  * @info: Pointer to platform data
  * @genpd: PM domain
  * @mux_dsi_div_params: pll5 mux and dsi div parameters
+ * @crit_clk_hw_head: Head of the linked list critical clk entries
  */
 struct rzg2l_cpg_priv {
 	struct reset_controller_dev rcdev;
@@ -186,8 +193,26 @@ struct rzg2l_cpg_priv {
 	struct generic_pm_domain genpd;
 
 	struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
+
+	struct rzg2l_crit_clk_hw *crit_clk_hw_head;
 };
 
+static int rzg2l_cpg_add_crit_clk_hw_entry(struct rzg2l_cpg_priv *priv,
+					   struct clk_hw *hw)
+{
+	struct rzg2l_crit_clk_hw *node;
+
+	node = devm_kzalloc(priv->dev, sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	node->hw = hw;
+	node->next = priv->crit_clk_hw_head;
+	priv->crit_clk_hw_head = node;
+
+	return 0;
+}
+
 static inline u8 rzg2l_cpg_div_ab(u8 a, u8 b)
 {
 	return (b + 1) << a;
@@ -1737,6 +1762,13 @@ rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod,
 		goto fail;
 	}
 
+	if (init.flags & CLK_IS_CRITICAL) {
+		if (rzg2l_cpg_add_crit_clk_hw_entry(priv, &clock->hw)) {
+			clk = ERR_PTR(-ENOMEM);
+			goto fail;
+		}
+	}
+
 	clk = clock->hw.clk;
 	dev_dbg(dev, "Module clock %pC at %lu Hz\n", clk, clk_get_rate(clk));
 	priv->clks[id] = clk;
@@ -2086,8 +2118,17 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev)
 static int rzg2l_cpg_resume(struct device *dev)
 {
 	struct rzg2l_cpg_priv *priv = dev_get_drvdata(dev);
+	struct rzg2l_crit_clk_hw *node;
 	int ret;
 
+	for (node = priv->crit_clk_hw_head; node; node = node->next) {
+		if (!rzg2l_mod_clock_is_enabled(node->hw)) {
+			ret = rzg2l_mod_clock_endisable(node->hw, true);
+			if (ret)
+				return ret;
+		}
+	}
+
 	ret = rzg2l_cpg_deassert_crit_resets(&priv->rcdev, priv->info);
 	if (ret)
 		return ret;
-- 
2.43.0
Re: [PATCH v5 4/9] clk: renesas: rzg2l-cpg: Re-enable critical module clocks during resume
Posted by Geert Uytterhoeven 2 weeks, 5 days ago
Hi Biju,

On Wed, 18 Mar 2026 at 09:42, Biju <biju.das.au@gmail.com> wrote:
> From: Biju Das <biju.das.jz@bp.renesas.com>
>
> After a suspend/resume cycle, critical module clocks may be left disabled
> as the hardware state is not automatically restored. Unlike regular clocks
> which are re-enabled by their respective drivers, critical clocks
> (CLK_IS_CRITICAL) have no owning driver to restore them, so the CPG driver
> must take responsibility for re-enabling them on resume.
>
> Introduce struct rzg2l_crit_clk_hw to track critical module clock hardware
> entries in a singly-linked list anchored at crit_clk_hw_head in
> rzg2l_cpg_priv. Populate the list during module clock registration by
> checking for the CLK_IS_CRITICAL flag after clk_hw_register() succeeds.
>
> On resume, walk the list and re-enable any critical module clock that is
> found to be disabled, before deasserting critical resets, ensuring the
> correct clock-before-reset restore ordering.
>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Thanks for your patch!

> --- a/drivers/clk/renesas/rzg2l-cpg.c
> +++ b/drivers/clk/renesas/rzg2l-cpg.c
> @@ -130,6 +130,12 @@ struct div_hw_data {
>         u32 width;
>  };
>
> +/* Critical clk list  */
> +struct rzg2l_crit_clk_hw {
> +       struct clk_hw *hw;
> +       struct rzg2l_crit_clk_hw *next;
> +};
> +
>  #define to_div_hw_data(_hw)    container_of(_hw, struct div_hw_data, hw_data)
>
>  struct rzg2l_pll5_param {
> @@ -168,6 +174,7 @@ struct rzg2l_pll5_mux_dsi_div_param {
>   * @info: Pointer to platform data
>   * @genpd: PM domain
>   * @mux_dsi_div_params: pll5 mux and dsi div parameters
> + * @crit_clk_hw_head: Head of the linked list critical clk entries
>   */
>  struct rzg2l_cpg_priv {
>         struct reset_controller_dev rcdev;
> @@ -186,8 +193,26 @@ struct rzg2l_cpg_priv {
>         struct generic_pm_domain genpd;
>
>         struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
> +
> +       struct rzg2l_crit_clk_hw *crit_clk_hw_head;
>  };
>
> +static int rzg2l_cpg_add_crit_clk_hw_entry(struct rzg2l_cpg_priv *priv,
> +                                          struct clk_hw *hw)
> +{
> +       struct rzg2l_crit_clk_hw *node;
> +
> +       node = devm_kzalloc(priv->dev, sizeof(*node), GFP_KERNEL);

This ends up allocating quite some memory to store just a single
clk_hw pointer.   Alternatively, you could use an array and size,
and grow that using devm_krealloc().

Another alternative would be saving and restoring all clocks during
suspend/resume, like renesas-cpg-mssr.c does.
Thoughts?

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
Re: [PATCH v5 4/9] clk: renesas: rzg2l-cpg: Re-enable critical module clocks during resume
Posted by Geert Uytterhoeven 2 weeks, 5 days ago
Hi Biju,

On Wed, 18 Mar 2026 at 15:54, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> On Wed, 18 Mar 2026 at 09:42, Biju <biju.das.au@gmail.com> wrote:
> > From: Biju Das <biju.das.jz@bp.renesas.com>
> >
> > After a suspend/resume cycle, critical module clocks may be left disabled
> > as the hardware state is not automatically restored. Unlike regular clocks
> > which are re-enabled by their respective drivers, critical clocks
> > (CLK_IS_CRITICAL) have no owning driver to restore them, so the CPG driver
> > must take responsibility for re-enabling them on resume.
> >
> > Introduce struct rzg2l_crit_clk_hw to track critical module clock hardware
> > entries in a singly-linked list anchored at crit_clk_hw_head in
> > rzg2l_cpg_priv. Populate the list during module clock registration by
> > checking for the CLK_IS_CRITICAL flag after clk_hw_register() succeeds.
> >
> > On resume, walk the list and re-enable any critical module clock that is
> > found to be disabled, before deasserting critical resets, ensuring the
> > correct clock-before-reset restore ordering.
> >
> > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
>
> Thanks for your patch!
>
> > --- a/drivers/clk/renesas/rzg2l-cpg.c
> > +++ b/drivers/clk/renesas/rzg2l-cpg.c
> > @@ -130,6 +130,12 @@ struct div_hw_data {
> >         u32 width;
> >  };
> >
> > +/* Critical clk list  */
> > +struct rzg2l_crit_clk_hw {
> > +       struct clk_hw *hw;
> > +       struct rzg2l_crit_clk_hw *next;
> > +};
> > +
> >  #define to_div_hw_data(_hw)    container_of(_hw, struct div_hw_data, hw_data)
> >
> >  struct rzg2l_pll5_param {
> > @@ -168,6 +174,7 @@ struct rzg2l_pll5_mux_dsi_div_param {
> >   * @info: Pointer to platform data
> >   * @genpd: PM domain
> >   * @mux_dsi_div_params: pll5 mux and dsi div parameters
> > + * @crit_clk_hw_head: Head of the linked list critical clk entries
> >   */
> >  struct rzg2l_cpg_priv {
> >         struct reset_controller_dev rcdev;
> > @@ -186,8 +193,26 @@ struct rzg2l_cpg_priv {
> >         struct generic_pm_domain genpd;
> >
> >         struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
> > +
> > +       struct rzg2l_crit_clk_hw *crit_clk_hw_head;
> >  };
> >
> > +static int rzg2l_cpg_add_crit_clk_hw_entry(struct rzg2l_cpg_priv *priv,
> > +                                          struct clk_hw *hw)
> > +{
> > +       struct rzg2l_crit_clk_hw *node;
> > +
> > +       node = devm_kzalloc(priv->dev, sizeof(*node), GFP_KERNEL);
>
> This ends up allocating quite some memory to store just a single
> clk_hw pointer.   Alternatively, you could use an array and size,
> and grow that using devm_krealloc().

Upon second thought, you already know how many there are upfront,
thanks to rzg2l_cpg_info.num_crit_mod_clks? You even already have an
array (but it's __initconst).

> Another alternative would be saving and restoring all clocks during
> suspend/resume, like renesas-cpg-mssr.c does.

Another alternative: rzg2l_mod_clock_init_mstop() already iterates
over all module clocks during resume, so it could be modified to also
force-enable all critical module clocks.

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