From 68f34fad760b68e878aced43934cc02b9d1bed89 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:42 -0500 Subject: [PATCH 1/5] cpupower: Add generic CPPC performance display Arm64 machines, and possibly others, use the standard ACPI defined CPPC infrastructure. cpupower has CPPC support, but it's largely written around the intricacies of AMD processors, using platform MSRs to avoid shortcomings in the specification. Add a generic CPPC display that depends only on standardized fields. The computed frequency values are best effort and rely on the FW providing optional values that can be used to derive a meaningful frequency at a given unique performance level. Link: https://lore.kernel.org/r/20260709221344.1919794-2-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/cppc.c | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tools/power/cpupower/utils/helpers/cppc.c diff --git a/tools/power/cpupower/utils/helpers/cppc.c b/tools/power/cpupower/utils/helpers/cppc.c new file mode 100644 index 000000000000..3493ce8551ea --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cppc.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#include "helpers/helpers.h" +#include "cpufreq.h" +#include "acpi_cppc.h" + +#define cppc_to_frequency(perf) (roundf(slope * (perf) + intercept)) + +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding) +{ + int64_t nominal = acpi_cppc_get_data(cpu, NOMINAL_PERF); + int64_t nominal_freq = acpi_cppc_get_data(cpu, NOMINAL_FREQ) * 1000; + int64_t lowest = acpi_cppc_get_data(cpu, LOWEST_PERF); + int64_t lowest_freq = acpi_cppc_get_data(cpu, LOWEST_FREQ) * 1000; + unsigned long non_linear = acpi_cppc_get_data(cpu, LOWEST_NONLINEAR_PERF); + unsigned long highest = acpi_cppc_get_data(cpu, HIGHEST_PERF); + float slope, intercept; + + /* do the optional freq fields look invalid? */ + if (!nominal_freq || !lowest_freq || nominal == lowest) + return; + + slope = (float)(nominal_freq - lowest_freq) / (nominal - lowest); + intercept = lowest_freq - slope * lowest; + + printf(_(" CPPC limits:\n")); + printf(_(" Highest Performance: %lu. Maximum Frequency: "), + highest); + /* + * If boost isn't active, the cpuinfo_max doesn't indicate real max + * frequency. + */ + print_speed(cppc_to_frequency(highest), no_rounding); + printf(".\n"); + + printf(_(" Nominal Performance: %lu. Nominal Frequency: "), + acpi_cppc_get_data(cpu, NOMINAL_PERF)); + print_speed(nominal_freq, no_rounding); + printf(".\n"); + + printf(_(" Lowest Non-linear Performance: %lu. Lowest Non-linear Frequency: "), + non_linear); + print_speed(cppc_to_frequency(non_linear), no_rounding); + printf(".\n"); + + printf(_(" Lowest Performance: %lu. Lowest Frequency: "), + acpi_cppc_get_data(cpu, LOWEST_PERF)); + print_speed(lowest_freq, no_rounding); + printf(".\n"); +} From 6b8ff068542a62c0fd58a7134282d48dd8a729c9 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:43 -0500 Subject: [PATCH 2/5] cpupower: Build and call CPPC information on non-AMD processors Now that we have a generic CPPC printout, call it on !AMD processors. If it fails to detect CPPC, or the registers don't look reasonable then it will exit without printing anything. Link: https://lore.kernel.org/r/20260709221344.1919794-3-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 2 +- tools/power/cpupower/utils/cpufreq-info.c | 3 ++- tools/power/cpupower/utils/helpers/helpers.h | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 969716dfe8de..cd8b7315fe74 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -131,7 +131,7 @@ override CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \ UTIL_OBJS = utils/helpers/amd.o utils/helpers/msr.o \ utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \ - utils/helpers/pci.o utils/helpers/bitmask.o \ + utils/helpers/pci.o utils/helpers/bitmask.o utils/helpers/cppc.o \ utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \ utils/idle_monitor/hsw_ext_idle.o \ utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \ diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 5a242b491a9d..105f06e690cc 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -477,12 +477,13 @@ static int get_latency(unsigned int cpu, unsigned int human) } /* --performance / -c */ - static int get_perf_cap(unsigned int cpu) { if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) amd_pstate_show_perf_and_freq(cpu, no_rounding); + else + cppc_show_perf_and_freq(cpu, no_rounding); return 0; } diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index a3ad80b9c2c2..9c5126b63966 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -221,4 +221,6 @@ void print_online_cpus(void); void print_offline_cpus(void); void print_speed(unsigned long speed, int no_rounding); +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding); + #endif /* __CPUPOWERUTILS_HELPERS__ */ From 5100bd356cd315112c4e27e66e4f0129800eabd2 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Thu, 9 Jul 2026 17:13:44 -0500 Subject: [PATCH 3/5] cpupower: Print kernel and hardware frequency information The kernel asserted frequency from scaling_cur_freq may not always match the hardware reported frequency from cpuinfo_cur_freq. Print both values when they are available, and only print the unavailable message on x86 when the hardware frequency can't be read. Link: https://lore.kernel.org/r/20260709221344.1919794-4-jeremy.linton@arm.com Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/cpufreq-info.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 105f06e690cc..11629ae49f98 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -270,10 +270,10 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human) { unsigned long freq; - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) + freq = cpufreq_get_freq_hardware(cpu); + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF) && !freq) return -EINVAL; - freq = cpufreq_get_freq_hardware(cpu); printf(_(" current CPU frequency: ")); if (!freq) { printf("Unable to call hardware\n"); @@ -514,8 +514,8 @@ static void debug_output_one(unsigned int cpu) get_available_governors(cpu); get_policy(cpu); - if (get_freq_hardware(cpu, 1) < 0) - get_freq_kernel(cpu, 1); + get_freq_hardware(cpu, 1); + get_freq_kernel(cpu, 1); get_boost_mode(cpu); get_perf_cap(cpu); } From eeed6071ceda7104e3397dca24cfd3dc73948150 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 20 Jul 2026 13:14:54 -0500 Subject: [PATCH 4/5] cpupower: Add libm to cpupower for generic CPPC view The patch ("cpupower: Add generic CPPC performance display") uses roundf() but didn't include libm explicitly. This results in build breaks in environments where its not automatically inlined. Add libm to the cpupower makefile to correct this. Fixes: 68f34fad760b ("cpupower: Add generic CPPC performance display") Signed-off-by: Jeremy Linton Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index cd8b7315fe74..ab428e336d87 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -236,9 +236,9 @@ $(OUTPUT)%.o: %.c $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)$(LIBCPUPOWER) $(ECHO) " CC " $@ ifeq ($(strip $(STATIC)),true) - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lrt -lpci -L$(OUTPUT) -o $@ else - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ endif $(QUIET) $(STRIPCMD) $@ From adfe0057326ffbc2cd5ff69a57ec0e50b5e74095 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:37 +0900 Subject: [PATCH 5/5] cpupower: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Link: https://lore.kernel.org/linux-pm/20260723184538.3888637-36-ekffu200098@gmail.com/raw Signed-off-by: Sang-Heon Jeon Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/powercap-info.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/power/cpupower/utils/powercap-info.c b/tools/power/cpupower/utils/powercap-info.c index e53033488218..88a5edb76315 100644 --- a/tools/power/cpupower/utils/powercap-info.c +++ b/tools/power/cpupower/utils/powercap-info.c @@ -47,8 +47,6 @@ static int powercap_print_one_zone(struct powercap_zone *zone) printf("\n"); - if (ret != 0) - return ret; return ret; }