Unverified Commit 139fa599 authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown
Browse files

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: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Link: https://patch.msgid.link/87seps2522.wl-kuninori.morimoto.gx@renesas.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 8f0defd2
Loading
Loading
Loading
Loading
+23 −5
Original line number Diff line number Diff line
@@ -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 {
			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;
+1 −3
Original line number Diff line number Diff line
@@ -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 = {
+1 −1
Original line number Diff line number Diff line
@@ -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);

/*