Commit bcbeef5f authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files
Pull more operating performance points (OPP) framework changes for v4.21
from Viresh Kumar:

"- Fix missing OPP debugfs directory (Viresh Kumar).

 - Make genpd performance states orthogonal to idlestates (Ulf
   Hansson).

 - Propagate performance state changes from genpd to its master (Viresh
   Kumar).

 - Minor improvement of some OPP helpers (Viresh Kumar)."

* 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  PM / Domains: Propagate performance state updates
  PM / Domains: Factorize dev_pm_genpd_set_performance_state()
  PM / Domains: Save OPP table pointer in genpd
  OPP: Don't return 0 on error from of_get_required_opp_performance_state()
  OPP: Add dev_pm_opp_xlate_performance_state() helper
  OPP: Improve _find_table_of_opp_np()
  PM / Domains: Make genpd performance states orthogonal to the idlestates
  OPP: Fix missing debugfs supply directory for OPPs
  OPP: Use opp_table->regulators to verify no regulator case
parents 83fd1e52 ade0c949
Loading
Loading
Loading
Loading
+148 −54
Original line number Diff line number Diff line
@@ -239,6 +239,127 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
#endif

static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
					   unsigned int state)
{
	struct generic_pm_domain_data *pd_data;
	struct pm_domain_data *pdd;
	struct gpd_link *link;

	/* New requested state is same as Max requested state */
	if (state == genpd->performance_state)
		return state;

	/* New requested state is higher than Max requested state */
	if (state > genpd->performance_state)
		return state;

	/* Traverse all devices within the domain */
	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
		pd_data = to_gpd_data(pdd);

		if (pd_data->performance_state > state)
			state = pd_data->performance_state;
	}

	/*
	 * Traverse all sub-domains within the domain. This can be
	 * done without any additional locking as the link->performance_state
	 * field is protected by the master genpd->lock, which is already taken.
	 *
	 * Also note that link->performance_state (subdomain's performance state
	 * requirement to master domain) is different from
	 * link->slave->performance_state (current performance state requirement
	 * of the devices/sub-domains of the subdomain) and so can have a
	 * different value.
	 *
	 * Note that we also take vote from powered-off sub-domains into account
	 * as the same is done for devices right now.
	 */
	list_for_each_entry(link, &genpd->master_links, master_node) {
		if (link->performance_state > state)
			state = link->performance_state;
	}

	return state;
}

static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
					unsigned int state, int depth)
{
	struct generic_pm_domain *master;
	struct gpd_link *link;
	int master_state, ret;

	if (state == genpd->performance_state)
		return 0;

	/* Propagate to masters of genpd */
	list_for_each_entry(link, &genpd->slave_links, slave_node) {
		master = link->master;

		if (!master->set_performance_state)
			continue;

		/* Find master's performance state */
		ret = dev_pm_opp_xlate_performance_state(genpd->opp_table,
							 master->opp_table,
							 state);
		if (unlikely(ret < 0))
			goto err;

		master_state = ret;

		genpd_lock_nested(master, depth + 1);

		link->prev_performance_state = link->performance_state;
		link->performance_state = master_state;
		master_state = _genpd_reeval_performance_state(master,
						master_state);
		ret = _genpd_set_performance_state(master, master_state, depth + 1);
		if (ret)
			link->performance_state = link->prev_performance_state;

		genpd_unlock(master);

		if (ret)
			goto err;
	}

	ret = genpd->set_performance_state(genpd, state);
	if (ret)
		goto err;

	genpd->performance_state = state;
	return 0;

err:
	/* Encountered an error, lets rollback */
	list_for_each_entry_continue_reverse(link, &genpd->slave_links,
					     slave_node) {
		master = link->master;

		if (!master->set_performance_state)
			continue;

		genpd_lock_nested(master, depth + 1);

		master_state = link->prev_performance_state;
		link->performance_state = master_state;

		master_state = _genpd_reeval_performance_state(master,
						master_state);
		if (_genpd_set_performance_state(master, master_state, depth + 1)) {
			pr_err("%s: Failed to roll back to %d performance state\n",
			       master->name, master_state);
		}

		genpd_unlock(master);
	}

	return ret;
}

