diff options
| author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2025-01-09 00:40:05 +0000 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2025-01-09 13:17:02 +0000 | 
| commit | 139fa599cea0fd9d38e00246ea9f79af6c59acbd (patch) | |
| tree | 5e7299ee4b9de8a6cbfb71b2b9acba206e841e00 | |
| parent | 8f0defd2e52d22eb994d7e770b1261caa24917d8 (diff) | |
ASoC: rsnd: check rsnd_adg_clk_enable() return value
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>
| -rw-r--r-- | sound/soc/renesas/rcar/adg.c | 28 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/core.c | 4 | ||||
| -rw-r--r-- | sound/soc/renesas/rcar/rsnd.h | 2 | 
3 files changed, 25 insertions, 9 deletions
| diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c index 0f190abf00e7..191f212d338c 100644 --- a/sound/soc/renesas/rcar/adg.c +++ b/sound/soc/renesas/rcar/adg.c @@ -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; diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index e2234928c9e8..d3709fd0409e 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -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 = { diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index 3c164d8e3b16..a5f54b65313c 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -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);  /* | 
