[PATCH v6 2/2] cpufreq: CPPC: Reflect ospm_nominal_perf in boost and limits

Sumit Gupta <[email protected]>
Newsgroups org.kernel.vger.linux-tegra,dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.