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 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 { |
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 1eca50c8e7ca..e56b4388fe61 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 b417fc46f3fc..52e8c55ff314 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 |