All of lore.kernel.org
 help / color / mirror / Atom feed
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>, <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 v6 2/2] cpufreq: CPPC: Reflect ospm_nominal_perf in boost and limits
Date: Sat, 18 Jul 2026 03:23:30 +0530	[thread overview]
Message-ID: <20260717215330.2215058-3-sumitg@nvidia.com> (raw)
In-Reply-To: <20260717215330.2215058-1-sumitg@nvidia.com>

OSPM Nominal Performance lets OSPM request a nominal level below the
platform-reported nominal. Since boost is the range above nominal,
lowering the nominal enlarges the boost range and drops the non-boost
ceiling.

cppc_cpufreq currently uses the platform nominal as the non-boost
ceiling. Changing the OSPM nominal then leaves the policy limits
unchanged, so the boost and non-boost ranges no longer match the
register.

Reflect the OSPM nominal in the cpufreq policy so that boost and the
frequency limits stay consistent with the register:

  - Add cppc_cpufreq_get_effective_nominal(), which yields the OSPM
    Nominal Performance when set and the platform nominal otherwise.
  - Use it to derive cpuinfo.max_freq for the non-boost ceiling in init()
    and in set_boost() while boost is disabled.

The cpufreq core includes a per-policy boost QoS request among the
constraints that cap scaling_max_freq. It updates that request to match
cpuinfo.max_freq, but only when boost is toggled. Writing
ospm_nominal_freq changes cpuinfo.max_freq without toggling boost, so the
request (boost_freq_req) is left stale and keeps scaling_max_freq at the
old nominal. cppc_cpufreq_reflect_nominal() therefore updates the request
directly when the nominal changes while boost is disabled.

At boot, when highest_perf == nominal_perf there is no boost range, but
lowering the OSPM nominal later from sysfs can create one if
highest_perf > lowest_perf. Since the core registers its boost
FREQ_QOS_MAX request at policy setup only when boost_supported is already
set, set boost_supported in init() when the OSPM register is supported
and such a range is possible. Boost can then be enabled once the nominal
is lowered.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 119 ++++++++++++++++++++++++++++++---
 1 file changed, 111 insertions(+), 8 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index eb6746810fa6..048bb567ec45 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -761,11 +761,63 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
 	policy->driver_data = NULL;
 }
 
+/**
+ * cppc_cpufreq_get_effective_nominal() - Get the effective nominal performance
+ * @policy: cpufreq policy associated with the CPU
+ * @nominal: Updated with the effective nominal performance
+ * @supported: Updated with whether OSPM Nominal Performance is supported.
+ *	       Pass NULL if not needed
+ *
+ * Use the OSPM Nominal Performance value when the register is supported and
+ * contains a nonzero value. Otherwise, use the platform-reported Nominal
+ * Performance. The resulting value is the non-boost performance ceiling.
+ *
+ * Return: 0 on success, or a negative error code if the register cannot be
+ * read or contains a value outside the supported performance range.
+ */
+static int cppc_cpufreq_get_effective_nominal(struct cpufreq_policy *policy,
+					      u32 *nominal, bool *supported)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+	u32 effective_nominal = caps->nominal_perf;
+	bool ospm_supported = false;
+	u64 ospm_nominal;
+	int ret;
+
+	ret = cppc_get_ospm_nominal_perf(policy->cpu, &ospm_nominal);
+	if (ret == -EOPNOTSUPP)
+		goto out;
+	if (ret)
+		return ret;
+
+	ospm_supported = true;
+
+	/* A zero value means OSPM has not selected a nominal level. */
+	if (!ospm_nominal)
+		goto out;
+
+	if (ospm_nominal < caps->lowest_perf ||
+	    ospm_nominal > caps->nominal_perf)
+		return -EINVAL;
+
+	effective_nominal = (u32)ospm_nominal;
+
+out:
+	*nominal = effective_nominal;
+	if (supported)
+		*supported = ospm_supported;
+
+	return 0;
+}
+
 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	unsigned int cpu = policy->cpu;
 	struct cppc_cpudata *cpu_data;
 	struct cppc_perf_caps *caps;
+	bool ospm_supported;
+	u32 nominal, perf;
 	int ret;
 
 	cpu_data = cppc_cpufreq_get_cpu_data(cpu);
@@ -788,8 +840,17 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	 * nonlinear perf
 	 */
 	policy->cpuinfo.min_freq = cppc_perf_to_khz(caps, caps->lowest_perf);
-	policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, policy->boost_enabled ?
-						    caps->highest_perf : caps->nominal_perf);
+
+	ret = cppc_cpufreq_get_effective_nominal(policy, &nominal,
+						 &ospm_supported);
+	if (ret) {
+		pr_debug("CPU%u: failed to get effective nominal: %d\n",
+			 cpu, ret);
+		goto out;
+	}
+
+	perf = policy->boost_enabled ? caps->highest_perf : nominal;
+	policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, perf);
 
 	policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu);
 	policy->shared_type = cpu_data->shared_type;
@@ -819,9 +880,13 @@ 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 ||
+	    (ospm_supported && caps->highest_perf > caps->lowest_perf))
 		policy->boost_supported = true;
 
 	/* Set policy->cur to max now. The governors will adjust later. */
@@ -992,11 +1057,16 @@ 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 perf = caps->highest_perf;
+	int ret;
 
-	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);
+	if (!state) {
+		ret = cppc_cpufreq_get_effective_nominal(policy, &perf, NULL);
+		if (ret)
+			return ret;
+	}
+
+	policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, perf);
 
 	return 0;
 }
@@ -1025,6 +1095,35 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
 	return sysfs_emit(buf, "%d\n", val);
 }
 
+static int cppc_cpufreq_reflect_nominal(struct cpufreq_policy *policy,
+					u32 nominal)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	int ret;
+
+	/*
+	 * 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.
+	 */
+	if (!policy->boost_enabled) {
+		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)
+				return ret;
+		}
+	}
+
+	refresh_frequency_limits(policy);
+
+	return 0;
+}
+
 static ssize_t store_auto_select(struct cpufreq_policy *policy,
 				 const char *buf, size_t count)
 {
@@ -1208,6 +1307,10 @@ static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
 	if (ret)
 		return ret;
 
+	ret = cppc_cpufreq_reflect_nominal(policy, perf);
+	if (ret)
+		return ret;
+
 	return count;
 }
 
-- 
2.34.1


      parent reply	other threads:[~2026-07-17 21:54 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-17 21:53 [PATCH v6 0/2] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
2026-07-17 21:53 ` [PATCH v6 1/2] ACPI: " Sumit Gupta
2026-07-17 21:53 ` 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=20260717215330.2215058-3-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=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.