diff options
29 files changed, 734 insertions, 188 deletions
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index d6a801f45b48..2875f1f74a07 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power | |||
| @@ -114,3 +114,18 @@ Description: | |||
| 114 | if this file contains "1", which is the default. It may be | 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 | 115 | disabled by writing "0" to this file, in which case all devices |
| 116 | will be suspended and resumed synchronously. | 116 | will be suspended and resumed synchronously. |
| 117 | |||
| 118 | What: /sys/power/wakeup_count | ||
| 119 | Date: July 2010 | ||
| 120 | Contact: Rafael J. Wysocki <rjw@sisk.pl> | ||
| 121 | Description: | ||
| 122 | The /sys/power/wakeup_count file allows user space to put the | ||
| 123 | system into a sleep state while taking into account the | ||
| 124 | concurrent arrival of wakeup events. Reading from it returns | ||
| 125 | the current number of registered wakeup events and it blocks if | ||
| 126 | some wakeup events are being processed at the time the file is | ||
| 127 | read from. Writing to it will only succeed if the current | ||
| 128 | number of wakeup events is equal to the written value and, if | ||
| 129 | successful, will make the kernel abort a subsequent transition | ||
| 130 | to a sleep state if any wakeup events are reported after the | ||
| 131 | write has returned. | ||
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 89de75325cea..cbccf9a3cee4 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | obj-$(CONFIG_PM) += sysfs.o | 1 | obj-$(CONFIG_PM) += sysfs.o |
| 2 | obj-$(CONFIG_PM_SLEEP) += main.o | 2 | obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o |
| 3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o | 3 | obj-$(CONFIG_PM_RUNTIME) += runtime.o |
| 4 | obj-$(CONFIG_PM_OPS) += generic_ops.o | 4 | obj-$(CONFIG_PM_OPS) += generic_ops.o |
| 5 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o | 5 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o |
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 941fcb87e52a..5419a49ff135 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c | |||
| @@ -59,6 +59,7 @@ void device_pm_init(struct device *dev) | |||
| 59 | { | 59 | { |
| 60 | dev->power.status = DPM_ON; | 60 | dev->power.status = DPM_ON; |
| 61 | init_completion(&dev->power.completion); | 61 | init_completion(&dev->power.completion); |
| 62 | dev->power.wakeup_count = 0; | ||
| 62 | pm_runtime_init(dev); | 63 | pm_runtime_init(dev); |
| 63 | } | 64 | } |
| 64 | 65 | ||
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b0ec0e9f27e9..b78c401ffa73 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -123,6 +123,45 @@ int pm_runtime_idle(struct device *dev) | |||
| 123 | } | 123 | } |
| 124 | EXPORT_SYMBOL_GPL(pm_runtime_idle); | 124 | EXPORT_SYMBOL_GPL(pm_runtime_idle); |
| 125 | 125 | ||
| 126 | |||
| 127 | /** | ||
| 128 | * update_pm_runtime_accounting - Update the time accounting of power states | ||
| 129 | * @dev: Device to update the accounting for | ||
| 130 | * | ||
| 131 | * In order to be able to have time accounting of the various power states | ||
| 132 | * (as used by programs such as PowerTOP to show the effectiveness of runtime | ||
| 133 | * PM), we need to track the time spent in each state. | ||
| 134 | * update_pm_runtime_accounting must be called each time before the | ||
| 135 | * runtime_status field is updated, to account the time in the old state | ||
| 136 | * correctly. | ||
| 137 | */ | ||
| 138 | void update_pm_runtime_accounting(struct device *dev) | ||
| 139 | { | ||
| 140 | unsigned long now = jiffies; | ||
| 141 | int delta; | ||
| 142 | |||
| 143 | delta = now - dev->power.accounting_timestamp; | ||
| 144 | |||
| 145 | if (delta < 0) | ||
| 146 | delta = 0; | ||
| 147 | |||
| 148 | dev->power.accounting_timestamp = now; | ||
| 149 | |||
| 150 | if (dev->power.disable_depth > 0) | ||
| 151 | return; | ||
| 152 | |||
| 153 | if (dev->power.runtime_status == RPM_SUSPENDED) | ||
| 154 | dev->power.suspended_jiffies += delta; | ||
| 155 | else | ||
| 156 | dev->power.active_jiffies += delta; | ||
| 157 | } | ||
| 158 | |||
| 159 | static void __update_runtime_status(struct device *dev, enum rpm_status status) | ||
| 160 | { | ||
| 161 | update_pm_runtime_accounting(dev); | ||
| 162 | dev->power.runtime_status = status; | ||
| 163 | } | ||
| 164 | |||
| 126 | /** | 165 | /** |
| 127 | * __pm_runtime_suspend - Carry out run-time suspend of given device. | 166 | * __pm_runtime_suspend - Carry out run-time suspend of given device. |
| 128 | * @dev: Device to suspend. | 167 | * @dev: Device to suspend. |
| @@ -197,7 +236,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
| 197 | goto repeat; | 236 | goto repeat; |
| 198 | } | 237 | } |
| 199 | 238 | ||
| 200 | dev->power.runtime_status = RPM_SUSPENDING; | 239 | __update_runtime_status(dev, RPM_SUSPENDING); |
| 201 | dev->power.deferred_resume = false; | 240 | dev->power.deferred_resume = false; |
| 202 | 241 | ||
| 203 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { | 242 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { |
| @@ -228,7 +267,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
| 228 | } | 267 | } |
| 229 | 268 | ||
| 230 | if (retval) { | 269 | if (retval) { |
| 231 | dev->power.runtime_status = RPM_ACTIVE; | 270 | __update_runtime_status(dev, RPM_ACTIVE); |
| 232 | if (retval == -EAGAIN || retval == -EBUSY) { | 271 | if (retval == -EAGAIN || retval == -EBUSY) { |
| 233 | if (dev->power.timer_expires == 0) | 272 | if (dev->power.timer_expires == 0) |
| 234 | notify = true; | 273 | notify = true; |
| @@ -237,7 +276,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) | |||
| 237 | pm_runtime_cancel_pending(dev); | 276 | pm_runtime_cancel_pending(dev); |
| 238 | } | 277 | } |
| 239 | } else { | 278 | } else { |
| 240 | dev->power.runtime_status = RPM_SUSPENDED; | 279 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 241 | pm_runtime_deactivate_timer(dev); | 280 | pm_runtime_deactivate_timer(dev); |
| 242 | 281 | ||
| 243 | if (dev->parent) { | 282 | if (dev->parent) { |
| @@ -381,7 +420,7 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) | |||
| 381 | goto repeat; | 420 | goto repeat; |
| 382 | } | 421 | } |
| 383 | 422 | ||
| 384 | dev->power.runtime_status = RPM_RESUMING; | 423 | __update_runtime_status(dev, RPM_RESUMING); |
| 385 | 424 | ||
| 386 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { | 425 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { |
| 387 | spin_unlock_irq(&dev->power.lock); | 426 | spin_unlock_irq(&dev->power.lock); |
| @@ -411,10 +450,10 @@ int __pm_runtime_resume(struct device *dev, bool from_wq) | |||
| 411 | } | 450 | } |
| 412 | 451 | ||
| 413 | if (retval) { | 452 | if (retval) { |
| 414 | dev->power.runtime_status = RPM_SUSPENDED; | 453 | __update_runtime_status(dev, RPM_SUSPENDED); |
| 415 | pm_runtime_cancel_pending(dev); | 454 | pm_runtime_cancel_pending(dev); |
| 416 | } else { | 455 | } else { |
