]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: rsnd: adg: make rsnd_adg_clk_control() idempotent
authorJohn Madieu <john.madieu.xa@bp.renesas.com>
Wed, 10 Jun 2026 16:47:04 +0000 (16:47 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 1 Jul 2026 15:57:26 +0000 (16:57 +0100)
rsnd_adg_clk_control() is asymmetric on the disable path: the clkin
clocks are guarded by clkin_rate[], but the "adg" clock is disabled
unconditionally. If an enable attempt fails (for example a clkin
failing to turn on during resume), the error path correctly rolls
everything back, but rsnd_resume() ignores the return value, so the
following system suspend calls rsnd_adg_clk_disable() again and
underflows the "adg" clock enable count:

  adg_0_clks1 already disabled
  WARNING: drivers/clk/clk.c:1188 clk_core_disable+0xa4/0xac
  Call trace:
   clk_core_disable+0xa4/0xac (P)
   clk_disable+0x30/0x4c
   rsnd_adg_clk_control+0x9c/0x2cc
   rsnd_suspend+0x20/0x74
   device_suspend+0x140/0x3ec
   dpm_suspend+0x168/0x270

Track the enable state explicitly and bail out of redundant
enable/disable calls, mirroring what is already done for the per-SSI
clock prepare state. A failed enable leaves the state as disabled, so
the next suspend becomes a no-op and the next resume retries cleanly.

Fixes: 47899d53f86f ("ASoC: rsnd: adg: Add per-SSI ADG and SSIF supply clock management")
Signed-off-by: John Madieu <john.madieu.xa@bp.renesas.com>
Acked-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://patch.msgid.link/20260610164704.2211321-1-john.madieu.xa@bp.renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/renesas/rcar/adg.c

index 5479cefb6dbebe8e6cd280e0ba3332a97dcb42d3..53efd1be51392aca731056ceed7cdd64962a6dab 100644 (file)
@@ -45,6 +45,7 @@ struct rsnd_adg {
        struct rsnd_mod mod;
        int clkin_rate[CLKINMAX];
        bool ssi_clk_prepared;
+       bool clk_enabled;
        int clkin_size;
        int clkout_size;
        u32 ckr;
@@ -463,6 +464,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
        struct clk *clk;
        int ret = 0, i;
 
+       /*
+        * rsnd_adg_clk_enable() and rsnd_adg_clk_disable() can be called
+        * redundantly, for example when system suspend follows a resume
+        * whose enable failed. Make this function idempotent so that the
+        * "adg" clock, which has no clkin_rate[] style guard, is never
+        * disabled twice.
+        */
+       if (enable) {
+               if (adg->clk_enabled)
+                       return 0;
+       } else {
+               if (!adg->clk_enabled)
+                       return 0;
+               adg->clk_enabled = false;
+       }
+
        if (enable) {
                ret = clk_prepare_enable(adg->adg);
                if (ret < 0)
@@ -520,12 +537,22 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
         * rsnd_adg_clk_enable() might return error (_disable() will not).
         * We need to rollback in such case
         */
-       if (ret < 0)
+       if (ret < 0) {
+               /*
+                * Mark as enabled so that the rollback below is not
+                * short-circuited by the idempotency guard. It clears
+                * the flag again on its way through.
+                */
+               adg->clk_enabled = true;
                rsnd_adg_clk_disable(priv);
+               return ret;
+       }
 
        /* disable adg */
        if (!enable)
                clk_disable_unprepare(adg->adg);
+       else
+               adg->clk_enabled = true;
 
        return ret;
 }