All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support
@ 2026-10-30 17:27 Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 1/3] ACPI: " Sumit Gupta
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Sumit Gupta @ 2026-10-30 17:27 UTC (permalink / raw)
  To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
	ionela.voinescu, zhenglifeng1, zhanjie9, lenb, mario.limonciello,
	maciej.wieczor-retman, pawel.chmielewski, linux-pm, linux-acpi,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	mochs, bbasu, sumitg

This series adds support for the OSPM Nominal Performance register
(ACPI 6.6, Section 8.4.6.1.2.6) to the CPPC ACPI and cpufreq drivers.

Unlike the read-only Nominal Performance register, OSPM Nominal
Performance is writable and lets OSPM request a nominal level below
the platform-reported nominal. The platform treats performance above
this level as boost and below as throttle.

 - Patch 1: adds ACPI helpers to write the register and to check that
   the platform supports it.

 - Patch 2: adds the per-policy ospm_nominal_freq attribute in kHz. The
   value is kept across CPU hotplug and suspend/resume. The register
   cannot be read back, so a read returns the last value written.

 - Patch 3: uses the OSPM nominal as the maximum frequency while boost
   is off. Lowering the nominal creates a boost range on a platform
   that has none, and boost can then be enabled.

This series applies on top of v7 of:
  cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload
  https://lore.kernel.org/lkml/20261030092416.1993889-1-sumitg@nvidia.com/

v7 -> v8:
 - Rebase on Christian's CPPC fixes series.
 - Patch 1:
   - use cpc_is_writable() from that series and drop cpc_reg_writable().
   - add the register to the list of controls the driver writes.
   - take cpc_desc_lock in cppc_ospm_nominal_perf_supported(), so the
     descriptor cannot be removed during the check.
 - Patch 2:
   - make ospm_nominal_freq read-write. A read returns the last value
     written, or "<unset>" before the first write. (Christian, Mario)
   - keep saving the values in the store handler, not at init(), so
     the driver writes the register only after the OS sets it. (Pierre)
 - Patch 3:
   - document in the ABI file how the OSPM nominal sets cpuinfo_max_freq
     and caps scaling_max_freq.

Sumit Gupta (3):
  ACPI: CPPC: Add ospm_nominal_perf support
  cpufreq: CPPC: Add ospm_nominal_freq attribute
  cpufreq: CPPC: Reflect the OSPM nominal in boost and limits

 .../ABI/testing/sysfs-devices-system-cpu      |  31 ++++
 drivers/acpi/cppc_acpi.c                      |  44 +++++
 drivers/cpufreq/cppc_cpufreq.c                | 173 +++++++++++++++++-
 include/acpi/cppc_acpi.h                      |  10 +
 4 files changed, 248 insertions(+), 10 deletions(-)

v7: https://lore.kernel.org/lkml/20260807214837.863209-1-sumitg@nvidia.com/
v6: https://lore.kernel.org/lkml/20260717215330.2215058-1-sumitg@nvidia.com/
v5: https://lore.kernel.org/lkml/20260615185934.2383514-1-sumitg@nvidia.com/
v4: https://lore.kernel.org/lkml/20260527194626.185286-1-sumitg@nvidia.com/
v3: https://lore.kernel.org/lkml/20260514194822.1841748-1-sumitg@nvidia.com/
v2: https://lore.kernel.org/lkml/20260430142430.755437-1-sumitg@nvidia.com/
v1: https://lore.kernel.org/lkml/20260427051823.280419-1-sumitg@nvidia.com/

