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
This commit is contained in:
commit
c7e29dcef9
@ -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
|
||||
|
@ -686,8 +686,10 @@ 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. */
|
||||
|
@ -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 @@ free_cpufreq_put:
|
||||
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;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user