.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * kernel/power/suspend.c - Suspend to RAM and standby functionality. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2003 Patrick Mochel |
---|
5 | 6 | * Copyright (c) 2003 Open Source Development Lab |
---|
6 | 7 | * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. |
---|
7 | | - * |
---|
8 | | - * This file is released under the GPLv2. |
---|
9 | 8 | */ |
---|
10 | 9 | |
---|
11 | 10 | #define pr_fmt(fmt) "PM: " fmt |
---|
.. | .. |
---|
17 | 16 | #include <linux/console.h> |
---|
18 | 17 | #include <linux/cpu.h> |
---|
19 | 18 | #include <linux/cpuidle.h> |
---|
20 | | -#include <linux/syscalls.h> |
---|
21 | 19 | #include <linux/gfp.h> |
---|
22 | 20 | #include <linux/io.h> |
---|
23 | 21 | #include <linux/kernel.h> |
---|
.. | .. |
---|
46 | 44 | [PM_SUSPEND_TO_IDLE] = "s2idle", |
---|
47 | 45 | [PM_SUSPEND_STANDBY] = "shallow", |
---|
48 | 46 | [PM_SUSPEND_MEM] = "deep", |
---|
49 | | - [PM_SUSPEND_MEM_LITE] = "lite", |
---|
50 | | - [PM_SUSPEND_MEM_ULTRA] = "ultra", |
---|
51 | 47 | }; |
---|
52 | 48 | const char *mem_sleep_states[PM_SUSPEND_MAX]; |
---|
53 | 49 | |
---|
.. | .. |
---|
66 | 62 | enum s2idle_states __read_mostly s2idle_state; |
---|
67 | 63 | static DEFINE_RAW_SPINLOCK(s2idle_lock); |
---|
68 | 64 | |
---|
69 | | -bool pm_suspend_via_s2idle(void) |
---|
| 65 | +/** |
---|
| 66 | + * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend. |
---|
| 67 | + * |
---|
| 68 | + * Return 'true' if suspend-to-idle has been selected as the default system |
---|
| 69 | + * suspend method. |
---|
| 70 | + */ |
---|
| 71 | +bool pm_suspend_default_s2idle(void) |
---|
70 | 72 | { |
---|
71 | 73 | return mem_sleep_current == PM_SUSPEND_TO_IDLE; |
---|
72 | 74 | } |
---|
73 | | -EXPORT_SYMBOL_GPL(pm_suspend_via_s2idle); |
---|
| 75 | +EXPORT_SYMBOL_GPL(pm_suspend_default_s2idle); |
---|
74 | 76 | |
---|
75 | 77 | void s2idle_set_ops(const struct platform_s2idle_ops *ops) |
---|
76 | 78 | { |
---|
.. | .. |
---|
78 | 80 | s2idle_ops = ops; |
---|
79 | 81 | unlock_system_sleep(); |
---|
80 | 82 | } |
---|
81 | | -EXPORT_SYMBOL_GPL(s2idle_set_ops); |
---|
82 | 83 | |
---|
83 | 84 | static void s2idle_begin(void) |
---|
84 | 85 | { |
---|
.. | .. |
---|
121 | 122 | { |
---|
122 | 123 | pm_pr_dbg("suspend-to-idle\n"); |
---|
123 | 124 | |
---|
| 125 | + /* |
---|
| 126 | + * Suspend-to-idle equals: |
---|
| 127 | + * frozen processes + suspended devices + idle processors. |
---|
| 128 | + * Thus s2idle_enter() should be called right after all devices have |
---|
| 129 | + * been suspended. |
---|
| 130 | + * |
---|
| 131 | + * Wakeups during the noirq suspend of devices may be spurious, so try |
---|
| 132 | + * to avoid them upfront. |
---|
| 133 | + */ |
---|
124 | 134 | for (;;) { |
---|
125 | | - int error; |
---|
126 | | - |
---|
127 | | - dpm_noirq_begin(); |
---|
128 | | - |
---|
129 | | - /* |
---|
130 | | - * Suspend-to-idle equals |
---|
131 | | - * frozen processes + suspended devices + idle processors. |
---|
132 | | - * Thus s2idle_enter() should be called right after |
---|
133 | | - * all devices have been suspended. |
---|
134 | | - * |
---|
135 | | - * Wakeups during the noirq suspend of devices may be spurious, |
---|
136 | | - * so prevent them from terminating the loop right away. |
---|
137 | | - */ |
---|
138 | | - error = dpm_noirq_suspend_devices(PMSG_SUSPEND); |
---|
139 | | - if (!error) |
---|
140 | | - s2idle_enter(); |
---|
141 | | - else if (error == -EBUSY && pm_wakeup_pending()) |
---|
142 | | - error = 0; |
---|
143 | | - |
---|
144 | | - if (!error && s2idle_ops && s2idle_ops->wake) |
---|
145 | | - s2idle_ops->wake(); |
---|
146 | | - |
---|
147 | | - dpm_noirq_resume_devices(PMSG_RESUME); |
---|
148 | | - |
---|
149 | | - dpm_noirq_end(); |
---|
150 | | - |
---|
151 | | - if (error) |
---|
| 135 | + if (s2idle_ops && s2idle_ops->wake) { |
---|
| 136 | + if (s2idle_ops->wake()) |
---|
| 137 | + break; |
---|
| 138 | + } else if (pm_wakeup_pending()) { |
---|
152 | 139 | break; |
---|
| 140 | + } |
---|
153 | 141 | |
---|
154 | | - if (s2idle_ops && s2idle_ops->sync) |
---|
155 | | - s2idle_ops->sync(); |
---|
156 | | - |
---|
157 | | - if (pm_wakeup_pending()) |
---|
158 | | - break; |
---|
159 | | - |
---|
160 | | - pm_wakeup_clear(false); |
---|
161 | 142 | clear_wakeup_reasons(); |
---|
| 143 | + s2idle_enter(); |
---|
162 | 144 | } |
---|
163 | 145 | |
---|
164 | 146 | pm_pr_dbg("resume from suspend-to-idle\n"); |
---|
.. | .. |
---|
232 | 214 | } |
---|
233 | 215 | if (valid_state(PM_SUSPEND_MEM)) { |
---|
234 | 216 | mem_sleep_states[PM_SUSPEND_MEM] = mem_sleep_labels[PM_SUSPEND_MEM]; |
---|
235 | | - mem_sleep_states[PM_SUSPEND_MEM_LITE] = mem_sleep_labels[PM_SUSPEND_MEM_LITE]; |
---|
236 | | - mem_sleep_states[PM_SUSPEND_MEM_ULTRA] = mem_sleep_labels[PM_SUSPEND_MEM_ULTRA]; |
---|
237 | 217 | if (mem_sleep_default >= PM_SUSPEND_MEM) |
---|
238 | 218 | mem_sleep_current = PM_SUSPEND_MEM; |
---|
239 | 219 | } |
---|
.. | .. |
---|
274 | 254 | |
---|
275 | 255 | static int platform_suspend_prepare_noirq(suspend_state_t state) |
---|
276 | 256 | { |
---|
277 | | - return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ? |
---|
278 | | - suspend_ops->prepare_late() : 0; |
---|
| 257 | + if (state == PM_SUSPEND_TO_IDLE) |
---|
| 258 | + return s2idle_ops && s2idle_ops->prepare_late ? |
---|
| 259 | + s2idle_ops->prepare_late() : 0; |
---|
| 260 | + |
---|
| 261 | + return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0; |
---|
279 | 262 | } |
---|
280 | 263 | |
---|
281 | 264 | static void platform_resume_noirq(suspend_state_t state) |
---|
282 | 265 | { |
---|
283 | | - if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake) |
---|
| 266 | + if (state == PM_SUSPEND_TO_IDLE) { |
---|
| 267 | + if (s2idle_ops && s2idle_ops->restore_early) |
---|
| 268 | + s2idle_ops->restore_early(); |
---|
| 269 | + } else if (suspend_ops->wake) { |
---|
284 | 270 | suspend_ops->wake(); |
---|
| 271 | + } |
---|
285 | 272 | } |
---|
286 | 273 | |
---|
287 | 274 | static void platform_resume_early(suspend_state_t state) |
---|
.. | .. |
---|
355 | 342 | */ |
---|
356 | 343 | static int suspend_prepare(suspend_state_t state) |
---|
357 | 344 | { |
---|
358 | | - int error, nr_calls = 0; |
---|
| 345 | + int error; |
---|
359 | 346 | |
---|
360 | 347 | if (!sleep_state_supported(state)) |
---|
361 | 348 | return -EPERM; |
---|
362 | 349 | |
---|
363 | 350 | pm_prepare_console(); |
---|
364 | 351 | |
---|
365 | | - error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls); |
---|
366 | | - if (error) { |
---|
367 | | - nr_calls--; |
---|
368 | | - goto Finish; |
---|
369 | | - } |
---|
| 352 | + error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND); |
---|
| 353 | + if (error) |
---|
| 354 | + goto Restore; |
---|
370 | 355 | |
---|
371 | 356 | trace_suspend_resume(TPS("freeze_processes"), 0, true); |
---|
372 | 357 | error = suspend_freeze_processes(); |
---|
.. | .. |
---|
377 | 362 | log_suspend_abort_reason("One or more tasks refusing to freeze"); |
---|
378 | 363 | suspend_stats.failed_freeze++; |
---|
379 | 364 | dpm_save_failed_step(SUSPEND_FREEZE); |
---|
380 | | - Finish: |
---|
381 | | - __pm_notifier_call_chain(PM_POST_SUSPEND, nr_calls, NULL); |
---|
| 365 | + pm_notifier_call_chain(PM_POST_SUSPEND); |
---|
| 366 | + Restore: |
---|
382 | 367 | pm_restore_console(); |
---|
383 | 368 | return error; |
---|
384 | 369 | } |
---|
.. | .. |
---|
423 | 408 | if (error) |
---|
424 | 409 | goto Devices_early_resume; |
---|
425 | 410 | |
---|
426 | | - if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) { |
---|
427 | | - s2idle_loop(); |
---|
428 | | - goto Platform_early_resume; |
---|
429 | | - } |
---|
430 | | - |
---|
431 | 411 | error = dpm_suspend_noirq(PMSG_SUSPEND); |
---|
432 | 412 | if (error) { |
---|
433 | 413 | last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; |
---|
.. | .. |
---|
444 | 424 | if (suspend_test(TEST_PLATFORM)) |
---|
445 | 425 | goto Platform_wake; |
---|
446 | 426 | |
---|
447 | | - error = disable_nonboot_cpus(); |
---|
| 427 | + if (state == PM_SUSPEND_TO_IDLE) { |
---|
| 428 | + s2idle_loop(); |
---|
| 429 | + goto Platform_wake; |
---|
| 430 | + } |
---|
| 431 | + |
---|
| 432 | + error = suspend_disable_secondary_cpus(); |
---|
448 | 433 | if (error || suspend_test(TEST_CPUS)) { |
---|
449 | 434 | log_suspend_abort_reason("Disabling non-boot cpus failed"); |
---|
450 | 435 | goto Enable_cpus; |
---|
.. | .. |
---|
476 | 461 | BUG_ON(irqs_disabled()); |
---|
477 | 462 | |
---|
478 | 463 | Enable_cpus: |
---|
479 | | - enable_nonboot_cpus(); |
---|
| 464 | + suspend_enable_secondary_cpus(); |
---|
480 | 465 | |
---|
481 | 466 | Platform_wake: |
---|
482 | 467 | platform_resume_noirq(state); |
---|
.. | .. |
---|
506 | 491 | return -ENOSYS; |
---|
507 | 492 | |
---|
508 | 493 | pm_suspend_target_state = state; |
---|
| 494 | + |
---|
| 495 | + if (state == PM_SUSPEND_TO_IDLE) |
---|
| 496 | + pm_set_suspend_no_platform(); |
---|
509 | 497 | |
---|
510 | 498 | error = platform_suspend_begin(state); |
---|
511 | 499 | if (error) |
---|
.. | .. |
---|
588 | 576 | if (state == PM_SUSPEND_TO_IDLE) |
---|
589 | 577 | s2idle_begin(); |
---|
590 | 578 | |
---|
591 | | -#ifndef CONFIG_SUSPEND_SKIP_SYNC |
---|
592 | | - trace_suspend_resume(TPS("sync_filesystems"), 0, true); |
---|
593 | | - pr_info("Syncing filesystems ... "); |
---|
594 | | - ksys_sync(); |
---|
595 | | - pr_cont("done.\n"); |
---|
596 | | - trace_suspend_resume(TPS("sync_filesystems"), 0, false); |
---|
597 | | -#endif |
---|
| 579 | + if (sync_on_suspend_enabled) { |
---|
| 580 | + trace_suspend_resume(TPS("sync_filesystems"), 0, true); |
---|
| 581 | + ksys_sync_helper(); |
---|
| 582 | + trace_suspend_resume(TPS("sync_filesystems"), 0, false); |
---|
| 583 | + } |
---|
598 | 584 | |
---|
599 | 585 | pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); |
---|
600 | 586 | pm_suspend_clear_flags(); |
---|
.. | .. |
---|
635 | 621 | return -EINVAL; |
---|
636 | 622 | |
---|
637 | 623 | pr_info("suspend entry (%s)\n", mem_sleep_labels[state]); |
---|
638 | | - |
---|
639 | | - if (state == PM_SUSPEND_MEM_LITE || state == PM_SUSPEND_MEM_ULTRA) |
---|
640 | | - state = PM_SUSPEND_MEM; |
---|
641 | | - |
---|
642 | 624 | error = enter_state(state); |
---|
643 | 625 | if (error) { |
---|
644 | 626 | suspend_stats.fail++; |
---|