Commit 5d113aa6 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'pm-opp' and 'powercap'

* pm-opp:
  PM / OPP: _of_add_opp_table_v2(): increment count only if OPP is added
  cpufreq: dt: Try freeing static OPPs only if we have added them
  OPP: Return error on error from dev_pm_opp_get_opp_count()
  OPP: Improve error handling in dev_pm_opp_of_cpumask_add_table()
  OPP: Pass OPP table to _of_add_opp_table_v{1|2}()
  OPP: Prevent creating multiple OPP tables for devices sharing OPP nodes
  OPP: Use a single mechanism to free the OPP table
  OPP: Don't remove dynamic OPPs from _dev_pm_opp_remove_table()
  cpufreq: mvebu: Remove OPPs using dev_pm_opp_remove()
  OPP: Create separate kref for static OPPs list
  OPP: Don't take OPP table's kref for static OPPs
  OPP: Parse OPP table's DT properties from _of_init_opp_table()
  OPP: Pass index to _of_init_opp_table()
  OPP: Protect dev_list with opp_table lock
  OPP: Don't try to remove all OPP tables on failure
  OPP: Free OPP table properly on performance state irregularities

* powercap:
  powercap: RAPL: Get rid of custom RAPL_CPU() macro
Loading
Loading
Loading
Loading
+19 −15
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ struct private_data {
	struct device *cpu_dev;
	struct thermal_cooling_device *cdev;
	const char *reg_name;
	bool have_static_opps;
};

