[PATCH] ASoC: ak4458: Check reset status after deassert

From: shengjiu . wang

Date: Sat Oct 10 2026 - 04:41:34 EST


From: Shengjiu Wang <shengjiu.wang@xxxxxxx>

The reset line of the ak4458 can be shared, for example when two ak4458
devices use the same reset GPIO. In that case the two codecs may call
reset_control_deassert() concurrently from their runtime resume paths.

Only the first caller performs the actual hardware deassert, which may
sleep (for example a GPIO-expander based reset controller). A second
caller merely increments the shared deassert count and returns
immediately, so it can continue while the first caller's .deassert() has
not finished releasing the line yet, leaving the device still in reset.

Poll reset_control_status() after deassert so a caller does not continue
until the reset is really released. reset_control_status() returns a
positive value while the line is still asserted, zero once it is
deasserted, and a negative errno when the reset controller does not
implement status reporting. Stop polling on status <= 0 so controllers
without a .status callback do not spin for the full timeout, and check
the reset_control_deassert() return value as well.

Fixes: 8a0de73cf9dc ("ASoC: ak4458: add optional reset control to instead of gpio")
Signed-off-by: Shengjiu Wang <shengjiu.wang@xxxxxxx>
---
sound/soc/codecs/ak4458.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
index a74be99e877d..64984974ff88 100644
--- a/sound/soc/codecs/ak4458.c
+++ b/sound/soc/codecs/ak4458.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
@@ -635,11 +636,36 @@ static struct snd_soc_dai_driver ak4497_dai = {

static void ak4458_reset(struct ak4458_priv *ak4458, bool active)
{
+ int ret, status;
+
if (!IS_ERR_OR_NULL(ak4458->reset)) {
- if (active)
+ if (active) {
reset_control_assert(ak4458->reset);
- else
- reset_control_deassert(ak4458->reset);
+ } else {
+ ret = reset_control_deassert(ak4458->reset);
+ if (ret) {
+ dev_warn(ak4458->dev,
+ "failed to deassert reset: %d\n", ret);
+ } else {
+ /*
+ * On a shared reset line another consumer may
+ * still be in its hardware .deassert(), which can
+ * sleep, so the line may not be released yet.
+ * Poll the status until it reads deasserted (0).
+ * A negative return means the controller does not
+ * report status, so stop polling in that case.
+ */
+ ret = read_poll_timeout(reset_control_status, status,
+ status <= 0, 100, 20000,
+ false, ak4458->reset);
+ if (ret)
+ dev_warn(ak4458->dev,
+ "reset still asserted after deassert\n");
+ else if (status < 0)
+ dev_dbg(ak4458->dev,
+ "reset status not supported: %d\n", status);
+ }
+ }
usleep_range(1000, 2000);
}
}
--
2.34.1