diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index ae1673b36ac4..8047f168b6d7 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -55,10 +55,10 @@ #define AMD_PSTATE_TRANSITION_DELAY 1000 #define AMD_PSTATE_FAST_CPPC_TRANSITION_DELAY 600 -#define AMD_CPPC_EPP_PERFORMANCE 0x00 -#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80 -#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF -#define AMD_CPPC_EPP_POWERSAVE 0xFF +#define AMD_CPPC_EPP_LEGACY_PERFORMANCE 0x00 +#define AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE 0x80 +#define AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE 0xBF +#define AMD_CPPC_EPP_LEGACY_POWERSAVE 0xFF static const char * const amd_pstate_mode_string[] = { [AMD_PSTATE_UNDEFINED] = "undefined", @@ -129,14 +129,86 @@ static const char * const energy_perf_strings[] = { }; static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX); -static unsigned int epp_values[] = { - [EPP_INDEX_DEFAULT] = 0, - [EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_PERFORMANCE, - [EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_BALANCE_PERFORMANCE, - [EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE, - [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE, +/* + * 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 + * used as the fallback on any platform not listed in amd_pstate_epp_soc_ids[]; + * amd_pstate_init_epp_values() overwrites slots at boot when the running SoC + * has a per-SoC (and potentially per-CPU-type) override. + */ +static u8 epp_values[][EPP_INDEX_MAX] = { + /* + * Initialize all CPU types to legacy defaults. + * amd_pstate_init_epp_values() will fix these up + * based on the platform during boot. + */ + [TOPO_CPU_TYPE_ANY ... TOPO_CPU_TYPE_LOW_POWER] = { + [EPP_INDEX_DEFAULT] = 0, + [EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_PERFORMANCE, + [EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE, + [EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE, + [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_LEGACY_POWERSAVE, + }, +}; +static_assert(ARRAY_SIZE(epp_values) == TOPO_CPU_TYPE_LOW_POWER + 1, + "epp_values must have entries for all CPU types up to TOPO_CPU_TYPE_LOW_POWER"); + +/* + * Get the EPP value row for a given CPU, accounting for hybrid CPU types. + * 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) +{ + switch (cpu_type) { + case TOPO_CPU_TYPE_PERFORMANCE: + case TOPO_CPU_TYPE_EFFICIENCY: + case TOPO_CPU_TYPE_LOW_POWER: + return epp_values[cpu_type]; + default: + return epp_values[TOPO_CPU_TYPE_ANY]; + } +} + +/** + * struct amd_pstate_epp_values - EPP values for the four named preferences + * @performance: value for the "performance" preference + * @balance_performance: value for the "balance_performance" preference + * @balance_power: value for the "balance_power" preference + * @power: value for the "power" preference + */ +struct amd_pstate_epp_values { + u8 performance; + u8 balance_performance; + u8 balance_power; + u8 power; +}; + +/** + * struct amd_pstate_epp_soc - per-CPU-type EPP overrides for hybrid systems + * @performance_core: values for TOPO_CPU_TYPE_PERFORMANCE cores + * @efficiency_core: values for TOPO_CPU_TYPE_EFFICIENCY cores + * @low_power_core: values for TOPO_CPU_TYPE_LOW_POWER cores + * + * Referenced from amd_pstate_epp_soc_ids[] to give a hybrid platform its own + * numeric EPP values for the four named preferences, with distinct values per + * CPU type. Non-hybrid systems are not listed in the table and always use the + * legacy defaults. + */ +struct amd_pstate_epp_soc { + struct amd_pstate_epp_values performance_core; + struct amd_pstate_epp_values efficiency_core; + struct amd_pstate_epp_values low_power_core; +}; + +/* + * Per-CPU-type EPP overrides for hybrid systems. Only hybrid SoCs should be + * listed here; non-hybrid systems always use the legacy defaults. + */ +static const struct x86_cpu_id amd_pstate_epp_soc_ids[] __initconst = { + {} }; -static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2); typedef int (*cppc_mode_transition_fn)(int); @@ -1055,6 +1127,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) return -ENOMEM; cpudata->cpu = policy->cpu; + cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type; ret = amd_pstate_init_perf(cpudata); if (ret) @@ -1182,13 +1255,16 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy, enum platform_profile_option profile) { + struct amd_cpudata *cpudata = policy->driver_data; + u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type); + switch (profile) { case PLATFORM_PROFILE_PERFORMANCE: - return AMD_CPPC_EPP_PERFORMANCE; + return values[EPP_INDEX_PERFORMANCE]; case PLATFORM_PROFILE_BALANCED: return amd_pstate_get_balanced_epp(policy); case PLATFORM_PROFILE_LOW_POWER: - return AMD_CPPC_EPP_POWERSAVE; + return values[EPP_INDEX_POWERSAVE]; default: break; } @@ -1398,6 +1474,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, const char *buf, size_t count) { struct amd_cpudata *cpudata = policy->driver_data; + u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type); ssize_t ret; bool raw_epp = false; u8 epp; @@ -1432,12 +1509,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, } if (ret) - epp = epp_values[ret]; + epp = values[ret]; else epp = cpudata->epp_default_dc; } - if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (epp > 0 && epp != values[EPP_INDEX_PERFORMANCE] && + cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { pr_debug("EPP cannot be set under performance policy\n"); return -EBUSY; } @@ -1462,34 +1540,31 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference); ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf) { struct amd_cpudata *cpudata = policy->driver_data; - u8 preference, epp; + u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type); + u8 epp; + int i; epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached); if (!cpudata->dynamic_epp && cpudata->raw_epp) return sysfs_emit(buf, "%u\n", epp); - switch (epp) { - case AMD_CPPC_EPP_PERFORMANCE: - preference = EPP_INDEX_PERFORMANCE; - break; - case AMD_CPPC_EPP_BALANCE_PERFORMANCE: - preference = EPP_INDEX_BALANCE_PERFORMANCE; - break; - case AMD_CPPC_EPP_BALANCE_POWERSAVE: - preference = EPP_INDEX_BALANCE_POWERSAVE; - break; - case AMD_CPPC_EPP_POWERSAVE: - preference = EPP_INDEX_POWERSAVE; - break; - default: - return -EINVAL; + /* + * Map the cached EPP value back to a named preference. Skip the + * "default" slot (index 0) so an EPP of 0 reports as "performance". + * Stop at POWERSAVE; CUSTOM and DYNAMIC are not initialized in epp_values. + */ + for (i = EPP_INDEX_PERFORMANCE; i <= EPP_INDEX_POWERSAVE; i++) { + const char *name = energy_perf_strings[i]; + + if (epp == values[i]) { + if (cpudata->dynamic_epp) + return sysfs_emit(buf, "dynamic(profile:%s)\n", name); + return sysfs_emit(buf, "%s\n", name); + } } - if (cpudata->dynamic_epp) - return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]); - - return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); + return sysfs_emit(buf, "%u\n", epp); } EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference); @@ -1881,6 +1956,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) return -ENOMEM; cpudata->cpu = policy->cpu; + cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type; ret = amd_pstate_init_perf(cpudata); if (ret) @@ -1931,9 +2007,11 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp; cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE; } else { + u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type); + policy->policy = CPUFREQ_POLICY_POWERSAVE; - cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE; - cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE; + cpudata->epp_default_ac = values[EPP_INDEX_PERFORMANCE]; + cpudata->epp_default_dc = values[EPP_INDEX_BALANCE_PERFORMANCE]; cpudata->current_profile = PLATFORM_PROFILE_BALANCED; } @@ -2232,6 +2310,34 @@ static bool amd_cppc_supported(void) return true; } +/* + * Resolve the numeric EPP values for hybrid systems. Only hybrid SoCs are listed + * in amd_pstate_epp_soc_ids[]; non-hybrid systems always use the legacy defaults. + */ +static inline void __init amd_pstate_set_epp_values(enum x86_topology_cpu_type type, + const struct amd_pstate_epp_values *core) +{ + epp_values[type][EPP_INDEX_PERFORMANCE] = core->performance; + epp_values[type][EPP_INDEX_BALANCE_PERFORMANCE] = core->balance_performance; + epp_values[type][EPP_INDEX_BALANCE_POWERSAVE] = core->balance_power; + epp_values[type][EPP_INDEX_POWERSAVE] = core->power; +} + +static void __init amd_pstate_init_epp_values(void) +{ + const struct x86_cpu_id *id = x86_match_cpu(amd_pstate_epp_soc_ids); + const struct amd_pstate_epp_soc *soc; + + if (!id || !id->driver_data) + return; + + soc = (const struct amd_pstate_epp_soc *)id->driver_data; + + amd_pstate_set_epp_values(TOPO_CPU_TYPE_PERFORMANCE, &soc->performance_core); + amd_pstate_set_epp_values(TOPO_CPU_TYPE_EFFICIENCY, &soc->efficiency_core); + amd_pstate_set_epp_values(TOPO_CPU_TYPE_LOW_POWER, &soc->low_power_core); +} + static int __init amd_pstate_init(void) { struct device *dev_root; @@ -2259,6 +2365,9 @@ static int __init amd_pstate_init(void) /* check if this machine need CPPC quirks */ dmi_check_system(amd_pstate_quirks_table); + /* resolve per-SoC EPP values for the named preferences */ + amd_pstate_init_epp_values(); + /* * determine the driver mode from the command line or kernel config. * If no command line input is provided, cppc_state will be AMD_PSTATE_UNDEFINED. diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index 578465187b7a..c7189d177b81 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -146,6 +146,8 @@ struct amd_cpudata { enum platform_profile_option current_profile; struct device *ppdev; char *profile_name; + + enum x86_topology_cpu_type cpu_type; }; /*