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