Commit f0f6dbaf authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files
Pull OPP (Operating Performance Points) updates for 5.11-rc1 from
Viresh Kumar:

"This contains the following updates:

 - Allow empty (node-less) OPP tables in DT for passing just the
   dependency related information (Nicola Mazzucato).

 - Fix a potential lockdep in OPP core and other OPP core cleanups
   (Viresh Kumar).

 - Don't abuse dev_pm_opp_get_opp_table() to create an OPP table, fix
   cpufreq-dt driver for the same (Viresh Kumar).

 - dev_pm_opp_put_regulators() accepts a NULL argument now, updates to
   all the users as well (Viresh Kumar)."

* 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  opp: of: Allow empty opp-table with opp-shared
  dt-bindings: opp: Allow empty OPP tables
  media: venus: dev_pm_opp_put_*() accepts NULL argument
  drm/panfrost: dev_pm_opp_put_*() accepts NULL argument
  drm/lima: dev_pm_opp_put_*() accepts NULL argument
  PM / devfreq: exynos: dev_pm_opp_put_*() accepts NULL argument
  cpufreq: qcom-cpufreq-nvmem: dev_pm_opp_put_*() accepts NULL argument
  cpufreq: dt: dev_pm_opp_put_regulators() accepts NULL argument
  opp: Allow dev_pm_opp_put_*() APIs to accept NULL opp_table
  opp: Don't create an OPP table from dev_pm_opp_get_opp_table()
  cpufreq: dt: Don't (ab)use dev_pm_opp_get_opp_table() to create OPP table
  opp: Reduce the size of critical section in _opp_kref_release()
  opp: Don't return opp_dev from _find_opp_dev()
  opp: Allocate the OPP table outside of opp_table_lock
  opp: Always add entries in dev_list with opp_table->lock held
parents 0477e928 2c07b0fd
Loading
Loading
Loading
Loading
+53 −1
Original line number Diff line number Diff line
@@ -65,7 +65,9 @@ Required properties:

- OPP nodes: One or more OPP nodes describing voltage-current-frequency
  combinations. Their name isn't significant but their phandle can be used to
  reference an OPP.
  reference an OPP. These are mandatory except for the case where the OPP table
  is present only to indicate dependency between devices using the opp-shared
  property.

Optional properties:
- opp-shared: Indicates that device nodes using this OPP Table Node's phandle
@@ -568,3 +570,53 @@ Example 6: opp-microvolt-<name>, opp-microamp-<name>:
		};
	};
};

Example 7: Single cluster Quad-core ARM cortex A53, OPP points from firmware,
distinct clock controls but two sets of clock/voltage/current lines.

/ {
	cpus {
		#address-cells = <2>;
		#size-cells = <0>;

		cpu@0 {
			compatible = "arm,cortex-a53";
			reg = <0x0 0x100>;
			next-level-cache = <&A53_L2>;
			clocks = <&dvfs_controller 0>;
			operating-points-v2 = <&cpu_opp0_table>;
		};
		cpu@1 {
			compatible = "arm,cortex-a53";
			reg = <0x0 0x101>;
			next-level-cache = <&A53_L2>;
			clocks = <&dvfs_controller 1>;
			operating-points-v2 = <&cpu_opp0_table>;
		};
		cpu@2 {
			compatible = "arm,cortex-a53";
			reg = <0x0 0x102>;
			next-level-cache = <&A53_L2>;
			clocks = <&dvfs_controller 2>;
			operating-points-v2 = <&cpu_opp1_table>;
		};
		cpu@3 {
			compatible = "arm,cortex-a53";
			reg = <0x0 0x103>;
			next-level-cache = <&A53_L2>;
			clocks = <&dvfs_controller 3>;
			operating-points-v2 = <&cpu_opp1_table>;
		};

	};

	cpu_opp0_table: opp0_table {
		compatible = "operating-points-v2";
		opp-shared;
	};

	cpu_opp1_table: opp1_table {
		compatible = "operating-points-v2";
		opp-shared;
	};
};
+1 −1
Original line number Diff line number Diff line
@@ -2249,7 +2249,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
			 * Save table for faster processing while setting
			 * performance state.
			 */
			genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
			genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
			WARN_ON(IS_ERR(genpd->opp_table));
		}

+66 −89
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ struct private_data {
	cpumask_var_t cpus;
	struct device *cpu_dev;
	struct opp_table *opp_table;
	struct opp_table *reg_opp_table;
	struct cpufreq_frequency_table *freq_table;
	bool have_static_opps;
};

