Commit 16fb8d8a authored by Henry Tseng's avatar Henry Tseng Committed by Rafael J. Wysocki
Browse files

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: default avatarHenry Tseng <henrytseng@qnap.com>
Link: https://patch.msgid.link/20260324090948.1667340-1-henrytseng@qnap.com


Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 16c1e838
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1944,7 +1944,7 @@ static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
}

/* Look up the max frequency in DMI */
static u64 cppc_get_dmi_max_khz(void)
u64 cppc_get_dmi_max_khz(void)
{
	u16 mhz = 0;

@@ -1958,6 +1958,7 @@ static u64 cppc_get_dmi_max_khz(void)

	return KHZ_PER_MHZ * mhz;
}
EXPORT_SYMBOL_GPL(cppc_get_dmi_max_khz);

/*
 * If CPPC lowest_freq and nominal_freq registers are exposed then we can
+24 −7
Original line number Diff line number Diff line
@@ -675,6 +675,29 @@ static inline u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
}
#endif

static void acpi_cpufreq_resolve_max_freq(struct cpufreq_policy *policy,
					  unsigned int pss_max_freq)
{
#ifdef CONFIG_ACPI_CPPC_LIB
	u64 max_speed = cppc_get_dmi_max_khz();
	/*
	 * Use DMI "Max Speed" if it looks plausible: must be
	 * above _PSS P0 frequency and within 2x of it.
	 */
	if (max_speed > pss_max_freq && max_speed < pss_max_freq * 2) {
		policy->cpuinfo.max_freq = max_speed;
		return;
	}
#endif
	/*
	 * If the maximum "boost" frequency is unknown, ask the arch
	 * scale-invariance code to use the "nominal" performance for
	 * CPU utilization scaling so as to prevent the schedutil
	 * governor from selecting inadequate CPU frequencies.
	 */
	arch_set_max_freq_ratio(true);
}

static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *freq_table;
@@ -849,13 +872,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)

		policy->cpuinfo.max_freq = freq * max_boost_ratio >> SCHED_CAPACITY_SHIFT;
	} else {
		/*
		 * If the maximum "boost" frequency is unknown, ask the arch
		 * scale-invariance code to use the "nominal" performance for
		 * CPU utilization scaling so as to prevent the schedutil
		 * governor from selecting inadequate CPU frequencies.
		 */
		arch_set_max_freq_ratio(true);
		acpi_cpufreq_resolve_max_freq(policy, freq_table[0].frequency);
	}

	policy->freq_table = freq_table;
+1 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ extern int cppc_set_enable(int cpu, bool enable);
extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
extern bool cppc_perf_ctrs_in_pcc_cpu(unsigned int cpu);
extern bool cppc_perf_ctrs_in_pcc(void);
extern u64 cppc_get_dmi_max_khz(void);
extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf);
extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq);
extern bool acpi_cpc_valid(void);