/**
 * dev_pm_genpd_set_performance_state- Set performance state of device's power
 * domain.
@@ -257,10 +378,9 @@ static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
{
	struct generic_pm_domain *genpd;
	struct generic_pm_domain_data *gpd_data, *pd_data;
	struct pm_domain_data *pdd;
	struct generic_pm_domain_data *gpd_data;
	unsigned int prev;
	int ret = 0;
	int ret;

	genpd = dev_to_genpd(dev);
	if (IS_ERR(genpd))
@@ -281,47 +401,11 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
	prev = gpd_data->performance_state;
	gpd_data->performance_state = state;

	/* New requested state is same as Max requested state */
	if (state == genpd->performance_state)
		goto unlock;

	/* New requested state is higher than Max requested state */
	if (state > genpd->performance_state)
		goto update_state;

	/* Traverse all devices within the domain */
	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
		pd_data = to_gpd_data(pdd);

		if (pd_data->performance_state > state)
			state = pd_data->performance_state;
	}

	if (state == genpd->performance_state)
		goto unlock;

	/*
	 * We aren't propagating performance state changes of a subdomain to its
	 * masters as we don't have hardware that needs it. Over that, the
	 * performance states of subdomain and its masters may not have
	 * one-to-one mapping and would require additional information. We can
	 * get back to this once we have hardware that needs it. For that
	 * reason, we don't have to consider performance state of the subdomains
	 * of genpd here.
	 */

update_state:
	if (genpd_status_on(genpd)) {
		ret = genpd->set_performance_state(genpd, state);
		if (ret) {
	state = _genpd_reeval_performance_state(genpd, state);
	ret = _genpd_set_performance_state(genpd, state, 0);
	if (ret)
		gpd_data->performance_state = prev;
			goto unlock;
		}
	}

	genpd->performance_state = state;

unlock:
	genpd_unlock(genpd);

	return ret;
@@ -347,15 +431,6 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
		return ret;

	elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));

	if (unlikely(genpd->set_performance_state)) {
		ret = genpd->set_performance_state(genpd, genpd->performance_state);
		if (ret) {
			pr_warn("%s: Failed to set performance state %d (%d)\n",
				genpd->name, genpd->performance_state, ret);
		}
	}

	if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
		return ret;

@@ -1907,12 +1982,21 @@ int of_genpd_add_provider_simple(struct device_node *np,
				ret);
			goto unlock;
		}

		/*
		 * Save table for faster processing while setting performance
		 * state.
		 */
		genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
		WARN_ON(!genpd->opp_table);
	}

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

		goto unlock;
	}
@@ -1965,6 +2049,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
					i, ret);
				goto error;
			}

			/*
			 * Save table for faster processing while setting
			 * performance state.
			 */
			genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
			WARN_ON(!genpd->opp_table);
		}

		genpd->provider = &np->fwnode;
@@ -1989,9 +2080,11 @@ error:
		genpd->provider = NULL;
		genpd->has_provider = false;

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

	mutex_unlock(&gpd_list_lock);

@@ -2024,6 +2117,7 @@ void of_genpd_del_provider(struct device_node *np)
					if (!gpd->set_performance_state)
						continue;

					dev_pm_opp_put_opp_table(gpd->opp_table);
					dev_pm_opp_of_remove_table(&gpd->dev);
				}
			}
+79 −7
Original line number Diff line number Diff line
@@ -196,12 +196,12 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
	if (IS_ERR(opp_table))
		return 0;

	count = opp_table->regulator_count;

	/* Regulator may not be required for the device */
	if (!count)
	if (!opp_table->regulators)
		goto put_opp_table;

	count = opp_table->regulator_count;

	uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL);
	if (!uV)
		goto put_opp_table;
@@ -843,6 +843,9 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
	mutex_init(&opp_table->genpd_virt_dev_lock);
	INIT_LIST_HEAD(&opp_table->dev_list);

	/* Mark regulator count uninitialized */
	opp_table->regulator_count = -1;

	opp_dev = _add_opp_dev(dev, opp_table);
	if (!opp_dev) {
		kfree(opp_table);
@@ -1063,7 +1066,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *table)
	int count, supply_size;

	/* Allocate space for at least one supply */
	count = table->regulator_count ? table->regulator_count : 1;
	count = table->regulator_count > 0 ? table->regulator_count : 1;
	supply_size = sizeof(*opp->supplies) * count;

	/* allocate new OPP node and supplies structures */