static struct freq_attr *cpufreq_dt_attr[] = {
@@ -204,6 +205,15 @@ static int cpufreq_init(struct cpufreq_policy *policy)
		}
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		ret = -ENOMEM;
		goto out_put_regulator;
	}

	priv->reg_name = name;
	priv->opp_table = opp_table;

	/*
	 * Initialize OPP tables for all policy->cpus. They will be shared by
	 * all CPUs which have marked their CPUs shared with OPP bindings.
@@ -214,7 +224,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)
	 *
	 * OPPs might be populated at runtime, don't check for error here
	 */
	dev_pm_opp_of_cpumask_add_table(policy->cpus);
	if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
		priv->have_static_opps = true;

	/*
	 * But we need OPP table to function so if it is not there let's
@@ -240,19 +251,10 @@ static int cpufreq_init(struct cpufreq_policy *policy)
				__func__, ret);
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		ret = -ENOMEM;
		goto out_free_opp;
	}

	priv->reg_name = name;
	priv->opp_table = opp_table;

	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
	if (ret) {
		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
		goto out_free_priv;
		goto out_free_opp;
	}

	priv->cpu_dev = cpu_dev;
@@ -282,10 +284,11 @@ static int cpufreq_init(struct cpufreq_policy *policy)

out_free_cpufreq_table:
	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
	kfree(priv);
out_free_opp:
	if (priv->have_static_opps)
		dev_pm_opp_of_cpumask_remove_table(policy->cpus);
	kfree(priv);
out_put_regulator:
	if (name)
		dev_pm_opp_put_regulators(opp_table);
out_put_clk:
@@ -300,6 +303,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy)

	cpufreq_cooling_unregister(priv->cdev);
	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
	if (priv->have_static_opps)
		dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
	if (priv->reg_name)
		dev_pm_opp_put_regulators(priv->opp_table);
+2 −7
Original line number Diff line number Diff line
@@ -84,9 +84,10 @@ static int __init armada_xp_pmsu_cpufreq_init(void)

		ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0);
		if (ret) {
			dev_pm_opp_remove(cpu_dev, clk_get_rate(clk));
			clk_put(clk);
			dev_err(cpu_dev, "Failed to register OPPs\n");
			goto opp_register_failed;
			return ret;
		}

		ret = dev_pm_opp_set_sharing_cpus(cpu_dev,
@@ -99,11 +100,5 @@ static int __init armada_xp_pmsu_cpufreq_init(void)

	platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
	return 0;

opp_register_failed:
	/* As registering has failed remove all the opp for all cpus */
	dev_pm_opp_cpumask_remove_table(cpu_possible_mask);

	return ret;
}
device_initcall(armada_xp_pmsu_cpufreq_init);
+98 −51
Original line number Diff line number Diff line
@@ -48,9 +48,14 @@ static struct opp_device *_find_opp_dev(const struct device *dev,
static struct opp_table *_find_opp_table_unlocked(struct device *dev)
{
	struct opp_table *opp_table;
	bool found;

	list_for_each_entry(opp_table, &opp_tables, node) {
		if (_find_opp_dev(dev, opp_table)) {
		mutex_lock(&opp_table->lock);
		found = !!_find_opp_dev(dev, opp_table);
		mutex_unlock(&opp_table->lock);

		if (found) {
			_get_opp_table_kref(opp_table);

			return opp_table;
@@ -313,7 +318,7 @@ int dev_pm_opp_get_opp_count(struct device *dev)
		count = PTR_ERR(opp_table);
		dev_dbg(dev, "%s: OPP table not found (%d)\n",
			__func__, count);
		return 0;
		return count;
	}

	count = _get_opp_count(opp_table);
@@ -754,7 +759,7 @@ static void _remove_opp_dev(struct opp_device *opp_dev,
	kfree(opp_dev);
}

struct opp_device *_add_opp_dev(const struct device *dev,
static struct opp_device *_add_opp_dev_unlocked(const struct device *dev,
						struct opp_table *opp_table)
{
	struct opp_device *opp_dev;
@@ -766,6 +771,7 @@ struct opp_device *_add_opp_dev(const struct device *dev,

	/* Initialize opp-dev */
	opp_dev->dev = dev;

	list_add(&opp_dev->node, &opp_table->dev_list);

	/* Create debugfs entries for the opp_table */
@@ -777,7 +783,19 @@ struct opp_device *_add_opp_dev(const struct device *dev,
	return opp_dev;
}

static struct opp_table *_allocate_opp_table(struct device *dev)
struct opp_device *_add_opp_dev(const struct device *dev,
				struct opp_table *opp_table)
{
	struct opp_device *opp_dev;

	mutex_lock(&opp_table->lock);
	opp_dev = _add_opp_dev_unlocked(dev, opp_table);
	mutex_unlock(&opp_table->lock);

	return opp_dev;
}

static struct opp_table *_allocate_opp_table(struct device *dev, int index)
{
	struct opp_table *opp_table;
	struct opp_device *opp_dev;
@@ -791,6 +809,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev)
	if (!opp_table)
		return NULL;

	mutex_init(&opp_table->lock);
	INIT_LIST_HEAD(&opp_table->dev_list);

	opp_dev = _add_opp_dev(dev, opp_table);
@@ -799,7 +818,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev)
		return NULL;
	}

	_of_init_opp_table(opp_table, dev);
	_of_init_opp_table(opp_table, dev, index);

	/* Find clk for the device */
	opp_table->clk = clk_get(dev, NULL);
@@ -812,7 +831,6 @@ static struct opp_table *_allocate_opp_table(struct device *dev)

	BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
	INIT_LIST_HEAD(&opp_table->opp_list);
	mutex_init(&opp_table->lock);
	kref_init(&opp_table->kref);

	/* Secure the device table modification */
@@ -825,7 +843,7 @@ void _get_opp_table_kref(struct opp_table *opp_table)
	kref_get(&opp_table->kref);
}

struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
static struct opp_table *_opp_get_opp_table(struct device *dev, int index)
{
	struct opp_table *opp_table;

@@ -836,31 +854,56 @@ struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
	if (!IS_ERR(opp_table))
		goto unlock;

	opp_table = _allocate_opp_table(dev);
	opp_table = _managed_opp(dev, index);
	if (opp_table) {
		if (!_add_opp_dev_unlocked(dev, opp_table)) {
			dev_pm_opp_put_opp_table(opp_table);
			opp_table = NULL;
		}
		goto unlock;
	}

	opp_table = _allocate_opp_table(dev, index);

unlock:
	mutex_unlock(&opp_table_lock);

	return opp_table;
}

struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
{
	return _opp_get_opp_table(dev, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table);

struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev,
						   int index)
{
	return _opp_get_opp_table(dev, index);
}

static void _opp_table_kref_release(struct kref *kref)
{
	struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
	struct opp_device *opp_dev;
	struct opp_device *opp_dev, *temp;

	/* Release clk */
	if (!IS_ERR(opp_table->clk))
		clk_put(opp_table->clk);

	opp_dev = list_first_entry(&opp_table->dev_list, struct opp_device,
				   node);
	WARN_ON(!list_empty(&opp_table->opp_list));

	_remove_opp_dev(opp_dev, opp_table);
	list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
		/*
		 * The OPP table is getting removed, drop the performance state
		 * constraints.
		 */
		if (opp_table->genpd_performance_state)
			dev_pm_genpd_set_performance_state((struct device *)(opp_dev->dev), 0);

	/* dev_list must be empty now */
	WARN_ON(!list_empty(&opp_table->dev_list));
		_remove_opp_dev(opp_dev, opp_table);
	}

	mutex_destroy(&opp_table->lock);
	list_del(&opp_table->node);
@@ -869,6 +912,33 @@ static void _opp_table_kref_release(struct kref *kref)
	mutex_unlock(&opp_table_lock);
}

void _opp_remove_all_static(struct opp_table *opp_table)
{
	struct dev_pm_opp *opp, *tmp;

	list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
		if (!opp->dynamic)
			dev_pm_opp_put(opp);
	}

	opp_table->parsed_static_opps = false;
}

static void _opp_table_list_kref_release(struct kref *kref)
{
	struct opp_table *opp_table = container_of(kref, struct opp_table,
						   list_kref);

	_opp_remove_all_static(opp_table);
	mutex_unlock(&opp_table_lock);
}

void _put_opp_list_kref(struct opp_table *opp_table)
{
	kref_put_mutex(&opp_table->list_kref, _opp_table_list_kref_release,
		       &opp_table_lock);
}

void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
{
	kref_put_mutex(&opp_table->kref, _opp_table_kref_release,
@@ -896,7 +966,6 @@ static void _opp_kref_release(struct kref *kref)
	kfree(opp);

	mutex_unlock(&opp_table->lock);
	dev_pm_opp_put_opp_table(opp_table);
}

void dev_pm_opp_get(struct dev_pm_opp *opp)
@@ -940,11 +1009,15 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)

	if (found) {
		dev_pm_opp_put(opp);

		/* Drop the reference taken by dev_pm_opp_add() */
		dev_pm_opp_put_opp_table(opp_table);
	} else {
		dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
			 __func__, freq);
	}

	/* Drop the reference taken by _find_opp_table() */
	dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
@@ -1062,9 +1135,6 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
	new_opp->opp_table = opp_table;
	kref_init(&new_opp->kref);

	/* Get a reference to the OPP table */
	_get_opp_table_kref(opp_table);

	ret = opp_debug_create_one(new_opp, opp_table);
	if (ret)
		dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
@@ -1543,8 +1613,9 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
		return -ENOMEM;

	ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);

	if (ret)
		dev_pm_opp_put_opp_table(opp_table);

	return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
