diff options
-rw-r--r-- | Documentation/power/runtime_pm.txt | 37 | ||||
-rw-r--r-- | drivers/base/power/power.h | 1 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 54 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 56 | ||||
-rw-r--r-- | include/linux/pm.h | 7 | ||||
-rw-r--r-- | include/linux/pm_runtime.h | 2 |
6 files changed, 149 insertions, 8 deletions
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 55b859b3bc72..9ba49b21ac86 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt | |||
@@ -1,6 +1,7 @@ | |||
1 | Run-time Power Management Framework for I/O Devices | 1 | Run-time Power Management Framework for I/O Devices |
2 | 2 | ||
3 | (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. | 3 | (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. |
4 | (C) 2010 Alan Stern <stern@rowland.harvard.edu> | ||
4 | 5 | ||
5 | 1. Introduction | 6 | 1. Introduction |
6 | 7 | ||
@@ -230,6 +231,11 @@ defined in include/linux/pm.h: | |||
230 | interface; it may only be modified with the help of the pm_runtime_allow() | 231 | interface; it may only be modified with the help of the pm_runtime_allow() |
231 | and pm_runtime_forbid() helper functions | 232 | and pm_runtime_forbid() helper functions |
232 | 233 | ||
234 | unsigned int no_callbacks; | ||
235 | - indicates that the device does not use the run-time PM callbacks (see | ||
236 | Section 8); it may be modified only by the pm_runtime_no_callbacks() | ||
237 | helper function | ||
238 | |||
233 | All of the above fields are members of the 'power' member of 'struct device'. | 239 | All of the above fields are members of the 'power' member of 'struct device'. |
234 | 240 | ||
235 | 4. Run-time PM Device Helper Functions | 241 | 4. Run-time PM Device Helper Functions |
@@ -349,6 +355,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: | |||
349 | counter (used by the /sys/devices/.../power/control interface to | 355 | counter (used by the /sys/devices/.../power/control interface to |
350 | effectively prevent the device from being power managed at run time) | 356 | effectively prevent the device from being power managed at run time) |
351 | 357 | ||
358 | void pm_runtime_no_callbacks(struct device *dev); | ||
359 | - set the power.no_callbacks flag for the device and remove the run-time | ||
360 | PM attributes from /sys/devices/.../power (or prevent them from being | ||
361 | added when the device is registered) | ||
362 | |||
352 | It is safe to execute the following helper functions from interrupt context: | 363 | It is safe to execute the following helper functions from interrupt context: |
353 | 364 | ||
354 | pm_request_idle() | 365 | pm_request_idle() |
@@ -524,3 +535,29 @@ poweroff and run-time suspend callback, and similarly for system resume, thaw, | |||
524 | restore, and run-time resume, can achieve this with the help of the | 535 | restore, and run-time resume, can achieve this with the help of the |
525 | UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its | 536 | UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its |
526 | last argument to NULL). | 537 | last argument to NULL). |
538 | |||
539 | 8. "No-Callback" Devices | ||
540 | |||
541 | Some "devices" are only logical sub-devices of their parent and cannot be | ||
542 | power-managed on their own. (The prototype example is a USB interface. Entire | ||
543 | USB devices can go into low-power mode or send wake-up requests, but neither is | ||
544 | possible for individual interfaces.) The drivers for these devices have no | ||
545 | need of run-time PM callbacks; if the callbacks did exist, ->runtime_suspend() | ||
546 | and ->runtime_resume() would always return 0 without doing anything else and | ||
547 | ->runtime_idle() would always call pm_runtime_suspend(). | ||
548 | |||
549 | Subsystems can tell the PM core about these devices by calling | ||
550 | pm_runtime_no_callbacks(). This should be done after the device structure is | ||
551 | initialized and before it is registered (although after device registration is | ||
552 | also okay). The routine will set the device's power.no_callbacks flag and | ||
553 | prevent the non-debugging run-time PM sysfs attributes from being created. | ||
554 | |||
555 | When power.no_callbacks is set, the PM core will not invoke the | ||
556 | ->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks. | ||
557 | Instead it will assume that suspends and resumes always succeed and that idle | ||
558 | devices should be suspended. | ||
559 | |||
560 | As a consequence, the PM core will never directly inform the device's subsystem | ||
561 | or driver about run-time power changes. Instead, the driver for the device's | ||
562 | parent must take responsibility for telling the device's driver when the | ||
563 | parent's power state changes. | ||
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 8b2745c69e2a..698dde742587 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h | |||
@@ -60,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {} | |||
60 | 60 | ||
61 | extern int dpm_sysfs_add(struct device *); | 61 | extern int dpm_sysfs_add(struct device *); |
62 | extern void dpm_sysfs_remove(struct device *); | 62 | extern void dpm_sysfs_remove(struct device *); |
63 | extern void rpm_sysfs_remove(struct device *); | ||
63 | 64 | ||
64 | #else /* CONFIG_PM */ | 65 | #else /* CONFIG_PM */ |
65 | 66 | ||
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ed227b7c1bb5..5bd4daa93ef1 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
@@ -10,8 +10,10 @@ | |||
10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
11 | #include <linux/pm_runtime.h> | 11 | #include <linux/pm_runtime.h> |
12 | #include <linux/jiffies.h> | 12 | #include <linux/jiffies.h> |
13 | #include "power.h" | ||
13 | 14 | ||
14 | static int rpm_resume(struct device *dev, int rpmflags); | 15 | static int rpm_resume(struct device *dev, int rpmflags); |
16 | static int rpm_suspend(struct device *dev, int rpmflags); | ||
15 | 17 | ||
16 | /** | 18 | /** |
17 | * update_pm_runtime_accounting - Update the time accounting of power states | 19 | * update_pm_runtime_accounting - Update the time accounting of power states |
@@ -148,6 +150,12 @@ static int rpm_idle(struct device *dev, int rpmflags) | |||
148 | /* Pending requests need to be canceled. */ | 150 | /* Pending requests need to be canceled. */ |
149 | dev->power.request = RPM_REQ_NONE; | 151 | dev->power.request = RPM_REQ_NONE; |
150 | 152 | ||
153 | if (dev->power.no_callbacks) { | ||
154 | /* Assume ->runtime_idle() callback would have suspended. */ | ||
155 | retval = rpm_suspend(dev, rpmflags); | ||
156 | goto out; | ||
157 | } | ||
158 | |||
151 | /* Carry out an asynchronous or a synchronous idle notification. */ | 159 | /* Carry out an asynchronous or a synchronous idle notification. */ |
152 | if (rpmflags & RPM_ASYNC) { | 160 | if (rpmflags & RPM_ASYNC) { |
153 | dev->power.request = RPM_REQ_IDLE; | 161 | dev->power.request = RPM_REQ_IDLE; |
@@ -254,6 +262,10 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
254 | goto repeat; | 262 | goto repeat; |
255 | } | 263 | } |
256 | 264 | ||
265 | dev->power.deferred_resume = false; | ||
266 | if (dev->power.no_callbacks) | ||
267 | goto no_callback; /* Assume success. */ | ||
268 | |||
257 | /* Carry out an asynchronous or a synchronous suspend. */ | 269 | /* Carry out an asynchronous or a synchronous suspend. */ |
258 | if (rpmflags & RPM_ASYNC) { | 270 | if (rpmflags & RPM_ASYNC) { |
259 | dev->power.request = RPM_REQ_SUSPEND; | 271 | dev->power.request = RPM_REQ_SUSPEND; |
@@ -265,7 +277,6 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
265 | } | 277 | } |
266 | 278 | ||
267 | __update_runtime_status(dev, RPM_SUSPENDING); | 279 | __update_runtime_status(dev, RPM_SUSPENDING); |
268 | dev->power.deferred_resume = false; | ||
269 | 280 | ||
270 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { | 281 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { |
271 | spin_unlock_irq(&dev->power.lock); | 282 | spin_unlock_irq(&dev->power.lock); |
@@ -305,6 +316,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) | |||
305 | pm_runtime_cancel_pending(dev); | 316 | pm_runtime_cancel_pending(dev); |
306 | } | 317 | } |
307 | } else { | 318 | } else { |
319 | no_callback: | ||
308 | __update_runtime_status(dev, RPM_SUSPENDED); | 320 | __update_runtime_status(dev, RPM_SUSPENDED); |
309 | pm_runtime_deactivate_timer(dev); | 321 | pm_runtime_deactivate_timer(dev); |
310 | 322 | ||
@@ -409,6 +421,23 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
409 | goto repeat; | 421 | goto repeat; |
410 | } | 422 | } |
411 | 423 | ||
424 | /* | ||
425 | * See if we can skip waking up the parent. This is safe only if | ||
426 | * power.no_callbacks is set, because otherwise we don't know whether | ||
427 | * the resume will actually succeed. | ||
428 | */ | ||
429 | if (dev->power.no_callbacks && !parent && dev->parent) { | ||
430 | spin_lock(&dev->parent->power.lock); | ||
431 | if (dev->parent->power.disable_depth > 0 | ||
432 | || dev->parent->power.ignore_children | ||
433 | || dev->parent->power.runtime_status == RPM_ACTIVE) { | ||
434 | atomic_inc(&dev->parent->power.child_count); | ||
435 | spin_unlock(&dev->parent->power.lock); | ||
436 | goto no_callback; /* Assume success. */ | ||
437 | } | ||
438 | spin_unlock(&dev->parent->power.lock); | ||
439 | } | ||
440 | |||
412 | /* Carry out an asynchronous or a synchronous resume. */ | 441 | /* Carry out an asynchronous or a synchronous resume. */ |
413 | if (rpmflags & RPM_ASYNC) { | 442 | if (rpmflags & RPM_ASYNC) { |
414 | dev->power.request = RPM_REQ_RESUME; | 443 | dev->power.request = RPM_REQ_RESUME; |
@@ -449,6 +478,9 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
449 | goto repeat; | 478 | goto repeat; |
450 | } | 479 | } |
451 | 480 | ||
481 | if (dev->power.no_callbacks) | ||
482 | goto no_callback; /* Assume success. */ | ||
483 | |||
452 | __update_runtime_status(dev, RPM_RESUMING); | 484 | __update_runtime_status(dev, RPM_RESUMING); |
453 | 485 | ||
454 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { | 486 | if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { |
@@ -482,6 +514,7 @@ static int rpm_resume(struct device *dev, int rpmflags) | |||
482 | __update_runtime_status(dev, RPM_SUSPENDED); | 514 | __update_runtime_status(dev, RPM_SUSPENDED); |
483 | pm_runtime_cancel_pending(dev); | 515 | pm_runtime_cancel_pending(dev); |
484 | } else { | 516 | } else { |
517 | no_callback: | ||
485 | __update_runtime_status(dev, RPM_ACTIVE); | 518 | __update_runtime_status(dev, RPM_ACTIVE); |
486 | if (parent) | 519 | if (parent) |
487 | atomic_inc(&parent->power.child_count); | 520 | atomic_inc(&parent->power.child_count); |
@@ -955,6 +988,25 @@ void pm_runtime_allow(struct device *dev) | |||
955 | EXPORT_SYMBOL_GPL(pm_runtime_allow); | 988 | EXPORT_SYMBOL_GPL(pm_runtime_allow); |
956 | 989 | ||
957 | /** | 990 | /** |
991 | * pm_runtime_no_callbacks - Ignore run-time PM callbacks for a device. | ||
992 | * @dev: Device to handle. | ||
993 | * | ||
994 | * Set the power.no_callbacks flag, which tells the PM core that this | ||
995 | * device is power-managed through its parent and has no run-time PM | ||
996 | * callbacks of its own. The run-time sysfs attributes will be removed. | ||
997 | * | ||
998 | */ | ||
999 | void pm_runtime_no_callbacks(struct device *dev) | ||
1000 | { | ||
1001 | spin_lock_irq(&dev->power.lock); | ||
1002 | dev->power.no_callbacks = 1; | ||
1003 | spin_unlock_irq(&dev->power.lock); | ||
1004 | if (device_is_registered(dev)) | ||
1005 | rpm_sysfs_remove(dev); | ||
1006 | } | ||
1007 | EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); | ||
1008 | |||
1009 | /** | ||
958 | * pm_runtime_init - Initialize run-time PM fields in given device object. | 1010 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
959 | * @dev: Device object to initialize. | 1011 | * @dev: Device object to initialize. |
960 | */ | 1012 | */ |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 8859780817e1..b5708c47ce2d 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
@@ -81,6 +81,9 @@ | |||
81 | static const char enabled[] = "enabled"; | 81 | static const char enabled[] = "enabled"; |
82 | static const char disabled[] = "disabled"; | 82 | static const char disabled[] = "disabled"; |
83 | 83 | ||
84 | const char power_group_name[] = "power"; | ||
85 | EXPORT_SYMBOL_GPL(power_group_name); | ||
86 | |||
84 | #ifdef CONFIG_PM_RUNTIME | 87 | #ifdef CONFIG_PM_RUNTIME |
85 | static const char ctrl_auto[] = "auto"; | 88 | static const char ctrl_auto[] = "auto"; |
86 | static const char ctrl_on[] = "on"; | 89 | static const char ctrl_on[] = "on"; |
@@ -390,12 +393,6 @@ static DEVICE_ATTR(async, 0644, async_show, async_store); | |||
390 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ | 393 | #endif /* CONFIG_PM_ADVANCED_DEBUG */ |
391 | 394 | ||
392 | static struct attribute * power_attrs[] = { | 395 | static struct attribute * power_attrs[] = { |
393 | #ifdef CONFIG_PM_RUNTIME | ||
394 | &dev_attr_control.attr, | ||
395 | &dev_attr_runtime_status.attr, | ||
396 | &dev_attr_runtime_suspended_time.attr, | ||
397 | &dev_attr_runtime_active_time.attr, | ||
398 | #endif | ||
399 | &dev_attr_wakeup.attr, | 396 | &dev_attr_wakeup.attr, |
400 | #ifdef CONFIG_PM_SLEEP | 397 | #ifdef CONFIG_PM_SLEEP |
401 | &dev_attr_wakeup_count.attr, | 398 | &dev_attr_wakeup_count.attr, |
@@ -409,6 +406,7 @@ static struct attribute * power_attrs[] = { | |||
409 | #ifdef CONFIG_PM_ADVANCED_DEBUG | 406 | #ifdef CONFIG_PM_ADVANCED_DEBUG |
410 | &dev_attr_async.attr, | 407 | &dev_attr_async.attr, |
411 | #ifdef CONFIG_PM_RUNTIME | 408 | #ifdef CONFIG_PM_RUNTIME |
409 | &dev_attr_runtime_status.attr, | ||
412 | &dev_attr_runtime_usage.attr, | 410 | &dev_attr_runtime_usage.attr, |
413 | &dev_attr_runtime_active_kids.attr, | 411 | &dev_attr_runtime_active_kids.attr, |
414 | &dev_attr_runtime_enabled.attr, | 412 | &dev_attr_runtime_enabled.attr, |
@@ -417,10 +415,52 @@ static struct attribute * power_attrs[] = { | |||
417 | NULL, | 415 | NULL, |
418 | }; | 416 | }; |
419 | static struct attribute_group pm_attr_group = { | 417 | static struct attribute_group pm_attr_group = { |
420 | .name = "power", | 418 | .name = power_group_name, |
421 | .attrs = power_attrs, | 419 | .attrs = power_attrs, |
422 | }; | 420 | }; |
423 | 421 | ||
422 | #ifdef CONFIG_PM_RUNTIME | ||
423 | |||
424 | static struct attribute *runtime_attrs[] = { | ||
425 | #ifndef CONFIG_PM_ADVANCED_DEBUG | ||
426 | &dev_attr_runtime_status.attr, | ||
427 | #endif | ||
428 | &dev_attr_control.attr, | ||
429 | &dev_attr_runtime_suspended_time.attr, | ||
430 | &dev_attr_runtime_active_time.attr, | ||
431 | NULL, | ||
432 | }; | ||
433 | static struct attribute_group pm_runtime_attr_group = { | ||
434 | .name = power_group_name, | ||
435 | .attrs = runtime_attrs, | ||
436 | }; | ||
437 | |||
438 | int dpm_sysfs_add(struct device *dev) | ||
439 | { | ||
440 | int rc; | ||
441 | |||
442 | rc = sysfs_create_group(&dev->kobj, &pm_attr_group); | ||
443 | if (rc == 0 && !dev->power.no_callbacks) { | ||
444 | rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group); | ||
445 | if (rc) | ||
446 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | ||
447 | } | ||
448 | return rc; | ||
449 | } | ||
450 | |||
451 | void rpm_sysfs_remove(struct device *dev) | ||
452 | { | ||
453 | sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); | ||
454 | } | ||
455 | |||
456 | void dpm_sysfs_remove(struct device *dev) | ||
457 | { | ||
458 | rpm_sysfs_remove(dev); | ||
459 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | ||
460 | } | ||
461 | |||
462 | #else /* CONFIG_PM_RUNTIME */ | ||
463 | |||
424 | int dpm_sysfs_add(struct device * dev) | 464 | int dpm_sysfs_add(struct device * dev) |
425 | { | 465 | { |
426 | return sysfs_create_group(&dev->kobj, &pm_attr_group); | 466 | return sysfs_create_group(&dev->kobj, &pm_attr_group); |
@@ -430,3 +470,5 @@ void dpm_sysfs_remove(struct device * dev) | |||
430 | { | 470 | { |
431 | sysfs_remove_group(&dev->kobj, &pm_attr_group); | 471 | sysfs_remove_group(&dev->kobj, &pm_attr_group); |
432 | } | 472 | } |
473 | |||
474 | #endif | ||
diff --git a/include/linux/pm.h b/include/linux/pm.h index 1abfe84f447d..abd81ffaba3c 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
@@ -41,6 +41,12 @@ extern void (*pm_power_off_prepare)(void); | |||
41 | 41 | ||
42 | struct device; | 42 | struct device; |
43 | 43 | ||
44 | #ifdef CONFIG_PM | ||
45 | extern const char power_group_name[]; /* = "power" */ | ||
46 | #else | ||
47 | #define power_group_name NULL | ||
48 | #endif | ||
49 | |||
44 | typedef struct pm_message { | 50 | typedef struct pm_message { |
45 | int event; | 51 | int event; |
46 | } pm_message_t; | 52 | } pm_message_t; |
@@ -475,6 +481,7 @@ struct dev_pm_info { | |||
475 | unsigned int deferred_resume:1; | 481 | unsigned int deferred_resume:1; |
476 | unsigned int run_wake:1; | 482 | unsigned int run_wake:1; |
477 | unsigned int runtime_auto:1; | 483 | unsigned int runtime_auto:1; |
484 | unsigned int no_callbacks:1; | ||
478 | enum rpm_request request; | 485 | enum rpm_request request; |
479 | enum rpm_status runtime_status; | 486 | enum rpm_status runtime_status; |
480 | int runtime_error; | 487 | int runtime_error; |
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 5869d87fffac..8ca52f7c357e 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h | |||
@@ -36,6 +36,7 @@ extern void pm_runtime_forbid(struct device *dev); | |||
36 | extern int pm_generic_runtime_idle(struct device *dev); | 36 | extern int pm_generic_runtime_idle(struct device *dev); |
37 | extern int pm_generic_runtime_suspend(struct device *dev); | 37 | extern int pm_generic_runtime_suspend(struct device *dev); |
38 | extern int pm_generic_runtime_resume(struct device *dev); | 38 | extern int pm_generic_runtime_resume(struct device *dev); |
39 | extern void pm_runtime_no_callbacks(struct device *dev); | ||
39 | 40 | ||
40 | static inline bool pm_children_suspended(struct device *dev) | 41 | static inline bool pm_children_suspended(struct device *dev) |
41 | { | 42 | { |
@@ -110,6 +111,7 @@ static inline bool pm_runtime_suspended(struct device *dev) { return false; } | |||
110 | static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } | 111 | static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } |
111 | static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } | 112 | static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } |
112 | static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } | 113 | static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } |
114 | static inline void pm_runtime_no_callbacks(struct device *dev) {} | ||
113 | 115 | ||
114 | #endif /* !CONFIG_PM_RUNTIME */ | 116 | #endif /* !CONFIG_PM_RUNTIME */ |
115 | 117 | ||