@@ -102,7 +102,6 @@ node_put:

static int cpufreq_init(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *freq_table;
	struct private_data *priv;
	struct device *cpu_dev;
	struct clk *cpu_clk;
@@ -114,9 +113,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
		pr_err("failed to find data for cpu%d\n", policy->cpu);
		return -ENODEV;
	}

	cpu_dev = priv->cpu_dev;
	cpumask_copy(policy->cpus, priv->cpus);

	cpu_clk = clk_get(cpu_dev, NULL);
	if (IS_ERR(cpu_clk)) {
@@ -125,67 +122,32 @@ static int cpufreq_init(struct cpufreq_policy *policy)
		return ret;
	}

	/*
	 * Initialize OPP tables for all policy->cpus. They will be shared by
	 * all CPUs which have marked their CPUs shared with OPP bindings.
	 *
	 * For platforms not using operating-points-v2 bindings, we do this
	 * before updating policy->cpus. Otherwise, we will end up creating
	 * duplicate OPPs for policy->cpus.
	 *
	 * OPPs might be populated at runtime, don't check for error here
	 */
	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
	 * give platform code chance to provide it for us.
	 */
	ret = dev_pm_opp_get_opp_count(cpu_dev);
	if (ret <= 0) {
		dev_err(cpu_dev, "OPP table can't be empty\n");
		ret = -ENODEV;
		goto out_free_opp;
	}

	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_opp;
	}
	transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
	if (!transition_latency)
		transition_latency = CPUFREQ_ETERNAL;

	cpumask_copy(policy->cpus, priv->cpus);
	policy->driver_data = priv;
	policy->clk = cpu_clk;
	policy->freq_table = freq_table;

	policy->freq_table = priv->freq_table;
	policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
	policy->cpuinfo.transition_latency = transition_latency;
	policy->dvfs_possible_from_any_cpu = true;

	/* Support turbo/boost mode */
	if (policy_has_boost_freq(policy)) {
		/* This gets disabled by core on driver unregister */
		ret = cpufreq_enable_boost_support();
		if (ret)
			goto out_free_cpufreq_table;
			goto out_clk_put;
		cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
	}

	transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
	if (!transition_latency)
		transition_latency = CPUFREQ_ETERNAL;

	policy->cpuinfo.transition_latency = transition_latency;
	policy->dvfs_possible_from_any_cpu = true;

	dev_pm_opp_of_register_em(cpu_dev, policy->cpus);

	return 0;

out_free_cpufreq_table:
	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_opp:
	if (priv->have_static_opps)
		dev_pm_opp_of_cpumask_remove_table(policy->cpus);
out_clk_put:
	clk_put(cpu_clk);

	return ret;
@@ -208,11 +170,6 @@ static int cpufreq_offline(struct cpufreq_policy *policy)

static int cpufreq_exit(struct cpufreq_policy *policy)
{
	struct private_data *priv = policy->driver_data;

	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);
	clk_put(policy->clk);
	return 0;
}
@@ -236,6 +193,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
{
	struct private_data *priv;
	struct device *cpu_dev;
	bool fallback = false;
	const char *reg_name;
	int ret;

@@ -254,68 +212,86 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
	if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
		return -ENOMEM;

	cpumask_set_cpu(cpu, priv->cpus);
	priv->cpu_dev = cpu_dev;

	/* Try to get OPP table early to ensure resources are available */
	priv->opp_table = dev_pm_opp_get_opp_table(cpu_dev);
	if (IS_ERR(priv->opp_table)) {
		ret = PTR_ERR(priv->opp_table);
		if (ret != -EPROBE_DEFER)
			dev_err(cpu_dev, "failed to get OPP table: %d\n", ret);
		goto free_cpumask;
	}

	/*
	 * OPP layer will be taking care of regulators now, but it needs to know
	 * the name of the regulator first.
	 */
	reg_name = find_supply_name(cpu_dev);
	if (reg_name) {
		priv->reg_opp_table = dev_pm_opp_set_regulators(cpu_dev,
								&reg_name, 1);
		if (IS_ERR(priv->reg_opp_table)) {
			ret = PTR_ERR(priv->reg_opp_table);
		priv->opp_table = dev_pm_opp_set_regulators(cpu_dev, &reg_name,
							    1);
		if (IS_ERR(priv->opp_table)) {
			ret = PTR_ERR(priv->opp_table);
			if (ret != -EPROBE_DEFER)
				dev_err(cpu_dev, "failed to set regulators: %d\n",
					ret);
			goto put_table;
			goto free_cpumask;
		}
	}

	/* Find OPP sharing information so we can fill pri->cpus here */
	/* Get OPP-sharing information from "operating-points-v2" bindings */
	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
	if (ret) {
		if (ret != -ENOENT)
			goto put_reg;
			goto out;

		/*
		 * operating-points-v2 not supported, fallback to all CPUs share
		 * OPP for backward compatibility if the platform hasn't set
		 * sharing CPUs.
		 */
		if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus)) {
			cpumask_setall(priv->cpus);
		if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus))
			fallback = true;
	}

	/*
	 * Initialize OPP tables for all priv->cpus. They will be shared by
	 * all CPUs which have marked their CPUs shared with OPP bindings.
	 *
	 * For platforms not using operating-points-v2 bindings, we do this
	 * before updating priv->cpus. Otherwise, we will end up creating
	 * duplicate OPPs for the CPUs.
	 *
	 * OPPs might be populated at runtime, don't check for error here.
	 */
	if (!dev_pm_opp_of_cpumask_add_table(priv->cpus))
		priv->have_static_opps = true;

	/*
			 * OPP tables are initialized only for cpu, do it for
			 * others as well.
	 * The OPP table must be initialized, statically or dynamically, by this
	 * point.
	 */
	ret = dev_pm_opp_get_opp_count(cpu_dev);
	if (ret <= 0) {
		dev_err(cpu_dev, "OPP table can't be empty\n");
		ret = -ENODEV;
		goto out;
	}

	if (fallback) {
		cpumask_setall(priv->cpus);
		ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
		if (ret)
			dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
				__func__, ret);
	}

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

	list_add(&priv->node, &priv_list);
	return 0;

