]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mfd: cs42l43: Disable IRQs during suspend
authorCharles Keepax <ckeepax@opensource.cirrus.com>
Mon, 14 Oct 2024 09:52:02 +0000 (10:52 +0100)
committerLee Jones <lee@kernel.org>
Thu, 31 Oct 2024 16:17:09 +0000 (16:17 +0000)
The ASoC CODEC driver masks the IRQs whilst entering and exiting
system suspend to avoid issues where the IRQ handler can run but PM
runtime is disabled. However, as the IRQs could also be used from
other parts of the driver, it would be better to move this handling to
the MFD level.

Remove the handling from the ASoC driver and move it to the MFD
driver. Whilst moving also ensure the IRQs are all masked at the device
level before powering down the device, as per hardware recommendations.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Acked-by: Mark Brown <broonie@kernel.org.>
Link: https://lore.kernel.org/r/20241014095202.828194-1-ckeepax@opensource.cirrus.com
Signed-off-by: Lee Jones <lee@kernel.org>
drivers/mfd/cs42l43.c
sound/soc/codecs/cs42l43.c

index 3b4efb29447136d5b6d6463b1cf28b9e92158777..e5f17fc430e4879b52bf4758af3313ee66614692 100644 (file)
@@ -1109,16 +1109,39 @@ EXPORT_SYMBOL_NS_GPL(cs42l43_dev_remove, MFD_CS42L43);
 static int cs42l43_suspend(struct device *dev)
 {
        struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+       static const struct reg_sequence mask_all[] = {
+               { CS42L43_DECIM_MASK,                   0xFFFFFFFF, },
+               { CS42L43_EQ_MIX_MASK,                  0xFFFFFFFF, },
+               { CS42L43_ASP_MASK,                     0xFFFFFFFF, },
+               { CS42L43_PLL_MASK,                     0xFFFFFFFF, },
+               { CS42L43_SOFT_MASK,                    0xFFFFFFFF, },
+               { CS42L43_SWIRE_MASK,                   0xFFFFFFFF, },
+               { CS42L43_MSM_MASK,                     0xFFFFFFFF, },
+               { CS42L43_ACC_DET_MASK,                 0xFFFFFFFF, },
+               { CS42L43_I2C_TGT_MASK,                 0xFFFFFFFF, },
+               { CS42L43_SPI_MSTR_MASK,                0xFFFFFFFF, },
+               { CS42L43_SW_TO_SPI_BRIDGE_MASK,        0xFFFFFFFF, },
+               { CS42L43_OTP_MASK,                     0xFFFFFFFF, },
+               { CS42L43_CLASS_D_AMP_MASK,             0xFFFFFFFF, },
+               { CS42L43_GPIO_INT_MASK,                0xFFFFFFFF, },
+               { CS42L43_ASRC_MASK,                    0xFFFFFFFF, },
+               { CS42L43_HPOUT_MASK,                   0xFFFFFFFF, },
+       };
        int ret;
 
-       /*
-        * Don't care about being resumed here, but the driver does want
-        * force_resume to always trigger an actual resume, so that register
-        * state for the MCU/GPIOs is returned as soon as possible after system
-        * resume. force_resume will resume if the reference count is resumed on
-        * suspend hence the get_noresume.
-        */
-       pm_runtime_get_noresume(dev);
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret) {
+               dev_err(cs42l43->dev, "Failed to resume for suspend: %d\n", ret);
+               return ret;
+       }
+
+       /* The IRQs will be re-enabled on resume by the cache sync */
+       ret = regmap_multi_reg_write_bypassed(cs42l43->regmap,
+                                             mask_all, ARRAY_SIZE(mask_all));
+       if (ret) {
+               dev_err(cs42l43->dev, "Failed to mask IRQs: %d\n", ret);
+               return ret;
+       }
 
        ret = pm_runtime_force_suspend(dev);
        if (ret) {
@@ -1133,6 +1156,26 @@ static int cs42l43_suspend(struct device *dev)
        if (ret)
                return ret;
 
+       disable_irq(cs42l43->irq);
+
+       return 0;
+}
+
+static int cs42l43_suspend_noirq(struct device *dev)
+{
+       struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+       enable_irq(cs42l43->irq);
+
+       return 0;
+}
+
+static int cs42l43_resume_noirq(struct device *dev)
+{
+       struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
+
+       disable_irq(cs42l43->irq);
+
        return 0;
 }
 
@@ -1145,6 +1188,8 @@ static int cs42l43_resume(struct device *dev)
        if (ret)
                return ret;
 
+       enable_irq(cs42l43->irq);
+
        ret = pm_runtime_force_resume(dev);
        if (ret) {
                dev_err(cs42l43->dev, "Failed to force resume: %d\n", ret);
@@ -1212,6 +1257,7 @@ err:
 
 EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = {
        SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume)
+       NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend_noirq, cs42l43_resume_noirq)
        RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL)
 };
 
index d0098b4558b5298a497a6cf8ded78c857c551d80..3ede0e3110f3471b4c28d349af35347aa5ac0270 100644 (file)
@@ -2402,49 +2402,7 @@ static int cs42l43_codec_runtime_resume(struct device *dev)
        return 0;
 }
 
-static int cs42l43_codec_suspend(struct device *dev)
-{
-       struct cs42l43_codec *priv = dev_get_drvdata(dev);
-       struct cs42l43 *cs42l43 = priv->core;
-
-       disable_irq(cs42l43->irq);
-
-       return 0;
-}
-
-static int cs42l43_codec_suspend_noirq(struct device *dev)
-{
-       struct cs42l43_codec *priv = dev_get_drvdata(dev);
-       struct cs42l43 *cs42l43 = priv->core;
-
-       enable_irq(cs42l43->irq);
-
-       return 0;
-}
-
-static int cs42l43_codec_resume(struct device *dev)
-{
-       struct cs42l43_codec *priv = dev_get_drvdata(dev);
-       struct cs42l43 *cs42l43 = priv->core;
-
-       enable_irq(cs42l43->irq);
-
-       return 0;
-}
-
-static int cs42l43_codec_resume_noirq(struct device *dev)
-{
-       struct cs42l43_codec *priv = dev_get_drvdata(dev);
-       struct cs42l43 *cs42l43 = priv->core;
-
-       disable_irq(cs42l43->irq);
-
-       return 0;
-}
-
 static const struct dev_pm_ops cs42l43_codec_pm_ops = {
-       SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
-       NOIRQ_SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend_noirq, cs42l43_codec_resume_noirq)
        RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
 };