diff options
| -rw-r--r-- | drivers/base/power/runtime.c | 54 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 30 | ||||
| -rw-r--r-- | include/linux/pm.h | 6 |
3 files changed, 83 insertions, 7 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b0ec0e9f27e..b78c401ffa7 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 { |
| 417 | dev->power.runtime_status = RPM_ACTIVE; | 456 | __update_runtime_status(dev, RPM_ACTIVE); |
| 418 | if (parent) | 457 | if (parent) |
| 419 | atomic_inc(&parent->power.child_count); | 458 | atomic_inc(&parent->power.child_count); |
| 420 | } | 459 | } |
| @@ -848,7 +887,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status) | |||
| 848 | } | 887 | } |
| 849 | 888 | ||
| 850 | out_set: | 889 | out_set: |
| 851 | dev->power.runtime_status = status; | 890 | __update_runtime_status(dev, status); |
| 852 | dev->power.runtime_error = 0; | 891 | dev->power.runtime_error = 0; |
| 853 | out: | 892 | out: |
| 854 | spin_unlock_irqrestore(&dev->power.lock, flags); | 893 | spin_unlock_irqrestore(&dev->power.lock, flags); |
| @@ -1077,6 +1116,7 @@ void pm_runtime_init(struct device *dev) | |||
| 1077 | dev->power.request_pending = false; | 1116 | dev->power.request_pending = false; |
| 1078 | dev->power.request = RPM_REQ_NONE; | 1117 | dev->power.request = RPM_REQ_NONE; |
| 1079 | dev->power.deferred_resume = false; | 1118 | dev->power.deferred_resume = false; |
| 1119 | dev->power.accounting_timestamp = jiffies; | ||
| 1080 | INIT_WORK(&dev->power.work, pm_runtime_work); | 1120 | INIT_WORK(&dev->power.work, pm_runtime_work); |
| 1081 | 1121 | ||
| 1082 | dev->power.timer_expires = 0; | 1122 | dev->power.timer_expires = 0; |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 1eca50c8e7c..e56b4388fe6 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
| 7 | #include <linux/pm_runtime.h> | 7 | #include <linux/pm_runtime.h> |
| 8 | #include <asm/atomic.h> | 8 | #include <asm/atomic.h> |
| 9 | #include <linux/jiffies.h> | ||
| 9 | #include "power.h" | 10 | #include "power.h" |
| 10 | 11 | ||
| 11 | /* | 12 | /* |
| @@ -111,6 +112,33 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr, | |||
| 111 | 112 | ||
| 112 | static DEVICE_ATTR(control, 0644, control_show, control_store); | 113 | static DEVICE_ATTR(control, 0644, control_show, control_store); |
| 113 | 114 | ||
| 115 | static ssize_t rtpm_active_time_show(struct device *dev, | ||
| 116 | struct device_attribute *attr, char *buf) | ||
| 117 | { | ||
| 118 | int ret; | ||
| 119 | spin_lock_irq(&dev->power.lock); | ||
| 120 | update_pm_runtime_accounting(dev); | ||
| 121 | ret = sprintf(buf, "%i\n", jiffies_to_msecs(dev->power.active_jiffies)); | ||
| 122 | spin_unlock_irq(&dev->power.lock); | ||
| 123 | return ret; | ||
| 124 | } | ||
| 125 | |||
| 126 | static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL); | ||
| 127 | |||
| 128 | static ssize_t rtpm_suspended_time_show(struct device *dev, | ||
| 129 | struct device_attribute *attr, char *buf) | ||
| 130 | { | ||
| 131 | int ret; | ||
| 132 | spin_lock_irq(&dev->power.lock); | ||
| 133 | update_pm_runtime_accounting(dev); | ||
| 134 | ret = sprintf(buf, "%i\n", | ||
| 135 | jiffies_to_msecs(dev->power.suspended_jiffies)); | ||
| 136 | spin_unlock_irq(&dev->power.lock); | ||
| 137 | return ret; | ||
| 138 | } | ||
| 139 | |||
| 140 | static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL); | ||
| 141 | |||
| 114 | static ssize_t rtpm_status_show(struct device *dev, | 142 | static ssize_t rtpm_status_show(struct device *dev, |
| 115 | struct device_attribute *attr, char *buf) | 143 | struct device_attribute *attr, char *buf) |
| 116 | { | 144 | { |
| @@ -254,6 +282,8 @@ static struct attribute * power_attrs[] = { | |||
| 254 | #ifdef CONFIG_PM_RUNTIME | 282 | #ifdef CONFIG_PM_RUNTIME |
| 255 | &dev_attr_control.attr, | 283 | &dev_attr_control.attr, |
| 256 | &dev_attr_runtime_status.attr, | 284 | &dev_attr_runtime_status.attr, |
| 285 | &dev_attr_runtime_suspended_time.attr, | ||
| 286 | &dev_attr_runtime_active_time.attr, | ||
| 257 | #endif | 287 | #endif |
| 258 | &dev_attr_wakeup.attr, | 288 | &dev_attr_wakeup.attr, |
| 259 | #ifdef CONFIG_PM_SLEEP | 289 | #ifdef CONFIG_PM_SLEEP |
diff --git a/include/linux/pm.h b/include/linux/pm.h index b417fc46f3f..52e8c55ff31 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -477,9 +477,15 @@ struct dev_pm_info { | |||
| 477 | enum rpm_request request; | 477 | enum rpm_request request; |
| 478 | enum rpm_status runtime_status; | 478 | enum rpm_status runtime_status; |
| 479 | int runtime_error; | 479 | int runtime_error; |
| 480 | unsigned long active_jiffies; | ||
| 481 | unsigned long suspended_jiffies; | ||
| 482 | unsigned long accounting_timestamp; | ||
| 480 | #endif | 483 | #endif |
| 481 | }; | 484 | }; |
| 482 | 485 | ||
| 486 | extern void update_pm_runtime_accounting(struct device *dev); | ||
| 487 | |||
| 488 | |||
| 483 | /* | 489 | /* |
| 484 | * The PM_EVENT_ messages are also used by drivers implementing the legacy | 490 | * The PM_EVENT_ messages are also used by drivers implementing the legacy |
| 485 | * suspend framework, based on the ->suspend() and ->resume() callbacks common | 491 | * suspend framework, based on the ->suspend() and ->resume() callbacks common |
