When system try to do the suspend:
T1:suspend_thread T2: interrupt thread handler
enter_state() arizona_irq_thread()
suspend_devices_and_enter() regmap_read()
__device_suspend()
regmap_spi_read()
spi_write_then_read()
spi_sync()
__spi_sync()
wait_for_completion()
T2 <== Blocked here due to spi suspended
suspend_enter()
dpm_suspend_end()
dpm_suspend_noirq()
suspend_device_irqs()
synchronize_irq()
T1 <== Blocked here due waiting for T2 finished
Then T1 and T2 is deadlocked there.
Here the arizona irq is not NOSUSPEND irq, so when doing device suspend,
we can disable the arizona irq, and enable it until devices resuming finished.
Signed-off-by: liu chuansheng <[email protected]>
---
drivers/mfd/arizona-core.c | 11 ++++-------
1 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index b562c7b..b11bd01 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -264,11 +264,11 @@ static int arizona_runtime_suspend(struct device *dev)
#endif
#ifdef CONFIG_PM_SLEEP
-static int arizona_resume_noirq(struct device *dev)
+static int arizona_suspend(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
- dev_dbg(arizona->dev, "Early resume, disabling IRQ\n");
+ dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
disable_irq(arizona->irq);
return 0;
@@ -278,7 +278,7 @@ static int arizona_resume(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
- dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n");
+ dev_dbg(arizona->dev, "Resume, reenabling IRQ\n");
enable_irq(arizona->irq);
return 0;
@@ -289,10 +289,7 @@ const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
- SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
-#ifdef CONFIG_PM_SLEEP
- .resume_noirq = arizona_resume_noirq,
-#endif
+ SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
};
EXPORT_SYMBOL_GPL(arizona_pm_ops);
--
1.7.0.4
On Wed, Apr 10, 2013 at 08:39:09PM +0800, Chuansheng Liu wrote:
> Here the arizona irq is not NOSUSPEND irq, so when doing device suspend,
> we can disable the arizona irq, and enable it until devices resuming finished.
Hrm, well - actually the primary IRQ probably ought to be a nosuspend in
the first place so this probably isn't what we want. Something like the
below which does a similar thing to what we do on resume might help
here... needs testing though.
From 538e817db94dc0c689ecfea7151b1a3f86bb204a Mon Sep 17 00:00:00 2001
From: Mark Brown <[email protected]>
Date: Wed, 10 Apr 2013 12:40:26 +0100
Subject: [PATCH] mfd: arizona: Disable interrupts during suspend
We aren't able to handle interrupts after the device has suspended since
we need to runtime resume it in order to do so but the controller may not
be available any more. Handle this in the same way as we handle a similar
issue on resume.
Reported-by: Chuansheng Liu <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/mfd/arizona-core.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 98023d8..c6be1f6 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -399,6 +399,26 @@ static int arizona_runtime_suspend(struct device *dev)
#endif
#ifdef CONFIG_PM_SLEEP
+static int arizona_suspend(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
+ disable_irq(arizona->irq);
+
+ return 0;
+}
+
+static int arizona_suspend_late(struct device *dev)
+{
+ struct arizona *arizona = dev_get_drvdata(dev);
+
+ dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
+ enable_irq(arizona->irq);
+
+ return 0;
+}
+
static int arizona_resume_noirq(struct device *dev)
{
struct arizona *arizona = dev_get_drvdata(dev);
@@ -424,8 +444,9 @@ const struct dev_pm_ops arizona_pm_ops = {
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
arizona_runtime_resume,
NULL)
- SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
#ifdef CONFIG_PM_SLEEP
+ .suspend_late = arizona_suspend_late,
.resume_noirq = arizona_resume_noirq,
#endif
};
--
1.7.10.4
Hi Mark,
> -----Original Message-----
> From: Mark Brown [mailto:[email protected]]
> Sent: Wednesday, April 10, 2013 8:30 PM
> To: Liu, Chuansheng
> Cc: [email protected]; [email protected];
> [email protected]
> Subject: Re: mfd, arizona: Fix the deadlock between interrupt handler and
> dpm_suspend
>
> On Wed, Apr 10, 2013 at 08:39:09PM +0800, Chuansheng Liu wrote:
>
> > Here the arizona irq is not NOSUSPEND irq, so when doing device suspend,
> > we can disable the arizona irq, and enable it until devices resuming finished.
>
> Hrm, well - actually the primary IRQ probably ought to be a nosuspend in
Could we set the irq as NOSUSPEND directly?
> the first place so this probably isn't what we want. Something like the
> below which does a similar thing to what we do on resume might help
> here... needs testing though.
We will take your patch to do the test, thanks.
>
> From 538e817db94dc0c689ecfea7151b1a3f86bb204a Mon Sep 17 00:00:00
> 2001
> From: Mark Brown <[email protected]>
> Date: Wed, 10 Apr 2013 12:40:26 +0100
> Subject: [PATCH] mfd: arizona: Disable interrupts during suspend
>
> We aren't able to handle interrupts after the device has suspended since
> we need to runtime resume it in order to do so but the controller may not
> be available any more. Handle this in the same way as we handle a similar
> issue on resume.
>
> Reported-by: Chuansheng Liu <[email protected]>
> Signed-off-by: Mark Brown <[email protected]>
> ---
> drivers/mfd/arizona-core.c | 23 ++++++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
> index 98023d8..c6be1f6 100644
> --- a/drivers/mfd/arizona-core.c
> +++ b/drivers/mfd/arizona-core.c
> @@ -399,6 +399,26 @@ static int arizona_runtime_suspend(struct device
> *dev)
> #endif
>
> #ifdef CONFIG_PM_SLEEP
> +static int arizona_suspend(struct device *dev)
> +{
> + struct arizona *arizona = dev_get_drvdata(dev);
> +
> + dev_dbg(arizona->dev, "Suspend, disabling IRQ\n");
> + disable_irq(arizona->irq);
> +
> + return 0;
> +}
> +
> +static int arizona_suspend_late(struct device *dev)
> +{
> + struct arizona *arizona = dev_get_drvdata(dev);
> +
> + dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
> + enable_irq(arizona->irq);
Here, after later suspending, is it possible the irq coming again?
and one more question, why the irq is needed to be enabled even after suspended?
> +
> + return 0;
> +}
> +
> static int arizona_resume_noirq(struct device *dev)
> {
> struct arizona *arizona = dev_get_drvdata(dev);
> @@ -424,8 +444,9 @@ const struct dev_pm_ops arizona_pm_ops = {
> SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
> arizona_runtime_resume,
> NULL)
> - SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume)
> + SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume)
> #ifdef CONFIG_PM_SLEEP
> + .suspend_late = arizona_suspend_late,
> .resume_noirq = arizona_resume_noirq,
> #endif
> };
> --
> 1.7.10.4
On Wed, Apr 10, 2013 at 12:46:55PM +0000, Liu, Chuansheng wrote:
> Hi Mark,
> > > Here the arizona irq is not NOSUSPEND irq, so when doing device suspend,
> > > we can disable the arizona irq, and enable it until devices resuming finished.
> > Hrm, well - actually the primary IRQ probably ought to be a nosuspend in
> Could we set the irq as NOSUSPEND directly?
Worth checking, yes.
> > +static int arizona_suspend_late(struct device *dev)
> > +{
> > + struct arizona *arizona = dev_get_drvdata(dev);
> > +
> > + dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n");
> > + enable_irq(arizona->irq);
> Here, after later suspending, is it possible the irq coming again?
No, by the time late suspend happens all interrupts ought to be off.
> and one more question, why the irq is needed to be enabled even after suspended?
We want interrupts to function as wake sources and want to minimise the
risk of confusing things - the enable/disable ought to nest with what
the core is doing.