Commit 815fb87b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull power management fixes from Rafael Wysocki:
 "These fix issues in two cpufreq drivers, in the AMD P-state driver and
  in the power-capping DTPM framework.

  Specifics:

   - Fix the AMD P-state driver's EPP sysfs interface in the cases when
     the performance governor is in use (Ayush Jain)

   - Make the ->fast_switch() callback in the AMD P-state driver return
     the target frequency as expected (Gautham R. Shenoy)

   - Allow user space to control the range of frequencies to use via
     scaling_min_freq and scaling_max_freq when AMD P-state driver is in
     use (Wyes Karny)

   - Prevent power domains needed for wakeup signaling from being turned
     off during system suspend on Qualcomm systems and prevent
     performance states votes from runtime-suspended devices from being
     lost across a system suspend-resume cycle in qcom-cpufreq-nvmem
     (Stephan Gerhold)

   - Fix disabling the 792 Mhz OPP in the imx6q cpufreq driver for the
     i.MX6ULL types that can run at that frequency (Christoph
     Niedermaier)

   - Eliminate unnecessary and harmful conversions to uW from the DTPM
     (dynamic thermal and power management) framework (Lukasz Luba)"

* tag 'pm-6.7-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  cpufreq/amd-pstate: Only print supported EPP values for performance governor
  cpufreq/amd-pstate: Fix scaling_min_freq and scaling_max_freq update
  powercap: DTPM: Fix unneeded conversions to micro-Watts
  cpufreq/amd-pstate: Fix the return value of amd_pstate_fast_switch()
  pmdomain: qcom: rpmpd: Set GENPD_FLAG_ACTIVE_WAKEUP
  cpufreq: qcom-nvmem: Preserve PM domain votes in system suspend
  cpufreq: qcom-nvmem: Enable virtual power domain devices
  cpufreq: imx6q: Don't disable 792 Mhz OPP unnecessarily
parents ce474ae7 a6b31256
Loading
Loading
Loading
Loading
+56 −15
Original line number Diff line number Diff line
@@ -307,11 +307,11 @@ static int pstate_init_perf(struct amd_cpudata *cpudata)
		highest_perf = AMD_CPPC_HIGHEST_PERF(cap1);

	WRITE_ONCE(cpudata->highest_perf, highest_perf);

	WRITE_ONCE(cpudata->max_limit_perf, highest_perf);
	WRITE_ONCE(cpudata->nominal_perf, AMD_CPPC_NOMINAL_PERF(cap1));
	WRITE_ONCE(cpudata->lowest_nonlinear_perf, AMD_CPPC_LOWNONLIN_PERF(cap1));
	WRITE_ONCE(cpudata->lowest_perf, AMD_CPPC_LOWEST_PERF(cap1));

	WRITE_ONCE(cpudata->min_limit_perf, AMD_CPPC_LOWEST_PERF(cap1));
	return 0;
}

@@ -329,11 +329,12 @@ static int cppc_init_perf(struct amd_cpudata *cpudata)
		highest_perf = cppc_perf.highest_perf;

	WRITE_ONCE(cpudata->highest_perf, highest_perf);

	WRITE_ONCE(cpudata->max_limit_perf, highest_perf);
	WRITE_ONCE(cpudata->nominal_perf, cppc_perf.nominal_perf);
	WRITE_ONCE(cpudata->lowest_nonlinear_perf,
		   cppc_perf.lowest_nonlinear_perf);
	WRITE_ONCE(cpudata->lowest_perf, cppc_perf.lowest_perf);
	WRITE_ONCE(cpudata->min_limit_perf, cppc_perf.lowest_perf);

	if (cppc_state == AMD_PSTATE_ACTIVE)
		return 0;
