2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-24 13:13:57 +08:00

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>
This commit is contained in:
Viresh Kumar 2016-02-09 10:30:33 +05:30 committed by Rafael J. Wysocki
parent 388f7b1d6e
commit 9f8ea969d5
3 changed files with 124 additions and 0 deletions

View File

@ -19,6 +19,7 @@
#include <linux/device.h>
#include <linux/of.h>
#include <linux/export.h>
#include <linux/regulator/consumer.h>
#include "opp.h"
@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp)
if (dev_opp->prop_name)
return;
if (!IS_ERR_OR_NULL(dev_opp->regulator))
return;
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
node);
@ -1085,6 +1089,113 @@ unlock:
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
/**
* dev_pm_opp_set_regulator() - Set regulator name for the device
* @dev: Device for which regulator name is being set.
* @name: Name of the regulator.
*
* In order to support OPP switching, OPP layer needs to know the name of the
* device's regulator, as the core would be required to switch voltages as well.
*
* This must be called before any OPPs are initialized for the device.
*
* Locking: The internal device_opp and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
int dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
struct device_opp *dev_opp;
struct regulator *reg;
int ret;
mutex_lock(&dev_opp_list_lock);
dev_opp = _add_device_opp(dev);
if (!dev_opp) {
ret = -ENOMEM;
goto unlock;
}
/* This should be called before OPPs are initialized */
if (WARN_ON(!list_empty(&dev_opp->opp_list))) {
ret = -EBUSY;
goto err;
}
/* Already have a regulator set */
if (WARN_ON(!IS_ERR_OR_NULL(dev_opp->regulator))) {
ret = -EBUSY;
goto err;
}
/* Allocate the regulator */
reg = regulator_get_optional(dev, name);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
if (ret != -EPROBE_DEFER)
dev_err(dev, "%s: no regulator (%s) found: %d\n",
__func__, name, ret);
goto err;
}
dev_opp->regulator = reg;
mutex_unlock(&dev_opp_list_lock);
return 0;
err:
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
/**
* dev_pm_opp_put_regulator() - Releases resources blocked for regulator
* @dev: Device for which regulator was set.
*
* Locking: The internal device_opp and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
void dev_pm_opp_put_regulator(struct device *dev)
{
struct device_opp *dev_opp;
mutex_lock(&dev_opp_list_lock);
/* Check for existing list for 'dev' first */
dev_opp = _find_device_opp(dev);
if (IS_ERR(dev_opp)) {
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
goto unlock;
}
if (IS_ERR_OR_NULL(dev_opp->regulator)) {
dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
goto unlock;
}
/* Make sure there are no concurrent readers while updating dev_opp */
WARN_ON(!list_empty(&dev_opp->opp_list));
regulator_put(dev_opp->regulator);
dev_opp->regulator = ERR_PTR(-EINVAL);
/* Try freeing device_opp if this was the last blocking resource */
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
struct device_node *np)
{

View File

@ -22,6 +22,8 @@
#include <linux/rculist.h>
#include <linux/rcupdate.h>
struct regulator;
/* Lock to allow exclusive modification to the device and opp lists */
extern struct mutex dev_opp_list_lock;
@ -132,6 +134,7 @@ struct device_list_opp {
* @supported_hw: Array of version number to support.
* @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them.
* @regulator: Supply regulator
* @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry.
*
@ -159,6 +162,7 @@ struct device_opp {
unsigned int *supported_hw;
unsigned int supported_hw_count;
const char *prop_name;
struct regulator *regulator;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;

View File

@ -60,6 +60,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
void dev_pm_opp_put_supported_hw(struct device *dev);
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
void dev_pm_opp_put_prop_name(struct device *dev);
int dev_pm_opp_set_regulator(struct device *dev, const char *name);
void dev_pm_opp_put_regulator(struct device *dev);
#else
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
@ -151,6 +153,13 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
return -EINVAL;
}
static inline void dev_pm_opp_put_regulator(struct device *dev) {}
#endif /* CONFIG_PM_OPP */
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)