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

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

Merge branch 'pm-opp'

* pm-opp: (24 commits)
  PM / Domains: Drop unused parameter in genpd_allocate_dev_data()
  PM / Domains: Drop genpd as in-param for pm_genpd_remove_device()
  PM / Domains: Drop __pm_genpd_add_device()
  PM / Domains: Drop extern declarations of functions in pm_domain.h
  PM / domains: Add perf_state attribute to genpd debugfs
  OPP: Allow same OPP table to be used for multiple genpd
  PM / Domain: Return 0 on error from of_genpd_opp_to_performance_state()
  PM / OPP: Fix shared OPP table support in dev_pm_opp_register_set_opp_helper()
  PM / OPP: Fix shared OPP table support in dev_pm_opp_set_regulators()
  PM / OPP: Fix shared OPP table support in dev_pm_opp_set_prop_name()
  PM / OPP: Fix shared OPP table support in dev_pm_opp_set_supported_hw()
  PM / OPP: silence an uninitialized variable warning
  PM / OPP: Remove dev_pm_opp_{un}register_get_pstate_helper()
  PM / OPP: Get performance state using genpd helper
  PM / Domain: Implement of_genpd_opp_to_performance_state()
  PM / Domain: Add support to parse domain's OPP table
  PM / Domain: Add struct device to genpd
  PM / OPP: Implement dev_pm_opp_get_of_node()
  PM / OPP: Implement of_dev_pm_opp_find_required_opp()
  PM / OPP: Implement dev_pm_opp_of_add_table_indexed()
  ...
parents 5b550c92 96c1bf68
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -82,7 +82,10 @@ This defines voltage-current-frequency combinations along with other related
properties.

Required properties:
- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer.
- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. This is a
  required property for all device nodes but devices like power domains. The
  power domain nodes must have another (implementation dependent) property which
  uniquely identifies the OPP nodes.

Optional properties:
- opp-microvolt: voltage in micro Volts.
@@ -159,7 +162,7 @@ Optional properties:

- status: Marks the node enabled/disabled.

- required-opp: This contains phandle to an OPP node in another device's OPP
- required-opps: This contains phandle to an OPP node in another device's OPP
  table. It may contain an array of phandles, where each phandle points to an
  OPP of a different device. It should not contain multiple phandles to the OPP
  nodes in the same OPP table. This specifies the minimum required OPP of the
+3 −3
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ inside a PM domain with index 0 of a power controller represented by a node
with the label "power".

Optional properties:
- required-opp: This contains phandle to an OPP node in another device's OPP
- required-opps: This contains phandle to an OPP node in another device's OPP
  table. It may contain an array of phandles, where each phandle points to an
  OPP of a different device. It should not contain multiple phandles to the OPP
  nodes in the same OPP table. This specifies the minimum required OPP of the
@@ -175,14 +175,14 @@ Example:
		compatible = "foo,i-leak-current";
		reg = <0x12350000 0x1000>;
		power-domains = <&power 0>;
		required-opp = <&domain0_opp_0>;
		required-opps = <&domain0_opp_0>;
	};

	leaky-device1@12350000 {
		compatible = "foo,i-leak-current";
		reg = <0x12350000 0x1000>;
		power-domains = <&power 1>;
		required-opp = <&domain1_opp_1>;
		required-opps = <&domain1_opp_1>;
	};

[1]. Documentation/devicetree/bindings/power/domain-idle-state.txt
+141 −26
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
@@ -1315,7 +1316,6 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
#endif /* CONFIG_PM_SLEEP */

static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
					struct generic_pm_domain *genpd,
					struct gpd_timing_data *td)
{
	struct generic_pm_domain_data *gpd_data;
@@ -1384,7 +1384,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
		return -EINVAL;

	gpd_data = genpd_alloc_dev_data(dev, genpd, td);
	gpd_data = genpd_alloc_dev_data(dev, td);
	if (IS_ERR(gpd_data))
		return PTR_ERR(gpd_data);

@@ -1413,23 +1413,21 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
}