-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v8 1/3] ACPI: CPPC: Add ospm_nominal_perf support
  2026-10-30 17:27 [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
@ 2026-10-30 17:27 ` Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits Sumit Gupta
  2 siblings, 0 replies; 4+ messages in thread
From: Sumit Gupta @ 2026-10-30 17:27 UTC (permalink / raw)
  To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
	ionela.voinescu, zhenglifeng1, zhanjie9, lenb, mario.limonciello,
	maciej.wieczor-retman, pawel.chmielewski, linux-pm, linux-acpi,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	mochs, bbasu, sumitg

Expose the OSPM Nominal Performance register (ACPI 6.6, Section
8.4.6.1.2.6), which conveys the nominal performance level at which the
platform may run. Unlike the read-only Nominal Performance register, it
is writable, so OSPM can ask for a nominal level below the
platform-reported one, which then becomes the boundary between boosted
and throttled operation.

Add cppc_set_ospm_nominal_perf() to write the register. Per the spec,
the value must lie in [Lowest Performance, Nominal Performance], which
the caller is responsible for validating.

Add cppc_ospm_nominal_perf_supported() to report whether the platform
implements the register as writable, since a write-only register cannot
be probed by reading it. It uses the same cpc_is_writable() test as the
setter, so support is never reported for a register that a write would
reject, and takes cpc_desc_lock while looking the descriptor up.

The register also joins the writable set that the SystemMemory overlap
validation uses, as the driver now writes it and can compete with
another control sharing an access unit.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c | 44 ++++++++++++++++++++++++++++++++++++++++
 include/acpi/cppc_acpi.h | 10 +++++++++
 2 files changed, 54 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 5a1f84fddb7f..3a0ecdd1ab11 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -463,6 +463,7 @@ static bool cpc_reg_is_writable(unsigned int reg_idx)
 	case AUTO_SEL_ENABLE:
 	case AUTO_ACT_WINDOW:
 	case ENERGY_PERF:
+	case OSPM_NOMINAL_PERF:
 		return true;
 	default:
 		return false;
@@ -3232,6 +3233,49 @@ int cppc_set_epp(int cpu, u64 epp_val)
 }
 EXPORT_SYMBOL_GPL(cppc_set_epp);
 
+/**
+ * cppc_set_ospm_nominal_perf() - Write OSPM Nominal Performance register.
+ * @cpu: CPU on which to write register.
+ * @ospm_nominal_perf: Value to write to the OSPM Nominal Performance register.
+ *
+ * OSPM Nominal Performance conveys the desired nominal performance level
+ * at which the platform may run. Per ACPI 6.6, s8.4.6.1.2.6, the value
+ * must lie within [Lowest Performance, Nominal Performance] and may be
+ * set independently of Minimum, Maximum and Desired performance. The
+ * caller is responsible for validating the range.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the platform does not implement the
+ * register as writable, or another negative error code.
+ */
+int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
+{
+	return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
+
+/**
+ * cppc_ospm_nominal_perf_supported() - Check OSPM Nominal Performance support.
+ * @cpu: CPU to query.
+ *
+ * The OSPM Nominal Performance register is write-only, so its value
+ * cannot be read back. This only reports whether the platform implements
+ * it as a writable register.
+ *
+ * Context: Process context.
+ * Return: true if the register is supported, false otherwise.
+ */
+bool cppc_ospm_nominal_perf_supported(int cpu)
+{
+	struct cpc_desc *cpc_desc;
+
+	guard(mutex)(&cpc_desc_lock);
+	cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+
+	return cpc_desc &&
+	       cpc_is_writable(&cpc_desc->cpc_regs[OSPM_NOMINAL_PERF]);
+}
+EXPORT_SYMBOL_GPL(cppc_ospm_nominal_perf_supported);
+
 /**
  * cppc_get_auto_act_window() - Read autonomous activity window register.
  * @cpu: CPU from which to read register.
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index a5545fd3f8bd..2cec54b4d2e2 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -195,6 +195,8 @@ extern int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val);
 extern int cppc_get_epp_perf(int cpunum, u64 *epp_perf);
 extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable);
 extern int cppc_set_epp(int cpu, u64 epp_val);
+extern int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf);
+extern bool cppc_ospm_nominal_perf_supported(int cpu);
 extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
 extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
 extern int cppc_get_auto_sel(int cpu, u64 *enable);
@@ -290,6 +292,14 @@ static inline int cppc_set_epp(int cpu, u64 epp_val)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
+{
+	return -EOPNOTSUPP;
+}
+static inline bool cppc_ospm_nominal_perf_supported(int cpu)
+{
+	return false;
+}
 static inline int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
 {
 	return -EOPNOTSUPP;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v8 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute
  2026-10-30 17:27 [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 1/3] ACPI: " Sumit Gupta
@ 2026-10-30 17:27 ` Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits Sumit Gupta
  2 siblings, 0 replies; 4+ messages in thread
From: Sumit Gupta @ 2026-10-30 17:27 UTC (permalink / raw)
  To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
	ionela.voinescu, zhenglifeng1, zhanjie9, lenb, mario.limonciello,
	maciej.wieczor-retman, pawel.chmielewski, linux-pm, linux-acpi,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	mochs, bbasu, sumitg

OSPM Nominal Performance (ACPI 6.6, Section 8.4.6.1.2.6) lets the OS
request a nominal performance level below the platform-reported one. The
platform treats performance above that level as boosted and below it as
throttled. A lower value moves that boundary down, so more levels count
as boost.

Expose it as a per-policy cpufreq attribute in kHz, matching the unit
convention of the other frequency attributes:

  /sys/devices/system/cpu/cpuX/cpufreq/ospm_nominal_freq

Writes are converted with cppc_khz_to_perf() and rejected unless they
fall in [Lowest Performance, Nominal Performance]. The register cannot
be read back, so a read returns the last value written, or "<unset>"
before the first write.

Also track the register in the OSPM-set save/restore table, so a
requested value survives CPU hotplug and suspend/resume. The store
handler records the request rather than init() capturing a firmware
value, so the driver writes the register only once the OS has set it.
CPU offline and driver unload write the platform-reported Nominal
Performance to the register.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../ABI/testing/sysfs-devices-system-cpu      |  27 +++++
 drivers/cpufreq/cppc_cpufreq.c                | 101 ++++++++++++++++--
 2 files changed, 121 insertions(+), 7 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 545687ebadcc..6af7fa86e44c 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -350,6 +350,33 @@ Description:	Performance Limited
 
 		This file is only present if the cppc-cpufreq driver is in use.
 
+What:		/sys/devices/system/cpu/cpuX/cpufreq/ospm_nominal_freq
+Date:		October 2026
+Contact:	linux-pm@vger.kernel.org
+Description:	OSPM Nominal Performance (kHz)
+
+		OSPM uses this attribute to request a nominal performance level
+		lower than the platform-reported nominal. The platform treats
+		performance above this level as boost and below as throttle.
+
+		Write a value in kHz, between the frequencies corresponding to
+		Lowest Performance and Nominal Performance.
+
+		The register cannot be read back, so a read returns the last
+		value written, rounded to a performance level. It returns
+		"<unset>" before the first write. Reads and writes fail with
+		-EOPNOTSUPP if the platform does not implement the register.
+
+		Note that tasks may be migrated from one CPU to another by the
+		scheduler's load-balancing algorithm, and if different OSPM
+		Nominal Performance values are set for those CPUs (through
+		different cpufreq policies), that may lead to undesirable
+		outcomes. To avoid such issues it is better to set the same
+		value across all policies, or to pin every task potentially
+		sensitive to it to a specific CPU.
+
+		This file is only present if the cppc-cpufreq driver is in use.
+
 What:		/sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
 Date:		August 2008
 KernelVersion:	2.6.27
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 76cb803617f5..662397526433 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -34,11 +34,13 @@ static struct cpufreq_driver cppc_cpufreq_driver;
  * reapplied from online() across CPU hotplug, and the firmware value is
  * restored from offline().
  *
- * Autonomous Selection (auto_sel) is kept first, as the registers after it
- * only have meaning while it is enabled.
+ * Autonomous Selection (auto_sel) splits the list: the registers before it are
+ * independent of it, and those after it have meaning only while autonomous
+ * selection is enabled. Place a new register on the matching side.
  */
 enum cppc_saved_reg_id {
-	CPPC_SAVED_AUTO_SEL,
+	CPPC_SAVED_OSPM_NOMINAL_PERF,
+	CPPC_SAVED_AUTO_SEL,	/* Entries below need auto_sel enabled. */
 	CPPC_SAVED_EPP,
 	CPPC_SAVED_AUTO_ACT_WINDOW,
 	CPPC_NR_SAVED_REGS,
@@ -51,6 +53,11 @@ struct cppc_saved_reg {
 };
 
 static const struct cppc_saved_reg cppc_saved_regs[CPPC_NR_SAVED_REGS] = {
+	/* The register is write-only, so the driver tracks the requested value. */
+	[CPPC_SAVED_OSPM_NOMINAL_PERF] = {
+		.name = "ospm_nominal_perf",
+		.set = cppc_set_ospm_nominal_perf,
+	},
 	[CPPC_SAVED_AUTO_SEL] = {
 		.name = "auto_sel",
 		.get = cppc_get_auto_sel,
@@ -83,6 +90,9 @@ enum cppc_saved_type {
  *   CPPC_SAVED_REQUESTED - value the OS asked for, reapplied at online().
  *                          It starts as the firmware value and changes only
  *                          when a write from the OS succeeds.
+ *
+ * A write-only register cannot be read back. init() leaves both values unset,
+ * and its store handler sets them.
  */
 struct cppc_policy_state {
 	u64 regs[CPPC_NR_SAVED_REGS][CPPC_NR_SAVED_TYPES];
@@ -135,11 +145,19 @@ static int cppc_cpufreq_save_firmware_regs(struct cpufreq_policy *policy)
 	int i, ret;
 
 	for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
-		ret = cppc_saved_regs[i].get(cpu, &val);
-		if (ret == -EOPNOTSUPP)
+		const struct cppc_saved_reg *reg = &cppc_saved_regs[i];
+
+		if (!reg->get) {
+			/* A write-only register has no value to read. */
 			val = U64_MAX;
-		else if (ret)
-			return ret;
+		} else {
+			ret = reg->get(cpu, &val);
+			/* Leave a register the platform does not implement unset. */
+			if (ret == -EOPNOTSUPP)
+				val = U64_MAX;
+			else if (ret)
+				return ret;
+		}
 
 		st->regs[i][CPPC_SAVED_FIRMWARE] = val;
 		st->regs[i][CPPC_SAVED_REQUESTED] = val;
@@ -198,6 +216,12 @@ static void cppc_cpufreq_apply_saved_regs(struct cpufreq_policy *policy,
 	u64 auto_sel, val;
 	int i;
 
+	/* Registers before auto_sel do not depend on it. */
+	for (i = 0; i < CPPC_SAVED_AUTO_SEL; i++) {
+		val = st->regs[i][saved_type];
+		cppc_cpufreq_write_saved_reg(cpu, i, val, saved_type);
+	}
+
 	auto_sel = st->regs[CPPC_SAVED_AUTO_SEL][saved_type];
 
 	if (auto_sel)
@@ -1470,11 +1494,73 @@ 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)
 
+static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
+{
+	const struct cppc_policy_state *st = cppc_cpufreq_policy_state(policy);
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	u64 perf;
+
+	if (!cppc_ospm_nominal_perf_supported(policy->cpu))
+		return -EOPNOTSUPP;
+
+	/* The register cannot be read back, so report the tracked request. */
+	perf = st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_REQUESTED];
+	if (perf == U64_MAX)
+		return sysfs_emit(buf, "<unset>\n");
+
+	return sysfs_emit(buf, "%u\n",
+			  cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
+				       const char *buf, size_t count)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_policy_state *st;
+	unsigned int freq_khz;
+	u32 perf;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &freq_khz);
+	if (ret)
+		return ret;
+
+	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+	if (perf < cpu_data->perf_caps.lowest_perf ||
+	    perf > cpu_data->perf_caps.nominal_perf)
+		return -EINVAL;
+
+	ret = cppc_set_ospm_nominal_perf(policy->cpu, perf);
+	if (ret)
+		return ret;
+
+	/*
+	 * The OS can write this register but cannot read it back.
+	 *
+	 * The register lets the OS ask for a nominal level below the platform
+	 * Nominal Performance. offline() and driver unload write the platform
+	 * Nominal in place of the requested value. This removes the lower
+	 * level the OS asked for. The value from before the first OS write
+	 * cannot be read, so the driver cannot restore it. online() writes
+	 * the requested value again.
+	 *
+	 * Set both values here and not at init(). The driver then writes the
+	 * register only after the OS has set it.
+	 */
+	st = cppc_cpufreq_policy_state(policy);
+	st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_REQUESTED] = perf;
+	st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_FIRMWARE] =
+		cpu_data->perf_caps.nominal_perf;
+
+	return count;
+}
+
 cpufreq_freq_attr_ro(freqdomain_cpus);
 cpufreq_freq_attr_rw(auto_select);
 cpufreq_freq_attr_rw(auto_act_window);
 cpufreq_freq_attr_rw(energy_performance_preference_val);
 cpufreq_freq_attr_rw(perf_limited);
+cpufreq_freq_attr_rw(ospm_nominal_freq);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
 	&freqdomain_cpus,
@@ -1482,6 +1568,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
 	&auto_act_window,
 	&energy_performance_preference_val,
 	&perf_limited,
+	&ospm_nominal_freq,
 	NULL,
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v8 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits
  2026-10-30 17:27 [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 1/3] ACPI: " Sumit Gupta
  2026-10-30 17:27 ` [PATCH v8 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute Sumit Gupta
@ 2026-10-30 17:27 ` Sumit Gupta
  2 siblings, 0 replies; 4+ messages in thread
From: Sumit Gupta @ 2026-10-30 17:27 UTC (permalink / raw)
  To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
	ionela.voinescu, zhenglifeng1, zhanjie9, lenb, mario.limonciello,
	maciej.wieczor-retman, pawel.chmielewski, linux-pm, linux-acpi,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	mochs, bbasu, sumitg

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>
---
 .../ABI/testing/sysfs-devices-system-cpu      |  4 ++
 drivers/cpufreq/cppc_cpufreq.c                | 72 ++++++++++++++++++-
 2 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 6af7fa86e44c..d219fc27a0c9 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -362,6 +362,10 @@ Description:	OSPM Nominal Performance (kHz)
 		Write a value in kHz, between the frequencies corresponding to
 		Lowest Performance and Nominal Performance.
 
+		While boost is disabled, cpuinfo_max_freq is set to this value
+		and scaling_max_freq cannot go above it. While boost is
+		enabled, cpuinfo_max_freq is the Highest Performance frequency.
+
 		The register cannot be read back, so a read returns the last
 		value written, rounded to a performance level. It returns
 		"<unset>" before the first write. Reads and writes fail with
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 662397526433..7cac31de7325 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -915,6 +915,29 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
 	policy->driver_data = NULL;
 }
 
+/*
+ * Return the non-boost performance ceiling. This is 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_policy_state *st = cppc_cpufreq_policy_state(policy);
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	u64 ospm_nominal;
+
+	ospm_nominal = st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_REQUESTED];
+
+	/* 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;
@@ -974,9 +997,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;
 
 	/*
@@ -1280,11 +1308,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;
 }
@@ -1494,6 +1523,41 @@ 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. Without that, raising the nominal again would leave the
+ * stale request capping the policy.
+ *
+ * Refresh the limits directly rather than leave it to the QoS notifier, which
+ * only schedules the update, so the new ceiling is in place once the write
+ * returns. The sysfs store path holds policy->rwsem, which the refresh needs.
+ */
+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 show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
 {
 	const struct cppc_policy_state *st = cppc_cpufreq_policy_state(policy);
@@ -1552,6 +1616,8 @@ static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
 	st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_FIRMWARE] =
 		cpu_data->perf_caps.nominal_perf;
 
+	cppc_cpufreq_update_nominal_limits(policy);
+
 	return count;
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-30 17:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 17:27 [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
2026-10-30 17:27 ` [PATCH v8 1/3] ACPI: " Sumit Gupta
2026-10-30 17:27 ` [PATCH v8 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute Sumit Gupta
2026-10-30 17:27 ` [PATCH v8 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits Sumit Gupta

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.