From: Stefan Binding <sbinding@opensource.cirrus.com>
To: Mark Brown <broonie@kernel.org>, Jaroslav Kysela <perex@perex.cz>,
Takashi Iwai <tiwai@suse.com>
Cc: linux-sound@vger.kernel.org, linux-kernel@vger.kernel.org,
patches@opensource.cirrus.com,
Stefan Binding <sbinding@opensource.cirrus.com>
Subject: [PATCH v1 1/2] ALSA: hda: cs35l41: Add common wait_for_boot api and reset_and_boot api
Date: Fri, 30 Oct 2026 17:10:14 +0000 [thread overview]
Message-ID: <20261030171045.469944-2-sbinding@opensource.cirrus.com> (raw)
In-Reply-To: <20261030171045.469944-1-sbinding@opensource.cirrus.com>
Both the ASoC CS35L41 and HDA CS35L41 drivers currently use
regmap_read_poll_timeout() calls to wait for the chip to boot.
However, this macro will exit immediately if one of the regmap_read()
calls fail. This could be cause by the hardware not being ready yet
after a reset, which then breaks the loop early and leads to probe
and resume calls failing.
This patch replaces the existing code in the HDA driver with a
function with a loop which does not break on regmap_read() failure.
Since both drivers need to do this, make a common function in
cs35l41-lib.c to be called from both drivers.
This patch only updates the HDA driver, a separate patch will update
the ASoC driver.
In addition, before the call to wait to boot, the HDA driver also
does a software reset to ensure the device is in a known state before
probe or resume (for example, if there is no reset gpio). Since this
write could fail for the same reason, add a common api to do this
with a retry mechanism.
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
---
include/sound/cs35l41.h | 2 +
sound/hda/codecs/side-codecs/cs35l41_hda.c | 33 +----------
sound/soc/codecs/cs35l41-lib.c | 67 ++++++++++++++++++++++
3 files changed, 71 insertions(+), 31 deletions(-)
diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h
index 7542cabfa726..b6b338c55ff0 100644
--- a/include/sound/cs35l41.h
+++ b/include/sound/cs35l41.h
@@ -924,5 +924,7 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type);
int cs35l41_mdsync_up(struct regmap *regmap);
int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type,
int enable, struct cs_dsp *dsp);
+int cs35l41_wait_boot_done(struct device *dev, struct regmap *regmap);
+int cs35l41_reset_and_boot(struct device *dev, struct regmap *regmap);
#endif /* __CS35L41_H */
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
index f879e3ffbb53..23582f23a880 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -974,30 +974,6 @@ static int cs35l41_system_suspend(struct device *dev)
return ret;
}
-static int cs35l41_wait_boot_done(struct cs35l41_hda *cs35l41)
-{
- unsigned int int_status;
- int ret;
-
- ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status,
- int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000);
- if (ret) {
- dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE\n");
- return ret;
- }
-
- ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
- if (ret || (int_status & CS35L41_OTP_BOOT_ERR)) {
- dev_err(cs35l41->dev, "OTP Boot status %x error\n",
- int_status & CS35L41_OTP_BOOT_ERR);
- if (!ret)
- ret = -EIO;
- return ret;
- }
-
- return 0;
-}
-
static int cs35l41_system_resume(struct device *dev)
{
struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
@@ -1020,10 +996,7 @@ static int cs35l41_system_resume(struct device *dev)
regcache_cache_only(cs35l41->regmap, false);
- regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
- usleep_range(2000, 2100);
-
- ret = cs35l41_wait_boot_done(cs35l41);
+ ret = cs35l41_reset_and_boot(cs35l41->dev, cs35l41->regmap);
if (ret)
return ret;
@@ -2003,11 +1976,9 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i
gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
}
- usleep_range(2000, 2100);
- regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
usleep_range(2000, 2100);
- ret = cs35l41_wait_boot_done(cs35l41);
+ ret = cs35l41_reset_and_boot(cs35l41->dev, cs35l41->regmap);
if (ret)
goto err;
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index b0fa80705be7..973713626ebd 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -17,6 +17,8 @@
#include <sound/cs35l41.h>
#define CS35L41_FIRMWARE_OLD_VERSION 0x001C00 /* v0.28.0 */
+#define CS35L41_GLOBAL_BOOT_DONE_TIMEOUT_US 100000
+#define CS35L41_GLOBAL_BOOT_DONE_SLEEP_US 2000
static const struct reg_default cs35l41_reg[] = {
{ CS35L41_PWR_CTRL1, 0x00000000 },
@@ -1586,6 +1588,71 @@ int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap)
}
EXPORT_SYMBOL_GPL(cs35l41_exit_hibernate);
+int cs35l41_wait_boot_done(struct device *dev, struct regmap *regmap)
+{
+ ktime_t timeout;
+ unsigned int val = 0;
+ int ret = 0;
+
+ /*
+ * The chip could NAK a read request while it is booting so ignore
+ * errors from regmap_read.
+ */
+ timeout = ktime_add_us(ktime_get(), CS35L41_GLOBAL_BOOT_DONE_TIMEOUT_US);
+ regmap_read(regmap, CS35L41_IRQ1_STATUS4, &val);
+ while (!(val & CS35L41_OTP_BOOT_DONE) && !ktime_after(ktime_get(), timeout)) {
+ usleep_range(CS35L41_GLOBAL_BOOT_DONE_SLEEP_US / 2,
+ CS35L41_GLOBAL_BOOT_DONE_SLEEP_US);
+ regmap_read(regmap, CS35L41_IRQ1_STATUS4, &val);
+ }
+
+ if (!(val & CS35L41_OTP_BOOT_DONE)) {
+ dev_err(dev, "Timeout waiting for OTP_BOOT_DONE\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_read(regmap, CS35L41_IRQ1_STATUS3, &val);
+ if (ret || (val & CS35L41_OTP_BOOT_ERR)) {
+ dev_err(dev, "OTP Boot status %x error\n",
+ val & CS35L41_OTP_BOOT_ERR);
+ if (!ret)
+ ret = -EIO;
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l41_wait_boot_done);
+
+int cs35l41_reset_and_boot(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+
+ ret = regmap_write(regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ if (ret) {
+ dev_warn(dev, "Failed to write software reset: %d\n", ret);
+
+ /*
+ * If the write failed, it is likely because the chip is still booting after a
+ * reset. Wait for boot, then retry the software reset. If the initial write failed
+ * for another reason, then either waiting for boot, or the second write will fail,
+ * So we can return the error then.
+ */
+ ret = cs35l41_wait_boot_done(dev, regmap);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ if (ret) {
+ dev_err(dev, "Failed to write software reset on retry: %d\n", ret);
+ return ret;
+ }
+ }
+ usleep_range(2000, 2100);
+
+ return cs35l41_wait_boot_done(dev, regmap);
+}
+EXPORT_SYMBOL_GPL(cs35l41_reset_and_boot);
+
MODULE_DESCRIPTION("CS35L41 library");
MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
--
2.43.0
next prev parent reply other threads:[~2026-10-30 17:11 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 17:10 [PATCH v1 0/2] Add common wait_for_boot and reset_and_boot apis for cs35l41 drivers Stefan Binding
2026-10-30 17:10 ` Stefan Binding [this message]
2026-10-30 17:26 ` [PATCH v1 1/2] ALSA: hda: cs35l41: Add common wait_for_boot api and reset_and_boot api sashiko-bot
2026-10-30 17:10 ` [PATCH v1 2/2] ASoC: cs35l41: Use common wait_for_boot and reset_and_boot apis Stefan Binding
2026-10-30 17:20 ` Mark Brown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030171045.469944-2-sbinding@opensource.cirrus.com \
--to=sbinding@opensource.cirrus.com \
--cc=broonie@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=patches@opensource.cirrus.com \
--cc=perex@perex.cz \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.