summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-10-12 13:47:24 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-10-12 13:47:24 +0200
commitb85cc49bc29895673ceb2f85370cb8af34768170 (patch)
treee042223d7c47c24587034dcdd5b7c297c2d6a672
parent85a6229f08d4d9f17441413f5c43fb2dc2286e15 (diff)
parent6b64d65c83688db2e58fbd9de7c74090f15271b7 (diff)
Merge tag 'amd-pstate-v7.4-2026-10-14' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/superm1/linux
Pull amd-pstate content for 7.4 (10/4/26) from Mario Limonciello: "* Fixups for a few bugs identified by Sashiko * Initial support for maximum frequency capacity handling on Zen6 / client * Updated EPP tunings for Zen6 / client"
-rw-r--r--arch/x86/kernel/acpi/cppc.c166
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c17
-rw-r--r--drivers/cpufreq/amd-pstate-ut.c54
-rw-r--r--drivers/cpufreq/amd-pstate.c116
-rw-r--r--drivers/cpufreq/amd-pstate.h44
-rw-r--r--include/acpi/cppc_acpi.h14
6 files changed, 264 insertions, 147 deletions
diff --git a/arch/x86/kernel/acpi/cppc.c b/arch/x86/kernel/acpi/cppc.c
index bbade0da5130..f230e9323187 100644
--- a/arch/x86/kernel/acpi/cppc.c
+++ b/arch/x86/kernel/acpi/cppc.c
@@ -10,6 +10,7 @@
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/topology.h>
+#include <asm/cpu_device_id.h>
#define CPPC_HIGHEST_PERF_PERFORMANCE 196
#define CPPC_HIGHEST_PERF_PREFCORE 166
@@ -81,31 +82,18 @@ int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
static void amd_set_max_freq_ratio(void)
{
- struct cppc_perf_caps perf_caps;
- u64 numerator, nominal_perf;
+ u64 numerator, denominator;
u64 perf_ratio;
int rc;
- rc = cppc_get_perf_caps(0, &perf_caps);
- if (rc) {
- pr_debug("Could not retrieve perf counters (%d)\n", rc);
- return;
- }
-
- rc = amd_get_boost_ratio_numerator(0, &numerator);
+ rc = amd_get_boost_ratio(0, &numerator, &denominator);
if (rc) {
- pr_debug("Could not retrieve highest performance (%d)\n", rc);
- return;
- }
- nominal_perf = perf_caps.nominal_perf;
-
- if (!nominal_perf) {
- pr_debug("Could not retrieve nominal performance\n");
+ pr_debug("Could not retrieve boost ratio (%d)\n", rc);
return;
}
/* midpoint between max_boost and max_P */
- perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, nominal_perf) + SCHED_CAPACITY_SCALE) >> 1;
+ perf_ratio = (div_u64(numerator * SCHED_CAPACITY_SCALE, denominator) + SCHED_CAPACITY_SCALE) >> 1;
freq_invariance_set_perf_ratio(perf_ratio, false);
}
@@ -225,35 +213,30 @@ int amd_detect_prefcore(bool *detected)
EXPORT_SYMBOL_GPL(amd_detect_prefcore);
/**
- * amd_get_boost_ratio_numerator: Get the numerator to use for boost ratio calculation
- * @cpu: CPU to get numerator for.
- * @numerator: Output variable for numerator.
- *
- * Determine the numerator to use for calculating the boost ratio on
- * a CPU. On systems that support preferred cores, this will be a hardcoded
- * value. On other systems this will the highest performance register value.
+ * amd_get_effective_highest_perf: Get the effective highest performance value
+ * @cpu: CPU to get highest performance for.
*
- * If booting the system with amd-pstate enabled but preferred cores disabled then
- * the correct boost numerator will be returned to match hardware capabilities
- * even if the preferred cores scheduling hints are not enabled.
+ * Get the effective highest performance value for a CPU, accounting for
+ * preferred cores and heterogeneous topologies. On systems with preferred
+ * cores, this may be a hardcoded value. On heterogeneous systems, this
+ * may be a per-CPU value. On other systems, this is the shared highest
+ * performance value.
*
- * Return: 0 for success, negative error code otherwise.
+ * Return: Effective highest performance value, or negative error code.
*/
-int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
+int amd_get_effective_highest_perf(unsigned int cpu)
{
bool prefcore;
int ret;
u32 tmp;
ret = amd_detect_prefcore(&prefcore);
- if (ret)
+ if (ret < 0)
return ret;
/* without preferred cores, return the highest perf register value */
- if (!prefcore) {
- *numerator = boost_numerator;
- return 0;
- }
+ if (!prefcore)
+ return boost_numerator;
/*
* For AMD CPUs with Family ID 19H and Model ID range 0x70 to 0x7f,
@@ -263,8 +246,7 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
if (cpu_feature_enabled(X86_FEATURE_ZEN4)) {
switch (boot_cpu_data.x86_model) {
case 0x70 ... 0x7f:
- *numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
- return 0;
+ return CPPC_HIGHEST_PERF_PERFORMANCE;
default:
break;
}
@@ -272,28 +254,100 @@ int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
/* detect if running on heterogeneous design */
if (cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) {
- switch (cpu_data(cpu).topo.cpu_type) {
- case TOPO_CPU_TYPE_UNKNOWN:
- case TOPO_CPU_TYPE_ANY:
- pr_warn("Undefined core type found for cpu %d\n", cpu);
- break;
- case TOPO_CPU_TYPE_PERFORMANCE:
- /* use the max scale for performance cores */
- *numerator = CPPC_HIGHEST_PERF_PERFORMANCE;
- return 0;
- case TOPO_CPU_TYPE_LOW_POWER:
- case TOPO_CPU_TYPE_EFFICIENCY:
- /* use the highest perf value for efficiency and low-power cores */
- ret = amd_get_highest_perf(cpu, &tmp);
- if (ret)
- return ret;
- *numerator = tmp;
- return 0;
- }
+ if (cpu_feature_enabled(X86_FEATURE_ZEN5) &&
+ cpu_data(cpu).topo.cpu_type == TOPO_CPU_TYPE_PERFORMANCE)
+ return CPPC_HIGHEST_PERF_PERFORMANCE;
+
+ /* Zen 5 efficiency, and Zen 6+ */
+ ret = amd_get_highest_perf(cpu, &tmp);
+ if (ret < 0)
+ return ret;
+
+ return tmp;
+ }
+
+ return CPPC_HIGHEST_PERF_PREFCORE;
+}
+EXPORT_SYMBOL_GPL(amd_get_effective_highest_perf);
+
+struct amd_max_freq {
+ u32 performance;
+ u32 efficiency;
+ u32 low_power;
+};
+
+static const struct amd_max_freq max_freq_zen6_client = {
+ .performance = 5025,
+ .efficiency = 3524,
+ .low_power = 2399,
+};
+
+int amd_get_max_frequency(unsigned int cpu)
+{
+ enum x86_topology_cpu_type type = cpu_data(cpu).topo.cpu_type;
+ const struct amd_max_freq *max_freq = NULL;
+
+ if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
+ cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
+ max_freq = &max_freq_zen6_client;
+
+ if (!max_freq)
+ return 0;
+
+ switch (type) {
+ case TOPO_CPU_TYPE_PERFORMANCE:
+ return max_freq->performance;
+ case TOPO_CPU_TYPE_EFFICIENCY:
+ return max_freq->efficiency;
+ case TOPO_CPU_TYPE_LOW_POWER:
+ return max_freq->low_power;
+ default:
+ break;
}
- *numerator = CPPC_HIGHEST_PERF_PREFCORE;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amd_get_max_frequency);
+
+/**
+ * amd_get_boost_ratio: Get numerator and denominator for boost ratio
+ * @cpu: CPU to get the boost ratio for.
+ * @numerator: Output variable for numerator.
+ * @denominator: Output variable for denominator.
+ *
+ * Get the numerator and denominator for calculating the boost ratio.
+ *
+ * Return: 0 for success, negative error code otherwise.
+ */
+int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
+{
+ struct cppc_perf_caps perf_caps;
+ int max_frequency;
+ int ret;
+
+ ret = cppc_get_perf_caps(cpu, &perf_caps);
+ if (ret)
+ return ret;
+
+ max_frequency = amd_get_max_frequency(cpu);
+ /* Use frequency values if available */
+ if (max_frequency && perf_caps.nominal_freq) {
+ *numerator = max_frequency;
+ *denominator = perf_caps.nominal_freq;
+ return 0;
+ }
+
+ /* Fall back to performance values */
+ ret = amd_get_effective_highest_perf(cpu);
+ if (ret < 0)
+ return ret;
+
+ *numerator = ret;
+
+ *denominator = perf_caps.nominal_perf;
+ if (!*denominator)
+ return -EINVAL;
return 0;
}
-EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator);
+EXPORT_SYMBOL_GPL(amd_get_boost_ratio);
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 10ea6035f4ad..2e74a000cdc7 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -626,7 +626,7 @@ static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c)
static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
{
struct cppc_perf_caps perf_caps;
- u64 highest_perf, nominal_perf;
+ u64 numerator, denominator;
int ret;
if (acpi_pstate_strict)
@@ -640,32 +640,31 @@ static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
}
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
- ret = amd_get_boost_ratio_numerator(cpu, &highest_perf);
+ ret = amd_get_boost_ratio(cpu, &numerator, &denominator);
if (ret) {
- pr_debug("CPU%d: Unable to get boost ratio numerator (%d)\n",
+ pr_debug("CPU%d: Unable to get boost ratio (%d)\n",
cpu, ret);
return 0;
}
} else {
- highest_perf = perf_caps.highest_perf;
+ numerator = perf_caps.highest_perf;
+ denominator = perf_caps.nominal_perf;
}
- nominal_perf = perf_caps.nominal_perf;
-
if (nominal_freq)
*nominal_freq = perf_caps.nominal_freq * 1000;
- if (!highest_perf || !nominal_perf) {
+ if (!numerator || !denominator) {
pr_debug("CPU%d: highest or nominal performance missing\n", cpu);
return 0;
}
- if (highest_perf < nominal_perf) {
+ if (numerator < denominator) {
pr_debug("CPU%d: nominal performance above highest\n", cpu);
return 0;
}
- return div_u64(highest_perf << SCHED_CAPACITY_SHIFT, nominal_perf);
+ return div_u64(numerator << SCHED_CAPACITY_SHIFT, denominator);
}
#else
diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
index c2c1a166b3a9..506d7bb2e660 100644
--- a/drivers/cpufreq/amd-pstate-ut.c
+++ b/drivers/cpufreq/amd-pstate-ut.c
@@ -216,7 +216,14 @@ static int amd_pstate_ut_check_perf(u32 index)
/*
* Check if frequency values are reasonable.
- * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
+ * nominal_freq > lowest_nonlinear_freq >= min_freq > 0
+ * max_freq >= lowest_nonlinear_freq
+ *
+ * On most parts the boost frequency is the highest, i.e.
+ * max_freq >= nominal_freq. On heterogeneous designs (e.g. Zen6) a
+ * low-power core can have a maximum frequency below the shared nominal
+ * reference frequency, so only require the boost frequency to sit above
+ * the lowest nonlinear frequency here.
* check max freq when set support boost mode.
*/
static int amd_pstate_ut_check_freq(u32 index)
@@ -235,11 +242,11 @@ static int amd_pstate_ut_check_freq(u32 index)
cpudata = policy->driver_data;
perf = READ_ONCE(cpudata->perf);
- if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
+ if (!((policy->cpuinfo.max_freq >= cpudata->lowest_nonlinear_freq) &&
(cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
(cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
(policy->cpuinfo.min_freq > 0))) {
- pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
+ pr_err("%s cpu%d max=%d, nominal=%d, lowest_nonlinear=%d, min=%d, the formula is incorrect!\n",
__func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
return -EINVAL;
@@ -297,12 +304,12 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
static int amd_pstate_ut_epp(u32 index)
{
- static const char * const epp_strings[] = {
- "dynamic",
- "power",
- "balance_power",
- "balance_performance",
- "performance",
+ enum energy_perf_value_index epp_modes[] = {
+ EPP_INDEX_DYNAMIC,
+ EPP_INDEX_POWERSAVE,
+ EPP_INDEX_BALANCE_POWERSAVE,
+ EPP_INDEX_BALANCE_PERFORMANCE,
+ EPP_INDEX_PERFORMANCE,
};
char *buf __free(cleanup_page) = NULL;
struct cpufreq_policy *policy = NULL;
@@ -371,9 +378,9 @@ static int amd_pstate_ut_epp(u32 index)
}
}
- for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
+ for (i = 0; i < ARRAY_SIZE(epp_modes); i++) {
memset(buf, 0, PAGE_SIZE);
- snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
+ snprintf(buf, PAGE_SIZE, "%s", energy_perf_strings[epp_modes[i]]);
ret = store_energy_performance_preference(policy, buf, strlen(buf));
if (ret < 0)
goto out;
@@ -389,8 +396,24 @@ static int amd_pstate_ut_epp(u32 index)
*/
strreplace(buf, '(', '\0');
- if (strcmp(buf, epp_strings[i])) {
- pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
+ if (strcmp(buf, energy_perf_strings[epp_modes[i]])) {
+ u8 epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
+
+ /*
+ * The preference read back may legitimately differ from the one
+ * written: some platforms (e.g. Zen6) program the same raw EPP
+ * value for more than one named preference, so show() reports the
+ * first name that matches that value.
+ *
+ * If strings mismatch, confirm the CPU is programmed with the correct
+ * EPP value for the mode. This verifies hardware programming while
+ * tolerating such aliasing in driver.
+ */
+ if (epp == amd_pstate_cpu_epp_values(cpudata->cpu_type)[epp_modes[i]])
+ continue;
+
+ pr_err("String EPP value mismatch: %s != %s\n",
+ buf, energy_perf_strings[epp_modes[i]]);
ret = -EINVAL;
goto out;
}
@@ -406,11 +429,12 @@ out:
* restore it here before dropping policy reference.
*/
if (orig_dynamic_epp) {
+ const char * const dynamic_string = energy_perf_strings[epp_modes[0]];
int ret2;
ret2 = store_energy_performance_preference(policy,
- epp_strings[0],
- strlen(epp_strings[0]));
+ dynamic_string,
+ strlen(dynamic_string));
if (!ret && (ret2 < 0))
ret = ret2;
}
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 83960a6336b9..4bf158f677f3 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -44,7 +44,6 @@
#include <acpi/cppc_acpi.h>
#include <asm/msr.h>
-#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <asm/cpu_device_id.h>
@@ -90,46 +89,6 @@ static bool amd_pstate_prefcore = true;
static struct quirk_entry *quirks;
/*
- * AMD Energy Preference Performance (EPP)
- * The EPP is used in the CCLK DPM controller to drive
- * the frequency that a core is going to operate during
- * short periods of activity. EPP values will be utilized for
- * different OS profiles (balanced, performance, power savings)
- * display strings corresponding to EPP index in the
- * energy_perf_strings[]
- * index String
- *-------------------------------------
- * 0 default
- * 1 performance
- * 2 balance_performance
- * 3 balance_power
- * 4 power
- * 5 custom (for raw EPP values)
- * 6 dynamic (platform profile driven selection)
- */
-enum energy_perf_value_index {
- EPP_INDEX_DEFAULT = 0,
- EPP_INDEX_PERFORMANCE,
- EPP_INDEX_BALANCE_PERFORMANCE,
- EPP_INDEX_BALANCE_POWERSAVE,
- EPP_INDEX_POWERSAVE,
- EPP_INDEX_CUSTOM,
- EPP_INDEX_DYNAMIC,
- EPP_INDEX_MAX,
-};
-
-static const char * const energy_perf_strings[] = {
- [EPP_INDEX_DEFAULT] = "default",
- [EPP_INDEX_PERFORMANCE] = "performance",
- [EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
- [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
- [EPP_INDEX_POWERSAVE] = "power",
- [EPP_INDEX_CUSTOM] = "custom",
- [EPP_INDEX_DYNAMIC] = "dynamic",
-};
-static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
-
-/*
* The numeric EPP value programmed for each named preference. First dimension
* is CPU type (TOPO_CPU_TYPE_ANY for non-hybrid, TOPO_CPU_TYPE_PERFORMANCE/
* EFFICIENCY/LOW_POWER for hybrid). The initializer holds the legacy values
@@ -159,7 +118,7 @@ static_assert(ARRAY_SIZE(epp_values) == TOPO_CPU_TYPE_LOW_POWER + 1,
* Non-hybrid systems use TOPO_CPU_TYPE_ANY; hybrid systems use the CPU's
* actual type (PERFORMANCE/EFFICIENCY/LOW_POWER).
*/
-static inline u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
+u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
{
switch (cpu_type) {
case TOPO_CPU_TYPE_PERFORMANCE:
@@ -170,6 +129,7 @@ static inline u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
return epp_values[TOPO_CPU_TYPE_ANY];
}
}
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_cpu_epp_values);
/**
* struct amd_pstate_epp_values - EPP values for the four named preferences
@@ -211,7 +171,7 @@ static const struct amd_pstate_epp_soc epp_soc_zen6_client __initconst = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
- .power = 64,
+ .power = 115,
},
.efficiency_core = {
.performance = 25,
@@ -221,9 +181,9 @@ static const struct amd_pstate_epp_soc epp_soc_zen6_client __initconst = {
},
.low_power_core = {
.performance = 25,
- .balance_performance = 51,
+ .balance_performance = 64,
.balance_power = 64,
- .power = 115,
+ .power = 64,
},
};
@@ -555,11 +515,13 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
if (ret)
return ret;
- ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
- if (ret)
+ ret = amd_get_effective_highest_perf(cpudata->cpu);
+ if (ret < 0)
return ret;
- ret = rdmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
+ numerator = ret;
+
+ ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, &cppc_req);
if (ret)
return ret;
@@ -590,10 +552,12 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
if (ret)
return ret;
- ret = amd_get_boost_ratio_numerator(cpudata->cpu, &numerator);
- if (ret)
+ ret = amd_get_effective_highest_perf(cpudata->cpu);
+ if (ret < 0)
return ret;
+ numerator = ret;
+
perf.highest_perf = numerator;
perf.max_limit_perf = numerator;
perf.min_limit_perf = cppc_perf.lowest_perf;
@@ -609,8 +573,10 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
return 0;
}
- ret = cppc_set_auto_sel(cpudata->cpu,
- (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
+ if ((cppc_state != AMD_PSTATE_PASSIVE) == auto_sel)
+ return 0;
+
+ ret = cppc_set_auto_sel(cpudata->cpu, !auto_sel);
if (ret)
pr_warn("failed to set auto_sel, ret: %d\n", ret);
@@ -772,7 +738,10 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
struct amd_cpudata *cpudata = policy->driver_data;
union perf_cached perf = READ_ONCE(cpudata->perf);
- perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
+ if (policy->max >= READ_ONCE(cpudata->max_freq))
+ perf.max_limit_perf = perf.highest_perf;
+ else
+ perf.max_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->max);
WRITE_ONCE(cpudata->max_limit_freq, policy->max);
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
@@ -1098,8 +1067,10 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
WRITE_ONCE(cpudata->nominal_freq, nominal_freq);
- /* max_freq is calculated according to (nominal_freq * highest_perf)/nominal_perf */
- max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
+ /* try to look up from BIOS/quirk first, fall back to (nominal_freq * highest_perf)/nominal_perf */
+ max_freq = amd_get_max_frequency(cpudata->cpu) * 1000;
+ if (!max_freq)
+ max_freq = perf_to_freq(perf, nominal_freq, perf.highest_perf);
WRITE_ONCE(cpudata->max_freq, max_freq);
lowest_nonlinear_freq = perf_to_freq(perf, nominal_freq, perf.lowest_nonlinear_perf);
@@ -1381,8 +1352,10 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
return ret;
cpudata->profile_name = kasprintf(GFP_KERNEL, "amd-pstate-epp-cpu%d", cpudata->cpu);
- if (!cpudata->profile_name)
- return -ENOMEM;
+ if (!cpudata->profile_name) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
cpudata->ppdev = platform_profile_register(get_cpu_device(policy->cpu),
cpudata->profile_name,
@@ -1809,6 +1782,7 @@ static int amd_pstate_unregister_driver(int dummy)
static int amd_pstate_change_mode_without_dvr_change(int mode)
{
int cpu = 0;
+ int ret;
cppc_state = mode;
@@ -1816,7 +1790,9 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
return 0;
for_each_online_cpu(cpu) {
- cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
+ ret = cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
+ if (ret)
+ return ret;
}
return 0;
@@ -1824,6 +1800,7 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
static int amd_pstate_change_driver_mode(int mode)
{
+ int old_mode = cppc_state;
int ret;
lockdep_assert_held(&amd_pstate_driver_lock);
@@ -1833,10 +1810,16 @@ static int amd_pstate_change_driver_mode(int mode)
return ret;
ret = amd_pstate_register_driver(mode);
- if (ret)
- return ret;
+ if (ret) {
+ pr_err("Failed to register %s mode, restoring %s mode\n",
+ amd_pstate_get_mode_string(mode),
+ amd_pstate_get_mode_string(old_mode));
+ if (amd_pstate_register_driver(old_mode))
+ pr_err("Failed to restore %s mode\n",
+ amd_pstate_get_mode_string(old_mode));
+ }
- return 0;
+ return ret;
}
static cppc_mode_transition_fn mode_state_machine[AMD_PSTATE_MAX][AMD_PSTATE_MAX] = {
@@ -1882,6 +1865,7 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
int amd_pstate_update_status(const char *buf, size_t size)
{
+ cppc_mode_transition_fn fn;
int mode_idx;
if (size > strlen("passive") || size < strlen("active"))
@@ -1891,10 +1875,12 @@ int amd_pstate_update_status(const char *buf, size_t size)
if (mode_idx < 0)
return mode_idx;
- if (mode_state_machine[cppc_state][mode_idx]) {
- guard(mutex)(&amd_pstate_driver_lock);
- return mode_state_machine[cppc_state][mode_idx](mode_idx);
- }
+ guard(mutex)(&amd_pstate_driver_lock);
+
+ /* Resolve the transition under the lock; cppc_state is only stable while held. */
+ fn = mode_state_machine[cppc_state][mode_idx];
+ if (fn)
+ return fn(mode_idx);
return 0;
}
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index c7189d177b81..422188c639b1 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -11,6 +11,8 @@
#include <linux/pm_qos.h>
#include <linux/platform_profile.h>
+#include <asm/processor.h>
+
#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT)
#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \
EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut")
@@ -162,6 +164,48 @@ enum amd_pstate_mode {
AMD_PSTATE_MAX,
};
+/*
+ * AMD Energy Preference Performance (EPP)
+ * The EPP is used in the CCLK DPM controller to drive
+ * the frequency that a core is going to operate during
+ * short periods of activity. EPP values will be utilized for
+ * different OS profiles (balanced, performance, power savings)
+ * display strings corresponding to EPP index in the
+ * energy_perf_strings[]
+ * index String
+ *-------------------------------------
+ * 0 default
+ * 1 performance
+ * 2 balance_performance
+ * 3 balance_power
+ * 4 power
+ * 5 custom (for raw EPP values)
+ * 6 dynamic (platform profile driven selection)
+ */
+enum energy_perf_value_index {
+ EPP_INDEX_DEFAULT = 0,
+ EPP_INDEX_PERFORMANCE,
+ EPP_INDEX_BALANCE_PERFORMANCE,
+ EPP_INDEX_BALANCE_POWERSAVE,
+ EPP_INDEX_POWERSAVE,
+ EPP_INDEX_CUSTOM,
+ EPP_INDEX_DYNAMIC,
+ EPP_INDEX_MAX,
+};
+
+static const char * const energy_perf_strings[] = {
+ [EPP_INDEX_DEFAULT] = "default",
+ [EPP_INDEX_PERFORMANCE] = "performance",
+ [EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
+ [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
+ [EPP_INDEX_POWERSAVE] = "power",
+ [EPP_INDEX_CUSTOM] = "custom",
+ [EPP_INDEX_DYNAMIC] = "dynamic",
+};
+static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
+
+u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type);
+
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
{
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 94a6277edab2..4c0b11a718e3 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -192,7 +192,9 @@ extern int cppc_set_auto_sel(int cpu, bool enable);
extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
-extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
+extern int amd_get_effective_highest_perf(unsigned int cpu);
+extern int amd_get_max_frequency(unsigned int cpu);
+extern int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator);
extern int amd_detect_prefcore(bool *detected);
#else /* !CONFIG_ACPI_CPPC_LIB */
static inline int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
@@ -305,7 +307,15 @@ static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
{
return -ENODEV;
}
-static inline int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator)
+static inline int amd_get_effective_highest_perf(unsigned int cpu)
+{
+ return -EOPNOTSUPP;
+}
+static inline int amd_get_max_frequency(unsigned int cpu)
+{
+ return 0;
+}
+static inline int amd_get_boost_ratio(unsigned int cpu, u64 *numerator, u64 *denominator)
{
return -EOPNOTSUPP;
}