Skip to content

Commit

Permalink
Merge branch 'amd-pstate'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Rafael J. Wysocki committed May 2, 2024
2 parents a2bd1d2 + 70f83f5 commit c7e29dc
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 109 deletions.
3 changes: 3 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion drivers/acpi/cppc_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
258 changes: 155 additions & 103 deletions drivers/cpufreq/amd-pstate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit c7e29dc

Please sign in to comment.