@@ -432,6 +433,10 @@ static void amd_pstate_update(struct amd_cpudata *cpudata, u32 min_perf,
	u64 prev = READ_ONCE(cpudata->cppc_req_cached);
	u64 value = prev;

	min_perf = clamp_t(unsigned long, min_perf, cpudata->min_limit_perf,
			cpudata->max_limit_perf);
	max_perf = clamp_t(unsigned long, max_perf, cpudata->min_limit_perf,
			cpudata->max_limit_perf);
	des_perf = clamp_t(unsigned long, des_perf, min_perf, max_perf);

	if ((cppc_state == AMD_PSTATE_GUIDED) && (gov_flags & CPUFREQ_GOV_DYNAMIC_SWITCHING)) {
@@ -470,6 +475,22 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy)
	return 0;
}

static int amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
{
	u32 max_limit_perf, min_limit_perf;
	struct amd_cpudata *cpudata = policy->driver_data;

	max_limit_perf = div_u64(policy->max * cpudata->highest_perf, cpudata->max_freq);
	min_limit_perf = div_u64(policy->min * cpudata->highest_perf, cpudata->max_freq);

	WRITE_ONCE(cpudata->max_limit_perf, max_limit_perf);
	WRITE_ONCE(cpudata->min_limit_perf, min_limit_perf);
	WRITE_ONCE(cpudata->max_limit_freq, policy->max);
	WRITE_ONCE(cpudata->min_limit_freq, policy->min);

	return 0;
}