@@ -1707,35 +1778,7 @@ int dev_pm_opp_unregister_notifier(struct device *dev,
}
EXPORT_SYMBOL(dev_pm_opp_unregister_notifier);

/*
 * Free OPPs either created using static entries present in DT or even the
 * dynamically added entries based on remove_all param.
 */
void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
			      bool remove_all)
{
	struct dev_pm_opp *opp, *tmp;

	/* Find if opp_table manages a single device */
	if (list_is_singular(&opp_table->dev_list)) {
		/* Free static OPPs */
		list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
			if (remove_all || !opp->dynamic)
				dev_pm_opp_put(opp);
		}

		/*
		 * The OPP table is getting removed, drop the performance state
		 * constraints.
		 */
		if (opp_table->genpd_performance_state)
			dev_pm_genpd_set_performance_state(dev, 0);
	} else {
		_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
	}
}

void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all)
void _dev_pm_opp_find_and_remove_table(struct device *dev)
{
	struct opp_table *opp_table;

@@ -1752,8 +1795,12 @@ void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all)
		return;
	}

	_dev_pm_opp_remove_table(opp_table, dev, remove_all);
	_put_opp_list_kref(opp_table);

	/* Drop reference taken by _find_opp_table() */
	dev_pm_opp_put_opp_table(opp_table);

	/* Drop reference taken while the OPP table was added */
	dev_pm_opp_put_opp_table(opp_table);
}

@@ -1766,6 +1813,6 @@ void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all)
 */
