mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
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:
parent
95edf2dbb4
commit
ed0abc8be2
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user