diff options
Diffstat (limited to 'drivers/base/power')
-rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/base/power/main.c | 344 | ||||
-rw-r--r-- | drivers/base/power/power.h | 32 | ||||
-rw-r--r-- | drivers/base/power/resume.c | 149 | ||||
-rw-r--r-- | drivers/base/power/suspend.c | 210 |
5 files changed, 343 insertions, 394 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 9caeaea753a3..a803733c839e 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-y := shutdown.o | 1 | obj-y := shutdown.o |
2 | obj-$(CONFIG_PM_SLEEP) += main.o suspend.o resume.o sysfs.o | 2 | obj-$(CONFIG_PM_SLEEP) += main.o sysfs.o |
3 | obj-$(CONFIG_PM_TRACE) += trace.o | 3 | obj-$(CONFIG_PM_TRACE) += trace.o |
4 | 4 | ||
5 | ifeq ($(CONFIG_DEBUG_DRIVER),y) | 5 | ifeq ($(CONFIG_DEBUG_DRIVER),y) |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index eb9f38d0aa58..0ab4ab21f564 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -20,19 +20,24 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/device.h> | 22 | #include <linux/device.h> |
23 | #include <linux/kallsyms.h> | ||
23 | #include <linux/mutex.h> | 24 | #include <linux/mutex.h> |
25 | #include <linux/pm.h> | ||
26 | #include <linux/resume-trace.h> | ||
24 | 27 | ||
28 | #include "../base.h" | ||
25 | #include "power.h" | 29 | #include "power.h" |
26 | 30 | ||
27 | LIST_HEAD(dpm_active); | 31 | LIST_HEAD(dpm_active); |
28 | LIST_HEAD(dpm_off); | 32 | static LIST_HEAD(dpm_off); |
29 | LIST_HEAD(dpm_off_irq); | 33 | static LIST_HEAD(dpm_off_irq); |
30 | 34 | ||
31 | DEFINE_MUTEX(dpm_mtx); | 35 | static DEFINE_MUTEX(dpm_mtx); |
32 | DEFINE_MUTEX(dpm_list_mtx); | 36 | static DEFINE_MUTEX(dpm_list_mtx); |
33 | 37 | ||
34 | int (*platform_enable_wakeup)(struct device *dev, int is_on); | 38 | int (*platform_enable_wakeup)(struct device *dev, int is_on); |
35 | 39 | ||
40 | |||
36 | int device_pm_add(struct device *dev) | 41 | int device_pm_add(struct device *dev) |
37 | { | 42 | { |
38 | int error; | 43 | int error; |
@@ -61,3 +66,334 @@ void device_pm_remove(struct device *dev) | |||
61 | } | 66 | } |
62 | 67 | ||
63 | 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); | ||
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 49e64dbcce96..5c4efd493fa5 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -11,32 +11,11 @@ extern void device_shutdown(void); | |||
11 | * main.c | 11 | * main.c |
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* | 14 | extern struct list_head dpm_active; /* The active device list */ |
15 | * Used to synchronize global power management operations. | ||
16 | */ | ||
17 | extern struct mutex dpm_mtx; | ||
18 | |||
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 | 15 | ||
37 | static inline struct device * to_device(struct list_head * entry) | 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 | extern int device_pm_add(struct device *); | 21 | extern int device_pm_add(struct device *); |
@@ -49,13 +28,6 @@ extern void device_pm_remove(struct device *); | |||
49 | extern int dpm_sysfs_add(struct device *); | 28 | extern int dpm_sysfs_add(struct device *); |
50 | extern void dpm_sysfs_remove(struct device *); | 29 | extern void dpm_sysfs_remove(struct device *); |
51 | 30 | ||
52 | /* | ||
53 | * resume.c | ||
54 | */ | ||
55 | |||
56 | extern void dpm_resume(void); | ||
57 | extern void dpm_power_up(void); | ||
58 | |||
59 | #else /* CONFIG_PM_SLEEP */ | 31 | #else /* CONFIG_PM_SLEEP */ |
60 | 32 | ||
61 | 33 | ||
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c deleted file mode 100644 index 67948bac9938..000000000000 --- a/drivers/base/power/resume.c +++ /dev/null | |||
@@ -1,149 +0,0 @@ | |||
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); | ||
148 | |||
149 | |||
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c deleted file mode 100644 index 204517afb15e..000000000000 --- a/drivers/base/power/suspend.c +++ /dev/null | |||
@@ -1,210 +0,0 @@ | |||
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); | ||