void dev_pm_opp_remove_table(struct device *dev)
{
	_dev_pm_opp_find_and_remove_table(dev, true);
	_dev_pm_opp_find_and_remove_table(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table);
+9 −6
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev,
EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
#endif	/* CONFIG_CPU_FREQ */

void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of)
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask,
				      int last_cpu)
{
	struct device *cpu_dev;
	int cpu;
@@ -116,6 +117,9 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of)
	WARN_ON(cpumask_empty(cpumask));

	for_each_cpu(cpu, cpumask) {
		if (cpu == last_cpu)
			break;

		cpu_dev = get_cpu_device(cpu);
		if (!cpu_dev) {
			pr_err("%s: failed to get cpu%d device\n", __func__,
@@ -123,10 +127,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of)
			continue;
		}

		if (of)
			dev_pm_opp_of_remove_table(cpu_dev);
		else
			dev_pm_opp_remove_table(cpu_dev);
		_dev_pm_opp_find_and_remove_table(cpu_dev);
	}
}

@@ -140,7 +141,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of)
 */
void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask)
{
	_dev_pm_opp_cpumask_remove_table(cpumask, false);
	_dev_pm_opp_cpumask_remove_table(cpumask, -1);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_remove_table);

@@ -222,8 +223,10 @@ int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
	cpumask_clear(cpumask);

	if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
		mutex_lock(&opp_table->lock);
		list_for_each_entry(opp_dev, &opp_table->dev_list, node)
			cpumask_set_cpu(opp_dev->dev->id, cpumask);
		mutex_unlock(&opp_table->lock);
	} else {
		cpumask_set_cpu(cpu_dev->id, cpumask);
	}
+130 −107
Original line number Diff line number Diff line
@@ -23,11 +23,32 @@

#include "opp.h"

static struct opp_table *_managed_opp(const struct device_node *np)
/*
 * Returns opp descriptor node for a device node, caller must
 * do of_node_put().
 */
static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
						     int index)
{
	/* "operating-points-v2" can be an array for power domain providers */
	return of_parse_phandle(np, "operating-points-v2", index);
}

/* Returns opp descriptor node for a device, caller must do of_node_put() */
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
{
	return _opp_of_get_opp_desc_node(dev->of_node, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);

struct opp_table *_managed_opp(struct device *dev, int index)
{
	struct opp_table *opp_table, *managed_table = NULL;
	struct device_node *np;

	mutex_lock(&opp_table_lock);
	np = _opp_of_get_opp_desc_node(dev->of_node, index);
	if (!np)
		return NULL;

	list_for_each_entry(opp_table, &opp_tables, node) {
		if (opp_table->np == np) {
@@ -47,29 +68,45 @@ static struct opp_table *_managed_opp(const struct device_node *np)
		}
	}

	mutex_unlock(&opp_table_lock);
	of_node_put(np);

	return managed_table;
}

void _of_init_opp_table(struct opp_table *opp_table, struct device *dev)
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
			int index)
{
	struct device_node *np;
	struct device_node *np, *opp_np;
	u32 val;

	/*
	 * Only required for backward compatibility with v1 bindings, but isn't
	 * harmful for other cases. And so we do it unconditionally.
	 */
	np = of_node_get(dev->of_node);
	if (np) {
		u32 val;
	if (!np)
		return;

	if (!of_property_read_u32(np, "clock-latency", &val))
		opp_table->clock_latency_ns_max = val;
	of_property_read_u32(np, "voltage-tolerance",
			     &opp_table->voltage_tolerance_v1);

	/* Get OPP table node */
	opp_np = _opp_of_get_opp_desc_node(np, index);
	of_node_put(np);
	}

	if (!opp_np)
		return;

	if (of_property_read_bool(opp_np, "opp-shared"))
		opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
	else
		opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;

	opp_table->np = opp_np;

	of_node_put(opp_np);
}

static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
@@ -245,26 +282,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
 */
void dev_pm_opp_of_remove_table(struct device *dev)
{
	_dev_pm_opp_find_and_remove_table(dev, false);
	_dev_pm_opp_find_and_remove_table(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);

/* Returns opp descriptor node for a device node, caller must
 * do of_node_put() */
static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
						     int index)
{
	/* "operating-points-v2" can be an array for power domain providers */
	return of_parse_phandle(np, "operating-points-v2", index);
}

