aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2016-02-09 00:00:33 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-02-09 19:11:53 -0500
commit9f8ea969d5cfdd4353d2adb004e8e2286b984369 (patch)
tree89a110d6f65b74a62667b490e241bc3968dad687
parent388f7b1d6e8ca06762e2454d28d6c3c55ad0fe95 (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.c111
-rw-r--r--drivers/base/power/opp/opp.h4
-rw-r--r--include/linux/pm_opp.h9
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}
1086EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); 1090EXPORT_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 */
1108int 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
1148err:
1149 _remove_device_opp(dev_opp);
1150unlock:
1151 mutex_unlock(&dev_opp_list_lock);
1152
1153 return ret;
1154}
1155EXPORT_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 */
1167void 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
1194unlock:
1195 mutex_unlock(&dev_opp_list_lock);
1196}
1197EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
1198
1088static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, 1199static 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
25struct 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 */
26extern struct mutex dev_opp_list_lock; 28extern 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,
60void dev_pm_opp_put_supported_hw(struct device *dev); 60void dev_pm_opp_put_supported_hw(struct device *dev);
61int dev_pm_opp_set_prop_name(struct device *dev, const char *name); 61int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
62void dev_pm_opp_put_prop_name(struct device *dev); 62void dev_pm_opp_put_prop_name(struct device *dev);
63int dev_pm_opp_set_regulator(struct device *dev, const char *name);
64void dev_pm_opp_put_regulator(struct device *dev);
63#else 65#else
64static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) 66static 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
152static inline void dev_pm_opp_put_prop_name(struct device *dev) {} 154static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
153 155
156static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
157{
158 return -EINVAL;
159}
160
161static 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)