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:
parent
388f7b1d6e
commit
9f8ea969d5
@ -19,6 +19,7 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include "opp.h"
|
#include "opp.h"
|
||||||
|
|
||||||
@ -565,6 +566,9 @@ static void _remove_device_opp(struct device_opp *dev_opp)
|
|||||||
if (dev_opp->prop_name)
|
if (dev_opp->prop_name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(dev_opp->regulator))
|
||||||
|
return;
|
||||||
|
|
||||||
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
|
||||||
node);
|
node);
|
||||||
|
|
||||||
@ -1085,6 +1089,113 @@ unlock:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
|
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,
|
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
|
||||||
struct device_node *np)
|
struct device_node *np)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <linux/rculist.h>
|
#include <linux/rculist.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
|
||||||
|
struct regulator;
|
||||||
|
|
||||||
/* Lock to allow exclusive modification to the device and opp lists */
|
/* Lock to allow exclusive modification to the device and opp lists */
|
||||||
extern struct mutex dev_opp_list_lock;
|
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: Array of version number to support.
|
||||||
* @supported_hw_count: Number of elements in supported_hw array.
|
* @supported_hw_count: Number of elements in supported_hw array.
|
||||||
* @prop_name: A name to postfix to many DT properties, while parsing them.
|
* @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: debugfs dentry pointer of the real device directory (not links).
|
||||||
* @dentry_name: Name of the real dentry.
|
* @dentry_name: Name of the real dentry.
|
||||||
*
|
*
|
||||||
@ -159,6 +162,7 @@ struct device_opp {
|
|||||||
unsigned int *supported_hw;
|
unsigned int *supported_hw;
|
||||||
unsigned int supported_hw_count;
|
unsigned int supported_hw_count;
|
||||||
const char *prop_name;
|
const char *prop_name;
|
||||||
|
struct regulator *regulator;
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
@ -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);
|
void dev_pm_opp_put_supported_hw(struct device *dev);
|
||||||
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
|
int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
|
||||||
void dev_pm_opp_put_prop_name(struct device *dev);
|
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
|
#else
|
||||||
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
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 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 */
|
#endif /* CONFIG_PM_OPP */
|
||||||
|
|
||||||
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user