Commit | Line | Data |
---|---|---|
a9d70523 RW |
1 | /* |
2 | * kernel/power/suspend.c - Suspend to RAM and standby functionality. | |
3 | * | |
4 | * Copyright (c) 2003 Patrick Mochel | |
5 | * Copyright (c) 2003 Open Source Development Lab | |
6 | * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. | |
7 | * | |
8 | * This file is released under the GPLv2. | |
9 | */ | |
10 | ||
11 | #include <linux/string.h> | |
12 | #include <linux/delay.h> | |
13 | #include <linux/errno.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/console.h> | |
16 | #include <linux/cpu.h> | |
17 | #include <linux/syscalls.h> | |
5a0e3ad6 | 18 | #include <linux/gfp.h> |
dd4c4f17 MG |
19 | #include <linux/io.h> |
20 | #include <linux/kernel.h> | |
21 | #include <linux/list.h> | |
22 | #include <linux/mm.h> | |
23 | #include <linux/slab.h> | |
6e5fdeed | 24 | #include <linux/export.h> |
dd4c4f17 | 25 | #include <linux/suspend.h> |
40dc166c | 26 | #include <linux/syscore_ops.h> |
938cfed1 | 27 | #include <trace/events/power.h> |
a9d70523 RW |
28 | |
29 | #include "power.h" | |
30 | ||
31 | const char *const pm_states[PM_SUSPEND_MAX] = { | |
32 | [PM_SUSPEND_STANDBY] = "standby", | |
33 | [PM_SUSPEND_MEM] = "mem", | |
34 | }; | |
35 | ||
2f55ac07 | 36 | static const struct platform_suspend_ops *suspend_ops; |
a9d70523 RW |
37 | |
38 | /** | |
55ae4519 RW |
39 | * suspend_set_ops - Set the global suspend method table. |
40 | * @ops: Suspend operations to use. | |
a9d70523 | 41 | */ |
2f55ac07 | 42 | void suspend_set_ops(const struct platform_suspend_ops *ops) |
a9d70523 | 43 | { |
bcda53fa | 44 | lock_system_sleep(); |
a9d70523 | 45 | suspend_ops = ops; |
bcda53fa | 46 | unlock_system_sleep(); |
a9d70523 | 47 | } |
a5e4fd87 | 48 | EXPORT_SYMBOL_GPL(suspend_set_ops); |
a9d70523 RW |
49 | |
50 | bool valid_state(suspend_state_t state) | |
51 | { | |
52 | /* | |
53 | * All states need lowlevel support and need to be valid to the lowlevel | |
54 | * implementation, no valid callback implies that none are valid. | |
55 | */ | |
56 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); | |
57 | } | |
58 | ||
59 | /** | |
55ae4519 | 60 | * suspend_valid_only_mem - Generic memory-only valid callback. |
a9d70523 | 61 | * |
55ae4519 RW |
62 | * Platform drivers that implement mem suspend only and only need to check for |
63 | * that in their .valid() callback can use this instead of rolling their own | |
64 | * .valid() callback. | |
a9d70523 RW |
65 | */ |
66 | int suspend_valid_only_mem(suspend_state_t state) | |
67 | { | |
68 | return state == PM_SUSPEND_MEM; | |
69 | } | |
a5e4fd87 | 70 | EXPORT_SYMBOL_GPL(suspend_valid_only_mem); |
a9d70523 RW |
71 | |
72 | static int suspend_test(int level) | |
73 | { | |
74 | #ifdef CONFIG_PM_DEBUG | |
75 | if (pm_test_level == level) { | |
76 | printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); | |
77 | mdelay(5000); | |
78 | return 1; | |
79 | } | |
80 | #endif /* !CONFIG_PM_DEBUG */ | |
81 | return 0; | |
82 | } | |
83 | ||
84 | /** | |
55ae4519 | 85 | * suspend_prepare - Prepare for entering system sleep state. |
a9d70523 | 86 | * |
55ae4519 RW |
87 | * Common code run for every system sleep state that can be entered (except for |
88 | * hibernation). Run suspend notifiers, allocate the "suspend" console and | |
89 | * freeze processes. | |
a9d70523 RW |
90 | */ |
91 | static int suspend_prepare(void) | |
92 | { | |
93 | int error; | |
94 | ||
95 | if (!suspend_ops || !suspend_ops->enter) | |
96 | return -EPERM; | |
97 | ||
98 | pm_prepare_console(); | |
99 | ||
100 | error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); | |
101 | if (error) | |
102 | goto Finish; | |
103 | ||
a9d70523 | 104 | error = suspend_freeze_processes(); |
03afed8b | 105 | if (!error) |
a9d70523 RW |
106 | return 0; |
107 | ||
03afed8b TH |
108 | suspend_stats.failed_freeze++; |
109 | dpm_save_failed_step(SUSPEND_FREEZE); | |
a9d70523 RW |
110 | Finish: |
111 | pm_notifier_call_chain(PM_POST_SUSPEND); | |
112 | pm_restore_console(); | |
113 | return error; | |
114 | } | |
115 | ||
116 | /* default implementation */ | |
117 | void __attribute__ ((weak)) arch_suspend_disable_irqs(void) | |
118 | { | |
119 | local_irq_disable(); | |
120 | } | |
121 | ||
122 | /* default implementation */ | |
123 | void __attribute__ ((weak)) arch_suspend_enable_irqs(void) | |
124 | { | |
125 | local_irq_enable(); | |
126 | } | |
127 | ||
128 | /** | |
55ae4519 RW |
129 | * suspend_enter - Make the system enter the given sleep state. |
130 | * @state: System sleep state to enter. | |
131 | * @wakeup: Returns information that the sleep state should not be re-entered. | |
a9d70523 | 132 | * |
3b5fe852 | 133 | * This function should be called after devices have been suspended. |
a9d70523 | 134 | */ |
3b5fe852 | 135 | static int suspend_enter(suspend_state_t state, bool *wakeup) |
a9d70523 RW |
136 | { |
137 | int error; | |
138 | ||
139 | if (suspend_ops->prepare) { | |
140 | error = suspend_ops->prepare(); | |
141 | if (error) | |
ce441011 | 142 | goto Platform_finish; |
a9d70523 RW |
143 | } |
144 | ||
cf579dfb | 145 | error = dpm_suspend_end(PMSG_SUSPEND); |
a9d70523 RW |
146 | if (error) { |
147 | printk(KERN_ERR "PM: Some devices failed to power down\n"); | |
ce441011 | 148 | goto Platform_finish; |
a9d70523 RW |
149 | } |
150 | ||
151 | if (suspend_ops->prepare_late) { | |
152 | error = suspend_ops->prepare_late(); | |
153 | if (error) | |
ce441011 | 154 | goto Platform_wake; |
a9d70523 RW |
155 | } |
156 | ||
157 | if (suspend_test(TEST_PLATFORM)) | |
158 | goto Platform_wake; | |
159 | ||
160 | error = disable_nonboot_cpus(); | |
161 | if (error || suspend_test(TEST_CPUS)) | |
162 | goto Enable_cpus; | |
163 | ||
164 | arch_suspend_disable_irqs(); | |
165 | BUG_ON(!irqs_disabled()); | |
166 | ||
2e711c04 | 167 | error = syscore_suspend(); |
a9d70523 | 168 | if (!error) { |
3b5fe852 MH |
169 | *wakeup = pm_wakeup_pending(); |
170 | if (!(suspend_test(TEST_CORE) || *wakeup)) { | |
a9d70523 | 171 | error = suspend_ops->enter(state); |
c125e96f RW |
172 | events_check_enabled = false; |
173 | } | |
40dc166c | 174 | syscore_resume(); |
a9d70523 RW |
175 | } |
176 | ||
177 | arch_suspend_enable_irqs(); | |
178 | BUG_ON(irqs_disabled()); | |
179 | ||
180 | Enable_cpus: | |
181 | enable_nonboot_cpus(); | |
182 | ||
183 | Platform_wake: | |
184 | if (suspend_ops->wake) | |
185 | suspend_ops->wake(); | |
186 | ||
cf579dfb | 187 | dpm_resume_start(PMSG_RESUME); |
a9d70523 | 188 | |
ce441011 | 189 | Platform_finish: |
a9d70523 RW |
190 | if (suspend_ops->finish) |
191 | suspend_ops->finish(); | |
192 | ||
193 | return error; | |
194 | } | |
195 | ||
196 | /** | |
55ae4519 RW |
197 | * suspend_devices_and_enter - Suspend devices and enter system sleep state. |
198 | * @state: System sleep state to enter. | |
a9d70523 RW |
199 | */ |
200 | int suspend_devices_and_enter(suspend_state_t state) | |
201 | { | |
202 | int error; | |
3b5fe852 | 203 | bool wakeup = false; |
a9d70523 RW |
204 | |
205 | if (!suspend_ops) | |
206 | return -ENOSYS; | |
207 | ||
938cfed1 | 208 | trace_machine_suspend(state); |
a9d70523 RW |
209 | if (suspend_ops->begin) { |
210 | error = suspend_ops->begin(state); | |
211 | if (error) | |
212 | goto Close; | |
213 | } | |
214 | suspend_console(); | |
215 | suspend_test_start(); | |
216 | error = dpm_suspend_start(PMSG_SUSPEND); | |
217 | if (error) { | |
218 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); | |
219 | goto Recover_platform; | |
220 | } | |
221 | suspend_test_finish("suspend devices"); | |
222 | if (suspend_test(TEST_DEVICES)) | |
223 | goto Recover_platform; | |
224 | ||
3b5fe852 MH |
225 | do { |
226 | error = suspend_enter(state, &wakeup); | |
227 | } while (!error && !wakeup | |
228 | && suspend_ops->suspend_again && suspend_ops->suspend_again()); | |
a9d70523 RW |
229 | |
230 | Resume_devices: | |
231 | suspend_test_start(); | |
232 | dpm_resume_end(PMSG_RESUME); | |
233 | suspend_test_finish("resume devices"); | |
234 | resume_console(); | |
235 | Close: | |
236 | if (suspend_ops->end) | |
237 | suspend_ops->end(); | |
938cfed1 | 238 | trace_machine_suspend(PWR_EVENT_EXIT); |
a9d70523 RW |
239 | return error; |
240 | ||
241 | Recover_platform: | |
242 | if (suspend_ops->recover) | |
243 | suspend_ops->recover(); | |
244 | goto Resume_devices; | |
245 | } | |
246 | ||
247 | /** | |
55ae4519 | 248 | * suspend_finish - Clean up before finishing the suspend sequence. |
a9d70523 | 249 | * |
55ae4519 RW |
250 | * Call platform code to clean up, restart processes, and free the console that |
251 | * we've allocated. This routine is not called for hibernation. | |
a9d70523 RW |
252 | */ |
253 | static void suspend_finish(void) | |
254 | { | |
255 | suspend_thaw_processes(); | |
a9d70523 RW |
256 | pm_notifier_call_chain(PM_POST_SUSPEND); |
257 | pm_restore_console(); | |
258 | } | |
259 | ||
260 | /** | |
55ae4519 RW |
261 | * enter_state - Do common work needed to enter system sleep state. |
262 | * @state: System sleep state to enter. | |
a9d70523 | 263 | * |
55ae4519 RW |
264 | * Make sure that no one else is trying to put the system into a sleep state. |
265 | * Fail if that's not the case. Otherwise, prepare for system suspend, make the | |
266 | * system enter the given sleep state and clean up after wakeup. | |
a9d70523 | 267 | */ |
93e1ee43 | 268 | static int enter_state(suspend_state_t state) |
a9d70523 RW |
269 | { |
270 | int error; | |
271 | ||
272 | if (!valid_state(state)) | |
273 | return -ENODEV; | |
274 | ||
275 | if (!mutex_trylock(&pm_mutex)) | |
276 | return -EBUSY; | |
277 | ||
278 | printk(KERN_INFO "PM: Syncing filesystems ... "); | |
279 | sys_sync(); | |
280 | printk("done.\n"); | |
281 | ||
282 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | |
283 | error = suspend_prepare(); | |
284 | if (error) | |
285 | goto Unlock; | |
286 | ||
287 | if (suspend_test(TEST_FREEZER)) | |
288 | goto Finish; | |
289 | ||
290 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); | |
87186475 | 291 | pm_restrict_gfp_mask(); |
a9d70523 | 292 | error = suspend_devices_and_enter(state); |
87186475 | 293 | pm_restore_gfp_mask(); |
a9d70523 RW |
294 | |
295 | Finish: | |
296 | pr_debug("PM: Finishing wakeup.\n"); | |
297 | suspend_finish(); | |
298 | Unlock: | |
299 | mutex_unlock(&pm_mutex); | |
300 | return error; | |
301 | } | |
302 | ||
303 | /** | |
55ae4519 RW |
304 | * pm_suspend - Externally visible function for suspending the system. |
305 | * @state: System sleep state to enter. | |
a9d70523 | 306 | * |
55ae4519 RW |
307 | * Check if the value of @state represents one of the supported states, |
308 | * execute enter_state() and update system suspend statistics. | |
a9d70523 RW |
309 | */ |
310 | int pm_suspend(suspend_state_t state) | |
311 | { | |
bc25cf50 RW |
312 | int error; |
313 | ||
314 | if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) | |
315 | return -EINVAL; | |
316 | ||
317 | error = enter_state(state); | |
318 | if (error) { | |
319 | suspend_stats.fail++; | |
320 | dpm_save_failed_errno(error); | |
321 | } else { | |
322 | suspend_stats.success++; | |
2a77c46d | 323 | } |
bc25cf50 | 324 | return error; |
a9d70523 RW |
325 | } |
326 | EXPORT_SYMBOL(pm_suspend); |