diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-10-12 13:47:24 +0200 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-10-12 13:47:24 +0200 |
| commit | b85cc49bc29895673ceb2f85370cb8af34768170 (patch) | |
| tree | e042223d7c47c24587034dcdd5b7c297c2d6a672 | |
| parent | 85a6229f08d4d9f17441413f5c43fb2dc2286e15 (diff) | |
| parent | 6b64d65c83688db2e58fbd9de7c74090f15271b7 (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.c | 166 | ||||
| -rw-r--r-- | drivers/cpufreq/acpi-cpufreq.c | 17 | ||||
| -rw-r--r-- | drivers/cpufreq/amd-pstate-ut.c | 54 | ||||
| -rw-r--r-- | drivers/cpufreq/amd-pstate.c | 116 | ||||
| -rw-r--r-- | drivers/cpufreq/amd-pstate.h | 44 | ||||
| -rw-r--r-- | include/acpi/cppc_acpi.h | 14 |
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; } |
