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

Merge branch 'amd-pstate'

Merge AMD P-state driver changes from Perry Yuan for v6.10:

"- Enable CPPC v2 for certain processors in the family 17H, as requested
   by TR40 processor users who expect improved performance and lower
   system temperature.

 - Change latency and delay values to be read from platform firmware
   firstly for more accurate timing.

 - A new quirk is introduced for supporting amd-pstate on legacy
   processors which either lack CPPC capability, or only only have CPPC
   v2 capability."

* amd-pstate:
  MAINTAINERS: cpufreq: amd-pstate: Add co-maintainers and reviewer
  cpufreq: amd-pstate: remove unused variable lowest_nonlinear_freq
  cpufreq: amd-pstate: fix code format problems
  cpufreq: amd-pstate: Add quirk for the pstate CPPC capabilities missing
  cppc_acpi: print error message if CPPC is unsupported
  cpufreq: amd-pstate: get transition delay and latency value from ACPI tables
  cpufreq: amd-pstate: Bail out if min/max/nominal_freq is 0
  cpufreq: amd-pstate: Remove amd_get_{min,max,nominal,lowest_nonlinear}_freq()
  cpufreq: amd-pstate: Unify computation of {max,min,nominal,lowest_nonlinear}_freq
  cpufreq: amd-pstate: Document the units for freq variables in amd_cpudata
  cpufreq: amd-pstate: Document *_limit_* fields in struct amd_cpudata
parents a2bd1d26 70f83f52
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1062,6 +1062,9 @@ F: drivers/gpu/drm/amd/pm/
AMD PSTATE DRIVER
M:	Huang Rui <ray.huang@amd.com>
M:	Gautham R. Shenoy <gautham.shenoy@amd.com>
M:	Mario Limonciello <mario.limonciello@amd.com>
R:	Perry Yuan <perry.yuan@amd.com>
L:	linux-pm@vger.kernel.org
S:	Supported
F:	Documentation/admin-guide/pm/amd-pstate.rst
+3 −1
Original line number Diff line number Diff line
@@ -686,9 +686,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)

	if (!osc_sb_cppc2_support_acked) {
		pr_debug("CPPC v2 _OSC not acked\n");
		if (!cpc_supported_by_cpu())
		if (!cpc_supported_by_cpu()) {
			pr_debug("CPPC is not supported by the CPU\n");
			return -ENODEV;
		}
	}

	/* Parse the ACPI _CPC table for this CPU. */
	status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output,
+155 −103
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ static struct cpufreq_driver amd_pstate_epp_driver;
static int cppc_state = AMD_PSTATE_UNDEFINED;
static bool cppc_enabled;
static bool amd_pstate_prefcore = true;
static struct quirk_entry *quirks;

/*
 * AMD Energy Preference Performance (EPP)
@@ -111,6 +112,41 @@ static unsigned int epp_values[] = {

typedef int (*cppc_mode_transition_fn)(int);

static struct quirk_entry quirk_amd_7k62 = {
	.nominal_freq = 2600,
	.lowest_freq = 550,
};

static int __init dmi_matched_7k62_bios_bug(const struct dmi_system_id *dmi)
{
	/**
	 * match the broken bios for family 17h processor support CPPC V2
	 * broken BIOS lack of nominal_freq and lowest_freq capabilities
	 * definition in ACPI tables
	 */
	if (boot_cpu_has(X86_FEATURE_ZEN2)) {
		quirks = dmi->driver_data;
		pr_info("Overriding nominal and lowest frequencies for %s\n", dmi->ident);
		return 1;
	}

	return 0;
}

static const struct dmi_system_id amd_pstate_quirks_table[] __initconst = {
	{
		.callback = dmi_matched_7k62_bios_bug,
		.ident = "AMD EPYC 7K62",
		.matches = {
			DMI_MATCH(DMI_BIOS_VERSION, "5.14"),
			DMI_MATCH(DMI_BIOS_RELEASE, "12/12/2019"),
		},
		.driver_data = &quirk_amd_7k62,
	},
	{}
};
MODULE_DEVICE_TABLE(dmi, amd_pstate_quirks_table);

