Blame view
drivers/base/power/main.c
26.7 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 |
/* * drivers/base/power/main.c - Where the driver meets power management. * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * * This file is released under the GPLv2 * * * The driver model core calls device_pm_add() when a device is registered. |
b595076a1 tree-wide: fix co... |
11 |
* This will initialize the embedded device_pm_info object in the device |
1da177e4c Linux-2.6.12-rc2 |
12 13 14 |
* and add it to the list of power-controlled devices. sysfs entries for * controlling device power management will also be added. * |
1eede070a Introduce new top... |
15 16 17 |
* A separate list is used for keeping track of power info, because the power * domain dependencies may differ from the ancestral dependencies that the * subsystem list maintains. |
1da177e4c Linux-2.6.12-rc2 |
18 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
19 |
#include <linux/device.h> |
cd59abfcc PM: merge device ... |
20 |
#include <linux/kallsyms.h> |
1b6bc32f0 drivers/base: Add... |
21 |
#include <linux/export.h> |
11048dcf3 Power Management:... |
22 |
#include <linux/mutex.h> |
cd59abfcc PM: merge device ... |
23 |
#include <linux/pm.h> |
5e928f77a PM: Introduce cor... |
24 |
#include <linux/pm_runtime.h> |
cd59abfcc PM: merge device ... |
25 |
#include <linux/resume-trace.h> |
2ed8d2b3a PM: Rework handli... |
26 |
#include <linux/interrupt.h> |
f25117748 PM: Add initcall_... |
27 |
#include <linux/sched.h> |
5af84b827 PM: Asynchronous ... |
28 |
#include <linux/async.h> |
1e75227ef PM: Prevent dpm_p... |
29 |
#include <linux/suspend.h> |
11048dcf3 Power Management:... |
30 |
|
cd59abfcc PM: merge device ... |
31 |
#include "../base.h" |
1da177e4c Linux-2.6.12-rc2 |
32 |
#include "power.h" |
9cf519d1c PM / Sleep: Make ... |
33 |
typedef int (*pm_callback_t)(struct device *); |
775b64d2b PM: Acquire devic... |
34 |
/* |
1eede070a Introduce new top... |
35 |
* The entries in the dpm_list list are in a depth first order, simply |
775b64d2b PM: Acquire devic... |
36 37 38 |
* because children are guaranteed to be discovered after parents, and * are inserted at the back of the list on discovery. * |
8e9394ce2 Driver core: crea... |
39 40 |
* Since device_pm_add() may be called with a device lock held, * we must never try to acquire a device lock while holding |
775b64d2b PM: Acquire devic... |
41 42 |
* dpm_list_mutex. */ |
1eede070a Introduce new top... |
43 |
LIST_HEAD(dpm_list); |
8a43a9ab7 PM: Use a differe... |
44 45 46 |
LIST_HEAD(dpm_prepared_list); LIST_HEAD(dpm_suspended_list); LIST_HEAD(dpm_noirq_list); |
1da177e4c Linux-2.6.12-rc2 |
47 |
|
2a77c46de PM / Suspend: Add... |
48 |
struct suspend_stats suspend_stats; |
cd59abfcc PM: merge device ... |
49 |
static DEFINE_MUTEX(dpm_list_mtx); |
5af84b827 PM: Asynchronous ... |
50 |
static pm_message_t pm_transition; |
1da177e4c Linux-2.6.12-rc2 |
51 |
|
098dff738 PM: Fix potential... |
52 |
static int async_error; |
1eede070a Introduce new top... |
53 |
/** |
20d652d7d PM: Update kernel... |
54 |
* device_pm_init - Initialize the PM-related part of a device object. |
5e928f77a PM: Introduce cor... |
55 56 57 58 |
* @dev: Device object being initialized. */ void device_pm_init(struct device *dev) { |
f76b168b6 PM: Rename dev_pm... |
59 |
dev->power.is_prepared = false; |
6d0e0e84f PM: Fix async res... |
60 |
dev->power.is_suspended = false; |
5af84b827 PM: Asynchronous ... |
61 |
init_completion(&dev->power.completion); |
152e1d592 PM: Prevent waiti... |
62 |
complete_all(&dev->power.completion); |
074037ec7 PM / Wakeup: Intr... |
63 64 |
dev->power.wakeup = NULL; spin_lock_init(&dev->power.lock); |
5e928f77a PM: Introduce cor... |
65 |
pm_runtime_init(dev); |
22110faf8 PM / Wakeup: Fix ... |
66 |
INIT_LIST_HEAD(&dev->power.entry); |
1a9a91525 PM / QoS: Add fun... |
67 |
dev->power.power_state = PMSG_INVALID; |
5e928f77a PM: Introduce cor... |
68 69 70 |
} /** |
20d652d7d PM: Update kernel... |
71 |
* device_pm_lock - Lock the list of active devices used by the PM core. |
1eede070a Introduce new top... |
72 73 74 75 76 77 78 |
*/ void device_pm_lock(void) { mutex_lock(&dpm_list_mtx); } /** |
20d652d7d PM: Update kernel... |
79 |
* device_pm_unlock - Unlock the list of active devices used by the PM core. |
1eede070a Introduce new top... |
80 81 82 83 84 |
*/ void device_pm_unlock(void) { mutex_unlock(&dpm_list_mtx); } |
075c17715 define platform w... |
85 |
|
775b64d2b PM: Acquire devic... |
86 |
/** |
20d652d7d PM: Update kernel... |
87 88 |
* device_pm_add - Add a device to the PM core's list of active devices. * @dev: Device to add to the list. |
775b64d2b PM: Acquire devic... |
89 |
*/ |
3b98aeaf3 PM: don't skip de... |
90 |
void device_pm_add(struct device *dev) |
1da177e4c Linux-2.6.12-rc2 |
91 |
{ |
1da177e4c Linux-2.6.12-rc2 |
92 93 |
pr_debug("PM: Adding info for %s:%s ", |
5c1a07ab3 PM: Use dev_name(... |
94 |
dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
11048dcf3 Power Management:... |
95 |
mutex_lock(&dpm_list_mtx); |
f76b168b6 PM: Rename dev_pm... |
96 |
if (dev->parent && dev->parent->power.is_prepared) |
b64959e61 PM: Permit regist... |
97 98 99 |
dev_warn(dev, "parent %s should not be sleeping ", dev_name(dev->parent)); |
3b98aeaf3 PM: don't skip de... |
100 |
list_add_tail(&dev->power.entry, &dpm_list); |
91ff4cb80 PM QoS: Implement... |
101 |
dev_pm_qos_constraints_init(dev); |
1a9a91525 PM / QoS: Add fun... |
102 |
mutex_unlock(&dpm_list_mtx); |
1da177e4c Linux-2.6.12-rc2 |
103 |
} |
775b64d2b PM: Acquire devic... |
104 |
/** |
20d652d7d PM: Update kernel... |
105 106 |
* device_pm_remove - Remove a device from the PM core's list of active devices. * @dev: Device to be removed from the list. |
775b64d2b PM: Acquire devic... |
107 |
*/ |
9cddad775 PM: Remove pm_par... |
108 |
void device_pm_remove(struct device *dev) |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 |
{ pr_debug("PM: Removing info for %s:%s ", |
5c1a07ab3 PM: Use dev_name(... |
112 |
dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
5af84b827 PM: Asynchronous ... |
113 |
complete_all(&dev->power.completion); |
11048dcf3 Power Management:... |
114 |
mutex_lock(&dpm_list_mtx); |
1a9a91525 PM / QoS: Add fun... |
115 |
dev_pm_qos_constraints_destroy(dev); |
1da177e4c Linux-2.6.12-rc2 |
116 |
list_del_init(&dev->power.entry); |
11048dcf3 Power Management:... |
117 |
mutex_unlock(&dpm_list_mtx); |
074037ec7 PM / Wakeup: Intr... |
118 |
device_wakeup_disable(dev); |
5e928f77a PM: Introduce cor... |
119 |
pm_runtime_remove(dev); |
775b64d2b PM: Acquire devic... |
120 |
} |
1eede070a Introduce new top... |
121 |
/** |
20d652d7d PM: Update kernel... |
122 123 124 |
* device_pm_move_before - Move device in the PM core's list of active devices. * @deva: Device to move in dpm_list. * @devb: Device @deva should come before. |
ffa6a7054 Driver core: Fix ... |
125 126 127 128 129 |
*/ void device_pm_move_before(struct device *deva, struct device *devb) { pr_debug("PM: Moving %s:%s before %s:%s ", |
5c1a07ab3 PM: Use dev_name(... |
130 131 |
deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); |
ffa6a7054 Driver core: Fix ... |
132 133 134 135 136 |
/* Delete deva from dpm_list and reinsert before devb. */ list_move_tail(&deva->power.entry, &devb->power.entry); } /** |
20d652d7d PM: Update kernel... |
137 138 139 |
* device_pm_move_after - Move device in the PM core's list of active devices. * @deva: Device to move in dpm_list. * @devb: Device @deva should come after. |
ffa6a7054 Driver core: Fix ... |
140 141 142 143 144 |
*/ void device_pm_move_after(struct device *deva, struct device *devb) { pr_debug("PM: Moving %s:%s after %s:%s ", |
5c1a07ab3 PM: Use dev_name(... |
145 146 |
deva->bus ? deva->bus->name : "No Bus", dev_name(deva), devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); |
ffa6a7054 Driver core: Fix ... |
147 148 149 150 151 |
/* Delete deva from dpm_list and reinsert after devb. */ list_move(&deva->power.entry, &devb->power.entry); } /** |
20d652d7d PM: Update kernel... |
152 153 |
* device_pm_move_last - Move device to end of the PM core's list of devices. * @dev: Device to move in dpm_list. |
ffa6a7054 Driver core: Fix ... |
154 155 156 157 158 |
*/ void device_pm_move_last(struct device *dev) { pr_debug("PM: Moving %s:%s to end of list ", |
5c1a07ab3 PM: Use dev_name(... |
159 |
dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
ffa6a7054 Driver core: Fix ... |
160 161 |
list_move_tail(&dev->power.entry, &dpm_list); } |
875ab0b74 PM: Make the init... |
162 163 164 165 166 |
static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); if (initcall_debug) { |
0c6aebe31 PM / Sleep: Unify... |
167 168 169 170 |
pr_info("calling %s+ @ %i, parent: %s ", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); |
875ab0b74 PM: Make the init... |
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
calltime = ktime_get(); } return calltime; } static void initcall_debug_report(struct device *dev, ktime_t calltime, int error) { ktime_t delta, rettime; if (initcall_debug) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs ", dev_name(dev), error, (unsigned long long)ktime_to_ns(delta) >> 10); } } |
ffa6a7054 Driver core: Fix ... |
190 |
/** |
5af84b827 PM: Asynchronous ... |
191 192 193 194 195 196 197 198 |
* dpm_wait - Wait for a PM operation to complete. * @dev: Device to wait for. * @async: If unset, wait only if the device's power.async_suspend flag is set. */ static void dpm_wait(struct device *dev, bool async) { if (!dev) return; |
0e06b4a89 PM: Add a switch ... |
199 |
if (async || (pm_async_enabled && dev->power.async_suspend)) |
5af84b827 PM: Asynchronous ... |
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
wait_for_completion(&dev->power.completion); } static int dpm_wait_fn(struct device *dev, void *async_ptr) { dpm_wait(dev, *((bool *)async_ptr)); return 0; } static void dpm_wait_for_children(struct device *dev, bool async) { device_for_each_child(dev, &async, dpm_wait_fn); } /** |
9cf519d1c PM / Sleep: Make ... |
215 |
* pm_op - Return the PM operation appropriate for given PM event. |
20d652d7d PM: Update kernel... |
216 217 |
* @ops: PM operations to choose from. * @state: PM transition of the system being carried out. |
1eede070a Introduce new top... |
218 |
*/ |
9cf519d1c PM / Sleep: Make ... |
219 |
static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state) |
1eede070a Introduce new top... |
220 |
{ |
1eede070a Introduce new top... |
221 222 223 |
switch (state.event) { #ifdef CONFIG_SUSPEND case PM_EVENT_SUSPEND: |
9cf519d1c PM / Sleep: Make ... |
224 |
return ops->suspend; |
1eede070a Introduce new top... |
225 |
case PM_EVENT_RESUME: |
9cf519d1c PM / Sleep: Make ... |
226 |
return ops->resume; |
1eede070a Introduce new top... |
227 |
#endif /* CONFIG_SUSPEND */ |
1f112cee0 PM / Hibernate: I... |
228 |
#ifdef CONFIG_HIBERNATE_CALLBACKS |
1eede070a Introduce new top... |
229 230 |
case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: |
9cf519d1c PM / Sleep: Make ... |
231 |
return ops->freeze; |
1eede070a Introduce new top... |
232 |
case PM_EVENT_HIBERNATE: |
9cf519d1c PM / Sleep: Make ... |
233 |
return ops->poweroff; |
1eede070a Introduce new top... |
234 235 |
case PM_EVENT_THAW: case PM_EVENT_RECOVER: |
9cf519d1c PM / Sleep: Make ... |
236 |
return ops->thaw; |
1eede070a Introduce new top... |
237 238 |
break; case PM_EVENT_RESTORE: |
9cf519d1c PM / Sleep: Make ... |
239 |
return ops->restore; |
1f112cee0 PM / Hibernate: I... |
240 |
#endif /* CONFIG_HIBERNATE_CALLBACKS */ |
1eede070a Introduce new top... |
241 |
} |
f25117748 PM: Add initcall_... |
242 |
|
9cf519d1c PM / Sleep: Make ... |
243 |
return NULL; |
1eede070a Introduce new top... |
244 245 246 |
} /** |
9cf519d1c PM / Sleep: Make ... |
247 |
* pm_noirq_op - Return the PM operation appropriate for given PM event. |
20d652d7d PM: Update kernel... |
248 249 |
* @ops: PM operations to choose from. * @state: PM transition of the system being carried out. |
1eede070a Introduce new top... |
250 |
* |
20d652d7d PM: Update kernel... |
251 252 |
* The driver of @dev will not receive interrupts while this function is being * executed. |
1eede070a Introduce new top... |
253 |
*/ |
9cf519d1c PM / Sleep: Make ... |
254 |
static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t state) |
1eede070a Introduce new top... |
255 |
{ |
1eede070a Introduce new top... |
256 257 258 |
switch (state.event) { #ifdef CONFIG_SUSPEND case PM_EVENT_SUSPEND: |
9cf519d1c PM / Sleep: Make ... |
259 |
return ops->suspend_noirq; |
1eede070a Introduce new top... |
260 |
case PM_EVENT_RESUME: |
9cf519d1c PM / Sleep: Make ... |
261 |
return ops->resume_noirq; |
1eede070a Introduce new top... |
262 |
#endif /* CONFIG_SUSPEND */ |
1f112cee0 PM / Hibernate: I... |
263 |
#ifdef CONFIG_HIBERNATE_CALLBACKS |
1eede070a Introduce new top... |
264 265 |
case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: |
9cf519d1c PM / Sleep: Make ... |
266 |
return ops->freeze_noirq; |
1eede070a Introduce new top... |
267 |
case PM_EVENT_HIBERNATE: |
9cf519d1c PM / Sleep: Make ... |
268 |
return ops->poweroff_noirq; |
1eede070a Introduce new top... |
269 270 |
case PM_EVENT_THAW: case PM_EVENT_RECOVER: |
9cf519d1c PM / Sleep: Make ... |
271 |
return ops->thaw_noirq; |
1eede070a Introduce new top... |
272 |
case PM_EVENT_RESTORE: |
9cf519d1c PM / Sleep: Make ... |
273 |
return ops->restore_noirq; |
1f112cee0 PM / Hibernate: I... |
274 |
#endif /* CONFIG_HIBERNATE_CALLBACKS */ |
1eede070a Introduce new top... |
275 |
} |
f25117748 PM: Add initcall_... |
276 |
|
9cf519d1c PM / Sleep: Make ... |
277 |
return NULL; |
1eede070a Introduce new top... |
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
} static char *pm_verb(int event) { switch (event) { case PM_EVENT_SUSPEND: return "suspend"; case PM_EVENT_RESUME: return "resume"; case PM_EVENT_FREEZE: return "freeze"; case PM_EVENT_QUIESCE: return "quiesce"; case PM_EVENT_HIBERNATE: return "hibernate"; case PM_EVENT_THAW: return "thaw"; case PM_EVENT_RESTORE: return "restore"; case PM_EVENT_RECOVER: return "recover"; default: return "(unknown PM event)"; } } static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) { dev_dbg(dev, "%s%s%s ", info, pm_verb(state.event), ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? ", may wakeup" : ""); } static void pm_dev_err(struct device *dev, pm_message_t state, char *info, int error) { printk(KERN_ERR "PM: Device %s failed to %s%s: error %d ", |
5c1a07ab3 PM: Use dev_name(... |
317 |
dev_name(dev), pm_verb(state.event), info, error); |
1eede070a Introduce new top... |
318 |
} |
ecf762b25 PM: Measure devic... |
319 320 321 |
static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) { ktime_t calltime; |
0702d9ee0 PM: Fix signed/un... |
322 |
u64 usecs64; |
ecf762b25 PM: Measure devic... |
323 324 325 326 327 328 329 330 331 332 333 334 335 |
int usecs; calltime = ktime_get(); usecs64 = ktime_to_ns(ktime_sub(calltime, starttime)); do_div(usecs64, NSEC_PER_USEC); usecs = usecs64; if (usecs == 0) usecs = 1; pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs ", info ?: "", info ? " " : "", pm_verb(state.event), usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); } |
9cf519d1c PM / Sleep: Make ... |
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
static int dpm_run_callback(pm_callback_t cb, struct device *dev, pm_message_t state, char *info) { ktime_t calltime; int error; if (!cb) return 0; calltime = initcall_debug_start(dev); pm_dev_dbg(dev, state, info); error = cb(dev); suspend_report_result(cb, error); initcall_debug_report(dev, calltime, error); return error; } |
cd59abfcc PM: merge device ... |
355 356 357 |
/*------------------------- Resume routines -------------------------*/ /** |
20d652d7d PM: Update kernel... |
358 359 360 |
* device_resume_noirq - Execute an "early resume" callback for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. |
cd59abfcc PM: merge device ... |
361 |
* |
20d652d7d PM: Update kernel... |
362 363 |
* The driver of @dev will not receive interrupts while this function is being * executed. |
cd59abfcc PM: merge device ... |
364 |
*/ |
d16163029 PM core: rename s... |
365 |
static int device_resume_noirq(struct device *dev, pm_message_t state) |
cd59abfcc PM: merge device ... |
366 |
{ |
9cf519d1c PM / Sleep: Make ... |
367 368 |
pm_callback_t callback = NULL; char *info = NULL; |
cd59abfcc PM: merge device ... |
369 370 371 372 |
int error = 0; TRACE_DEVICE(dev); TRACE_RESUME(0); |
564b905ab PM / Domains: Ren... |
373 |
if (dev->pm_domain) { |
9cf519d1c PM / Sleep: Make ... |
374 375 |
info = "EARLY power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); |
4d27e9dcf PM: Make power do... |
376 |
} else if (dev->type && dev->type->pm) { |
9cf519d1c PM / Sleep: Make ... |
377 378 |
info = "EARLY type "; callback = pm_noirq_op(dev->type->pm, state); |
9659cc067 PM: Make system-w... |
379 |
} else if (dev->class && dev->class->pm) { |
9cf519d1c PM / Sleep: Make ... |
380 381 |
info = "EARLY class "; callback = pm_noirq_op(dev->class->pm, state); |
9659cc067 PM: Make system-w... |
382 |
} else if (dev->bus && dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
383 |
info = "EARLY bus "; |
9cf519d1c PM / Sleep: Make ... |
384 |
callback = pm_noirq_op(dev->bus->pm, state); |
e7176a37d power: support _n... |
385 |
} |
35cd133c6 PM: Run the drive... |
386 387 388 389 |
if (!callback && dev->driver && dev->driver->pm) { info = "EARLY driver "; callback = pm_noirq_op(dev->driver->pm, state); } |
9cf519d1c PM / Sleep: Make ... |
390 |
error = dpm_run_callback(callback, dev, state, info); |
775b64d2b PM: Acquire devic... |
391 392 393 394 395 |
TRACE_RESUME(error); return error; } /** |
20d652d7d PM: Update kernel... |
396 397 |
* dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
398 |
* |
20d652d7d PM: Update kernel... |
399 400 |
* Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and * enable device drivers to receive interrupts. |
775b64d2b PM: Acquire devic... |
401 |
*/ |
d16163029 PM core: rename s... |
402 |
void dpm_resume_noirq(pm_message_t state) |
775b64d2b PM: Acquire devic... |
403 |
{ |
ecf762b25 PM: Measure devic... |
404 |
ktime_t starttime = ktime_get(); |
775b64d2b PM: Acquire devic... |
405 |
|
32bdfac54 PM: Do not hold d... |
406 |
mutex_lock(&dpm_list_mtx); |
8a43a9ab7 PM: Use a differe... |
407 408 |
while (!list_empty(&dpm_noirq_list)) { struct device *dev = to_device(dpm_noirq_list.next); |
5b219a51f PM: Remove redund... |
409 |
int error; |
d08a5ace1 PM: Allow devices... |
410 411 |
get_device(dev); |
5b219a51f PM: Remove redund... |
412 413 |
list_move_tail(&dev->power.entry, &dpm_suspended_list); mutex_unlock(&dpm_list_mtx); |
d08a5ace1 PM: Allow devices... |
414 |
|
5b219a51f PM: Remove redund... |
415 |
error = device_resume_noirq(dev, state); |
2a77c46de PM / Suspend: Add... |
416 417 418 419 |
if (error) { suspend_stats.failed_resume_noirq++; dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); dpm_save_failed_dev(dev_name(dev)); |
5b219a51f PM: Remove redund... |
420 |
pm_dev_err(dev, state, " early", error); |
2a77c46de PM / Suspend: Add... |
421 |
} |
d08a5ace1 PM: Allow devices... |
422 |
|
5b219a51f PM: Remove redund... |
423 |
mutex_lock(&dpm_list_mtx); |
d08a5ace1 PM: Allow devices... |
424 425 |
put_device(dev); } |
32bdfac54 PM: Do not hold d... |
426 |
mutex_unlock(&dpm_list_mtx); |
ecf762b25 PM: Measure devic... |
427 |
dpm_show_time(starttime, state, "early"); |
2ed8d2b3a PM: Rework handli... |
428 |
resume_device_irqs(); |
775b64d2b PM: Acquire devic... |
429 |
} |
d16163029 PM core: rename s... |
430 |
EXPORT_SYMBOL_GPL(dpm_resume_noirq); |
775b64d2b PM: Acquire devic... |
431 432 |
/** |
97df8c129 PM: Start asynchr... |
433 |
* device_resume - Execute "resume" callbacks for given device. |
20d652d7d PM: Update kernel... |
434 435 |
* @dev: Device to handle. * @state: PM transition of the system being carried out. |
5af84b827 PM: Asynchronous ... |
436 |
* @async: If true, the device is being resumed asynchronously. |
775b64d2b PM: Acquire devic... |
437 |
*/ |
97df8c129 PM: Start asynchr... |
438 |
static int device_resume(struct device *dev, pm_message_t state, bool async) |
775b64d2b PM: Acquire devic... |
439 |
{ |
9cf519d1c PM / Sleep: Make ... |
440 441 |
pm_callback_t callback = NULL; char *info = NULL; |
775b64d2b PM: Acquire devic... |
442 |
int error = 0; |
1e2ef05bb PM: Limit race co... |
443 |
bool put = false; |
775b64d2b PM: Acquire devic... |
444 445 446 |
TRACE_DEVICE(dev); TRACE_RESUME(0); |
cd59abfcc PM: merge device ... |
447 |
|
5af84b827 PM: Asynchronous ... |
448 |
dpm_wait(dev->parent, async); |
8e9394ce2 Driver core: crea... |
449 |
device_lock(dev); |
7a8d37a37 PM: Do not acquir... |
450 |
|
f76b168b6 PM: Rename dev_pm... |
451 452 453 454 455 |
/* * This is a fib. But we'll allow new children to be added below * a resumed device, even if the device hasn't been completed yet. */ dev->power.is_prepared = false; |
97df8c129 PM: Start asynchr... |
456 |
|
6d0e0e84f PM: Fix async res... |
457 458 |
if (!dev->power.is_suspended) goto Unlock; |
1e2ef05bb PM: Limit race co... |
459 460 |
pm_runtime_enable(dev); put = true; |
564b905ab PM / Domains: Ren... |
461 |
if (dev->pm_domain) { |
9cf519d1c PM / Sleep: Make ... |
462 463 |
info = "power domain "; callback = pm_op(&dev->pm_domain->ops, state); |
35cd133c6 PM: Run the drive... |
464 |
goto Driver; |
7538e3db6 PM: Add support f... |
465 |
} |
9659cc067 PM: Make system-w... |
466 |
if (dev->type && dev->type->pm) { |
9cf519d1c PM / Sleep: Make ... |
467 468 |
info = "type "; callback = pm_op(dev->type->pm, state); |
35cd133c6 PM: Run the drive... |
469 |
goto Driver; |
cd59abfcc PM: merge device ... |
470 |
} |
1eede070a Introduce new top... |
471 472 |
if (dev->class) { if (dev->class->pm) { |
9cf519d1c PM / Sleep: Make ... |
473 474 |
info = "class "; callback = pm_op(dev->class->pm, state); |
35cd133c6 PM: Run the drive... |
475 |
goto Driver; |
1eede070a Introduce new top... |
476 |
} else if (dev->class->resume) { |
9cf519d1c PM / Sleep: Make ... |
477 478 |
info = "legacy class "; callback = dev->class->resume; |
9659cc067 PM: Make system-w... |
479 |
goto End; |
1eede070a Introduce new top... |
480 |
} |
cd59abfcc PM: merge device ... |
481 |
} |
9659cc067 PM: Make system-w... |
482 483 484 |
if (dev->bus) { if (dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
485 |
info = "bus "; |
9cf519d1c PM / Sleep: Make ... |
486 |
callback = pm_op(dev->bus->pm, state); |
9659cc067 PM: Make system-w... |
487 |
} else if (dev->bus->resume) { |
35cd133c6 PM: Run the drive... |
488 |
info = "legacy bus "; |
9cf519d1c PM / Sleep: Make ... |
489 |
callback = dev->bus->resume; |
35cd133c6 PM: Run the drive... |
490 |
goto End; |
9659cc067 PM: Make system-w... |
491 492 |
} } |
35cd133c6 PM: Run the drive... |
493 494 495 496 497 |
Driver: if (!callback && dev->driver && dev->driver->pm) { info = "driver "; callback = pm_op(dev->driver->pm, state); } |
1eede070a Introduce new top... |
498 |
End: |
9cf519d1c PM / Sleep: Make ... |
499 |
error = dpm_run_callback(callback, dev, state, info); |
6d0e0e84f PM: Fix async res... |
500 501 502 |
dev->power.is_suspended = false; Unlock: |
8e9394ce2 Driver core: crea... |
503 |
device_unlock(dev); |
5af84b827 PM: Asynchronous ... |
504 |
complete_all(&dev->power.completion); |
7a8d37a37 PM: Do not acquir... |
505 |
|
cd59abfcc PM: merge device ... |
506 |
TRACE_RESUME(error); |
1e2ef05bb PM: Limit race co... |
507 508 509 |
if (put) pm_runtime_put_sync(dev); |
cd59abfcc PM: merge device ... |
510 511 |
return error; } |
5af84b827 PM: Asynchronous ... |
512 513 514 515 |
static void async_resume(void *data, async_cookie_t cookie) { struct device *dev = (struct device *)data; int error; |
97df8c129 PM: Start asynchr... |
516 |
error = device_resume(dev, pm_transition, true); |
5af84b827 PM: Asynchronous ... |
517 518 519 520 |
if (error) pm_dev_err(dev, pm_transition, " async", error); put_device(dev); } |
97df8c129 PM: Start asynchr... |
521 |
static bool is_async(struct device *dev) |
5af84b827 PM: Asynchronous ... |
522 |
{ |
97df8c129 PM: Start asynchr... |
523 524 |
return dev->power.async_suspend && pm_async_enabled && !pm_trace_is_enabled(); |
5af84b827 PM: Asynchronous ... |
525 |
} |
775b64d2b PM: Acquire devic... |
526 |
/** |
20d652d7d PM: Update kernel... |
527 528 |
* dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
529 |
* |
20d652d7d PM: Update kernel... |
530 531 |
* Execute the appropriate "resume" callback for all devices whose status * indicates that they are suspended. |
1eede070a Introduce new top... |
532 |
*/ |
91e7c75ba PM: Allow drivers... |
533 |
void dpm_resume(pm_message_t state) |
1eede070a Introduce new top... |
534 |
{ |
97df8c129 PM: Start asynchr... |
535 |
struct device *dev; |
ecf762b25 PM: Measure devic... |
536 |
ktime_t starttime = ktime_get(); |
1eede070a Introduce new top... |
537 |
|
91e7c75ba PM: Allow drivers... |
538 |
might_sleep(); |
1eede070a Introduce new top... |
539 |
mutex_lock(&dpm_list_mtx); |
5af84b827 PM: Asynchronous ... |
540 |
pm_transition = state; |
098dff738 PM: Fix potential... |
541 |
async_error = 0; |
1eede070a Introduce new top... |
542 |
|
8a43a9ab7 PM: Use a differe... |
543 |
list_for_each_entry(dev, &dpm_suspended_list, power.entry) { |
97df8c129 PM: Start asynchr... |
544 545 546 547 548 549 |
INIT_COMPLETION(dev->power.completion); if (is_async(dev)) { get_device(dev); async_schedule(async_resume, dev); } } |
8a43a9ab7 PM: Use a differe... |
550 551 |
while (!list_empty(&dpm_suspended_list)) { dev = to_device(dpm_suspended_list.next); |
1eede070a Introduce new top... |
552 |
get_device(dev); |
5b219a51f PM: Remove redund... |
553 |
if (!is_async(dev)) { |
1eede070a Introduce new top... |
554 |
int error; |
1eede070a Introduce new top... |
555 |
mutex_unlock(&dpm_list_mtx); |
97df8c129 PM: Start asynchr... |
556 |
error = device_resume(dev, state, false); |
2a77c46de PM / Suspend: Add... |
557 558 559 560 |
if (error) { suspend_stats.failed_resume++; dpm_save_failed_step(SUSPEND_RESUME); dpm_save_failed_dev(dev_name(dev)); |
1eede070a Introduce new top... |
561 |
pm_dev_err(dev, state, "", error); |
2a77c46de PM / Suspend: Add... |
562 |
} |
5b219a51f PM: Remove redund... |
563 564 |
mutex_lock(&dpm_list_mtx); |
1eede070a Introduce new top... |
565 566 |
} if (!list_empty(&dev->power.entry)) |
8a43a9ab7 PM: Use a differe... |
567 |
list_move_tail(&dev->power.entry, &dpm_prepared_list); |
1eede070a Introduce new top... |
568 569 |
put_device(dev); } |
1eede070a Introduce new top... |
570 |
mutex_unlock(&dpm_list_mtx); |
5af84b827 PM: Asynchronous ... |
571 |
async_synchronize_full(); |
ecf762b25 PM: Measure devic... |
572 |
dpm_show_time(starttime, state, NULL); |
1eede070a Introduce new top... |
573 574 575 |
} /** |
20d652d7d PM: Update kernel... |
576 577 578 |
* device_complete - Complete a PM transition for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. |
1eede070a Introduce new top... |
579 |
*/ |
d16163029 PM core: rename s... |
580 |
static void device_complete(struct device *dev, pm_message_t state) |
1eede070a Introduce new top... |
581 |
{ |
35cd133c6 PM: Run the drive... |
582 583 |
void (*callback)(struct device *) = NULL; char *info = NULL; |
8e9394ce2 Driver core: crea... |
584 |
device_lock(dev); |
1eede070a Introduce new top... |
585 |
|
564b905ab PM / Domains: Ren... |
586 |
if (dev->pm_domain) { |
35cd133c6 PM: Run the drive... |
587 588 |
info = "completing power domain "; callback = dev->pm_domain->ops.complete; |
4d27e9dcf PM: Make power do... |
589 |
} else if (dev->type && dev->type->pm) { |
35cd133c6 PM: Run the drive... |
590 591 |
info = "completing type "; callback = dev->type->pm->complete; |
9659cc067 PM: Make system-w... |
592 |
} else if (dev->class && dev->class->pm) { |
35cd133c6 PM: Run the drive... |
593 594 |
info = "completing class "; callback = dev->class->pm->complete; |
9659cc067 PM: Make system-w... |
595 |
} else if (dev->bus && dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
596 597 598 599 600 601 602 603 604 605 606 607 |
info = "completing bus "; callback = dev->bus->pm->complete; } if (!callback && dev->driver && dev->driver->pm) { info = "completing driver "; callback = dev->driver->pm->complete; } if (callback) { pm_dev_dbg(dev, state, info); callback(dev); |
1eede070a Introduce new top... |
608 |
} |
8e9394ce2 Driver core: crea... |
609 |
device_unlock(dev); |
1eede070a Introduce new top... |
610 611 612 |
} /** |
20d652d7d PM: Update kernel... |
613 614 |
* dpm_complete - Complete a PM transition for all non-sysdev devices. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
615 |
* |
20d652d7d PM: Update kernel... |
616 617 |
* Execute the ->complete() callbacks for all devices whose PM status is not * DPM_ON (this allows new devices to be registered). |
cd59abfcc PM: merge device ... |
618 |
*/ |
91e7c75ba PM: Allow drivers... |
619 |
void dpm_complete(pm_message_t state) |
cd59abfcc PM: merge device ... |
620 |
{ |
1eede070a Introduce new top... |
621 |
struct list_head list; |
91e7c75ba PM: Allow drivers... |
622 |
might_sleep(); |
1eede070a Introduce new top... |
623 |
INIT_LIST_HEAD(&list); |
cd59abfcc PM: merge device ... |
624 |
mutex_lock(&dpm_list_mtx); |
8a43a9ab7 PM: Use a differe... |
625 626 |
while (!list_empty(&dpm_prepared_list)) { struct device *dev = to_device(dpm_prepared_list.prev); |
cd59abfcc PM: merge device ... |
627 |
|
1eede070a Introduce new top... |
628 |
get_device(dev); |
f76b168b6 PM: Rename dev_pm... |
629 |
dev->power.is_prepared = false; |
5b219a51f PM: Remove redund... |
630 631 |
list_move(&dev->power.entry, &list); mutex_unlock(&dpm_list_mtx); |
1eede070a Introduce new top... |
632 |
|
5b219a51f PM: Remove redund... |
633 |
device_complete(dev, state); |
1eede070a Introduce new top... |
634 |
|
5b219a51f PM: Remove redund... |
635 |
mutex_lock(&dpm_list_mtx); |
1eede070a Introduce new top... |
636 |
put_device(dev); |
cd59abfcc PM: merge device ... |
637 |
} |
1eede070a Introduce new top... |
638 |
list_splice(&list, &dpm_list); |
cd59abfcc PM: merge device ... |
639 640 |
mutex_unlock(&dpm_list_mtx); } |
cd59abfcc PM: merge device ... |
641 |
/** |
20d652d7d PM: Update kernel... |
642 643 |
* dpm_resume_end - Execute "resume" callbacks and complete system transition. * @state: PM transition of the system being carried out. |
cd59abfcc PM: merge device ... |
644 |
* |
20d652d7d PM: Update kernel... |
645 646 |
* Execute "resume" callbacks for all devices and complete the PM transition of * the system. |
cd59abfcc PM: merge device ... |
647 |
*/ |
d16163029 PM core: rename s... |
648 |
void dpm_resume_end(pm_message_t state) |
cd59abfcc PM: merge device ... |
649 |
{ |
1eede070a Introduce new top... |
650 651 |
dpm_resume(state); dpm_complete(state); |
cd59abfcc PM: merge device ... |
652 |
} |
d16163029 PM core: rename s... |
653 |
EXPORT_SYMBOL_GPL(dpm_resume_end); |
cd59abfcc PM: merge device ... |
654 655 656 |
/*------------------------- Suspend routines -------------------------*/ |
1eede070a Introduce new top... |
657 |
/** |
20d652d7d PM: Update kernel... |
658 659 660 661 662 |
* resume_event - Return a "resume" message for given "suspend" sleep state. * @sleep_state: PM message representing a sleep state. * * Return a PM message representing the resume event corresponding to given * sleep state. |
1eede070a Introduce new top... |
663 664 |
*/ static pm_message_t resume_event(pm_message_t sleep_state) |
cd59abfcc PM: merge device ... |
665 |
{ |
1eede070a Introduce new top... |
666 667 668 669 670 671 672 673 |
switch (sleep_state.event) { case PM_EVENT_SUSPEND: return PMSG_RESUME; case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: return PMSG_RECOVER; case PM_EVENT_HIBERNATE: return PMSG_RESTORE; |
cd59abfcc PM: merge device ... |
674 |
} |
1eede070a Introduce new top... |
675 |
return PMSG_ON; |
cd59abfcc PM: merge device ... |
676 677 678 |
} /** |
20d652d7d PM: Update kernel... |
679 680 681 |
* device_suspend_noirq - Execute a "late suspend" callback for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
682 |
* |
20d652d7d PM: Update kernel... |
683 684 |
* The driver of @dev will not receive interrupts while this function is being * executed. |
cd59abfcc PM: merge device ... |
685 |
*/ |
d16163029 PM core: rename s... |
686 |
static int device_suspend_noirq(struct device *dev, pm_message_t state) |
775b64d2b PM: Acquire devic... |
687 |
{ |
9cf519d1c PM / Sleep: Make ... |
688 689 |
pm_callback_t callback = NULL; char *info = NULL; |
e7176a37d power: support _n... |
690 |
|
564b905ab PM / Domains: Ren... |
691 |
if (dev->pm_domain) { |
9cf519d1c PM / Sleep: Make ... |
692 693 |
info = "LATE power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); |
4d27e9dcf PM: Make power do... |
694 |
} else if (dev->type && dev->type->pm) { |
9cf519d1c PM / Sleep: Make ... |
695 696 |
info = "LATE type "; callback = pm_noirq_op(dev->type->pm, state); |
9659cc067 PM: Make system-w... |
697 |
} else if (dev->class && dev->class->pm) { |
9cf519d1c PM / Sleep: Make ... |
698 699 |
info = "LATE class "; callback = pm_noirq_op(dev->class->pm, state); |
9659cc067 PM: Make system-w... |
700 |
} else if (dev->bus && dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
701 |
info = "LATE bus "; |
9cf519d1c PM / Sleep: Make ... |
702 |
callback = pm_noirq_op(dev->bus->pm, state); |
7538e3db6 PM: Add support f... |
703 |
} |
35cd133c6 PM: Run the drive... |
704 705 706 707 |
if (!callback && dev->driver && dev->driver->pm) { info = "LATE driver "; callback = pm_noirq_op(dev->driver->pm, state); } |
9cf519d1c PM / Sleep: Make ... |
708 |
return dpm_run_callback(callback, dev, state, info); |
775b64d2b PM: Acquire devic... |
709 710 711 |
} /** |
20d652d7d PM: Update kernel... |
712 713 |
* dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
714 |
* |
20d652d7d PM: Update kernel... |
715 716 |
* Prevent device drivers from receiving interrupts and call the "noirq" suspend * handlers for all non-sysdev devices. |
775b64d2b PM: Acquire devic... |
717 |
*/ |
d16163029 PM core: rename s... |
718 |
int dpm_suspend_noirq(pm_message_t state) |
775b64d2b PM: Acquire devic... |
719 |
{ |
ecf762b25 PM: Measure devic... |
720 |
ktime_t starttime = ktime_get(); |
775b64d2b PM: Acquire devic... |
721 |
int error = 0; |
2ed8d2b3a PM: Rework handli... |
722 |
suspend_device_irqs(); |
32bdfac54 PM: Do not hold d... |
723 |
mutex_lock(&dpm_list_mtx); |
8a43a9ab7 PM: Use a differe... |
724 725 |
while (!list_empty(&dpm_suspended_list)) { struct device *dev = to_device(dpm_suspended_list.prev); |
d08a5ace1 PM: Allow devices... |
726 727 728 |
get_device(dev); mutex_unlock(&dpm_list_mtx); |
d16163029 PM core: rename s... |
729 |
error = device_suspend_noirq(dev, state); |
d08a5ace1 PM: Allow devices... |
730 731 |
mutex_lock(&dpm_list_mtx); |
775b64d2b PM: Acquire devic... |
732 |
if (error) { |
1eede070a Introduce new top... |
733 |
pm_dev_err(dev, state, " late", error); |
2a77c46de PM / Suspend: Add... |
734 735 736 |
suspend_stats.failed_suspend_noirq++; dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); dpm_save_failed_dev(dev_name(dev)); |
d08a5ace1 PM: Allow devices... |
737 |
put_device(dev); |
775b64d2b PM: Acquire devic... |
738 739 |
break; } |
d08a5ace1 PM: Allow devices... |
740 |
if (!list_empty(&dev->power.entry)) |
8a43a9ab7 PM: Use a differe... |
741 |
list_move(&dev->power.entry, &dpm_noirq_list); |
d08a5ace1 PM: Allow devices... |
742 |
put_device(dev); |
775b64d2b PM: Acquire devic... |
743 |
} |
32bdfac54 PM: Do not hold d... |
744 |
mutex_unlock(&dpm_list_mtx); |
775b64d2b PM: Acquire devic... |
745 |
if (error) |
d16163029 PM core: rename s... |
746 |
dpm_resume_noirq(resume_event(state)); |
ecf762b25 PM: Measure devic... |
747 748 |
else dpm_show_time(starttime, state, "late"); |
775b64d2b PM: Acquire devic... |
749 750 |
return error; } |
d16163029 PM core: rename s... |
751 |
EXPORT_SYMBOL_GPL(dpm_suspend_noirq); |
775b64d2b PM: Acquire devic... |
752 753 |
/** |
875ab0b74 PM: Make the init... |
754 |
* legacy_suspend - Execute a legacy (bus or class) suspend callback for device. |
0a8842231 power: fix kernel... |
755 756 757 |
* @dev: Device to suspend. * @state: PM transition of the system being carried out. * @cb: Suspend callback to execute. |
875ab0b74 PM: Make the init... |
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 |
*/ static int legacy_suspend(struct device *dev, pm_message_t state, int (*cb)(struct device *dev, pm_message_t state)) { int error; ktime_t calltime; calltime = initcall_debug_start(dev); error = cb(dev, state); suspend_report_result(cb, error); initcall_debug_report(dev, calltime, error); return error; } /** |
20d652d7d PM: Update kernel... |
776 777 778 |
* device_suspend - Execute "suspend" callbacks for given device. * @dev: Device to handle. * @state: PM transition of the system being carried out. |
5af84b827 PM: Asynchronous ... |
779 |
* @async: If true, the device is being suspended asynchronously. |
775b64d2b PM: Acquire devic... |
780 |
*/ |
5af84b827 PM: Asynchronous ... |
781 |
static int __device_suspend(struct device *dev, pm_message_t state, bool async) |
cd59abfcc PM: merge device ... |
782 |
{ |
9cf519d1c PM / Sleep: Make ... |
783 784 |
pm_callback_t callback = NULL; char *info = NULL; |
cd59abfcc PM: merge device ... |
785 |
int error = 0; |
5af84b827 PM: Asynchronous ... |
786 |
dpm_wait_for_children(dev, async); |
7a8d37a37 PM: Do not acquir... |
787 |
|
5af84b827 PM: Asynchronous ... |
788 |
if (async_error) |
1e2ef05bb PM: Limit race co... |
789 790 791 792 793 |
return 0; pm_runtime_get_noresume(dev); if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) pm_wakeup_event(dev, 0); |
5af84b827 PM: Asynchronous ... |
794 |
|
d83f905e1 PM: Use pm_wakeup... |
795 |
if (pm_wakeup_pending()) { |
1e2ef05bb PM: Limit race co... |
796 |
pm_runtime_put_sync(dev); |
d83f905e1 PM: Use pm_wakeup... |
797 |
async_error = -EBUSY; |
1e2ef05bb PM: Limit race co... |
798 |
return 0; |
d83f905e1 PM: Use pm_wakeup... |
799 |
} |
1e2ef05bb PM: Limit race co... |
800 |
device_lock(dev); |
564b905ab PM / Domains: Ren... |
801 |
if (dev->pm_domain) { |
9cf519d1c PM / Sleep: Make ... |
802 803 804 |
info = "power domain "; callback = pm_op(&dev->pm_domain->ops, state); goto Run; |
4d27e9dcf PM: Make power do... |
805 |
} |
9659cc067 PM: Make system-w... |
806 |
if (dev->type && dev->type->pm) { |
9cf519d1c PM / Sleep: Make ... |
807 808 809 |
info = "type "; callback = pm_op(dev->type->pm, state); goto Run; |
9659cc067 PM: Make system-w... |
810 |
} |
1eede070a Introduce new top... |
811 812 |
if (dev->class) { if (dev->class->pm) { |
9cf519d1c PM / Sleep: Make ... |
813 814 815 |
info = "class "; callback = pm_op(dev->class->pm, state); goto Run; |
1eede070a Introduce new top... |
816 817 |
} else if (dev->class->suspend) { pm_dev_dbg(dev, state, "legacy class "); |
875ab0b74 PM: Make the init... |
818 |
error = legacy_suspend(dev, state, dev->class->suspend); |
4d27e9dcf PM: Make power do... |
819 |
goto End; |
1eede070a Introduce new top... |
820 |
} |
cd59abfcc PM: merge device ... |
821 |
} |
1eede070a Introduce new top... |
822 823 |
if (dev->bus) { if (dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
824 |
info = "bus "; |
9cf519d1c PM / Sleep: Make ... |
825 |
callback = pm_op(dev->bus->pm, state); |
1eede070a Introduce new top... |
826 |
} else if (dev->bus->suspend) { |
35cd133c6 PM: Run the drive... |
827 |
pm_dev_dbg(dev, state, "legacy bus "); |
875ab0b74 PM: Make the init... |
828 |
error = legacy_suspend(dev, state, dev->bus->suspend); |
9cf519d1c PM / Sleep: Make ... |
829 |
goto End; |
1eede070a Introduce new top... |
830 |
} |
7538e3db6 PM: Add support f... |
831 |
} |
9cf519d1c PM / Sleep: Make ... |
832 |
Run: |
35cd133c6 PM: Run the drive... |
833 834 835 836 |
if (!callback && dev->driver && dev->driver->pm) { info = "driver "; callback = pm_op(dev->driver->pm, state); } |
9cf519d1c PM / Sleep: Make ... |
837 |
error = dpm_run_callback(callback, dev, state, info); |
1eede070a Introduce new top... |
838 |
End: |
4ca46ff3e PM / Sleep: Mark ... |
839 840 |
if (!error) { dev->power.is_suspended = true; |
8b258cc8a PM Sleep: Do not ... |
841 842 |
if (dev->power.wakeup_path && dev->parent && !dev->parent->power.ignore_children) |
4ca46ff3e PM / Sleep: Mark ... |
843 844 |
dev->parent->power.wakeup_path = true; } |
6d0e0e84f PM: Fix async res... |
845 |
|
8e9394ce2 Driver core: crea... |
846 |
device_unlock(dev); |
5af84b827 PM: Asynchronous ... |
847 |
complete_all(&dev->power.completion); |
7a8d37a37 PM: Do not acquir... |
848 |
|
1e2ef05bb PM: Limit race co... |
849 850 |
if (error) { pm_runtime_put_sync(dev); |
098dff738 PM: Fix potential... |
851 |
async_error = error; |
1e2ef05bb PM: Limit race co... |
852 853 854 |
} else if (dev->power.is_suspended) { __pm_runtime_disable(dev, false); } |
098dff738 PM: Fix potential... |
855 |
|
cd59abfcc PM: merge device ... |
856 857 |
return error; } |
5af84b827 PM: Asynchronous ... |
858 859 860 861 862 863 |
static void async_suspend(void *data, async_cookie_t cookie) { struct device *dev = (struct device *)data; int error; error = __device_suspend(dev, pm_transition, true); |
2a77c46de PM / Suspend: Add... |
864 865 |
if (error) { dpm_save_failed_dev(dev_name(dev)); |
5af84b827 PM: Asynchronous ... |
866 |
pm_dev_err(dev, pm_transition, " async", error); |
2a77c46de PM / Suspend: Add... |
867 |
} |
5af84b827 PM: Asynchronous ... |
868 869 870 871 872 873 874 |
put_device(dev); } static int device_suspend(struct device *dev) { INIT_COMPLETION(dev->power.completion); |
0e06b4a89 PM: Add a switch ... |
875 |
if (pm_async_enabled && dev->power.async_suspend) { |
5af84b827 PM: Asynchronous ... |
876 877 878 879 880 881 882 |
get_device(dev); async_schedule(async_suspend, dev); return 0; } return __device_suspend(dev, pm_transition, false); } |
cd59abfcc PM: merge device ... |
883 |
/** |
20d652d7d PM: Update kernel... |
884 885 |
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. * @state: PM transition of the system being carried out. |
cd59abfcc PM: merge device ... |
886 |
*/ |
91e7c75ba PM: Allow drivers... |
887 |
int dpm_suspend(pm_message_t state) |
cd59abfcc PM: merge device ... |
888 |
{ |
ecf762b25 PM: Measure devic... |
889 |
ktime_t starttime = ktime_get(); |
cd59abfcc PM: merge device ... |
890 |
int error = 0; |
91e7c75ba PM: Allow drivers... |
891 |
might_sleep(); |
cd59abfcc PM: merge device ... |
892 |
mutex_lock(&dpm_list_mtx); |
5af84b827 PM: Asynchronous ... |
893 894 |
pm_transition = state; async_error = 0; |
8a43a9ab7 PM: Use a differe... |
895 896 |
while (!list_empty(&dpm_prepared_list)) { struct device *dev = to_device(dpm_prepared_list.prev); |
58aca2322 PM: Handle device... |
897 |
|
1eede070a Introduce new top... |
898 |
get_device(dev); |
cd59abfcc PM: merge device ... |
899 |
mutex_unlock(&dpm_list_mtx); |
1eede070a Introduce new top... |
900 |
|
5af84b827 PM: Asynchronous ... |
901 |
error = device_suspend(dev); |
1eede070a Introduce new top... |
902 |
|
1b3cbec1d PM: fix new mutex... |
903 |
mutex_lock(&dpm_list_mtx); |
775b64d2b PM: Acquire devic... |
904 |
if (error) { |
1eede070a Introduce new top... |
905 |
pm_dev_err(dev, state, "", error); |
2a77c46de PM / Suspend: Add... |
906 |
dpm_save_failed_dev(dev_name(dev)); |
1eede070a Introduce new top... |
907 |
put_device(dev); |
775b64d2b PM: Acquire devic... |
908 909 |
break; } |
7a8d37a37 PM: Do not acquir... |
910 |
if (!list_empty(&dev->power.entry)) |
8a43a9ab7 PM: Use a differe... |
911 |
list_move(&dev->power.entry, &dpm_suspended_list); |
1eede070a Introduce new top... |
912 |
put_device(dev); |
5af84b827 PM: Asynchronous ... |
913 914 |
if (async_error) break; |
cd59abfcc PM: merge device ... |
915 916 |
} mutex_unlock(&dpm_list_mtx); |
5af84b827 PM: Asynchronous ... |
917 918 919 |
async_synchronize_full(); if (!error) error = async_error; |
2a77c46de PM / Suspend: Add... |
920 921 922 923 |
if (error) { suspend_stats.failed_suspend++; dpm_save_failed_step(SUSPEND_SUSPEND); } else |
ecf762b25 PM: Measure devic... |
924 |
dpm_show_time(starttime, state, NULL); |
1eede070a Introduce new top... |
925 926 927 928 |
return error; } /** |
20d652d7d PM: Update kernel... |
929 930 931 932 933 934 |
* device_prepare - Prepare a device for system power transition. * @dev: Device to handle. * @state: PM transition of the system being carried out. * * Execute the ->prepare() callback(s) for given device. No new children of the * device may be registered after this function has returned. |
1eede070a Introduce new top... |
935 |
*/ |
d16163029 PM core: rename s... |
936 |
static int device_prepare(struct device *dev, pm_message_t state) |
1eede070a Introduce new top... |
937 |
{ |
35cd133c6 PM: Run the drive... |
938 939 |
int (*callback)(struct device *) = NULL; char *info = NULL; |
1eede070a Introduce new top... |
940 |
int error = 0; |
8e9394ce2 Driver core: crea... |
941 |
device_lock(dev); |
1eede070a Introduce new top... |
942 |
|
4ca46ff3e PM / Sleep: Mark ... |
943 |
dev->power.wakeup_path = device_may_wakeup(dev); |
564b905ab PM / Domains: Ren... |
944 |
if (dev->pm_domain) { |
35cd133c6 PM: Run the drive... |
945 946 |
info = "preparing power domain "; callback = dev->pm_domain->ops.prepare; |
4d27e9dcf PM: Make power do... |
947 |
} else if (dev->type && dev->type->pm) { |
35cd133c6 PM: Run the drive... |
948 949 |
info = "preparing type "; callback = dev->type->pm->prepare; |
9659cc067 PM: Make system-w... |
950 |
} else if (dev->class && dev->class->pm) { |
35cd133c6 PM: Run the drive... |
951 952 |
info = "preparing class "; callback = dev->class->pm->prepare; |
9659cc067 PM: Make system-w... |
953 |
} else if (dev->bus && dev->bus->pm) { |
35cd133c6 PM: Run the drive... |
954 955 956 957 958 959 960 961 962 963 964 965 |
info = "preparing bus "; callback = dev->bus->pm->prepare; } if (!callback && dev->driver && dev->driver->pm) { info = "preparing driver "; callback = dev->driver->pm->prepare; } if (callback) { error = callback(dev); suspend_report_result(callback, error); |
1eede070a Introduce new top... |
966 |
} |
7538e3db6 PM: Add support f... |
967 |
|
8e9394ce2 Driver core: crea... |
968 |
device_unlock(dev); |
1eede070a Introduce new top... |
969 970 971 |
return error; } |
cd59abfcc PM: merge device ... |
972 |
|
1eede070a Introduce new top... |
973 |
/** |
20d652d7d PM: Update kernel... |
974 975 |
* dpm_prepare - Prepare all non-sysdev devices for a system PM transition. * @state: PM transition of the system being carried out. |
1eede070a Introduce new top... |
976 |
* |
20d652d7d PM: Update kernel... |
977 |
* Execute the ->prepare() callback(s) for all devices. |
1eede070a Introduce new top... |
978 |
*/ |
91e7c75ba PM: Allow drivers... |
979 |
int dpm_prepare(pm_message_t state) |
1eede070a Introduce new top... |
980 |
{ |
1eede070a Introduce new top... |
981 |
int error = 0; |
91e7c75ba PM: Allow drivers... |
982 |
might_sleep(); |
1eede070a Introduce new top... |
983 |
mutex_lock(&dpm_list_mtx); |
1eede070a Introduce new top... |
984 985 986 987 |
while (!list_empty(&dpm_list)) { struct device *dev = to_device(dpm_list.next); get_device(dev); |
1eede070a Introduce new top... |
988 |
mutex_unlock(&dpm_list_mtx); |
1e2ef05bb PM: Limit race co... |
989 |
error = device_prepare(dev, state); |
1eede070a Introduce new top... |
990 991 992 |
mutex_lock(&dpm_list_mtx); if (error) { |
1eede070a Introduce new top... |
993 994 |
if (error == -EAGAIN) { put_device(dev); |
886a7a337 PM: Clear -EAGAIN... |
995 |
error = 0; |
1eede070a Introduce new top... |
996 997 |
continue; } |
1e75227ef PM: Prevent dpm_p... |
998 999 1000 |
printk(KERN_INFO "PM: Device %s not prepared " "for power transition: code %d ", |
5c1a07ab3 PM: Use dev_name(... |
1001 |
dev_name(dev), error); |
1eede070a Introduce new top... |
1002 1003 1004 |
put_device(dev); break; } |
f76b168b6 PM: Rename dev_pm... |
1005 |
dev->power.is_prepared = true; |
1eede070a Introduce new top... |
1006 |
if (!list_empty(&dev->power.entry)) |
8a43a9ab7 PM: Use a differe... |
1007 |
list_move_tail(&dev->power.entry, &dpm_prepared_list); |
1eede070a Introduce new top... |
1008 1009 |
put_device(dev); } |
1eede070a Introduce new top... |
1010 |
mutex_unlock(&dpm_list_mtx); |
cd59abfcc PM: merge device ... |
1011 1012 |
return error; } |
cd59abfcc PM: merge device ... |
1013 |
/** |
20d652d7d PM: Update kernel... |
1014 1015 |
* dpm_suspend_start - Prepare devices for PM transition and suspend them. * @state: PM transition of the system being carried out. |
775b64d2b PM: Acquire devic... |
1016 |
* |
20d652d7d PM: Update kernel... |
1017 1018 |
* Prepare all non-sysdev devices for system PM transition and execute "suspend" * callbacks for them. |
775b64d2b PM: Acquire devic... |
1019 |
*/ |
d16163029 PM core: rename s... |
1020 |
int dpm_suspend_start(pm_message_t state) |
775b64d2b PM: Acquire devic... |
1021 1022 |
{ int error; |
cd59abfcc PM: merge device ... |
1023 |
|
1eede070a Introduce new top... |
1024 |
error = dpm_prepare(state); |
2a77c46de PM / Suspend: Add... |
1025 1026 1027 1028 |
if (error) { suspend_stats.failed_prepare++; dpm_save_failed_step(SUSPEND_PREPARE); } else |
1eede070a Introduce new top... |
1029 |
error = dpm_suspend(state); |
cd59abfcc PM: merge device ... |
1030 |
return error; |
cd59abfcc PM: merge device ... |
1031 |
} |
d16163029 PM core: rename s... |
1032 |
EXPORT_SYMBOL_GPL(dpm_suspend_start); |
cd59abfcc PM: merge device ... |
1033 1034 1035 |
void __suspend_report_result(const char *function, void *fn, int ret) { |
c80cfb040 vsprintf: use new... |
1036 1037 1038 |
if (ret) printk(KERN_ERR "%s(): %pF returns %d ", function, fn, ret); |
cd59abfcc PM: merge device ... |
1039 1040 |
} EXPORT_SYMBOL_GPL(__suspend_report_result); |
f8824cee4 PM: Allow device ... |
1041 1042 1043 1044 1045 1046 |
/** * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. * @dev: Device to wait for. * @subordinate: Device that needs to wait for @dev. */ |
098dff738 PM: Fix potential... |
1047 |
int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) |
f8824cee4 PM: Allow device ... |
1048 1049 |
{ dpm_wait(dev, subordinate->power.async_suspend); |
098dff738 PM: Fix potential... |
1050 |
return async_error; |
f8824cee4 PM: Allow device ... |
1051 1052 |
} EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); |