]> www.infradead.org Git - users/hch/misc.git/commitdiff
ASoC: rsnd: check rsnd_adg_clk_enable() return value
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 9 Jan 2025 00:40:05 +0000 (00:40 +0000)
committerMark Brown <broonie@kernel.org>
Thu, 9 Jan 2025 13:17:02 +0000 (13:17 +0000)
rsnd_adg_clk_enable() might be failed for some reasons, but it doesn't
check return value for now. In such case, we might get below WARNING from
clk_disable() during probe or suspend. Check rsnd_adg_clk_enable() return
value.

    clk_multiplier already disabled
    ...
    Call trace:
     clk_core_disable+0xd0/0xd8 (P)
     clk_disable+0x2c/0x44
     rsnd_adg_clk_control+0x80/0xf4

According to Geert, it happened only 7 times during the last 2 years.
So I have reproduced the issue and created patch by Intentionally making
an error.

Link: https://lore.kernel.org/r/CAMuHMdVUKpO2rsia+36BLFFwdMapE8LrYS0duyd0FmrxDvwEfg@mail.gmail.com
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://patch.msgid.link/87seps2522.wl-kuninori.morimoto.gx@renesas.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/renesas/rcar/adg.c
sound/soc/renesas/rcar/core.c
sound/soc/renesas/rcar/rsnd.h

index 0f190abf00e756cae97cb29ecbf7adb2f02c9e5a..191f212d338c22ce02214a92e26fac6e45260c3e 100644 (file)
@@ -374,12 +374,12 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
        return 0;
 }
 
-void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
+int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
 {
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct clk *clk;
-       int i;
+       int ret = 0, i;
 
        if (enable) {
                rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
@@ -389,18 +389,33 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
 
        for_each_rsnd_clkin(clk, adg, i) {
                if (enable) {
-                       clk_prepare_enable(clk);
+                       ret = clk_prepare_enable(clk);
 
                        /*
                         * We shouldn't use clk_get_rate() under
                         * atomic context. Let's keep it when
                         * rsnd_adg_clk_enable() was called
                         */
+                       if (ret < 0)
+                               break;
+
                        adg->clkin_rate[i] = clk_get_rate(clk);
                } else {
-                       clk_disable_unprepare(clk);
+                       if (adg->clkin_rate[i])
+                               clk_disable_unprepare(clk);
+
+                       adg->clkin_rate[i] = 0;
                }
        }
+
+       /*
+        * rsnd_adg_clk_enable() might return error (_disable() will not).
+        * We need to rollback in such case
+        */
+       if (ret < 0)
+               rsnd_adg_clk_disable(priv);
+
+       return ret;
 }
 
 static struct clk *rsnd_adg_create_null_clk(struct rsnd_priv *priv,
@@ -753,7 +768,10 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
        if (ret)
                return ret;
 
-       rsnd_adg_clk_enable(priv);
+       ret = rsnd_adg_clk_enable(priv);
+       if (ret)
+               return ret;
+
        rsnd_adg_clk_dbg_info(priv, NULL);
 
        return 0;
index e2234928c9e881d3ea2af3f496e70a49db0e0880..d3709fd0409e43a2266cbc306db0812435e4c4bc 100644 (file)
@@ -2086,9 +2086,7 @@ static int __maybe_unused rsnd_resume(struct device *dev)
 {
        struct rsnd_priv *priv = dev_get_drvdata(dev);
 
-       rsnd_adg_clk_enable(priv);
-
-       return 0;
+       return rsnd_adg_clk_enable(priv);
 }
 
 static const struct dev_pm_ops rsnd_pm_ops = {
index 3c164d8e3b16bf19d140b4db6da2b115468e132e..a5f54b65313c401e5491229f38ec464efe3eece4 100644 (file)
@@ -608,7 +608,7 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
                                 struct rsnd_dai_stream *io);
 #define rsnd_adg_clk_enable(priv)      rsnd_adg_clk_control(priv, 1)
 #define rsnd_adg_clk_disable(priv)     rsnd_adg_clk_control(priv, 0)
-void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
+int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable);
 void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m);
 
 /*