[PATCH v5 1/3] cpufreq: cppc: Add update_limits support for Highest Performance changes
Xueqin Luo <[email protected]>
| Newsgroups | dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
ACPI CPPC specification requires OSPM to re-evaluate the Highest Performance register when Notify(0x85) is received for a processor device. Implement cppc_cpufreq_update_limits() to refresh the cached highest_perf capability through cppc_get_highest_perf() and update policy->cpuinfo.max_freq from Highest Performance. Use refresh_frequency_limits() so policy->max follows the standard cpufreq_set_policy() path. cpuinfo.max_freq always tracks Highest Performance. When boost is disabled but still supported, constrain policy->max by updating the existing boost_freq_req (created in cpufreq_policy_init_qos) to nominal, instead of hiding the hardware maximum in cpuinfo. Signed-off-by: Xueqin Luo <[email protected]> --- drivers/cpufreq/cppc_cpufreq.c | 98 ++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 6fe0e972952a..089f734f851b 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -855,6 +855,103 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state) return 0; } +/** + * cppc_cpufreq_sync_boost_limits - Sync boost flag, cpuinfo max and boost QoS + * @policy: cpufreq policy + * @boost_supported: whether highest_perf currently exceeds nominal_perf + * + * Highest Performance can appear or disappear at runtime via Notify(0x85). + * + * cpuinfo.max_freq always tracks the hardware maximum derived from Highest + * Performance so that sysfs reflects Notify(0x85) updates. Boost being off + * is enforced by updating the existing boost_freq_req to nominal (capping + * policy->max) rather than by hiding the hardware max in cpuinfo. + * boost_freq_req itself is only created at policy init, not here. + */ +static void cppc_cpufreq_sync_boost_limits(struct cpufreq_policy *policy, + bool boost_supported) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + struct cppc_perf_caps *caps = &cpu_data->perf_caps; + unsigned int highest_freq, nominal_freq, qos_freq; + int ret; + + if (!boost_supported && policy->boost_enabled) + policy->boost_enabled = false; + + policy->boost_supported = boost_supported; + + highest_freq = cppc_perf_to_khz(caps, caps->highest_perf); + nominal_freq = cppc_perf_to_khz(caps, caps->nominal_perf); + + /* + * Report the current hardware maximum. If Highest dropped below + * Nominal (unusual, but possible with test overrides), never + * advertise more than Highest allows. + */ + policy->cpuinfo.max_freq = highest_freq; + + if (boost_supported && !policy->boost_enabled) + qos_freq = min(nominal_freq, highest_freq); + else + qos_freq = highest_freq; + + /* + * boost_freq_req is created in cpufreq_policy_init_qos() when + * boost_supported is true at policy init. Runtime Highest changes + * only update that existing request; they do not add or remove it. + */ + if (freq_qos_request_active(&policy->boost_freq_req)) { + ret = freq_qos_update_request(&policy->boost_freq_req, + qos_freq); + if (ret < 0) + pr_debug("CPU%d: failed to sync boost QoS: %d\n", + policy->cpu, ret); + } + + pr_debug("CPU%d: highest_perf=%u boost_en=%d cpuinfo_max=%u qos_max=%u\n", + policy->cpu, caps->highest_perf, policy->boost_enabled, + policy->cpuinfo.max_freq, qos_freq); +} + +static void cppc_cpufreq_update_limits(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data; + struct cppc_perf_caps *caps; + u64 prev_highest_perf; + u64 highest_perf; + int ret; + + guard(cpufreq_policy_write)(policy); + + cpu_data = policy->driver_data; + caps = &cpu_data->perf_caps; + + prev_highest_perf = caps->highest_perf; + + ret = cppc_get_highest_perf(policy->cpu, &highest_perf); + if (ret) + return; + + if (highest_perf == prev_highest_perf) + return; + + caps->highest_perf = highest_perf; + + /* + * Re-evaluate boost capability/status based on the updated Highest + * Performance. Boost is supported when highest_perf exceeds + * nominal_perf. + */ + cppc_cpufreq_sync_boost_limits(policy, + highest_perf > caps->nominal_perf); + + refresh_frequency_limits(policy); + + pr_debug("CPU%d: highest_perf updated %llu -> %llu\n", + policy->cpu, prev_highest_perf, highest_perf); +} + static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) { struct cppc_cpudata *cpu_data = policy->driver_data; @@ -1048,6 +1145,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = { .init = cppc_cpufreq_cpu_init, .exit = cppc_cpufreq_cpu_exit, .set_boost = cppc_cpufreq_set_boost, + .update_limits = cppc_cpufreq_update_limits, .attr = cppc_cpufreq_attr, .name = "cppc_cpufreq", }; -- 2.43.0