Skip to content

Commit c070696

Browse files
vireshkAlex Shi
authored andcommitted
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> (cherry picked from commit 9f8ea969d5cfdd4353d2adb004e8e2286b984369) Signed-off-by: Alex Shi <alex.shi@linaro.org>
1 parent 5b9202b commit c070696

3 files changed

Lines changed: 124 additions & 0 deletions

File tree

drivers/base/power/opp/core.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/device.h>
2020
#include <linux/of.h>
2121
#include <linux/export.h>
22+
#include <linux/regulator/consumer.h>
2223

2324
#include "opp.h"
2425

@@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp)
565566
if (dev_opp->prop_name)
566567
return;
567568

569+
if (!IS_ERR_OR_NULL(dev_opp->regulator))
570+
return;
571+
568572
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
569573
node);
570574

@@ -1085,6 +1089,113 @@ void dev_pm_opp_put_prop_name(struct device *dev)
10851089
}
10861090
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
10871091

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+
10881199
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
10891200
struct device_node *np)
10901201
{

drivers/base/power/opp/opp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <linux/rculist.h>
2323
#include <linux/rcupdate.h>
2424

25+
struct regulator;
26+
2527
/* Lock to allow exclusive modification to the device and opp lists */
2628
extern struct mutex dev_opp_list_lock;
2729

@@ -132,6 +134,7 @@ struct device_list_opp {
132134
* @supported_hw: Array of version number to support.
133135
* @supported_hw_count: Number of elements in supported_hw array.
134136
* @prop_name: A name to postfix to many DT properties, while parsing them.
137+
* @regulator: Supply regulator
135138
* @dentry: debugfs dentry pointer of the real device directory (not links).
136139
* @dentry_name: Name of the real dentry.
137140
*
@@ -159,6 +162,7 @@ struct device_opp {
159162
unsigned int *supported_hw;
160163
unsigned int supported_hw_count;
161164
const char *prop_name;
165+
struct regulator *regulator;
162166

163167
#ifdef CONFIG_DEBUG_FS
164168
struct dentry *dentry;

include/linux/pm_opp.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
6060
void dev_pm_opp_put_supported_hw(struct device *dev);
6161
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
6262
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);
6365
#else
6466
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
6567
{
@@ -151,6 +153,13 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
151153

152154
static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
153155

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+
154163
#endif /* CONFIG_PM_OPP */
155164

156165
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)

0 commit comments

Comments
 (0)