static int amd_pstate_update_freq(struct cpufreq_policy *policy,
				  unsigned int target_freq, bool fast_switch)
{
@@ -480,6 +501,9 @@ static int amd_pstate_update_freq(struct cpufreq_policy *policy,
	if (!cpudata->max_freq)
		return -ENODEV;

	if (policy->min != cpudata->min_limit_freq || policy->max != cpudata->max_limit_freq)
		amd_pstate_update_min_max_limit(policy);

	cap_perf = READ_ONCE(cpudata->highest_perf);
	min_perf = READ_ONCE(cpudata->lowest_perf);
	max_perf = cap_perf;
@@ -518,7 +542,9 @@ static int amd_pstate_target(struct cpufreq_policy *policy,
static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
				  unsigned int target_freq)
{
	return amd_pstate_update_freq(policy, target_freq, true);
	if (!amd_pstate_update_freq(policy, target_freq, true))
		return target_freq;
	return policy->cur;
}

static void amd_pstate_adjust_perf(unsigned int cpu,
@@ -532,6 +558,10 @@ static void amd_pstate_adjust_perf(unsigned int cpu,
	struct amd_cpudata *cpudata = policy->driver_data;
	unsigned int target_freq;

	if (policy->min != cpudata->min_limit_freq || policy->max != cpudata->max_limit_freq)
		amd_pstate_update_min_max_limit(policy);


	cap_perf = READ_ONCE(cpudata->highest_perf);
	lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
	max_freq = READ_ONCE(cpudata->max_freq);
@@ -745,6 +775,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
	/* Initial processor data capability frequencies */
	cpudata->max_freq = max_freq;
	cpudata->min_freq = min_freq;
	cpudata->max_limit_freq = max_freq;
	cpudata->min_limit_freq = min_freq;
	cpudata->nominal_freq = nominal_freq;
	cpudata->lowest_nonlinear_freq = lowest_nonlinear_freq;

@@ -850,11 +882,16 @@ static ssize_t show_energy_performance_available_preferences(
{
	int i = 0;
	int offset = 0;
	struct amd_cpudata *cpudata = policy->driver_data;

	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
		return sysfs_emit_at(buf, offset, "%s\n",
				energy_perf_strings[EPP_INDEX_PERFORMANCE]);

	while (energy_perf_strings[i] != NULL)
		offset += sysfs_emit_at(buf, offset, "%s ", energy_perf_strings[i++]);

	sysfs_emit_at(buf, offset, "\n");
	offset += sysfs_emit_at(buf, offset, "\n");

	return offset;
}
@@ -1183,16 +1220,25 @@ static int amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
	return 0;
}

static void amd_pstate_epp_init(unsigned int cpu)
static void amd_pstate_epp_update_limit(struct cpufreq_policy *policy)
{
	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
	struct amd_cpudata *cpudata = policy->driver_data;
	u32 max_perf, min_perf;
	u32 max_perf, min_perf, min_limit_perf, max_limit_perf;
	u64 value;
	s16 epp;

	max_perf = READ_ONCE(cpudata->highest_perf);
	min_perf = READ_ONCE(cpudata->lowest_perf);
	max_limit_perf = div_u64(policy->max * cpudata->highest_perf, cpudata->max_freq);
	min_limit_perf = div_u64(policy->min * cpudata->highest_perf, cpudata->max_freq);

	max_perf = clamp_t(unsigned long, max_perf, cpudata->min_limit_perf,
			cpudata->max_limit_perf);
	min_perf = clamp_t(unsigned long, min_perf, cpudata->min_limit_perf,
			cpudata->max_limit_perf);

	WRITE_ONCE(cpudata->max_limit_perf, max_limit_perf);
	WRITE_ONCE(cpudata->min_limit_perf, min_limit_perf);

	value = READ_ONCE(cpudata->cppc_req_cached);

@@ -1210,9 +1256,6 @@ static void amd_pstate_epp_init(unsigned int cpu)
	value &= ~AMD_CPPC_DES_PERF(~0L);
	value |= AMD_CPPC_DES_PERF(0);

	if (cpudata->epp_policy == cpudata->policy)
		goto skip_epp;

	cpudata->epp_policy = cpudata->policy;

	/* Get BIOS pre-defined epp value */
@@ -1222,7 +1265,7 @@ static void amd_pstate_epp_init(unsigned int cpu)
		 * This return value can only be negative for shared_memory
		 * systems where EPP register read/write not supported.
		 */
		goto skip_epp;
		return;
	}

	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
@@ -1236,8 +1279,6 @@ static void amd_pstate_epp_init(unsigned int cpu)

	WRITE_ONCE(cpudata->cppc_req_cached, value);
	amd_pstate_set_epp(cpudata, epp);
skip_epp:
	cpufreq_cpu_put(policy);
}

static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)
@@ -1252,7 +1293,7 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)

	cpudata->policy = policy->policy;

	amd_pstate_epp_init(policy->cpu);
	amd_pstate_epp_update_limit(policy);

	return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -327,7 +327,7 @@ static int imx6ul_opp_check_speed_grading(struct device *dev)
			imx6x_disable_freq_in_opp(dev, 696000000);

	if (of_machine_is_compatible("fsl,imx6ull")) {
		if (val != OCOTP_CFG3_6ULL_SPEED_792MHZ)
		if (val < OCOTP_CFG3_6ULL_SPEED_792MHZ)
			imx6x_disable_freq_in_opp(dev, 792000000);

		if (val != OCOTP_CFG3_6ULL_SPEED_900MHZ)
+70 −3
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smem.h>

@@ -55,6 +57,7 @@ struct qcom_cpufreq_match_data {

struct qcom_cpufreq_drv_cpu {
	int opp_token;
	struct device **virt_devs;
};

struct qcom_cpufreq_drv {
@@ -424,6 +427,30 @@ static const struct qcom_cpufreq_match_data match_data_ipq8074 = {
	.get_version = qcom_cpufreq_ipq8074_name_version,
};

static void qcom_cpufreq_suspend_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu)
{
	const char * const *name = drv->data->genpd_names;
	int i;

	if (!drv->cpus[cpu].virt_devs)
		return;

	for (i = 0; *name; i++, name++)
		device_set_awake_path(drv->cpus[cpu].virt_devs[i]);
}

static void qcom_cpufreq_put_virt_devs(struct qcom_cpufreq_drv *drv, unsigned int cpu)
{
	const char * const *name = drv->data->genpd_names;
	int i;

	if (!drv->cpus[cpu].virt_devs)
		return;

	for (i = 0; *name; i++, name++)
		pm_runtime_put(drv->cpus[cpu].virt_devs[i]);
}

static int qcom_cpufreq_probe(struct platform_device *pdev)
{
	struct qcom_cpufreq_drv *drv;
@@ -478,6 +505,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
	of_node_put(np);

	for_each_possible_cpu(cpu) {
		struct device **virt_devs = NULL;
		struct dev_pm_opp_config config = {
			.supported_hw = NULL,
		};
@@ -498,7 +526,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)

		if (drv->data->genpd_names) {
			config.genpd_names = drv->data->genpd_names;
			config.virt_devs = NULL;
			config.virt_devs = &virt_devs;
		}

		if (config.supported_hw || config.genpd_names) {
@@ -509,6 +537,27 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
				goto free_opp;
			}
		}

		if (virt_devs) {
			const char * const *name = config.genpd_names;
			int i, j;

			for (i = 0; *name; i++, name++) {
				ret = pm_runtime_resume_and_get(virt_devs[i]);
				if (ret) {
					dev_err(cpu_dev, "failed to resume %s: %d\n",
						*name, ret);

					/* Rollback previous PM runtime calls */
					name = config.genpd_names;
					for (j = 0; *name && j < i; j++, name++)
						pm_runtime_put(virt_devs[j]);

					goto free_opp;
				}
			}
			drv->cpus[cpu].virt_devs = virt_devs;
		}
	}

	cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
@@ -522,8 +571,10 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
	dev_err(cpu_dev, "Failed to register platform device\n");

free_opp:
	for_each_possible_cpu(cpu)
	for_each_possible_cpu(cpu) {
		qcom_cpufreq_put_virt_devs(drv, cpu);
		dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
	}
	return ret;
}

@@ -534,15 +585,31 @@ static void qcom_cpufreq_remove(struct platform_device *pdev)

	platform_device_unregister(cpufreq_dt_pdev);

	for_each_possible_cpu(cpu)
	for_each_possible_cpu(cpu) {
		qcom_cpufreq_put_virt_devs(drv, cpu);
		dev_pm_opp_clear_config(drv->cpus[cpu].opp_token);
	}
}

