diff options
author | David S. Miller <davem@davemloft.net> | 2010-01-23 01:45:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-23 01:45:46 -0500 |
commit | 6be325719b3e54624397e413efd4b33a997e55a3 (patch) | |
tree | 57f321a56794cab2222e179b16731e0d76a4a68a /drivers/base/power/main.c | |
parent | 26d92f9276a56d55511a427fb70bd70886af647a (diff) | |
parent | 92dcffb916d309aa01778bf8963a6932e4014d07 (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r-- | drivers/base/power/main.c | 145 |
1 files changed, 127 insertions, 18 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 8aa2443182d5..a5142bddef41 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
@@ -23,8 +23,8 @@ | |||
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/pm_runtime.h> | 24 | #include <linux/pm_runtime.h> |
25 | #include <linux/resume-trace.h> | 25 | #include <linux/resume-trace.h> |
26 | #include <linux/rwsem.h> | ||
27 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/sched.h> | ||
28 | 28 | ||
29 | #include "../base.h" | 29 | #include "../base.h" |
30 | #include "power.h" | 30 | #include "power.h" |
@@ -161,6 +161,32 @@ void device_pm_move_last(struct device *dev) | |||
161 | list_move_tail(&dev->power.entry, &dpm_list); | 161 | list_move_tail(&dev->power.entry, &dpm_list); |
162 | } | 162 | } |
163 | 163 | ||
164 | static ktime_t initcall_debug_start(struct device *dev) | ||
165 | { | ||
166 | ktime_t calltime = ktime_set(0, 0); | ||
167 | |||
168 | if (initcall_debug) { | ||
169 | pr_info("calling %s+ @ %i\n", | ||
170 | dev_name(dev), task_pid_nr(current)); | ||
171 | calltime = ktime_get(); | ||
172 | } | ||
173 | |||
174 | return calltime; | ||
175 | } | ||
176 | |||
177 | static void initcall_debug_report(struct device *dev, ktime_t calltime, | ||
178 | int error) | ||
179 | { | ||
180 | ktime_t delta, rettime; | ||
181 | |||
182 | if (initcall_debug) { | ||
183 | rettime = ktime_get(); | ||
184 | delta = ktime_sub(rettime, calltime); | ||
185 | pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), | ||
186 | error, (unsigned long long)ktime_to_ns(delta) >> 10); | ||
187 | } | ||
188 | } | ||
189 | |||
164 | /** | 190 | /** |
165 | * pm_op - Execute the PM operation appropriate for given PM event. | 191 | * pm_op - Execute the PM operation appropriate for given PM event. |
166 | * @dev: Device to handle. | 192 | * @dev: Device to handle. |
@@ -172,6 +198,9 @@ static int pm_op(struct device *dev, | |||
172 | pm_message_t state) | 198 | pm_message_t state) |
173 | { | 199 | { |
174 | int error = 0; | 200 | int error = 0; |
201 | ktime_t calltime; | ||
202 | |||
203 | calltime = initcall_debug_start(dev); | ||
175 | 204 | ||
176 | switch (state.event) { | 205 | switch (state.event) { |
177 | #ifdef CONFIG_SUSPEND | 206 | #ifdef CONFIG_SUSPEND |
@@ -219,6 +248,9 @@ static int pm_op(struct device *dev, | |||
219 | default: | 248 | default: |
220 | error = -EINVAL; | 249 | error = -EINVAL; |
221 | } | 250 | } |
251 | |||
252 | initcall_debug_report(dev, calltime, error); | ||
253 | |||
222 | return error; | 254 | return error; |
223 | } | 255 | } |
224 | 256 | ||
@@ -236,6 +268,13 @@ static int pm_noirq_op(struct device *dev, | |||
236 | pm_message_t state) | 268 | pm_message_t state) |
237 | { | 269 | { |
238 | int error = 0; | 270 | int error = 0; |
271 | ktime_t calltime, delta, rettime; | ||
272 | |||
273 | if (initcall_debug) { | ||
274 | pr_info("calling %s_i+ @ %i\n", | ||
275 | dev_name(dev), task_pid_nr(current)); | ||
276 | calltime = ktime_get(); | ||
277 | } | ||
239 | 278 | ||
240 | switch (state.event) { | 279 | switch (state.event) { |
241 | #ifdef CONFIG_SUSPEND | 280 | #ifdef CONFIG_SUSPEND |
@@ -283,6 +322,15 @@ static int pm_noirq_op(struct device *dev, | |||
283 | default: | 322 | default: |
284 | error = -EINVAL; | 323 | error = -EINVAL; |
285 | } | 324 | } |
325 | |||
326 | if (initcall_debug) { | ||
327 | rettime = ktime_get(); | ||
328 | delta = ktime_sub(rettime, calltime); | ||
329 | printk("initcall %s_i+ returned %d after %Ld usecs\n", | ||
330 | dev_name(dev), error, | ||
331 | (unsigned long long)ktime_to_ns(delta) >> 10); | ||
332 | } | ||
333 | |||
286 | return error; | 334 | return error; |
287 | } | 335 | } |
288 | 336 | ||
@@ -324,6 +372,23 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, | |||
324 | kobject_name(&dev->kobj), pm_verb(state.event), info, error); | 372 | kobject_name(&dev->kobj), pm_verb(state.event), info, error); |
325 | } | 373 | } |
326 | 374 | ||
375 | static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) | ||
376 | { | ||
377 | ktime_t calltime; | ||
378 | s64 usecs64; | ||
379 | int usecs; | ||
380 | |||
381 | calltime = ktime_get(); | ||
382 | usecs64 = ktime_to_ns(ktime_sub(calltime, starttime)); | ||
383 | do_div(usecs64, NSEC_PER_USEC); | ||
384 | usecs = usecs64; | ||
385 | if (usecs == 0) | ||
386 | usecs = 1; | ||
387 | pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs\n", | ||
388 | info ?: "", info ? " " : "", pm_verb(state.event), | ||
389 | usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); | ||
390 | } | ||
391 | |||
327 | /*------------------------- Resume routines -------------------------*/ | 392 | /*------------------------- Resume routines -------------------------*/ |
328 | 393 | ||
329 | /** | 394 | /** |
@@ -341,14 +406,11 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) | |||
341 | TRACE_DEVICE(dev); | 406 | TRACE_DEVICE(dev); |
342 | TRACE_RESUME(0); | 407 | TRACE_RESUME(0); |
343 | 408 | ||
344 | if (!dev->bus) | 409 | if (dev->bus && dev->bus->pm) { |
345 | goto End; | ||
346 | |||
347 | if (dev->bus->pm) { | ||
348 | pm_dev_dbg(dev, state, "EARLY "); | 410 | pm_dev_dbg(dev, state, "EARLY "); |
349 | error = pm_noirq_op(dev, dev->bus->pm, state); | 411 | error = pm_noirq_op(dev, dev->bus->pm, state); |
350 | } | 412 | } |
351 | End: | 413 | |
352 | TRACE_RESUME(error); | 414 | TRACE_RESUME(error); |
353 | return error; | 415 | return error; |
354 | } | 416 | } |
@@ -363,6 +425,7 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) | |||
363 | void dpm_resume_noirq(pm_message_t state) | 425 | void dpm_resume_noirq(pm_message_t state) |
364 | { | 426 | { |
365 | struct device *dev; | 427 | struct device *dev; |
428 | ktime_t starttime = ktime_get(); | ||
366 | 429 | ||
367 | mutex_lock(&dpm_list_mtx); | 430 | mutex_lock(&dpm_list_mtx); |
368 | transition_started = false; | 431 | transition_started = false; |
@@ -376,11 +439,32 @@ void dpm_resume_noirq(pm_message_t state) | |||
376 | pm_dev_err(dev, state, " early", error); | 439 | pm_dev_err(dev, state, " early", error); |
377 | } | 440 | } |
378 | mutex_unlock(&dpm_list_mtx); | 441 | mutex_unlock(&dpm_list_mtx); |
442 | dpm_show_time(starttime, state, "early"); | ||
379 | resume_device_irqs(); | 443 | resume_device_irqs(); |
380 | } | 444 | } |
381 | EXPORT_SYMBOL_GPL(dpm_resume_noirq); | 445 | EXPORT_SYMBOL_GPL(dpm_resume_noirq); |
382 | 446 | ||
383 | /** | 447 | /** |
448 | * legacy_resume - Execute a legacy (bus or class) resume callback for device. | ||
449 | * @dev: Device to resume. | ||
450 | * @cb: Resume callback to execute. | ||
451 | */ | ||
452 | static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) | ||
453 | { | ||
454 | int error; | ||
455 | ktime_t calltime; | ||
456 | |||
457 | calltime = initcall_debug_start(dev); | ||
458 | |||
459 | error = cb(dev); | ||
460 | suspend_report_result(cb, error); | ||
461 | |||
462 | initcall_debug_report(dev, calltime, error); | ||
463 | |||
464 | return error; | ||
465 | } | ||
466 | |||
467 | /** | ||
384 | * device_resume - Execute "resume" callbacks for given device. | 468 | * device_resume - Execute "resume" callbacks for given device. |
385 | * @dev: Device to handle. | 469 | * @dev: Device to handle. |
386 | * @state: PM transition of the system being carried out. | 470 | * @state: PM transition of the system being carried out. |
@@ -400,7 +484,7 @@ static int device_resume(struct device *dev, pm_message_t state) | |||
400 | error = pm_op(dev, dev->bus->pm, state); | 484 | error = pm_op(dev, dev->bus->pm, state); |
401 | } else if (dev->bus->resume) { | 485 | } else if (dev->bus->resume) { |
402 | pm_dev_dbg(dev, state, "legacy "); | 486 | pm_dev_dbg(dev, state, "legacy "); |
403 | error = dev->bus->resume(dev); | 487 | error = legacy_resume(dev, dev->bus->resume); |
404 | } | 488 | } |
405 | if (error) | 489 | if (error) |
406 | goto End; | 490 | goto End; |
@@ -421,7 +505,7 @@ static int device_resume(struct device *dev, pm_message_t state) | |||
421 | error = pm_op(dev, dev->class->pm, state); | 505 | error = pm_op(dev, dev->class->pm, state); |
422 | } else if (dev->class->resume) { | 506 | } else if (dev->class->resume) { |
423 | pm_dev_dbg(dev, state, "legacy class "); | 507 | pm_dev_dbg(dev, state, "legacy class "); |
424 | error = dev->class->resume(dev); | 508 | error = legacy_resume(dev, dev->class->resume); |
425 | } | 509 | } |
426 | } | 510 | } |
427 | End: | 511 | End: |
@@ -441,6 +525,7 @@ static int device_resume(struct device *dev, pm_message_t state) | |||
441 | static void dpm_resume(pm_message_t state) | 525 | static void dpm_resume(pm_message_t state) |
442 | { | 526 | { |
443 | struct list_head list; | 527 | struct list_head list; |
528 | ktime_t starttime = ktime_get(); | ||
444 | 529 | ||
445 | INIT_LIST_HEAD(&list); | 530 | INIT_LIST_HEAD(&list); |
446 | mutex_lock(&dpm_list_mtx); | 531 | mutex_lock(&dpm_list_mtx); |
@@ -469,6 +554,7 @@ static void dpm_resume(pm_message_t state) | |||
469 | } | 554 | } |
470 | list_splice(&list, &dpm_list); | 555 | list_splice(&list, &dpm_list); |
471 | mutex_unlock(&dpm_list_mtx); | 556 | mutex_unlock(&dpm_list_mtx); |
557 | dpm_show_time(starttime, state, NULL); | ||
472 | } | 558 | } |
473 | 559 | ||
474 | /** | 560 | /** |
@@ -521,7 +607,7 @@ static void dpm_complete(pm_message_t state) | |||
521 | mutex_unlock(&dpm_list_mtx); | 607 | mutex_unlock(&dpm_list_mtx); |
522 | 608 | ||
523 | device_complete(dev, state); | 609 | device_complete(dev, state); |
524 | pm_runtime_put_noidle(dev); | 610 | pm_runtime_put_sync(dev); |
525 | 611 | ||
526 | mutex_lock(&dpm_list_mtx); | 612 | mutex_lock(&dpm_list_mtx); |
527 | } | 613 | } |
@@ -584,10 +670,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) | |||
584 | { | 670 | { |
585 | int error = 0; | 671 | int error = 0; |
586 | 672 | ||
587 | if (!dev->bus) | 673 | if (dev->bus && dev->bus->pm) { |
588 | return 0; | ||
589 | |||
590 | if (dev->bus->pm) { | ||
591 | pm_dev_dbg(dev, state, "LATE "); | 674 | pm_dev_dbg(dev, state, "LATE "); |
592 | error = pm_noirq_op(dev, dev->bus->pm, state); | 675 | error = pm_noirq_op(dev, dev->bus->pm, state); |
593 | } | 676 | } |
@@ -604,6 +687,7 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) | |||
604 | int dpm_suspend_noirq(pm_message_t state) | 687 | int dpm_suspend_noirq(pm_message_t state) |
605 | { | 688 | { |
606 | struct device *dev; | 689 | struct device *dev; |
690 | ktime_t starttime = ktime_get(); | ||
607 | int error = 0; | 691 | int error = 0; |
608 | 692 | ||
609 | suspend_device_irqs(); | 693 | suspend_device_irqs(); |
@@ -619,11 +703,35 @@ int dpm_suspend_noirq(pm_message_t state) | |||
619 | mutex_unlock(&dpm_list_mtx); | 703 | mutex_unlock(&dpm_list_mtx); |
620 | if (error) | 704 | if (error) |
621 | dpm_resume_noirq(resume_event(state)); | 705 | dpm_resume_noirq(resume_event(state)); |
706 | else | ||
707 | dpm_show_time(starttime, state, "late"); | ||
622 | return error; | 708 | return error; |
623 | } | 709 | } |
624 | EXPORT_SYMBOL_GPL(dpm_suspend_noirq); | 710 | EXPORT_SYMBOL_GPL(dpm_suspend_noirq); |
625 | 711 | ||
626 | /** | 712 | /** |
713 | * legacy_suspend - Execute a legacy (bus or class) suspend callback for device. | ||
714 | * @dev: Device to suspend. | ||
715 | * @state: PM transition of the system being carried out. | ||
716 | * @cb: Suspend callback to execute. | ||
717 | */ | ||
718 | static int legacy_suspend(struct device *dev, pm_message_t state, | ||
719 | int (*cb)(struct device *dev, pm_message_t state)) | ||
720 | { | ||
721 | int error; | ||
722 | ktime_t calltime; | ||
723 | |||
724 | calltime = initcall_debug_start(dev); | ||
725 | |||
726 | error = cb(dev, state); | ||
727 | suspend_report_result(cb, error); | ||
728 | |||
729 | initcall_debug_report(dev, calltime, error); | ||
730 | |||
731 | return error; | ||
732 | } | ||
733 | |||
734 | /** | ||
627 | * device_suspend - Execute "suspend" callbacks for given device. | 735 | * device_suspend - Execute "suspend" callbacks for given device. |
628 | * @dev: Device to handle. | 736 | * @dev: Device to handle. |
629 | * @state: PM transition of the system being carried out. | 737 | * @state: PM transition of the system being carried out. |
@@ -640,8 +748,7 @@ static int device_suspend(struct device *dev, pm_message_t state) | |||
640 | error = pm_op(dev, dev->class->pm, state); | 748 | error = pm_op(dev, dev->class->pm, state); |
641 | } else if (dev->class->suspend) { | 749 | } else if (dev->class->suspend) { |
642 | pm_dev_dbg(dev, state, "legacy class "); | 750 | pm_dev_dbg(dev, state, "legacy class "); |
643 | error = dev->class->suspend(dev, state); | 751 | error = legacy_suspend(dev, state, dev->class->suspend); |
644 | suspend_report_result(dev->class->suspend, error); | ||
645 | } | 752 | } |
646 | if (error) | 753 | if (error) |
647 | goto End; | 754 | goto End; |
@@ -662,8 +769,7 @@ static int device_suspend(struct device *dev, pm_message_t state) | |||
662 | error = pm_op(dev, dev->bus->pm, state); | 769 | error = pm_op(dev, dev->bus->pm, state); |
663 | } else if (dev->bus->suspend) { | 770 | } else if (dev->bus->suspend) { |
664 | pm_dev_dbg(dev, state, "legacy "); | 771 | pm_dev_dbg(dev, state, "legacy "); |
665 | error = dev->bus->suspend(dev, state); | 772 | error = legacy_suspend(dev, state, dev->bus->suspend); |
666 | suspend_report_result(dev->bus->suspend, error); | ||
667 | } | 773 | } |
668 | } | 774 | } |
669 | End: | 775 | End: |
@@ -679,6 +785,7 @@ static int device_suspend(struct device *dev, pm_message_t state) | |||
679 | static int dpm_suspend(pm_message_t state) | 785 | static int dpm_suspend(pm_message_t state) |
680 | { | 786 | { |
681 | struct list_head list; | 787 | struct list_head list; |
788 | ktime_t starttime = ktime_get(); | ||
682 | int error = 0; | 789 | int error = 0; |
683 | 790 | ||
684 | INIT_LIST_HEAD(&list); | 791 | INIT_LIST_HEAD(&list); |
@@ -704,6 +811,8 @@ static int dpm_suspend(pm_message_t state) | |||
704 | } | 811 | } |
705 | list_splice(&list, dpm_list.prev); | 812 | list_splice(&list, dpm_list.prev); |
706 | mutex_unlock(&dpm_list_mtx); | 813 | mutex_unlock(&dpm_list_mtx); |
814 | if (!error) | ||
815 | dpm_show_time(starttime, state, NULL); | ||
707 | return error; | 816 | return error; |
708 | } | 817 | } |
709 | 818 | ||
@@ -772,7 +881,7 @@ static int dpm_prepare(pm_message_t state) | |||
772 | pm_runtime_get_noresume(dev); | 881 | pm_runtime_get_noresume(dev); |
773 | if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { | 882 | if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { |
774 | /* Wake-up requested during system sleep transition. */ | 883 | /* Wake-up requested during system sleep transition. */ |
775 | pm_runtime_put_noidle(dev); | 884 | pm_runtime_put_sync(dev); |
776 | error = -EBUSY; | 885 | error = -EBUSY; |
777 | } else { | 886 | } else { |
778 | error = device_prepare(dev, state); | 887 | error = device_prepare(dev, state); |