From: Sumit Gupta <sumitg@nvidia.com>
To: <rafael@kernel.org>, <viresh.kumar@linaro.org>,
<pierre.gondois@arm.com>, <christian.loehle@arm.com>,
<ionela.voinescu@arm.com>, <zhenglifeng1@huawei.com>,
<zhanjie9@hisilicon.com>, <lenb@kernel.org>,
<saket.dumbre@intel.co>, <mario.limonciello@amd.com>,
<linux-kernel@vger.kernel.org>, <linux-pm@vger.kernel.org>,
<linux-acpi@vger.kernel.org>, <acpica-devel@lists.linux.dev>,
<linux-tegra@vger.kernel.org>
Cc: <treding@nvidia.com>, <jonathanh@nvidia.com>, <vsethi@nvidia.com>,
<ksitaraman@nvidia.com>, <sanjayc@nvidia.com>, <mochs@nvidia.com>,
<bbasu@nvidia.com>, <sumitg@nvidia.com>
Subject: [PATCH v7 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits
Date: Sat, 8 Aug 2026 03:18:37 +0530 [thread overview]
Message-ID: <20260807214837.863209-4-sumitg@nvidia.com> (raw)
In-Reply-To: <20260807214837.863209-1-sumitg@nvidia.com>
Boost is the performance range above nominal, so lowering the OSPM
Nominal Performance enlarges the boost range and drops the non-boost
ceiling. Keep the policy limits consistent with the register.
Add cppc_cpufreq_effective_nominal(), which returns the OSPM Nominal
Performance when set and the platform nominal otherwise. Use it for the
non-boost ceiling in set_boost(), and to update the policy limits when
ospm_nominal_freq is written.
The cpufreq core caps scaling_max_freq with a per-policy QoS request,
boost_freq_req, that it sets to cpuinfo.max_freq and refreshes only when
boost is toggled. A write to ospm_nominal_freq changes cpuinfo.max_freq
without toggling boost, so cppc_cpufreq_update_nominal_limits() updates
the request itself, rather than leaving scaling_max_freq at the old
nominal.
When boost is enabled the ceiling is highest_perf, not the nominal, so
the update is skipped. set_boost() applies the new nominal when boost is
turned off.
A platform with highest_perf == nominal_perf has no boost range at boot.
Lowering the OSPM nominal from sysfs can create one. The core, however,
adds its boost FREQ_QOS_MAX request only at policy setup, and only if
boost_supported is already set. Set boost_supported in init() when the
OSPM register is supported and highest_perf > lowest_perf. Boost can
then be enabled after the nominal is lowered.
Suggested-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 65 ++++++++++++++++++++++++++++++++--
1 file changed, 62 insertions(+), 3 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index fe714e71826a..bcac46ad25c3 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -892,6 +892,27 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
policy->driver_data = NULL;
}
+/*
+ * Return the non-boost performance ceiling: the OSPM Nominal Performance the
+ * driver last requested, or the platform-reported Nominal Performance if none
+ * was requested.
+ *
+ * The register is write-only, so the requested value comes from the
+ * software-tracked state rather than from hardware.
+ */
+static u32 cppc_cpufreq_effective_nominal(struct cpufreq_policy *policy)
+{
+ const struct cppc_saved_vals *st = cppc_cpufreq_policy_state(policy)->regs;
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u64 ospm_nominal = st[CPPC_SAVED_OSPM_NOMINAL_PERF].requested_val;
+
+ /* U64_MAX means OSPM has not selected a nominal level. */
+ if (ospm_nominal == U64_MAX)
+ return cpu_data->perf_caps.nominal_perf;
+
+ return (u32)ospm_nominal;
+}
+
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
@@ -950,9 +971,14 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
/*
* If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost
- * is supported.
+ * is supported. A writable OSPM Nominal Performance register can also
+ * open a boost range at runtime by lowering the nominal, so assume
+ * boost is supported in that case too, letting the core register its
+ * QoS request up front.
*/
- if (caps->highest_perf > caps->nominal_perf)
+ if (caps->highest_perf > caps->nominal_perf ||
+ (caps->highest_perf > caps->lowest_perf &&
+ cppc_ospm_nominal_perf_supported(cpu)))
policy->boost_supported = true;
/* Set policy->cur to max now. The governors will adjust later. */
@@ -1233,11 +1259,12 @@ static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state)
{
struct cppc_cpudata *cpu_data = policy->driver_data;
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+ u32 nominal = cppc_cpufreq_effective_nominal(policy);
if (state)
policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->highest_perf);
else
- policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, caps->nominal_perf);
+ policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, nominal);
return 0;
}
@@ -1411,6 +1438,36 @@ static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited)
CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered,
cppc_set_perf_limited)
+/*
+ * While boost is disabled, the nominal is the ceiling. Set cpuinfo.max_freq to
+ * it and update the core's boost_freq_req to match. The core only syncs
+ * boost_freq_req when boost is enabled or disabled, so a plain nominal change
+ * must update it here.
+ */
+static void cppc_cpufreq_update_nominal_limits(struct cpufreq_policy *policy)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u32 nominal;
+ int ret;
+
+ if (policy->boost_enabled)
+ return;
+
+ nominal = cppc_cpufreq_effective_nominal(policy);
+ policy->cpuinfo.max_freq = cppc_perf_to_khz(&cpu_data->perf_caps,
+ nominal);
+
+ if (freq_qos_request_active(&policy->boost_freq_req)) {
+ ret = freq_qos_update_request(&policy->boost_freq_req,
+ policy->cpuinfo.max_freq);
+ if (ret < 0)
+ pr_debug("Failed to update boost limit on CPU%u (%d)\n",
+ policy->cpu, ret);
+ }
+
+ refresh_frequency_limits(policy);
+}
+
static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
@@ -1442,6 +1499,8 @@ static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
st->requested_val = perf;
st->firmware_val = cpu_data->perf_caps.nominal_perf;
+ cppc_cpufreq_update_nominal_limits(policy);
+
return count;
}
--
2.34.1
prev parent reply other threads:[~2026-08-07 21:49 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 21:48 [PATCH v7 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
2026-08-07 21:48 ` [PATCH v7 1/3] ACPI: " Sumit Gupta
2026-08-07 21:48 ` [PATCH v7 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute Sumit Gupta
2026-08-07 21:48 ` Sumit Gupta [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260807214837.863209-4-sumitg@nvidia.com \
--to=sumitg@nvidia.com \
--cc=acpica-devel@lists.linux.dev \
--cc=bbasu@nvidia.com \
--cc=christian.loehle@arm.com \
--cc=ionela.voinescu@arm.com \
--cc=jonathanh@nvidia.com \
--cc=ksitaraman@nvidia.com \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=mochs@nvidia.com \
--cc=pierre.gondois@arm.com \
--cc=rafael@kernel.org \
--cc=saket.dumbre@intel.co \
--cc=sanjayc@nvidia.com \
--cc=treding@nvidia.com \
--cc=viresh.kumar@linaro.org \
--cc=vsethi@nvidia.com \
--cc=zhanjie9@hisilicon.com \
--cc=zhenglifeng1@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.