aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/sleep.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-06-12 16:56:34 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-06-14 18:55:44 -0400
commit33e4f80ee69b5168badf37edbfed796eb48434b9 (patch)
tree004c10f12c53aa2fef2dd0229e645bf699610e21 /drivers/acpi/sleep.c
parent63dada87f7ef7d4a536765c816fbbe7c4b9f3c85 (diff)
ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle
The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ during suspend-to-idle transitions and, consequently, any events signaled through it wake up the system from that state. However, on some systems some of the events signaled via the ACPI SCI while suspended to idle should not cause the system to wake up. In fact, quite often they should just be discarded. Arguably, systems should not resume entirely on such events, but in order to decide which events really should cause the system to resume and which are spurious, it is necessary to resume up to the point when ACPI SCIs are actually handled and processed, which is after executing dpm_resume_noirq() in the system resume path. For this reasons, add a loop around freeze_enter() in which the platforms can process events signaled via multiplexed IRQ lines like the ACPI SCI and add suspend-to-idle hooks that can be used for this purpose to struct platform_freeze_ops. In the ACPI case, the ->wake hook is used for checking if the SCI has triggered while suspended and deferring the interrupt-induced system wakeup until the events signaled through it are actually processed sufficiently to decide whether or not the system should resume. In turn, the ->sync hook allows all of the relevant event queues to be flushed so as to prevent events from being missed due to race conditions. In addition to that, some ACPI code processing wakeup events needs to be modified to use the "hard" version of wakeup triggers, so that it will cause a system resume to happen on device-induced wakeup events even if the "soft" mechanism to prevent the system from suspending is not enabled. However, to preserve the existing behavior with respect to suspend-to-RAM, this only is done in the suspend-to-idle case and only if an SCI has occurred while suspended. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/sleep.c')
-rw-r--r--drivers/acpi/sleep.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index a4782c75ebdd..555de11a56b6 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -650,6 +650,8 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
650 .recover = acpi_pm_finish, 650 .recover = acpi_pm_finish,
651}; 651};
652 652
653static bool s2idle_wakeup;
654
653static int acpi_freeze_begin(void) 655static int acpi_freeze_begin(void)
654{ 656{
655 acpi_scan_lock_acquire(); 657 acpi_scan_lock_acquire();
@@ -666,6 +668,33 @@ static int acpi_freeze_prepare(void)
666 return 0; 668 return 0;
667} 669}
668 670
671static void acpi_freeze_wake(void)
672{
673 /*
674 * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
675 * that the SCI has triggered while suspended, so cancel the wakeup in
676 * case it has not been a wakeup event (the GPEs will be checked later).
677 */
678 if (acpi_sci_irq_valid() &&
679 !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
680 pm_system_cancel_wakeup();
681 s2idle_wakeup = true;
682 }
683}
684
685static void acpi_freeze_sync(void)
686{
687 /*
688 * Process all pending events in case there are any wakeup ones.
689 *
690 * The EC driver uses the system workqueue, so that one needs to be
691 * flushed too.
692 */
693 acpi_os_wait_events_complete();
694 flush_scheduled_work();
695 s2idle_wakeup = false;
696}
697
669static void acpi_freeze_restore(void) 698static void acpi_freeze_restore(void)
670{ 699{
671 if (acpi_sci_irq_valid()) 700 if (acpi_sci_irq_valid())
@@ -682,6 +711,8 @@ static void acpi_freeze_end(void)
682static const struct platform_freeze_ops acpi_freeze_ops = { 711static const struct platform_freeze_ops acpi_freeze_ops = {
683 .begin = acpi_freeze_begin, 712 .begin = acpi_freeze_begin,
684 .prepare = acpi_freeze_prepare, 713 .prepare = acpi_freeze_prepare,
714 .wake = acpi_freeze_wake,
715 .sync = acpi_freeze_sync,
685 .restore = acpi_freeze_restore, 716 .restore = acpi_freeze_restore,
686 .end = acpi_freeze_end, 717 .end = acpi_freeze_end,
687}; 718};
@@ -700,9 +731,15 @@ static void acpi_sleep_suspend_setup(void)
700} 731}
701 732
702#else /* !CONFIG_SUSPEND */ 733#else /* !CONFIG_SUSPEND */
734#define s2idle_wakeup (false)
703static inline void acpi_sleep_suspend_setup(void) {} 735static inline void acpi_sleep_suspend_setup(void) {}
704#endif /* !CONFIG_SUSPEND */ 736#endif /* !CONFIG_SUSPEND */
705 737
738bool acpi_s2idle_wakeup(void)
739{
740 return s2idle_wakeup;
741}
742
706#ifdef CONFIG_PM_SLEEP 743#ifdef CONFIG_PM_SLEEP
707static u32 saved_bm_rld; 744static u32 saved_bm_rld;
708 745