static int qcom_cpufreq_suspend(struct device *dev)
{
	struct qcom_cpufreq_drv *drv = dev_get_drvdata(dev);
	unsigned int cpu;

	for_each_possible_cpu(cpu)
		qcom_cpufreq_suspend_virt_devs(drv, cpu);

	return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(qcom_cpufreq_pm_ops, qcom_cpufreq_suspend, NULL);

static struct platform_driver qcom_cpufreq_driver = {
	.probe = qcom_cpufreq_probe,
	.remove_new = qcom_cpufreq_remove,
	.driver = {
		.name = "qcom-cpufreq-nvmem",
		.pm = pm_sleep_ptr(&qcom_cpufreq_pm_ops),
	},
};

+1 −0
Original line number Diff line number Diff line
@@ -1044,6 +1044,7 @@ static int rpmpd_probe(struct platform_device *pdev)
		rpmpds[i]->pd.power_off = rpmpd_power_off;
		rpmpds[i]->pd.power_on = rpmpd_power_on;
		rpmpds[i]->pd.set_performance_state = rpmpd_set_performance;
		rpmpds[i]->pd.flags = GENPD_FLAG_ACTIVE_WAKEUP;
		pm_genpd_init(&rpmpds[i]->pd, NULL, true);

		data->domains[i] = &rpmpds[i]->pd;
+1 −5
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@
#include <linux/of.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/units.h>

struct dtpm_cpu {
	struct dtpm dtpm;
@@ -104,8 +103,7 @@ static u64 get_pd_power_uw(struct dtpm *dtpm)
		if (pd->table[i].frequency < freq)
			continue;

		return scale_pd_power_uw(pd_mask, pd->table[i].power *
					 MICROWATT_PER_MILLIWATT);
		return scale_pd_power_uw(pd_mask, pd->table[i].power);
	}

	return 0;
@@ -122,11 +120,9 @@ static int update_pd_power_uw(struct dtpm *dtpm)
	nr_cpus = cpumask_weight(&cpus);

	dtpm->power_min = em->table[0].power;
	dtpm->power_min *= MICROWATT_PER_MILLIWATT;
	dtpm->power_min *= nr_cpus;

	dtpm->power_max = em->table[em->nr_perf_states - 1].power;
	dtpm->power_max *= MICROWATT_PER_MILLIWATT;
	dtpm->power_max *= nr_cpus;

	return 0;
Loading