Commit cd59abfcc441b2abb4cf2cd62c1eb0f02a60e8dd
Committed by
Greg Kroah-Hartman
1 parent
6d66f5cd26
Exists in
master
and in
7 other branches
PM: merge device power-management source files
This patch (as993) merges the suspend.c and resume.c files in drivers/base/power into main.c, making some public symbols private. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Showing 5 changed files with 343 additions and 394 deletions Side-by-side Diff
drivers/base/power/Makefile
drivers/base/power/main.c
... | ... | @@ -20,19 +20,24 @@ |
20 | 20 | */ |
21 | 21 | |
22 | 22 | #include <linux/device.h> |
23 | +#include <linux/kallsyms.h> | |
23 | 24 | #include <linux/mutex.h> |
25 | +#include <linux/pm.h> | |
26 | +#include <linux/resume-trace.h> | |
24 | 27 | |
28 | +#include "../base.h" | |
25 | 29 | #include "power.h" |
26 | 30 | |
27 | 31 | LIST_HEAD(dpm_active); |
28 | -LIST_HEAD(dpm_off); | |
29 | -LIST_HEAD(dpm_off_irq); | |
32 | +static LIST_HEAD(dpm_off); | |
33 | +static LIST_HEAD(dpm_off_irq); | |
30 | 34 | |
31 | -DEFINE_MUTEX(dpm_mtx); | |
32 | -DEFINE_MUTEX(dpm_list_mtx); | |
35 | +static DEFINE_MUTEX(dpm_mtx); | |
36 | +static DEFINE_MUTEX(dpm_list_mtx); | |
33 | 37 | |
34 | 38 | int (*platform_enable_wakeup)(struct device *dev, int is_on); |
35 | 39 | |
40 | + | |
36 | 41 | int device_pm_add(struct device *dev) |
37 | 42 | { |
38 | 43 | int error; |
... | ... | @@ -59,4 +64,337 @@ |
59 | 64 | list_del_init(&dev->power.entry); |
60 | 65 | mutex_unlock(&dpm_list_mtx); |
61 | 66 | } |
67 | + | |
68 | + | |
69 | +/*------------------------- Resume routines -------------------------*/ | |
70 | + | |
71 | +/** | |
72 | + * resume_device - Restore state for one device. | |
73 | + * @dev: Device. | |
74 | + * | |
75 | + */ | |
76 | + | |
77 | +static int resume_device(struct device * dev) | |
78 | +{ | |
79 | + int error = 0; | |
80 | + | |
81 | + TRACE_DEVICE(dev); | |
82 | + TRACE_RESUME(0); | |
83 | + | |
84 | + down(&dev->sem); | |
85 | + | |
86 | + if (dev->bus && dev->bus->resume) { | |
87 | + dev_dbg(dev,"resuming\n"); | |
88 | + error = dev->bus->resume(dev); | |
89 | + } | |
90 | + | |
91 | + if (!error && dev->type && dev->type->resume) { | |
92 | + dev_dbg(dev,"resuming\n"); | |
93 | + error = dev->type->resume(dev); | |
94 | + } | |
95 | + | |
96 | + if (!error && dev->class && dev->class->resume) { | |
97 | + dev_dbg(dev,"class resume\n"); | |
98 | + error = dev->class->resume(dev); | |
99 | + } | |
100 | + | |
101 | + up(&dev->sem); | |
102 | + | |
103 | + TRACE_RESUME(error); | |
104 | + return error; | |
105 | +} | |
106 | + | |
107 | + | |
108 | +static int resume_device_early(struct device * dev) | |
109 | +{ | |
110 | + int error = 0; | |
111 | + | |
112 | + TRACE_DEVICE(dev); | |
113 | + TRACE_RESUME(0); | |
114 | + if (dev->bus && dev->bus->resume_early) { | |
115 | + dev_dbg(dev,"EARLY resume\n"); | |
116 | + error = dev->bus->resume_early(dev); | |
117 | + } | |
118 | + TRACE_RESUME(error); | |
119 | + return error; | |
120 | +} | |
121 | + | |
122 | +/* | |
123 | + * Resume the devices that have either not gone through | |
124 | + * the late suspend, or that did go through it but also | |
125 | + * went through the early resume | |
126 | + */ | |
127 | +static void dpm_resume(void) | |
128 | +{ | |
129 | + mutex_lock(&dpm_list_mtx); | |
130 | + while(!list_empty(&dpm_off)) { | |
131 | + struct list_head * entry = dpm_off.next; | |
132 | + struct device * dev = to_device(entry); | |
133 | + | |
134 | + get_device(dev); | |
135 | + list_move_tail(entry, &dpm_active); | |
136 | + | |
137 | + mutex_unlock(&dpm_list_mtx); | |
138 | + resume_device(dev); | |
139 | + mutex_lock(&dpm_list_mtx); | |
140 | + put_device(dev); | |
141 | + } | |
142 | + mutex_unlock(&dpm_list_mtx); | |
143 | +} | |
144 | + | |
145 | + | |
146 | +/** | |
147 | + * device_resume - Restore state of each device in system. | |
148 | + * | |
149 | + * Walk the dpm_off list, remove each entry, resume the device, | |
150 | + * then add it to the dpm_active list. | |
151 | + */ | |
152 | + | |
153 | +void device_resume(void) | |
154 | +{ | |
155 | + might_sleep(); | |
156 | + mutex_lock(&dpm_mtx); | |
157 | + dpm_resume(); | |
158 | + mutex_unlock(&dpm_mtx); | |
159 | +} | |
160 | + | |
161 | +EXPORT_SYMBOL_GPL(device_resume); | |
162 | + | |
163 | + | |
164 | +/** | |
165 | + * dpm_power_up - Power on some devices. | |
166 | + * | |
167 | + * Walk the dpm_off_irq list and power each device up. This | |
168 | + * is used for devices that required they be powered down with | |
169 | + * interrupts disabled. As devices are powered on, they are moved | |
170 | + * to the dpm_active list. | |
171 | + * | |
172 | + * Interrupts must be disabled when calling this. | |
173 | + */ | |
174 | + | |
175 | +static void dpm_power_up(void) | |
176 | +{ | |
177 | + while(!list_empty(&dpm_off_irq)) { | |
178 | + struct list_head * entry = dpm_off_irq.next; | |
179 | + struct device * dev = to_device(entry); | |
180 | + | |
181 | + list_move_tail(entry, &dpm_off); | |
182 | + resume_device_early(dev); | |
183 | + } | |
184 | +} | |
185 | + | |
186 | + | |
187 | +/** | |
188 | + * device_power_up - Turn on all devices that need special attention. | |
189 | + * | |
190 | + * Power on system devices then devices that required we shut them down | |
191 | + * with interrupts disabled. | |
192 | + * Called with interrupts disabled. | |
193 | + */ | |
194 | + | |
195 | +void device_power_up(void) | |
196 | +{ | |
197 | + sysdev_resume(); | |
198 | + dpm_power_up(); | |
199 | +} | |
200 | + | |
201 | +EXPORT_SYMBOL_GPL(device_power_up); | |
202 | + | |
203 | + | |
204 | +/*------------------------- Suspend routines -------------------------*/ | |
205 | + | |
206 | +/* | |
207 | + * The entries in the dpm_active list are in a depth first order, simply | |
208 | + * because children are guaranteed to be discovered after parents, and | |
209 | + * are inserted at the back of the list on discovery. | |
210 | + * | |
211 | + * All list on the suspend path are done in reverse order, so we operate | |
212 | + * on the leaves of the device tree (or forests, depending on how you want | |
213 | + * to look at it ;) first. As nodes are removed from the back of the list, | |
214 | + * they are inserted into the front of their destintation lists. | |
215 | + * | |
216 | + * Things are the reverse on the resume path - iterations are done in | |
217 | + * forward order, and nodes are inserted at the back of their destination | |
218 | + * lists. This way, the ancestors will be accessed before their descendents. | |
219 | + */ | |
220 | + | |
221 | +static inline char *suspend_verb(u32 event) | |
222 | +{ | |
223 | + switch (event) { | |
224 | + case PM_EVENT_SUSPEND: return "suspend"; | |
225 | + case PM_EVENT_FREEZE: return "freeze"; | |
226 | + case PM_EVENT_PRETHAW: return "prethaw"; | |
227 | + default: return "(unknown suspend event)"; | |
228 | + } | |
229 | +} | |
230 | + | |
231 | + | |
232 | +static void | |
233 | +suspend_device_dbg(struct device *dev, pm_message_t state, char *info) | |
234 | +{ | |
235 | + dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), | |
236 | + ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? | |
237 | + ", may wakeup" : ""); | |
238 | +} | |
239 | + | |
240 | +/** | |
241 | + * suspend_device - Save state of one device. | |
242 | + * @dev: Device. | |
243 | + * @state: Power state device is entering. | |
244 | + */ | |
245 | + | |
246 | +static int suspend_device(struct device * dev, pm_message_t state) | |
247 | +{ | |
248 | + int error = 0; | |
249 | + | |
250 | + down(&dev->sem); | |
251 | + if (dev->power.power_state.event) { | |
252 | + dev_dbg(dev, "PM: suspend %d-->%d\n", | |
253 | + dev->power.power_state.event, state.event); | |
254 | + } | |
255 | + | |
256 | + if (dev->class && dev->class->suspend) { | |
257 | + suspend_device_dbg(dev, state, "class "); | |
258 | + error = dev->class->suspend(dev, state); | |
259 | + suspend_report_result(dev->class->suspend, error); | |
260 | + } | |
261 | + | |
262 | + if (!error && dev->type && dev->type->suspend) { | |
263 | + suspend_device_dbg(dev, state, "type "); | |
264 | + error = dev->type->suspend(dev, state); | |
265 | + suspend_report_result(dev->type->suspend, error); | |
266 | + } | |
267 | + | |
268 | + if (!error && dev->bus && dev->bus->suspend) { | |
269 | + suspend_device_dbg(dev, state, ""); | |
270 | + error = dev->bus->suspend(dev, state); | |
271 | + suspend_report_result(dev->bus->suspend, error); | |
272 | + } | |
273 | + up(&dev->sem); | |
274 | + return error; | |
275 | +} | |
276 | + | |
277 | + | |
278 | +/* | |
279 | + * This is called with interrupts off, only a single CPU | |
280 | + * running. We can't acquire a mutex or semaphore (and we don't | |
281 | + * need the protection) | |
282 | + */ | |
283 | +static int suspend_device_late(struct device *dev, pm_message_t state) | |
284 | +{ | |
285 | + int error = 0; | |
286 | + | |
287 | + if (dev->bus && dev->bus->suspend_late) { | |
288 | + suspend_device_dbg(dev, state, "LATE "); | |
289 | + error = dev->bus->suspend_late(dev, state); | |
290 | + suspend_report_result(dev->bus->suspend_late, error); | |
291 | + } | |
292 | + return error; | |
293 | +} | |
294 | + | |
295 | +/** | |
296 | + * device_suspend - Save state and stop all devices in system. | |
297 | + * @state: Power state to put each device in. | |
298 | + * | |
299 | + * Walk the dpm_active list, call ->suspend() for each device, and move | |
300 | + * it to the dpm_off list. | |
301 | + * | |
302 | + * (For historical reasons, if it returns -EAGAIN, that used to mean | |
303 | + * that the device would be called again with interrupts disabled. | |
304 | + * These days, we use the "suspend_late()" callback for that, so we | |
305 | + * print a warning and consider it an error). | |
306 | + * | |
307 | + * If we get a different error, try and back out. | |
308 | + * | |
309 | + * If we hit a failure with any of the devices, call device_resume() | |
310 | + * above to bring the suspended devices back to life. | |
311 | + * | |
312 | + */ | |
313 | + | |
314 | +int device_suspend(pm_message_t state) | |
315 | +{ | |
316 | + int error = 0; | |
317 | + | |
318 | + might_sleep(); | |
319 | + mutex_lock(&dpm_mtx); | |
320 | + mutex_lock(&dpm_list_mtx); | |
321 | + while (!list_empty(&dpm_active) && error == 0) { | |
322 | + struct list_head * entry = dpm_active.prev; | |
323 | + struct device * dev = to_device(entry); | |
324 | + | |
325 | + get_device(dev); | |
326 | + mutex_unlock(&dpm_list_mtx); | |
327 | + | |
328 | + error = suspend_device(dev, state); | |
329 | + | |
330 | + mutex_lock(&dpm_list_mtx); | |
331 | + | |
332 | + /* Check if the device got removed */ | |
333 | + if (!list_empty(&dev->power.entry)) { | |
334 | + /* Move it to the dpm_off list */ | |
335 | + if (!error) | |
336 | + list_move(&dev->power.entry, &dpm_off); | |
337 | + } | |
338 | + if (error) | |
339 | + printk(KERN_ERR "Could not suspend device %s: " | |
340 | + "error %d%s\n", | |
341 | + kobject_name(&dev->kobj), error, | |
342 | + error == -EAGAIN ? " (please convert to suspend_late)" : ""); | |
343 | + put_device(dev); | |
344 | + } | |
345 | + mutex_unlock(&dpm_list_mtx); | |
346 | + if (error) | |
347 | + dpm_resume(); | |
348 | + | |
349 | + mutex_unlock(&dpm_mtx); | |
350 | + return error; | |
351 | +} | |
352 | + | |
353 | +EXPORT_SYMBOL_GPL(device_suspend); | |
354 | + | |
355 | +/** | |
356 | + * device_power_down - Shut down special devices. | |
357 | + * @state: Power state to enter. | |
358 | + * | |
359 | + * Walk the dpm_off_irq list, calling ->power_down() for each device that | |
360 | + * couldn't power down the device with interrupts enabled. When we're | |
361 | + * done, power down system devices. | |
362 | + */ | |
363 | + | |
364 | +int device_power_down(pm_message_t state) | |
365 | +{ | |
366 | + int error = 0; | |
367 | + struct device * dev; | |
368 | + | |
369 | + while (!list_empty(&dpm_off)) { | |
370 | + struct list_head * entry = dpm_off.prev; | |
371 | + | |
372 | + dev = to_device(entry); | |
373 | + error = suspend_device_late(dev, state); | |
374 | + if (error) | |
375 | + goto Error; | |
376 | + list_move(&dev->power.entry, &dpm_off_irq); | |
377 | + } | |
378 | + | |
379 | + error = sysdev_suspend(state); | |
380 | + Done: | |
381 | + return error; | |
382 | + Error: | |
383 | + printk(KERN_ERR "Could not power down device %s: " | |
384 | + "error %d\n", kobject_name(&dev->kobj), error); | |
385 | + dpm_power_up(); | |
386 | + goto Done; | |
387 | +} | |
388 | + | |
389 | +EXPORT_SYMBOL_GPL(device_power_down); | |
390 | + | |
391 | +void __suspend_report_result(const char *function, void *fn, int ret) | |
392 | +{ | |
393 | + if (ret) { | |
394 | + printk(KERN_ERR "%s(): ", function); | |
395 | + print_fn_descriptor_symbol("%s() returns ", (unsigned long)fn); | |
396 | + printk("%d\n", ret); | |
397 | + } | |
398 | +} | |
399 | +EXPORT_SYMBOL_GPL(__suspend_report_result); |
drivers/base/power/power.h
... | ... | @@ -11,32 +11,11 @@ |
11 | 11 | * main.c |
12 | 12 | */ |
13 | 13 | |
14 | -/* | |
15 | - * Used to synchronize global power management operations. | |
16 | - */ | |
17 | -extern struct mutex dpm_mtx; | |
14 | +extern struct list_head dpm_active; /* The active device list */ | |
18 | 15 | |
19 | -/* | |
20 | - * Used to serialize changes to the dpm_* lists. | |
21 | - */ | |
22 | -extern struct mutex dpm_list_mtx; | |
23 | - | |
24 | -/* | |
25 | - * The PM lists. | |
26 | - */ | |
27 | -extern struct list_head dpm_active; | |
28 | -extern struct list_head dpm_off; | |
29 | -extern struct list_head dpm_off_irq; | |
30 | - | |
31 | - | |
32 | -static inline struct dev_pm_info * to_pm_info(struct list_head * entry) | |
33 | -{ | |
34 | - return container_of(entry, struct dev_pm_info, entry); | |
35 | -} | |
36 | - | |
37 | 16 | static inline struct device * to_device(struct list_head * entry) |
38 | 17 | { |
39 | - return container_of(to_pm_info(entry), struct device, power); | |
18 | + return container_of(entry, struct device, power.entry); | |
40 | 19 | } |
41 | 20 | |
42 | 21 | extern int device_pm_add(struct device *); |
... | ... | @@ -48,13 +27,6 @@ |
48 | 27 | |
49 | 28 | extern int dpm_sysfs_add(struct device *); |
50 | 29 | extern void dpm_sysfs_remove(struct device *); |
51 | - | |
52 | -/* | |
53 | - * resume.c | |
54 | - */ | |
55 | - | |
56 | -extern void dpm_resume(void); | |
57 | -extern void dpm_power_up(void); | |
58 | 30 | |
59 | 31 | #else /* CONFIG_PM_SLEEP */ |
60 | 32 |
drivers/base/power/resume.c
1 | -/* | |
2 | - * resume.c - Functions for waking devices up. | |
3 | - * | |
4 | - * Copyright (c) 2003 Patrick Mochel | |
5 | - * Copyright (c) 2003 Open Source Development Labs | |
6 | - * | |
7 | - * This file is released under the GPLv2 | |
8 | - * | |
9 | - */ | |
10 | - | |
11 | -#include <linux/device.h> | |
12 | -#include <linux/resume-trace.h> | |
13 | -#include "../base.h" | |
14 | -#include "power.h" | |
15 | - | |
16 | - | |
17 | -/** | |
18 | - * resume_device - Restore state for one device. | |
19 | - * @dev: Device. | |
20 | - * | |
21 | - */ | |
22 | - | |
23 | -static int resume_device(struct device * dev) | |
24 | -{ | |
25 | - int error = 0; | |
26 | - | |
27 | - TRACE_DEVICE(dev); | |
28 | - TRACE_RESUME(0); | |
29 | - | |
30 | - down(&dev->sem); | |
31 | - | |
32 | - if (dev->bus && dev->bus->resume) { | |
33 | - dev_dbg(dev,"resuming\n"); | |
34 | - error = dev->bus->resume(dev); | |
35 | - } | |
36 | - | |
37 | - if (!error && dev->type && dev->type->resume) { | |
38 | - dev_dbg(dev,"resuming\n"); | |
39 | - error = dev->type->resume(dev); | |
40 | - } | |
41 | - | |
42 | - if (!error && dev->class && dev->class->resume) { | |
43 | - dev_dbg(dev,"class resume\n"); | |
44 | - error = dev->class->resume(dev); | |
45 | - } | |
46 | - | |
47 | - up(&dev->sem); | |
48 | - | |
49 | - TRACE_RESUME(error); | |
50 | - return error; | |
51 | -} | |
52 | - | |
53 | - | |
54 | -static int resume_device_early(struct device * dev) | |
55 | -{ | |
56 | - int error = 0; | |
57 | - | |
58 | - TRACE_DEVICE(dev); | |
59 | - TRACE_RESUME(0); | |
60 | - if (dev->bus && dev->bus->resume_early) { | |
61 | - dev_dbg(dev,"EARLY resume\n"); | |
62 | - error = dev->bus->resume_early(dev); | |
63 | - } | |
64 | - TRACE_RESUME(error); | |
65 | - return error; | |
66 | -} | |
67 | - | |
68 | -/* | |
69 | - * Resume the devices that have either not gone through | |
70 | - * the late suspend, or that did go through it but also | |
71 | - * went through the early resume | |
72 | - */ | |
73 | -void dpm_resume(void) | |
74 | -{ | |
75 | - mutex_lock(&dpm_list_mtx); | |
76 | - while(!list_empty(&dpm_off)) { | |
77 | - struct list_head * entry = dpm_off.next; | |
78 | - struct device * dev = to_device(entry); | |
79 | - | |
80 | - get_device(dev); | |
81 | - list_move_tail(entry, &dpm_active); | |
82 | - | |
83 | - mutex_unlock(&dpm_list_mtx); | |
84 | - resume_device(dev); | |
85 | - mutex_lock(&dpm_list_mtx); | |
86 | - put_device(dev); | |
87 | - } | |
88 | - mutex_unlock(&dpm_list_mtx); | |
89 | -} | |
90 | - | |
91 | - | |
92 | -/** | |
93 | - * device_resume - Restore state of each device in system. | |
94 | - * | |
95 | - * Walk the dpm_off list, remove each entry, resume the device, | |
96 | - * then add it to the dpm_active list. | |
97 | - */ | |
98 | - | |
99 | -void device_resume(void) | |
100 | -{ | |
101 | - might_sleep(); | |
102 | - mutex_lock(&dpm_mtx); | |
103 | - dpm_resume(); | |
104 | - mutex_unlock(&dpm_mtx); | |
105 | -} | |
106 | - | |
107 | -EXPORT_SYMBOL_GPL(device_resume); | |
108 | - | |
109 | - | |
110 | -/** | |
111 | - * dpm_power_up - Power on some devices. | |
112 | - * | |
113 | - * Walk the dpm_off_irq list and power each device up. This | |
114 | - * is used for devices that required they be powered down with | |
115 | - * interrupts disabled. As devices are powered on, they are moved | |
116 | - * to the dpm_active list. | |
117 | - * | |
118 | - * Interrupts must be disabled when calling this. | |
119 | - */ | |
120 | - | |
121 | -void dpm_power_up(void) | |
122 | -{ | |
123 | - while(!list_empty(&dpm_off_irq)) { | |
124 | - struct list_head * entry = dpm_off_irq.next; | |
125 | - struct device * dev = to_device(entry); | |
126 | - | |
127 | - list_move_tail(entry, &dpm_off); | |
128 | - resume_device_early(dev); | |
129 | - } | |
130 | -} | |
131 | - | |
132 | - | |
133 | -/** | |
134 | - * device_power_up - Turn on all devices that need special attention. | |
135 | - * | |
136 | - * Power on system devices then devices that required we shut them down | |
137 | - * with interrupts disabled. | |
138 | - * Called with interrupts disabled. | |
139 | - */ | |
140 | - | |
141 | -void device_power_up(void) | |
142 | -{ | |
143 | - sysdev_resume(); | |
144 | - dpm_power_up(); | |
145 | -} | |
146 | - | |
147 | -EXPORT_SYMBOL_GPL(device_power_up); |
drivers/base/power/suspend.c
1 | -/* | |
2 | - * suspend.c - Functions for putting devices to sleep. | |
3 | - * | |
4 | - * Copyright (c) 2003 Patrick Mochel | |
5 | - * Copyright (c) 2003 Open Source Development Labs | |
6 | - * | |
7 | - * This file is released under the GPLv2 | |
8 | - * | |
9 | - */ | |
10 | - | |
11 | -#include <linux/device.h> | |
12 | -#include <linux/kallsyms.h> | |
13 | -#include <linux/pm.h> | |
14 | -#include "../base.h" | |
15 | -#include "power.h" | |
16 | - | |
17 | -/* | |
18 | - * The entries in the dpm_active list are in a depth first order, simply | |
19 | - * because children are guaranteed to be discovered after parents, and | |
20 | - * are inserted at the back of the list on discovery. | |
21 | - * | |
22 | - * All list on the suspend path are done in reverse order, so we operate | |
23 | - * on the leaves of the device tree (or forests, depending on how you want | |
24 | - * to look at it ;) first. As nodes are removed from the back of the list, | |
25 | - * they are inserted into the front of their destintation lists. | |
26 | - * | |
27 | - * Things are the reverse on the resume path - iterations are done in | |
28 | - * forward order, and nodes are inserted at the back of their destination | |
29 | - * lists. This way, the ancestors will be accessed before their descendents. | |
30 | - */ | |
31 | - | |
32 | -static inline char *suspend_verb(u32 event) | |
33 | -{ | |
34 | - switch (event) { | |
35 | - case PM_EVENT_SUSPEND: return "suspend"; | |
36 | - case PM_EVENT_FREEZE: return "freeze"; | |
37 | - case PM_EVENT_PRETHAW: return "prethaw"; | |
38 | - default: return "(unknown suspend event)"; | |
39 | - } | |
40 | -} | |
41 | - | |
42 | - | |
43 | -static void | |
44 | -suspend_device_dbg(struct device *dev, pm_message_t state, char *info) | |
45 | -{ | |
46 | - dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), | |
47 | - ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? | |
48 | - ", may wakeup" : ""); | |
49 | -} | |
50 | - | |
51 | -/** | |
52 | - * suspend_device - Save state of one device. | |
53 | - * @dev: Device. | |
54 | - * @state: Power state device is entering. | |
55 | - */ | |
56 | - | |
57 | -static int suspend_device(struct device * dev, pm_message_t state) | |
58 | -{ | |
59 | - int error = 0; | |
60 | - | |
61 | - down(&dev->sem); | |
62 | - if (dev->power.power_state.event) { | |
63 | - dev_dbg(dev, "PM: suspend %d-->%d\n", | |
64 | - dev->power.power_state.event, state.event); | |
65 | - } | |
66 | - | |
67 | - if (dev->class && dev->class->suspend) { | |
68 | - suspend_device_dbg(dev, state, "class "); | |
69 | - error = dev->class->suspend(dev, state); | |
70 | - suspend_report_result(dev->class->suspend, error); | |
71 | - } | |
72 | - | |
73 | - if (!error && dev->type && dev->type->suspend) { | |
74 | - suspend_device_dbg(dev, state, "type "); | |
75 | - error = dev->type->suspend(dev, state); | |
76 | - suspend_report_result(dev->type->suspend, error); | |
77 | - } | |
78 | - | |
79 | - if (!error && dev->bus && dev->bus->suspend) { | |
80 | - suspend_device_dbg(dev, state, ""); | |
81 | - error = dev->bus->suspend(dev, state); | |
82 | - suspend_report_result(dev->bus->suspend, error); | |
83 | - } | |
84 | - up(&dev->sem); | |
85 | - return error; | |
86 | -} | |
87 | - | |
88 | - | |
89 | -/* | |
90 | - * This is called with interrupts off, only a single CPU | |
91 | - * running. We can't acquire a mutex or semaphore (and we don't | |
92 | - * need the protection) | |
93 | - */ | |
94 | -static int suspend_device_late(struct device *dev, pm_message_t state) | |
95 | -{ | |
96 | - int error = 0; | |
97 | - | |
98 | - if (dev->bus && dev->bus->suspend_late) { | |
99 | - suspend_device_dbg(dev, state, "LATE "); | |
100 | - error = dev->bus->suspend_late(dev, state); | |
101 | - suspend_report_result(dev->bus->suspend_late, error); | |
102 | - } | |
103 | - return error; | |
104 | -} | |
105 | - | |
106 | -/** | |
107 | - * device_suspend - Save state and stop all devices in system. | |
108 | - * @state: Power state to put each device in. | |
109 | - * | |
110 | - * Walk the dpm_active list, call ->suspend() for each device, and move | |
111 | - * it to the dpm_off list. | |
112 | - * | |
113 | - * (For historical reasons, if it returns -EAGAIN, that used to mean | |
114 | - * that the device would be called again with interrupts disabled. | |
115 | - * These days, we use the "suspend_late()" callback for that, so we | |
116 | - * print a warning and consider it an error). | |
117 | - * | |
118 | - * If we get a different error, try and back out. | |
119 | - * | |
120 | - * If we hit a failure with any of the devices, call device_resume() | |
121 | - * above to bring the suspended devices back to life. | |
122 | - * | |
123 | - */ | |
124 | - | |
125 | -int device_suspend(pm_message_t state) | |
126 | -{ | |
127 | - int error = 0; | |
128 | - | |
129 | - might_sleep(); | |
130 | - mutex_lock(&dpm_mtx); | |
131 | - mutex_lock(&dpm_list_mtx); | |
132 | - while (!list_empty(&dpm_active) && error == 0) { | |
133 | - struct list_head * entry = dpm_active.prev; | |
134 | - struct device * dev = to_device(entry); | |
135 | - | |
136 | - get_device(dev); | |
137 | - mutex_unlock(&dpm_list_mtx); | |
138 | - | |
139 | - error = suspend_device(dev, state); | |
140 | - | |
141 | - mutex_lock(&dpm_list_mtx); | |
142 | - | |
143 | - /* Check if the device got removed */ | |
144 | - if (!list_empty(&dev->power.entry)) { | |
145 | - /* Move it to the dpm_off list */ | |
146 | - if (!error) | |
147 | - list_move(&dev->power.entry, &dpm_off); | |
148 | - } | |
149 | - if (error) | |
150 | - printk(KERN_ERR "Could not suspend device %s: " | |
151 | - "error %d%s\n", | |
152 | - kobject_name(&dev->kobj), error, | |
153 | - error == -EAGAIN ? " (please convert to suspend_late)" : ""); | |
154 | - put_device(dev); | |
155 | - } | |
156 | - mutex_unlock(&dpm_list_mtx); | |
157 | - if (error) | |
158 | - dpm_resume(); | |
159 | - | |
160 | - mutex_unlock(&dpm_mtx); | |
161 | - return error; | |
162 | -} | |
163 | - | |
164 | -EXPORT_SYMBOL_GPL(device_suspend); | |
165 | - | |
166 | -/** | |
167 | - * device_power_down - Shut down special devices. | |
168 | - * @state: Power state to enter. | |
169 | - * | |
170 | - * Walk the dpm_off_irq list, calling ->power_down() for each device that | |
171 | - * couldn't power down the device with interrupts enabled. When we're | |
172 | - * done, power down system devices. | |
173 | - */ | |
174 | - | |
175 | -int device_power_down(pm_message_t state) | |
176 | -{ | |
177 | - int error = 0; | |
178 | - struct device * dev; | |
179 | - | |
180 | - while (!list_empty(&dpm_off)) { | |
181 | - struct list_head * entry = dpm_off.prev; | |
182 | - | |
183 | - dev = to_device(entry); | |
184 | - error = suspend_device_late(dev, state); | |
185 | - if (error) | |
186 | - goto Error; | |
187 | - list_move(&dev->power.entry, &dpm_off_irq); | |
188 | - } | |
189 | - | |
190 | - error = sysdev_suspend(state); | |
191 | - Done: | |
192 | - return error; | |
193 | - Error: | |
194 | - printk(KERN_ERR "Could not power down device %s: " | |
195 | - "error %d\n", kobject_name(&dev->kobj), error); | |
196 | - dpm_power_up(); | |
197 | - goto Done; | |
198 | -} | |
199 | - | |
200 | -EXPORT_SYMBOL_GPL(device_power_down); | |
201 | - | |
202 | -void __suspend_report_result(const char *function, void *fn, int ret) | |
203 | -{ | |
204 | - if (ret) { | |
205 | - printk(KERN_ERR "%s(): ", function); | |
206 | - print_fn_descriptor_symbol("%s() returns ", (unsigned long)fn); | |
207 | - printk("%d\n", ret); | |
208 | - } | |
209 | -} | |
210 | -EXPORT_SYMBOL_GPL(__suspend_report_result); |