Commit 1ede53f7 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

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
parents 3930f660 0597e818
...@@ -11,6 +11,8 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq, ...@@ -770,9 +776,10 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq,
} }
/* 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) ...@@ -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) ...@@ -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 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -939,7 +946,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
*/ */
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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment