[PATCH] cpufreq: CPPC: Program policy limits during initialization

From: Christian Loehle

Date: Thu Nov 12 2026 - 06:38:10 EST


cppc_cpufreq_cpu_init() keeps valid firmware MIN/MAX limits and relies on
the governor to update them. If its first target equals policy->cur, the
core can skip the driver callback, allowing autonomous selection below
scaling_min_freq.

Program MIN from Lowest Nonlinear and MAX from Nominal or Highest according
to the boost state before starting the governor. Set Desired to MAX and
publish policy->cur only after the write succeeds. Leave policy->max unset
so the initial maximum QoS request does not restrict later boost use.

Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://sashiko.dev/#/patchset/20261106193304.3347374-1-sumitg@xxxxxxxxxx
Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks")
Link: https://lore.kernel.org/all/CAJZ5v0jQVRQhJzLoAy3btKovHGYkqGKJAP_5VfENM_WAREQnyQ@xxxxxxxxxxxxxx/
Signed-off-by: Christian Loehle <christian.loehle@xxxxxxx>
---
drivers/cpufreq/cppc_cpufreq.c | 34 +++++++++++++---------------------
1 file changed, 13 insertions(+), 21 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index a62589c0674f..92a5bb28571c 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1018,29 +1018,20 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->boost_supported = true;

/*
- * The request starts from the live limits, which may be unusable.
- * Firmware may leave them zeroed. An interrupted earlier update may
- * have left MIN above MAX. cppc_set_perf() would reject either, so
- * request the full range instead.
- */
- if (!cpu_data->perf_ctrls.max_perf ||
- cpu_data->perf_ctrls.min_perf > cpu_data->perf_ctrls.max_perf) {
- cpu_data->perf_ctrls.min_perf = caps->lowest_perf;
- cpu_data->perf_ctrls.min_perf_valid = true;
- cpu_data->perf_ctrls.max_perf = caps->highest_perf;
- }
-
- /*
- * Keep the initial request within controls left by an earlier driver
- * instance or firmware. The governor will update the full tuple later.
+ * Program the initial policy bounds before starting the governor. Its
+ * first target request may be skipped if it already matches policy->cur.
+ * cppc_set_perf() orders the writes against the live firmware limits.
+ *
+ * New policies start with boost disabled, so this matches the initial
+ * cpuinfo.max_freq. The core applies global boost after governor startup
+ * and handles any resulting limit change through QoS.
*/
- desired_perf = caps->highest_perf;
- if (cpu_data->perf_ctrls.max_perf)
- desired_perf = min(desired_perf, cpu_data->perf_ctrls.max_perf);
- if (cpu_data->perf_ctrls.min_perf_valid)
- desired_perf = max(desired_perf, cpu_data->perf_ctrls.min_perf);
+ cpu_data->perf_ctrls.min_perf = caps->lowest_nonlinear_perf;
+ cpu_data->perf_ctrls.min_perf_valid = true;
+ cpu_data->perf_ctrls.max_perf = policy->boost_enabled ?
+ caps->highest_perf : caps->nominal_perf;

- policy->cur = cppc_perf_to_khz(caps, desired_perf);
+ desired_perf = cpu_data->perf_ctrls.max_perf;
cpu_data->perf_ctrls.desired_perf = desired_perf;

cppc_cpufreq_policy_state(policy)->suspend_regs_handled = false;
@@ -1057,6 +1048,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto out;
}
cpu_data->perf_limits_valid = true;
+ policy->cur = cppc_perf_to_khz(caps, desired_perf);

cppc_cpufreq_cpu_fie_init(policy);
return 0;
--
2.34.1