@@ -1084,6 +1087,9 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
	struct regulator *reg;
	int i;

	if (!opp_table->regulators)
		return true;

	for (i = 0; i < opp_table->regulator_count; i++) {
		reg = opp_table->regulators[i];

@@ -1368,7 +1374,7 @@ static int _allocate_set_opp_data(struct opp_table *opp_table)
	struct dev_pm_set_opp_data *data;
	int len, count = opp_table->regulator_count;

	if (WARN_ON(!count))
	if (WARN_ON(!opp_table->regulators))
		return -EINVAL;

	/* space for set_opp_data */
@@ -1465,7 +1471,7 @@ free_regulators:

	kfree(opp_table->regulators);
	opp_table->regulators = NULL;
	opp_table->regulator_count = 0;
	opp_table->regulator_count = -1;
err:
	dev_pm_opp_put_opp_table(opp_table);

@@ -1494,7 +1500,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)

	kfree(opp_table->regulators);
	opp_table->regulators = NULL;
	opp_table->regulator_count = 0;
	opp_table->regulator_count = -1;

put_opp_table:
	dev_pm_opp_put_opp_table(opp_table);
@@ -1707,6 +1713,69 @@ void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
		dev_err(virt_dev, "Failed to find required device entry\n");
}

/**
 * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
 * @src_table: OPP table which has dst_table as one of its required OPP table.
 * @dst_table: Required OPP table of the src_table.
 * @pstate: Current performance state of the src_table.
 *
 * This Returns pstate of the OPP (present in @dst_table) pointed out by the
 * "required-opps" property of the OPP (present in @src_table) which has
 * performance state set to @pstate.
 *
 * Return: Zero or positive performance state on success, otherwise negative
 * value on errors.
 */
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table,
				       struct opp_table *dst_table,
				       unsigned int pstate)
{
	struct dev_pm_opp *opp;
	int dest_pstate = -EINVAL;
	int i;

	if (!pstate)
		return 0;

	/*
	 * Normally the src_table will have the "required_opps" property set to
	 * point to one of the OPPs in the dst_table, but in some cases the
	 * genpd and its master have one to one mapping of performance states
	 * and so none of them have the "required-opps" property set. Return the
	 * pstate of the src_table as it is in such cases.
	 */
	if (!src_table->required_opp_count)
		return pstate;

	for (i = 0; i < src_table->required_opp_count; i++) {
		if (src_table->required_opp_tables[i]->np == dst_table->np)
			break;
	}

	if (unlikely(i == src_table->required_opp_count)) {
		pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
		       __func__, src_table, dst_table);
		return -EINVAL;
	}

	mutex_lock(&src_table->lock);

	list_for_each_entry(opp, &src_table->opp_list, node) {
		if (opp->pstate == pstate) {
			dest_pstate = opp->required_opps[i]->pstate;
			goto unlock;
		}
	}

	pr_err("%s: Couldn't find matching OPP (%p: %p)\n", __func__, src_table,
	       dst_table);

unlock:
	mutex_unlock(&src_table->lock);

	return dest_pstate;
}

/**
 * dev_pm_opp_add()  - Add an OPP table from a table definitions
 * @dev:	device for which we do this operation
@@ -1733,6 +1802,9 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
	if (!opp_table)
		return -ENOMEM;

	/* Fix regulator count for dynamic OPPs */
	opp_table->regulator_count = 1;

	ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);
	if (ret)
		dev_pm_opp_put_opp_table(opp_table);
