|
|
|
|
@@ -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:
|
|
|
|
|
|