Root/
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> |
18 | #include <linux/gfp.h> |
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> |
24 | #include <linux/suspend.h> |
25 | #include <trace/events/power.h> |
26 | |
27 | #include "power.h" |
28 | |
29 | const char *const pm_states[PM_SUSPEND_MAX] = { |
30 | [PM_SUSPEND_STANDBY] = "standby", |
31 | [PM_SUSPEND_MEM] = "mem", |
32 | }; |
33 | |
34 | static const struct platform_suspend_ops *suspend_ops; |
35 | |
36 | /** |
37 | * suspend_set_ops - Set the global suspend method table. |
38 | * @ops: Pointer to ops structure. |
39 | */ |
40 | void suspend_set_ops(const struct platform_suspend_ops *ops) |
41 | { |
42 | mutex_lock(&pm_mutex); |
43 | suspend_ops = ops; |
44 | mutex_unlock(&pm_mutex); |
45 | } |
46 | |
47 | bool valid_state(suspend_state_t state) |
48 | { |
49 | /* |
50 | * All states need lowlevel support and need to be valid to the lowlevel |
51 | * implementation, no valid callback implies that none are valid. |
52 | */ |
53 | return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); |
54 | } |
55 | |
56 | /** |
57 | * suspend_valid_only_mem - generic memory-only valid callback |
58 | * |
59 | * Platform drivers that implement mem suspend only and only need |
60 | * to check for that in their .valid callback can use this instead |
61 | * of rolling their own .valid callback. |
62 | */ |
63 | int suspend_valid_only_mem(suspend_state_t state) |
64 | { |
65 | return state == PM_SUSPEND_MEM; |
66 | } |
67 | |
68 | static int suspend_test(int level) |
69 | { |
70 | #ifdef CONFIG_PM_DEBUG |
71 | if (pm_test_level == level) { |
72 | printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); |
73 | mdelay(5000); |
74 | return 1; |
75 | } |
76 | #endif /* !CONFIG_PM_DEBUG */ |
77 | return 0; |
78 | } |
79 | |
80 | /** |
81 | * suspend_prepare - Do prep work before entering low-power state. |
82 | * |
83 | * This is common code that is called for each state that we're entering. |
84 | * Run suspend notifiers, allocate a console and stop all processes. |
85 | */ |
86 | static int suspend_prepare(void) |
87 | { |
88 | int error; |
89 | |
90 | if (!suspend_ops || !suspend_ops->enter) |
91 | return -EPERM; |
92 | |
93 | pm_prepare_console(); |
94 | |
95 | error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); |
96 | if (error) |
97 | goto Finish; |
98 | |
99 | error = usermodehelper_disable(); |
100 | if (error) |
101 | goto Finish; |
102 | |
103 | error = suspend_freeze_processes(); |
104 | if (!error) |
105 | return 0; |
106 | |
107 | suspend_thaw_processes(); |
108 | usermodehelper_enable(); |
109 | Finish: |
110 | pm_notifier_call_chain(PM_POST_SUSPEND); |
111 | pm_restore_console(); |
112 | return error; |
113 | } |
114 | |
115 | /* default implementation */ |
116 | void __attribute__ ((weak)) arch_suspend_disable_irqs(void) |
117 | { |
118 | local_irq_disable(); |
119 | } |
120 | |
121 | /* default implementation */ |
122 | void __attribute__ ((weak)) arch_suspend_enable_irqs(void) |
123 | { |
124 | local_irq_enable(); |
125 | } |
126 | |
127 | /** |
128 | * suspend_enter - enter the desired system sleep state. |
129 | * @state: state to enter |
130 | * |
131 | * This function should be called after devices have been suspended. |
132 | */ |
133 | static int suspend_enter(suspend_state_t state) |
134 | { |
135 | int error; |
136 | |
137 | if (suspend_ops->prepare) { |
138 | error = suspend_ops->prepare(); |
139 | if (error) |
140 | goto Platform_finish; |
141 | } |
142 | |
143 | error = dpm_suspend_noirq(PMSG_SUSPEND); |
144 | if (error) { |
145 | printk(KERN_ERR "PM: Some devices failed to power down\n"); |
146 | goto Platform_finish; |
147 | } |
148 | |
149 | if (suspend_ops->prepare_late) { |
150 | error = suspend_ops->prepare_late(); |
151 | if (error) |
152 | goto Platform_wake; |
153 | } |
154 | |
155 | if (suspend_test(TEST_PLATFORM)) |
156 | goto Platform_wake; |
157 | |
158 | error = disable_nonboot_cpus(); |
159 | if (error || suspend_test(TEST_CPUS)) |
160 | goto Enable_cpus; |
161 | |
162 | arch_suspend_disable_irqs(); |
163 | BUG_ON(!irqs_disabled()); |
164 | |
165 | error = sysdev_suspend(PMSG_SUSPEND); |
166 | if (!error) { |
167 | if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { |
168 | error = suspend_ops->enter(state); |
169 | events_check_enabled = false; |
170 | } |
171 | sysdev_resume(); |
172 | } |
173 | |
174 | arch_suspend_enable_irqs(); |
175 | BUG_ON(irqs_disabled()); |
176 | |
177 | Enable_cpus: |
178 | enable_nonboot_cpus(); |
179 | |
180 | Platform_wake: |
181 | if (suspend_ops->wake) |
182 | suspend_ops->wake(); |
183 | |
184 | dpm_resume_noirq(PMSG_RESUME); |
185 | |
186 | Platform_finish: |
187 | if (suspend_ops->finish) |
188 | suspend_ops->finish(); |
189 | |
190 | return error; |
191 | } |
192 | |
193 | /** |
194 | * suspend_devices_and_enter - suspend devices and enter the desired system |
195 | * sleep state. |
196 | * @state: state to enter |
197 | */ |
198 | int suspend_devices_and_enter(suspend_state_t state) |
199 | { |
200 | int error; |
201 | |
202 | if (!suspend_ops) |
203 | return -ENOSYS; |
204 | |
205 | trace_machine_suspend(state); |
206 | if (suspend_ops->begin) { |
207 | error = suspend_ops->begin(state); |
208 | if (error) |
209 | goto Close; |
210 | } |
211 | suspend_console(); |
212 | pm_restrict_gfp_mask(); |
213 | suspend_test_start(); |
214 | error = dpm_suspend_start(PMSG_SUSPEND); |
215 | if (error) { |
216 | printk(KERN_ERR "PM: Some devices failed to suspend\n"); |
217 | goto Recover_platform; |
218 | } |
219 | suspend_test_finish("suspend devices"); |
220 | if (suspend_test(TEST_DEVICES)) |
221 | goto Recover_platform; |
222 | |
223 | suspend_enter(state); |
224 | |
225 | Resume_devices: |
226 | suspend_test_start(); |
227 | dpm_resume_end(PMSG_RESUME); |
228 | suspend_test_finish("resume devices"); |
229 | pm_restore_gfp_mask(); |
230 | resume_console(); |
231 | Close: |
232 | if (suspend_ops->end) |
233 | suspend_ops->end(); |
234 | trace_machine_suspend(PWR_EVENT_EXIT); |
235 | return error; |
236 | |
237 | Recover_platform: |
238 | if (suspend_ops->recover) |
239 | suspend_ops->recover(); |
240 | goto Resume_devices; |
241 | } |
242 | |
243 | /** |
244 | * suspend_finish - Do final work before exiting suspend sequence. |
245 | * |
246 | * Call platform code to clean up, restart processes, and free the |
247 | * console that we've allocated. This is not called for suspend-to-disk. |
248 | */ |
249 | static void suspend_finish(void) |
250 | { |
251 | suspend_thaw_processes(); |
252 | usermodehelper_enable(); |
253 | pm_notifier_call_chain(PM_POST_SUSPEND); |
254 | pm_restore_console(); |
255 | } |
256 | |
257 | /** |
258 | * enter_state - Do common work of entering low-power state. |
259 | * @state: pm_state structure for state we're entering. |
260 | * |
261 | * Make sure we're the only ones trying to enter a sleep state. Fail |
262 | * if someone has beat us to it, since we don't want anything weird to |
263 | * happen when we wake up. |
264 | * Then, do the setup for suspend, enter the state, and cleaup (after |
265 | * we've woken up). |
266 | */ |
267 | int enter_state(suspend_state_t state) |
268 | { |
269 | int error; |
270 | |
271 | if (!valid_state(state)) |
272 | return -ENODEV; |
273 | |
274 | if (!mutex_trylock(&pm_mutex)) |
275 | return -EBUSY; |
276 | |
277 | printk(KERN_INFO "PM: Syncing filesystems ... "); |
278 | sys_sync(); |
279 | printk("done.\n"); |
280 | |
281 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
282 | error = suspend_prepare(); |
283 | if (error) |
284 | goto Unlock; |
285 | |
286 | if (suspend_test(TEST_FREEZER)) |
287 | goto Finish; |
288 | |
289 | pr_debug("PM: Entering %s sleep\n", pm_states[state]); |
290 | error = suspend_devices_and_enter(state); |
291 | |
292 | Finish: |
293 | pr_debug("PM: Finishing wakeup.\n"); |
294 | suspend_finish(); |
295 | Unlock: |
296 | mutex_unlock(&pm_mutex); |
297 | return error; |
298 | } |
299 | |
300 | /** |
301 | * pm_suspend - Externally visible function for suspending system. |
302 | * @state: Enumerated value of state to enter. |
303 | * |
304 | * Determine whether or not value is within range, get state |
305 | * structure, and enter (above). |
306 | */ |
307 | int pm_suspend(suspend_state_t state) |
308 | { |
309 | if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) |
310 | return enter_state(state); |
311 | return -EINVAL; |
312 | } |
313 | EXPORT_SYMBOL(pm_suspend); |
314 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9