static inline int get_mode_idx_from_str(const char *str, size_t size)
{
	int i;
@@ -604,78 +640,6 @@ static void amd_pstate_adjust_perf(unsigned int cpu,
	cpufreq_cpu_put(policy);
}

static int amd_get_min_freq(struct amd_cpudata *cpudata)
{
	struct cppc_perf_caps cppc_perf;

	int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
	if (ret)
		return ret;

	/* Switch to khz */
	return cppc_perf.lowest_freq * 1000;
}

static int amd_get_max_freq(struct amd_cpudata *cpudata)
{
	struct cppc_perf_caps cppc_perf;
	u32 max_perf, max_freq, nominal_freq, nominal_perf;
	u64 boost_ratio;

	int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
	if (ret)
		return ret;

	nominal_freq = cppc_perf.nominal_freq;
	nominal_perf = READ_ONCE(cpudata->nominal_perf);
	max_perf = READ_ONCE(cpudata->highest_perf);

	boost_ratio = div_u64(max_perf << SCHED_CAPACITY_SHIFT,
			      nominal_perf);

	max_freq = nominal_freq * boost_ratio >> SCHED_CAPACITY_SHIFT;

	/* Switch to khz */
	return max_freq * 1000;
}

static int amd_get_nominal_freq(struct amd_cpudata *cpudata)
{
	struct cppc_perf_caps cppc_perf;

	int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
	if (ret)
		return ret;

	/* Switch to khz */
	return cppc_perf.nominal_freq * 1000;
}

static int amd_get_lowest_nonlinear_freq(struct amd_cpudata *cpudata)
{
	struct cppc_perf_caps cppc_perf;
	u32 lowest_nonlinear_freq, lowest_nonlinear_perf,
	    nominal_freq, nominal_perf;
	u64 lowest_nonlinear_ratio;

	int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
	if (ret)
		return ret;

	nominal_freq = cppc_perf.nominal_freq;
	nominal_perf = READ_ONCE(cpudata->nominal_perf);

	lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;

	lowest_nonlinear_ratio = div_u64(lowest_nonlinear_perf << SCHED_CAPACITY_SHIFT,
					 nominal_perf);

	lowest_nonlinear_freq = nominal_freq * lowest_nonlinear_ratio >> SCHED_CAPACITY_SHIFT;

	/* Switch to khz */
	return lowest_nonlinear_freq * 1000;
}

static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state)
{
	struct amd_cpudata *cpudata = policy->driver_data;
@@ -828,9 +792,93 @@ static void amd_pstate_update_limits(unsigned int cpu)
	mutex_unlock(&amd_pstate_driver_lock);
}

/*
 * Get pstate transition delay time from ACPI tables that firmware set
 * instead of using hardcode value directly.
 */
static u32 amd_pstate_get_transition_delay_us(unsigned int cpu)
{
	u32 transition_delay_ns;

	transition_delay_ns = cppc_get_transition_latency(cpu);
	if (transition_delay_ns == CPUFREQ_ETERNAL)
		return AMD_PSTATE_TRANSITION_DELAY;

	return transition_delay_ns / NSEC_PER_USEC;
}

/*
 * Get pstate transition latency value from ACPI tables that firmware
 * set instead of using hardcode value directly.
 */
static u32 amd_pstate_get_transition_latency(unsigned int cpu)
{
	u32 transition_latency;

	transition_latency = cppc_get_transition_latency(cpu);
	if (transition_latency  == CPUFREQ_ETERNAL)
		return AMD_PSTATE_TRANSITION_LATENCY;

	return transition_latency;
}

/*
 * amd_pstate_init_freq: Initialize the max_freq, min_freq,
 *                       nominal_freq and lowest_nonlinear_freq for
 *                       the @cpudata object.
 *
 *  Requires: highest_perf, lowest_perf, nominal_perf and
 *            lowest_nonlinear_perf members of @cpudata to be
 *            initialized.
 *
 *  Returns 0 on success, non-zero value on failure.
 */