put_reg:
	if (priv->reg_opp_table)
		dev_pm_opp_put_regulators(priv->reg_opp_table);
put_table:
	dev_pm_opp_put_opp_table(priv->opp_table);
out:
	if (priv->have_static_opps)
		dev_pm_opp_of_cpumask_remove_table(priv->cpus);
	dev_pm_opp_put_regulators(priv->opp_table);
free_cpumask:
	free_cpumask_var(priv->cpus);
	return ret;
@@ -326,9 +302,10 @@ static void dt_cpufreq_release(void)
	struct private_data *priv, *tmp;

	list_for_each_entry_safe(priv, tmp, &priv_list, node) {
		if (priv->reg_opp_table)
			dev_pm_opp_put_regulators(priv->reg_opp_table);
		dev_pm_opp_put_opp_table(priv->opp_table);
		dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
		if (priv->have_static_opps)
			dev_pm_opp_of_cpumask_remove_table(priv->cpus);
		dev_pm_opp_put_regulators(priv->opp_table);
		free_cpumask_var(priv->cpus);
		list_del(&priv->node);
	}
+6 −9
Original line number Diff line number Diff line
@@ -397,19 +397,19 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)

free_genpd_opp:
	for_each_possible_cpu(cpu) {
		if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu]))
		if (IS_ERR(drv->genpd_opp_tables[cpu]))
			break;
		dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
	}
	kfree(drv->genpd_opp_tables);
free_opp:
	for_each_possible_cpu(cpu) {
		if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu]))
		if (IS_ERR(drv->names_opp_tables[cpu]))
			break;
		dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
	}
	for_each_possible_cpu(cpu) {
		if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu]))
		if (IS_ERR(drv->hw_opp_tables[cpu]))
			break;
		dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
	}
@@ -430,11 +430,8 @@ static int qcom_cpufreq_remove(struct platform_device *pdev)
	platform_device_unregister(cpufreq_dt_pdev);

	for_each_possible_cpu(cpu) {
		if (drv->names_opp_tables[cpu])
		dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
		if (drv->hw_opp_tables[cpu])
		dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
		if (drv->genpd_opp_tables[cpu])
		dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
	}

+4 −8
Original line number Diff line number Diff line
@@ -158,11 +158,9 @@ static void exynos_bus_exit(struct device *dev)

	dev_pm_opp_of_remove_table(dev);
	clk_disable_unprepare(bus->clk);
	if (bus->opp_table) {
	dev_pm_opp_put_regulators(bus->opp_table);
	bus->opp_table = NULL;
}
}

static void exynos_bus_passive_exit(struct device *dev)
{
@@ -444,10 +442,8 @@ err:
	dev_pm_opp_of_remove_table(dev);
	clk_disable_unprepare(bus->clk);
err_reg:
	if (!passive) {
	dev_pm_opp_put_regulators(bus->opp_table);
	bus->opp_table = NULL;
	}

	return ret;
}
Loading