diff options
Diffstat (limited to 'drivers/base/power/sysfs.c')
-rw-r--r-- | drivers/base/power/sysfs.c | 263 |
1 files changed, 243 insertions, 20 deletions
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index e56b4388fe61..a9f5b8979611 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -75,12 +75,27 @@ | |||
75 | * attribute is set to "enabled" by bus type code or device drivers and in | 75 | * attribute is set to "enabled" by bus type code or device drivers and in |
76 | * that cases it should be safe to leave the default value. | 76 | * that cases it should be safe to leave the default value. |
77 | * | 77 | * |
78 | * autosuspend_delay_ms - Report/change a device's autosuspend_delay value | ||
79 | * | ||
80 | * Some drivers don't want to carry out a runtime suspend as soon as a | ||
81 | * device becomes idle; they want it always to remain idle for some period | ||
82 | * of time before suspending it. This period is the autosuspend_delay | ||
83 | * value (expressed in milliseconds) and it can be controlled by the user. | ||
84 | * If the value is negative then the device will never be runtime | ||
85 | * suspended. | ||
86 | * | ||
87 | * NOTE: The autosuspend_delay_ms attribute and the autosuspend_delay | ||
88 | * value are used only if the driver calls pm_runtime_use_autosuspend(). | ||
89 | * | ||
78 | * wakeup_count - Report the number of wakeup events related to the device | 90 | * wakeup_count - Report the number of wakeup events related to the device |
79 | */ | 91 | */ |
80 | 92 | ||
81 | static const char enabled[] = "enabled"; | 93 | static const char enabled[] = "enabled"; |
82 | static const char disabled[] = "disabled"; | 94 | static const char disabled[] = "disabled"; |
83 | 95 | ||
96 | const char power_group_name[] = "power"; | ||
97 | EXPORT_SYMBOL_GPL(power_group_name); | ||
98 | |||
84 | #ifdef CONFIG_PM_RUNTIME | 99 | #ifdef CONFIG_PM_RUNTIME |
85 | static const char ctrl_auto[] = "auto"; | 100 | static const char ctrl_auto[] = "auto"; |
86 | static const char ctrl_on[] = "on"; | 101 | static const char ctrl_on[] = "on"; |
@@ -170,8 +185,36 @@ static ssize_t rtpm_status_show(struct device *dev, | |||
170 | } | 185 | } |
171 | 186 | ||
172 | static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); | 187 | static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); |
173 | #endif | ||
174 | 188 | ||
189 | static ssize_t autosuspend_delay_ms_show(struct device *dev, | ||
190 | struct device_attribute *attr, char *buf) | ||
191 | { | ||
192 | if (!dev->power.use_autosuspend) | ||
193 | return -EIO; | ||
194 | return sprintf(buf, "%d\n", dev->power.autosuspend_delay); | ||
195 | } | ||
196 | |||
197 | static ssize_t autosuspend_delay_ms_store(struct device *dev, | ||
198 | struct device_attribute *attr, const char *buf, size_t n) | ||
199 | { | ||
200 | long delay; | ||
201 | |||
202 | if (!dev->power.use_autosuspend) | ||
203 | return -EIO; | ||
204 | |||
205 | if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay) | ||
206 | return -EINVAL; | ||
207 | |||
208 | pm_runtime_set_autosuspend_delay(dev, delay); | ||
209 | return n; | ||
210 | } | ||
211 | |||
212 | static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, | ||
213 | autosuspend_delay_ms_store); | ||
214 | |||
215 | #endif /* CONFIG_PM_RUNTIME */ | ||
216 | |||
217 | #ifdef CONFIG_PM_SLEEP | ||
175 | static ssize_t | 218 | static ssize_t |
176 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) | 219 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) |
177 | { | 220 | { |
@@ -206,15 +249,125 @@ wake_store(struct device * dev, struct device_attribute *attr, | |||
206 | 249 | ||
207 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); | 250 | static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); |
208 | 251 | ||
209 | #ifdef CONFIG_PM_SLEEP | ||
210 | static ssize_t wakeup_count_show(struct device *dev, | 252 | static ssize_t wakeup_count_show(struct device *dev, |
211 | struct device_attribute *attr, char *buf) | 253 | struct device_attribute *attr, char *buf) |
212 | { | 254 | { |
213 | return sprintf(buf, "%lu\n", dev->power.wakeup_count); | 255 | unsigned long count = 0; |
256 | bool enabled = false; | ||
257 | |||
258 | spin_lock_irq(&dev->power.lock); | ||
259 | if (dev->power.wakeup) { | ||
260 | count = dev->power.wakeup->event_count; | ||
261 | enabled = true; | ||
262 | } | ||
263 | spin_unlock_irq(&dev->power.lock); | ||
264 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); | ||
214 | } | 265 | } |
215 | 266 | ||
216 | static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL); | 267 | static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL); |
217 | #endif | 268 | |
269 | static ssize_t wakeup_active_count_show(struct device *dev, | ||
270 | struct device_attribute *attr, char *buf) | ||
271 | { | ||
272 | unsigned long count = 0; | ||
273 | bool enabled = false; | ||
274 | |||
275 | spin_lock_irq(&dev->power.lock); | ||
276 | if (dev->power.wakeup) { | ||
277 | count = dev->power.wakeup->active_count; | ||
278 | enabled = true; | ||
279 | } | ||
280 | spin_unlock_irq(&dev->power.lock); | ||
281 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); | ||
282 | } | ||
283 | |||
284 | static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL); | ||
285 | |||
286 | static ssize_t wakeup_hit_count_show(struct device *dev, | ||
287 | struct device_attribute *attr, char *buf) | ||
288 | { | ||
289 | unsigned long count = 0; | ||
290 | bool enabled = false; | ||
291 | |||
292 | spin_lock_irq(&dev->power.lock); | ||
293 | if (dev->power.wakeup) { | ||
294 | count = dev->power.wakeup->hit_count; | ||
295 | enabled = true; | ||
296 | } | ||
297 | spin_unlock_irq(&dev->power.lock); | ||
298 | return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n"); | ||
299 | } | ||
300 | |||
301 | static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL); | ||
302 | |||
303 | static ssize_t wakeup_active_show(struct device *dev, | ||
304 | struct device_attribute *attr, char *buf) | ||
305 | { | ||
306 | unsigned int active = 0; | ||
307 | bool enabled = false; | ||
308 | |||
309 | spin_lock_irq(&dev->power.lock); | ||
310 | if (dev->power.wakeup) { | ||
311 | active = dev->power.wakeup->active; | ||
312 | enabled = true; | ||
313 | } | ||
314 | spin_unlock_irq(&dev->power.lock); | ||
315 | return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n"); | ||
316 | } | ||
317 | |||
318 | static DEVICE_ATTR(wakeup_active, 0444, wakeup_active_show, NULL); | ||
319 | |||
320 | static ssize_t wakeup_total_time_show(struct device *dev, | ||
321 | struct device_attribute *attr, char *buf) | ||
322 | { | ||
323 | s64 msec = 0; | ||
324 | bool enabled = false; | ||
325 | |||
326 | spin_lock_irq(&dev->power.lock); | ||
327 | if (dev->power.wakeup) { | ||
328 | msec = ktime_to_ms(dev->power.wakeup->total_time); | ||
329 | enabled = true; | ||
330 | } | ||
331 | spin_unlock_irq(&dev->power.lock); | ||
332 | return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); | ||
333 | } | ||
334 | |||
335 | static DEVICE_ATTR(wakeup_total_time_ms, 0444, wakeup_total_time_show, NULL); | ||
336 | |||
337 | static ssize_t wakeup_max_time_show(struct device *dev, | ||
338 | struct device_attribute *attr, char *buf) | ||
339 | { | ||
340 | s64 msec = 0; | ||
341 | bool enabled = false; | ||
342 | |||
343 | spin_lock_irq(&dev->power.lock); | ||
344 | if (dev->power.wakeup) { | ||
345 | msec = ktime_to_ms(dev->power.wakeup->max_time); | ||
346 | enabled = true; | ||
347 | } | ||
348 | spin_unlock_irq(&dev->power.lock); | ||
349 | return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); | ||
350 | } | ||
351 | |||
352 | static DEVICE_ATTR(wakeup_max_time_ms, 0444, wakeup_max_time_show, NULL); | ||
353 | |||
354 | static ssize_t wakeup_last_time_show(struct device *dev, | ||
355 | struct device_attribute *attr, char *buf) | ||
356 | { | ||
357 | s64 msec = 0; | ||
358 | bool enabled = false; | ||
359 | |||
360 | spin_lock_irq(&dev->power.lock); | ||
361 | if (dev->power.wakeup) { | ||
362 | msec = ktime_to_ms(dev->power.wakeup->last_time); | ||
363 | enabled = true; | ||
364 | } | ||
365 | spin_unlock_irq(&dev->power.lock); | ||
366 | return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n"); | ||
367 | } | ||
368 | |||
369 | static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL); | ||
370 | #endif /* CONFIG_PM_SLEEP */ | ||
218 | 371 | ||
219 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 372 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
220 | #ifdef CONFIG_PM_RUNTIME | 373 | #ifdef CONFIG_PM_RUNTIME |
@@ -278,38 +431,108 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, | |||
278 | static DEVICE_ATTR(async, 0644, async_show, async_store); | 431 | static DEVICE_ATTR(async, 0644, async_show, async_store); |
279 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ | 432 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ |
280 | 433 | ||
281 | static struct attribute * power_attrs[] = { | 434 | static struct attribute *power_attrs[] = { |
282 | #ifdef CONFIG_PM_RUNTIME | ||
283 | &dev_attr_control.attr, | ||
284 | &dev_attr_runtime_status.attr, | ||
285 | &dev_attr_runtime_suspended_time.attr, | ||
286 | &dev_attr_runtime_active_time.attr, | ||
287 | #endif | ||
288 | &dev_attr_wakeup.attr, | ||
289 | #ifdef CONFIG_PM_SLEEP | ||
290 | &dev_attr_wakeup_count.attr, | ||
291 | #endif | ||
292 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 435 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
436 | #ifdef CONFIG_PM_SLEEP | ||
293 | &dev_attr_async.attr, | 437 | &dev_attr_async.attr, |
438 | #endif | ||
294 | #ifdef CONFIG_PM_RUNTIME | 439 | #ifdef CONFIG_PM_RUNTIME |
440 | &dev_attr_runtime_status.attr, | ||
295 | &dev_attr_runtime_usage.attr, | 441 | &dev_attr_runtime_usage.attr, |
296 | &dev_attr_runtime_active_kids.attr, | 442 | &dev_attr_runtime_active_kids.attr, |
297 | &dev_attr_runtime_enabled.attr, | 443 | &dev_attr_runtime_enabled.attr, |
298 | #endif | 444 | #endif |
299 | #endif | 445 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ |
300 | NULL, | 446 | NULL, |
301 | }; | 447 | }; |
302 | static struct attribute_group pm_attr_group = { | 448 | static struct attribute_group pm_attr_group = { |
303 | .name = "power", | 449 | .name = power_group_name, |
304 | .attrs = power_attrs, | 450 | .attrs = power_attrs, |
305 | }; | 451 | }; |
306 | 452 | ||
307 | int dpm_sysfs_add(struct device * dev) | 453 | static struct attribute *wakeup_attrs[] = { |
454 | #ifdef CONFIG_PM_SLEEP | ||
455 | &dev_attr_wakeup.attr, | ||
456 | &dev_attr_wakeup_count.attr, | ||
457 | &dev_attr_wakeup_active_count.attr, | ||
458 | &dev_attr_wakeup_hit_count.attr, | ||
459 | &dev_attr_wakeup_active.attr, | ||
460 | &dev_attr_wakeup_total_time_ms.attr, | ||
461 | &dev_attr_wakeup_max_time_ms.attr, | ||
462 | &dev_attr_wakeup_last_time_ms.attr, | ||
463 | #endif | ||
464 | NULL, | ||
465 | }; | ||
466 | static struct attribute_group pm_wakeup_attr_group = { | ||
467 | .name = power_group_name, | ||
468 | .attrs = wakeup_attrs, | ||
469 | }; | ||
470 | |||
471 | static struct attribute *runtime_attrs[] = { | ||
472 | #ifdef CONFIG_PM_RUNTIME | ||
473 | #ifndef CONFIG_PM_ADVANCED_DEBUG | ||
474 | &dev_attr_runtime_status.attr, | ||
475 | #endif | ||
476 | &dev_attr_control.attr, | ||
477 | &dev_attr_runtime_suspended_time.attr, | ||
478 | &dev_attr_runtime_active_time.attr, | ||
479 | &dev_attr_autosuspend_delay_ms.attr, | ||
480 | #endif /* CONFIG_PM_RUNTIME */ | ||
481 | NULL, | ||
482 | }; | ||
483 | static struct attribute_group pm_runtime_attr_group = { | ||
484 | .name = power_group_name, | ||
485 | .attrs = runtime_attrs, | ||
486 | }; | ||
487 | |||
488 | int dpm_sysfs_add(struct device *dev) | ||
489 | { | ||
490 | int rc; | ||
491 | |||
492 | rc = sysfs_create_group(&dev->kobj, &pm_attr_group); | ||
493 | if (rc) | ||
494 | return rc; | ||
495 | |||
496 | if (pm_runtime_callbacks_present(dev)) { | ||
497 | rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); | ||
498 | if (rc) | ||
499 | goto err_out; | ||
500 | } | ||
501 | |||
502 | if (device_can_wakeup(dev)) { | ||
503 | rc = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group); | ||
504 | if (rc) { | ||
505 | if (pm_runtime_callbacks_present(dev)) | ||
506 | sysfs_unmerge_group(&dev->kobj, | ||
507 | &pm_runtime_attr_group); | ||
508 | goto err_out; | ||
509 | } | ||
510 | } | ||
511 | return 0; | ||
512 | |||
513 | err_out: | ||
514 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | ||
515 | return rc; | ||
516 | } | ||
517 | |||
518 | int wakeup_sysfs_add(struct device *dev) | ||
519 | { | ||
520 | return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group); | ||
521 | } | ||
522 | |||
523 | void wakeup_sysfs_remove(struct device *dev) | ||
524 | { | ||
525 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); | ||
526 | } | ||
527 | |||
528 | void rpm_sysfs_remove(struct device *dev) | ||
308 | { | 529 | { |
309 | return sysfs_create_group(&dev->kobj, &pm_attr_group); | 530 | sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); |
310 | } | 531 | } |
311 | 532 | ||
312 | void dpm_sysfs_remove(struct device * dev) | 533 | void dpm_sysfs_remove(struct device *dev) |
313 | { | 534 | { |
535 | rpm_sysfs_remove(dev); | ||
536 | sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); | ||
314 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | 537 | sysfs_remove_group(&dev->kobj, &pm_attr_group); |
315 | } | 538 | } |