/* Returns opp descriptor node for a device, caller must do of_node_put() */
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
{
	return _opp_of_get_opp_desc_node(dev->of_node, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);

/**
 * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
 * @opp_table:	OPP table
@@ -276,15 +297,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
 * removed by dev_pm_opp_remove.
 *
 * Return:
 * 0		On success OR
 * Valid OPP pointer:
 *		On success
 * NULL:
 *		Duplicate OPPs (both freq and volt are same) and opp->available
 * -EEXIST	Freq are same and volt are different OR
 *		OR if the OPP is not supported by hardware.
 * ERR_PTR(-EEXIST):
 *		Freq are same and volt are different OR
 *		Duplicate OPPs (both freq and volt are same) and !opp->available
 * -ENOMEM	Memory allocation failure
 * -EINVAL	Failed parsing the OPP node
 * ERR_PTR(-ENOMEM):
 *		Memory allocation failure
 * ERR_PTR(-EINVAL):
 *		Failed parsing the OPP node
 */
static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
			      struct device_node *np)
static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
		struct device *dev, struct device_node *np)
{
	struct dev_pm_opp *new_opp;
	u64 rate = 0;
@@ -294,7 +321,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,

	new_opp = _opp_allocate(opp_table);
	if (!new_opp)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	ret = of_property_read_u64(np, "opp-hz", &rate);
	if (ret < 0) {
@@ -369,52 +396,47 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
	 * frequency/voltage list.
	 */
	blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
	return 0;
	return new_opp;

free_opp:
	_opp_free(new_opp);

	return ret;
	return ERR_PTR(ret);
}

/* Initializes OPP tables based on new bindings */
static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
{
	struct device_node *np;
	struct opp_table *opp_table;
	int ret = 0, count = 0, pstate_count = 0;
	int ret, count = 0, pstate_count = 0;
	struct dev_pm_opp *opp;

	opp_table = _managed_opp(opp_np);
	if (opp_table) {
		/* OPPs are already managed */
		if (!_add_opp_dev(dev, opp_table))
			ret = -ENOMEM;
		goto put_opp_table;
	/* OPP table is already initialized for the device */
	if (opp_table->parsed_static_opps) {
		kref_get(&opp_table->list_kref);
		return 0;
	}

	opp_table = dev_pm_opp_get_opp_table(dev);
	if (!opp_table)
		return -ENOMEM;
	kref_init(&opp_table->list_kref);

	/* We have opp-table node now, iterate over it and add OPPs */
	for_each_available_child_of_node(opp_np, np) {
		count++;

		ret = _opp_add_static_v2(opp_table, dev, np);
		if (ret) {
	for_each_available_child_of_node(opp_table->np, np) {
		opp = _opp_add_static_v2(opp_table, dev, np);
		if (IS_ERR(opp)) {
			ret = PTR_ERR(opp);
			dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
				ret);
			_dev_pm_opp_remove_table(opp_table, dev, false);
			of_node_put(np);
			goto put_opp_table;
			goto put_list_kref;
		} else if (opp) {
			count++;
		}
	}

	/* There should be one of more OPP defined */
	if (WARN_ON(!count)) {
		ret = -ENOENT;
		goto put_opp_table;
		goto put_list_kref;
	}

	list_for_each_entry(opp, &opp_table->opp_list, node)
@@ -425,28 +447,25 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
		dev_err(dev, "Not all nodes have performance state set (%d: %d)\n",
			count, pstate_count);
		ret = -ENOENT;
		goto put_opp_table;
		goto put_list_kref;
	}

	if (pstate_count)
		opp_table->genpd_performance_state = true;

	opp_table->np = opp_np;
	if (of_property_read_bool(opp_np, "opp-shared"))
		opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
	else
		opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
	opp_table->parsed_static_opps = true;

put_opp_table:
	dev_pm_opp_put_opp_table(opp_table);
	return 0;

put_list_kref:
	_put_opp_list_kref(opp_table);

	return ret;
}

/* Initializes OPP tables based on old-deprecated bindings */
static int _of_add_opp_table_v1(struct device *dev)
static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
{
	struct opp_table *opp_table;
	const struct property *prop;
	const __be32 *val;
	int nr, ret = 0;
@@ -467,9 +486,7 @@ static int _of_add_opp_table_v1(struct device *dev)
		return -EINVAL;
	}

	opp_table = dev_pm_opp_get_opp_table(dev);
	if (!opp_table)
		return -ENOMEM;
	kref_init(&opp_table->list_kref);

	val = prop->value;
	while (nr) {
@@ -480,13 +497,12 @@ static int _of_add_opp_table_v1(struct device *dev)
		if (ret) {
			dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
				__func__, freq, ret);
			_dev_pm_opp_remove_table(opp_table, dev, false);
			break;
			_put_opp_list_kref(opp_table);
			return ret;
		}
		nr -= 2;
	}