+31 −13
Original line number Diff line number Diff line
@@ -114,19 +114,25 @@ static struct device_node *of_parse_required_opp(struct device_node *np,
static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
{
	struct opp_table *opp_table;
	struct dev_pm_opp *opp;
	struct device_node *opp_table_np;

	lockdep_assert_held(&opp_table_lock);

	opp_table_np = of_get_parent(opp_np);
	if (!opp_table_np)
		goto err;

	/* It is safe to put the node now as all we need now is its address */
	of_node_put(opp_table_np);

	list_for_each_entry(opp_table, &opp_tables, node) {
		opp = _find_opp_of_np(opp_table, opp_np);
		if (opp) {
			dev_pm_opp_put(opp);
		if (opp_table_np == opp_table->np) {
			_get_opp_table_kref(opp_table);
			return opp_table;
		}
	}

err:
	return ERR_PTR(-ENODEV);
}

@@ -385,12 +391,10 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
			      struct opp_table *opp_table)
{
	u32 *microvolt, *microamp = NULL;
	int supplies, vcount, icount, ret, i, j;
	int supplies = opp_table->regulator_count, vcount, icount, ret, i, j;
	struct property *prop = NULL;
	char name[NAME_MAX];

	supplies = opp_table->regulator_count ? opp_table->regulator_count : 1;

	/* Search for "opp-microvolt-<name>" */
	if (opp_table->prop_name) {
		snprintf(name, sizeof(name), "opp-microvolt-%s",
@@ -405,7 +409,13 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,

		/* Missing property isn't a problem, but an invalid entry is */
		if (!prop) {
			if (!opp_table->regulator_count)
			if (unlikely(supplies == -1)) {
				/* Initialize regulator_count */
				opp_table->regulator_count = 0;
				return 0;
			}

			if (!supplies)
				return 0;

			dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n",
@@ -414,6 +424,14 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
		}
	}

	if (unlikely(supplies == -1)) {
		/* Initialize regulator_count */
		supplies = opp_table->regulator_count = 1;
	} else if (unlikely(!supplies)) {
		dev_err(dev, "%s: opp-microvolt wasn't expected\n", __func__);
		return -EINVAL;
	}

	vcount = of_property_count_u32_elems(opp->np, name);
	if (vcount < 0) {
		dev_err(dev, "%s: Invalid %s property (%d)\n",
@@ -975,19 +993,19 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus);
 * Returns the performance state of the OPP pointed out by the "required-opps"
 * property at @index in @np.
 *
 * Return: Positive performance state on success, otherwise 0 on errors.
 * Return: Zero or positive performance state on success, otherwise negative
 * value on errors.
 */
unsigned int of_get_required_opp_performance_state(struct device_node *np,
						   int index)
int of_get_required_opp_performance_state(struct device_node *np, int index)
{
	struct dev_pm_opp *opp;
	struct device_node *required_np;
	struct opp_table *opp_table;
	unsigned int pstate = 0;
	int pstate = -EINVAL;

	required_np = of_parse_required_opp(np, index);
	if (!required_np)
		return 0;
		return -EINVAL;

	opp_table = _find_table_of_opp_np(required_np);
	if (IS_ERR(opp_table)) {
+4 −2
Original line number Diff line number Diff line
@@ -145,7 +145,9 @@ enum opp_table_access {
 * @prop_name: A name to postfix to many DT properties, while parsing them.
 * @clk: Device's clock handle
 * @regulators: Supply regulators
 * @regulator_count: Number of power supply regulators
 * @regulator_count: Number of power supply regulators. Its value can be -1
 * (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
 * property).
 * @genpd_performance_state: Device's power domain support performance state.
 * @is_genpd: Marks if the OPP table belongs to a genpd.
 * @set_opp: Platform specific set_opp callback
@@ -189,7 +191,7 @@ struct opp_table {
	const char *prop_name;
	struct clk *clk;
	struct regulator **regulators;
	unsigned int regulator_count;
	int regulator_count;
	bool genpd_performance_state;
	bool is_genpd;

+6 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ struct genpd_power_state {

struct genpd_lock_ops;
struct dev_pm_opp;
struct opp_table;

struct generic_pm_domain {
	struct device dev;
@@ -94,6 +95,7 @@ struct generic_pm_domain {
	unsigned int performance_state;	/* Aggregated max performance state */
	int (*power_off)(struct generic_pm_domain *domain);
	int (*power_on)(struct generic_pm_domain *domain);
	struct opp_table *opp_table;	/* OPP table of the genpd */
	unsigned int (*opp_to_performance_state)(struct generic_pm_domain *genpd,
						 struct dev_pm_opp *opp);
	int (*set_performance_state)(struct generic_pm_domain *genpd,
@@ -134,6 +136,10 @@ struct gpd_link {
	struct list_head master_node;
	struct generic_pm_domain *slave;
	struct list_head slave_node;

	/* Sub-domain's per-master domain performance state */
	unsigned int performance_state;
	unsigned int prev_performance_state;
};

struct gpd_timing_data {
Loading