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:
parent
388f7b1d6e
commit
9f8ea969d5
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user