/**
 * __pm_genpd_add_device - Add a device to an I/O PM domain.
 * pm_genpd_add_device - Add a device to an I/O PM domain.
 * @genpd: PM domain to add the device to.
 * @dev: Device to be added.
 * @td: Set of PM QoS timing parameters to attach to the device.
 */
int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
			  struct gpd_timing_data *td)
int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
{
	int ret;

	mutex_lock(&gpd_list_lock);
	ret = genpd_add_device(genpd, dev, td);
	ret = genpd_add_device(genpd, dev, NULL);
	mutex_unlock(&gpd_list_lock);

	return ret;
}
EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
EXPORT_SYMBOL_GPL(pm_genpd_add_device);

static int genpd_remove_device(struct generic_pm_domain *genpd,
			       struct device *dev)
@@ -1476,13 +1474,13 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,

/**
 * pm_genpd_remove_device - Remove a device from an I/O PM domain.
 * @genpd: PM domain to remove the device from.
 * @dev: Device to be removed.
 */
int pm_genpd_remove_device(struct generic_pm_domain *genpd,
			   struct device *dev)
int pm_genpd_remove_device(struct device *dev)
{
	if (!genpd || genpd != genpd_lookup_dev(dev))
	struct generic_pm_domain *genpd = genpd_lookup_dev(dev);

	if (!genpd)
		return -EINVAL;

	return genpd_remove_device(genpd, dev);
@@ -1691,6 +1689,9 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
			return ret;
	}

	device_initialize(&genpd->dev);
	dev_set_name(&genpd->dev, "%s", genpd->name);

	mutex_lock(&gpd_list_lock);
	list_add(&genpd->gpd_list_node, &gpd_list);
	mutex_unlock(&gpd_list_lock);
@@ -1887,14 +1888,33 @@ int of_genpd_add_provider_simple(struct device_node *np,

	mutex_lock(&gpd_list_lock);

	if (genpd_present(genpd)) {
	if (!genpd_present(genpd))
		goto unlock;

	genpd->dev.of_node = np;

	/* Parse genpd OPP table */
	if (genpd->set_performance_state) {
		ret = dev_pm_opp_of_add_table(&genpd->dev);
		if (ret) {
			dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
				ret);
			goto unlock;
		}
	}

	ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
		if (!ret) {
	if (ret) {
		if (genpd->set_performance_state)
			dev_pm_opp_of_remove_table(&genpd->dev);

		goto unlock;
	}

	genpd->provider = &np->fwnode;
	genpd->has_provider = true;
		}
	}

unlock:
	mutex_unlock(&gpd_list_lock);

	return ret;
@@ -1909,6 +1929,7 @@ EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
int of_genpd_add_provider_onecell(struct device_node *np,
				  struct genpd_onecell_data *data)
{
	struct generic_pm_domain *genpd;
	unsigned int i;
	int ret = -EINVAL;

@@ -1921,13 +1942,27 @@ int of_genpd_add_provider_onecell(struct device_node *np,
		data->xlate = genpd_xlate_onecell;

	for (i = 0; i < data->num_domains; i++) {
		if (!data->domains[i])
		genpd = data->domains[i];

		if (!genpd)
			continue;
		if (!genpd_present(data->domains[i]))
		if (!genpd_present(genpd))
			goto error;

		genpd->dev.of_node = np;

		/* Parse genpd OPP table */
		if (genpd->set_performance_state) {
			ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
			if (ret) {
				dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
					i, ret);
				goto error;
			}
		}

		data->domains[i]->provider = &np->fwnode;
		data->domains[i]->has_provider = true;
		genpd->provider = &np->fwnode;
		genpd->has_provider = true;
	}

	ret = genpd_add_provider(np, data->xlate, data);
@@ -1940,10 +1975,16 @@ int of_genpd_add_provider_onecell(struct device_node *np,

error:
	while (i--) {
		if (!data->domains[i])
		genpd = data->domains[i];

		if (!genpd)
			continue;
		data->domains[i]->provider = NULL;
		data->domains[i]->has_provider = false;

		genpd->provider = NULL;
		genpd->has_provider = false;

		if (genpd->set_performance_state)
			dev_pm_opp_of_remove_table(&genpd->dev);
	}

	mutex_unlock(&gpd_list_lock);
@@ -1970,10 +2011,17 @@ void of_genpd_del_provider(struct device_node *np)
			 * provider, set the 'has_provider' to false
			 * so that the PM domain can be safely removed.
			 */
			list_for_each_entry(gpd, &gpd_list, gpd_list_node)
				if (gpd->provider == &np->fwnode)
			list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
				if (gpd->provider == &np->fwnode) {
					gpd->has_provider = false;

					if (!gpd->set_performance_state)
						continue;

					dev_pm_opp_of_remove_table(&gpd->dev);
				}
			}

			list_del(&cp->link);
			of_node_put(cp->node);
			kfree(cp);
@@ -2346,6 +2394,55 @@ int of_genpd_parse_idle_states(struct device_node *dn,
}
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);

/**
 * of_genpd_opp_to_performance_state- Gets performance state of device's
 * power domain corresponding to a DT node's "required-opps" property.
 *
 * @dev: Device for which the performance-state needs to be found.
 * @opp_node: DT node where the "required-opps" property is present. This can be
 *	the device node itself (if it doesn't have an OPP table) or a node
 *	within the OPP table of a device (if device has an OPP table).
 * @state: Pointer to return performance state.
 *
 * Returns performance state corresponding to the "required-opps" property of
 * a DT node. This calls platform specific genpd->opp_to_performance_state()
 * callback to translate power domain OPP to performance state.
 *
 * Returns performance state on success and 0 on failure.
 */
unsigned int of_genpd_opp_to_performance_state(struct device *dev,
					       struct device_node *opp_node)
{
	struct generic_pm_domain *genpd;
	struct dev_pm_opp *opp;
	int state = 0;

	genpd = dev_to_genpd(dev);
	if (IS_ERR(genpd))
		return 0;

	if (unlikely(!genpd->set_performance_state))
		return 0;

	genpd_lock(genpd);

	opp = of_dev_pm_opp_find_required_opp(&genpd->dev, opp_node);
	if (IS_ERR(opp)) {
		dev_err(dev, "Failed to find required OPP: %ld\n",
			PTR_ERR(opp));
		goto unlock;
	}

	state = genpd->opp_to_performance_state(genpd, opp);
	dev_pm_opp_put(opp);

unlock:
	genpd_unlock(genpd);

	return state;
}
EXPORT_SYMBOL_GPL(of_genpd_opp_to_performance_state);

#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */


@@ -2613,6 +2710,19 @@ static int genpd_devices_show(struct seq_file *s, void *data)
	return ret;
}

static int genpd_perf_state_show(struct seq_file *s, void *data)
{
	struct generic_pm_domain *genpd = s->private;

	if (genpd_lock_interruptible(genpd))
		return -ERESTARTSYS;

	seq_printf(s, "%u\n", genpd->performance_state);

	genpd_unlock(genpd);
	return 0;
}

#define define_genpd_open_function(name) \
static int genpd_##name##_open(struct inode *inode, struct file *file) \
{ \
@@ -2626,6 +2736,7 @@ define_genpd_open_function(idle_states);
define_genpd_open_function(active_time);
define_genpd_open_function(total_idle_time);
define_genpd_open_function(devices);
define_genpd_open_function(perf_state);

#define define_genpd_debugfs_fops(name) \
static const struct file_operations genpd_##name##_fops = { \
@@ -2642,6 +2753,7 @@ define_genpd_debugfs_fops(idle_states);
define_genpd_debugfs_fops(active_time);
define_genpd_debugfs_fops(total_idle_time);
define_genpd_debugfs_fops(devices);
define_genpd_debugfs_fops(perf_state);

static int __init genpd_debug_init(void)
{
@@ -2675,6 +2787,9 @@ static int __init genpd_debug_init(void)
				d, genpd, &genpd_total_idle_time_fops);
		debugfs_create_file("devices", 0444,
				d, genpd, &genpd_devices_fops);
		if (genpd->set_performance_state)
			debugfs_create_file("perf_state", 0444,
					    d, genpd, &genpd_perf_state_fops);
	}

	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -513,7 +513,7 @@ static int acp_hw_fini(void *handle)
	if (adev->acp.acp_genpd) {
		for (i = 0; i < ACP_DEVS ; i++) {
			dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
			ret = pm_genpd_remove_device(&adev->acp.acp_genpd->gpd, dev);
			ret = pm_genpd_remove_device(dev);
			/* If removal fails, dont giveup and try rest */
			if (ret)
				dev_err(dev, "remove dev from genpd failed\n");
+79 −188
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ LIST_HEAD(opp_tables);
/* Lock to allow exclusive modification to the device and opp lists */
DEFINE_MUTEX(opp_table_lock);

static void dev_pm_opp_get(struct dev_pm_opp *opp);

static struct opp_device *_find_opp_dev(const struct device *dev,
					struct opp_table *opp_table)
{
@@ -281,6 +279,23 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);

int _get_opp_count(struct opp_table *opp_table)
{
	struct dev_pm_opp *opp;
	int count = 0;

	mutex_lock(&opp_table->lock);

	list_for_each_entry(opp, &opp_table->opp_list, node) {
		if (opp->available)
			count++;
	}

	mutex_unlock(&opp_table->lock);

	return count;
}

/**
 * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table
 * @dev:	device for which we do this operation
@@ -291,25 +306,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
int dev_pm_opp_get_opp_count(struct device *dev)
{
	struct opp_table *opp_table;
	struct dev_pm_opp *temp_opp;
	int count = 0;
	int count;

	opp_table = _find_opp_table(dev);
	if (IS_ERR(opp_table)) {
		count = PTR_ERR(opp_table);
		dev_dbg(dev, "%s: OPP table not found (%d)\n",
			__func__, count);
		return count;
	}

	mutex_lock(&opp_table->lock);

	list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
		if (temp_opp->available)
			count++;
		return 0;
	}

	mutex_unlock(&opp_table->lock);
	count = _get_opp_count(opp_table);
	dev_pm_opp_put_opp_table(opp_table);

	return count;
@@ -892,7 +899,7 @@ static void _opp_kref_release(struct kref *kref)
	dev_pm_opp_put_opp_table(opp_table);
}

static void dev_pm_opp_get(struct dev_pm_opp *opp)
void dev_pm_opp_get(struct dev_pm_opp *opp)
{
	kref_get(&opp->kref);
}
@@ -985,22 +992,11 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
	return true;
}

/*
 * Returns:
 * 0: On success. And appropriate error message for duplicate OPPs.
 * -EBUSY: For OPP with same freq/volt and is available. The callers of
 *  _opp_add() must return 0 if they receive -EBUSY from it. This is to make
 *  sure we don't print error messages unnecessarily if different parts of
 *  kernel try to initialize the OPP table.
 * -EEXIST: For OPP with same freq but different volt or is unavailable. This
 *  should be considered an error by the callers of _opp_add().
 */
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
	     struct opp_table *opp_table)
static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
			     struct opp_table *opp_table,
			     struct list_head **head)
{
	struct dev_pm_opp *opp;
	struct list_head *head;
	int ret;

	/*
	 * Insert new OPP in order of increasing frequency and discard if
@@ -1010,17 +1006,14 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
	 * loop, don't replace it with head otherwise it will become an infinite
	 * loop.
	 */
	mutex_lock(&opp_table->lock);
	head = &opp_table->opp_list;

	list_for_each_entry(opp, &opp_table->opp_list, node) {
		if (new_opp->rate > opp->rate) {
			head = &opp->node;
			*head = &opp->node;
			continue;
		}

		if (new_opp->rate < opp->rate)
			break;
			return 0;

		/* Duplicate OPPs */
		dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
@@ -1029,15 +1022,39 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
			 new_opp->supplies[0].u_volt, new_opp->available);

		/* Should we compare voltages for all regulators here ? */
		ret = opp->available &&
		return opp->available &&
		       new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST;
	}

	return 0;
}

/*
 * Returns:
 * 0: On success. And appropriate error message for duplicate OPPs.
 * -EBUSY: For OPP with same freq/volt and is available. The callers of
 *  _opp_add() must return 0 if they receive -EBUSY from it. This is to make
 *  sure we don't print error messages unnecessarily if different parts of
 *  kernel try to initialize the OPP table.
 * -EEXIST: For OPP with same freq but different volt or is unavailable. This
 *  should be considered an error by the callers of _opp_add().
 */
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
	     struct opp_table *opp_table, bool rate_not_available)
{
	struct list_head *head;
	int ret;

	mutex_lock(&opp_table->lock);
	head = &opp_table->opp_list;

	if (likely(!rate_not_available)) {
		ret = _opp_is_duplicate(dev, new_opp, opp_table, &head);
		if (ret) {
			mutex_unlock(&opp_table->lock);
			return ret;
		}

	if (opp_table->get_pstate)
		new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate);
	}

	list_add(&new_opp->node, head);
	mutex_unlock(&opp_table->lock);
@@ -1104,7 +1121,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
	new_opp->available = true;
	new_opp->dynamic = dynamic;

	ret = _opp_add(dev, new_opp, opp_table);
	ret = _opp_add(dev, new_opp, opp_table, false);
	if (ret) {
		/* Don't return error for duplicate OPPs */
		if (ret == -EBUSY)
@@ -1140,7 +1157,6 @@ struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev,
			const u32 *versions, unsigned int count)
{
	struct opp_table *opp_table;
	int ret;

	opp_table = dev_pm_opp_get_opp_table(dev);
	if (!opp_table)
@@ -1149,29 +1165,20 @@ struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev,
	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	/* Do we already have a version hierarchy associated with opp_table? */
	if (opp_table->supported_hw) {
		dev_err(dev, "%s: Already have supported hardware list\n",
			__func__);
		ret = -EBUSY;
		goto err;
	}
	/* Another CPU that shares the OPP table has set the property ? */
	if (opp_table->supported_hw)
		return opp_table;

	opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions),
					GFP_KERNEL);
	if (!opp_table->supported_hw) {
		ret = -ENOMEM;
		goto err;
		dev_pm_opp_put_opp_table(opp_table);
		return ERR_PTR(-ENOMEM);
	}

	opp_table->supported_hw_count = count;

	return opp_table;

err:
	dev_pm_opp_put_opp_table(opp_table);

	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);

@@ -1188,12 +1195,6 @@ void dev_pm_opp_put_supported_hw(struct opp_table *opp_table)
	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	if (!opp_table->supported_hw) {
		pr_err("%s: Doesn't have supported hardware list\n",
		       __func__);
		return;
	}

	kfree(opp_table->supported_hw);
	opp_table->supported_hw = NULL;
	opp_table->supported_hw_count = 0;
@@ -1215,7 +1216,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{
	struct opp_table *opp_table;
	int ret;

	opp_table = dev_pm_opp_get_opp_table(dev);
	if (!opp_table)
@@ -1224,26 +1224,17 @@ struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	/* Do we already have a prop-name associated with opp_table? */
	if (opp_table->prop_name) {
		dev_err(dev, "%s: Already have prop-name %s\n", __func__,
			opp_table->prop_name);
		ret = -EBUSY;
		goto err;
	}
	/* Another CPU that shares the OPP table has set the property ? */
	if (opp_table->prop_name)
		return opp_table;

	opp_table->prop_name = kstrdup(name, GFP_KERNEL);
	if (!opp_table->prop_name) {
		ret = -ENOMEM;
		goto err;
		dev_pm_opp_put_opp_table(opp_table);
		return ERR_PTR(-ENOMEM);
	}

	return opp_table;

err:
	dev_pm_opp_put_opp_table(opp_table);

	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);

@@ -1260,11 +1251,6 @@ void dev_pm_opp_put_prop_name(struct opp_table *opp_table)
	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	if (!opp_table->prop_name) {
		pr_err("%s: Doesn't have a prop-name\n", __func__);
		return;
	}

	kfree(opp_table->prop_name);
	opp_table->prop_name = NULL;

