summaryrefslogtreecommitdiffstats
path: root/kernel/power
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 /kernel/power
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 'kernel/power')
-rw-r--r--kernel/power/process.c2
-rw-r--r--kernel/power/suspend.c35
2 files changed, 30 insertions, 7 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c
index c7209f060eeb..78672d324a6e 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -132,7 +132,7 @@ int freeze_processes(void)
132 if (!pm_freezing) 132 if (!pm_freezing)
133 atomic_inc(&system_freezing_cnt); 133 atomic_inc(&system_freezing_cnt);
134 134
135 pm_wakeup_clear(); 135 pm_wakeup_clear(true);
136 pr_info("Freezing user space processes ... "); 136 pr_info("Freezing user space processes ... ");
137 pm_freezing = true; 137 pm_freezing = true;
138 error = try_to_freeze_tasks(true); 138 error = try_to_freeze_tasks(true);
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 15e6baef5c73..3ecf275d7e44 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -72,6 +72,8 @@ static void freeze_begin(void)
72 72
73static void freeze_enter(void) 73static void freeze_enter(void)
74{ 74{
75 trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, true);
76
75 spin_lock_irq(&suspend_freeze_lock); 77 spin_lock_irq(&suspend_freeze_lock);
76 if (pm_wakeup_pending()) 78 if (pm_wakeup_pending())
77 goto out; 79 goto out;
@@ -84,11 +86,9 @@ static void freeze_enter(void)
84 86
85 /* Push all the CPUs into the idle loop. */ 87 /* Push all the CPUs into the idle loop. */
86 wake_up_all_idle_cpus(); 88 wake_up_all_idle_cpus();
87 pr_debug("PM: suspend-to-idle\n");
88 /* Make the current CPU wait so it can enter the idle loop too. */ 89 /* Make the current CPU wait so it can enter the idle loop too. */
89 wait_event(suspend_freeze_wait_head, 90 wait_event(suspend_freeze_wait_head,
90 suspend_freeze_state == FREEZE_STATE_WAKE); 91 suspend_freeze_state == FREEZE_STATE_WAKE);
91 pr_debug("PM: resume from suspend-to-idle\n");
92 92
93 cpuidle_pause(); 93 cpuidle_pause();
94 put_online_cpus(); 94 put_online_cpus();
@@ -98,6 +98,31 @@ static void freeze_enter(void)
98 out: 98 out:
99 suspend_freeze_state = FREEZE_STATE_NONE; 99 suspend_freeze_state = FREEZE_STATE_NONE;
100 spin_unlock_irq(&suspend_freeze_lock); 100 spin_unlock_irq(&suspend_freeze_lock);
101
102 trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_FREEZE, false);
103}
104
105static void s2idle_loop(void)
106{
107 pr_debug("PM: suspend-to-idle\n");
108
109 do {
110 freeze_enter();
111
112 if (freeze_ops && freeze_ops->wake)
113 freeze_ops->wake();
114
115 dpm_resume_noirq(PMSG_RESUME);
116 if (freeze_ops && freeze_ops->sync)
117 freeze_ops->sync();
118
119 if (pm_wakeup_pending())
120 break;
121
122 pm_wakeup_clear(false);
123 } while (!dpm_suspend_noirq(PMSG_SUSPEND));
124
125 pr_debug("PM: resume from suspend-to-idle\n");
101} 126}
102 127
103void freeze_wake(void) 128void freeze_wake(void)
@@ -371,10 +396,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
371 * all the devices are suspended. 396 * all the devices are suspended.
372 */ 397 */
373 if (state == PM_SUSPEND_FREEZE) { 398 if (state == PM_SUSPEND_FREEZE) {
374 trace_suspend_resume(TPS("machine_suspend"), state, true); 399 s2idle_loop();
375 freeze_enter(); 400 goto Platform_early_resume;
376 trace_suspend_resume(TPS("machine_suspend"), state, false);
377 goto Platform_wake;
378 } 401 }
379 402
380 error = disable_nonboot_cpus(); 403 error = disable_nonboot_cpus();