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

Commit 41de256b authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files
Pull operating performance points (OPP) framework changes for v5.3
from Viresh Kumar:

"This pull request contains:

 - OPP core changes to support a wider range of devices, like IO
   devices (Rajendra Nayak and Stehpen Boyd).
 - Fixes around genpd_virt_devs (Viresh Kumar).
 - Fix for platform with set_opp() callback (Dmitry Osipenko)."

* 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  opp: Don't use IS_ERR on invalid supplies
  opp: Make dev_pm_opp_set_rate() handle freq = 0 to drop performance votes
  opp: Don't overwrite rounded clk rate
  opp: Allocate genpd_virt_devs from dev_pm_opp_attach_genpd()
  opp: Attach genpds to devices from within OPP core
parents 4b972a01 560d1bca
Loading
Loading
Loading
Loading
+115 −59
Original line number Diff line number Diff line
@@ -682,7 +682,7 @@ static int _set_opp_custom(const struct opp_table *opp_table,

	data->old_opp.rate = old_freq;
	size = sizeof(*old_supply) * opp_table->regulator_count;
	if (IS_ERR(old_supply))
	if (!old_supply)
		memset(data->old_opp.supplies, 0, size);
	else
		memcpy(data->old_opp.supplies, old_supply, size);
@@ -708,7 +708,7 @@ static int _set_required_opps(struct device *dev,

	/* Single genpd case */
	if (!genpd_virt_devs) {
		pstate = opp->required_opps[0]->pstate;
		pstate = likely(opp) ? opp->required_opps[0]->pstate : 0;
		ret = dev_pm_genpd_set_performance_state(dev, pstate);
		if (ret) {
			dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
@@ -726,7 +726,7 @@ static int _set_required_opps(struct device *dev,
	mutex_lock(&opp_table->genpd_virt_dev_lock);

	for (i = 0; i < opp_table->required_opp_count; i++) {
		pstate = opp->required_opps[i]->pstate;
		pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;

		if (!genpd_virt_devs[i])
			continue;
@@ -748,29 +748,37 @@ static int _set_required_opps(struct device *dev,
 * @dev:	 device for which we do this operation
 * @target_freq: frequency to achieve
 *
 * This configures the power-supplies and clock source to the levels specified
 * by the OPP corresponding to the target_freq.
 * This configures the power-supplies to the levels specified by the OPP
 * corresponding to the target_freq, and programs the clock to a value <=
 * target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax
 * provided by the opp, should have already rounded to the target OPP's
 * frequency.
 */
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
	struct opp_table *opp_table;
	unsigned long freq, old_freq;
	unsigned long freq, old_freq, temp_freq;
	struct dev_pm_opp *old_opp, *opp;
	struct clk *clk;
	int ret;

	if (unlikely(!target_freq)) {
		dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
			target_freq);
		return -EINVAL;
	}

	opp_table = _find_opp_table(dev);
	if (IS_ERR(opp_table)) {
		dev_err(dev, "%s: device opp doesn't exist\n", __func__);
		return PTR_ERR(opp_table);
	}

	if (unlikely(!target_freq)) {
		if (opp_table->required_opp_tables) {
			ret = _set_required_opps(dev, opp_table, NULL);
		} else {
			dev_err(dev, "target frequency can't be 0\n");
			ret = -EINVAL;
		}

		goto put_opp_table;
	}

	clk = opp_table->clk;
	if (IS_ERR(clk)) {
		dev_err(dev, "%s: No clock available for the device\n",
@@ -793,13 +801,15 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
		goto put_opp_table;
	}

	old_opp = _find_freq_ceil(opp_table, &old_freq);
	temp_freq = old_freq;
	old_opp = _find_freq_ceil(opp_table, &temp_freq);
	if (IS_ERR(old_opp)) {
		dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
			__func__, old_freq, PTR_ERR(old_opp));
	}

	opp = _find_freq_ceil(opp_table, &freq);
	temp_freq = freq;
	opp = _find_freq_ceil(opp_table, &temp_freq);
	if (IS_ERR(opp)) {
		ret = PTR_ERR(opp);
		dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
@@ -1741,91 +1751,137 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);

static void _opp_detach_genpd(struct opp_table *opp_table)
{
	int index;

	for (index = 0; index < opp_table->required_opp_count; index++) {
		if (!opp_table->genpd_virt_devs[index])
			continue;

		dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
		opp_table->genpd_virt_devs[index] = NULL;
	}

	kfree(opp_table->genpd_virt_devs);
	opp_table->genpd_virt_devs = NULL;
}

/**
 * dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
 * @dev: Consumer device for which the genpd device is getting set.
 * @virt_dev: virtual genpd device.
 * @index: index.
 * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
 * @dev: Consumer device for which the genpd is getting attached.
 * @names: Null terminated array of pointers containing names of genpd to attach.
 *
 * Multiple generic power domains for a device are supported with the help of
 * virtual genpd devices, which are created for each consumer device - genpd
 * pair. These are the device structures which are attached to the power domain
 * and are required by the OPP core to set the performance state of the genpd.
 * The same API also works for the case where single genpd is available and so
 * we don't need to support that separately.
 *
 * This helper will normally be called by the consumer driver of the device
 * "dev", as only that has details of the genpd devices.
 * "dev", as only that has details of the genpd names.
 *
 * This helper needs to be called once for each of those virtual devices, but
 * only if multiple domains are available for a device. Otherwise the original
 * device structure will be used instead by the OPP core.
 * This helper needs to be called once with a list of all genpd to attach.
 * Otherwise the original device structure will be used instead by the OPP core.
 */
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev,
						struct device *virt_dev,
						int index)
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
{
	struct opp_table *opp_table;
	struct device *virt_dev;
	int index, ret = -EINVAL;
	const char **name = names;

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

	/*
	 * If the genpd's OPP table isn't already initialized, parsing of the
	 * required-opps fail for dev. We should retry this after genpd's OPP
	 * table is added.
	 */
	if (!opp_table->required_opp_count) {
		ret = -EPROBE_DEFER;
		goto put_table;
	}

	mutex_lock(&opp_table->genpd_virt_dev_lock);

	if (unlikely(!opp_table->genpd_virt_devs ||
		     index >= opp_table->required_opp_count ||
		     opp_table->genpd_virt_devs[index])) {
	opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
					     sizeof(*opp_table->genpd_virt_devs),
					     GFP_KERNEL);
	if (!opp_table->genpd_virt_devs)
		goto unlock;

		dev_err(dev, "Invalid request to set required device\n");
		dev_pm_opp_put_opp_table(opp_table);
		mutex_unlock(&opp_table->genpd_virt_dev_lock);
	while (*name) {
		index = of_property_match_string(dev->of_node,
						 "power-domain-names", *name);
		if (index < 0) {
			dev_err(dev, "Failed to find power domain: %s (%d)\n",
				*name, index);
			goto err;
		}

		return ERR_PTR(-EINVAL);
		if (index >= opp_table->required_opp_count) {
			dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
				*name, opp_table->required_opp_count, index);
			goto err;
		}

		if (opp_table->genpd_virt_devs[index]) {
			dev_err(dev, "Genpd virtual device already set %s\n",
				*name);
			goto err;
		}

		virt_dev = dev_pm_domain_attach_by_name(dev, *name);
		if (IS_ERR(virt_dev)) {
			ret = PTR_ERR(virt_dev);
			dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret);
			goto err;
		}

		opp_table->genpd_virt_devs[index] = virt_dev;
		name++;
	}

	mutex_unlock(&opp_table->genpd_virt_dev_lock);

	return opp_table;

err:
	_opp_detach_genpd(opp_table);
unlock:
	mutex_unlock(&opp_table->genpd_virt_dev_lock);

put_table:
	dev_pm_opp_put_opp_table(opp_table);

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

/**
 * dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
 * @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
 * @virt_dev: virtual genpd device.
 *
 * This releases the resource previously acquired with a call to
 * dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
 * if it doesn't want OPP core to update performance state of a power domain
 * anymore.
 * dev_pm_opp_detach_genpd() - Detach genpd(s) from the device.
 * @opp_table: OPP table returned by dev_pm_opp_attach_genpd().
 *
 * This detaches the genpd(s), resets the virtual device pointers, and puts the
 * OPP table.
 */
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
				   struct device *virt_dev)
void dev_pm_opp_detach_genpd(struct opp_table *opp_table)
{
	int i;

	/*
	 * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
	 * used in parallel.
	 */
	mutex_lock(&opp_table->genpd_virt_dev_lock);

	for (i = 0; i < opp_table->required_opp_count; i++) {
		if (opp_table->genpd_virt_devs[i] != virt_dev)
			continue;

		opp_table->genpd_virt_devs[i] = NULL;
		dev_pm_opp_put_opp_table(opp_table);

		/* Drop the vote */
		dev_pm_genpd_set_performance_state(virt_dev, 0);
		break;
	}

	_opp_detach_genpd(opp_table);
	mutex_unlock(&opp_table->genpd_virt_dev_lock);

	if (unlikely(i == opp_table->required_opp_count))
		dev_err(virt_dev, "Failed to find required device entry\n");
	dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd);

/**
 * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
+2 −28
Original line number Diff line number Diff line
@@ -138,7 +138,6 @@ static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
static void _opp_table_free_required_tables(struct opp_table *opp_table)
{
	struct opp_table **required_opp_tables = opp_table->required_opp_tables;
	struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
	int i;

	if (!required_opp_tables)
@@ -152,10 +151,8 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
	}

	kfree(required_opp_tables);
	kfree(genpd_virt_devs);

	opp_table->required_opp_count = 0;
	opp_table->genpd_virt_devs = NULL;
	opp_table->required_opp_tables = NULL;
}

@@ -168,9 +165,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
					     struct device_node *opp_np)
{
	struct opp_table **required_opp_tables;
	struct device **genpd_virt_devs = NULL;
	struct device_node *required_np, *np;
	int count, count_pd, i;
	int count, i;

	/* Traversing the first OPP node is all we need */
	np = of_get_next_available_child(opp_np, NULL);
@@ -183,33 +179,11 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
	if (!count)
		goto put_np;

	/*
	 * Check the number of power-domains to know if we need to deal
	 * with virtual devices. In some cases we have devices with multiple
	 * power domains but with only one of them being scalable, hence
	 * 'count' could be 1, but we still have to deal with multiple genpds
	 * and virtual devices.
	 */
	count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
					      "#power-domain-cells");
	if (!count_pd)
		goto put_np;

	if (count_pd > 1) {
		genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
					GFP_KERNEL);
		if (!genpd_virt_devs)
			goto put_np;
	}

	required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
				      GFP_KERNEL);
	if (!required_opp_tables) {
		kfree(genpd_virt_devs);
	if (!required_opp_tables)
		goto put_np;
	}

	opp_table->genpd_virt_devs = genpd_virt_devs;
	opp_table->required_opp_tables = required_opp_tables;
	opp_table->required_opp_count = count;

+4 −4
Original line number Diff line number Diff line
@@ -128,8 +128,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
void dev_pm_opp_put_clkname(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
@@ -292,12 +292,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const

static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}

static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index)
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
{
	return ERR_PTR(-ENOTSUPP);
}

static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {}
static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}

static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
{