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>
This commit is contained in:
Kuninori Morimoto 2025-01-09 00:40:05 +00:00 committed by Mark Brown
parent 8f0defd2e5
commit 139fa599ce
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 25 additions and 9 deletions

View 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;

View 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 = {

View 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);
/*