	dev_pm_opp_put_opp_table(opp_table);
	return ret;
}

@@ -509,24 +525,24 @@ static int _of_add_opp_table_v1(struct device *dev)
 */
int dev_pm_opp_of_add_table(struct device *dev)
{
	struct device_node *opp_np;
	struct opp_table *opp_table;
	int ret;

	opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0);
	if (!opp_table)
		return -ENOMEM;

	/*
	 * OPPs have two version of bindings now. The older one is deprecated,
	 * try for the new binding first.
	 */
	opp_np = dev_pm_opp_of_get_opp_desc_node(dev);
	if (!opp_np) {
		/*
		 * Try old-deprecated bindings for backward compatibility with
		 * older dtbs.
	 * OPPs have two version of bindings now. Also try the old (v1)
	 * bindings for backward compatibility with older dtbs.
	 */
		return _of_add_opp_table_v1(dev);
	}
	if (opp_table->np)
		ret = _of_add_opp_table_v2(dev, opp_table);
	else
		ret = _of_add_opp_table_v1(dev, opp_table);

	ret = _of_add_opp_table_v2(dev, opp_np);
	of_node_put(opp_np);
	if (ret)
		dev_pm_opp_put_opp_table(opp_table);

	return ret;
}
@@ -553,28 +569,29 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
 */
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
	struct device_node *opp_np;
	struct opp_table *opp_table;
	int ret, count;

again:
	opp_np = _opp_of_get_opp_desc_node(dev->of_node, index);
	if (!opp_np) {
	if (index) {
		/*
		 * If only one phandle is present, then the same OPP table
		 * applies for all index requests.
		 */
		count = of_count_phandle_with_args(dev->of_node,
						   "operating-points-v2", NULL);
		if (count == 1 && index) {
		if (count != 1)
			return -ENODEV;

		index = 0;
			goto again;
	}

		return -ENODEV;
	}
	opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
	if (!opp_table)
		return -ENOMEM;

	ret = _of_add_opp_table_v2(dev, opp_np);
	of_node_put(opp_np);
	ret = _of_add_opp_table_v2(dev, opp_table);
	if (ret)
		dev_pm_opp_put_opp_table(opp_table);

	return ret;
}
@@ -591,7 +608,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
 */
void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
{
	_dev_pm_opp_cpumask_remove_table(cpumask, true);
	_dev_pm_opp_cpumask_remove_table(cpumask, -1);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table);

@@ -604,16 +621,18 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table);
int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
{
	struct device *cpu_dev;
	int cpu, ret = 0;
	int cpu, ret;

	WARN_ON(cpumask_empty(cpumask));
	if (WARN_ON(cpumask_empty(cpumask)))
		return -ENODEV;

	for_each_cpu(cpu, cpumask) {
		cpu_dev = get_cpu_device(cpu);
		if (!cpu_dev) {
			pr_err("%s: failed to get cpu%d device\n", __func__,
			       cpu);
			continue;
			ret = -ENODEV;
			goto remove_table;
		}

		ret = dev_pm_opp_of_add_table(cpu_dev);
@@ -625,12 +644,16 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
			pr_debug("%s: couldn't find opp table for cpu:%d, %d\n",
				 __func__, cpu, ret);

			/* Free all other OPPs */
			dev_pm_opp_of_cpumask_remove_table(cpumask);
			break;
			goto remove_table;
		}
	}

	return 0;

remove_table:
	/* Free all other OPPs */
	_dev_pm_opp_cpumask_remove_table(cpumask, cpu);

	return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
Loading