diff options
28 files changed, 631 insertions, 84 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power new file mode 100644 index 000000000000..6123c523bfd7 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | What: /sys/devices/.../power/ | ||
| 2 | Date: January 2009 | ||
| 3 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 4 | Description: | ||
| 5 | The /sys/devices/.../power directory contains attributes | ||
| 6 | allowing the user space to check and modify some power | ||
| 7 | management related properties of given device. | ||
| 8 | |||
| 9 | What: /sys/devices/.../power/wakeup | ||
| 10 | Date: January 2009 | ||
| 11 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 12 | Description: | ||
| 13 | The /sys/devices/.../power/wakeup attribute allows the user | ||
| 14 | space to check if the device is enabled to wake up the system | ||
| 15 | from sleep states, such as the memory sleep state (suspend to | ||
| 16 | RAM) and hibernation (suspend to disk), and to enable or disable | ||
| 17 | it to do that as desired. | ||
| 18 | |||
| 19 | Some devices support "wakeup" events, which are hardware signals | ||
| 20 | used to activate the system from a sleep state. Such devices | ||
| 21 | have one of the following two values for the sysfs power/wakeup | ||
| 22 | file: | ||
| 23 | |||
| 24 | + "enabled\n" to issue the events; | ||
| 25 | + "disabled\n" not to do so; | ||
| 26 | |||
| 27 | In that cases the user space can change the setting represented | ||
| 28 | by the contents of this file by writing either "enabled", or | ||
| 29 | "disabled" to it. | ||
| 30 | |||
| 31 | For the devices that are not capable of generating system wakeup | ||
| 32 | events this file contains "\n". In that cases the user space | ||
| 33 | cannot modify the contents of this file and the device cannot be | ||
| 34 | enabled to wake up the system. | ||
| 35 | |||
| 36 | What: /sys/devices/.../power/control | ||
| 37 | Date: January 2009 | ||
| 38 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 39 | Description: | ||
| 40 | The /sys/devices/.../power/control attribute allows the user | ||
| 41 | space to control the run-time power management of the device. | ||
| 42 | |||
| 43 | All devices have one of the following two values for the | ||
| 44 | power/control file: | ||
| 45 | |||
| 46 | + "auto\n" to allow the device to be power managed at run time; | ||
| 47 | + "on\n" to prevent the device from being power managed; | ||
| 48 | |||
| 49 | The default for all devices is "auto", which means that they may | ||
| 50 | be subject to automatic power management, depending on their | ||
| 51 | drivers. Changing this attribute to "on" prevents the driver | ||
| 52 | from power managing the device at run time. Doing that while | ||
| 53 | the device is suspended causes it to be woken up. | ||
| 54 | |||
| 55 | What: /sys/devices/.../power/async | ||
| 56 | Date: January 2009 | ||
| 57 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 58 | Description: | ||
| 59 | The /sys/devices/.../async attribute allows the user space to | ||
| 60 | enable or diasble the device's suspend and resume callbacks to | ||
| 61 | be executed asynchronously (ie. in separate threads, in parallel | ||
| 62 | with the main suspend/resume thread) during system-wide power | ||
| 63 | transitions (eg. suspend to RAM, hibernation). | ||
| 64 | |||
| 65 | All devices have one of the following two values for the | ||
| 66 | power/async file: | ||
| 67 | |||
| 68 | + "enabled\n" to permit the asynchronous suspend/resume; | ||
| 69 | + "disabled\n" to forbid it; | ||
| 70 | |||
| 71 | The value of this attribute may be changed by writing either | ||
| 72 | "enabled", or "disabled" to it. | ||
| 73 | |||
| 74 | It generally is unsafe to permit the asynchronous suspend/resume | ||
| 75 | of a device unless it is certain that all of the PM dependencies | ||
| 76 | of the device are known to the PM core. However, for some | ||
| 77 | devices this attribute is set to "enabled" by bus type code or | ||
| 78 | device drivers and in that cases it should be safe to leave the | ||
| 79 | default value. | ||
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index dcff4d0623ad..d6a801f45b48 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power | |||
| @@ -101,3 +101,16 @@ Description: | |||
| 101 | 101 | ||
| 102 | CAUTION: Using it will cause your machine's real-time (CMOS) | 102 | CAUTION: Using it will cause your machine's real-time (CMOS) |
| 103 | clock to be set to a random invalid time after a resume. | 103 | clock to be set to a random invalid time after a resume. |
| 104 | |||
| 105 | What: /sys/power/pm_async | ||
| 106 | Date: January 2009 | ||
| 107 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 108 | Description: | ||
| 109 | The /sys/power/pm_async file controls the switch allowing the | ||
| 110 | user space to enable or disable asynchronous suspend and resume | ||
| 111 | of devices. If enabled, this feature will cause some device | ||
| 112 | drivers' suspend and resume callbacks to be executed in parallel | ||
| 113 | with each other and with the main suspend thread. It is enabled | ||
| 114 | if this file contains "1", which is the default. It may be | ||
| 115 | disabled by writing "0" to this file, in which case all devices | ||
| 116 | will be suspended and resumed synchronously. | ||
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 0a46833c1b76..b9eba900e0f0 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt | |||
| @@ -64,6 +64,17 @@ Who: Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com> | |||
| 64 | 64 | ||
| 65 | --------------------------- | 65 | --------------------------- |
| 66 | 66 | ||
| 67 | What: Deprecated snapshot ioctls | ||
| 68 | When: 2.6.36 | ||
| 69 | |||
| 70 | Why: The ioctls in kernel/power/user.c were marked as deprecated long time | ||
| 71 | ago. Now they notify users about that so that they need to replace | ||
| 72 | their userspace. After some more time, remove them completely. | ||
| 73 | |||
| 74 | Who: Jiri Slaby <jirislaby@gmail.com> | ||
| 75 | |||
| 76 | --------------------------- | ||
| 77 | |||
| 67 | What: The ieee80211_regdom module parameter | 78 | What: The ieee80211_regdom module parameter |
| 68 | When: March 2010 / desktop catchup | 79 | When: March 2010 / desktop catchup |
| 69 | 80 | ||
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a5142bddef41..0e26a6f6fd48 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/resume-trace.h> | 25 | #include <linux/resume-trace.h> |
| 26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
| 27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
| 28 | #include <linux/async.h> | ||
| 28 | 29 | ||
| 29 | #include "../base.h" | 30 | #include "../base.h" |
| 30 | #include "power.h" | 31 | #include "power.h" |
| @@ -42,6 +43,7 @@ | |||
| 42 | LIST_HEAD(dpm_list); | 43 | LIST_HEAD(dpm_list); |
| 43 | 44 | ||
| 44 | static DEFINE_MUTEX(dpm_list_mtx); | 45 | static DEFINE_MUTEX(dpm_list_mtx); |
| 46 | static pm_message_t pm_transition; | ||
| 45 | 47 | ||
| 46 | /* | 48 | /* |
| 47 | * Set once the preparation of devices for a PM transition has started, reset | 49 | * Set once the preparation of devices for a PM transition has started, reset |
| @@ -56,6 +58,7 @@ static bool transition_started; | |||
| 56 | void device_pm_init(struct device *dev) | 58 | void device_pm_init(struct device *dev) |
| 57 | { | 59 | { |
| 58 | dev->power.status = DPM_ON; | 60 | dev->power.status = DPM_ON; |
| 61 | init_completion(&dev->power.completion); | ||
| 59 | pm_runtime_init(dev); | 62 | pm_runtime_init(dev); |
| 60 | } | 63 | } |
| 61 | 64 | ||
| @@ -111,6 +114,7 @@ void device_pm_remove(struct device *dev) | |||
| 111 | pr_debug("PM: Removing info for %s:%s\n", | 114 | pr_debug("PM: Removing info for %s:%s\n", |
| 112 | dev->bus ? dev->bus->name : "No Bus", | 115 | dev->bus ? dev->bus->name : "No Bus", |
| 113 | kobject_name(&dev->kobj)); | 116 | kobject_name(&dev->kobj)); |
| 117 | complete_all(&dev->power.completion); | ||
| 114 | mutex_lock(&dpm_list_mtx); | 118 | mutex_lock(&dpm_list_mtx); |
| 115 | list_del_init(&dev->power.entry); | 119 | list_del_init(&dev->power.entry); |
| 116 | mutex_unlock(&dpm_list_mtx); | 120 | mutex_unlock(&dpm_list_mtx); |
| @@ -188,6 +192,31 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, | |||
| 188 | } | 192 | } |
| 189 | 193 | ||
| 190 | /** | 194 | /** |
| 195 | * dpm_wait - Wait for a PM operation to complete. | ||
| 196 | * @dev: Device to wait for. | ||
| 197 | * @async: If unset, wait only if the device's power.async_suspend flag is set. | ||
| 198 | */ | ||
| 199 | static void dpm_wait(struct device *dev, bool async) | ||
| 200 | { | ||
| 201 | if (!dev) | ||
| 202 | return; | ||
| 203 | |||
| 204 | if (async || (pm_async_enabled && dev->power.async_suspend)) | ||
| 205 | wait_for_completion(&dev->power.completion); | ||
| 206 | } | ||
| 207 | |||
| 208 | static int dpm_wait_fn(struct device *dev, void *async_ptr) | ||
| 209 | { | ||
| 210 | dpm_wait(dev, *((bool *)async_ptr)); | ||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static void dpm_wait_for_children(struct device *dev, bool async) | ||
| 215 | { | ||
| 216 | device_for_each_child(dev, &async, dpm_wait_fn); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 191 | * pm_op - Execute the PM operation appropriate for given PM event. | 220 | * pm_op - Execute the PM operation appropriate for given PM event. |
| 192 | * @dev: Device to handle. | 221 | * @dev: Device to handle. |
| 193 | * @ops: PM operations to choose from. | 222 | * @ops: PM operations to choose from. |
| @@ -271,8 +300,9 @@ static int pm_noirq_op(struct device *dev, | |||
| 271 | ktime_t calltime, delta, rettime; | 300 | ktime_t calltime, delta, rettime; |
| 272 | 301 | ||
| 273 | if (initcall_debug) { | 302 | if (initcall_debug) { |
| 274 | pr_info("calling %s_i+ @ %i\n", | 303 | pr_info("calling %s+ @ %i, parent: %s\n", |
| 275 | dev_name(dev), task_pid_nr(current)); | 304 | dev_name(dev), task_pid_nr(current), |
| 305 | dev->parent ? dev_name(dev->parent) : "none"); | ||
| 276 | calltime = ktime_get(); | 306 | calltime = ktime_get(); |
| 277 | } | 307 | } |
| 278 | 308 | ||
| @@ -468,16 +498,20 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) | |||
| 468 | * device_resume - Execute "resume" callbacks for given device. | 498 | * device_resume - Execute "resume" callbacks for given device. |
| 469 | * @dev: Device to handle. | 499 | * @dev: Device to handle. |
| 470 | * @state: PM transition of the system being carried out. | 500 | * @state: PM transition of the system being carried out. |
| 501 | * @async: If true, the device is being resumed asynchronously. | ||
| 471 | */ | 502 | */ |
| 472 | static int device_resume(struct device *dev, pm_message_t state) | 503 | static int device_resume(struct device *dev, pm_message_t state, bool async) |
| 473 | { | 504 | { |
| 474 | int error = 0; | 505 | int error = 0; |
| 475 | 506 | ||
| 476 | TRACE_DEVICE(dev); | 507 | TRACE_DEVICE(dev); |
| 477 | TRACE_RESUME(0); | 508 | TRACE_RESUME(0); |
| 478 | 509 | ||
| 510 | dpm_wait(dev->parent, async); | ||
| 479 | down(&dev->sem); | 511 | down(&dev->sem); |
| 480 | 512 | ||
| 513 | dev->power.status = DPM_RESUMING; | ||
| 514 | |||
| 481 | if (dev->bus) { | 515 | if (dev->bus) { |
| 482 | if (dev->bus->pm) { | 516 | if (dev->bus->pm) { |
| 483 | pm_dev_dbg(dev, state, ""); | 517 | pm_dev_dbg(dev, state, ""); |
| @@ -510,11 +544,29 @@ static int device_resume(struct device *dev, pm_message_t state) | |||
| 510 | } | 544 | } |
| 511 | End: | 545 | End: |
| 512 | up(&dev->sem); | 546 | up(&dev->sem); |
| 547 | complete_all(&dev->power.completion); | ||
| 513 | 548 | ||
| 514 | TRACE_RESUME(error); | 549 | TRACE_RESUME(error); |
| 515 | return error; | 550 | return error; |
| 516 | } | 551 | } |
| 517 | 552 | ||
| 553 | static void async_resume(void *data, async_cookie_t cookie) | ||
| 554 | { | ||
| 555 | struct device *dev = (struct device *)data; | ||
| 556 | int error; | ||
| 557 | |||
| 558 | error = device_resume(dev, pm_transition, true); | ||
| 559 | if (error) | ||
| 560 | pm_dev_err(dev, pm_transition, " async", error); | ||
| 561 | put_device(dev); | ||
| 562 | } | ||
| 563 | |||
| 564 | static bool is_async(struct device *dev) | ||
| 565 | { | ||
| 566 | return dev->power.async_suspend && pm_async_enabled | ||
| 567 | && !pm_trace_is_enabled(); | ||
| 568 | } | ||
| 569 | |||
| 518 | /** | 570 | /** |
| 519 | * dpm_resume - Execute "resume" callbacks for non-sysdev devices. | 571 | * dpm_resume - Execute "resume" callbacks for non-sysdev devices. |
| 520 | * @state: PM transition of the system being carried out. | 572 | * @state: PM transition of the system being carried out. |
| @@ -525,21 +577,33 @@ static int device_resume(struct device *dev, pm_message_t state) | |||
| 525 | static void dpm_resume(pm_message_t state) | 577 | static void dpm_resume(pm_message_t state) |
| 526 | { | 578 | { |
| 527 | struct list_head list; | 579 | struct list_head list; |
| 580 | struct device *dev; | ||
| 528 | ktime_t starttime = ktime_get(); | 581 | ktime_t starttime = ktime_get(); |
| 529 | 582 | ||
| 530 | INIT_LIST_HEAD(&list); | 583 | INIT_LIST_HEAD(&list); |
| 531 | mutex_lock(&dpm_list_mtx); | 584 | mutex_lock(&dpm_list_mtx); |
| 532 | while (!list_empty(&dpm_list)) { | 585 | pm_transition = state; |
| 533 | struct device *dev = to_device(dpm_list.next); | 586 | |
| 587 | list_for_each_entry(dev, &dpm_list, power.entry) { | ||
| 588 | if (dev->power.status < DPM_OFF) | ||
| 589 | continue; | ||
| 590 | |||
| 591 | INIT_COMPLETION(dev->power.completion); | ||
| 592 | if (is_async(dev)) { | ||
| 593 | get_device(dev); | ||
| 594 | async_schedule(async_resume, dev); | ||
| 595 | } | ||
| 596 | } | ||
| 534 | 597 | ||
| 598 | while (!list_empty(&dpm_list)) { | ||
| 599 | dev = to_device(dpm_list.next); | ||
| 535 | get_device(dev); | 600 | get_device(dev); |
| 536 | if (dev->power.status >= DPM_OFF) { | 601 | if (dev->power.status >= DPM_OFF && !is_async(dev)) { |
| 537 | int error; | 602 | int error; |
| 538 | 603 | ||
| 539 | dev->power.status = DPM_RESUMING; | ||
| 540 | mutex_unlock(&dpm_list_mtx); | 604 | mutex_unlock(&dpm_list_mtx); |
| 541 | 605 | ||
| 542 | error = device_resume(dev, state); | 606 | error = device_resume(dev, state, false); |
| 543 | 607 | ||
| 544 | mutex_lock(&dpm_list_mtx); | 608 | mutex_lock(&dpm_list_mtx); |
| 545 | if (error) | 609 | if (error) |
| @@ -554,6 +618,7 @@ static void dpm_resume(pm_message_t state) | |||
| 554 | } | 618 | } |
| 555 | list_splice(&list, &dpm_list); | 619 | list_splice(&list, &dpm_list); |
| 556 | mutex_unlock(&dpm_list_mtx); | 620 | mutex_unlock(&dpm_list_mtx); |
| 621 | async_synchronize_full(); | ||
| 557 | dpm_show_time(starttime, state, NULL); | 622 | dpm_show_time(starttime, state, NULL); |
| 558 | } | 623 | } |
| 559 | 624 | ||
| @@ -731,17 +796,24 @@ static int legacy_suspend(struct device *dev, pm_message_t state, | |||
| 731 | return error; | 796 | return error; |
| 732 | } | 797 | } |
| 733 | 798 | ||
| 799 | static int async_error; | ||
| 800 | |||
| 734 | /** | 801 | /** |
| 735 | * device_suspend - Execute "suspend" callbacks for given device. | 802 | * device_suspend - Execute "suspend" callbacks for given device. |
| 736 | * @dev: Device to handle. | 803 | * @dev: Device to handle. |
| 737 | * @state: PM transition of the system being carried out. | 804 | * @state: PM transition of the system being carried out. |
| 805 | * @async: If true, the device is being suspended asynchronously. | ||
| 738 | */ | 806 | */ |
| 739 | static int device_suspend(struct device *dev, pm_message_t state) | 807 | static int __device_suspend(struct device *dev, pm_message_t state, bool async) |
| 740 | { | 808 | { |
| 741 | int error = 0; | 809 | int error = 0; |
| 742 | 810 | ||
| 811 | dpm_wait_for_children(dev, async); | ||
| 743 | down(&dev->sem); | 812 | down(&dev->sem); |
| 744 | 813 | ||
| 814 | if (async_error) | ||
| 815 | goto End; | ||
| 816 | |||
| 745 | if (dev->class) { | 817 | if (dev->class) { |
| 746 | if (dev->class->pm) { | 818 | if (dev->class->pm) { |
| 747 | pm_dev_dbg(dev, state, "class "); | 819 | pm_dev_dbg(dev, state, "class "); |
| @@ -772,12 +844,44 @@ static int device_suspend(struct device *dev, pm_message_t state) | |||
| 772 | error = legacy_suspend(dev, state, dev->bus->suspend); | 844 | error = legacy_suspend(dev, state, dev->bus->suspend); |
| 773 | } | 845 | } |
| 774 | } | 846 | } |
| 847 | |||
| 848 | if (!error) | ||
| 849 | dev->power.status = DPM_OFF; | ||
| 850 | |||
| 775 | End: | 851 | End: |
| 776 | up(&dev->sem); | 852 | up(&dev->sem); |
| 853 | complete_all(&dev->power.completion); | ||
| 777 | 854 | ||
| 778 | return error; | 855 | return error; |
| 779 | } | 856 | } |
| 780 | 857 | ||
| 858 | static void async_suspend(void *data, async_cookie_t cookie) | ||
| 859 | { | ||
| 860 | struct device *dev = (struct device *)data; | ||
| 861 | int error; | ||
| 862 | |||
| 863 | error = __device_suspend(dev, pm_transition, true); | ||
| 864 | if (error) { | ||
| 865 | pm_dev_err(dev, pm_transition, " async", error); | ||
| 866 | async_error = error; | ||
| 867 | } | ||
| 868 | |||
| 869 | put_device(dev); | ||
| 870 | } | ||
| 871 | |||
| 872 | static int device_suspend(struct device *dev) | ||
| 873 | { | ||
| 874 | INIT_COMPLETION(dev->power.completion); | ||
| 875 | |||
| 876 | if (pm_async_enabled && dev->power.async_suspend) { | ||
| 877 | get_device(dev); | ||
| 878 | async_schedule(async_suspend, dev); | ||
| 879 | return 0; | ||
| 880 | } | ||
| 881 | |||
| 882 | return __device_suspend(dev, pm_transition, false); | ||
| 883 | } | ||
| 884 | |||
| 781 | /** | 885 | /** |
| 782 | * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. | 886 | * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. |
| 783 | * @state: PM transition of the system being carried out. | 887 | * @state: PM transition of the system being carried out. |
| @@ -790,13 +894,15 @@ static int dpm_suspend(pm_message_t state) | |||
| 790 | 894 | ||
| 791 | INIT_LIST_HEAD(&list); | 895 | INIT_LIST_HEAD(&list); |
| 792 | mutex_lock(&dpm_list_mtx); | 896 | mutex_lock(&dpm_list_mtx); |
| 897 | pm_transition = state; | ||
| 898 | async_error = 0; | ||
| 793 | while (!list_empty(&dpm_list)) { | 899 | while (!list_empty(&dpm_list)) { |
| 794 | struct device *dev = to_device(dpm_list.prev); | 900 | struct device *dev = to_device(dpm_list.prev); |
| 795 | 901 | ||
| 796 | get_device(dev); | 902 | get_device(dev); |
| 797 | mutex_unlock(&dpm_list_mtx); | 903 | mutex_unlock(&dpm_list_mtx); |
| 798 | 904 | ||
| 799 | error = device_suspend(dev, state); | 905 | error = device_suspend(dev); |
| 800 | 906 | ||
| 801 | mutex_lock(&dpm_list_mtx); | 907 | mutex_lock(&dpm_list_mtx); |
| 802 | if (error) { | 908 | if (error) { |
| @@ -804,13 +910,17 @@ static int dpm_suspend(pm_message_t state) | |||
| 804 | put_device(dev); | 910 | put_device(dev); |
| 805 | break; | 911 | break; |
| 806 | } | 912 | } |
| 807 | dev->power.status = DPM_OFF; | ||
| 808 | if (!list_empty(&dev->power.entry)) | 913 | if (!list_empty(&dev->power.entry)) |
| 809 | list_move(&dev->power.entry, &list); | 914 | list_move(&dev->power.entry, &list); |
| 810 | put_device(dev); | 915 | put_device(dev); |
| 916 | if (async_error) | ||
| 917 | break; | ||
| 811 | } | 918 | } |
| 812 | list_splice(&list, dpm_list.prev); | 919 | list_splice(&list, dpm_list.prev); |
| 813 | mutex_unlock(&dpm_list_mtx); | 920 | mutex_unlock(&dpm_list_mtx); |
| 921 | async_synchronize_full(); | ||
| 922 | if (!error) | ||
| 923 | error = async_error; | ||
| 814 | if (!error) | 924 | if (!error) |
| 815 | dpm_show_time(starttime, state, NULL); | 925 | dpm_show_time(starttime, state, NULL); |
| 816 | return error; | 926 | return error; |
| @@ -936,3 +1046,14 @@ void __suspend_report_result(const char *function, void *fn, int ret) | |||
| 936 | printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); | 1046 | printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); |
| 937 | } | 1047 | } |
| 938 | EXPORT_SYMBOL_GPL(__suspend_report_result); | 1048 | EXPORT_SYMBOL_GPL(__suspend_report_result); |
| 1049 | |||
| 1050 | /** | ||
| 1051 | * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. | ||
| 1052 | * @dev: Device to wait for. | ||
| 1053 | * @subordinate: Device that needs to wait for @dev. | ||
| 1054 | */ | ||
| 1055 | void device_pm_wait_for_dev(struct device *subordinate, struct device *dev) | ||
| 1056 | { | ||
| 1057 | dpm_wait(dev, subordinate->power.async_suspend); | ||
| 1058 | } | ||
| 1059 | EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); | ||
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index b8fa1aa5225a..c0bd03c83b9c 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
| @@ -12,10 +12,10 @@ static inline void pm_runtime_remove(struct device *dev) {} | |||
| 12 | 12 | ||
| 13 | #ifdef CONFIG_PM_SLEEP | 13 | #ifdef CONFIG_PM_SLEEP |
| 14 | 14 | ||
| 15 | /* | 15 | /* kernel/power/main.c */ |
| 16 | * main.c | 16 | extern int pm_async_enabled; |
| 17 | */ | ||
| 18 | 17 | ||
| 18 | /* drivers/base/power/main.c */ | ||
| 19 | extern struct list_head dpm_list; /* The active device list */ | 19 | extern struct list_head dpm_list; /* The active device list */ |
| 20 | 20 | ||
| 21 | static inline struct device *to_device(struct list_head *entry) | 21 | static inline struct device *to_device(struct list_head *entry) |
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index f8b044e8aef7..626dd147b75f 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -1011,6 +1011,50 @@ void pm_runtime_enable(struct device *dev) | |||
| 1011 | EXPORT_SYMBOL_GPL(pm_runtime_enable); | 1011 | EXPORT_SYMBOL_GPL(pm_runtime_enable); |
| 1012 | 1012 | ||
| 1013 | /** | 1013 | /** |
| 1014 | * pm_runtime_forbid - Block run-time PM of a device. | ||
| 1015 | * @dev: Device to handle. | ||
| 1016 | * | ||
| 1017 | * Increase the device's usage count and clear its power.runtime_auto flag, | ||
| 1018 | * so that it cannot be suspended at run time until pm_runtime_allow() is called | ||
| 1019 | * for it. | ||
| 1020 | */ | ||
| 1021 | void pm_runtime_forbid(struct device *dev) | ||
| 1022 | { | ||
| 1023 | spin_lock_irq(&dev->power.lock); | ||
| 1024 | if (!dev->power.runtime_auto) | ||
| 1025 | goto out; | ||
| 1026 | |||
| 1027 | dev->power.runtime_auto = false; | ||
| 1028 | atomic_inc(&dev->power.usage_count); | ||
| 1029 | __pm_runtime_resume(dev, false); | ||
| 1030 | |||
| 1031 | out: | ||
| 1032 | spin_unlock_irq(&dev->power.lock); | ||
| 1033 | } | ||
| 1034 | EXPORT_SYMBOL_GPL(pm_runtime_forbid); | ||
| 1035 | |||
| 1036 | /** | ||
| 1037 | * pm_runtime_allow - Unblock run-time PM of a device. | ||
| 1038 | * @dev: Device to handle. | ||
| 1039 | * | ||
| 1040 | * Decrease the device's usage count and set its power.runtime_auto flag. | ||
| 1041 | */ | ||
| 1042 | void pm_runtime_allow(struct device *dev) | ||
| 1043 | { | ||
| 1044 | spin_lock_irq(&dev->power.lock); | ||
| 1045 | if (dev->power.runtime_auto) | ||
| 1046 | goto out; | ||
| 1047 | |||
| 1048 | dev->power.runtime_auto = true; | ||
| 1049 | if (atomic_dec_and_test(&dev->power.usage_count)) | ||
| 1050 | __pm_runtime_idle(dev); | ||
| 1051 | |||
| 1052 | out: | ||
| 1053 | spin_unlock_irq(&dev->power.lock); | ||
| 1054 | } | ||
| 1055 | EXPORT_SYMBOL_GPL(pm_runtime_allow); | ||
| 1056 | |||
| 1057 | /** | ||
| 1014 | * pm_runtime_init - Initialize run-time PM fields in given device object. | 1058 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
| 1015 | * @dev: Device object to initialize. | 1059 | * @dev: Device object to initialize. |
| 1016 | */ | 1060 | */ |
| @@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev) | |||
| 1028 | 1072 | ||
| 1029 | atomic_set(&dev->power.child_count, 0); | 1073 | atomic_set(&dev->power.child_count, 0); |
| 1030 | pm_suspend_ignore_children(dev, false); | 1074 | pm_suspend_ignore_children(dev, false); |
| 1075 | dev->power.runtime_auto = true; | ||
| 1031 | 1076 | ||
| 1032 | dev->power.request_pending = false; | 1077 | dev->power.request_pending = false; |
| 1033 | dev->power.request = RPM_REQ_NONE; | 1078 | dev->power.request = RPM_REQ_NONE; |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 596aeecfdffe..86fd9373447e 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
| @@ -4,9 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #include <linux/device.h> | 5 | #include <linux/device.h> |
| 6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
| 7 | #include <linux/pm_runtime.h> | ||
| 7 | #include "power.h" | 8 | #include "power.h" |
| 8 | 9 | ||
| 9 | /* | 10 | /* |
| 11 | * control - Report/change current runtime PM setting of the device | ||
| 12 | * | ||
| 13 | * Runtime power management of a device can be blocked with the help of | ||
| 14 | * this attribute. All devices have one of the following two values for | ||
| 15 | * the power/control file: | ||
| 16 | * | ||
| 17 | * + "auto\n" to allow the device to be power managed at run time; | ||
| 18 | * + "on\n" to prevent the device from being power managed at run time; | ||
| 19 | * | ||
| 20 | * The default for all devices is "auto", which means that devices may be | ||
| 21 | * subject to automatic power management, depending on their drivers. | ||
| 22 | * Changing this attribute to "on" prevents the driver from power managing | ||
| 23 | * the device at run time. Doing that while the device is suspended causes | ||
| 24 | * it to be woken up. | ||
| 25 | * | ||
| 10 | * wakeup - Report/change current wakeup option for device | 26 | * wakeup - Report/change current wakeup option for device |
| 11 | * | 27 | * |
| 12 | * Some devices support "wakeup" events, which are hardware signals | 28 | * Some devices support "wakeup" events, which are hardware signals |
| @@ -38,11 +54,61 @@ | |||
| 38 | * wakeup events internally (unless they are disabled), keeping | 54 | * wakeup events internally (unless they are disabled), keeping |
| 39 | * their hardware in low power modes whenever they're unused. This | 55 | * their hardware in low power modes whenever they're unused. This |
| 40 | * saves runtime power, without requiring system-wide sleep states. | 56 | * saves runtime power, without requiring system-wide sleep states. |
| 57 | * | ||
| 58 | * async - Report/change current async suspend setting for the device | ||
| 59 | * | ||
| 60 | * Asynchronous suspend and resume of the device during system-wide power | ||
| 61 | * state transitions can be enabled by writing "enabled" to this file. | ||
| 62 | * Analogously, if "disabled" is written to this file, the device will be | ||
| 63 | * suspended and resumed synchronously. | ||
| 64 | * | ||
| 65 | * All devices have one of the following two values for power/async: | ||
| 66 | * | ||
| 67 | * + "enabled\n" to permit the asynchronous suspend/resume of the device; | ||
| 68 | * + "disabled\n" to forbid it; | ||
| 69 | * | ||
| 70 | * NOTE: It generally is unsafe to permit the asynchronous suspend/resume | ||
| 71 | * of a device unless it is certain that all of the PM dependencies of the | ||
| 72 | * device are known to the PM core. However, for some devices this | ||
| 73 | * attribute is set to "enabled" by bus type code or device drivers and in | ||
| 74 | * that cases it should be safe to leave the default value. | ||
| 41 | */ | 75 | */ |
| 42 | 76 | ||
| 43 | static const char enabled[] = "enabled"; | 77 | static const char enabled[] = "enabled"; |
| 44 | static const char disabled[] = "disabled"; | 78 | static const char disabled[] = "disabled"; |
| 45 | 79 | ||
| 80 | #ifdef CONFIG_PM_RUNTIME | ||
| 81 | static const char ctrl_auto[] = "auto"; | ||
| 82 | static const char ctrl_on[] = "on"; | ||
| 83 | |||
| 84 | static ssize_t control_show(struct device *dev, struct device_attribute *attr, | ||
| 85 | char *buf) | ||
| 86 | { | ||
| 87 | return sprintf(buf, "%s\n", | ||
| 88 | dev->power.runtime_auto ? ctrl_auto : ctrl_on); | ||
| 89 | } | ||
| 90 | |||
| 91 | static ssize_t control_store(struct device * dev, struct device_attribute *attr, | ||
| 92 | const char * buf, size_t n) | ||
| 93 | { | ||
| 94 | char *cp; | ||
| 95 | int len = n; | ||
| 96 | |||
| 97 | cp = memchr(buf, '\n', n); | ||
| 98 | if (cp) | ||
| 99 | len = cp - buf; | ||
| 100 | if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0) | ||
| 101 | pm_runtime_allow(dev); | ||
| 102 | else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0) | ||
| 103 | pm_runtime_forbid(dev); | ||
| 104 | else | ||
| 105 | return -EINVAL; | ||
| 106 | return n; | ||
| 107 | } | ||
| 108 | |||
| 109 | static DEVICE_ATTR(control, 0644, control_show, control_store); | ||
| 110 | #endif | ||
| 111 | |||
| 46 | static ssize_t | 112 | static ssize_t |
| 47 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) | 113 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) |
| 48 | { | 114 | { |
| @@ -77,9 +143,43 @@ wake_store(struct device * dev, struct device_attribute *attr, | |||
| 77 | 143 | ||
| 78 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); | 144 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); |
| 79 | 145 | ||
| 146 | #ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG | ||
| 147 | static ssize_t async_show(struct device *dev, struct device_attribute *attr, | ||
| 148 | char *buf) | ||
| 149 | { | ||
| 150 | return sprintf(buf, "%s\n", | ||
| 151 | device_async_suspend_enabled(dev) ? enabled : disabled); | ||
| 152 | } | ||
| 153 | |||
| 154 | static ssize_t async_store(struct device *dev, struct device_attribute *attr, | ||
| 155 | const char *buf, size_t n) | ||
| 156 | { | ||
| 157 | char *cp; | ||
| 158 | int len = n; | ||
| 159 | |||
| 160 | cp = memchr(buf, '\n', n); | ||
| 161 | if (cp) | ||
| 162 | len = cp - buf; | ||
| 163 | if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0) | ||
| 164 | device_enable_async_suspend(dev); | ||
| 165 | else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0) | ||
| 166 | device_disable_async_suspend(dev); | ||
| 167 | else | ||
| 168 | return -EINVAL; | ||
| 169 | return n; | ||
| 170 | } | ||
| 171 | |||
| 172 | static DEVICE_ATTR(async, 0644, async_show, async_store); | ||
| 173 | #endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ | ||
| 80 | 174 | ||
| 81 | static struct attribute * power_attrs[] = { | 175 | static struct attribute * power_attrs[] = { |
| 176 | #ifdef CONFIG_PM_RUNTIME | ||
| 177 | &dev_attr_control.attr, | ||
| 178 | #endif | ||
| 82 | &dev_attr_wakeup.attr, | 179 | &dev_attr_wakeup.attr, |
| 180 | #ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG | ||
| 181 | &dev_attr_async.attr, | ||
| 182 | #endif | ||
| 83 | NULL, | 183 | NULL, |
| 84 | }; | 184 | }; |
| 85 | static struct attribute_group pm_attr_group = { | 185 | static struct attribute_group pm_attr_group = { |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f4a2738bf0bf..2b9ac9e594af 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -1540,6 +1540,7 @@ void pci_pm_init(struct pci_dev *dev) | |||
| 1540 | int pm; | 1540 | int pm; |
| 1541 | u16 pmc; | 1541 | u16 pmc; |
| 1542 | 1542 | ||
| 1543 | device_enable_async_suspend(&dev->dev); | ||
| 1543 | dev->wakeup_prepared = false; | 1544 | dev->wakeup_prepared = false; |
| 1544 | dev->pm_cap = 0; | 1545 | dev->pm_cap = 0; |
| 1545 | 1546 | ||
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0d34ff415399..e73effbe402c 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c | |||
| @@ -285,6 +285,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq) | |||
| 285 | pci_name(pdev), | 285 | pci_name(pdev), |
| 286 | get_descriptor_id(pdev->pcie_type, service)); | 286 | get_descriptor_id(pdev->pcie_type, service)); |
| 287 | device->parent = &pdev->dev; | 287 | device->parent = &pdev->dev; |
| 288 | device_enable_async_suspend(device); | ||
| 288 | 289 | ||
| 289 | retval = device_register(device); | 290 | retval = device_register(device); |
| 290 | if (retval) | 291 | if (retval) |
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 270d069819f7..2a943090a3b7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
| @@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent, | |||
| 1436 | if (error) | 1436 | if (error) |
| 1437 | goto dev_reg_err; | 1437 | goto dev_reg_err; |
| 1438 | b->bridge = get_device(dev); | 1438 | b->bridge = get_device(dev); |
| 1439 | device_enable_async_suspend(b->bridge); | ||
| 1439 | 1440 | ||
| 1440 | if (!parent) | 1441 | if (!parent) |
| 1441 | set_dev_node(b->bridge, pcibus_to_node(b)); | 1442 | set_dev_node(b->bridge, pcibus_to_node(b)); |
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 554626e18062..09dbcb847b73 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c | |||
| @@ -215,6 +215,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, | |||
| 215 | shost->shost_gendev.parent = dev ? dev : &platform_bus; | 215 | shost->shost_gendev.parent = dev ? dev : &platform_bus; |
| 216 | shost->dma_dev = dma_dev; | 216 | shost->dma_dev = dma_dev; |
| 217 | 217 | ||
| 218 | device_enable_async_suspend(&shost->shost_gendev); | ||
| 219 | |||
| 218 | error = device_add(&shost->shost_gendev); | 220 | error = device_add(&shost->shost_gendev); |
| 219 | if (error) | 221 | if (error) |
| 220 | goto out; | 222 | goto out; |
| @@ -222,6 +224,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, | |||
| 222 | scsi_host_set_state(shost, SHOST_RUNNING); | 224 | scsi_host_set_state(shost, SHOST_RUNNING); |
| 223 | get_device(shost->shost_gendev.parent); | 225 | get_device(shost->shost_gendev.parent); |
| 224 | 226 | ||
| 227 | device_enable_async_suspend(&shost->shost_dev); | ||
| 228 | |||
| 225 | error = device_add(&shost->shost_dev); | 229 | error = device_add(&shost->shost_dev); |
| 226 | if (error) | 230 | if (error) |
| 227 | goto out_del_gendev; | 231 | goto out_del_gendev; |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index a4936c4e2f46..19ec9e2d3f39 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
| @@ -847,6 +847,8 @@ static int scsi_target_add(struct scsi_target *starget) | |||
| 847 | if (starget->state != STARGET_CREATED) | 847 | if (starget->state != STARGET_CREATED) |
| 848 | return 0; | 848 | return 0; |
| 849 | 849 | ||
| 850 | device_enable_async_suspend(&starget->dev); | ||
| 851 | |||
| 850 | error = device_add(&starget->dev); | 852 | error = device_add(&starget->dev); |
| 851 | if (error) { | 853 | if (error) { |
| 852 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); | 854 | dev_err(&starget->dev, "target device_add failed, error %d\n", error); |
| @@ -887,11 +889,13 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) | |||
| 887 | return error; | 889 | return error; |
| 888 | 890 | ||
| 889 | transport_configure_device(&starget->dev); | 891 | transport_configure_device(&starget->dev); |
| 892 | device_enable_async_suspend(&sdev->sdev_gendev); | ||
| 890 | error = device_add(&sdev->sdev_gendev); | 893 | error = device_add(&sdev->sdev_gendev); |
| 891 | if (error) { | 894 | if (error) { |
| 892 | printk(KERN_INFO "error 1\n"); | 895 | printk(KERN_INFO "error 1\n"); |
| 893 | return error; | 896 | return error; |
| 894 | } | 897 | } |
| 898 | device_enable_async_suspend(&sdev->sdev_dev); | ||
| 895 | error = device_add(&sdev->sdev_dev); | 899 | error = device_add(&sdev->sdev_dev); |
| 896 | if (error) { | 900 | if (error) { |
| 897 | printk(KERN_INFO "error 2\n"); | 901 | printk(KERN_INFO "error 2\n"); |
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 60a45f1e3a67..f2f055eb6831 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
| @@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) | |||
| 1022 | goto done; | 1022 | goto done; |
| 1023 | } | 1023 | } |
| 1024 | 1024 | ||
| 1025 | /* Non-root devices on a full/low-speed bus must wait for their | ||
| 1026 | * companion high-speed root hub, in case a handoff is needed. | ||
| 1027 | */ | ||
| 1028 | if (!(msg.event & PM_EVENT_AUTO) && udev->parent && | ||
| 1029 | udev->bus->hs_companion) | ||
| 1030 | device_pm_wait_for_dev(&udev->dev, | ||
| 1031 | &udev->bus->hs_companion->root_hub->dev); | ||
| 1032 | |||
| 1025 | if (udev->quirks & USB_QUIRK_RESET_RESUME) | 1033 | if (udev->quirks & USB_QUIRK_RESET_RESUME) |
| 1026 | udev->reset_resume = 1; | 1034 | udev->reset_resume = 1; |
| 1027 | 1035 | ||
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index fdfaa7885515..d26b9ea981f9 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c | |||
| @@ -186,6 +186,7 @@ int usb_create_ep_devs(struct device *parent, | |||
| 186 | ep_dev->dev.parent = parent; | 186 | ep_dev->dev.parent = parent; |
| 187 | ep_dev->dev.release = ep_device_release; | 187 | ep_dev->dev.release = ep_device_release; |
| 188 | dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); | 188 | dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); |
| 189 | device_enable_async_suspend(&ep_dev->dev); | ||
| 189 | 190 | ||
| 190 | retval = device_register(&ep_dev->dev); | 191 | retval = device_register(&ep_dev->dev); |
| 191 | if (retval) | 192 | if (retval) |
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 2dcf906df569..15286533c15a 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
| 22 | #include <linux/pm_runtime.h> | ||
| 22 | #include <linux/usb.h> | 23 | #include <linux/usb.h> |
| 23 | 24 | ||
| 24 | #include <asm/io.h> | 25 | #include <asm/io.h> |
| @@ -37,6 +38,122 @@ | |||
| 37 | 38 | ||
| 38 | /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ | 39 | /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ |
| 39 | 40 | ||
| 41 | #ifdef CONFIG_PM_SLEEP | ||
| 42 | |||
| 43 | /* Coordinate handoffs between EHCI and companion controllers | ||
| 44 | * during system resume | ||
| 45 | */ | ||
| 46 | |||
| 47 | static DEFINE_MUTEX(companions_mutex); | ||
| 48 | |||
| 49 | #define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI | ||
| 50 | #define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI | ||
| 51 | #define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI | ||
| 52 | |||
| 53 | enum companion_action { | ||
| 54 | SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS | ||
| 55 | }; | ||
| 56 | |||
| 57 | static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, | ||
| 58 | enum companion_action action) | ||
| 59 | { | ||
| 60 | struct pci_dev *companion; | ||
| 61 | struct usb_hcd *companion_hcd; | ||
| 62 | unsigned int slot = PCI_SLOT(pdev->devfn); | ||
| 63 | |||
| 64 | /* Iterate through other PCI functions in the same slot. | ||
| 65 | * If pdev is OHCI or UHCI then we are looking for EHCI, and | ||
| 66 | * vice versa. | ||
| 67 | */ | ||
| 68 | companion = NULL; | ||
| 69 | for (;;) { | ||
| 70 | companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion); | ||
| 71 | if (!companion) | ||
| 72 | break; | ||
| 73 | if (companion->bus != pdev->bus || | ||
| 74 | PCI_SLOT(companion->devfn) != slot) | ||
| 75 | continue; | ||
| 76 | |||
| 77 | companion_hcd = pci_get_drvdata(companion); | ||
| 78 | if (!companion_hcd) | ||
| 79 | continue; | ||
| 80 | |||
| 81 | /* For SET_HS_COMPANION, store a pointer to the EHCI bus in | ||
| 82 | * the OHCI/UHCI companion bus structure. | ||
| 83 | * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus | ||
| 84 | * in the OHCI/UHCI companion bus structure. | ||
| 85 | * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI | ||
| 86 | * companion controllers have fully resumed. | ||
| 87 | */ | ||
| 88 | |||
| 89 | if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) && | ||
| 90 | companion->class == CL_EHCI) { | ||
| 91 | /* action must be SET_HS_COMPANION */ | ||
| 92 | dev_dbg(&companion->dev, "HS companion for %s\n", | ||
| 93 | dev_name(&pdev->dev)); | ||
| 94 | hcd->self.hs_companion = &companion_hcd->self; | ||
| 95 | |||
| 96 | } else if (pdev->class == CL_EHCI && | ||
| 97 | (companion->class == CL_OHCI || | ||
| 98 | companion->class == CL_UHCI)) { | ||
| 99 | switch (action) { | ||
| 100 | case SET_HS_COMPANION: | ||
| 101 | dev_dbg(&pdev->dev, "HS companion for %s\n", | ||
| 102 | dev_name(&companion->dev)); | ||
| 103 | companion_hcd->self.hs_companion = &hcd->self; | ||
| 104 | break; | ||
| 105 | case CLEAR_HS_COMPANION: | ||
| 106 | companion_hcd->self.hs_companion = NULL; | ||
| 107 | break; | ||
| 108 | case WAIT_FOR_COMPANIONS: | ||
| 109 | device_pm_wait_for_dev(&pdev->dev, | ||
| 110 | &companion->dev); | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) | ||
| 118 | { | ||
| 119 | mutex_lock(&companions_mutex); | ||
| 120 | dev_set_drvdata(&pdev->dev, hcd); | ||
| 121 | companion_common(pdev, hcd, SET_HS_COMPANION); | ||
| 122 | mutex_unlock(&companions_mutex); | ||
| 123 | } | ||
| 124 | |||
| 125 | static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd) | ||
| 126 | { | ||
| 127 | mutex_lock(&companions_mutex); | ||
| 128 | dev_set_drvdata(&pdev->dev, NULL); | ||
| 129 | |||
| 130 | /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */ | ||
| 131 | if (pdev->class == CL_OHCI || pdev->class == CL_UHCI) | ||
| 132 | hcd->self.hs_companion = NULL; | ||
| 133 | |||
| 134 | /* Otherwise search for companion buses and clear their pointers */ | ||
| 135 | else | ||
| 136 | companion_common(pdev, hcd, CLEAR_HS_COMPANION); | ||
| 137 | mutex_unlock(&companions_mutex); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd) | ||
| 141 | { | ||
| 142 | /* Only EHCI controllers need to wait. | ||
| 143 | * No locking is needed because a controller cannot be resumed | ||
| 144 | * while one of its companions is getting unbound. | ||
| 145 | */ | ||
| 146 | if (pdev->class == CL_EHCI) | ||
| 147 | companion_common(pdev, hcd, WAIT_FOR_COMPANIONS); | ||
| 148 | } | ||
| 149 | |||
| 150 | #else /* !CONFIG_PM_SLEEP */ | ||
| 151 | |||
| 152 | static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | ||
| 153 | static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | ||
| 154 | static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {} | ||
| 155 | |||
| 156 | #endif /* !CONFIG_PM_SLEEP */ | ||
| 40 | 157 | ||
| 41 | /*-------------------------------------------------------------------------*/ | 158 | /*-------------------------------------------------------------------------*/ |
| 42 | 159 | ||
| @@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 123 | if (region == PCI_ROM_RESOURCE) { | 240 | if (region == PCI_ROM_RESOURCE) { |
| 124 | dev_dbg(&dev->dev, "no i/o regions available\n"); | 241 | dev_dbg(&dev->dev, "no i/o regions available\n"); |
| 125 | retval = -EBUSY; | 242 | retval = -EBUSY; |
| 126 | goto err1; | 243 | goto err2; |
| 127 | } | 244 | } |
| 128 | } | 245 | } |
| 129 | 246 | ||
| @@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 132 | retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); | 249 | retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); |
| 133 | if (retval != 0) | 250 | if (retval != 0) |
| 134 | goto err4; | 251 | goto err4; |
| 252 | set_hs_companion(dev, hcd); | ||
| 135 | return retval; | 253 | return retval; |
| 136 | 254 | ||
| 137 | err4: | 255 | err4: |
| @@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
| 142 | } else | 260 | } else |
| 143 | release_region(hcd->rsrc_start, hcd->rsrc_len); | 261 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
| 144 | err2: | 262 | err2: |
| 263 | clear_hs_companion(dev, hcd); | ||
| 145 | usb_put_hcd(hcd); | 264 | usb_put_hcd(hcd); |
| 146 | err1: | 265 | err1: |
| 147 | pci_disable_device(dev); | 266 | pci_disable_device(dev); |
| @@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev) | |||
| 180 | } else { | 299 | } else { |
| 181 | release_region(hcd->rsrc_start, hcd->rsrc_len); | 300 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
| 182 | } | 301 | } |
| 302 | clear_hs_companion(dev, hcd); | ||
| 183 | usb_put_hcd(hcd); | 303 | usb_put_hcd(hcd); |
| 184 | pci_disable_device(dev); | 304 | pci_disable_device(dev); |
| 185 | } | 305 | } |
| @@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated) | |||
| 344 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); | 464 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); |
| 345 | 465 | ||
| 346 | if (hcd->driver->pci_resume) { | 466 | if (hcd->driver->pci_resume) { |
| 467 | /* This call should be made only during system resume, | ||
| 468 | * not during runtime resume. | ||
| 469 | */ | ||
| 470 | wait_for_companions(pci_dev, hcd); | ||
| 471 | |||
| 347 | retval = hcd->driver->pci_resume(hcd, hibernated); | 472 | retval = hcd->driver->pci_resume(hcd, hibernated); |
| 348 | if (retval) { | 473 | if (retval) { |
| 349 | dev_err(dev, "PCI post-resume error %d!\n", retval); | 474 | dev_err(dev, "PCI post-resume error %d!\n", retval); |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 35cc8b9ba1f5..20ecb4cec8de 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
| @@ -1817,6 +1817,7 @@ int usb_new_device(struct usb_device *udev) | |||
| 1817 | /* Tell the world! */ | 1817 | /* Tell the world! */ |
| 1818 | announce_device(udev); | 1818 | announce_device(udev); |
| 1819 | 1819 | ||
| 1820 | device_enable_async_suspend(&udev->dev); | ||
| 1820 | /* Register the device. The device driver is responsible | 1821 | /* Register the device. The device driver is responsible |
| 1821 | * for configuring the device and invoking the add-device | 1822 | * for configuring the device and invoking the add-device |
| 1822 | * notifier chain (used by usbfs and possibly others). | 1823 | * notifier chain (used by usbfs and possibly others). |
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9bc95fec793f..df73574a9cc9 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
| @@ -1867,6 +1867,7 @@ free_interfaces: | |||
| 1867 | "adding %s (config #%d, interface %d)\n", | 1867 | "adding %s (config #%d, interface %d)\n", |
| 1868 | dev_name(&intf->dev), configuration, | 1868 | dev_name(&intf->dev), configuration, |
| 1869 | intf->cur_altsetting->desc.bInterfaceNumber); | 1869 | intf->cur_altsetting->desc.bInterfaceNumber); |
| 1870 | device_enable_async_suspend(&intf->dev); | ||
| 1870 | ret = device_add(&intf->dev); | 1871 | ret = device_add(&intf->dev); |
| 1871 | if (ret != 0) { | 1872 | if (ret != 0) { |
| 1872 | dev_err(&dev->dev, "device_add(%s) --> %d\n", | 1873 | dev_err(&dev->dev, "device_add(%s) --> %d\n", |
diff --git a/include/linux/device.h b/include/linux/device.h index a62799f2ab00..b30527db3ac0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
| @@ -472,6 +472,23 @@ static inline int device_is_registered(struct device *dev) | |||
| 472 | return dev->kobj.state_in_sysfs; | 472 | return dev->kobj.state_in_sysfs; |
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | static inline void device_enable_async_suspend(struct device *dev) | ||
| 476 | { | ||
| 477 | if (dev->power.status == DPM_ON) | ||
| 478 | dev->power.async_suspend = true; | ||
| 479 | } | ||
| 480 | |||
| 481 | static inline void device_disable_async_suspend(struct device *dev) | ||
| 482 | { | ||
| 483 | if (dev->power.status == DPM_ON) | ||
| 484 | dev->power.async_suspend = false; | ||
| 485 | } | ||
| 486 | |||
| 487 | static inline bool device_async_suspend_enabled(struct device *dev) | ||
| 488 | { | ||
| 489 | return !!dev->power.async_suspend; | ||
| 490 | } | ||
| 491 | |||
| 475 | void driver_init(void); | 492 | void driver_init(void); |
| 476 | 493 | ||
| 477 | /* | 494 | /* |
diff --git a/include/linux/pm.h b/include/linux/pm.h index 198b8f9fe05e..e80df06ad22a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/spinlock.h> | 26 | #include <linux/spinlock.h> |
| 27 | #include <linux/wait.h> | 27 | #include <linux/wait.h> |
| 28 | #include <linux/timer.h> | 28 | #include <linux/timer.h> |
| 29 | #include <linux/completion.h> | ||
| 29 | 30 | ||
| 30 | /* | 31 | /* |
| 31 | * Callbacks for platform drivers to implement. | 32 | * Callbacks for platform drivers to implement. |
| @@ -412,9 +413,11 @@ struct dev_pm_info { | |||
| 412 | pm_message_t power_state; | 413 | pm_message_t power_state; |
| 413 | unsigned int can_wakeup:1; | 414 | unsigned int can_wakeup:1; |
| 414 | unsigned int should_wakeup:1; | 415 | unsigned int should_wakeup:1; |
| 416 | unsigned async_suspend:1; | ||
| 415 | enum dpm_state status; /* Owned by the PM core */ | 417 | enum dpm_state status; /* Owned by the PM core */ |
| 416 | #ifdef CONFIG_PM_SLEEP | 418 | #ifdef CONFIG_PM_SLEEP |
| 417 | struct list_head entry; | 419 | struct list_head entry; |
| 420 | struct completion completion; | ||
| 418 | #endif | 421 | #endif |
| 419 | #ifdef CONFIG_PM_RUNTIME | 422 | #ifdef CONFIG_PM_RUNTIME |
| 420 | struct timer_list suspend_timer; | 423 | struct timer_list suspend_timer; |
| @@ -430,6 +433,7 @@ struct dev_pm_info { | |||
| 430 | unsigned int request_pending:1; | 433 | unsigned int request_pending:1; |
| 431 | unsigned int deferred_resume:1; | 434 | unsigned int deferred_resume:1; |
| 432 | unsigned int run_wake:1; | 435 | unsigned int run_wake:1; |
| 436 | unsigned int runtime_auto:1; | ||
| 433 | enum rpm_request request; | 437 | enum rpm_request request; |
| 434 | enum rpm_status runtime_status; | 438 | enum rpm_status runtime_status; |
| 435 | int runtime_error; | 439 | int runtime_error; |
| @@ -508,6 +512,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); | |||
| 508 | __suspend_report_result(__func__, fn, ret); \ | 512 | __suspend_report_result(__func__, fn, ret); \ |
| 509 | } while (0) | 513 | } while (0) |
| 510 | 514 | ||
| 515 | extern void device_pm_wait_for_dev(struct device *sub, struct device *dev); | ||
| 511 | #else /* !CONFIG_PM_SLEEP */ | 516 | #else /* !CONFIG_PM_SLEEP */ |
| 512 | 517 | ||
| 513 | #define device_pm_lock() do {} while (0) | 518 | #define device_pm_lock() do {} while (0) |
| @@ -520,6 +525,7 @@ static inline int dpm_suspend_start(pm_message_t state) | |||
| 520 | 525 | ||
| 521 | #define suspend_report_result(fn, ret) do {} while (0) | 526 | #define suspend_report_result(fn, ret) do {} while (0) |
| 522 | 527 | ||
| 528 | static inline void device_pm_wait_for_dev(struct device *a, struct device *b) {} | ||
| 523 | #endif /* !CONFIG_PM_SLEEP */ | 529 | #endif /* !CONFIG_PM_SLEEP */ |
| 524 | 530 | ||
| 525 | /* How to reorder dpm_list after device_move() */ | 531 | /* How to reorder dpm_list after device_move() */ |
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 370ce0a6fe4a..7d773aac5314 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h | |||
| @@ -28,6 +28,8 @@ extern int __pm_runtime_set_status(struct device *dev, unsigned int status); | |||
| 28 | extern int pm_runtime_barrier(struct device *dev); | 28 | extern int pm_runtime_barrier(struct device *dev); |
| 29 | extern void pm_runtime_enable(struct device *dev); | 29 | extern void pm_runtime_enable(struct device *dev); |
| 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); | 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); |
| 31 | extern void pm_runtime_allow(struct device *dev); | ||
| 32 | extern void pm_runtime_forbid(struct device *dev); | ||
| 31 | 33 | ||
| 32 | static inline bool pm_children_suspended(struct device *dev) | 34 | static inline bool pm_children_suspended(struct device *dev) |
| 33 | { | 35 | { |
| @@ -78,6 +80,8 @@ static inline int __pm_runtime_set_status(struct device *dev, | |||
| 78 | static inline int pm_runtime_barrier(struct device *dev) { return 0; } | 80 | static inline int pm_runtime_barrier(struct device *dev) { return 0; } |
| 79 | static inline void pm_runtime_enable(struct device *dev) {} | 81 | static inline void pm_runtime_enable(struct device *dev) {} |
| 80 | static inline void __pm_runtime_disable(struct device *dev, bool c) {} | 82 | static inline void __pm_runtime_disable(struct device *dev, bool c) {} |
| 83 | static inline void pm_runtime_allow(struct device *dev) {} | ||
| 84 | static inline void pm_runtime_forbid(struct device *dev) {} | ||
| 81 | 85 | ||
| 82 | static inline bool pm_children_suspended(struct device *dev) { return false; } | 86 | static inline bool pm_children_suspended(struct device *dev) { return false; } |
| 83 | static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} | 87 | static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} |
diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h index c9ba2fdf807d..bc8c3881c729 100644 --- a/include/linux/resume-trace.h +++ b/include/linux/resume-trace.h | |||
| @@ -6,6 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | extern int pm_trace_enabled; | 7 | extern int pm_trace_enabled; |
| 8 | 8 | ||
| 9 | static inline int pm_trace_is_enabled(void) | ||
| 10 | { | ||
| 11 | return pm_trace_enabled; | ||
| 12 | } | ||
| 13 | |||
| 9 | struct device; | 14 | struct device; |
| 10 | extern void set_trace_device(struct device *); | 15 | extern void set_trace_device(struct device *); |
| 11 | extern void generate_resume_trace(const void *tracedata, unsigned int user); | 16 | extern void generate_resume_trace(const void *tracedata, unsigned int user); |
| @@ -17,6 +22,8 @@ extern void generate_resume_trace(const void *tracedata, unsigned int user); | |||
| 17 | 22 | ||
| 18 | #else | 23 | #else |
| 19 | 24 | ||
| 25 | static inline int pm_trace_is_enabled(void) { return 0; } | ||
| 26 | |||
| 20 | #define TRACE_DEVICE(dev) do { } while (0) | 27 | #define TRACE_DEVICE(dev) do { } while (0) |
| 21 | #define TRACE_RESUME(dev) do { } while (0) | 28 | #define TRACE_RESUME(dev) do { } while (0) |
| 22 | 29 | ||
diff --git a/include/linux/usb.h b/include/linux/usb.h index d7ace1b80f09..332eaea61021 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
| @@ -339,6 +339,7 @@ struct usb_bus { | |||
| 339 | 339 | ||
| 340 | struct usb_devmap devmap; /* device address allocation map */ | 340 | struct usb_devmap devmap; /* device address allocation map */ |
| 341 | struct usb_device *root_hub; /* Root hub */ | 341 | struct usb_device *root_hub; /* Root hub */ |
| 342 | struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ | ||
| 342 | struct list_head bus_list; /* list of busses */ | 343 | struct list_head bus_list; /* list of busses */ |
| 343 | 344 | ||
| 344 | int bandwidth_allocated; /* on this bus: how much of the time | 345 | int bandwidth_allocated; /* on this bus: how much of the time |
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 4c9cffcf69c7..5c36ea9d55d2 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
| @@ -27,6 +27,15 @@ config PM_DEBUG | |||
| 27 | code. This is helpful when debugging and reporting PM bugs, like | 27 | code. This is helpful when debugging and reporting PM bugs, like |
| 28 | suspend support. | 28 | suspend support. |
| 29 | 29 | ||
| 30 | config PM_ADVANCED_DEBUG | ||
| 31 | bool "Extra PM attributes in sysfs for low-level debugging/testing" | ||
| 32 | depends on PM_DEBUG | ||
| 33 | default n | ||
| 34 | ---help--- | ||
| 35 | Add extra sysfs attributes allowing one to access some Power Management | ||
| 36 | fields of device objects from user space. If you are not a kernel | ||
| 37 | developer interested in debugging/testing Power Management, say "no". | ||
| 38 | |||
| 30 | config PM_VERBOSE | 39 | config PM_VERBOSE |
| 31 | bool "Verbose Power Management debugging" | 40 | bool "Verbose Power Management debugging" |
| 32 | depends on PM_DEBUG | 41 | depends on PM_DEBUG |
| @@ -85,6 +94,11 @@ config PM_SLEEP | |||
| 85 | depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE | 94 | depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE |
| 86 | default y | 95 | default y |
| 87 | 96 | ||
| 97 | config PM_SLEEP_ADVANCED_DEBUG | ||
| 98 | bool | ||
| 99 | depends on PM_ADVANCED_DEBUG | ||
| 100 | default n | ||
| 101 | |||
| 88 | config SUSPEND | 102 | config SUSPEND |
| 89 | bool "Suspend to RAM and standby" | 103 | bool "Suspend to RAM and standby" |
| 90 | depends on PM && ARCH_SUSPEND_POSSIBLE | 104 | depends on PM && ARCH_SUSPEND_POSSIBLE |
diff --git a/kernel/power/main.c b/kernel/power/main.c index 0998c7139053..b58800b21fc0 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
| @@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long val) | |||
| 44 | == NOTIFY_BAD) ? -EINVAL : 0; | 44 | == NOTIFY_BAD) ? -EINVAL : 0; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | /* If set, devices may be suspended and resumed asynchronously. */ | ||
| 48 | int pm_async_enabled = 1; | ||
| 49 | |||
| 50 | static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr, | ||
| 51 | char *buf) | ||
| 52 | { | ||
| 53 | return sprintf(buf, "%d\n", pm_async_enabled); | ||
| 54 | } | ||
| 55 | |||
| 56 | static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr, | ||
| 57 | const char *buf, size_t n) | ||
| 58 | { | ||
| 59 | unsigned long val; | ||
| 60 | |||
| 61 | if (strict_strtoul(buf, 10, &val)) | ||
| 62 | return -EINVAL; | ||
| 63 | |||
| 64 | if (val > 1) | ||
| 65 | return -EINVAL; | ||
| 66 | |||
| 67 | pm_async_enabled = val; | ||
| 68 | return n; | ||
| 69 | } | ||
| 70 | |||
| 71 | power_attr(pm_async); | ||
| 72 | |||
| 47 | #ifdef CONFIG_PM_DEBUG | 73 | #ifdef CONFIG_PM_DEBUG |
| 48 | int pm_test_level = TEST_NONE; | 74 | int pm_test_level = TEST_NONE; |
| 49 | 75 | ||
| @@ -208,9 +234,12 @@ static struct attribute * g[] = { | |||
| 208 | #ifdef CONFIG_PM_TRACE | 234 | #ifdef CONFIG_PM_TRACE |
| 209 | &pm_trace_attr.attr, | 235 | &pm_trace_attr.attr, |
| 210 | #endif | 236 | #endif |
| 211 | #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) | 237 | #ifdef CONFIG_PM_SLEEP |
| 238 | &pm_async_attr.attr, | ||
| 239 | #ifdef CONFIG_PM_DEBUG | ||
| 212 | &pm_test_attr.attr, | 240 | &pm_test_attr.attr, |
| 213 | #endif | 241 | #endif |
| 242 | #endif | ||
| 214 | NULL, | 243 | NULL, |
| 215 | }; | 244 | }; |
| 216 | 245 | ||
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 36cb168e4330..830cadecbdfc 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
| @@ -1181,7 +1181,7 @@ static void free_unnecessary_pages(void) | |||
| 1181 | 1181 | ||
| 1182 | memory_bm_position_reset(©_bm); | 1182 | memory_bm_position_reset(©_bm); |
| 1183 | 1183 | ||
| 1184 | while (to_free_normal > 0 && to_free_highmem > 0) { | 1184 | while (to_free_normal > 0 || to_free_highmem > 0) { |
| 1185 | unsigned long pfn = memory_bm_next_pfn(©_bm); | 1185 | unsigned long pfn = memory_bm_next_pfn(©_bm); |
| 1186 | struct page *page = pfn_to_page(pfn); | 1186 | struct page *page = pfn_to_page(pfn); |
| 1187 | 1187 | ||
| @@ -1500,7 +1500,7 @@ asmlinkage int swsusp_save(void) | |||
| 1500 | { | 1500 | { |
| 1501 | unsigned int nr_pages, nr_highmem; | 1501 | unsigned int nr_pages, nr_highmem; |
| 1502 | 1502 | ||
| 1503 | printk(KERN_INFO "PM: Creating hibernation image: \n"); | 1503 | printk(KERN_INFO "PM: Creating hibernation image:\n"); |
| 1504 | 1504 | ||
| 1505 | drain_local_pages(NULL); | 1505 | drain_local_pages(NULL); |
| 1506 | nr_pages = count_data_pages(); | 1506 | nr_pages = count_data_pages(); |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 09b2b0ae9e9d..1d575733d4e1 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
| @@ -657,10 +657,6 @@ int swsusp_read(unsigned int *flags_p) | |||
| 657 | struct swsusp_info *header; | 657 | struct swsusp_info *header; |
| 658 | 658 | ||
| 659 | *flags_p = swsusp_header->flags; | 659 | *flags_p = swsusp_header->flags; |
| 660 | if (IS_ERR(resume_bdev)) { | ||
| 661 | pr_debug("PM: Image device not initialised\n"); | ||
| 662 | return PTR_ERR(resume_bdev); | ||
| 663 | } | ||
| 664 | 660 | ||
| 665 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 661 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
| 666 | error = snapshot_write_next(&snapshot, PAGE_SIZE); | 662 | error = snapshot_write_next(&snapshot, PAGE_SIZE); |
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c deleted file mode 100644 index 5b3601bd1893..000000000000 --- a/kernel/power/swsusp.c +++ /dev/null | |||
| @@ -1,58 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/kernel/power/swsusp.c | ||
| 3 | * | ||
| 4 | * This file provides code to write suspend image to swap and read it back. | ||
| 5 | * | ||
| 6 | * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> | ||
| 7 | * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz> | ||
| 8 | * | ||
| 9 | * This file is released under the GPLv2. | ||
| 10 | * | ||
| 11 | * I'd like to thank the following people for their work: | ||
| 12 | * | ||
| 13 | * Pavel Machek <pavel@ucw.cz>: | ||
| 14 | * Modifications, defectiveness pointing, being with me at the very beginning, | ||
| 15 | * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. | ||
| 16 | * | ||
| 17 | * Steve Doddi <dirk@loth.demon.co.uk>: | ||
| 18 | * Support the possibility of hardware state restoring. | ||
| 19 | * | ||
| 20 | * Raph <grey.havens@earthling.net>: | ||
| 21 | * Support for preserving states of network devices and virtual console | ||
| 22 | * (including X and svgatextmode) | ||
| 23 | * | ||
| 24 | * Kurt Garloff <garloff@suse.de>: | ||
| 25 | * Straightened the critical function in order to prevent compilers from | ||
| 26 | * playing tricks with local variables. | ||
| 27 | * | ||
| 28 | * Andreas Mohr <a.mohr@mailto.de> | ||
| 29 | * | ||
| 30 | * Alex Badea <vampire@go.ro>: | ||
| 31 | * Fixed runaway init | ||
| 32 | * | ||
| 33 | * Rafael J. Wysocki <rjw@sisk.pl> | ||
| 34 | * Reworked the freeing of memory and the handling of swap | ||
| 35 | * | ||
| 36 | * More state savers are welcome. Especially for the scsi layer... | ||
| 37 | * | ||
| 38 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | ||
| 39 | */ | ||
| 40 | |||
| 41 | #include <linux/mm.h> | ||
| 42 | #include <linux/suspend.h> | ||
| 43 | #include <linux/spinlock.h> | ||
| 44 | #include <linux/kernel.h> | ||
| 45 | #include <linux/major.h> | ||
| 46 | #include <linux/swap.h> | ||
| 47 | #include <linux/pm.h> | ||
| 48 | #include <linux/swapops.h> | ||
| 49 | #include <linux/bootmem.h> | ||
| 50 | #include <linux/syscalls.h> | ||
| 51 | #include <linux/highmem.h> | ||
| 52 | #include <linux/time.h> | ||
| 53 | #include <linux/rbtree.h> | ||
| 54 | #include <linux/io.h> | ||
| 55 | |||
| 56 | #include "power.h" | ||
| 57 | |||
| 58 | int in_suspend __nosavedata = 0; | ||
diff --git a/kernel/power/user.c b/kernel/power/user.c index bf0014d6a5f0..4d2289626a84 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
| @@ -195,6 +195,15 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
| 195 | return res; | 195 | return res; |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | static void snapshot_deprecated_ioctl(unsigned int cmd) | ||
| 199 | { | ||
| 200 | if (printk_ratelimit()) | ||
| 201 | printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will " | ||
| 202 | "be removed soon, update your suspend-to-disk " | ||
| 203 | "utilities\n", | ||
| 204 | __builtin_return_address(0), cmd); | ||
| 205 | } | ||
| 206 | |||
| 198 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, | 207 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, |
| 199 | unsigned long arg) | 208 | unsigned long arg) |
| 200 | { | 209 | { |
| @@ -246,8 +255,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
| 246 | data->frozen = 0; | 255 | data->frozen = 0; |
| 247 | break; | 256 | break; |
| 248 | 257 | ||
| 249 | case SNAPSHOT_CREATE_IMAGE: | ||
| 250 | case SNAPSHOT_ATOMIC_SNAPSHOT: | 258 | case SNAPSHOT_ATOMIC_SNAPSHOT: |
| 259 | snapshot_deprecated_ioctl(cmd); | ||
| 260 | case SNAPSHOT_CREATE_IMAGE: | ||
| 251 | if (data->mode != O_RDONLY || !data->frozen || data->ready) { | 261 | if (data->mode != O_RDONLY || !data->frozen || data->ready) { |
| 252 | error = -EPERM; | 262 | error = -EPERM; |
| 253 | break; | 263 | break; |
| @@ -275,8 +285,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
| 275 | data->ready = 0; | 285 | data->ready = 0; |
| 276 | break; | 286 | break; |
| 277 | 287 | ||
| 278 | case SNAPSHOT_PREF_IMAGE_SIZE: | ||
| 279 | case SNAPSHOT_SET_IMAGE_SIZE: | 288 | case SNAPSHOT_SET_IMAGE_SIZE: |
| 289 | snapshot_deprecated_ioctl(cmd); | ||
| 290 | case SNAPSHOT_PREF_IMAGE_SIZE: | ||
| 280 | image_size = arg; | 291 | image_size = arg; |
| 281 | break; | 292 | break; |
| 282 | 293 | ||
| @@ -290,15 +301,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
| 290 | error = put_user(size, (loff_t __user *)arg); | 301 | error = put_user(size, (loff_t __user *)arg); |
| 291 | break; | 302 | break; |
| 292 | 303 | ||
| 293 | case SNAPSHOT_AVAIL_SWAP_SIZE: | ||
| 294 | case SNAPSHOT_AVAIL_SWAP: | 304 | case SNAPSHOT_AVAIL_SWAP: |
| 305 | snapshot_deprecated_ioctl(cmd); | ||
| 306 | case SNAPSHOT_AVAIL_SWAP_SIZE: | ||
| 295 | size = count_swap_pages(data->swap, 1); | 307 | size = count_swap_pages(data->swap, 1); |
| 296 | size <<= PAGE_SHIFT; | 308 | size <<= PAGE_SHIFT; |
| 297 | error = put_user(size, (loff_t __user *)arg); | 309 | error = put_user(size, (loff_t __user *)arg); |
| 298 | break; | 310 | break; |
| 299 | 311 | ||
| 300 | case SNAPSHOT_ALLOC_SWAP_PAGE: | ||
| 301 | case SNAPSHOT_GET_SWAP_PAGE: | 312 | case SNAPSHOT_GET_SWAP_PAGE: |
| 313 | snapshot_deprecated_ioctl(cmd); | ||
| 314 | case SNAPSHOT_ALLOC_SWAP_PAGE: | ||
| 302 | if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { | 315 | if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { |
| 303 | error = -ENODEV; | 316 | error = -ENODEV; |
| 304 | break; | 317 | break; |
| @@ -321,6 +334,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
| 321 | break; | 334 | break; |
| 322 | 335 | ||
| 323 | case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ | 336 | case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ |
| 337 | snapshot_deprecated_ioctl(cmd); | ||
| 324 | if (!swsusp_swap_in_use()) { | 338 | if (!swsusp_swap_in_use()) { |
| 325 | /* | 339 | /* |
| 326 | * User space encodes device types as two-byte values, | 340 | * User space encodes device types as two-byte values, |
| @@ -362,6 +376,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
| 362 | break; | 376 | break; |
| 363 | 377 | ||
| 364 | case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ | 378 | case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ |
| 379 | snapshot_deprecated_ioctl(cmd); | ||
| 365 | error = -EINVAL; | 380 | error = -EINVAL; |
| 366 | 381 | ||
| 367 | switch (arg) { | 382 | switch (arg) { |
