Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+30 −14
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@
 * published by the Free Software Foundation.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/errno.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -27,7 +29,7 @@
 */
static LIST_HEAD(dev_opp_list);
/* 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()					\
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
 * -EINVAL based on type of error.
 *
 * Locking: This function must be called under rcu_read_lock(). device_opp
 * is a RCU protected pointer. This means that device_opp is valid as long
 * as we are under RCU lock.
 * Locking: For readers, this function must be called under rcu_read_lock().
 * device_opp is a RCU protected pointer, which means that device_opp is valid
 * 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 *dev_opp;

	opp_rcu_lockdep_assert();

	if (IS_ERR_OR_NULL(dev)) {
		pr_err("%s: Invalid parameters\n", __func__);
		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
 * @freq:	Frequency in Hz 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
 * -ENOMEM	Memory allocation failure
 */
static int _opp_add_dynamic(struct device *dev, unsigned long freq,
			    long u_volt, bool dynamic)
static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
		       bool dynamic)
{
	struct device_opp *dev_opp;
	struct dev_pm_opp *new_opp;
@@ -770,9 +776,10 @@ static int _opp_add_dynamic(struct device *dev, unsigned long freq,
}

/* 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 val;
	int count, ret;

	/* 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_max = microvolt[2];

	if (!of_property_read_u32(opp->np, "opp-microamp", &val))
		opp->u_amp = val;

	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))
		new_opp->clock_latency_ns = val;

	ret = opp_get_microvolt(new_opp, dev);
	ret = opp_parse_supplies(new_opp, dev);
	if (ret)
		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);
	if (ret)
		goto free_opp;
@@ -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)
{
	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);

@@ -1172,13 +1179,17 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
	struct device_opp *dev_opp;
	int ret = 0, count = 0;

	mutex_lock(&dev_opp_list_lock);

	dev_opp = _managed_opp(opp_np);
	if (dev_opp) {
		/* OPPs are already managed */
		if (!_add_list_dev(dev, dev_opp))
			ret = -ENOMEM;
		mutex_unlock(&dev_opp_list_lock);
		return ret;
	}
	mutex_unlock(&dev_opp_list_lock);

	/* We have opp-list node now, iterate over it and add OPPs */
	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))
		return -ENOENT;

	mutex_lock(&dev_opp_list_lock);

	dev_opp = _find_device_opp(dev);
	if (WARN_ON(IS_ERR(dev_opp))) {
		ret = PTR_ERR(dev_opp);
		mutex_unlock(&dev_opp_list_lock);
		goto free_table;
	}

	dev_opp->np = opp_np;
	dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");

	mutex_unlock(&dev_opp_list_lock);

	return 0;

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 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",
				 __func__, freq);
		nr -= 2;
+8 −5
Original line number Diff line number Diff line
@@ -10,6 +10,9 @@
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/cpu.h>
#include <linux/cpufreq.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;
	int cpu, ret = 0;

	rcu_read_lock();
	mutex_lock(&dev_opp_list_lock);

	dev_opp = _find_device_opp(cpu_dev);
	if (IS_ERR(dev_opp)) {
		ret = -EINVAL;
		goto out_rcu_read_unlock;
		goto unlock;
	}

	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;
		}
	}
out_rcu_read_unlock:
	rcu_read_unlock();
unlock:
	mutex_unlock(&dev_opp_list_lock);

	return 0;
	return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);

+3 −0
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@
#include <linux/rculist.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
 * follows: