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

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

Merge branch 'opp/genpd-pstate-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Pull Operating Performance Points (OPP) library changes for v4.18
from Viresh Kumar.

* 'opp/genpd-pstate-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  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()
  PM / OPP: "opp-hz" is optional for power domains
  PM / OPP: dt-bindings: Make "opp-hz" optional for power domains
  PM / OPP: dt-bindings: Rename "required-opp" as "required-opps"
  soc/tegra: pmc: Don't allocate struct tegra_powergate on stack
parents 71f277a7 28fa4aca
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
+113 −14
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>
@@ -1691,6 +1692,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 +1891,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 +1932,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 +1945,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;

		data->domains[i]->provider = &np->fwnode;
		data->domains[i]->has_provider = true;
		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;
			}
		}

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

	ret = genpd_add_provider(np, data->xlate, data);
@@ -1940,10 +1978,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 +2014,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 +2397,54 @@ 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)) {
		state = 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 */


+58 −116
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)
@@ -1550,81 +1567,6 @@ void dev_pm_opp_unregister_set_opp_helper(struct 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
+13 −2
Original line number Diff line number Diff line
@@ -77,10 +77,21 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
{
	struct dentry *pdentry = opp_table->dentry;
	struct dentry *d;
	unsigned long id;
	char name[25];	/* 20 chars for 64 bit value + 5 (opp:\0) */

	/* Rate is unique to each OPP, use it to give opp-name */
	snprintf(name, sizeof(name), "opp:%lu", opp->rate);
	/*
	 * Get directory name for OPP.
	 *
	 * - Normally rate is unique to each OPP, use it to get unique opp-name.
	 * - For some devices rate isn't available, use index instead.
	 */
	if (likely(opp->rate))
		id = opp->rate;
	else
		id = _get_opp_count(opp_table);

	snprintf(name, sizeof(name), "opp:%lu", id);

	/* Create per-opp directory */
	d = debugfs_create_dir(name, pdentry);
Loading