[PATCH v3 0/2] arm64/cpufreq: report and track frequencies above 4.19 GHz

From: Oleg Keri

Date: Thu Sep 10 2026 - 02:44:42 EST


The Snapdragon X2 Elite (Glymur) is the first arm64 laptop part I have
seen whose boost OPP, 4723200 kHz, sits above 4194304 kHz. Two
independent problems become visible there, both of which make the
kernel believe a boosted CPU is running slower than it is.

Patch 1 fixes an overflow in arch_freq_get_on_cpu(): the u64 product of
the frequency scale and the reference frequency is truncated to
unsigned int before being shifted back down, which wraps for any
reference frequency above 2^32 / SCHED_CAPACITY_SCALE = 4194304 kHz.

Patch 2 makes capacity_freq_ref follow the boost state. It is latched
once on CPUFREQ_CREATE_POLICY, and boost frequencies are excluded from
policy->cpuinfo.max_freq while boost is off, so on a machine that boots
with boost disabled it keeps the sustained maximum forever. On arm64
that saturates the AMU frequency scale at SCHED_CAPACITY_SCALE, so the
scheduler cannot distinguish a boosted CPU from one at the sustained
maximum, and arch_freq_get_on_cpu() cannot report above it.

The order matters: patch 2 is what raises capacity_freq_ref past
4194304 kHz on this machine, so patch 1 has to land with or before it.

Measured on a Lenovo Yoga Slim 7x Gen 11 (Glymur, 4032000 kHz
sustained, 4723200 kHz boost), pinning a policy to a single OPP and
timing a fixed workload on one of its CPUs:

requested OPP time cpuinfo_avg_freq
---------------------------------------------------------
4032000 kHz 2.011s 4031325 (0.02% low)
4723200 kHz 1.726s 524283 before
4723200 kHz 1.726s 4032000 with patch 1 only
4723200 kHz 1.726s 4718587 with both (0.10% low)

The timings never change: 2.011 / 1.726 = 1.165 against a frequency
ratio of 4723200 / 4032000 = 1.171, so the hardware was running at the
requested frequency throughout. Only the kernel's view of it was wrong.

Note that cpuinfo_cur_freq still reports 4032000 kHz at the boost OPP
on this machine. That is a separate path -- scmi_dvfs_freq_get()
asking firmware for the current performance level -- with no clamp in
the kernel, and it is not addressed here.

Changes in v3:
- Patch 1: drop the Suggested-by trailer. It was not warranted for a
review comment on the shape of an existing patch; my mistake.
- No code changes.
- Link to v2: https://lore.kernel.org/all/20260909192351.33910-1-okerixx@xxxxxxxxx/

Changes in v2:
- Patch 1: fold the multiply and the shift into a single expression, as
suggested on v1, instead of reusing the u64 scale variable as scratch.
Same semantics - the multiply and the shift both stay in u64 and only
the final value is narrowed to the unsigned int - but it reads better.
- Patch 2: unchanged.
- Link to v1: https://lore.kernel.org/all/cover.1788712186.git.okerixx@xxxxxxxxx/


Oleg Keri (2):
arm64: topology: fix arch_freq_get_on_cpu() overflow above 4.19 GHz
cpufreq: update capacity_freq_ref when the boost state changes

arch/arm/include/asm/topology.h | 1 +
arch/arm64/include/asm/topology.h | 1 +
arch/arm64/kernel/topology.c | 3 +--
arch/riscv/include/asm/topology.h | 1 +
drivers/base/arch_topology.c | 19 +++++++++++++------
drivers/cpufreq/cpufreq.c | 2 ++
include/linux/arch_topology.h | 1 +
include/linux/cpufreq.h | 7 +++++++
8 files changed, 27 insertions(+), 8 deletions(-)

--
2.55.0


base-commit: df2908090cda368b01ff43709f51890076c56157