@@ -1334,11 +1320,9 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
		goto err;
	}

	/* Already have regulators set */
	if (opp_table->regulators) {
		ret = -EBUSY;
		goto err;
	}
	/* Another CPU that shares the OPP table has set the regulators ? */
	if (opp_table->regulators)
		return opp_table;

	opp_table->regulators = kmalloc_array(count,
					      sizeof(*opp_table->regulators),
@@ -1392,10 +1376,8 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
{
	int i;

	if (!opp_table->regulators) {
		pr_err("%s: Doesn't have regulators set\n", __func__);
		return;
	}
	if (!opp_table->regulators)
		goto put_opp_table;

	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));
@@ -1409,6 +1391,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
	opp_table->regulators = NULL;
	opp_table->regulator_count = 0;

put_opp_table:
	dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators);
@@ -1494,7 +1477,6 @@ struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev,
			int (*set_opp)(struct dev_pm_set_opp_data *data))
{
	struct opp_table *opp_table;
	int ret;

	if (!set_opp)
		return ERR_PTR(-EINVAL);
@@ -1505,24 +1487,15 @@ struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev,

	/* This should be called before OPPs are initialized */
	if (WARN_ON(!list_empty(&opp_table->opp_list))) {
		ret = -EBUSY;
		goto err;
	}

	/* Already have custom set_opp helper */
	if (WARN_ON(opp_table->set_opp)) {
		ret = -EBUSY;
		goto err;
		dev_pm_opp_put_opp_table(opp_table);
		return ERR_PTR(-EBUSY);
	}

	/* Another CPU that shares the OPP table has set the helper ? */
	if (!opp_table->set_opp)
		opp_table->set_opp = set_opp;

	return opp_table;

err:
	dev_pm_opp_put_opp_table(opp_table);

	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);

@@ -1535,96 +1508,14 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);
 */
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
{
	if (!opp_table->set_opp) {
		pr_err("%s: Doesn't have custom set_opp helper set\n",
		       __func__);
		return;
	}

	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	opp_table->set_opp = NULL;

	dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);

/**
 * dev_pm_opp_register_get_pstate_helper() - Register get_pstate() helper.
 * @dev: Device for which the helper is getting registered.
 * @get_pstate: Helper.
 *
 * TODO: Remove this callback after the same information is available via Device
 * Tree.
 *
 * This allows a platform to initialize the performance states of individual
 * OPPs for its devices, until we get similar information directly from DT.
 *
 * This must be called before the OPPs are initialized for the device.
 */
struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev,
		int (*get_pstate)(struct device *dev, unsigned long rate))
{
	struct opp_table *opp_table;
	int ret;

	if (!get_pstate)
		return ERR_PTR(-EINVAL);

	opp_table = dev_pm_opp_get_opp_table(dev);
	if (!opp_table)
		return ERR_PTR(-ENOMEM);

	/* This should be called before OPPs are initialized */
	if (WARN_ON(!list_empty(&opp_table->opp_list))) {
		ret = -EBUSY;
		goto err;
	}

	/* Already have genpd_performance_state set */
	if (WARN_ON(opp_table->genpd_performance_state)) {
		ret = -EBUSY;
		goto err;
	}

	opp_table->genpd_performance_state = true;
	opp_table->get_pstate = get_pstate;

	return opp_table;

err:
	dev_pm_opp_put_opp_table(opp_table);

	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_register_get_pstate_helper);

/**
 * dev_pm_opp_unregister_get_pstate_helper() - Releases resources blocked for
 *					   get_pstate() helper
 * @opp_table: OPP table returned from dev_pm_opp_register_get_pstate_helper().
 *
 * Release resources blocked for platform specific get_pstate() helper.
 */
void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table)
{
	if (!opp_table->genpd_performance_state) {
		pr_err("%s: Doesn't have performance states set\n",
		       __func__);
		return;
	}

	/* Make sure there are no concurrent readers while updating opp_table */
	WARN_ON(!list_empty(&opp_table->opp_list));

	opp_table->genpd_performance_state = false;
	opp_table->get_pstate = NULL;

	dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_get_pstate_helper);

/**
 * dev_pm_opp_add()  - Add an OPP table from a table definitions
 * @dev:	device for which we do this operation
Loading