ASoC: rsnd: adg: make rsnd_adg_clk_control() idempotent

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: 47899d53f8 ("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>
This commit is contained in:
John Madieu 2026-06-10 16:47:04 +00:00 committed by Mark Brown
parent 95edf2dbb4
commit ed0abc8be2
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

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