diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-06-23 00:08:52 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-06-23 00:08:52 -0400 |
| commit | 68d0080f1e222757c85606d3eaf81b5c4aa7719f (patch) | |
| tree | 5f177f7571a56e7dfe3888f67fcc86532be8235c | |
| parent | f957db4fcdd8f03e186aa8f041f4049e76ab741c (diff) | |
| parent | a5f76d5eba157bf637beb2dd18026db2917c512e (diff) | |
Merge branch 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
PCI / PM: Block races between runtime PM and system sleep
PM / Domains: Update documentation
PM / Runtime: Handle clocks correctly if CONFIG_PM_RUNTIME is unset
PM: Fix async resume following suspend failure
PM: Free memory bitmaps if opening /dev/snapshot fails
PM: Rename dev_pm_info.in_suspend to is_prepared
PM: Update documentation regarding sysdevs
PM / Runtime: Update doc: usage count no longer incremented across system PM
| -rw-r--r-- | Documentation/power/devices.txt | 67 | ||||
| -rw-r--r-- | Documentation/power/runtime_pm.txt | 5 | ||||
| -rw-r--r-- | drivers/base/power/clock_ops.c | 4 | ||||
| -rw-r--r-- | drivers/base/power/main.c | 28 | ||||
| -rw-r--r-- | drivers/pci/pci-driver.c | 4 | ||||
| -rw-r--r-- | drivers/usb/core/driver.c | 6 | ||||
| -rw-r--r-- | include/linux/device.h | 4 | ||||
| -rw-r--r-- | include/linux/pm.h | 3 | ||||
| -rw-r--r-- | kernel/power/user.c | 4 |
9 files changed, 50 insertions, 75 deletions
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt index 88880839ece4..64565aac6e40 100644 --- a/Documentation/power/devices.txt +++ b/Documentation/power/devices.txt | |||
| @@ -520,59 +520,20 @@ Support for power domains is provided through the pwr_domain field of struct | |||
| 520 | device. This field is a pointer to an object of type struct dev_power_domain, | 520 | device. This field is a pointer to an object of type struct dev_power_domain, |
| 521 | defined in include/linux/pm.h, providing a set of power management callbacks | 521 | defined in include/linux/pm.h, providing a set of power management callbacks |
| 522 | analogous to the subsystem-level and device driver callbacks that are executed | 522 | analogous to the subsystem-level and device driver callbacks that are executed |
| 523 | for the given device during all power transitions, in addition to the respective | 523 | for the given device during all power transitions, instead of the respective |
| 524 | subsystem-level callbacks. Specifically, the power domain "suspend" callbacks | 524 | subsystem-level callbacks. Specifically, if a device's pm_domain pointer is |
| 525 | (i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are | 525 | not NULL, the ->suspend() callback from the object pointed to by it will be |
| 526 | executed after the analogous subsystem-level callbacks, while the power domain | 526 | executed instead of its subsystem's (e.g. bus type's) ->suspend() callback and |
| 527 | "resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore, | 527 | anlogously for all of the remaining callbacks. In other words, power management |
| 528 | etc.) are executed before the analogous subsystem-level callbacks. Error codes | 528 | domain callbacks, if defined for the given device, always take precedence over |
| 529 | returned by the "suspend" and "resume" power domain callbacks are ignored. | 529 | the callbacks provided by the device's subsystem (e.g. bus type). |
| 530 | 530 | ||
| 531 | Power domain ->runtime_idle() callback is executed before the subsystem-level | 531 | The support for device power management domains is only relevant to platforms |
| 532 | ->runtime_idle() callback and the result returned by it is not ignored. Namely, | 532 | needing to use the same device driver power management callbacks in many |
| 533 | if it returns error code, the subsystem-level ->runtime_idle() callback will not | 533 | different power domain configurations and wanting to avoid incorporating the |
| 534 | be called and the helper function rpm_idle() executing it will return error | 534 | support for power domains into subsystem-level callbacks, for example by |
| 535 | code. This mechanism is intended to help platforms where saving device state | 535 | modifying the platform bus type. Other platforms need not implement it or take |
| 536 | is a time consuming operation and should only be carried out if all devices | 536 | it into account in any way. |
| 537 | in the power domain are idle, before turning off the shared power resource(s). | ||
| 538 | Namely, the power domain ->runtime_idle() callback may return error code until | ||
| 539 | the pm_runtime_idle() helper (or its asychronous version) has been called for | ||
| 540 | all devices in the power domain (it is recommended that the returned error code | ||
| 541 | be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle() | ||
| 542 | callback from being run prematurely. | ||
| 543 | |||
| 544 | The support for device power domains is only relevant to platforms needing to | ||
| 545 | use the same subsystem-level (e.g. platform bus type) and device driver power | ||
| 546 | management callbacks in many different power domain configurations and wanting | ||
| 547 | to avoid incorporating the support for power domains into the subsystem-level | ||
| 548 | callbacks. The other platforms need not implement it or take it into account | ||
| 549 | in any way. | ||
| 550 | |||
| 551 | |||
| 552 | System Devices | ||
| 553 | -------------- | ||
| 554 | System devices (sysdevs) follow a slightly different API, which can be found in | ||
| 555 | |||
| 556 | include/linux/sysdev.h | ||
| 557 | drivers/base/sys.c | ||
| 558 | |||
| 559 | System devices will be suspended with interrupts disabled, and after all other | ||
| 560 | devices have been suspended. On resume, they will be resumed before any other | ||
| 561 | devices, and also with interrupts disabled. These things occur in special | ||
| 562 | "sysdev_driver" phases, which affect only system devices. | ||
| 563 | |||
| 564 | Thus, after the suspend_noirq (or freeze_noirq or poweroff_noirq) phase, when | ||
| 565 | the non-boot CPUs are all offline and IRQs are disabled on the remaining online | ||
| 566 | CPU, then a sysdev_driver.suspend phase is carried out, and the system enters a | ||
| 567 | sleep state (or a system image is created). During resume (or after the image | ||
| 568 | has been created or loaded) a sysdev_driver.resume phase is carried out, IRQs | ||
| 569 | are enabled on the only online CPU, the non-boot CPUs are enabled, and the | ||
| 570 | resume_noirq (or thaw_noirq or restore_noirq) phase begins. | ||
| 571 | |||
| 572 | Code to actually enter and exit the system-wide low power state sometimes | ||
| 573 | involves hardware details that are only known to the boot firmware, and | ||
| 574 | may leave a CPU running software (from SRAM or flash memory) that monitors | ||
| 575 | the system and manages its wakeup sequence. | ||
| 576 | 537 | ||
| 577 | 538 | ||
| 578 | Device Low Power (suspend) States | 539 | Device Low Power (suspend) States |
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 654097b130b4..22accb3eb40e 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt | |||
| @@ -566,11 +566,6 @@ to do this is: | |||
| 566 | pm_runtime_set_active(dev); | 566 | pm_runtime_set_active(dev); |
| 567 | pm_runtime_enable(dev); | 567 | pm_runtime_enable(dev); |
| 568 | 568 | ||
| 569 | The PM core always increments the run-time usage counter before calling the | ||
| 570 | ->prepare() callback and decrements it after calling the ->complete() callback. | ||
| 571 | Hence disabling run-time PM temporarily like this will not cause any run-time | ||
| 572 | suspend callbacks to be lost. | ||
| 573 | |||
| 574 | 7. Generic subsystem callbacks | 569 | 7. Generic subsystem callbacks |
| 575 | 570 | ||
| 576 | Subsystems may wish to conserve code space by using the set of generic power | 571 | Subsystems may wish to conserve code space by using the set of generic power |
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index eaa8a854af03..ad367c4139b1 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c | |||
| @@ -387,7 +387,7 @@ static int pm_runtime_clk_notify(struct notifier_block *nb, | |||
| 387 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); | 387 | clknb = container_of(nb, struct pm_clk_notifier_block, nb); |
| 388 | 388 | ||
| 389 | switch (action) { | 389 | switch (action) { |
| 390 | case BUS_NOTIFY_ADD_DEVICE: | 390 | case BUS_NOTIFY_BIND_DRIVER: |
| 391 | if (clknb->con_ids[0]) { | 391 | if (clknb->con_ids[0]) { |
| 392 | for (con_id = clknb->con_ids; *con_id; con_id++) | 392 | for (con_id = clknb->con_ids; *con_id; con_id++) |
| 393 | enable_clock(dev, *con_id); | 393 | enable_clock(dev, *con_id); |
| @@ -395,7 +395,7 @@ static int pm_runtime_clk_notify(struct notifier_block *nb, | |||
| 395 | enable_clock(dev, NULL); | 395 | enable_clock(dev, NULL); |
| 396 | } | 396 | } |
| 397 | break; | 397 | break; |
| 398 | case BUS_NOTIFY_DEL_DEVICE: | 398 | case BUS_NOTIFY_UNBOUND_DRIVER: |
| 399 | if (clknb->con_ids[0]) { | 399 | if (clknb->con_ids[0]) { |
| 400 | for (con_id = clknb->con_ids; *con_id; con_id++) | 400 | for (con_id = clknb->con_ids; *con_id; con_id++) |
| 401 | disable_clock(dev, *con_id); | 401 | disable_clock(dev, *con_id); |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index aa6320207745..06f09bf89cb2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -57,7 +57,8 @@ static int async_error; | |||
| 57 | */ | 57 | */ |
| 58 | void device_pm_init(struct device *dev) | 58 | void device_pm_init(struct device *dev) |
| 59 | { | 59 | { |
| 60 | dev->power.in_suspend = false; | 60 | dev->power.is_prepared = false; |
| 61 | dev->power.is_suspended = false; | ||
| 61 | init_completion(&dev->power.completion); | 62 | init_completion(&dev->power.completion); |
| 62 | complete_all(&dev->power.completion); | 63 | complete_all(&dev->power.completion); |
| 63 | dev->power.wakeup = NULL; | 64 | dev->power.wakeup = NULL; |
| @@ -91,7 +92,7 @@ void device_pm_add(struct device *dev) | |||
| 91 | pr_debug("PM: Adding info for %s:%s\n", | 92 | pr_debug("PM: Adding info for %s:%s\n", |
| 92 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 93 | dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); |
| 93 | mutex_lock(&dpm_list_mtx); | 94 | mutex_lock(&dpm_list_mtx); |
| 94 | if (dev->parent && dev->parent->power.in_suspend) | 95 | if (dev->parent && dev->parent->power.is_prepared) |
| 95 | dev_warn(dev, "parent %s should not be sleeping\n", | 96 | dev_warn(dev, "parent %s should not be sleeping\n", |
| 96 | dev_name(dev->parent)); | 97 | dev_name(dev->parent)); |
| 97 | list_add_tail(&dev->power.entry, &dpm_list); | 98 | list_add_tail(&dev->power.entry, &dpm_list); |
| @@ -511,7 +512,14 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
| 511 | dpm_wait(dev->parent, async); | 512 | dpm_wait(dev->parent, async); |
| 512 | device_lock(dev); | 513 | device_lock(dev); |
| 513 | 514 | ||
| 514 | dev->power.in_suspend = false; | 515 | /* |
| 516 | * This is a fib. But we'll allow new children to be added below | ||
| 517 | * a resumed device, even if the device hasn't been completed yet. | ||
| 518 | */ | ||
| 519 | dev->power.is_prepared = false; | ||
| 520 | |||
| 521 | if (!dev->power.is_suspended) | ||
| 522 | goto Unlock; | ||
| 515 | 523 | ||
| 516 | if (dev->pwr_domain) { | 524 | if (dev->pwr_domain) { |
| 517 | pm_dev_dbg(dev, state, "power domain "); | 525 | pm_dev_dbg(dev, state, "power domain "); |
| @@ -548,6 +556,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) | |||
| 548 | } | 556 | } |
| 549 | 557 | ||
| 550 | End: | 558 | End: |
| 559 | dev->power.is_suspended = false; | ||
| 560 | |||
| 561 | Unlock: | ||
| 551 | device_unlock(dev); | 562 | device_unlock(dev); |
| 552 | complete_all(&dev->power.completion); | 563 | complete_all(&dev->power.completion); |
| 553 | 564 | ||
| @@ -670,7 +681,7 @@ void dpm_complete(pm_message_t state) | |||
| 670 | struct device *dev = to_device(dpm_prepared_list.prev); | 681 | struct device *dev = to_device(dpm_prepared_list.prev); |
| 671 | 682 | ||
| 672 | get_device(dev); | 683 | get_device(dev); |
| 673 | dev->power.in_suspend = false; | 684 | dev->power.is_prepared = false; |
| 674 | list_move(&dev->power.entry, &list); | 685 | list_move(&dev->power.entry, &list); |
| 675 | mutex_unlock(&dpm_list_mtx); | 686 | mutex_unlock(&dpm_list_mtx); |
| 676 | 687 | ||
| @@ -835,11 +846,11 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
| 835 | device_lock(dev); | 846 | device_lock(dev); |
| 836 | 847 | ||
| 837 | if (async_error) | 848 | if (async_error) |
| 838 | goto End; | 849 | goto Unlock; |
| 839 | 850 | ||
| 840 | if (pm_wakeup_pending()) { | 851 | if (pm_wakeup_pending()) { |
| 841 | async_error = -EBUSY; | 852 | async_error = -EBUSY; |
| 842 | goto End; | 853 | goto Unlock; |
| 843 | } | 854 | } |
| 844 | 855 | ||
| 845 | if (dev->pwr_domain) { | 856 | if (dev->pwr_domain) { |
| @@ -877,6 +888,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) | |||
| 877 | } | 888 | } |
| 878 | 889 | ||
| 879 | End: | 890 | End: |
| 891 | dev->power.is_suspended = !error; | ||
| 892 | |||
| 893 | Unlock: | ||
| 880 | device_unlock(dev); | 894 | device_unlock(dev); |
| 881 | complete_all(&dev->power.completion); | 895 | complete_all(&dev->power.completion); |
| 882 | 896 | ||
| @@ -1042,7 +1056,7 @@ int dpm_prepare(pm_message_t state) | |||
| 1042 | put_device(dev); | 1056 | put_device(dev); |
| 1043 | break; | 1057 | break; |
| 1044 | } | 1058 | } |
| 1045 | dev->power.in_suspend = true; | 1059 | dev->power.is_prepared = true; |
| 1046 | if (!list_empty(&dev->power.entry)) | 1060 | if (!list_empty(&dev->power.entry)) |
| 1047 | list_move_tail(&dev->power.entry, &dpm_prepared_list); | 1061 | list_move_tail(&dev->power.entry, &dpm_prepared_list); |
| 1048 | put_device(dev); | 1062 | put_device(dev); |
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 135df164a4c1..46767c53917a 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
| @@ -624,7 +624,7 @@ static int pci_pm_prepare(struct device *dev) | |||
| 624 | * system from the sleep state, we'll have to prevent it from signaling | 624 | * system from the sleep state, we'll have to prevent it from signaling |
| 625 | * wake-up. | 625 | * wake-up. |
| 626 | */ | 626 | */ |
| 627 | pm_runtime_resume(dev); | 627 | pm_runtime_get_sync(dev); |
| 628 | 628 | ||
| 629 | if (drv && drv->pm && drv->pm->prepare) | 629 | if (drv && drv->pm && drv->pm->prepare) |
| 630 | error = drv->pm->prepare(dev); | 630 | error = drv->pm->prepare(dev); |
| @@ -638,6 +638,8 @@ static void pci_pm_complete(struct device *dev) | |||
| 638 | 638 | ||
| 639 | if (drv && drv->pm && drv->pm->complete) | 639 | if (drv && drv->pm && drv->pm->complete) |
| 640 | drv->pm->complete(dev); | 640 | drv->pm->complete(dev); |
| 641 | |||
| 642 | pm_runtime_put_sync(dev); | ||
| 641 | } | 643 | } |
| 642 | 644 | ||
| 643 | #else /* !CONFIG_PM_SLEEP */ | 645 | #else /* !CONFIG_PM_SLEEP */ |
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index e35a17687c05..aa3cc465a601 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
| @@ -375,7 +375,7 @@ static int usb_unbind_interface(struct device *dev) | |||
| 375 | * Just re-enable it without affecting the endpoint toggles. | 375 | * Just re-enable it without affecting the endpoint toggles. |
| 376 | */ | 376 | */ |
| 377 | usb_enable_interface(udev, intf, false); | 377 | usb_enable_interface(udev, intf, false); |
| 378 | } else if (!error && !intf->dev.power.in_suspend) { | 378 | } else if (!error && !intf->dev.power.is_prepared) { |
| 379 | r = usb_set_interface(udev, intf->altsetting[0]. | 379 | r = usb_set_interface(udev, intf->altsetting[0]. |
| 380 | desc.bInterfaceNumber, 0); | 380 | desc.bInterfaceNumber, 0); |
| 381 | if (r < 0) | 381 | if (r < 0) |
| @@ -960,7 +960,7 @@ void usb_rebind_intf(struct usb_interface *intf) | |||
| 960 | } | 960 | } |
| 961 | 961 | ||
| 962 | /* Try to rebind the interface */ | 962 | /* Try to rebind the interface */ |
| 963 | if (!intf->dev.power.in_suspend) { | 963 | if (!intf->dev.power.is_prepared) { |
| 964 | intf->needs_binding = 0; | 964 | intf->needs_binding = 0; |
| 965 | rc = device_attach(&intf->dev); | 965 | rc = device_attach(&intf->dev); |
| 966 | if (rc < 0) | 966 | if (rc < 0) |
| @@ -1107,7 +1107,7 @@ static int usb_resume_interface(struct usb_device *udev, | |||
| 1107 | if (intf->condition == USB_INTERFACE_UNBOUND) { | 1107 | if (intf->condition == USB_INTERFACE_UNBOUND) { |
| 1108 | 1108 | ||
| 1109 | /* Carry out a deferred switch to altsetting 0 */ | 1109 | /* Carry out a deferred switch to altsetting 0 */ |
| 1110 | if (intf->needs_altsetting0 && !intf->dev.power.in_suspend) { | 1110 | if (intf->needs_altsetting0 && !intf->dev.power.is_prepared) { |
| 1111 | usb_set_interface(udev, intf->altsetting[0]. | 1111 | usb_set_interface(udev, intf->altsetting[0]. |
| 1112 | desc.bInterfaceNumber, 0); | 1112 | desc.bInterfaceNumber, 0); |
| 1113 | intf->needs_altsetting0 = 0; | 1113 | intf->needs_altsetting0 = 0; |
diff --git a/include/linux/device.h b/include/linux/device.h index c66111affca9..553fd37b173b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
| @@ -654,13 +654,13 @@ static inline int device_is_registered(struct device *dev) | |||
| 654 | 654 | ||
| 655 | static inline void device_enable_async_suspend(struct device *dev) | 655 | static inline void device_enable_async_suspend(struct device *dev) |
| 656 | { | 656 | { |
| 657 | if (!dev->power.in_suspend) | 657 | if (!dev->power.is_prepared) |
| 658 | dev->power.async_suspend = true; | 658 | dev->power.async_suspend = true; |
| 659 | } | 659 | } |
| 660 | 660 | ||
| 661 | static inline void device_disable_async_suspend(struct device *dev) | 661 | static inline void device_disable_async_suspend(struct device *dev) |
| 662 | { | 662 | { |
| 663 | if (!dev->power.in_suspend) | 663 | if (!dev->power.is_prepared) |
| 664 | dev->power.async_suspend = false; | 664 | dev->power.async_suspend = false; |
| 665 | } | 665 | } |
| 666 | 666 | ||
diff --git a/include/linux/pm.h b/include/linux/pm.h index 3160648ccdda..411e4f4be52b 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -425,7 +425,8 @@ struct dev_pm_info { | |||
| 425 | pm_message_t power_state; | 425 | pm_message_t power_state; |
| 426 | unsigned int can_wakeup:1; | 426 | unsigned int can_wakeup:1; |
| 427 | unsigned int async_suspend:1; | 427 | unsigned int async_suspend:1; |
| 428 | unsigned int in_suspend:1; /* Owned by the PM core */ | 428 | bool is_prepared:1; /* Owned by the PM core */ |
| 429 | bool is_suspended:1; /* Ditto */ | ||
| 429 | spinlock_t lock; | 430 | spinlock_t lock; |
| 430 | #ifdef CONFIG_PM_SLEEP | 431 | #ifdef CONFIG_PM_SLEEP |
| 431 | struct list_head entry; | 432 | struct list_head entry; |
diff --git a/kernel/power/user.c b/kernel/power/user.c index 7d02d33be699..42ddbc6f0de6 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
| @@ -113,8 +113,10 @@ static int snapshot_open(struct inode *inode, struct file *filp) | |||
| 113 | if (error) | 113 | if (error) |
| 114 | pm_notifier_call_chain(PM_POST_RESTORE); | 114 | pm_notifier_call_chain(PM_POST_RESTORE); |
| 115 | } | 115 | } |
| 116 | if (error) | 116 | if (error) { |
| 117 | free_basic_memory_bitmaps(); | ||
| 117 | atomic_inc(&snapshot_device_available); | 118 | atomic_inc(&snapshot_device_available); |
| 119 | } | ||
| 118 | data->frozen = 0; | 120 | data->frozen = 0; |
| 119 | data->ready = 0; | 121 | data->ready = 0; |
| 120 | data->platform_support = 0; | 122 | data->platform_support = 0; |
