diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-02-09 00:00:33 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-02-09 19:11:53 -0500 |
commit | 9f8ea969d5cfdd4353d2adb004e8e2286b984369 (patch) | |
tree | 89a110d6f65b74a62667b490e241bc3968dad687 | |
parent | 388f7b1d6e8ca06762e2454d28d6c3c55ad0fe95 (diff) |
PM / OPP: get/put regulators from OPP core
This allows the OPP core to request/free the regulator resource,
attached to a device OPP. The regulator device is fetched using the name
provided by the driver, while calling: dev_pm_opp_set_regulator().
This will work for both OPP-v1 and v2 bindings.
This is a preliminary step for moving the OPP switching logic into the
OPP core.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/opp/core.c | 111 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 4 | ||||
-rw-r--r-- | include/linux/pm_opp.h | 9 |
3 files changed, 124 insertions, 0 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index cf351d3dab1c..1e22b71abf1e 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/export.h> | 21 | #include <linux/export.h> |
22 | #include <linux/regulator/consumer.h> | ||
22 | 23 | ||
23 | #include "opp.h" | 24 | #include "opp.h" |
24 | 25 | ||
@@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp) | |||
565 | if (dev_opp->prop_name) | 566 | if (dev_opp->prop_name) |
566 | return; | 567 | return; |
567 | 568 | ||
569 | if (!IS_ERR_OR_NULL(dev_opp->regulator)) | ||
570 | return; | ||
571 | |||
568 | list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, | 572 | list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, |
569 | node); | 573 | node); |
570 | 574 | ||
@@ -1085,6 +1089,113 @@ unlock: | |||
1085 | } | 1089 | } |
1086 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); | 1090 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); |
1087 | 1091 | ||
1092 | /** | ||
1093 | * dev_pm_opp_set_regulator() - Set regulator name for the device | ||
1094 | * @dev: Device for which regulator name is being set. | ||
1095 | * @name: Name of the regulator. | ||
1096 | * | ||
1097 | * In order to support OPP switching, OPP layer needs to know the name of the | ||
1098 | * device's regulator, as the core would be required to switch voltages as well. | ||
1099 | * | ||
1100 | * This must be called before any OPPs are initialized for the device. | ||
1101 | * | ||
1102 | * Locking: The internal device_opp and opp structures are RCU protected. | ||
1103 | * Hence this function internally uses RCU updater strategy with mutex locks | ||
1104 | * to keep the integrity of the internal data structures. Callers should ensure | ||
1105 | * that this function is *NOT* called under RCU protection or in contexts where | ||
1106 | * mutex cannot be locked. | ||
1107 | */ | ||
1108 | int dev_pm_opp_set_regulator(struct device *dev, const char *name) | ||
1109 | { | ||
1110 | struct device_opp *dev_opp; | ||
1111 | struct regulator *reg; | ||
1112 | int ret; | ||
1113 | |||
1114 | mutex_lock(&dev_opp_list_lock); | ||
1115 | |||
1116 | dev_opp = _add_device_opp(dev); | ||
1117 | if (!dev_opp) { | ||
1118 | ret = -ENOMEM; | ||
1119 | goto unlock; | ||
1120 | } | ||
1121 | |||
1122 | /* This should be called before OPPs are initialized */ | ||
1123 | if (WARN_ON(!list_empty(&dev_opp->opp_list))) { | ||
1124 | ret = -EBUSY; | ||
1125 | goto err; | ||
1126 | } | ||
1127 | |||
1128 | /* Already have a regulator set */ | ||
1129 | if (WARN_ON(!IS_ERR_OR_NULL(dev_opp->regulator))) { | ||
1130 | ret = -EBUSY; | ||
1131 | goto err; | ||
1132 | } | ||
1133 | /* Allocate the regulator */ | ||
1134 | reg = regulator_get_optional(dev, name); | ||
1135 | if (IS_ERR(reg)) { | ||
1136 | ret = PTR_ERR(reg); | ||
1137 | if (ret != -EPROBE_DEFER) | ||
1138 | dev_err(dev, "%s: no regulator (%s) found: %d\n", | ||
1139 | __func__, name, ret); | ||
1140 | goto err; | ||
1141 | } | ||
1142 | |||
1143 | dev_opp->regulator = reg; | ||
1144 | |||
1145 | mutex_unlock(&dev_opp_list_lock); | ||
1146 | return 0; | ||
1147 | |||
1148 | err: | ||
1149 | _remove_device_opp(dev_opp); | ||
1150 | unlock: | ||
1151 | mutex_unlock(&dev_opp_list_lock); | ||
1152 | |||
1153 | return ret; | ||
1154 | } | ||
1155 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator); | ||
1156 | |||
1157 | /** | ||
1158 | * dev_pm_opp_put_regulator() - Releases resources blocked for regulator | ||
1159 | * @dev: Device for which regulator was set. | ||
1160 | * | ||
1161 | * Locking: The internal device_opp and opp structures are RCU protected. | ||
1162 | * Hence this function internally uses RCU updater strategy with mutex locks | ||
1163 | * to keep the integrity of the internal data structures. Callers should ensure | ||
1164 | * that this function is *NOT* called under RCU protection or in contexts where | ||
1165 | * mutex cannot be locked. | ||
1166 | */ | ||
1167 | void dev_pm_opp_put_regulator(struct device *dev) | ||
1168 | { | ||
1169 | struct device_opp *dev_opp; | ||
1170 | |||
1171 | mutex_lock(&dev_opp_list_lock); | ||
1172 | |||
1173 | /* Check for existing list for 'dev' first */ | ||
1174 | dev_opp = _find_device_opp(dev); | ||
1175 | if (IS_ERR(dev_opp)) { | ||
1176 | dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp)); | ||
1177 | goto unlock; | ||
1178 | } | ||
1179 | |||
1180 | if (IS_ERR_OR_NULL(dev_opp->regulator)) { | ||
1181 | dev_err(dev, "%s: Doesn't have regulator set\n", __func__); | ||
1182 | goto unlock; | ||
1183 | } | ||
1184 | |||
1185 | /* Make sure there are no concurrent readers while updating dev_opp */ | ||
1186 | WARN_ON(!list_empty(&dev_opp->opp_list)); | ||
1187 | |||
1188 | regulator_put(dev_opp->regulator); | ||
1189 | dev_opp->regulator = ERR_PTR(-EINVAL); | ||
1190 | |||
1191 | /* Try freeing device_opp if this was the last blocking resource */ | ||
1192 | _remove_device_opp(dev_opp); | ||
1193 | |||
1194 | unlock: | ||
1195 | mutex_unlock(&dev_opp_list_lock); | ||
1196 | } | ||
1197 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator); | ||
1198 | |||
1088 | static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, | 1199 | static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, |
1089 | struct device_node *np) | 1200 | struct device_node *np) |
1090 | { | 1201 | { |
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index 690638ef36ee..416293b7da23 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <linux/rculist.h> | 22 | #include <linux/rculist.h> |
23 | #include <linux/rcupdate.h> | 23 | #include <linux/rcupdate.h> |
24 | 24 | ||
25 | struct regulator; | ||
26 | |||
25 | /* Lock to allow exclusive modification to the device and opp lists */ | 27 | /* Lock to allow exclusive modification to the device and opp lists */ |
26 | extern struct mutex dev_opp_list_lock; | 28 | extern struct mutex dev_opp_list_lock; |
27 | 29 | ||
@@ -132,6 +134,7 @@ struct device_list_opp { | |||
132 | * @supported_hw: Array of version number to support. | 134 | * @supported_hw: Array of version number to support. |
133 | * @supported_hw_count: Number of elements in supported_hw array. | 135 | * @supported_hw_count: Number of elements in supported_hw array. |
134 | * @prop_name: A name to postfix to many DT properties, while parsing them. | 136 | * @prop_name: A name to postfix to many DT properties, while parsing them. |
137 | * @regulator: Supply regulator | ||
135 | * @dentry: debugfs dentry pointer of the real device directory (not links). | 138 | * @dentry: debugfs dentry pointer of the real device directory (not links). |
136 | * @dentry_name: Name of the real dentry. | 139 | * @dentry_name: Name of the real dentry. |
137 | * | 140 | * |
@@ -159,6 +162,7 @@ struct device_opp { | |||
159 | unsigned int *supported_hw; | 162 | unsigned int *supported_hw; |
160 | unsigned int supported_hw_count; | 163 | unsigned int supported_hw_count; |
161 | const char *prop_name; | 164 | const char *prop_name; |
165 | struct regulator *regulator; | ||
162 | 166 | ||
163 | #ifdef CONFIG_DEBUG_FS | 167 | #ifdef CONFIG_DEBUG_FS |
164 | struct dentry *dentry; | 168 | struct dentry *dentry; |
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 95403d2ccaf5..c70a18ac9c8a 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h | |||
@@ -60,6 +60,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, | |||
60 | void dev_pm_opp_put_supported_hw(struct device *dev); | 60 | void dev_pm_opp_put_supported_hw(struct device *dev); |
61 | int dev_pm_opp_set_prop_name(struct device *dev, const char *name); | 61 | int dev_pm_opp_set_prop_name(struct device *dev, const char *name); |
62 | void dev_pm_opp_put_prop_name(struct device *dev); | 62 | void dev_pm_opp_put_prop_name(struct device *dev); |
63 | int dev_pm_opp_set_regulator(struct device *dev, const char *name); | ||
64 | void dev_pm_opp_put_regulator(struct device *dev); | ||
63 | #else | 65 | #else |
64 | static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) | 66 | static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) |
65 | { | 67 | { |
@@ -151,6 +153,13 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) | |||
151 | 153 | ||
152 | static inline void dev_pm_opp_put_prop_name(struct device *dev) {} | 154 | static inline void dev_pm_opp_put_prop_name(struct device *dev) {} |
153 | 155 | ||
156 | static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name) | ||
157 | { | ||
158 | return -EINVAL; | ||
159 | } | ||
160 | |||
161 | static inline void dev_pm_opp_put_regulator(struct device *dev) {} | ||
162 | |||
154 | #endif /* CONFIG_PM_OPP */ | 163 | #endif /* CONFIG_PM_OPP */ |
155 | 164 | ||
156 | #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) | 165 | #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) |