diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index e75344f4e0cd..3f11456e1402 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -501,13 +501,6 @@ Kernel parameters disable Disable amd-pstate preferred core. - amd_dynamic_epp= - [X86] - disable - Disable amd-pstate dynamic EPP. - enable - Enable amd-pstate dynamic EPP. - amijoy.map= [HW,JOY] Amiga joystick support Map of devices attached to JOY0DAT and JOY1DAT Format: , diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst index 6fd72ba52df2..4c5d3d8acf64 100644 --- a/Documentation/admin-guide/pm/amd-pstate.rst +++ b/Documentation/admin-guide/pm/amd-pstate.rst @@ -481,12 +481,6 @@ For systems that support ``amd-pstate`` preferred core, the core rankings will always be advertised by the platform. But OS can choose to ignore that via the kernel parameter ``amd_prefcore=disable``. -``amd_dynamic_epp`` - -When AMD pstate is in auto mode, dynamic EPP will control whether the kernel -autonomously changes the EPP mode. The default is disabled. It can be enabled -with the kernel parameter ``amd_dynamic_epp=enable``. - User Space Interface in ``sysfs`` - General =========================================== diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index 78c2ccd5be13..7f1af15cb934 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -56,6 +56,7 @@ cluster0_opp: opp-table-0 { opp-507000000 { opp-hz = /bits/ 64 <507000000>; opp-microvolt = <859000>; + opp-suspend; }; opp-702000000 { opp-hz = /bits/ 64 <702000000>; @@ -93,6 +94,7 @@ cluster1_opp: opp-table-1 { opp-507000000 { opp-hz = /bits/ 64 <507000000>; opp-microvolt = <828000>; + opp-suspend; }; opp-702000000 { opp-hz = /bits/ 64 <702000000>; diff --git a/arch/x86/kernel/acpi/cppc.c b/arch/x86/kernel/acpi/cppc.c index d2185b7a2030..1c6bf56c3215 100644 --- a/arch/x86/kernel/acpi/cppc.c +++ b/arch/x86/kernel/acpi/cppc.c @@ -11,6 +11,7 @@ #include #include #include +#include #define CPPC_HIGHEST_PERF_PERFORMANCE 196 #define CPPC_HIGHEST_PERF_PREFCORE 166 @@ -95,31 +96,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); + rc = amd_get_boost_ratio(0, &numerator, &denominator); if (rc) { - pr_debug("Could not retrieve perf counters (%d)\n", rc); - return; - } - - rc = amd_get_boost_ratio_numerator(0, &numerator); - 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); } @@ -239,35 +227,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. + * amd_get_effective_highest_perf: Get the effective highest performance value + * @cpu: CPU to get highest performance for. * - * 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. + * 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. * - * 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. - * - * 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, @@ -277,8 +260,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; } @@ -286,28 +268,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; } - *numerator = CPPC_HIGHEST_PERF_PREFCORE; + 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; + } return 0; } -EXPORT_SYMBOL_GPL(amd_get_boost_ratio_numerator); +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); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index db83f3365698..edc1299098d4 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -288,6 +288,7 @@ endif if LOONGARCH config LOONGSON3_CPUFREQ tristate "Loongson3 CPUFreq Driver" + depends on MACH_LOONGSON64 help This option adds a CPUFreq driver for Loongson processors which support software configurable cpu frequency. 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/airoha-cpufreq.c b/drivers/cpufreq/airoha-cpufreq.c index 3e7770860d13..8283c528d0c1 100644 --- a/drivers/cpufreq/airoha-cpufreq.c +++ b/drivers/cpufreq/airoha-cpufreq.c @@ -64,7 +64,7 @@ static int airoha_cpufreq_probe(struct platform_device *pdev) /* Attach PM for OPP */ ret = dev_pm_domain_attach_list(cpu_dev, &attach_data, &priv->pd_list); - if (ret) + if (ret < 0) goto clear_opp_config; cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index e23773680e05..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) @@ -226,23 +233,44 @@ static int amd_pstate_ut_check_freq(u32 index) for_each_online_cpu(cpu) { struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL; struct amd_cpudata *cpudata; + union perf_cached perf; policy = cpufreq_cpu_get(cpu); if (!policy) continue; - cpudata = policy->driver_data; - if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) && + cpudata = policy->driver_data; + perf = READ_ONCE(cpudata->perf); + + 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; } - if (cpudata->lowest_nonlinear_freq != policy->min) { + if (perf.bios_min_perf) { + u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, + perf.bios_min_perf); + + /* + * User set bios_min_freq cannot be trusted to + * be within the driver limits. Clamp it similar + * to cpufreq_verify_within_cpu_limits(). + */ + bios_min_freq = clamp_t(u32, bios_min_freq, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + if (bios_min_freq != policy->min) { + pr_err("%s cpu%d bios_min_freq=%d policy_min=%d, they should be equal!\n", + __func__, cpu, bios_min_freq, policy->min); + return -EINVAL; + } + } else if (cpudata->lowest_nonlinear_freq != policy->min) { pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n", __func__, cpu, cpudata->lowest_nonlinear_freq, policy->min); return -EINVAL; @@ -276,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; @@ -350,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; @@ -368,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; } @@ -385,11 +429,12 @@ static int amd_pstate_ut_epp(u32 index) * 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 0a8b1aaa2660..4bedfbfc18b6 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -44,7 +44,6 @@ #include #include -#include #include #include @@ -55,10 +54,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", @@ -90,53 +89,112 @@ 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) + * 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. */ -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 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). + */ +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]; + } +} +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_cpu_epp_values); + +/** + * 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; }; -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", +/** + * 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; }; -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, +/* + * 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 amd_pstate_epp_soc epp_soc_zen6_client __initconst = { + .performance_core = { + .performance = 25, + .balance_performance = 51, + .balance_power = 64, + .power = 115, + }, + .efficiency_core = { + .performance = 25, + .balance_performance = 51, + .balance_power = 64, + .power = 115, + }, + .low_power_core = { + .performance = 25, + .balance_performance = 64, + .balance_power = 64, + .power = 64, + }, +}; + +static const struct x86_cpu_id amd_pstate_epp_soc_ids[] __initconst = { + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x80, &epp_soc_zen6_client), + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x81, &epp_soc_zen6_client), + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x84, &epp_soc_zen6_client), + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x85, &epp_soc_zen6_client), + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0xe0, &epp_soc_zen6_client), + {} }; -static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2); typedef int (*cppc_mode_transition_fn)(int); @@ -145,19 +203,6 @@ static struct quirk_entry quirk_amd_7k62 = { .lowest_freq = 550, }; -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); - - return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf); -} - -static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val) -{ - return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val, - perf.nominal_perf); -} - static int __init dmi_matched_7k62_bios_bug(const struct dmi_system_id *dmi) { /** @@ -470,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; @@ -505,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; @@ -524,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); @@ -687,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) { @@ -1013,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); @@ -1068,6 +1124,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) @@ -1195,13 +1252,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; } @@ -1292,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, @@ -1411,6 +1473,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; @@ -1445,12 +1508,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; } @@ -1475,34 +1539,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); @@ -1721,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; @@ -1728,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; @@ -1736,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); @@ -1745,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] = { @@ -1794,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 cpu, mode_idx; if (size > strlen("passive") || size < strlen("active")) @@ -1803,6 +1875,7 @@ int amd_pstate_update_status(const char *buf, size_t size) if (mode_idx < 0) return mode_idx; + /* Resolve the transition under the lock; cppc_state is only stable while held. */ guard(mutex)(&amd_pstate_driver_lock); if (!mode_state_machine[cppc_state][mode_idx]) @@ -1819,7 +1892,8 @@ int amd_pstate_update_status(const char *buf, size_t size) } } - return mode_state_machine[cppc_state][mode_idx](mode_idx); + fn = mode_state_machine[cppc_state][mode_idx]; + return fn(mode_idx); } EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status); @@ -1905,6 +1979,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) @@ -1955,9 +2030,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; } @@ -2256,6 +2333,38 @@ 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) { + if (cpu_feature_enabled(X86_FEATURE_ZEN6) && + cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES)) + pr_warn_once("No EPP tunings found for platform\n"); + 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; @@ -2283,6 +2392,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 9f5a81976eae..422188c639b1 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -11,6 +11,8 @@ #include #include +#include + #if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT) #define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \ EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut") @@ -146,6 +148,8 @@ struct amd_cpudata { enum platform_profile_option current_profile; struct device *ppdev; char *profile_name; + + enum x86_topology_cpu_type cpu_type; }; /* @@ -159,6 +163,62 @@ enum amd_pstate_mode { AMD_PSTATE_GUIDED, 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); + + return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf); +} + +static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val) +{ + return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val, + perf.nominal_perf); +} + const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode); int amd_pstate_get_status(void); int amd_pstate_update_status(const char *buf, size_t size); diff --git a/drivers/cpufreq/bmips-cpufreq.c b/drivers/cpufreq/bmips-cpufreq.c index a8e35bc75fb2..389fff5e6f65 100644 --- a/drivers/cpufreq/bmips-cpufreq.c +++ b/drivers/cpufreq/bmips-cpufreq.c @@ -132,8 +132,8 @@ static int bmips_cpufreq_init(struct cpufreq_policy *policy) freq_table = bmips_cpufreq_get_freq_table(policy); if (IS_ERR(freq_table)) { - pr_err("%s: couldn't determine frequency table (%ld).\n", - BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table)); + pr_err("%s: couldn't determine frequency table (%pe).\n", + BMIPS_CPUFREQ_NAME, freq_table); return PTR_ERR(freq_table); } diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 09e55bfdca88..957fa7775260 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -247,8 +247,7 @@ static int cppc_fie_kworker_init(void) worker = kthread_run_worker(0, "cppc_fie"); if (IS_ERR(worker)) { - pr_warn("%s: failed to create kworker_fie: %ld\n", __func__, - PTR_ERR(worker)); + pr_warn("%s: failed to create kworker_fie: %pe\n", __func__, worker); return PTR_ERR(worker); } diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 371ca1fe9020..bce4c87573a1 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -155,6 +155,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "nvidia,tegra234", }, { .compatible = "qcom,apq8096", }, + { .compatible = "qcom,milos", }, { .compatible = "qcom,msm8909", }, { .compatible = "qcom,msm8996", }, { .compatible = "qcom,msm8998", }, @@ -209,6 +210,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,ipq8064", }, { .compatible = "qcom,ipq8074", }, { .compatible = "qcom,ipq9574", }, + { .compatible = "qcom,ipq9650", }, { .compatible = "qcom,apq8064", }, { .compatible = "qcom,msm8974", }, { .compatible = "qcom,msm8960", }, diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 0b32ae28ec85..02bfd46543e9 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -85,10 +85,12 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) freq_step = get_freq_step(cs_tuners, policy); /* - * Decrease requested_freq one freq_step for each idle period that - * we didn't update the frequency. + * Apply deferred down steps only when the policy sample load is + * below down_threshold. Otherwise, multiple deferred down steps may + * cause a net frequency decrease outside the downscaling region. */ - if (policy_dbs->idle_periods < UINT_MAX) { + if (policy_dbs->max_sample_load < cs_tuners->down_threshold && + policy_dbs->idle_periods < UINT_MAX) { unsigned int freq_steps = policy_dbs->idle_periods * freq_step; if (requested_freq > policy->min + freq_steps) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 710d93ec89b5..f3bd3dbe7c41 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -124,7 +124,8 @@ unsigned int dbs_update(struct cpufreq_policy *policy) struct policy_dbs_info *policy_dbs = policy->governor_data; struct dbs_data *dbs_data = policy_dbs->dbs_data; unsigned int ignore_nice = dbs_data->ignore_nice_load; - unsigned int max_load = 0, idle_periods = UINT_MAX; + unsigned int max_load = 0, max_sample_load = 0; + unsigned int idle_periods = UINT_MAX; unsigned int sampling_rate, io_busy, j; u64 cur_nice; @@ -147,7 +148,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); u64 update_time, cur_idle_time; unsigned int idle_time, time_elapsed; - unsigned int load; + unsigned int load, sample_load; cur_idle_time = get_cpu_idle_time(j, &update_time, io_busy); @@ -186,6 +187,20 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_nice = cur_nice; + /* + * Compute the sample load separately from the prev_load value + * that may be reused after a long idle interval. The conservative + * governor uses it to decide whether to apply deferred down steps. + * If no time has elapsed, retain the existing behavior and use + * prev_load. + */ + if (unlikely(!time_elapsed)) + sample_load = j_cdbs->prev_load; + else if (time_elapsed > idle_time) + sample_load = 100 * (time_elapsed - idle_time) / time_elapsed; + else + sample_load = 0; + if (unlikely(!time_elapsed)) { /* * That can only happen when this function is called @@ -220,11 +235,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) load = j_cdbs->prev_load; j_cdbs->prev_load = 0; } else { - if (time_elapsed > idle_time) - load = 100 * (time_elapsed - idle_time) / time_elapsed; - else - load = 0; - + load = sample_load; j_cdbs->prev_load = load; } @@ -237,9 +248,13 @@ unsigned int dbs_update(struct cpufreq_policy *policy) if (load > max_load) max_load = load; + + if (sample_load > max_sample_load) + max_sample_load = sample_load; } policy_dbs->idle_periods = idle_periods; + policy_dbs->max_sample_load = max_sample_load; return max_load; } diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 73b8ed7cfaae..806d8fb4dff1 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -95,6 +95,8 @@ struct policy_dbs_info { /* Multiplier for increasing sample delay temporarily. */ unsigned int rate_mult; unsigned int idle_periods; /* For conservative */ + /* Maximum load from the current policy sample. */ + unsigned int max_sample_load; /* Status indicators */ bool is_shared; /* This object is used by multiple CPUs */ bool work_in_progress; /* Work is being queued up or in progress */ diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index 1e8715ea1b77..aea7c31e155a 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -16,14 +16,16 @@ #include #include #include +#include +#include /* Message */ union smc_message { u32 value; struct { - u32 id : 4; + u32 id : 8; u32 info : 4; - u32 val : 16; + u32 val : 12; u32 cmd : 6; u32 extra : 1; u32 complete : 1; @@ -164,27 +166,47 @@ union smc_message { #define FREQ_MAX_LEVEL 16 +#define MMIO_SMCMBX(node) \ + ((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX)) + +#define MMIO_MISC_FUNC(node) \ + ((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC)) + struct loongson3_freq_data { unsigned int def_freq_level; struct cpufreq_frequency_table table[]; }; -static struct mutex cpufreq_mutex[MAX_PACKAGES]; +static struct mutex cpufreq_mutex[MAX_NUMNODES]; static struct cpufreq_driver loongson3_cpufreq_driver; static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data); static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra) { int retries; - unsigned int cpu = raw_smp_processor_id(); - unsigned int package = cpu_data[cpu].package; + unsigned int cpu = 0, nid; union smc_message msg, last; - mutex_lock(&cpufreq_mutex[package]); +#ifdef CONFIG_SMP + switch (cmd) { + case CMD_GET_FREQ_INFO: + case CMD_SET_FREQ_INFO: + case CMD_GET_FREQ_LEVEL_NUM: + case CMD_GET_FREQ_LEVEL_INFO: + case CMD_GET_FREQ_BOOST_LEVEL: + cpu = cpu_number_map(id); + break; + default: + cpu = raw_smp_processor_id(); + } +#endif + nid = cpu_to_node(cpu); - last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX); + mutex_lock(&cpufreq_mutex[nid]); + + last.value = readl(MMIO_SMCMBX(nid)); if (!last.complete) { - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return -EPERM; } @@ -195,12 +217,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext msg.extra = extra; msg.complete = 0; - iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX); - iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT, - LOONGARCH_IOCSR_MISC_FUNC); + writel(msg.value, MMIO_SMCMBX(nid)); + writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid)); for (retries = 0; retries < 10000; retries++) { - msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX); + msg.value = readl(MMIO_SMCMBX(nid)); if (msg.complete) break; @@ -208,49 +229,51 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext } if (!msg.complete || msg.cmd != CMD_OK) { - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return -EPERM; } - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return msg.val; } static unsigned int loongson3_cpufreq_get(unsigned int cpu) { - int ret; + int ret, core = cpu_logical_map(cpu); - ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); - return ret * KILO; + return (ret < 0) ? 0 : ret * KILO; } static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) { - int ret; + int ret, core = cpu_logical_map(policy->cpu); - ret = do_service_request(cpu_data[policy->cpu].core, - FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, + index, 0); return (ret >= 0) ? 0 : ret; } static int configure_freq_table(int cpu) { - int i, ret, boost_level, max_level, freq_level; + int i, ret, core, boost_level, max_level, freq_level; struct platform_device *pdev = cpufreq_get_driver_data(); struct loongson3_freq_data *data; if (per_cpu(freq_data, cpu)) return 0; - ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0); + core = cpu_logical_map(cpu); + + ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0); if (ret < 0) return ret; max_level = ret; - ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0); + ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0); if (ret < 0) return ret; boost_level = ret; @@ -263,7 +286,7 @@ static int configure_freq_table(int cpu) data->def_freq_level = boost_level - 1; for (i = 0; i < freq_level; i++) { - ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0); if (ret < 0) { devm_kfree(&pdev->dev, data); return ret; @@ -337,7 +360,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev) { int i, ret; - for (i = 0; i < MAX_PACKAGES; i++) { + for (i = 0; i < MAX_NUMNODES; i++) { ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]); if (ret) return ret; diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 052ca7cd2f4f..f9b1eca176eb 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -603,6 +603,7 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy) policy->freq_table = freq_table; policy->driver_data = info; policy->clk = info->cpu_clk; + policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(info->cpu_dev) / 1000; return 0; } @@ -624,6 +625,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = { .init = mtk_cpufreq_init, .exit = mtk_cpufreq_exit, .register_em = cpufreq_register_em_with_opp, + .suspend = cpufreq_generic_suspend, .name = "mtk-cpufreq", }; diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 874ff3fb9d97..060a460716f7 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -322,6 +322,8 @@ static void qcom_get_related_cpus(int index, struct cpumask *m) if (index == args.args[0]) cpumask_set_cpu(cpu, m); + + of_node_put(args.np); } } @@ -554,6 +556,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) return ret; index = args.args[0]; + of_node_put(args.np); data = &qcom_cpufreq.data[index]; /* HW should be in enabled state to proceed */ diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index efa766e98d86..375f12d5d11a 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -211,6 +211,14 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, case QCOM_ID_IPQ5404: drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0); break; + case QCOM_ID_IPQ9610: + case QCOM_ID_IPQ9620: + case QCOM_ID_IPQ9630: + case QCOM_ID_IPQ9640: + case QCOM_ID_IPQ9650: + case QCOM_ID_IPQ9670: + drv->versions = (*speedbin == 0x4b) ? BIT(3) : BIT(2); + break; case QCOM_ID_MSM8996SG: case QCOM_ID_APQ8096SG: drv->versions = 1 << ((unsigned int)(*speedbin) + 4); @@ -633,6 +641,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u { .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 }, { .compatible = "qcom,apq8064", .data = &match_data_krait }, { .compatible = "qcom,ipq9574", .data = &match_data_kryo }, + { .compatible = "qcom,ipq9650", .data = &match_data_kryo }, { .compatible = "qcom,msm8974", .data = &match_data_krait }, { .compatible = "qcom,msm8960", .data = &match_data_krait }, {}, diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 42edb41ad459..a21b6f01218a 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -57,8 +57,7 @@ static u32 get_bus_freq(void) /* get platform freq by its clock name */ pltclk = clk_get(NULL, "cg-pll0-div1"); if (IS_ERR(pltclk)) { - pr_err("%s: can't get bus frequency %ld\n", - __func__, PTR_ERR(pltclk)); + pr_err("%s: can't get bus frequency %pe\n", __func__, pltclk); return PTR_ERR(pltclk); } diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index d7ead60bf8c2..2b1f18b7a1e7 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -225,4 +225,5 @@ fn probe<'bound>( authors: ["Viresh Kumar "], description: "Generic CPUFreq DT driver", license: "GPL v2", + alias: ["platform:cpufreq-dt"], } diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 9cef71528076..9a01592425ee 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -152,15 +152,14 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) policy->clk = clk_get(NULL, "armclk"); if (IS_ERR(policy->clk)) { - pr_err("Unable to obtain ARMCLK: %ld\n", - PTR_ERR(policy->clk)); + pr_err("Unable to obtain ARMCLK: %pe\n", policy->clk); return PTR_ERR(policy->clk); } #ifdef CONFIG_REGULATOR vddarm = regulator_get(NULL, "vddarm"); if (IS_ERR(vddarm)) { - pr_err("Failed to obtain VDDARM: %ld\n", PTR_ERR(vddarm)); + pr_err("Failed to obtain VDDARM: %pe\n", vddarm); pr_err("Only frequency scaling available\n"); vddarm = NULL; } else { diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index a68706406b88..5cda391ad03b 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -282,12 +282,11 @@ static int us2e_freq_cpu_init(struct cpufreq_policy *policy) table[1].frequency = clock_tick / 2; table[2].driver_data = 2; table[2].frequency = clock_tick / 4; - table[2].driver_data = 3; - table[2].frequency = clock_tick / 6; - table[2].driver_data = 4; - table[2].frequency = clock_tick / 8; - table[2].driver_data = 5; - table[3].frequency = CPUFREQ_TABLE_END; + table[3].driver_data = 3; + table[3].frequency = clock_tick / 6; + table[4].driver_data = 4; + table[4].frequency = clock_tick / 8; + table[5].frequency = CPUFREQ_TABLE_END; policy->cpuinfo.transition_latency = 0; policy->cur = clock_tick; diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c index b15b3142b5fe..144ac0f1d85d 100644 --- a/drivers/cpufreq/sti-cpufreq.c +++ b/drivers/cpufreq/sti-cpufreq.c @@ -263,7 +263,7 @@ static int __init sti_cpufreq_init(void) ddata.cpu = get_cpu_device(0); if (!ddata.cpu) { - dev_err(ddata.cpu, "Failed to get device for CPU0\n"); + pr_err("Failed to get device for CPU0\n"); goto skip_voltage_scaling; } @@ -281,7 +281,7 @@ static int __init sti_cpufreq_init(void) goto register_cpufreq_dt; skip_voltage_scaling: - dev_err(ddata.cpu, "Not doing voltage scaling\n"); + pr_err("Not doing voltage scaling\n"); register_cpufreq_dt: platform_device_register_simple("cpufreq-dt", -1, NULL, 0); diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c index c6375e14d445..1eb96e9f3310 100644 --- a/drivers/cpufreq/tegra194-cpufreq.c +++ b/drivers/cpufreq/tegra194-cpufreq.c @@ -367,7 +367,7 @@ static void tegra194_get_cpu_ndiv_sysreg(void *ndiv) static int tegra194_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv) { - return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, &ndiv, true); + return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, ndiv, true); } static void tegra194_set_cpu_ndiv_sysreg(void *data) diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 3f0005abac64..5bcdb0e553b9 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -202,7 +202,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) @@ -321,7 +323,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; } diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 49ccd6f1c185..14405a614991 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -73,6 +73,18 @@ static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy) (s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; } +static void sugov_update_rate_limit_us_sync(struct sugov_policy *sg_policy) +{ + /* + * Serialize with rate_limit_us_store() to prevent this path from using + * a stale rate limit tunable value to set the limit for the given + * policy. + */ + guard(mutex)(&sg_policy->tunables->attr_set.update_lock); + + sugov_update_rate_limit_us(sg_policy); +} + static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) { s64 delta_ns; @@ -861,7 +873,7 @@ static int sugov_start(struct cpufreq_policy *policy) void (*uu)(struct update_util_data *data, u64 time, unsigned int flags); unsigned int cpu; - sugov_update_rate_limit_us(sg_policy); + sugov_update_rate_limit_us_sync(sg_policy); sg_policy->last_freq_update_time = 0; sg_policy->next_freq = 0; sg_policy->work_in_progress = false; diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index affa2b9490ef..b75d692846a2 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -14,7 +14,13 @@ cpumask, device::{Bound, Device}, devres, - error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR}, + error::{ + code::*, + from_result, + to_result, + Result, + VTABLE_DEFAULT_ERROR, // + }, ffi::{c_char, c_ulong}, prelude::*, types::ForeignOwnable, @@ -29,7 +35,10 @@ marker::PhantomData, ops::{Deref, DerefMut}, pin::Pin, - ptr, + ptr::{ + self, + NonNull, // + }, }; use macros::vtable; @@ -687,12 +696,13 @@ fn clear_data(&mut self) -> Option { impl<'a> PolicyCpu<'a> { fn from_cpu(cpu: CpuId) -> Result { // SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU. - let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?; + let ptr = + NonNull::new(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) }).ok_or(ENODEV)?; Ok(Self( - // SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of - // the returned reference. - unsafe { Policy::from_raw_mut(ptr) }, + // SAFETY: `ptr` is non-NULL and `cpufreq_cpu_get()` took a reference on it, so it is + // valid for writing and remains valid for the lifetime of the returned reference. + unsafe { Policy::from_raw_mut(ptr.as_ptr()) }, )) } }