static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
{
	int ret;
	u32 min_freq;
	u32 highest_perf, max_freq;
	u32 nominal_perf, nominal_freq;
	u32 lowest_nonlinear_perf, lowest_nonlinear_freq;
	u32 boost_ratio, lowest_nonlinear_ratio;
	struct cppc_perf_caps cppc_perf;

	ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
	if (ret)
		return ret;

	if (quirks && quirks->lowest_freq)
		min_freq = quirks->lowest_freq * 1000;
	else
		min_freq = cppc_perf.lowest_freq * 1000;

	if (quirks && quirks->nominal_freq)
		nominal_freq = quirks->nominal_freq ;
	else
		nominal_freq = cppc_perf.nominal_freq;

	nominal_perf = READ_ONCE(cpudata->nominal_perf);

	highest_perf = READ_ONCE(cpudata->highest_perf);
	boost_ratio = div_u64(highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
	max_freq = (nominal_freq * boost_ratio >> SCHED_CAPACITY_SHIFT) * 1000;

	lowest_nonlinear_perf = READ_ONCE(cpudata->lowest_nonlinear_perf);
	lowest_nonlinear_ratio = div_u64(lowest_nonlinear_perf << SCHED_CAPACITY_SHIFT,
					 nominal_perf);
	lowest_nonlinear_freq = (nominal_freq * lowest_nonlinear_ratio >> SCHED_CAPACITY_SHIFT) * 1000;

	WRITE_ONCE(cpudata->min_freq, min_freq);
	WRITE_ONCE(cpudata->lowest_nonlinear_freq, lowest_nonlinear_freq);
	WRITE_ONCE(cpudata->nominal_freq, nominal_freq);
	WRITE_ONCE(cpudata->max_freq, max_freq);

	return 0;
}

static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
{
	int min_freq, max_freq, nominal_freq, lowest_nonlinear_freq, ret;
	int min_freq, max_freq, nominal_freq, ret;
	struct device *dev;
	struct amd_cpudata *cpudata;

@@ -855,20 +903,25 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
	if (ret)
		goto free_cpudata1;

	min_freq = amd_get_min_freq(cpudata);
	max_freq = amd_get_max_freq(cpudata);
	nominal_freq = amd_get_nominal_freq(cpudata);
	lowest_nonlinear_freq = amd_get_lowest_nonlinear_freq(cpudata);
	ret = amd_pstate_init_freq(cpudata);
	if (ret)
		goto free_cpudata1;

	min_freq = READ_ONCE(cpudata->min_freq);
	max_freq = READ_ONCE(cpudata->max_freq);
	nominal_freq = READ_ONCE(cpudata->nominal_freq);

	if (min_freq < 0 || max_freq < 0 || min_freq > max_freq) {
		dev_err(dev, "min_freq(%d) or max_freq(%d) value is incorrect\n",
			min_freq, max_freq);
	if (min_freq <= 0 || max_freq <= 0 ||
	    nominal_freq <= 0 || min_freq > max_freq) {
		dev_err(dev,
			"min_freq(%d) or max_freq(%d) or nominal_freq (%d) value is incorrect, check _CPC in ACPI tables\n",
			min_freq, max_freq, nominal_freq);
		ret = -EINVAL;
		goto free_cpudata1;
	}

	policy->cpuinfo.transition_latency = AMD_PSTATE_TRANSITION_LATENCY;
	policy->transition_delay_us = AMD_PSTATE_TRANSITION_DELAY;
	policy->cpuinfo.transition_latency = amd_pstate_get_transition_latency(policy->cpu);
	policy->transition_delay_us = amd_pstate_get_transition_delay_us(policy->cpu);

	policy->min = min_freq;
	policy->max = max_freq;
@@ -896,13 +949,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
		goto free_cpudata2;
	}

	/* 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;

	policy->driver_data = cpudata;

@@ -966,7 +1014,7 @@ static ssize_t show_amd_pstate_max_freq(struct cpufreq_policy *policy,
	int max_freq;
	struct amd_cpudata *cpudata = policy->driver_data;

	max_freq = amd_get_max_freq(cpudata);
	max_freq = READ_ONCE(cpudata->max_freq);
	if (max_freq < 0)
		return max_freq;

@@ -979,7 +1027,7 @@ static ssize_t show_amd_pstate_lowest_nonlinear_freq(struct cpufreq_policy *poli
	int freq;
	struct amd_cpudata *cpudata = policy->driver_data;

	freq = amd_get_lowest_nonlinear_freq(cpudata);
	freq = READ_ONCE(cpudata->lowest_nonlinear_freq);
	if (freq < 0)
		return freq;

@@ -1290,7 +1338,7 @@ static bool amd_pstate_acpi_pm_profile_undefined(void)

static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
{
	int min_freq, max_freq, nominal_freq, lowest_nonlinear_freq, ret;
	int min_freq, max_freq, nominal_freq, ret;
	struct amd_cpudata *cpudata;
	struct device *dev;
	u64 value;
@@ -1317,13 +1365,18 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
	if (ret)
		goto free_cpudata1;

	min_freq = amd_get_min_freq(cpudata);
	max_freq = amd_get_max_freq(cpudata);
	nominal_freq = amd_get_nominal_freq(cpudata);
	lowest_nonlinear_freq = amd_get_lowest_nonlinear_freq(cpudata);
	if (min_freq < 0 || max_freq < 0 || min_freq > max_freq) {
		dev_err(dev, "min_freq(%d) or max_freq(%d) value is incorrect\n",
				min_freq, max_freq);
	ret = amd_pstate_init_freq(cpudata);
	if (ret)
		goto free_cpudata1;

	min_freq = READ_ONCE(cpudata->min_freq);
	max_freq = READ_ONCE(cpudata->max_freq);
	nominal_freq = READ_ONCE(cpudata->nominal_freq);
	if (min_freq <= 0 || max_freq <= 0 ||
	    nominal_freq <= 0 || min_freq > max_freq) {
		dev_err(dev,
			"min_freq(%d) or max_freq(%d) or nominal_freq(%d) value is incorrect, check _CPC in ACPI tables\n",
			min_freq, max_freq, nominal_freq);
		ret = -EINVAL;
		goto free_cpudata1;
	}
@@ -1333,12 +1386,6 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
	/* It will be updated by governor */
	policy->cur = policy->cpuinfo.min_freq;

	/* Initial processor data capability frequencies */
	cpudata->max_freq = max_freq;
	cpudata->min_freq = min_freq;
	cpudata->nominal_freq = nominal_freq;
	cpudata->lowest_nonlinear_freq = lowest_nonlinear_freq;

	policy->driver_data = cpudata;

	cpudata->epp_cached = amd_pstate_get_epp(cpudata, 0);
@@ -1656,6 +1703,11 @@ static int __init amd_pstate_init(void)
	if (cpufreq_get_current_driver())
		return -EEXIST;

	quirks = NULL;

	/* check if this machine need CPPC quirks */
	dmi_check_system(amd_pstate_quirks_table);

	switch (cppc_state) {
	case AMD_PSTATE_UNDEFINED:
		/* Disable on the following configs by default:
+15 −5
Original line number Diff line number Diff line
@@ -49,13 +49,17 @@ struct amd_aperf_mperf {
 * @lowest_perf: the absolute lowest performance level of the processor
 * @prefcore_ranking: the preferred core ranking, the higher value indicates a higher
 * 		  priority.
 * @max_freq: the frequency that mapped to highest_perf
 * @min_freq: the frequency that mapped to lowest_perf
 * @nominal_freq: the frequency that mapped to nominal_perf
 * @lowest_nonlinear_freq: the frequency that mapped to lowest_nonlinear_perf
 * @min_limit_perf: Cached value of the performance corresponding to policy->min
 * @max_limit_perf: Cached value of the performance corresponding to policy->max
 * @min_limit_freq: Cached value of policy->min (in khz)
 * @max_limit_freq: Cached value of policy->max (in khz)
 * @max_freq: the frequency (in khz) that mapped to highest_perf
 * @min_freq: the frequency (in khz) that mapped to lowest_perf
 * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
 * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf
 * @cur: Difference of Aperf/Mperf/tsc count between last and current sample
 * @prev: Last Aperf/Mperf/tsc count value read from register
 * @freq: current cpu frequency value
 * @freq: current cpu frequency value (in khz)
 * @boost_supported: check whether the Processor or SBIOS supports boost mode
 * @hw_prefcore: check whether HW supports preferred core featue.
 * 		  Only when hw_prefcore and early prefcore param are true,
@@ -124,4 +128,10 @@ static const char * const amd_pstate_mode_string[] = {
	[AMD_PSTATE_GUIDED]      = "guided",
	NULL,
};

struct quirk_entry {
	u32 nominal_freq;
	u32 lowest_freq;
};

#endif /* _LINUX_AMD_PSTATE_H */