summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/sleep.c47
-rw-r--r--drivers/base/power/main.c5
-rw-r--r--include/linux/suspend.h1
-rw-r--r--kernel/power/suspend.c53
4 files changed, 48 insertions, 58 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 3debe1a42655..970ae7c7a3f7 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -986,33 +986,37 @@ static void acpi_s2idle_wake(void)
lpi_check_constraints();
/*
- * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
- * that the SCI has triggered while suspended, so cancel the wakeup in
- * case it has not been a wakeup event (the GPEs will be checked later).
+ * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has
+ * not triggered while suspended, so bail out.
*/
- if (acpi_sci_irq_valid() &&
- !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
+ if (!acpi_sci_irq_valid() ||
+ irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
+ return;
+
+ /*
+ * If there are EC events to process, the wakeup may be a spurious one
+ * coming from the EC.
+ */
+ if (acpi_ec_dispatch_gpe()) {
+ /*
+ * Cancel the wakeup and process all pending events in case
+ * there are any wakeup ones in there.
+ *
+ * Note that if any non-EC GPEs are active at this point, the
+ * SCI will retrigger after the rearming below, so no events
+ * should be missed by canceling the wakeup here.
+ */
pm_system_cancel_wakeup();
/*
- * On some platforms with the LPS0 _DSM device noirq resume
- * takes too much time for EC wakeup events to survive, so look
- * for them now.
+ * The EC driver uses the system workqueue and an additional
+ * special one, so those need to be flushed too.
*/
- acpi_ec_dispatch_gpe();
+ acpi_os_wait_events_complete(); /* synchronize EC GPE processing */
+ acpi_ec_flush_work();
+ acpi_os_wait_events_complete(); /* synchronize Notify handling */
}
-}
-static void acpi_s2idle_sync(void)
-{
- /*
- * Process all pending events in case there are any wakeup ones.
- *
- * The EC driver uses the system workqueue and an additional special
- * one, so those need to be flushed too.
- */
- acpi_os_wait_events_complete(); /* synchronize SCI IRQ handling */
- acpi_ec_flush_work();
- acpi_os_wait_events_complete(); /* synchronize Notify handling */
+ rearm_wake_irq(acpi_sci_irq);
}
static void acpi_s2idle_restore(void)
@@ -1044,7 +1048,6 @@ static const struct platform_s2idle_ops acpi_s2idle_ops = {
.begin = acpi_s2idle_begin,
.prepare = acpi_s2idle_prepare,
.wake = acpi_s2idle_wake,
- .sync = acpi_s2idle_sync,
.restore = acpi_s2idle_restore,
.end = acpi_s2idle_end,
};
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 7fb2c39bc725..f08332fab531 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1291,11 +1291,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (async_error)
goto Complete;
- if (pm_wakeup_pending()) {
- async_error = -EBUSY;
- goto Complete;
- }
-
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 9c0ad1a3a727..66ce3871ed61 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -191,7 +191,6 @@ struct platform_s2idle_ops {
int (*begin)(void);
int (*prepare)(void);
void (*wake)(void);
- void (*sync)(void);
void (*restore)(void);
void (*end)(void);
};
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index c874a7026e24..907b2be0372f 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -119,48 +119,41 @@ static void s2idle_enter(void)
static void s2idle_loop(void)
{
+ int error;
+
+ dpm_noirq_begin();
+ error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
+ if (error)
+ goto resume;
+
pm_pr_dbg("suspend-to-idle\n");
+ /*
+ * Suspend-to-idle equals:
+ * frozen processes + suspended devices + idle processors.
+ * Thus s2idle_enter() should be called right after all devices have
+ * been suspended.
+ *
+ * Wakeups during the noirq suspend of devices may be spurious, so try
+ * to avoid them upfront.
+ */
for (;;) {
- int error;
-
- dpm_noirq_begin();
-
- /*
- * Suspend-to-idle equals
- * frozen processes + suspended devices + idle processors.
- * Thus s2idle_enter() should be called right after
- * all devices have been suspended.
- *
- * Wakeups during the noirq suspend of devices may be spurious,
- * so prevent them from terminating the loop right away.
- */
- error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
- if (!error)
- s2idle_enter();
- else if (error == -EBUSY && pm_wakeup_pending())
- error = 0;
-
- if (!error && s2idle_ops && s2idle_ops->wake)
+ if (s2idle_ops && s2idle_ops->wake)
s2idle_ops->wake();
- dpm_noirq_resume_devices(PMSG_RESUME);
-
- dpm_noirq_end();
-
- if (error)
- break;
-
- if (s2idle_ops && s2idle_ops->sync)
- s2idle_ops->sync();
-
if (pm_wakeup_pending())
break;
pm_wakeup_clear(false);
+
+ s2idle_enter();
}
pm_pr_dbg("resume from suspend-to-idle\n");
+
+resume:
+ dpm_noirq_resume_devices(PMSG_RESUME);
+ dpm_noirq_end();
}
void s2idle_wake(void)