diff options
Diffstat (limited to 'kernel/power/suspend.c')
-rw-r--r-- | kernel/power/suspend.c | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c347e3ce3a55..b7d6b3a721b1 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -37,7 +37,9 @@ const char *pm_states[PM_SUSPEND_MAX]; | |||
37 | static const struct platform_suspend_ops *suspend_ops; | 37 | static const struct platform_suspend_ops *suspend_ops; |
38 | static const struct platform_freeze_ops *freeze_ops; | 38 | static const struct platform_freeze_ops *freeze_ops; |
39 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); | 39 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); |
40 | static bool suspend_freeze_wake; | 40 | |
41 | enum freeze_state __read_mostly suspend_freeze_state; | ||
42 | static DEFINE_SPINLOCK(suspend_freeze_lock); | ||
41 | 43 | ||
42 | void freeze_set_ops(const struct platform_freeze_ops *ops) | 44 | void freeze_set_ops(const struct platform_freeze_ops *ops) |
43 | { | 45 | { |
@@ -48,22 +50,49 @@ void freeze_set_ops(const struct platform_freeze_ops *ops) | |||
48 | 50 | ||
49 | static void freeze_begin(void) | 51 | static void freeze_begin(void) |
50 | { | 52 | { |
51 | suspend_freeze_wake = false; | 53 | suspend_freeze_state = FREEZE_STATE_NONE; |
52 | } | 54 | } |
53 | 55 | ||
54 | static void freeze_enter(void) | 56 | static void freeze_enter(void) |
55 | { | 57 | { |
56 | cpuidle_use_deepest_state(true); | 58 | spin_lock_irq(&suspend_freeze_lock); |
59 | if (pm_wakeup_pending()) | ||
60 | goto out; | ||
61 | |||
62 | suspend_freeze_state = FREEZE_STATE_ENTER; | ||
63 | spin_unlock_irq(&suspend_freeze_lock); | ||
64 | |||
65 | get_online_cpus(); | ||
57 | cpuidle_resume(); | 66 | cpuidle_resume(); |
58 | wait_event(suspend_freeze_wait_head, suspend_freeze_wake); | 67 | |
68 | /* Push all the CPUs into the idle loop. */ | ||
69 | wake_up_all_idle_cpus(); | ||
70 | pr_debug("PM: suspend-to-idle\n"); | ||
71 | /* Make the current CPU wait so it can enter the idle loop too. */ | ||
72 | wait_event(suspend_freeze_wait_head, | ||
73 | suspend_freeze_state == FREEZE_STATE_WAKE); | ||
74 | pr_debug("PM: resume from suspend-to-idle\n"); | ||
75 | |||
59 | cpuidle_pause(); | 76 | cpuidle_pause(); |
60 | cpuidle_use_deepest_state(false); | 77 | put_online_cpus(); |
78 | |||
79 | spin_lock_irq(&suspend_freeze_lock); | ||
80 | |||
81 | out: | ||
82 | suspend_freeze_state = FREEZE_STATE_NONE; | ||
83 | spin_unlock_irq(&suspend_freeze_lock); | ||
61 | } | 84 | } |
62 | 85 | ||
63 | void freeze_wake(void) | 86 | void freeze_wake(void) |
64 | { | 87 | { |
65 | suspend_freeze_wake = true; | 88 | unsigned long flags; |
66 | wake_up(&suspend_freeze_wait_head); | 89 | |
90 | spin_lock_irqsave(&suspend_freeze_lock, flags); | ||
91 | if (suspend_freeze_state > FREEZE_STATE_NONE) { | ||
92 | suspend_freeze_state = FREEZE_STATE_WAKE; | ||
93 | wake_up(&suspend_freeze_wait_head); | ||
94 | } | ||
95 | spin_unlock_irqrestore(&suspend_freeze_lock, flags); | ||
67 | } | 96 | } |
68 | EXPORT_SYMBOL_GPL(freeze_wake); | 97 | EXPORT_SYMBOL_GPL(freeze_wake); |
69 | 98 | ||