When the SAI peripheral is left in a running state by the bootloader,
the driver can experience an interrupt storm during probe that prevents
successful initialization. This occurs because the current code registers
the IRQ handler before resetting the hardware to a known state.

The issue manifests as:
- Continuous interrupts firing immediately after devm_request_irq()
- Driver probe failure or system hang
- Error messages about unhandled interrupts

This is particularly problematic on systems where U-Boot or other
bootloaders enable SAI for boot-time audio feedback or diagnostics
and don't properly disable it before handing control to Linux.

Fix this by reordering the probe sequence:
1. Add fsl_sai_reset_hw() to clear TCSR/RCSR control registers,
   which disables the transmitter/receiver and all interrupt sources
2. Move devm_request_irq() to after hardware initialization

This ensures the SAI is in a clean reset state before the interrupt
handler can be invoked, preventing the storm while maintaining proper
error handling and cleanup paths.

Signed-off-by: Shengjiu Wang <[email protected]>
---
 sound/soc/fsl/fsl_sai.c | 43 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 87a40e2b9fdf..d6dd95680892 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -1374,6 +1374,31 @@ static int fsl_sai_check_version(struct device *dev)
        return 0;
 }
 
+static int fsl_sai_reset_hw(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+       unsigned char ofs = sai->soc_data->reg_offset;
+       int ret;
+
+       /*
+        * Clear TCSR/RCSR to reset SAI and disable all interrupts.
+        * Bootloader may leave SAI running causing interrupt storm.
+        */
+       ret = regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0);
+       if (ret) {
+               dev_err(dev, "Failed to clear TCSR: %d\n", ret);
+               return ret;
+       }
+
+       ret = regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0);
+       if (ret) {
+               dev_err(dev, "Failed to clear RCSR: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /*
  * Calculate the offset between first two datalines, don't
  * different offset in one case.
@@ -1580,13 +1605,6 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
-                              np->name, sai);
-       if (ret) {
-               dev_err(dev, "failed to claim irq %u\n", irq);
-               return ret;
-       }
-
        memcpy(&sai->cpu_dai_drv, fsl_sai_dai_template,
               sizeof(*fsl_sai_dai_template) * 
ARRAY_SIZE(fsl_sai_dai_template));
 
@@ -1661,6 +1679,10 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (ret < 0)
                dev_warn(dev, "Error reading SAI version: %d\n", ret);
 
+       ret = fsl_sai_reset_hw(dev);
+       if (ret < 0)
+               dev_warn(dev, "Failed to reset hardware: %d\n", ret);
+
        /* Select MCLK direction */
        if (sai->mclk_direction_output &&
            sai->soc_data->max_register >= FSL_SAI_MCTL) {
@@ -1672,6 +1694,13 @@ static int fsl_sai_probe(struct platform_device *pdev)
        if (ret < 0 && ret != -ENOSYS)
                goto err_pm_get_sync;
 
+       ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
+                              np->name, sai);
+       if (ret) {
+               dev_err(dev, "failed to claim irq %u\n", irq);
+               goto err_pm_get_sync;
+       }
+
        if (of_device_is_compatible(np, "fsl,imx952-sai") &&
            !of_property_read_string(np, "fsl,sai-amix-mode", &str)) {
                if (!strcmp(str, "bypass"))
-- 
2.34.1


Reply via email to