diff options
| -rw-r--r-- | drivers/base/power/runtime.c | 45 | ||||
| -rw-r--r-- | drivers/base/power/sysfs.c | 51 | ||||
| -rw-r--r-- | include/linux/pm.h | 1 | ||||
| -rw-r--r-- | include/linux/pm_runtime.h | 4 |
4 files changed, 101 insertions, 0 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index f8b044e8aef7..626dd147b75f 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c | |||
| @@ -1011,6 +1011,50 @@ void pm_runtime_enable(struct device *dev) | |||
| 1011 | EXPORT_SYMBOL_GPL(pm_runtime_enable); | 1011 | EXPORT_SYMBOL_GPL(pm_runtime_enable); |
| 1012 | 1012 | ||
| 1013 | /** | 1013 | /** |
| 1014 | * pm_runtime_forbid - Block run-time PM of a device. | ||
| 1015 | * @dev: Device to handle. | ||
| 1016 | * | ||
| 1017 | * Increase the device's usage count and clear its power.runtime_auto flag, | ||
| 1018 | * so that it cannot be suspended at run time until pm_runtime_allow() is called | ||
| 1019 | * for it. | ||
| 1020 | */ | ||
| 1021 | void pm_runtime_forbid(struct device *dev) | ||
| 1022 | { | ||
| 1023 | spin_lock_irq(&dev->power.lock); | ||
| 1024 | if (!dev->power.runtime_auto) | ||
| 1025 | goto out; | ||
| 1026 | |||
| 1027 | dev->power.runtime_auto = false; | ||
| 1028 | atomic_inc(&dev->power.usage_count); | ||
| 1029 | __pm_runtime_resume(dev, false); | ||
| 1030 | |||
| 1031 | out: | ||
| 1032 | spin_unlock_irq(&dev->power.lock); | ||
| 1033 | } | ||
| 1034 | EXPORT_SYMBOL_GPL(pm_runtime_forbid); | ||
| 1035 | |||
| 1036 | /** | ||
| 1037 | * pm_runtime_allow - Unblock run-time PM of a device. | ||
| 1038 | * @dev: Device to handle. | ||
| 1039 | * | ||
| 1040 | * Decrease the device's usage count and set its power.runtime_auto flag. | ||
| 1041 | */ | ||
| 1042 | void pm_runtime_allow(struct device *dev) | ||
| 1043 | { | ||
| 1044 | spin_lock_irq(&dev->power.lock); | ||
| 1045 | if (dev->power.runtime_auto) | ||
| 1046 | goto out; | ||
| 1047 | |||
| 1048 | dev->power.runtime_auto = true; | ||
| 1049 | if (atomic_dec_and_test(&dev->power.usage_count)) | ||
| 1050 | __pm_runtime_idle(dev); | ||
| 1051 | |||
| 1052 | out: | ||
| 1053 | spin_unlock_irq(&dev->power.lock); | ||
| 1054 | } | ||
| 1055 | EXPORT_SYMBOL_GPL(pm_runtime_allow); | ||
| 1056 | |||
| 1057 | /** | ||
| 1014 | * pm_runtime_init - Initialize run-time PM fields in given device object. | 1058 | * pm_runtime_init - Initialize run-time PM fields in given device object. |
| 1015 | * @dev: Device object to initialize. | 1059 | * @dev: Device object to initialize. |
| 1016 | */ | 1060 | */ |
| @@ -1028,6 +1072,7 @@ void pm_runtime_init(struct device *dev) | |||
| 1028 | 1072 | ||
| 1029 | atomic_set(&dev->power.child_count, 0); | 1073 | atomic_set(&dev->power.child_count, 0); |
| 1030 | pm_suspend_ignore_children(dev, false); | 1074 | pm_suspend_ignore_children(dev, false); |
| 1075 | dev->power.runtime_auto = true; | ||
| 1031 | 1076 | ||
| 1032 | dev->power.request_pending = false; | 1077 | dev->power.request_pending = false; |
| 1033 | dev->power.request = RPM_REQ_NONE; | 1078 | dev->power.request = RPM_REQ_NONE; |
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 596aeecfdffe..c011ff15632c 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c | |||
| @@ -4,9 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #include <linux/device.h> | 5 | #include <linux/device.h> |
| 6 | #include <linux/string.h> | 6 | #include <linux/string.h> |
| 7 | #include <linux/pm_runtime.h> | ||
| 7 | #include "power.h" | 8 | #include "power.h" |
| 8 | 9 | ||
| 9 | /* | 10 | /* |
| 11 | * control - Report/change current runtime PM setting of the device | ||
| 12 | * | ||
| 13 | * Runtime power management of a device can be blocked with the help of | ||
| 14 | * this attribute. All devices have one of the following two values for | ||
| 15 | * the power/control file: | ||
| 16 | * | ||
| 17 | * + "auto\n" to allow the device to be power managed at run time; | ||
| 18 | * + "on\n" to prevent the device from being power managed at run time; | ||
| 19 | * | ||
| 20 | * The default for all devices is "auto", which means that devices may be | ||
| 21 | * subject to automatic power management, depending on their drivers. | ||
| 22 | * Changing this attribute to "on" prevents the driver from power managing | ||
| 23 | * the device at run time. Doing that while the device is suspended causes | ||
| 24 | * it to be woken up. | ||
| 25 | * | ||
| 10 | * wakeup - Report/change current wakeup option for device | 26 | * wakeup - Report/change current wakeup option for device |
| 11 | * | 27 | * |
| 12 | * Some devices support "wakeup" events, which are hardware signals | 28 | * Some devices support "wakeup" events, which are hardware signals |
| @@ -43,6 +59,38 @@ | |||
| 43 | static const char enabled[] = "enabled"; | 59 | static const char enabled[] = "enabled"; |
| 44 | static const char disabled[] = "disabled"; | 60 | static const char disabled[] = "disabled"; |
| 45 | 61 | ||
| 62 | #ifdef CONFIG_PM_RUNTIME | ||
| 63 | static const char ctrl_auto[] = "auto"; | ||
| 64 | static const char ctrl_on[] = "on"; | ||
| 65 | |||
| 66 | static ssize_t control_show(struct device *dev, struct device_attribute *attr, | ||
| 67 | char *buf) | ||
| 68 | { | ||
| 69 | return sprintf(buf, "%s\n", | ||
| 70 | dev->power.runtime_auto ? ctrl_auto : ctrl_on); | ||
| 71 | } | ||
| 72 | |||
| 73 | static ssize_t control_store(struct device * dev, struct device_attribute *attr, | ||
| 74 | const char * buf, size_t n) | ||
| 75 | { | ||
| 76 | char *cp; | ||
| 77 | int len = n; | ||
| 78 | |||
| 79 | cp = memchr(buf, '\n', n); | ||
| 80 | if (cp) | ||
| 81 | len = cp - buf; | ||
| 82 | if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0) | ||
| 83 | pm_runtime_allow(dev); | ||
| 84 | else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0) | ||
| 85 | pm_runtime_forbid(dev); | ||
| 86 | else | ||
| 87 | return -EINVAL; | ||
| 88 | return n; | ||
| 89 | } | ||
| 90 | |||
| 91 | static DEVICE_ATTR(control, 0644, control_show, control_store); | ||
| 92 | #endif | ||
| 93 | |||
| 46 | static ssize_t | 94 | static ssize_t |
| 47 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) | 95 | wake_show(struct device * dev, struct device_attribute *attr, char * buf) |
| 48 | { | 96 | { |
| @@ -79,6 +127,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); | |||
| 79 | 127 | ||
| 80 | 128 | ||
| 81 | static struct attribute * power_attrs[] = { | 129 | static struct attribute * power_attrs[] = { |
| 130 | #ifdef CONFIG_PM_RUNTIME | ||
| 131 | &dev_attr_control.attr, | ||
| 132 | #endif | ||
| 82 | &dev_attr_wakeup.attr, | 133 | &dev_attr_wakeup.attr, |
| 83 | NULL, | 134 | NULL, |
| 84 | }; | 135 | }; |
diff --git a/include/linux/pm.h b/include/linux/pm.h index 198b8f9fe05e..25b1eca8049d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
| @@ -430,6 +430,7 @@ struct dev_pm_info { | |||
| 430 | unsigned int request_pending:1; | 430 | unsigned int request_pending:1; |
| 431 | unsigned int deferred_resume:1; | 431 | unsigned int deferred_resume:1; |
| 432 | unsigned int run_wake:1; | 432 | unsigned int run_wake:1; |
| 433 | unsigned int runtime_auto:1; | ||
| 433 | enum rpm_request request; | 434 | enum rpm_request request; |
| 434 | enum rpm_status runtime_status; | 435 | enum rpm_status runtime_status; |
| 435 | int runtime_error; | 436 | int runtime_error; |
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 370ce0a6fe4a..7d773aac5314 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h | |||
| @@ -28,6 +28,8 @@ extern int __pm_runtime_set_status(struct device *dev, unsigned int status); | |||
| 28 | extern int pm_runtime_barrier(struct device *dev); | 28 | extern int pm_runtime_barrier(struct device *dev); |
| 29 | extern void pm_runtime_enable(struct device *dev); | 29 | extern void pm_runtime_enable(struct device *dev); |
| 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); | 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); |
| 31 | extern void pm_runtime_allow(struct device *dev); | ||
| 32 | extern void pm_runtime_forbid(struct device *dev); | ||
| 31 | 33 | ||
| 32 | static inline bool pm_children_suspended(struct device *dev) | 34 | static inline bool pm_children_suspended(struct device *dev) |
| 33 | { | 35 | { |
| @@ -78,6 +80,8 @@ static inline int __pm_runtime_set_status(struct device *dev, | |||
| 78 | static inline int pm_runtime_barrier(struct device *dev) { return 0; } | 80 | static inline int pm_runtime_barrier(struct device *dev) { return 0; } |
| 79 | static inline void pm_runtime_enable(struct device *dev) {} | 81 | static inline void pm_runtime_enable(struct device *dev) {} |
| 80 | static inline void __pm_runtime_disable(struct device *dev, bool c) {} | 82 | static inline void __pm_runtime_disable(struct device *dev, bool c) {} |
| 83 | static inline void pm_runtime_allow(struct device *dev) {} | ||
| 84 | static inline void pm_runtime_forbid(struct device *dev) {} | ||
| 81 | 85 | ||
| 82 | static inline bool pm_children_suspended(struct device *dev) { return false; } | 86 | static inline bool pm_children_suspended(struct device *dev) { return false; } |
| 83 | static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} | 87 | static inline void pm_suspend_ignore_children(struct device *dev, bool en) {} |
