diff options
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 375 |
1 files changed, 165 insertions, 210 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index c3d2dfcf438d..e2cc3d2e0ecc 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include "../base.h" | 32 | #include "../base.h" |
33 | #include "power.h" | 33 | #include "power.h" |
34 | 34 | ||
35 | typedef int (*pm_callback_t)(struct device *); | ||
36 | |||
35 | /* | 37 | /* |
36 | * The entries in the dpm_list list are in a depth first order, simply | 38 | * The entries in the dpm_list list are in a depth first order, simply |
37 | * because children are guaranteed to be discovered after parents, and | 39 | * because children are guaranteed to be discovered after parents, and |
@@ -164,8 +166,9 @@ static ktime_t initcall_debug_start(struct device *dev) | |||
164 | ktime_t calltime = ktime_set(0, 0); | 166 | ktime_t calltime = ktime_set(0, 0); |
165 | 167 | ||
166 | if (initcall_debug) { | 168 | if (initcall_debug) { |
167 | pr_info("calling %s+ @ %i\n", | 169 | pr_info("calling %s+ @ %i, parent: %s\n", |
168 | dev_name(dev), task_pid_nr(current)); | 170 | dev_name(dev), task_pid_nr(current), |
171 | dev->parent ? dev_name(dev->parent) : "none"); | ||
169 | calltime = ktime_get(); | 172 | calltime = ktime_get(); |
170 | } | 173 | } |
171 | 174 | ||
@@ -211,151 +214,69 @@ static void dpm_wait_for_children(struct device *dev, bool async) | |||
211 | } | 214 | } |
212 | 215 | ||
213 | /** | 216 | /** |
214 | * pm_op - Execute the PM operation appropriate for given PM event. | 217 | * pm_op - Return the PM operation appropriate for given PM event. |
215 | * @dev: Device to handle. | ||
216 | * @ops: PM operations to choose from. | 218 | * @ops: PM operations to choose from. |
217 | * @state: PM transition of the system being carried out. | 219 | * @state: PM transition of the system being carried out. |
218 | */ | 220 | */ |
219 | static int pm_op(struct device *dev, | 221 | static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state) |
220 | const struct dev_pm_ops *ops, | ||
221 | pm_message_t state) | ||
222 | { | 222 | { |
223 | int error = 0; | ||
224 | ktime_t calltime; | ||
225 | |||
226 | calltime = initcall_debug_start(dev); | ||
227 | |||
228 | switch (state.event) { | 223 | switch (state.event) { |
229 | #ifdef CONFIG_SUSPEND | 224 | #ifdef CONFIG_SUSPEND |
230 | case PM_EVENT_SUSPEND: | 225 | case PM_EVENT_SUSPEND: |
231 | if (ops->suspend) { | 226 | return ops->suspend; |
232 | error = ops->suspend(dev); | ||
233 | suspend_report_result(ops->suspend, error); | ||
234 | } | ||
235 | break; | ||
236 | case PM_EVENT_RESUME: | 227 | case PM_EVENT_RESUME: |
237 | if (ops->resume) { | 228 | return ops->resume; |
238 | error = ops->resume(dev); | ||
239 | suspend_report_result(ops->resume, error); | ||
240 | } | ||
241 | break; | ||
242 | #endif /* CONFIG_SUSPEND */ | 229 | #endif /* CONFIG_SUSPEND */ |
243 | #ifdef CONFIG_HIBERNATE_CALLBACKS | 230 | #ifdef CONFIG_HIBERNATE_CALLBACKS |
244 | case PM_EVENT_FREEZE: | 231 | case PM_EVENT_FREEZE: |
245 | case PM_EVENT_QUIESCE: | 232 | case PM_EVENT_QUIESCE: |
246 | if (ops->freeze) { | 233 | return ops->freeze; |
247 | error = ops->freeze(dev); | ||
248 | suspend_report_result(ops->freeze, error); | ||
249 | } | ||
250 | break; | ||
251 | case PM_EVENT_HIBERNATE: | 234 | case PM_EVENT_HIBERNATE: |
252 | if (ops->poweroff) { | 235 | return ops->poweroff; |
253 | error = ops->poweroff(dev); | ||
254 | suspend_report_result(ops->poweroff, error); | ||
255 | } | ||
256 | break; | ||
257 | case PM_EVENT_THAW: | 236 | case PM_EVENT_THAW: |
258 | case PM_EVENT_RECOVER: | 237 | case PM_EVENT_RECOVER: |
259 | if (ops->thaw) { | 238 | return ops->thaw; |
260 | error = ops->thaw(dev); | ||
261 | suspend_report_result(ops->thaw, error); | ||
262 | } | ||
263 | break; | 239 | break; |
264 | case PM_EVENT_RESTORE: | 240 | case PM_EVENT_RESTORE: |
265 | if (ops->restore) { | 241 | return ops->restore; |
266 | error = ops->restore(dev); | ||
267 | suspend_report_result(ops->restore, error); | ||
268 | } | ||
269 | break; | ||
270 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ | 242 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ |
271 | default: | ||
272 | error = -EINVAL; | ||
273 | } | 243 | } |
274 | 244 | ||
275 | initcall_debug_report(dev, calltime, error); | 245 | return NULL; |
276 | |||
277 | return error; | ||
278 | } | 246 | } |
279 | 247 | ||
280 | /** | 248 | /** |
281 | * pm_noirq_op - Execute the PM operation appropriate for given PM event. | 249 | * pm_noirq_op - Return the PM operation appropriate for given PM event. |
282 | * @dev: Device to handle. | ||
283 | * @ops: PM operations to choose from. | 250 | * @ops: PM operations to choose from. |
284 | * @state: PM transition of the system being carried out. | 251 | * @state: PM transition of the system being carried out. |
285 | * | 252 | * |
286 | * The driver of @dev will not receive interrupts while this function is being | 253 | * The driver of @dev will not receive interrupts while this function is being |
287 | * executed. | 254 | * executed. |
288 | */ | 255 | */ |
289 | static int pm_noirq_op(struct device *dev, | 256 | static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t state) |
290 | const struct dev_pm_ops *ops, | ||
291 | pm_message_t state) | ||
292 | { | 257 | { |
293 | int error = 0; | ||
294 | ktime_t calltime = ktime_set(0, 0), delta, rettime; | ||
295 | |||
296 | if (initcall_debug) { | ||
297 | pr_info("calling %s+ @ %i, parent: %s\n", | ||
298 | dev_name(dev), task_pid_nr(current), | ||
299 | dev->parent ? dev_name(dev->parent) : "none"); | ||
300 | calltime = ktime_get(); | ||
301 | } | ||
302 | |||
303 | switch (state.event) { | 258 | switch (state.event) { |
304 | #ifdef CONFIG_SUSPEND | 259 | #ifdef CONFIG_SUSPEND |
305 | case PM_EVENT_SUSPEND: | 260 | case PM_EVENT_SUSPEND: |
306 | if (ops->suspend_noirq) { | 261 | return ops->suspend_noirq; |
307 | error = ops->suspend_noirq(dev); | ||
308 | suspend_report_result(ops->suspend_noirq, error); | ||
309 | } | ||
310 | break; | ||
311 | case PM_EVENT_RESUME: | 262 | case PM_EVENT_RESUME: |
312 | if (ops->resume_noirq) { | 263 | return ops->resume_noirq; |
313 | error = ops->resume_noirq(dev); | ||
314 | suspend_report_result(ops->resume_noirq, error); | ||
315 | } | ||
316 | break; | ||
317 | #endif /* CONFIG_SUSPEND */ | 264 | #endif /* CONFIG_SUSPEND */ |
318 | #ifdef CONFIG_HIBERNATE_CALLBACKS | 265 | #ifdef CONFIG_HIBERNATE_CALLBACKS |
319 | case PM_EVENT_FREEZE: | 266 | case PM_EVENT_FREEZE: |
320 | case PM_EVENT_QUIESCE: | 267 | case PM_EVENT_QUIESCE: |
321 | if (ops->freeze_noirq) { | 268 | return ops->freeze_noirq; |
322 | error = ops->freeze_noirq(dev); | ||
323 | suspend_report_result(ops->freeze_noirq, error); | ||
324 | } | ||
325 | break; | ||
326 | case PM_EVENT_HIBERNATE: | 269 | case PM_EVENT_HIBERNATE: |
327 | if (ops->poweroff_noirq) { | 270 | return ops->poweroff_noirq; |
328 | error = ops->poweroff_noirq(dev); | ||
329 | suspend_report_result(ops->poweroff_noirq, error); | ||
330 | } | ||
331 | break; | ||
332 | case PM_EVENT_THAW: | 271 | case PM_EVENT_THAW: |
333 | case PM_EVENT_RECOVER: | 272 | case PM_EVENT_RECOVER: |
334 | if (ops->thaw_noirq) { | 273 | return ops->thaw_noirq; |
335 | error = ops->thaw_noirq(dev); | ||
336 | suspend_report_result(ops->thaw_noirq, error); | ||
337 | } | ||
338 | break; | ||
339 | case PM_EVENT_RESTORE: | 274 | case PM_EVENT_RESTORE: |
340 | if (ops->restore_noirq) { | 275 | return ops->restore_noirq; |
341 | error = ops->restore_noirq(dev); | ||
342 | suspend_report_result(ops->restore_noirq, error); | ||
343 | } | ||
344 | break; | ||
345 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ | 276 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ |
346 | default: | ||
347 | error = -EINVAL; | ||
348 | } | ||
349 | |||
350 | if (initcall_debug) { | ||
351 | rettime = ktime_get(); | ||
352 | delta = ktime_sub(rettime, calltime); | ||
353 | printk("initcall %s_i+ returned %d after %Ld usecs\n", | ||
354 | dev_name(dev), error, | ||
355 | (unsigned long long)ktime_to_ns(delta) >> 10); | ||
356 | } | 277 | } |
357 | 278 | ||
358 | return error; | 279 | return NULL; |
359 | } | 280 | } |
360 | 281 | ||
361 | static char *pm_verb(int event) | 282 | static char *pm_verb(int event) |
@@ -413,6 +334,26 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) | |||
413 | usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); | 334 | usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); |
414 | } | 335 | } |
415 | 336 | ||
337 | static int dpm_run_callback(pm_callback_t cb, struct device *dev, | ||
338 | pm_message_t state, char *info) | ||
339 | { | ||
340 | ktime_t calltime; | ||
341 | int error; | ||
342 | |||
343 | if (!cb) | ||
344 | return 0; | ||
345 | |||
346 | calltime = initcall_debug_start(dev); | ||
347 | |||
348 | pm_dev_dbg(dev, state, info); | ||
349 | error = cb(dev); | ||
350 | suspend_report_result(cb, error); | ||
351 | |||
352 | initcall_debug_report(dev, calltime, error); | ||
353 | |||
354 | return error; | ||
355 | } | ||
356 | |||
416 | /*------------------------- Resume routines -------------------------*/ | 357 | /*------------------------- Resume routines -------------------------*/ |
417 | 358 | ||
418 | /** | 359 | /** |
@@ -425,25 +366,34 @@ static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) | |||
425 | */ | 366 | */ |
426 | static int device_resume_noirq(struct device *dev, pm_message_t state) | 367 | static int device_resume_noirq(struct device *dev, pm_message_t state) |
427 | { | 368 | { |
369 | pm_callback_t callback = NULL; | ||
370 | char *info = NULL; | ||
428 | int error = 0; | 371 | int error = 0; |
429 | 372 | ||
430 | TRACE_DEVICE(dev); | 373 | TRACE_DEVICE(dev); |
431 | TRACE_RESUME(0); | 374 | TRACE_RESUME(0); |
432 | 375 | ||
433 | if (dev->pm_domain) { | 376 | if (dev->pm_domain) { |
434 | pm_dev_dbg(dev, state, "EARLY power domain "); | 377 | info = "EARLY power domain "; |
435 | error = pm_noirq_op(dev, &dev->pm_domain->ops, state); | 378 | callback = pm_noirq_op(&dev->pm_domain->ops, state); |
436 | } else if (dev->type && dev->type->pm) { | 379 | } else if (dev->type && dev->type->pm) { |
437 | pm_dev_dbg(dev, state, "EARLY type "); | 380 | info = "EARLY type "; |
438 | error = pm_noirq_op(dev, dev->type->pm, state); | 381 | callback = pm_noirq_op(dev->type->pm, state); |
439 | } else if (dev->class && dev->class->pm) { | 382 | } else if (dev->class && dev->class->pm) { |
440 | pm_dev_dbg(dev, state, "EARLY class "); | 383 | info = "EARLY class "; |
441 | error = pm_noirq_op(dev, dev->class->pm, state); | 384 | callback = pm_noirq_op(dev->class->pm, state); |
442 | } else if (dev->bus && dev->bus->pm) { | 385 | } else if (dev->bus && dev->bus->pm) { |
443 | pm_dev_dbg(dev, state, "EARLY "); | 386 | info = "EARLY bus "; |
444 | error = pm_noirq_op(dev, dev->bus->pm, state); | 387 | callback = pm_noirq_op(dev->bus->pm, state); |
445 | } | 388 | } |
446 | 389 | ||
390 | if (!callback && dev->driver && dev->driver->pm) { | ||
391 | info = "EARLY driver "; | ||
392 | callback = pm_noirq_op(dev->driver->pm, state); | ||
393 | } | ||
394 | |||
395 | error = dpm_run_callback(callback, dev, state, info); | ||
396 | |||
447 | TRACE_RESUME(error); | 397 | TRACE_RESUME(error); |
448 | return error; | 398 | return error; |
449 | } | 399 | } |
@@ -486,26 +436,6 @@ void dpm_resume_noirq(pm_message_t state) | |||
486 | EXPORT_SYMBOL_GPL(dpm_resume_noirq); | 436 | EXPORT_SYMBOL_GPL(dpm_resume_noirq); |
487 | 437 | ||
488 | /** | 438 | /** |
489 | * legacy_resume - Execute a legacy (bus or class) resume callback for device. | ||
490 | * @dev: Device to resume. | ||
491 | * @cb: Resume callback to execute. | ||
492 | */ | ||
493 | static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) | ||
494 | { | ||
495 | int error; | ||
496 | ktime_t calltime; | ||
497 | |||
498 | calltime = initcall_debug_start(dev); | ||
499 | |||
500 | error = cb(dev); | ||
501 | suspend_report_result(cb, error); | ||
502 | |||
503 | initcall_debug_report(dev, calltime, error); | ||
504 | |||
505 | return error; | ||
506 | } | ||
507 | |||
508 | /** | ||
509 | * device_resume - Execute "resume" callbacks for given device. | 439 | * device_resume - Execute "resume" callbacks for given device. |
510 | * @dev: Device to handle. | 440 | * @dev: Device to handle. |
511 | * @state: PM transition of the system being carried out. | 441 | * @state: PM transition of the system being carried out. |
@@ -513,6 +443,8 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) | |||
513 | */ | 443 | */ |
514 | static int device_resume(struct device *dev, pm_message_t state, bool async) | 444 | static int device_resume(struct device *dev, pm_message_t state, bool async) |
515 | { | 445 | { |
446 | pm_callback_t callback = NULL; | ||
447 | char *info = NULL; | ||
516 | int error = 0; | 448 | int error = 0; |
517 | bool put = false; | 449 | bool put = false; |
518 | 450 | ||
@@ -535,40 +467,48 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
535 | put = true; | 467 | put = true; |
536 | 468 | ||
537 | if (dev->pm_domain) { | 469 | if (dev->pm_domain) { |
538 | pm_dev_dbg(dev, state, "power domain "); | 470 | info = "power domain "; |
539 | error = pm_op(dev, &dev->pm_domain->ops, state); | 471 | callback = pm_op(&dev->pm_domain->ops, state); |
540 | goto End; | 472 | goto Driver; |
541 | } | 473 | } |
542 | 474 | ||
543 | if (dev->type && dev->type->pm) { | 475 | if (dev->type && dev->type->pm) { |
544 | pm_dev_dbg(dev, state, "type "); | 476 | info = "type "; |
545 | error = pm_op(dev, dev->type->pm, state); | 477 | callback = pm_op(dev->type->pm, state); |
546 | goto End; | 478 | goto Driver; |
547 | } | 479 | } |
548 | 480 | ||
549 | if (dev->class) { | 481 | if (dev->class) { |
550 | if (dev->class->pm) { | 482 | if (dev->class->pm) { |
551 | pm_dev_dbg(dev, state, "class "); | 483 | info = "class "; |
552 | error = pm_op(dev, dev->class->pm, state); | 484 | callback = pm_op(dev->class->pm, state); |
553 | goto End; | 485 | goto Driver; |
554 | } else if (dev->class->resume) { | 486 | } else if (dev->class->resume) { |
555 | pm_dev_dbg(dev, state, "legacy class "); | 487 | info = "legacy class "; |
556 | error = legacy_resume(dev, dev->class->resume); | 488 | callback = dev->class->resume; |
557 | goto End; | 489 | goto End; |
558 | } | 490 | } |
559 | } | 491 | } |
560 | 492 | ||
561 | if (dev->bus) { | 493 | if (dev->bus) { |
562 | if (dev->bus->pm) { | 494 | if (dev->bus->pm) { |
563 | pm_dev_dbg(dev, state, ""); | 495 | info = "bus "; |
564 | error = pm_op(dev, dev->bus->pm, state); | 496 | callback = pm_op(dev->bus->pm, state); |
565 | } else if (dev->bus->resume) { | 497 | } else if (dev->bus->resume) { |
566 | pm_dev_dbg(dev, state, "legacy "); | 498 | info = "legacy bus "; |
567 | error = legacy_resume(dev, dev->bus->resume); | 499 | callback = dev->bus->resume; |
500 | goto End; | ||
568 | } | 501 | } |
569 | } | 502 | } |
570 | 503 | ||
504 | Driver: | ||
505 | if (!callback && dev->driver && dev->driver->pm) { | ||
506 | info = "driver "; | ||
507 | callback = pm_op(dev->driver->pm, state); | ||
508 | } | ||
509 | |||
571 | End: | 510 | End: |
511 | error = dpm_run_callback(callback, dev, state, info); | ||
572 | dev->power.is_suspended = false; | 512 | dev->power.is_suspended = false; |
573 | 513 | ||
574 | Unlock: | 514 | Unlock: |
@@ -660,24 +600,33 @@ void dpm_resume(pm_message_t state) | |||
660 | */ | 600 | */ |
661 | static void device_complete(struct device *dev, pm_message_t state) | 601 | static void device_complete(struct device *dev, pm_message_t state) |
662 | { | 602 | { |
603 | void (*callback)(struct device *) = NULL; | ||
604 | char *info = NULL; | ||
605 | |||
663 | device_lock(dev); | 606 | device_lock(dev); |
664 | 607 | ||
665 | if (dev->pm_domain) { | 608 | if (dev->pm_domain) { |
666 | pm_dev_dbg(dev, state, "completing power domain "); | 609 | info = "completing power domain "; |
667 | if (dev->pm_domain->ops.complete) | 610 | callback = dev->pm_domain->ops.complete; |
668 | dev->pm_domain->ops.complete(dev); | ||
669 | } else if (dev->type && dev->type->pm) { | 611 | } else if (dev->type && dev->type->pm) { |
670 | pm_dev_dbg(dev, state, "completing type "); | 612 | info = "completing type "; |
671 | if (dev->type->pm->complete) | 613 | callback = dev->type->pm->complete; |
672 | dev->type->pm->complete(dev); | ||
673 | } else if (dev->class && dev->class->pm) { | 614 | } else if (dev->class && dev->class->pm) { |
674 | pm_dev_dbg(dev, state, "completing class "); | 615 | info = "completing class "; |
675 | if (dev->class->pm->complete) | 616 | callback = dev->class->pm->complete; |
676 | dev->class->pm->complete(dev); | ||
677 | } else if (dev->bus && dev->bus->pm) { | 617 | } else if (dev->bus && dev->bus->pm) { |
678 | pm_dev_dbg(dev, state, "completing "); | 618 | info = "completing bus "; |
679 | if (dev->bus->pm->complete) | 619 | callback = dev->bus->pm->complete; |
680 | dev->bus->pm->complete(dev); | 620 | } |
621 | |||
622 | if (!callback && dev->driver && dev->driver->pm) { | ||
623 | info = "completing driver "; | ||
624 | callback = dev->driver->pm->complete; | ||
625 | } | ||
626 | |||
627 | if (callback) { | ||
628 | pm_dev_dbg(dev, state, info); | ||
629 | callback(dev); | ||
681 | } | 630 | } |
682 | 631 | ||
683 | device_unlock(dev); | 632 | device_unlock(dev); |
@@ -763,31 +712,29 @@ static pm_message_t resume_event(pm_message_t sleep_state) | |||
763 | */ | 712 | */ |
764 | static int device_suspend_noirq(struct device *dev, pm_message_t state) | 713 | static int device_suspend_noirq(struct device *dev, pm_message_t state) |
765 | { | 714 | { |
766 | int error; | 715 | pm_callback_t callback = NULL; |
716 | char *info = NULL; | ||
767 | 717 | ||
768 | if (dev->pm_domain) { | 718 | if (dev->pm_domain) { |
769 | pm_dev_dbg(dev, state, "LATE power domain "); | 719 | info = "LATE power domain "; |
770 | error = pm_noirq_op(dev, &dev->pm_domain->ops, state); | 720 | callback = pm_noirq_op(&dev->pm_domain->ops, state); |
771 | if (error) | ||
772 | return error; | ||
773 | } else if (dev->type && dev->type->pm) { | 721 | } else if (dev->type && dev->type->pm) { |
774 | pm_dev_dbg(dev, state, "LATE type "); | 722 | info = "LATE type "; |
775 | error = pm_noirq_op(dev, dev->type->pm, state); | 723 | callback = pm_noirq_op(dev->type->pm, state); |
776 | if (error) | ||
777 | return error; | ||
778 | } else if (dev->class && dev->class->pm) { | 724 | } else if (dev->class && dev->class->pm) { |
779 | pm_dev_dbg(dev, state, "LATE class "); | 725 | info = "LATE class "; |
780 | error = pm_noirq_op(dev, dev->class->pm, state); | 726 | callback = pm_noirq_op(dev->class->pm, state); |
781 | if (error) | ||
782 | return error; | ||
783 | } else if (dev->bus && dev->bus->pm) { | 727 | } else if (dev->bus && dev->bus->pm) { |
784 | pm_dev_dbg(dev, state, "LATE "); | 728 | info = "LATE bus "; |
785 | error = pm_noirq_op(dev, dev->bus->pm, state); | 729 | callback = pm_noirq_op(dev->bus->pm, state); |
786 | if (error) | ||
787 | return error; | ||
788 | } | 730 | } |
789 | 731 | ||
790 | return 0; | 732 | if (!callback && dev->driver && dev->driver->pm) { |
733 | info = "LATE driver "; | ||
734 | callback = pm_noirq_op(dev->driver->pm, state); | ||
735 | } | ||
736 | |||
737 | return dpm_run_callback(callback, dev, state, info); | ||
791 | } | 738 | } |
792 | 739 | ||
793 | /** | 740 | /** |
@@ -864,6 +811,8 @@ static int legacy_suspend(struct device *dev, pm_message_t state, | |||
864 | */ | 811 | */ |
865 | static int __device_suspend(struct device *dev, pm_message_t state, bool async) | 812 | static int __device_suspend(struct device *dev, pm_message_t state, bool async) |
866 | { | 813 | { |
814 | pm_callback_t callback = NULL; | ||
815 | char *info = NULL; | ||
867 | int error = 0; | 816 | int error = 0; |
868 | 817 | ||
869 | dpm_wait_for_children(dev, async); | 818 | dpm_wait_for_children(dev, async); |
@@ -884,22 +833,22 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
884 | device_lock(dev); | 833 | device_lock(dev); |
885 | 834 | ||
886 | if (dev->pm_domain) { | 835 | if (dev->pm_domain) { |
887 | pm_dev_dbg(dev, state, "power domain "); | 836 | info = "power domain "; |
888 | error = pm_op(dev, &dev->pm_domain->ops, state); | 837 | callback = pm_op(&dev->pm_domain->ops, state); |
889 | goto End; | 838 | goto Run; |
890 | } | 839 | } |
891 | 840 | ||
892 | if (dev->type && dev->type->pm) { | 841 | if (dev->type && dev->type->pm) { |
893 | pm_dev_dbg(dev, state, "type "); | 842 | info = "type "; |
894 | error = pm_op(dev, dev->type->pm, state); | 843 | callback = pm_op(dev->type->pm, state); |
895 | goto End; | 844 | goto Run; |
896 | } | 845 | } |
897 | 846 | ||
898 | if (dev->class) { | 847 | if (dev->class) { |
899 | if (dev->class->pm) { | 848 | if (dev->class->pm) { |
900 | pm_dev_dbg(dev, state, "class "); | 849 | info = "class "; |
901 | error = pm_op(dev, dev->class->pm, state); | 850 | callback = pm_op(dev->class->pm, state); |
902 | goto End; | 851 | goto Run; |
903 | } else if (dev->class->suspend) { | 852 | } else if (dev->class->suspend) { |
904 | pm_dev_dbg(dev, state, "legacy class "); | 853 | pm_dev_dbg(dev, state, "legacy class "); |
905 | error = legacy_suspend(dev, state, dev->class->suspend); | 854 | error = legacy_suspend(dev, state, dev->class->suspend); |
@@ -909,14 +858,23 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
909 | 858 | ||
910 | if (dev->bus) { | 859 | if (dev->bus) { |
911 | if (dev->bus->pm) { | 860 | if (dev->bus->pm) { |
912 | pm_dev_dbg(dev, state, ""); | 861 | info = "bus "; |
913 | error = pm_op(dev, dev->bus->pm, state); | 862 | callback = pm_op(dev->bus->pm, state); |
914 | } else if (dev->bus->suspend) { | 863 | } else if (dev->bus->suspend) { |
915 | pm_dev_dbg(dev, state, "legacy "); | 864 | pm_dev_dbg(dev, state, "legacy bus "); |
916 | error = legacy_suspend(dev, state, dev->bus->suspend); | 865 | error = legacy_suspend(dev, state, dev->bus->suspend); |
866 | goto End; | ||
917 | } | 867 | } |
918 | } | 868 | } |
919 | 869 | ||
870 | Run: | ||
871 | if (!callback && dev->driver && dev->driver->pm) { | ||
872 | info = "driver "; | ||
873 | callback = pm_op(dev->driver->pm, state); | ||
874 | } | ||
875 | |||
876 | error = dpm_run_callback(callback, dev, state, info); | ||
877 | |||
920 | End: | 878 | End: |
921 | if (!error) { | 879 | if (!error) { |
922 | dev->power.is_suspended = true; | 880 | dev->power.is_suspended = true; |
@@ -1022,6 +980,8 @@ int dpm_suspend(pm_message_t state) | |||
1022 | */ | 980 | */ |
1023 | static int device_prepare(struct device *dev, pm_message_t state) | 981 | static int device_prepare(struct device *dev, pm_message_t state) |
1024 | { | 982 | { |
983 | int (*callback)(struct device *) = NULL; | ||
984 | char *info = NULL; | ||
1025 | int error = 0; | 985 | int error = 0; |
1026 | 986 | ||
1027 | device_lock(dev); | 987 | device_lock(dev); |
@@ -1029,34 +989,29 @@ static int device_prepare(struct device *dev, pm_message_t state) | |||
1029 | dev->power.wakeup_path = device_may_wakeup(dev); | 989 | dev->power.wakeup_path = device_may_wakeup(dev); |
1030 | 990 | ||
1031 | if (dev->pm_domain) { | 991 | if (dev->pm_domain) { |
1032 | pm_dev_dbg(dev, state, "preparing power domain "); | 992 | info = "preparing power domain "; |
1033 | if (dev->pm_domain->ops.prepare) | 993 | callback = dev->pm_domain->ops.prepare; |
1034 | error = dev->pm_domain->ops.prepare(dev); | ||
1035 | suspend_report_result(dev->pm_domain->ops.prepare, error); | ||
1036 | if (error) | ||
1037 | goto End; | ||
1038 | } else if (dev->type && dev->type->pm) { | 994 | } else if (dev->type && dev->type->pm) { |
1039 | pm_dev_dbg(dev, state, "preparing type "); | 995 | info = "preparing type "; |
1040 | if (dev->type->pm->prepare) | 996 | callback = dev->type->pm->prepare; |
1041 | error = dev->type->pm->prepare(dev); | ||
1042 | suspend_report_result(dev->type->pm->prepare, error); | ||
1043 | if (error) | ||
1044 | goto End; | ||
1045 | } else if (dev->class && dev->class->pm) { | 997 | } else if (dev->class && dev->class->pm) { |
1046 | pm_dev_dbg(dev, state, "preparing class "); | 998 | info = "preparing class "; |
1047 | if (dev->class->pm->prepare) | 999 | callback = dev->class->pm->prepare; |
1048 | error = dev->class->pm->prepare(dev); | ||
1049 | suspend_report_result(dev->class->pm->prepare, error); | ||
1050 | if (error) | ||
1051 | goto End; | ||
1052 | } else if (dev->bus && dev->bus->pm) { | 1000 | } else if (dev->bus && dev->bus->pm) { |
1053 | pm_dev_dbg(dev, state, "preparing "); | 1001 | info = "preparing bus "; |
1054 | if (dev->bus->pm->prepare) | 1002 | callback = dev->bus->pm->prepare; |
1055 | error = dev->bus->pm->prepare(dev); | 1003 | } |
1056 | suspend_report_result(dev->bus->pm->prepare, error); | 1004 | |
1005 | if (!callback && dev->driver && dev->driver->pm) { | ||
1006 | info = "preparing driver "; | ||
1007 | callback = dev->driver->pm->prepare; | ||
1008 | } | ||
1009 | |||
1010 | if (callback) { | ||
1011 | error = callback(dev); | ||
1012 | suspend_report_result(callback, error); | ||
1057 | } | 1013 | } |
1058 | 1014 | ||
1059 | End: | ||
1060 | device_unlock(dev); | 1015 | device_unlock(dev); |
1061 | 1016 | ||
1062 | return error; | 1017 | return error; |