diff options
Diffstat (limited to 'kernel/power/suspend.c')
-rw-r--r-- | kernel/power/suspend.c | 69 |
1 files changed, 56 insertions, 13 deletions
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 | ||