Merge branch 'pm-opp'
* pm-opp: PM / OPP: Add opp_rcu_lockdep_assert() to _find_device_opp() PM / OPP: Hold dev_opp_list_lock for writers PM / OPP: Protect updates to list_dev with mutex PM / OPP: Propagate error properly from dev_pm_opp_set_sharing_cpus() PM / OPP: Parse all power-supply related bindings together PM / OPP: Rename routines specific to old bindings with _v1 PM / OPP: Improve print messages with pr_fmt
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
static LIST_HEAD(dev_opp_list);
|
static LIST_HEAD(dev_opp_list);
|
||||||
/* Lock to allow exclusive modification to the device and opp lists */
|
/* Lock to allow exclusive modification to the device and opp lists */
|
||||||
static DEFINE_MUTEX(dev_opp_list_lock);
|
DEFINE_MUTEX(dev_opp_list_lock);
|
||||||
|
|
||||||
#define opp_rcu_lockdep_assert() \
|
#define opp_rcu_lockdep_assert() \
|
||||||
do { \
|
do { \
|
||||||
@@ -79,14 +81,18 @@ static struct device_opp *_managed_opp(const struct device_node *np)
|
|||||||
* Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or
|
* Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or
|
||||||
* -EINVAL based on type of error.
|
* -EINVAL based on type of error.
|
||||||
*
|
*
|
||||||
* Locking: This function must be called under rcu_read_lock(). device_opp
|
* Locking: For readers, this function must be called under rcu_read_lock().
|
||||||
* is a RCU protected pointer. This means that device_opp is valid as long
|
* device_opp is a RCU protected pointer, which means that device_opp is valid
|
||||||
* as we are under RCU lock.
|
* as long as we are under RCU lock.
|
||||||
|
*
|
||||||
|
* For Writers, this function must be called with dev_opp_list_lock held.
|
||||||
*/
|
*/
|
||||||
struct device_opp *_find_device_opp(struct device *dev)
|
struct device_opp *_find_device_opp(struct device *dev)
|
||||||
{
|
{
|
||||||
struct device_opp *dev_opp;
|
struct device_opp *dev_opp;
|
||||||
|
|
||||||
|
opp_rcu_lockdep_assert();
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(dev)) {
|
if (IS_ERR_OR_NULL(dev)) {
|
||||||
pr_err("%s: Invalid parameters\n", __func__);
|
pr_err("%s: Invalid parameters\n", __func__);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
@@ -701,7 +707,7 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _opp_add_dynamic() - Allocate a dynamic OPP.
|
* _opp_add_v1() - Allocate a OPP based on v1 bindings.
|
||||||
* @dev: device for which we do this operation
|
* @dev: device for which we do this operation
|
||||||
* @freq: Frequency in Hz for this OPP
|
* @freq: Frequency in Hz for this OPP
|
||||||
* @u_volt: Voltage in uVolts for this OPP
|
* @u_volt: Voltage in uVolts for this OPP
|
||||||
@@ -727,8 +733,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
|
|||||||
* Duplicate OPPs (both freq and volt are same) and !opp->available
|
* Duplicate OPPs (both freq and volt are same) and !opp->available
|
||||||
* -ENOMEM Memory allocation failure
|
* -ENOMEM Memory allocation failure
|
||||||
*/
|
*/
|
||||||
static int _opp_add_dynamic(struct device *dev, unsigned long freq,
|
static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
|
||||||
long u_volt, bool dynamic)
|
bool dynamic)
|
||||||
{
|
{
|
||||||
struct device_opp *dev_opp;
|
struct device_opp *dev_opp;
|
||||||
struct dev_pm_opp *new_opp;
|
struct dev_pm_opp *new_opp;
|
||||||
@@ -770,9 +776,10 @@ unlock:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Support multiple regulators */
|
/* TODO: Support multiple regulators */
|
||||||
static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev)
|
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
|
||||||
{
|
{
|
||||||
u32 microvolt[3] = {0};
|
u32 microvolt[3] = {0};
|
||||||
|
u32 val;
|
||||||
int count, ret;
|
int count, ret;
|
||||||
|
|
||||||
/* Missing property isn't a problem, but an invalid entry is */
|
/* Missing property isn't a problem, but an invalid entry is */
|
||||||
@@ -805,6 +812,9 @@ static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev)
|
|||||||
opp->u_volt_min = microvolt[1];
|
opp->u_volt_min = microvolt[1];
|
||||||
opp->u_volt_max = microvolt[2];
|
opp->u_volt_max = microvolt[2];
|
||||||
|
|
||||||
|
if (!of_property_read_u32(opp->np, "opp-microamp", &val))
|
||||||
|
opp->u_amp = val;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,13 +879,10 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
|
|||||||
if (!of_property_read_u32(np, "clock-latency-ns", &val))
|
if (!of_property_read_u32(np, "clock-latency-ns", &val))
|
||||||
new_opp->clock_latency_ns = val;
|
new_opp->clock_latency_ns = val;
|
||||||
|
|
||||||
ret = opp_get_microvolt(new_opp, dev);
|
ret = opp_parse_supplies(new_opp, dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_opp;
|
goto free_opp;
|
||||||
|
|
||||||
if (!of_property_read_u32(new_opp->np, "opp-microamp", &val))
|
|
||||||
new_opp->u_amp = val;
|
|
||||||
|
|
||||||
ret = _opp_add(dev, new_opp, dev_opp);
|
ret = _opp_add(dev, new_opp, dev_opp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_opp;
|
goto free_opp;
|
||||||
@@ -939,7 +946,7 @@ unlock:
|
|||||||
*/
|
*/
|
||||||
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
||||||
{
|
{
|
||||||
return _opp_add_dynamic(dev, freq, u_volt, true);
|
return _opp_add_v1(dev, freq, u_volt, true);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
|
||||||
|
|
||||||
@@ -1172,13 +1179,17 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
|
|||||||
struct device_opp *dev_opp;
|
struct device_opp *dev_opp;
|
||||||
int ret = 0, count = 0;
|
int ret = 0, count = 0;
|
||||||
|
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
dev_opp = _managed_opp(opp_np);
|
dev_opp = _managed_opp(opp_np);
|
||||||
if (dev_opp) {
|
if (dev_opp) {
|
||||||
/* OPPs are already managed */
|
/* OPPs are already managed */
|
||||||
if (!_add_list_dev(dev, dev_opp))
|
if (!_add_list_dev(dev, dev_opp))
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
/* We have opp-list node now, iterate over it and add OPPs */
|
/* We have opp-list node now, iterate over it and add OPPs */
|
||||||
for_each_available_child_of_node(opp_np, np) {
|
for_each_available_child_of_node(opp_np, np) {
|
||||||
@@ -1196,15 +1207,20 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
|
|||||||
if (WARN_ON(!count))
|
if (WARN_ON(!count))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
dev_opp = _find_device_opp(dev);
|
dev_opp = _find_device_opp(dev);
|
||||||
if (WARN_ON(IS_ERR(dev_opp))) {
|
if (WARN_ON(IS_ERR(dev_opp))) {
|
||||||
ret = PTR_ERR(dev_opp);
|
ret = PTR_ERR(dev_opp);
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
goto free_table;
|
goto free_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_opp->np = opp_np;
|
dev_opp->np = opp_np;
|
||||||
dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");
|
dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");
|
||||||
|
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_table:
|
free_table:
|
||||||
@@ -1241,7 +1257,7 @@ static int _of_add_opp_table_v1(struct device *dev)
|
|||||||
unsigned long freq = be32_to_cpup(val++) * 1000;
|
unsigned long freq = be32_to_cpup(val++) * 1000;
|
||||||
unsigned long volt = be32_to_cpup(val++);
|
unsigned long volt = be32_to_cpup(val++);
|
||||||
|
|
||||||
if (_opp_add_dynamic(dev, freq, volt, false))
|
if (_opp_add_v1(dev, freq, volt, false))
|
||||||
dev_warn(dev, "%s: Failed to add OPP %ld\n",
|
dev_warn(dev, "%s: Failed to add OPP %ld\n",
|
||||||
__func__, freq);
|
__func__, freq);
|
||||||
nr -= 2;
|
nr -= 2;
|
||||||
|
@@ -10,6 +10,9 @@
|
|||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
@@ -124,12 +127,12 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
|
|||||||
struct device *dev;
|
struct device *dev;
|
||||||
int cpu, ret = 0;
|
int cpu, ret = 0;
|
||||||
|
|
||||||
rcu_read_lock();
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
dev_opp = _find_device_opp(cpu_dev);
|
dev_opp = _find_device_opp(cpu_dev);
|
||||||
if (IS_ERR(dev_opp)) {
|
if (IS_ERR(dev_opp)) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out_rcu_read_unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each_cpu(cpu, cpumask) {
|
for_each_cpu(cpu, cpumask) {
|
||||||
@@ -150,10 +153,10 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_rcu_read_unlock:
|
unlock:
|
||||||
rcu_read_unlock();
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);
|
||||||
|
|
||||||
|
@@ -21,6 +21,9 @@
|
|||||||
#include <linux/rculist.h>
|
#include <linux/rculist.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
|
|
||||||
|
/* Lock to allow exclusive modification to the device and opp lists */
|
||||||
|
extern struct mutex dev_opp_list_lock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal data structure organization with the OPP layer library is as
|
* Internal data structure organization with the OPP layer library is as
|
||||||
* follows:
|
* follows:
|
||||||
|
Reference in New Issue
Block a user