diff options
-rw-r--r-- | drivers/base/power/wakeup.c | 6 | ||||
-rw-r--r-- | include/linux/suspend.h | 6 | ||||
-rw-r--r-- | kernel/power/main.c | 2 | ||||
-rw-r--r-- | kernel/power/suspend.c | 69 |
4 files changed, 68 insertions, 15 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index e6ee5e80e546..79715e7fa43e 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c | |||
@@ -382,6 +382,12 @@ static void wakeup_source_activate(struct wakeup_source *ws) | |||
382 | { | 382 | { |
383 | unsigned int cec; | 383 | unsigned int cec; |
384 | 384 | ||
385 | /* | ||
386 | * active wakeup source should bring the system | ||
387 | * out of PM_SUSPEND_FREEZE state | ||
388 | */ | ||
389 | freeze_wake(); | ||
390 | |||
385 | ws->active = true; | 391 | ws->active = true; |
386 | ws->active_count++; | 392 | ws->active_count++; |
387 | ws->last_time = ktime_get(); | 393 | ws->last_time = ktime_get(); |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 0c808d7fa579..d4e3f16d5e89 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -34,8 +34,10 @@ static inline void pm_restore_console(void) | |||
34 | typedef int __bitwise suspend_state_t; | 34 | typedef int __bitwise suspend_state_t; |
35 | 35 | ||
36 | #define PM_SUSPEND_ON ((__force suspend_state_t) 0) | 36 | #define PM_SUSPEND_ON ((__force suspend_state_t) 0) |
37 | #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1) | 37 | #define PM_SUSPEND_FREEZE ((__force suspend_state_t) 1) |
38 | #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2) | ||
38 | #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) | 39 | #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) |
40 | #define PM_SUSPEND_MIN PM_SUSPEND_FREEZE | ||
39 | #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) | 41 | #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) |
40 | 42 | ||
41 | enum suspend_stat_step { | 43 | enum suspend_stat_step { |
@@ -192,6 +194,7 @@ struct platform_suspend_ops { | |||
192 | */ | 194 | */ |
193 | extern void suspend_set_ops(const struct platform_suspend_ops *ops); | 195 | extern void suspend_set_ops(const struct platform_suspend_ops *ops); |
194 | extern int suspend_valid_only_mem(suspend_state_t state); | 196 | extern int suspend_valid_only_mem(suspend_state_t state); |
197 | extern void freeze_wake(void); | ||
195 | 198 | ||
196 | /** | 199 | /** |
197 | * arch_suspend_disable_irqs - disable IRQs for suspend | 200 | * arch_suspend_disable_irqs - disable IRQs for suspend |
@@ -217,6 +220,7 @@ extern int pm_suspend(suspend_state_t state); | |||
217 | 220 | ||
218 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} | 221 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} |
219 | static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } | 222 | static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } |
223 | static inline void freeze_wake(void) {} | ||
220 | #endif /* !CONFIG_SUSPEND */ | 224 | #endif /* !CONFIG_SUSPEND */ |
221 | 225 | ||
222 | /* struct pbe is used for creating lists of pages that should be restored | 226 | /* struct pbe is used for creating lists of pages that should be restored |
diff --git a/kernel/power/main.c b/kernel/power/main.c index 1c16f9167de1..b1c26a92ca9f 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -313,7 +313,7 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, | |||
313 | static suspend_state_t decode_state(const char *buf, size_t n) | 313 | static suspend_state_t decode_state(const char *buf, size_t n) |
314 | { | 314 | { |
315 | #ifdef CONFIG_SUSPEND | 315 | #ifdef CONFIG_SUSPEND |
316 | suspend_state_t state = PM_SUSPEND_STANDBY; | 316 | suspend_state_t state = PM_SUSPEND_MIN; |
317 | const char * const *s; | 317 | const char * const *s; |
318 | #endif | 318 | #endif |
319 | char *p; | 319 | char *p; |
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c8b7446b27df..d4feda084a3a 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -30,12 +30,38 @@ | |||
30 | #include "power.h" | 30 | #include "power.h" |
31 | 31 | ||
32 | const char *const pm_states[PM_SUSPEND_MAX] = { | 32 | const char *const pm_states[PM_SUSPEND_MAX] = { |
33 | [PM_SUSPEND_FREEZE] = "freeze", | ||
33 | [PM_SUSPEND_STANDBY] = "standby", | 34 | [PM_SUSPEND_STANDBY] = "standby", |
34 | [PM_SUSPEND_MEM] = "mem", | 35 | [PM_SUSPEND_MEM] = "mem", |
35 | }; | 36 | }; |
36 | 37 | ||
37 | static const struct platform_suspend_ops *suspend_ops; | 38 | static const struct platform_suspend_ops *suspend_ops; |
38 | 39 | ||
40 | static bool need_suspend_ops(suspend_state_t state) | ||
41 | { | ||
42 | return !!(state > PM_SUSPEND_FREEZE); | ||
43 | } | ||
44 | |||
45 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); | ||
46 | static bool suspend_freeze_wake; | ||
47 | |||
48 | static void freeze_begin(void) | ||
49 | { | ||
50 | suspend_freeze_wake = false; | ||
51 | } | ||
52 | |||
53 | static void freeze_enter(void) | ||
54 | { | ||
55 | wait_event(suspend_freeze_wait_head, suspend_freeze_wake); | ||
56 | } | ||
57 | |||
58 | void freeze_wake(void) | ||
59 | { | ||
60 | suspend_freeze_wake = true; | ||
61 | wake_up(&suspend_freeze_wait_head); | ||
62 | } | ||
63 | EXPORT_SYMBOL_GPL(freeze_wake); | ||
64 | |||
39 | /** | 65 | /** |
40 | * suspend_set_ops - Set the global suspend method table. | 66 | * suspend_set_ops - Set the global suspend method table. |
41 | * @ops: Suspend operations to use. | 67 | * @ops: Suspend operations to use. |
@@ -50,8 +76,11 @@ EXPORT_SYMBOL_GPL(suspend_set_ops); | |||
50 | 76 | ||
51 | bool valid_state(suspend_state_t state) | 77 | bool valid_state(suspend_state_t state) |
52 | { | 78 | { |
79 | if (state == PM_SUSPEND_FREEZE) | ||
80 | return true; | ||
53 | /* | 81 | /* |
54 | * All states need lowlevel support and need to be valid to the lowlevel | 82 | * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel |
83 | * support and need to be valid to the lowlevel | ||
55 | * implementation, no valid callback implies that none are valid. | 84 | * implementation, no valid callback implies that none are valid. |
56 | */ | 85 | */ |
57 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); | 86 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); |
@@ -89,11 +118,11 @@ static int suspend_test(int level) | |||
89 | * hibernation). Run suspend notifiers, allocate the "suspend" console and | 118 | * hibernation). Run suspend notifiers, allocate the "suspend" console and |
90 | * freeze processes. | 119 | * freeze processes. |
91 | */ | 120 | */ |
92 | static int suspend_prepare(void) | 121 | static int suspend_prepare(suspend_state_t state) |
93 | { | 122 | { |
94 | int error; | 123 | int error; |
95 | 124 | ||
96 | if (!suspend_ops || !suspend_ops->enter) | 125 | if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter)) |
97 | return -EPERM; | 126 | return -EPERM; |
98 | 127 | ||
99 | pm_prepare_console(); | 128 | pm_prepare_console(); |
@@ -137,7 +166,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
137 | { | 166 | { |
138 | int error; | 167 | int error; |
139 | 168 | ||
140 | if (suspend_ops->prepare) { | 169 | if (need_suspend_ops(state) && suspend_ops->prepare) { |
141 | error = suspend_ops->prepare(); | 170 | error = suspend_ops->prepare(); |
142 | if (error) | 171 | if (error) |
143 | goto Platform_finish; | 172 | goto Platform_finish; |
@@ -149,12 +178,23 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
149 | goto Platform_finish; | 178 | goto Platform_finish; |
150 | } | 179 | } |
151 | 180 | ||
152 | if (suspend_ops->prepare_late) { | 181 | if (need_suspend_ops(state) && suspend_ops->prepare_late) { |
153 | error = suspend_ops->prepare_late(); | 182 | error = suspend_ops->prepare_late(); |
154 | if (error) | 183 | if (error) |
155 | goto Platform_wake; | 184 | goto Platform_wake; |
156 | } | 185 | } |
157 | 186 | ||
187 | /* | ||
188 | * PM_SUSPEND_FREEZE equals | ||
189 | * frozen processes + suspended devices + idle processors. | ||
190 | * Thus we should invoke freeze_enter() soon after | ||
191 | * all the devices are suspended. | ||
192 | */ | ||
193 | if (state == PM_SUSPEND_FREEZE) { | ||
194 | freeze_enter(); | ||
195 | goto Platform_wake; | ||
196 | } | ||
197 | |||
158 | if (suspend_test(TEST_PLATFORM)) | 198 | if (suspend_test(TEST_PLATFORM)) |
159 | goto Platform_wake; | 199 | goto Platform_wake; |
160 | 200 | ||
@@ -182,13 +222,13 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) | |||
182 | enable_nonboot_cpus(); | 222 | enable_nonboot_cpus(); |
183 | 223 | ||
184 | Platform_wake: | 224 | Platform_wake: |
185 | if (suspend_ops->wake) | 225 | if (need_suspend_ops(state) && suspend_ops->wake) |
186 | suspend_ops->wake(); | 226 | suspend_ops->wake(); |
187 | 227 | ||
188 | dpm_resume_start(PMSG_RESUME); | 228 | dpm_resume_start(PMSG_RESUME); |
189 | 229 | ||
190 | Platform_finish: | 230 | Platform_finish: |
191 | if (suspend_ops->finish) | 231 | if (need_suspend_ops(state) && suspend_ops->finish) |
192 | suspend_ops->finish(); | 232 | suspend_ops->finish(); |
193 | 233 | ||
194 | return error; | 234 | return error; |
@@ -203,11 +243,11 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
203 | int error; | 243 | int error; |
204 | bool wakeup = false; | 244 | bool wakeup = false; |
205 | 245 | ||
206 | if (!suspend_ops) | 246 | if (need_suspend_ops(state) && !suspend_ops) |
207 | return -ENOSYS; | 247 | return -ENOSYS; |
208 | 248 | ||
209 | trace_machine_suspend(state); | 249 | trace_machine_suspend(state); |
210 | if (suspend_ops->begin) { | 250 | if (need_suspend_ops(state) && suspend_ops->begin) { |
211 | error = suspend_ops->begin(state); | 251 | error = suspend_ops->begin(state); |
212 | if (error) | 252 | if (error) |
213 | goto Close; | 253 | goto Close; |
@@ -226,7 +266,7 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
226 | 266 | ||
227 | do { | 267 | do { |
228 | error = suspend_enter(state, &wakeup); | 268 | error = suspend_enter(state, &wakeup); |
229 | } while (!error && !wakeup | 269 | } while (!error && !wakeup && need_suspend_ops(state) |
230 | && suspend_ops->suspend_again && suspend_ops->suspend_again()); | 270 | && suspend_ops->suspend_again && suspend_ops->suspend_again()); |
231 | 271 | ||
232 | Resume_devices: | 272 | Resume_devices: |
@@ -236,13 +276,13 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
236 | ftrace_start(); | 276 | ftrace_start(); |
237 | resume_console(); | 277 | resume_console(); |
238 | Close: | 278 | Close: |
239 | if (suspend_ops->end) | 279 | if (need_suspend_ops(state) && suspend_ops->end) |
240 | suspend_ops->end(); | 280 | suspend_ops->end(); |
241 | trace_machine_suspend(PWR_EVENT_EXIT); | 281 | trace_machine_suspend(PWR_EVENT_EXIT); |
242 | return error; | 282 | return error; |
243 | 283 | ||
244 | Recover_platform: | 284 | Recover_platform: |
245 | if (suspend_ops->recover) | 285 | if (need_suspend_ops(state) && suspend_ops->recover) |
246 | suspend_ops->recover(); | 286 | suspend_ops->recover(); |
247 | goto Resume_devices; | 287 | goto Resume_devices; |
248 | } | 288 | } |
@@ -278,12 +318,15 @@ static int enter_state(suspend_state_t state) | |||
278 | if (!mutex_trylock(&pm_mutex)) | 318 | if (!mutex_trylock(&pm_mutex)) |
279 | return -EBUSY; | 319 | return -EBUSY; |
280 | 320 | ||
321 | if (state == PM_SUSPEND_FREEZE) | ||
322 | freeze_begin(); | ||
323 | |||
281 | printk(KERN_INFO "PM: Syncing filesystems ... "); | 324 | printk(KERN_INFO "PM: Syncing filesystems ... "); |
282 | sys_sync(); | 325 | sys_sync(); |
283 | printk("done.\n"); | 326 | printk("done.\n"); |
284 | 327 | ||
285 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 328 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
286 | error = suspend_prepare(); | 329 | error = suspend_prepare(state); |
287 | if (error) | 330 | if (error) |
288 | goto Unlock; | 331 | goto Unlock; |
289 | 332 | ||