Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpufreq: acpi-cpufreq: use DMI max speed when CPPC is unavailable

On AMD Ryzen Embedded V1780B (Family 17h, Zen 1), the BIOS does not
provide ACPI _CPC objects and the CPU does not support MSR-based CPPC
(X86_FEATURE_CPPC). The _PSS table only lists nominal P-states
(P0 = 3350 MHz), so when get_max_boost_ratio() fails at
cppc_get_perf_caps(), cpuinfo_max_freq reports only the base frequency
instead of the rated boost frequency (3600 MHz).

dmesg:
ACPI CPPC: No CPC descriptor for CPU:0
acpi_cpufreq: CPU0: Unable to get performance capabilities (-19)

cppc-cpufreq already has a DMI fallback (cppc_get_dmi_max_khz()) that
reads the processor max speed from SMBIOS Type 4. Export it and reuse
it in acpi-cpufreq as a last-resort source for the boost frequency.

A sanity check ensures the DMI value is above the _PSS P0 frequency
and within 2x of it; values outside that range are ignored and the
existing arch_set_max_freq_ratio() path is taken instead. The 2x
upper bound is based on a survey of the AMD Ryzen Embedded V1000
series, where the highest boost-to-base ratio is 1.8x (V1404I:
2.0 GHz base / 3.6 GHz boost).

The DMI lookup and sanity check are wrapped in a helper,
acpi_cpufreq_resolve_max_freq(), which falls through to
arch_set_max_freq_ratio() if the DMI value is absent or
out of range.

Tested on AMD Ryzen Embedded V1780B with v7.0-rc4:

Before: cpuinfo_max_freq = 3350000 (base only)
After: cpuinfo_max_freq = 3600000 (includes boost)

Link: https://www.amd.com/en/products/embedded/ryzen/ryzen-v1000-series.html#specifications
Signed-off-by: Henry Tseng <henrytseng@qnap.com>
Link: https://patch.msgid.link/20260324090948.1667340-1-henrytseng@qnap.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Henry Tseng and committed by
Rafael J. Wysocki
16fb8d8a 16c1e838

+27 -8
+2 -1
drivers/acpi/cppc_acpi.c
··· 1944 1944 } 1945 1945 1946 1946 /* Look up the max frequency in DMI */ 1947 - static u64 cppc_get_dmi_max_khz(void) 1947 + u64 cppc_get_dmi_max_khz(void) 1948 1948 { 1949 1949 u16 mhz = 0; 1950 1950 ··· 1958 1958 1959 1959 return KHZ_PER_MHZ * mhz; 1960 1960 } 1961 + EXPORT_SYMBOL_GPL(cppc_get_dmi_max_khz); 1961 1962 1962 1963 /* 1963 1964 * If CPPC lowest_freq and nominal_freq registers are exposed then we can
+24 -7
drivers/cpufreq/acpi-cpufreq.c
··· 675 675 } 676 676 #endif 677 677 678 + static void acpi_cpufreq_resolve_max_freq(struct cpufreq_policy *policy, 679 + unsigned int pss_max_freq) 680 + { 681 + #ifdef CONFIG_ACPI_CPPC_LIB 682 + u64 max_speed = cppc_get_dmi_max_khz(); 683 + /* 684 + * Use DMI "Max Speed" if it looks plausible: must be 685 + * above _PSS P0 frequency and within 2x of it. 686 + */ 687 + if (max_speed > pss_max_freq && max_speed < pss_max_freq * 2) { 688 + policy->cpuinfo.max_freq = max_speed; 689 + return; 690 + } 691 + #endif 692 + /* 693 + * If the maximum "boost" frequency is unknown, ask the arch 694 + * scale-invariance code to use the "nominal" performance for 695 + * CPU utilization scaling so as to prevent the schedutil 696 + * governor from selecting inadequate CPU frequencies. 697 + */ 698 + arch_set_max_freq_ratio(true); 699 + } 700 + 678 701 static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) 679 702 { 680 703 struct cpufreq_frequency_table *freq_table; ··· 872 849 873 850 policy->cpuinfo.max_freq = freq * max_boost_ratio >> SCHED_CAPACITY_SHIFT; 874 851 } else { 875 - /* 876 - * If the maximum "boost" frequency is unknown, ask the arch 877 - * scale-invariance code to use the "nominal" performance for 878 - * CPU utilization scaling so as to prevent the schedutil 879 - * governor from selecting inadequate CPU frequencies. 880 - */ 881 - arch_set_max_freq_ratio(true); 852 + acpi_cpufreq_resolve_max_freq(policy, freq_table[0].frequency); 882 853 } 883 854 884 855 policy->freq_table = freq_table;
+1
include/acpi/cppc_acpi.h
··· 156 156 extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); 157 157 extern bool cppc_perf_ctrs_in_pcc_cpu(unsigned int cpu); 158 158 extern bool cppc_perf_ctrs_in_pcc(void); 159 + extern u64 cppc_get_dmi_max_khz(void); 159 160 extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf); 160 161 extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq); 161 162 extern bool acpi_cpc_valid(void);