mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6)
@ 2026-05-14 19:48 Sumit Gupta
  2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta
  2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
  0 siblings, 2 replies; 12+ messages in thread
From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg

Add initial kernel support for CPPC v4 (ACPI 6.6, Section 8.4.6),
which extends the _CPC package from 23 to 25 entries with two
optional fields:

  - OSPM Nominal Performance (8.4.6.1.2.6): register used by OSPM
    to tell the platform what it considers nominal. The platform
    classifies performance above this as boost and below as
    throttle for power/thermal decisions.

  - Resource Priority (8.4.6.1.2.7): Package of Resource Priority
    Register Descriptor sub-packages. Full parsing is not yet
    implemented; such entries are marked as unsupported.

Patch 1: Add v4 _CPC parsing - validate the 25-entry layout,
restrict the Resource Priority package fallback to that slot
only, and mark the two new registers optional.

Patch 2: Add per-policy sysfs attribute ospm_nominal_freq (kHz)
under cpufreq. Writes are converted to perf and applied to every
CPU in policy->cpus.

---
v2[2] -> v3:
- Patch 1:
  - Accept ACPI_TYPE_PACKAGE only at RESOURCE_PRIORITY; abort otherwise.
  - Added Reviewed-by from Pierre Gondois.
- Patch 2:
  - Sysfs moved from per-CPU acpi_cppc to per-policy cpufreq.
  - Read ospm_nominal_freq sysfs input in kHz and convert to perf.
  - Cached ospm_nominal_perf moved from cpc_desc to cppc_cpudata.
  - Validate input against [Lowest Performance, Nominal Performance]
    in cppc_set_ospm_nominal_perf().
  - Dropped unconditional init in cppc_cpufreq_cpu_init().
  - Remove to skip write if cache matches in cppc_set_ospm_nominal_perf.
  - Early -ENODEV return when no CPC descriptor.
  - Added ABI documentation at
    Documentation/ABI/testing/sysfs-devices-system-cpu.

v1[1] -> v2:
- Patch 1: Added Reviewed-by from Mario Limonciello.
- Patch 2:
  - Make ospm_nominal_perf sysfs read-write; cache last write in
    cpc_desc and skip redundant register writes.
  - Validate input in cppc_set_ospm_nominal_perf.

Sumit Gupta (2):
  ACPI: CPPC: Add support for CPPC v4
  ACPI: CPPC: Add ospm_nominal_perf support

 .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
 drivers/acpi/cppc_acpi.c                      | 58 +++++++++++++++++--
 drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++
 include/acpi/cppc_acpi.h                      | 15 ++++-
 4 files changed, 122 insertions(+), 8 deletions(-)

[1] https://lore.kernel.org/lkml/20260427051823.280419-1-sumitg@nvidia.com/
[2] https://lore.kernel.org/lkml/20260430142430.755437-1-sumitg@nvidia.com/

-- 
2.34.1


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

* [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4
  2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta
@ 2026-05-14 19:48 ` Sumit Gupta
  2026-05-26 17:38   ` Rafael J. Wysocki
  2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
  1 sibling, 1 reply; 12+ messages in thread
From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg,
	Mario Limonciello

CPPC v4 (ACPI 6.6, Section 8.4.6) adds two optional entries to the
_CPC package:

1. OSPM Nominal Performance (8.4.6.1.2.6): A write-only register that
   lets OSPM inform the platform what it considers nominal performance.
   The platform classifies performance above this level as boost and
   below as throttle for its power/thermal decisions.

2. Resource Priority (8.4.6.1.2.7): A Package of Resource Priority
   Register Descriptor sub-packages that allow OSPM to set relative
   priority among processors for shared resources (boost, throttle,
   L2/L3 cache, memory bandwidth). Parsing the full structure is not
   yet supported; such entries are marked as unsupported.

Add v4 _CPC table parsing (25 entries) and update REG_OPTIONAL to
mark the two new registers as optional.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
---
 drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------
 include/acpi/cppc_acpi.h |  8 ++++++--
 2 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index f370be8715ae..c76cfafa3589 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -134,7 +134,7 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
  * cpc_regs[] with the corresponding index. 0 means mandatory and 1
  * means optional.
  */
-#define REG_OPTIONAL (0x1FC7D0)
+#define REG_OPTIONAL (0x7FC7D0)
 
 /*
  * Use the index of the register in per-cpu cpc_regs[] to check if
@@ -751,18 +751,19 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	/*
 	 * Disregard _CPC if the number of entries in the return package is not
 	 * as expected, but support future revisions being proper supersets of
-	 * the v3 and only causing more entries to be returned by _CPC.
+	 * the v4 and only causing more entries to be returned by _CPC.
 	 */
 	if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) ||
 	    (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) ||
-	    (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) {
+	    (cpc_rev == CPPC_V4_REV && num_ent != CPPC_V4_NUM_ENT) ||
+	    (cpc_rev > CPPC_V4_REV && num_ent <= CPPC_V4_NUM_ENT)) {
 		pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n",
 			 num_ent, pr->id);
 		goto out_free;
 	}
-	if (cpc_rev > CPPC_V3_REV) {
-		num_ent = CPPC_V3_NUM_ENT;
-		cpc_rev = CPPC_V3_REV;
+	if (cpc_rev > CPPC_V4_REV) {
+		num_ent = CPPC_V4_NUM_ENT;
+		cpc_rev = CPPC_V4_REV;
 	}
 
 	cpc_ptr->num_entries = num_ent;
@@ -845,6 +846,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 
 			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
 			memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
+		} else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
+			/*
+			 * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a
+			 * Package of Resource Priority Register Descriptor sub-packages.
+			 * Parsing the full structure is not yet supported.
+			 * Mark the register as unsupported for now.
+			 */
+			pr_debug("CPU:%d Resource Priority not supported\n", pr->id);
+			cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
+			cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0;
 		} else {
 			pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
 				 i, pr->id);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index d1f02ceec4f9..8693890a7275 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -17,16 +17,18 @@
 #include <acpi/pcc.h>
 #include <acpi/processor.h>
 
-/* CPPCv2 and CPPCv3 support */
+/* CPPCv2, CPPCv3 and CPPCv4 support */
 #define CPPC_V2_REV	2
 #define CPPC_V3_REV	3
+#define CPPC_V4_REV	4
 #define CPPC_V2_NUM_ENT	21
 #define CPPC_V3_NUM_ENT	23
+#define CPPC_V4_NUM_ENT	25
 
 #define PCC_CMD_COMPLETE_MASK	(1 << 0)
 #define PCC_ERROR_MASK		(1 << 2)
 
-#define MAX_CPC_REG_ENT 21
+#define MAX_CPC_REG_ENT 23
 
 /* CPPC specific PCC commands. */
 #define	CMD_READ 0
@@ -109,6 +111,8 @@ enum cppc_regs {
 	REFERENCE_PERF,
 	LOWEST_FREQ,
 	NOMINAL_FREQ,
+	OSPM_NOMINAL_PERF,
+	RESOURCE_PRIORITY,
 };
 
 /*
-- 
2.34.1


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

* [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta
  2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta
@ 2026-05-14 19:48 ` Sumit Gupta
  2026-05-21 16:58   ` Pierre Gondois
  2026-05-22 17:20   ` Rafael J. Wysocki
  1 sibling, 2 replies; 12+ messages in thread
From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg

Expose the OSPM Nominal Performance register (ACPI 6.6, Section
8.4.6.1.2.6), which conveys the desired nominal performance level
at which the platform may run. Unlike the existing read-only
Nominal Performance register, it is writable and lets OSPM
request a lower nominal level than the platform-reported nominal.
The platform classifies performance above this level as boosted
and below as throttled for its power/thermal decisions.

It is exposed as a per-policy cpufreq sysfs attribute in kHz, to
match the cpufreq sysfs unit convention:

  /sys/devices/system/cpu/cpufreq/policyN/ospm_nominal_freq

The attribute is documented in
Documentation/ABI/testing/sysfs-devices-system-cpu.

Writes are converted to perf via cppc_khz_to_perf(), validated
against [Lowest Performance, Nominal Performance], and applied to
every CPU in policy->cpus.

The register is write-only; the kernel caches the last written
value in struct cppc_cpudata for sysfs readback (returns 0 until
userspace writes a value).

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++++
 drivers/acpi/cppc_acpi.c                      | 35 ++++++++++++++++
 drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++++++++
 include/acpi/cppc_acpi.h                      |  7 ++++
 4 files changed, 99 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 82d10d556cc8..ac1bf1b89ac4 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -346,6 +346,23 @@ 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:		May 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 for power and thermal decisions.
+
+		Read returns the last written value in kHz, or 0 if no
+		value has been written. Write a kHz value in the range
+		[lowest_freq, nominal_freq].
+
+		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/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index c76cfafa3589..ad6ece16c30d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1682,6 +1682,41 @@ 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.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cppc_perf_caps caps;
+	int ret;
+
+	if (!cpc_desc) {
+		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+		return -ENODEV;
+	}
+
+	ret = cppc_get_perf_caps(cpu, &caps);
+	if (ret)
+		return ret;
+
+	if (ospm_nominal_perf < caps.lowest_perf ||
+	    ospm_nominal_perf > caps.nominal_perf)
+		return -EINVAL;
+
+	return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
+
 /**
  * cppc_get_auto_act_window() - Read autonomous activity window register.
  * @cpu: CPU from which to read register.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 7e7f9dfb7a24..6379b7ceee34 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -985,11 +985,50 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
 CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
 			 cppc_set_perf_limited)
 
+static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	unsigned int freq_khz;
+
+	if (!cpu_data->ospm_nominal_perf)
+		return sysfs_emit(buf, "0\n");
+
+	freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
+				    cpu_data->ospm_nominal_perf);
+	return sysfs_emit(buf, "%u\n", freq_khz);
+}
+
+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;
+	unsigned int sib;
+	u64 freq_khz;
+	u32 perf;
+	int ret;
+
+	ret = kstrtou64(buf, 0, &freq_khz);
+	if (ret)
+		return ret;
+
+	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+
+	for_each_cpu(sib, policy->cpus) {
+		ret = cppc_set_ospm_nominal_perf(sib, perf);
+		if (ret)
+			return ret;
+	}
+
+	cpu_data->ospm_nominal_perf = 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,
@@ -997,6 +1036,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
 	&auto_act_window,
 	&energy_performance_preference_val,
 	&perf_limited,
+	&ospm_nominal_freq,
 	NULL,
 };
 
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 8693890a7275..0b1dcdbea10a 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -153,6 +153,8 @@ struct cppc_cpudata {
 	struct cppc_perf_fb_ctrs perf_fb_ctrs;
 	unsigned int shared_type;
 	cpumask_var_t shared_cpu_map;
+	/* Cached OSPM Nominal Performance value (write-only register). */
+	u32 ospm_nominal_perf;
 };
 
 #ifdef CONFIG_ACPI_CPPC_LIB
@@ -180,6 +182,7 @@ 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 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, bool *enable);
@@ -266,6 +269,10 @@ 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 int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
 {
 	return -EOPNOTSUPP;
-- 
2.34.1


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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
@ 2026-05-21 16:58   ` Pierre Gondois
  2026-05-26 18:24     ` Sumit Gupta
  2026-05-22 17:20   ` Rafael J. Wysocki
  1 sibling, 1 reply; 12+ messages in thread
From: Pierre Gondois @ 2026-05-21 16:58 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9,
	mario.limonciello, saket.dumbre, linux-acpi, linux-kernel,
	linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu

[-- Attachment #1: Type: text/plain, Size: 17680 bytes --]

Hello Sumit,

I copy-pasted and attached a complementary patch just to show
what I meant by caching the ospm_nominal_perf value at driver
init/exit.

This should also allow to handle the cases where a user tries
to store a new value for a policy with multiple CPUs,
one of the CPUs and we need to rollback.

I think another issue that should be solved is that:

- we don't the value of ospm_nominal_perf when loading the driver

- we leave the register in an unknown state if we unload/reload
the driver.

Note:
IMO caching values like in the patch would also benefit to the
auto_sel register.

------

If you think the patch helps, feel free to take it. Otherwise
please let me know the issues it would raise.

Regards,

Pierre


On 5/14/26 21:48, Sumit Gupta wrote:
> Expose the OSPM Nominal Performance register (ACPI 6.6, Section
> 8.4.6.1.2.6), which conveys the desired nominal performance level
> at which the platform may run. Unlike the existing read-only
> Nominal Performance register, it is writable and lets OSPM
> request a lower nominal level than the platform-reported nominal.
> The platform classifies performance above this level as boosted
> and below as throttled for its power/thermal decisions.
>
> It is exposed as a per-policy cpufreq sysfs attribute in kHz, to
> match the cpufreq sysfs unit convention:
>
>    /sys/devices/system/cpu/cpufreq/policyN/ospm_nominal_freq
>
> The attribute is documented in
> Documentation/ABI/testing/sysfs-devices-system-cpu.
>
> Writes are converted to perf via cppc_khz_to_perf(), validated
> against [Lowest Performance, Nominal Performance], and applied to
> every CPU in policy->cpus.
>
> The register is write-only; the kernel caches the last written
> value in struct cppc_cpudata for sysfs readback (returns 0 until
> userspace writes a value).
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>   .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++++
>   drivers/acpi/cppc_acpi.c                      | 35 ++++++++++++++++
>   drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++++++++
>   include/acpi/cppc_acpi.h                      |  7 ++++
>   4 files changed, 99 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index 82d10d556cc8..ac1bf1b89ac4 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -346,6 +346,23 @@ 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:		May 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 for power and thermal decisions.
> +
> +		Read returns the last written value in kHz, or 0 if no
> +		value has been written. Write a kHz value in the range
> +		[lowest_freq, nominal_freq].
> +
> +		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/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index c76cfafa3589..ad6ece16c30d 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1682,6 +1682,41 @@ 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.
> + *
> + * Return: 0 on success or negative error code.
> + */
> +int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
> +{
> +	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> +	struct cppc_perf_caps caps;
> +	int ret;
> +
> +	if (!cpc_desc) {
> +		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
> +		return -ENODEV;
> +	}
> +
> +	ret = cppc_get_perf_caps(cpu, &caps);
> +	if (ret)
> +		return ret;
> +
> +	if (ospm_nominal_perf < caps.lowest_perf ||
> +	    ospm_nominal_perf > caps.nominal_perf)
> +		return -EINVAL;
> +
> +	return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
> +
>   /**
>    * cppc_get_auto_act_window() - Read autonomous activity window register.
>    * @cpu: CPU from which to read register.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 7e7f9dfb7a24..6379b7ceee34 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -985,11 +985,50 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
>   CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>   			 cppc_set_perf_limited)
>   
> +static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
> +{
> +	struct cppc_cpudata *cpu_data = policy->driver_data;
> +	unsigned int freq_khz;
> +
> +	if (!cpu_data->ospm_nominal_perf)
> +		return sysfs_emit(buf, "0\n");
> +
> +	freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
> +				    cpu_data->ospm_nominal_perf);
> +	return sysfs_emit(buf, "%u\n", freq_khz);
> +}
> +
> +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;
> +	unsigned int sib;
> +	u64 freq_khz;
> +	u32 perf;
> +	int ret;
> +
> +	ret = kstrtou64(buf, 0, &freq_khz);
> +	if (ret)
> +		return ret;
> +
> +	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> +	for_each_cpu(sib, policy->cpus) {
> +		ret = cppc_set_ospm_nominal_perf(sib, perf);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	cpu_data->ospm_nominal_perf = 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,
> @@ -997,6 +1036,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>   	&auto_act_window,
>   	&energy_performance_preference_val,
>   	&perf_limited,
> +	&ospm_nominal_freq,
>   	NULL,
>   };
>   
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 8693890a7275..0b1dcdbea10a 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -153,6 +153,8 @@ struct cppc_cpudata {
>   	struct cppc_perf_fb_ctrs perf_fb_ctrs;
>   	unsigned int shared_type;
>   	cpumask_var_t shared_cpu_map;
> +	/* Cached OSPM Nominal Performance value (write-only register). */
> +	u32 ospm_nominal_perf;
>   };
>   
>   #ifdef CONFIG_ACPI_CPPC_LIB
> @@ -180,6 +182,7 @@ 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 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, bool *enable);
> @@ -266,6 +269,10 @@ 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 int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
>   {
>   	return -EOPNOTSUPP;


diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu 
b/Documentation/ABI/testing/sysfs-devices-system-cpu
index ac1bf1b89ac4d..f3d357917242a 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -356,9 +356,7 @@Description:        OSPM Nominal Performance (kHz)
                platform treats performance above this level as boost
                and below as throttle for power and thermal decisions.

-               Read returns the last written value in kHz, or 0 if no
-               value has been written. Write a kHz value in the range
-               [lowest_freq, nominal_freq].
+Values are in kHz and in the range [lowest_freq, nominal_freq].

                This file is only present if the cppc-cpufreq driver is
                in use.
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 97218c4ac162b..01542184e69b2 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1717,6 +1717,19 @@int cppc_set_ospm_nominal_perf(int cpu, u64 
ospm_nominal_perf)
}
EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);

+/**
+ * cppc_get_ospm_nominal_perf() - Read OSPM Nominal Performance register.
+ * @cpu: CPU from which to read register.
+ * @ospm_nominal_perf: Pointer to store the OSPM Nominal Performance value.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf)
+{
+return cppc_get_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_ospm_nominal_perf);
+
/**
  * cppc_get_auto_act_window() - Read autonomous activity window register.
  * @cpu: CPU from which to read register.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 6379b7ceee347..1a51017a81630 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -28,6 +28,9 @@

static struct cpufreq_driver cppc_cpufreq_driver;

+/* Values at driver init. */
+static u64 scratch_ospm_nominal_perf[NR_CPUS];
+
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
static enum {
        FIE_UNSET = -1,
@@ -642,7 +645,7 @@static void cppc_cpufreq_put_cpu_data(struct 
cpufreq_policy *policy)

static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
-       unsigned int cpu = policy->cpu;
+unsigned int sib, cpu = policy->cpu;
        struct cppc_cpudata *cpu_data;
        struct cppc_perf_caps *caps;
        int ret;
@@ -704,6 +707,34 @@static int cppc_cpufreq_cpu_init(struct 
cpufreq_policy *policy)
        if (caps->highest_perf > caps->nominal_perf)
                policy->boost_supported = true;

+/*
+ * CPUs are expected to have:
+ *   ospm_nominal_perf = nominal_perf
+ * at policy init.
+ */
+cpu_data->ospm_nominal_perf = caps->nominal_perf;
+
+for_each_cpu(sib, policy->cpus) {
+u64 ospm_nominal_perf;
+
+ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf);
+if (ret == -EOPNOTSUPP) {
+continue;
+} else if (ret) {
+pr_err("CPU%d: Could not read ospm_nominal_perf value\n",
+sib);
+goto out;
+}
+
+if (ospm_nominal_perf != caps->nominal_perf) {
+pr_err("Incoherent ospm_nominal_perf values: "
+"scratch_value=%llu expected_value=%u\n",
+scratch_ospm_nominal_perf[sib],
+caps->nominal_perf);
+goto out;
+}
+}
+
        /* Set policy->cur to max now. The governors will adjust later. */
        policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
        cpu_data->perf_ctrls.desired_perf =  caps->highest_perf;
@@ -727,9 +758,17 @@static void cppc_cpufreq_cpu_exit(struct 
cpufreq_policy *policy)
{
        struct cppc_cpudata *cpu_data = policy->driver_data;
        struct cppc_perf_caps *caps = &cpu_data->perf_caps;
-       unsigned int cpu = policy->cpu;
+unsigned int sib, cpu = policy->cpu;
        int ret;

+/*
+ * CPUs are expected to have:
+ *   ospm_nominal_perf = nominal_perf
+ * at policy init, so come back to that state.
+ */
+for_each_cpu(sib, policy->cpus)
+cppc_set_ospm_nominal_perf(sib, caps->nominal_perf);
+
        cppc_cpufreq_cpu_fie_exit(policy);

        cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
@@ -989,9 +1028,20 @@static ssize_t show_ospm_nominal_freq(struct 
cpufreq_policy *policy, char *buf)
{
        struct cppc_cpudata *cpu_data = policy->driver_data;
        unsigned int freq_khz;
+u64 perf;
+int ret;
+
+ret = cppc_get_ospm_nominal_perf(policy->cpu, &perf);
+if (ret == -EOPNOTSUPP)
+return sysfs_emit(buf, "<unsupported>\n");
+if (ret)
+return ret;

-       if (!cpu_data->ospm_nominal_perf)
-               return sysfs_emit(buf, "0\n");
+if (perf != cpu_data->ospm_nominal_perf) {
+pr_warn("ospm_nominal_freq value is not coherent: "
+"register=%llu scratch_value=%u\n",
+perf, cpu_data->ospm_nominal_perf);
+}

        freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
                                    cpu_data->ospm_nominal_perf);
@@ -1002,7 +1052,7 @@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;
-       unsigned int sib;
+unsigned int sib, failing_cpu;
        u64 freq_khz;
        u32 perf;
        int ret;
@@ -1015,12 +1065,23 @@static ssize_t store_ospm_nominal_freq(struct 
cpufreq_policy *policy,

        for_each_cpu(sib, policy->cpus) {
                ret = cppc_set_ospm_nominal_perf(sib, perf);
-               if (ret)
-                       return ret;
+if (ret) {
+failing_cpu = sib;
+goto error;
+}
        }

        cpu_data->ospm_nominal_perf = perf;
        return count;
+
+error:
+for_each_cpu(sib, policy->cpus) {
+if (sib == failing_cpu)
+break;
+cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf);
+}
+
+return ret;
}

cpufreq_freq_attr_ro(freqdomain_cpus);
@@ -1053,6 +1114,64 @@static struct cpufreq_driver cppc_cpufreq_driver = {
        .name = "cppc_cpufreq",
};

+static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void)
+{
+int cpu;
+
+for_each_present_cpu(cpu) {
+if (scratch_ospm_nominal_perf[cpu] == U64_MAX)
+continue;
+
+/* Reset ospm_nominal_perf to the value present at init. */
+cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]);
+}
+}
+
+static int __init cppc_cpufreq_ospm_nominal_perf_init(void)
+{
+int ret, cpu;
+
+ret = 0;
+
+for_each_present_cpu(cpu) {
+u64 nominal_perf;
+
+/*
+ * If one CPU failed, scratch_ospm_nominal_perf still needs
+ * to be initialized.
+ */
+if (ret) {
+scratch_ospm_nominal_perf[cpu] = U64_MAX;
+continue;
+}
+
+ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]);
+if (ret) {
+if (ret == -EOPNOTSUPP) {
+/* Valid reason to fail. Continue to iterate. */
+ret = 0;
+}
+
+scratch_ospm_nominal_perf[cpu] = U64_MAX;
+continue;
+}
+
+/* Set ospm_nominal_perf to nominal_perf before policy init. */
+ret = cppc_get_nominal_perf(cpu, &nominal_perf);
+if (ret)
+continue;
+
+ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf);
+if (ret)
+continue;
+}
+
+if (ret)
+cppc_cpufreq_ospm_nominal_perf_exit();
+
+return ret;
+}
+
static int __init cppc_cpufreq_init(void)
{
        int ret;
@@ -1063,9 +1182,17 @@static int __init cppc_cpufreq_init(void)
        cppc_freq_invariance_init();
        populate_efficiency_class();

+ret = cppc_cpufreq_ospm_nominal_perf_init();
+if (ret) {
+cppc_freq_invariance_exit();
+return ret;
+}
+
        ret = cpufreq_register_driver(&cppc_cpufreq_driver);
-       if (ret)
+if (ret) {
+cppc_cpufreq_ospm_nominal_perf_exit();
                cppc_freq_invariance_exit();
+}

        return ret;
}
@@ -1074,6 +1201,7 @@static void __exit cppc_cpufreq_exit(void)
{
        cpufreq_unregister_driver(&cppc_cpufreq_driver);
        cppc_freq_invariance_exit();
+cppc_cpufreq_ospm_nominal_perf_exit();
}

module_exit(cppc_cpufreq_exit);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 0b1dcdbea10a2..07e6598fa837a 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -183,6 +183,7 @@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 int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf);
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, bool *enable);
@@ -273,6 +274,10 @@static inline int cppc_set_ospm_nominal_perf(int 
cpu, u64 ospm_nominal_perf)
{
        return -EOPNOTSUPP;
}
+static inline int cppc_get_ospm_nominal_perf(int cpu, u64 
*ospm_nominal_perf)
+{
+return -EOPNOTSUPP;
+}
static inline int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
{
        return -EOPNOTSUPP;


[-- Attachment #2: 0001-ACPI-CPPC-Add-ospm_nominal_perf-support-additional-c.patch --]
[-- Type: text/x-patch, Size: 9277 bytes --]

From 28a1fd8e94f860a3bbf135779fa399621e9355ad Mon Sep 17 00:00:00 2001
From: Pierre Gondois <pierre.gondois@arm.com>
Date: Thu, 21 May 2026 14:53:46 +0200
Subject: [PATCH] ACPI: CPPC: Add ospm_nominal_perf support additional changes

Complementary modifications to:
- "ACPI: CPPC: Add ospm_nominal_perf support"

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
---
 .../ABI/testing/sysfs-devices-system-cpu      |   4 +-
 drivers/acpi/cppc_acpi.c                      |  13 ++
 drivers/cpufreq/cppc_cpufreq.c                | 144 +++++++++++++++++-
 include/acpi/cppc_acpi.h                      |   5 +
 4 files changed, 155 insertions(+), 11 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index ac1bf1b89ac4d..f3d357917242a 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -356,9 +356,7 @@ Description:	OSPM Nominal Performance (kHz)
 		platform treats performance above this level as boost
 		and below as throttle for power and thermal decisions.
 
-		Read returns the last written value in kHz, or 0 if no
-		value has been written. Write a kHz value in the range
-		[lowest_freq, nominal_freq].
+		Values are in kHz and in the range [lowest_freq, nominal_freq].
 
 		This file is only present if the cppc-cpufreq driver is
 		in use.
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 97218c4ac162b..01542184e69b2 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1717,6 +1717,19 @@ int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
 }
 EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
 
+/**
+ * cppc_get_ospm_nominal_perf() - Read OSPM Nominal Performance register.
+ * @cpu: CPU from which to read register.
+ * @ospm_nominal_perf: Pointer to store the OSPM Nominal Performance value.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf)
+{
+	return cppc_get_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_ospm_nominal_perf);
+
 /**
  * cppc_get_auto_act_window() - Read autonomous activity window register.
  * @cpu: CPU from which to read register.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 6379b7ceee347..1a51017a81630 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -28,6 +28,9 @@
 
 static struct cpufreq_driver cppc_cpufreq_driver;
 
+/* Values at driver init. */
+static u64 scratch_ospm_nominal_perf[NR_CPUS];
+
 #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
 static enum {
 	FIE_UNSET = -1,
@@ -642,7 +645,7 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
 
 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-	unsigned int cpu = policy->cpu;
+	unsigned int sib, cpu = policy->cpu;
 	struct cppc_cpudata *cpu_data;
 	struct cppc_perf_caps *caps;
 	int ret;
@@ -704,6 +707,34 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	if (caps->highest_perf > caps->nominal_perf)
 		policy->boost_supported = true;
 
+	/*
+	 * CPUs are expected to have:
+	 *   ospm_nominal_perf = nominal_perf
+	 * at policy init.
+	 */
+	cpu_data->ospm_nominal_perf = caps->nominal_perf;
+
+	for_each_cpu(sib, policy->cpus) {
+		u64 ospm_nominal_perf;
+
+		ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf);
+		if (ret == -EOPNOTSUPP) {
+			continue;
+		} else if (ret) {
+			pr_err("CPU%d: Could not read ospm_nominal_perf value\n",
+				sib);
+			goto out;
+		}
+
+		if (ospm_nominal_perf != caps->nominal_perf) {
+			pr_err("Incoherent ospm_nominal_perf values: "
+				"scratch_value=%llu expected_value=%u\n",
+				scratch_ospm_nominal_perf[sib],
+				caps->nominal_perf);
+			goto out;
+		}
+	}
+
 	/* Set policy->cur to max now. The governors will adjust later. */
 	policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
 	cpu_data->perf_ctrls.desired_perf =  caps->highest_perf;
@@ -727,9 +758,17 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
-	unsigned int cpu = policy->cpu;
+	unsigned int sib, cpu = policy->cpu;
 	int ret;
 
+	/*
+	 * CPUs are expected to have:
+	 *   ospm_nominal_perf = nominal_perf
+	 * at policy init, so come back to that state.
+	 */
+	for_each_cpu(sib, policy->cpus)
+		cppc_set_ospm_nominal_perf(sib, caps->nominal_perf);
+
 	cppc_cpufreq_cpu_fie_exit(policy);
 
 	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
@@ -989,9 +1028,20 @@ static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	unsigned int freq_khz;
+	u64 perf;
+	int ret;
+
+	ret = cppc_get_ospm_nominal_perf(policy->cpu, &perf);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
 
-	if (!cpu_data->ospm_nominal_perf)
-		return sysfs_emit(buf, "0\n");
+	if (perf != cpu_data->ospm_nominal_perf) {
+		pr_warn("ospm_nominal_freq value is not coherent: "
+			"register=%llu scratch_value=%u\n",
+			perf, cpu_data->ospm_nominal_perf);
+	}
 
 	freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
 				    cpu_data->ospm_nominal_perf);
@@ -1002,7 +1052,7 @@ 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;
-	unsigned int sib;
+	unsigned int sib, failing_cpu;
 	u64 freq_khz;
 	u32 perf;
 	int ret;
@@ -1015,12 +1065,23 @@ static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
 
 	for_each_cpu(sib, policy->cpus) {
 		ret = cppc_set_ospm_nominal_perf(sib, perf);
-		if (ret)
-			return ret;
+		if (ret) {
+			failing_cpu = sib;
+			goto error;
+		}
 	}
 
 	cpu_data->ospm_nominal_perf = perf;
 	return count;
+
+error:
+	for_each_cpu(sib, policy->cpus) {
+		if (sib == failing_cpu)
+			break;
+		cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf);
+	}
+
+	return ret;
 }
 
 cpufreq_freq_attr_ro(freqdomain_cpus);
@@ -1053,6 +1114,64 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
 	.name = "cppc_cpufreq",
 };
 
+static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void)
+{
+	int cpu;
+
+	for_each_present_cpu(cpu) {
+		if (scratch_ospm_nominal_perf[cpu] == U64_MAX)
+			continue;
+
+		/* Reset ospm_nominal_perf to the value present at init. */
+		cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]);
+	}
+}
+
+static int __init cppc_cpufreq_ospm_nominal_perf_init(void)
+{
+	int ret, cpu;
+
+	ret = 0;
+
+	for_each_present_cpu(cpu) {
+		u64 nominal_perf;
+
+		/*
+		 * If one CPU failed, scratch_ospm_nominal_perf still needs
+		 * to be initialized.
+		 */
+		if (ret) {
+			scratch_ospm_nominal_perf[cpu] = U64_MAX;
+			continue;
+		}
+
+		ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]);
+		if (ret) {
+			if (ret == -EOPNOTSUPP) {
+				/* Valid reason to fail. Continue to iterate. */
+				ret = 0;
+			}
+
+			scratch_ospm_nominal_perf[cpu] = U64_MAX;
+			continue;
+		}
+
+		/* Set ospm_nominal_perf to nominal_perf before policy init. */
+		ret = cppc_get_nominal_perf(cpu, &nominal_perf);
+		if (ret)
+			continue;
+
+		ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf);
+		if (ret)
+			continue;
+	}
+
+	if (ret)
+		cppc_cpufreq_ospm_nominal_perf_exit();
+
+	return ret;
+}
+
 static int __init cppc_cpufreq_init(void)
 {
 	int ret;
@@ -1063,9 +1182,17 @@ static int __init cppc_cpufreq_init(void)
 	cppc_freq_invariance_init();
 	populate_efficiency_class();
 
+	ret = cppc_cpufreq_ospm_nominal_perf_init();
+	if (ret) {
+		cppc_freq_invariance_exit();
+		return ret;
+	}
+
 	ret = cpufreq_register_driver(&cppc_cpufreq_driver);
-	if (ret)
+	if (ret) {
+		cppc_cpufreq_ospm_nominal_perf_exit();
 		cppc_freq_invariance_exit();
+	}
 
 	return ret;
 }
@@ -1074,6 +1201,7 @@ static void __exit cppc_cpufreq_exit(void)
 {
 	cpufreq_unregister_driver(&cppc_cpufreq_driver);
 	cppc_freq_invariance_exit();
+	cppc_cpufreq_ospm_nominal_perf_exit();
 }
 
 module_exit(cppc_cpufreq_exit);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 0b1dcdbea10a2..07e6598fa837a 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -183,6 +183,7 @@ 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 int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf);
 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, bool *enable);
@@ -273,6 +274,10 @@ static inline int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf)
+{
+	return -EOPNOTSUPP;
+}
 static inline int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
 {
 	return -EOPNOTSUPP;
-- 
2.43.0


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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
  2026-05-21 16:58   ` Pierre Gondois
@ 2026-05-22 17:20   ` Rafael J. Wysocki
  2026-05-26 19:02     ` Sumit Gupta
  1 sibling, 1 reply; 12+ messages in thread
From: Rafael J. Wysocki @ 2026-05-22 17:20 UTC (permalink / raw)
  To: Sumit Gupta
  Cc: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel, treding, jonathanh, vsethi,
	ksitaraman, sanjayc, bbasu

On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
> Expose the OSPM Nominal Performance register (ACPI 6.6, Section
> 8.4.6.1.2.6), which conveys the desired nominal performance level
> at which the platform may run. Unlike the existing read-only
> Nominal Performance register, it is writable and lets OSPM
> request a lower nominal level than the platform-reported nominal.
> The platform classifies performance above this level as boosted
> and below as throttled for its power/thermal decisions.
>
> It is exposed as a per-policy cpufreq sysfs attribute in kHz, to
> match the cpufreq sysfs unit convention:
>
>   /sys/devices/system/cpu/cpufreq/policyN/ospm_nominal_freq
>
> The attribute is documented in
> Documentation/ABI/testing/sysfs-devices-system-cpu.
>
> Writes are converted to perf via cppc_khz_to_perf(), validated
> against [Lowest Performance, Nominal Performance], and applied to
> every CPU in policy->cpus.
>
> The register is write-only; the kernel caches the last written
> value in struct cppc_cpudata for sysfs readback (returns 0 until
> userspace writes a value).
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>

Please see sashiko.dev's feedback on this patch:

https://sashiko.dev/#/patchset/20260514194822.1841748-1-sumitg%40nvidia.com

and address it, or let me know why you think that addressing it is not
necessary.

> ---
>  .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++++
>  drivers/acpi/cppc_acpi.c                      | 35 ++++++++++++++++
>  drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++++++++
>  include/acpi/cppc_acpi.h                      |  7 ++++
>  4 files changed, 99 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index 82d10d556cc8..ac1bf1b89ac4 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -346,6 +346,23 @@ 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:          May 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 for power and thermal decisions.
> +
> +               Read returns the last written value in kHz, or 0 if no
> +               value has been written. Write a kHz value in the range
> +               [lowest_freq, nominal_freq].
> +
> +               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/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index c76cfafa3589..ad6ece16c30d 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1682,6 +1682,41 @@ 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.
> + *
> + * Return: 0 on success or negative error code.
> + */
> +int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
> +{
> +       struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> +       struct cppc_perf_caps caps;
> +       int ret;
> +
> +       if (!cpc_desc) {
> +               pr_debug("No CPC descriptor for CPU:%d\n", cpu);
> +               return -ENODEV;
> +       }
> +
> +       ret = cppc_get_perf_caps(cpu, &caps);
> +       if (ret)
> +               return ret;
> +
> +       if (ospm_nominal_perf < caps.lowest_perf ||
> +           ospm_nominal_perf > caps.nominal_perf)
> +               return -EINVAL;
> +
> +       return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
> +
>  /**
>   * cppc_get_auto_act_window() - Read autonomous activity window register.
>   * @cpu: CPU from which to read register.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 7e7f9dfb7a24..6379b7ceee34 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -985,11 +985,50 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
>  CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>                          cppc_set_perf_limited)
>
> +static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
> +{
> +       struct cppc_cpudata *cpu_data = policy->driver_data;
> +       unsigned int freq_khz;
> +
> +       if (!cpu_data->ospm_nominal_perf)
> +               return sysfs_emit(buf, "0\n");
> +
> +       freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
> +                                   cpu_data->ospm_nominal_perf);
> +       return sysfs_emit(buf, "%u\n", freq_khz);
> +}
> +
> +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;
> +       unsigned int sib;
> +       u64 freq_khz;
> +       u32 perf;
> +       int ret;
> +
> +       ret = kstrtou64(buf, 0, &freq_khz);
> +       if (ret)
> +               return ret;
> +
> +       perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> +       for_each_cpu(sib, policy->cpus) {
> +               ret = cppc_set_ospm_nominal_perf(sib, perf);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       cpu_data->ospm_nominal_perf = 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,
> @@ -997,6 +1036,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>         &auto_act_window,
>         &energy_performance_preference_val,
>         &perf_limited,
> +       &ospm_nominal_freq,
>         NULL,
>  };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 8693890a7275..0b1dcdbea10a 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -153,6 +153,8 @@ struct cppc_cpudata {
>         struct cppc_perf_fb_ctrs perf_fb_ctrs;
>         unsigned int shared_type;
>         cpumask_var_t shared_cpu_map;
> +       /* Cached OSPM Nominal Performance value (write-only register). */
> +       u32 ospm_nominal_perf;
>  };
>
>  #ifdef CONFIG_ACPI_CPPC_LIB
> @@ -180,6 +182,7 @@ 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 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, bool *enable);
> @@ -266,6 +269,10 @@ 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 int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
>  {
>         return -EOPNOTSUPP;
> --
> 2.34.1
>
>

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

* Re: [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4
  2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta
@ 2026-05-26 17:38   ` Rafael J. Wysocki
  0 siblings, 0 replies; 12+ messages in thread
From: Rafael J. Wysocki @ 2026-05-26 17:38 UTC (permalink / raw)
  To: Sumit Gupta, mario.limonciello
  Cc: viresh.kumar, pierre.gondois, zhenglifeng1, zhanjie9,
	saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel,
	treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu,
	Mario Limonciello

On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
> CPPC v4 (ACPI 6.6, Section 8.4.6) adds two optional entries to the
> _CPC package:
>
> 1. OSPM Nominal Performance (8.4.6.1.2.6): A write-only register that
>    lets OSPM inform the platform what it considers nominal performance.
>    The platform classifies performance above this level as boost and
>    below as throttle for its power/thermal decisions.
>
> 2. Resource Priority (8.4.6.1.2.7): A Package of Resource Priority
>    Register Descriptor sub-packages that allow OSPM to set relative
>    priority among processors for shared resources (boost, throttle,
>    L2/L3 cache, memory bandwidth). Parsing the full structure is not
>    yet supported; such entries are marked as unsupported.
>
> Add v4 _CPC table parsing (25 entries) and update REG_OPTIONAL to
> mark the two new registers as optional.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>

I can apply this one if there are no objections.

The other patch in the series seems to require more work though.

Mario, does your follow-up series depend on both patches in this one?

> ---
>  drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------
>  include/acpi/cppc_acpi.h |  8 ++++++--
>  2 files changed, 23 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index f370be8715ae..c76cfafa3589 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -134,7 +134,7 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
>   * cpc_regs[] with the corresponding index. 0 means mandatory and 1
>   * means optional.
>   */
> -#define REG_OPTIONAL (0x1FC7D0)
> +#define REG_OPTIONAL (0x7FC7D0)
>
>  /*
>   * Use the index of the register in per-cpu cpc_regs[] to check if
> @@ -751,18 +751,19 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
>         /*
>          * Disregard _CPC if the number of entries in the return package is not
>          * as expected, but support future revisions being proper supersets of
> -        * the v3 and only causing more entries to be returned by _CPC.
> +        * the v4 and only causing more entries to be returned by _CPC.
>          */
>         if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) ||
>             (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) ||
> -           (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) {
> +           (cpc_rev == CPPC_V4_REV && num_ent != CPPC_V4_NUM_ENT) ||
> +           (cpc_rev > CPPC_V4_REV && num_ent <= CPPC_V4_NUM_ENT)) {
>                 pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n",
>                          num_ent, pr->id);
>                 goto out_free;
>         }
> -       if (cpc_rev > CPPC_V3_REV) {
> -               num_ent = CPPC_V3_NUM_ENT;
> -               cpc_rev = CPPC_V3_REV;
> +       if (cpc_rev > CPPC_V4_REV) {
> +               num_ent = CPPC_V4_NUM_ENT;
> +               cpc_rev = CPPC_V4_REV;
>         }
>
>         cpc_ptr->num_entries = num_ent;
> @@ -845,6 +846,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
>
>                         cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
>                         memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t));
> +               } else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) {
> +                       /*
> +                        * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a
> +                        * Package of Resource Priority Register Descriptor sub-packages.
> +                        * Parsing the full structure is not yet supported.
> +                        * Mark the register as unsupported for now.
> +                        */
> +                       pr_debug("CPU:%d Resource Priority not supported\n", pr->id);
> +                       cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER;
> +                       cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0;
>                 } else {
>                         pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n",
>                                  i, pr->id);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index d1f02ceec4f9..8693890a7275 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -17,16 +17,18 @@
>  #include <acpi/pcc.h>
>  #include <acpi/processor.h>
>
> -/* CPPCv2 and CPPCv3 support */
> +/* CPPCv2, CPPCv3 and CPPCv4 support */
>  #define CPPC_V2_REV    2
>  #define CPPC_V3_REV    3
> +#define CPPC_V4_REV    4
>  #define CPPC_V2_NUM_ENT        21
>  #define CPPC_V3_NUM_ENT        23
> +#define CPPC_V4_NUM_ENT        25
>
>  #define PCC_CMD_COMPLETE_MASK  (1 << 0)
>  #define PCC_ERROR_MASK         (1 << 2)
>
> -#define MAX_CPC_REG_ENT 21
> +#define MAX_CPC_REG_ENT 23
>
>  /* CPPC specific PCC commands. */
>  #define        CMD_READ 0
> @@ -109,6 +111,8 @@ enum cppc_regs {
>         REFERENCE_PERF,
>         LOWEST_FREQ,
>         NOMINAL_FREQ,
> +       OSPM_NOMINAL_PERF,
> +       RESOURCE_PRIORITY,
>  };
>
>  /*
> --
> 2.34.1
>
>

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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-21 16:58   ` Pierre Gondois
@ 2026-05-26 18:24     ` Sumit Gupta
  2026-05-28 12:07       ` Pierre Gondois
  0 siblings, 1 reply; 12+ messages in thread
From: Sumit Gupta @ 2026-05-26 18:24 UTC (permalink / raw)
  To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg


On 21/05/26 22:28, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sumit,
>
> I copy-pasted and attached a complementary patch just to show
> what I meant by caching the ospm_nominal_perf value at driver
> init/exit.
>
> This should also allow to handle the cases where a user tries
> to store a new value for a policy with multiple CPUs,
> one of the CPUs and we need to rollback.
>
> I think another issue that should be solved is that:
>
> - we don't the value of ospm_nominal_perf when loading the driver
>
> - we leave the register in an unknown state if we unload/reload
> the driver.
>
> Note:
> IMO caching values like in the patch would also benefit to the
> auto_sel register.
>
> ------
>
> If you think the patch helps, feel free to take it. Otherwise
> please let me know the issues it would raise.
>
> Regards,
>
> Pierre 


Hi Pierre,

Thanks for the review and the complementary patch.
Going point by point:

1. Rollback for a partially applied multiple CPU write in
     store_ospm_nominal_freq(): Agreed, will add into v4.

2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence
     checks that rely on it: I'd skip these as the register is write-only
     as per spec.

3. Initializing the register at startup and restoring at exit: In v3, we
     dropped the unconditional cpu_init write so user values would
     survive CPU hotplug. The spec also makes the explicit init
     unnecessary: "If this register is not provided, then OSPM must
     assume that the OSPM Nominal Performance value is equal to
     the Nominal Performance value.". The unwritten default already
     looks well defined.

4. pr_warn() in show_ospm_nominal_freq() on HW vs cache
     mismatch: Skipping it since it relies on (2).

So in v4, will pick up the rollback and leave the rest as is.
Happy to discuss further if you think differently.

Thank you,
Sumit Gupta


>
>
> On 5/14/26 21:48, Sumit Gupta wrote:
>> Expose the OSPM Nominal Performance register (ACPI 6.6, Section
>> 8.4.6.1.2.6), which conveys the desired nominal performance level
>> at which the platform may run. Unlike the existing read-only
>> Nominal Performance register, it is writable and lets OSPM
>> request a lower nominal level than the platform-reported nominal.
>> The platform classifies performance above this level as boosted
>> and below as throttled for its power/thermal decisions.
>>
>> It is exposed as a per-policy cpufreq sysfs attribute in kHz, to
>> match the cpufreq sysfs unit convention:
>>
>>    /sys/devices/system/cpu/cpufreq/policyN/ospm_nominal_freq
>>
>> The attribute is documented in
>> Documentation/ABI/testing/sysfs-devices-system-cpu.
>>
>> Writes are converted to perf via cppc_khz_to_perf(), validated
>> against [Lowest Performance, Nominal Performance], and applied to
>> every CPU in policy->cpus.
>>
>> The register is write-only; the kernel caches the last written
>> value in struct cppc_cpudata for sysfs readback (returns 0 until
>> userspace writes a value).
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>>   .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++++
>>   drivers/acpi/cppc_acpi.c                      | 35 ++++++++++++++++
>>   drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++++++++
>>   include/acpi/cppc_acpi.h                      |  7 ++++
>>   4 files changed, 99 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu 
>> b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> index 82d10d556cc8..ac1bf1b89ac4 100644
>> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
>> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> @@ -346,6 +346,23 @@ 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:                May 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 for power and thermal decisions.
>> +
>> +             Read returns the last written value in kHz, or 0 if no
>> +             value has been written. Write a kHz value in the range
>> +             [lowest_freq, nominal_freq].
>> +
>> +             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/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index c76cfafa3589..ad6ece16c30d 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1682,6 +1682,41 @@ 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.
>> + *
>> + * Return: 0 on success or negative error code.
>> + */
>> +int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
>> +{
>> +     struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> +     struct cppc_perf_caps caps;
>> +     int ret;
>> +
>> +     if (!cpc_desc) {
>> +             pr_debug("No CPC descriptor for CPU:%d\n", cpu);
>> +             return -ENODEV;
>> +     }
>> +
>> +     ret = cppc_get_perf_caps(cpu, &caps);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (ospm_nominal_perf < caps.lowest_perf ||
>> +         ospm_nominal_perf > caps.nominal_perf)
>> +             return -EINVAL;
>> +
>> +     return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, 
>> ospm_nominal_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
>> +
>>   /**
>>    * cppc_get_auto_act_window() - Read autonomous activity window 
>> register.
>>    * @cpu: CPU from which to read register.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c 
>> b/drivers/cpufreq/cppc_cpufreq.c
>> index 7e7f9dfb7a24..6379b7ceee34 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -985,11 +985,50 @@ store_energy_performance_preference_val(struct 
>> cpufreq_policy *policy,
>>   CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>>                        cppc_set_perf_limited)
>>
>> +static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, 
>> char *buf)
>> +{
>> +     struct cppc_cpudata *cpu_data = policy->driver_data;
>> +     unsigned int freq_khz;
>> +
>> +     if (!cpu_data->ospm_nominal_perf)
>> +             return sysfs_emit(buf, "0\n");
>> +
>> +     freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
>> + cpu_data->ospm_nominal_perf);
>> +     return sysfs_emit(buf, "%u\n", freq_khz);
>> +}
>> +
>> +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;
>> +     unsigned int sib;
>> +     u64 freq_khz;
>> +     u32 perf;
>> +     int ret;
>> +
>> +     ret = kstrtou64(buf, 0, &freq_khz);
>> +     if (ret)
>> +             return ret;
>> +
>> +     perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>> +
>> +     for_each_cpu(sib, policy->cpus) {
>> +             ret = cppc_set_ospm_nominal_perf(sib, perf);
>> +             if (ret)
>> +                     return ret;
>> +     }
>> +
>> +     cpu_data->ospm_nominal_perf = 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,
>> @@ -997,6 +1036,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>>       &auto_act_window,
>>       &energy_performance_preference_val,
>>       &perf_limited,
>> +     &ospm_nominal_freq,
>>       NULL,
>>   };
>>
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 8693890a7275..0b1dcdbea10a 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -153,6 +153,8 @@ struct cppc_cpudata {
>>       struct cppc_perf_fb_ctrs perf_fb_ctrs;
>>       unsigned int shared_type;
>>       cpumask_var_t shared_cpu_map;
>> +     /* Cached OSPM Nominal Performance value (write-only register). */
>> +     u32 ospm_nominal_perf;
>>   };
>>
>>   #ifdef CONFIG_ACPI_CPPC_LIB
>> @@ -180,6 +182,7 @@ 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 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, bool *enable);
>> @@ -266,6 +269,10 @@ 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 int cppc_get_auto_act_window(int cpu, u64 
>> *auto_act_window)
>>   {
>>       return -EOPNOTSUPP;
>
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu
> b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index ac1bf1b89ac4d..f3d357917242a 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -356,9 +356,7 @@Description:        OSPM Nominal Performance (kHz)
>                platform treats performance above this level as boost
>                and below as throttle for power and thermal decisions.
>
> -               Read returns the last written value in kHz, or 0 if no
> -               value has been written. Write a kHz value in the range
> -               [lowest_freq, nominal_freq].
> +Values are in kHz and in the range [lowest_freq, nominal_freq].
>
>                This file is only present if the cppc-cpufreq driver is
>                in use.
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 97218c4ac162b..01542184e69b2 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1717,6 +1717,19 @@int cppc_set_ospm_nominal_perf(int cpu, u64
> ospm_nominal_perf)
> }
> EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
>
> +/**
> + * cppc_get_ospm_nominal_perf() - Read OSPM Nominal Performance 
> register.
> + * @cpu: CPU from which to read register.
> + * @ospm_nominal_perf: Pointer to store the OSPM Nominal Performance 
> value.
> + *
> + * Return: 0 on success or negative error code.
> + */
> +int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf)
> +{
> +return cppc_get_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_ospm_nominal_perf);
> +
> /**
>  * cppc_get_auto_act_window() - Read autonomous activity window register.
>  * @cpu: CPU from which to read register.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c 
> b/drivers/cpufreq/cppc_cpufreq.c
> index 6379b7ceee347..1a51017a81630 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -28,6 +28,9 @@
>
> static struct cpufreq_driver cppc_cpufreq_driver;
>
> +/* Values at driver init. */
> +static u64 scratch_ospm_nominal_perf[NR_CPUS];
> +
> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
> static enum {
>        FIE_UNSET = -1,
> @@ -642,7 +645,7 @@static void cppc_cpufreq_put_cpu_data(struct
> cpufreq_policy *policy)
>
> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
> {
> -       unsigned int cpu = policy->cpu;
> +unsigned int sib, cpu = policy->cpu;
>        struct cppc_cpudata *cpu_data;
>        struct cppc_perf_caps *caps;
>        int ret;
> @@ -704,6 +707,34 @@static int cppc_cpufreq_cpu_init(struct
> cpufreq_policy *policy)
>        if (caps->highest_perf > caps->nominal_perf)
>                policy->boost_supported = true;
>
> +/*
> + * CPUs are expected to have:
> + *   ospm_nominal_perf = nominal_perf
> + * at policy init.
> + */
> +cpu_data->ospm_nominal_perf = caps->nominal_perf;
> +
> +for_each_cpu(sib, policy->cpus) {
> +u64 ospm_nominal_perf;
> +
> +ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf);
> +if (ret == -EOPNOTSUPP) {
> +continue;
> +} else if (ret) {
> +pr_err("CPU%d: Could not read ospm_nominal_perf value\n",
> +sib);
> +goto out;
> +}
> +
> +if (ospm_nominal_perf != caps->nominal_perf) {
> +pr_err("Incoherent ospm_nominal_perf values: "
> +"scratch_value=%llu expected_value=%u\n",
> +scratch_ospm_nominal_perf[sib],
> +caps->nominal_perf);
> +goto out;
> +}
> +}
> +
>        /* Set policy->cur to max now. The governors will adjust later. */
>        policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
>        cpu_data->perf_ctrls.desired_perf = caps->highest_perf;
> @@ -727,9 +758,17 @@static void cppc_cpufreq_cpu_exit(struct
> cpufreq_policy *policy)
> {
>        struct cppc_cpudata *cpu_data = policy->driver_data;
>        struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> -       unsigned int cpu = policy->cpu;
> +unsigned int sib, cpu = policy->cpu;
>        int ret;
>
> +/*
> + * CPUs are expected to have:
> + *   ospm_nominal_perf = nominal_perf
> + * at policy init, so come back to that state.
> + */
> +for_each_cpu(sib, policy->cpus)
> +cppc_set_ospm_nominal_perf(sib, caps->nominal_perf);
> +
>        cppc_cpufreq_cpu_fie_exit(policy);
>
>        cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
> @@ -989,9 +1028,20 @@static ssize_t show_ospm_nominal_freq(struct
> cpufreq_policy *policy, char *buf)
> {
>        struct cppc_cpudata *cpu_data = policy->driver_data;
>        unsigned int freq_khz;
> +u64 perf;
> +int ret;
> +
> +ret = cppc_get_ospm_nominal_perf(policy->cpu, &perf);
> +if (ret == -EOPNOTSUPP)
> +return sysfs_emit(buf, "<unsupported>\n");
> +if (ret)
> +return ret;
>
> -       if (!cpu_data->ospm_nominal_perf)
> -               return sysfs_emit(buf, "0\n");
> +if (perf != cpu_data->ospm_nominal_perf) {
> +pr_warn("ospm_nominal_freq value is not coherent: "
> +"register=%llu scratch_value=%u\n",
> +perf, cpu_data->ospm_nominal_perf);
> +}
>
>        freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
> cpu_data->ospm_nominal_perf);
> @@ -1002,7 +1052,7 @@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;
> -       unsigned int sib;
> +unsigned int sib, failing_cpu;
>        u64 freq_khz;
>        u32 perf;
>        int ret;
> @@ -1015,12 +1065,23 @@static ssize_t store_ospm_nominal_freq(struct
> cpufreq_policy *policy,
>
>        for_each_cpu(sib, policy->cpus) {
>                ret = cppc_set_ospm_nominal_perf(sib, perf);
> -               if (ret)
> -                       return ret;
> +if (ret) {
> +failing_cpu = sib;
> +goto error;
> +}
>        }
>
>        cpu_data->ospm_nominal_perf = perf;
>        return count;
> +
> +error:
> +for_each_cpu(sib, policy->cpus) {
> +if (sib == failing_cpu)
> +break;
> +cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf);
> +}
> +
> +return ret;
> }
>
> cpufreq_freq_attr_ro(freqdomain_cpus);
> @@ -1053,6 +1114,64 @@static struct cpufreq_driver cppc_cpufreq_driver 
> = {
>        .name = "cppc_cpufreq",
> };
>
> +static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void)
> +{
> +int cpu;
> +
> +for_each_present_cpu(cpu) {
> +if (scratch_ospm_nominal_perf[cpu] == U64_MAX)
> +continue;
> +
> +/* Reset ospm_nominal_perf to the value present at init. */
> +cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]);
> +}
> +}
> +
> +static int __init cppc_cpufreq_ospm_nominal_perf_init(void)
> +{
> +int ret, cpu;
> +
> +ret = 0;
> +
> +for_each_present_cpu(cpu) {
> +u64 nominal_perf;
> +
> +/*
> + * If one CPU failed, scratch_ospm_nominal_perf still needs
> + * to be initialized.
> + */
> +if (ret) {
> +scratch_ospm_nominal_perf[cpu] = U64_MAX;
> +continue;
> +}
> +
> +ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]);
> +if (ret) {
> +if (ret == -EOPNOTSUPP) {
> +/* Valid reason to fail. Continue to iterate. */
> +ret = 0;
> +}
> +
> +scratch_ospm_nominal_perf[cpu] = U64_MAX;
> +continue;
> +}
> +
> +/* Set ospm_nominal_perf to nominal_perf before policy init. */
> +ret = cppc_get_nominal_perf(cpu, &nominal_perf);
> +if (ret)
> +continue;
> +
> +ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf);
> +if (ret)
> +continue;
> +}
> +
> +if (ret)
> +cppc_cpufreq_ospm_nominal_perf_exit();
> +
> +return ret;
> +}
> +
> static int __init cppc_cpufreq_init(void)
> {
>        int ret;
> @@ -1063,9 +1182,17 @@static int __init cppc_cpufreq_init(void)
>        cppc_freq_invariance_init();
>        populate_efficiency_class();
>
> +ret = cppc_cpufreq_ospm_nominal_perf_init();
> +if (ret) {
> +cppc_freq_invariance_exit();
> +return ret;
> +}
> +
>        ret = cpufreq_register_driver(&cppc_cpufreq_driver);
> -       if (ret)
> +if (ret) {
> +cppc_cpufreq_ospm_nominal_perf_exit();
>                cppc_freq_invariance_exit();
> +}
>
>        return ret;
> }
> @@ -1074,6 +1201,7 @@static void __exit cppc_cpufreq_exit(void)
> {
>        cpufreq_unregister_driver(&cppc_cpufreq_driver);
>        cppc_freq_invariance_exit();
> +cppc_cpufreq_ospm_nominal_perf_exit();
> }
>
> module_exit(cppc_cpufreq_exit);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 0b1dcdbea10a2..07e6598fa837a 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -183,6 +183,7 @@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 int cppc_get_ospm_nominal_perf(int cpu, u64 *ospm_nominal_perf);
> 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, bool *enable);
> @@ -273,6 +274,10 @@static inline int cppc_set_ospm_nominal_perf(int
> cpu, u64 ospm_nominal_perf)
> {
>        return -EOPNOTSUPP;
> }
> +static inline int cppc_get_ospm_nominal_perf(int cpu, u64
> *ospm_nominal_perf)
> +{
> +return -EOPNOTSUPP;
> +}
> static inline int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
> {
>        return -EOPNOTSUPP;
>

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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-22 17:20   ` Rafael J. Wysocki
@ 2026-05-26 19:02     ` Sumit Gupta
  0 siblings, 0 replies; 12+ messages in thread
From: Sumit Gupta @ 2026-05-26 19:02 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: viresh.kumar, lenb, pierre.gondois, zhenglifeng1, zhanjie9,
	mario.limonciello, saket.dumbre, linux-acpi, linux-kernel,
	linux-pm, acpica-devel, treding, jonathanh, vsethi, ksitaraman,
	sanjayc, bbasu, sumitg


On 22/05/26 22:50, Rafael J. Wysocki wrote:
> External email: Use caution opening links or attachments
>
>
> On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>> Expose the OSPM Nominal Performance register (ACPI 6.6, Section
>> 8.4.6.1.2.6), which conveys the desired nominal performance level
>> at which the platform may run. Unlike the existing read-only
>> Nominal Performance register, it is writable and lets OSPM
>> request a lower nominal level than the platform-reported nominal.
>> The platform classifies performance above this level as boosted
>> and below as throttled for its power/thermal decisions.
>>
>> It is exposed as a per-policy cpufreq sysfs attribute in kHz, to
>> match the cpufreq sysfs unit convention:
>>
>>    /sys/devices/system/cpu/cpufreq/policyN/ospm_nominal_freq
>>
>> The attribute is documented in
>> Documentation/ABI/testing/sysfs-devices-system-cpu.
>>
>> Writes are converted to perf via cppc_khz_to_perf(), validated
>> against [Lowest Performance, Nominal Performance], and applied to
>> every CPU in policy->cpus.
>>
>> The register is write-only; the kernel caches the last written
>> value in struct cppc_cpudata for sysfs readback (returns 0 until
>> userspace writes a value).
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> Please see sashiko.dev's feedback on this patch:
>
> https://sashiko.dev/#/patchset/20260514194822.1841748-1-sumitg%40nvidia.com
>
> and address it, or let me know why you think that addressing it is not
> necessary.


Hi Rafael,

Sure, going through the Sashiko comments:

Patch 1:
- Comments #1 and #2 were also raised in the earlier Sashiko review.
   They are pre-existing issues with rare occurrence.
   Will address them in a separate hardening patch.

Patch 2:
 > Is it possible for a platform to define lowest_perf as 0?
....
Will fix by adding a bool cpu_data->ospm_nominal_perf_set to
distinguish an unwritten cpu_data->ospm_nominal_perf cache
from a valid 0 perf write.

 > Does passing the 64-bit freq_khz to cppc_khz_to_perf() cause a silent
 > truncation?
....
In v4, will switch freq_khz to 'unsigned int' and use kstrtouint() instead
of kstrtou64().

 > If cppc_set_ospm_nominal_perf() succeeds for the first CPU but fails on
 > a subsequent sibling, does this leave the policy out of sync?
In v4, will add rollback in store_ospm_nominal_freq() to restore the
previous value on siblings already updated before the failure.

Thank you,
Sumit Gupta


>
>> ---
>>   .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++++
>>   drivers/acpi/cppc_acpi.c                      | 35 ++++++++++++++++
>>   drivers/cpufreq/cppc_cpufreq.c                | 40 +++++++++++++++++++
>>   include/acpi/cppc_acpi.h                      |  7 ++++
>>   4 files changed, 99 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> index 82d10d556cc8..ac1bf1b89ac4 100644
>> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
>> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> @@ -346,6 +346,23 @@ 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:          May 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 for power and thermal decisions.
>> +
>> +               Read returns the last written value in kHz, or 0 if no
>> +               value has been written. Write a kHz value in the range
>> +               [lowest_freq, nominal_freq].
>> +
>> +               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/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index c76cfafa3589..ad6ece16c30d 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1682,6 +1682,41 @@ 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.
>> + *
>> + * Return: 0 on success or negative error code.
>> + */
>> +int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
>> +{
>> +       struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> +       struct cppc_perf_caps caps;
>> +       int ret;
>> +
>> +       if (!cpc_desc) {
>> +               pr_debug("No CPC descriptor for CPU:%d\n", cpu);
>> +               return -ENODEV;
>> +       }
>> +
>> +       ret = cppc_get_perf_caps(cpu, &caps);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (ospm_nominal_perf < caps.lowest_perf ||
>> +           ospm_nominal_perf > caps.nominal_perf)
>> +               return -EINVAL;
>> +
>> +       return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
>> +
>>   /**
>>    * cppc_get_auto_act_window() - Read autonomous activity window register.
>>    * @cpu: CPU from which to read register.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> index 7e7f9dfb7a24..6379b7ceee34 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -985,11 +985,50 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
>>   CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>>                           cppc_set_perf_limited)
>>
>> +static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
>> +{
>> +       struct cppc_cpudata *cpu_data = policy->driver_data;
>> +       unsigned int freq_khz;
>> +
>> +       if (!cpu_data->ospm_nominal_perf)
>> +               return sysfs_emit(buf, "0\n");
>> +
>> +       freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps,
>> +                                   cpu_data->ospm_nominal_perf);
>> +       return sysfs_emit(buf, "%u\n", freq_khz);
>> +}
>> +
>> +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;
>> +       unsigned int sib;
>> +       u64 freq_khz;
>> +       u32 perf;
>> +       int ret;
>> +
>> +       ret = kstrtou64(buf, 0, &freq_khz);
>> +       if (ret)
>> +               return ret;
>> +
>> +       perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>> +
>> +       for_each_cpu(sib, policy->cpus) {
>> +               ret = cppc_set_ospm_nominal_perf(sib, perf);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       cpu_data->ospm_nominal_perf = 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,
>> @@ -997,6 +1036,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>>          &auto_act_window,
>>          &energy_performance_preference_val,
>>          &perf_limited,
>> +       &ospm_nominal_freq,
>>          NULL,
>>   };
>>
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 8693890a7275..0b1dcdbea10a 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -153,6 +153,8 @@ struct cppc_cpudata {
>>          struct cppc_perf_fb_ctrs perf_fb_ctrs;
>>          unsigned int shared_type;
>>          cpumask_var_t shared_cpu_map;
>> +       /* Cached OSPM Nominal Performance value (write-only register). */
>> +       u32 ospm_nominal_perf;
>>   };
>>
>>   #ifdef CONFIG_ACPI_CPPC_LIB
>> @@ -180,6 +182,7 @@ 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 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, bool *enable);
>> @@ -266,6 +269,10 @@ 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 int cppc_get_auto_act_window(int cpu, u64 *auto_act_window)
>>   {
>>          return -EOPNOTSUPP;
>> --
>> 2.34.1
>>
>>

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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-26 18:24     ` Sumit Gupta
@ 2026-05-28 12:07       ` Pierre Gondois
  2026-06-09  8:53         ` Sumit Gupta
  0 siblings, 1 reply; 12+ messages in thread
From: Pierre Gondois @ 2026-05-28 12:07 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9,
	mario.limonciello, saket.dumbre, linux-acpi, linux-kernel,
	linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu

Hello Sumit,
>
> Hi Pierre,
>
> Thanks for the review and the complementary patch.
> Going point by point:
>
> 1. Rollback for a partially applied multiple CPU write in
>     store_ospm_nominal_freq(): Agreed, will add into v4.
>
> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence
>     checks that rely on it: I'd skip these as the register is write-only
>     as per spec.
>
NIT:
IIUC having a write-only register doesn't mean we cannot read it.
Cf. cppc_get_desired_perf()

> 3. Initializing the register at startup and restoring at exit: In v3, we
>     dropped the unconditional cpu_init write so user values would
>     survive CPU hotplug. The spec also makes the explicit init
>     unnecessary: "If this register is not provided, then OSPM must
>     assume that the OSPM Nominal Performance value is equal to
>     the Nominal Performance value.". The unwritten default already
>     looks well defined.

The concern I had was for the scenario where:

- the driver is loaded

- the user sets an ospm_nominal_freq value

- the driver is unloaded

In such case, the ospm_nominal_freq value will still be set to a
non-default value. The modifications suggested previously would
allow to handle that case to come back to the default value.

FWIU, we have:

+------+     +---------+     +-----------+     +------+
| User | <-> | CPPC    | <-> | CPPC      | <-> | CPPC |
+------+     | driver  |     | reg       |     | HW   |
              +---------+     | interface |     | reg  |
                              +-----------+     +------+

So if we want to handle:

- the case described above

- the case you mentioned, i.e. hot-plugging CPUs

maybe the scratch values should be stored along the CPPC register
interface. This would allow to handle complex cases where CPUs
are hotplugged and the driver is loaded/unloaded ?

Note: the same kind of scenario should apply to the auto_sel register


> 4. pr_warn() in show_ospm_nominal_freq() on HW vs cache
>     mismatch: Skipping it since it relies on (2).
>
> So in v4, will pick up the rollback and leave the rest as is.
> Happy to discuss further if you think differently.


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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-05-28 12:07       ` Pierre Gondois
@ 2026-06-09  8:53         ` Sumit Gupta
  2026-06-11 14:31           ` Pierre Gondois
  0 siblings, 1 reply; 12+ messages in thread
From: Sumit Gupta @ 2026-06-09  8:53 UTC (permalink / raw)
  To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
	linux-kernel, linux-pm, acpica-devel
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg


On 28/05/26 17:37, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sumit,
>>
>> Hi Pierre,
>>
>> Thanks for the review and the complementary patch.
>> Going point by point:
>>
>> 1. Rollback for a partially applied multiple CPU write in
>>     store_ospm_nominal_freq(): Agreed, will add into v4.
>>
>> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence
>>     checks that rely on it: I'd skip these as the register is write-only
>>     as per spec.
>>
> NIT:
> IIUC having a write-only register doesn't mean we cannot read it.
> Cf. cppc_get_desired_perf()


Good point.
v5 reads the register via a new cppc_get_ospm_nominal_perf().
So, show() returns the register value or "<unsupported>",
and dropped the cache/bool.


>
>> 3. Initializing the register at startup and restoring at exit: In v3, we
>>     dropped the unconditional cpu_init write so user values would
>>     survive CPU hotplug. The spec also makes the explicit init
>>     unnecessary: "If this register is not provided, then OSPM must
>>     assume that the OSPM Nominal Performance value is equal to
>>     the Nominal Performance value.". The unwritten default already
>>     looks well defined.
>
> The concern I had was for the scenario where:
>
> - the driver is loaded
>
> - the user sets an ospm_nominal_freq value
>
> - the driver is unloaded
>
> In such case, the ospm_nominal_freq value will still be set to a
> non-default value. The modifications suggested previously would
> allow to handle that case to come back to the default value.
>
> FWIU, we have:
>
> +------+     +---------+     +-----------+     +------+
> | User | <-> | CPPC    | <-> | CPPC      | <-> | CPPC |
> +------+     | driver  |     | reg       |     | HW   |
>              +---------+     | interface |     | reg  |
>                              +-----------+     +------+
>
> So if we want to handle:
>
> - the case described above
>
> - the case you mentioned, i.e. hot-plugging CPUs
>
> maybe the scratch values should be stored along the CPPC register
> interface. This would allow to handle complex cases where CPUs
> are hotplugged and the driver is loaded/unloaded ?
>
> Note: the same kind of scenario should apply to the auto_sel register
>

Right.
After unload, the register keeps the user set value instead of the
firmware value. In a follow-up, I will restore the firmware value on
unload and reapply the user value across hotplug, grouping the
OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP).
On my test platforms the registers survive hotplug, but that isn't
guaranteed in general.

I think it's better to keep the saved state in the cppc_cpufreq driver
rather than the CPPC register interface. intel_pstate and amd-pstate
do the same.
For reapply, will use a CPU hotplug callback rather than ->online/
->offline hooks. Those are only called when a policy gains its first
online CPU or loses its last one. cppc_cpufreq also has shared
(SHARED_TYPE_ANY) policy, offlining and onlining a single CPU
keeps the policy active, so neither hook is called for it. A per-CPU
hotplug callback is needed to cover that case.

Let me know if you have other thoughts.

Thank you,
Sumit Gupta


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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-06-09  8:53         ` Sumit Gupta
@ 2026-06-11 14:31           ` Pierre Gondois
  2026-06-15 18:23             ` Sumit Gupta
  0 siblings, 1 reply; 12+ messages in thread
From: Pierre Gondois @ 2026-06-11 14:31 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9,
	mario.limonciello, saket.dumbre
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, linux-pm,
	linux-kernel, acpica-devel, linux-acpi


On 6/9/26 10:53, Sumit Gupta wrote:
>
> On 28/05/26 17:37, Pierre Gondois wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> Hello Sumit,
>>>
>>> Hi Pierre,
>>>
>>> Thanks for the review and the complementary patch.
>>> Going point by point:
>>>
>>> 1. Rollback for a partially applied multiple CPU write in
>>>     store_ospm_nominal_freq(): Agreed, will add into v4.
>>>
>>> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence
>>>     checks that rely on it: I'd skip these as the register is 
>>> write-only
>>>     as per spec.
>>>
>> NIT:
>> IIUC having a write-only register doesn't mean we cannot read it.
>> Cf. cppc_get_desired_perf()
>
>
> Good point.
> v5 reads the register via a new cppc_get_ospm_nominal_perf().
> So, show() returns the register value or "<unsupported>",
> and dropped the cache/bool.
>
>
>>
>>> 3. Initializing the register at startup and restoring at exit: In 
>>> v3, we
>>>     dropped the unconditional cpu_init write so user values would
>>>     survive CPU hotplug. The spec also makes the explicit init
>>>     unnecessary: "If this register is not provided, then OSPM must
>>>     assume that the OSPM Nominal Performance value is equal to
>>>     the Nominal Performance value.". The unwritten default already
>>>     looks well defined.
>>
>> The concern I had was for the scenario where:
>>
>> - the driver is loaded
>>
>> - the user sets an ospm_nominal_freq value
>>
>> - the driver is unloaded
>>
>> In such case, the ospm_nominal_freq value will still be set to a
>> non-default value. The modifications suggested previously would
>> allow to handle that case to come back to the default value.
>>
>> FWIU, we have:
>>
>> +------+     +---------+     +-----------+     +------+
>> | User | <-> | CPPC    | <-> | CPPC      | <-> | CPPC |
>> +------+     | driver  |     | reg       |     | HW   |
>>              +---------+     | interface |     | reg  |
>>                              +-----------+     +------+
>>
>> So if we want to handle:
>>
>> - the case described above
>>
>> - the case you mentioned, i.e. hot-plugging CPUs
>>
>> maybe the scratch values should be stored along the CPPC register
>> interface. This would allow to handle complex cases where CPUs
>> are hotplugged and the driver is loaded/unloaded ?
>>
>> Note: the same kind of scenario should apply to the auto_sel register
>>
>
> Right.
> After unload, the register keeps the user set value instead of the
> firmware value. In a follow-up, I will restore the firmware value on
> unload and reapply the user value across hotplug, grouping the
> OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP).
> On my test platforms the registers survive hotplug, but that isn't
> guaranteed in general.
>
> I think it's better to keep the saved state in the cppc_cpufreq driver
> rather than the CPPC register interface. intel_pstate and amd-pstate
> do the same.
> For reapply, will use a CPU hotplug callback rather than ->online/
> ->offline hooks. Those are only called when a policy gains its first
> online CPU or loses its last one. cppc_cpufreq also has shared
> (SHARED_TYPE_ANY) policy, offlining and onlining a single CPU
> keeps the policy active, so neither hook is called for it. A per-CPU
> hotplug callback is needed to cover that case.
>
> Let me know if you have other thoughts.
>
Is it necessary to have cpuhp callbacks ?

The cppc_cpufreq driver should only support:
- SHARED_TYPE_HW/NONE: in such case, the polity should
only have one CPU
- SHARED_TYPE_ANY: in such case, configuring any CPU should
configure all the CPUs of the policy. The only issue is that
we currently don't know whether the CPUs in the policy
share the same CPPC registers or have individual registers.
I think it is currently strongly assumed there is only one
registers for all the CPUs.

For instance, if:
- CPU0/1 are in the same perf. domain
- don't have the same CPPC registers
- have a SHARED_TYPE_ANY policy (i.e. writing to any
of CPU0/1's CPPC registers triggers an update of the
hardware for the 2 CPUs)
- policy->cpu = 0
then:
- enabling auto_sel for the policy means configuring
CPU0's CPPC register
- if CPU0 is offlined, policy->cpu is updated to 1 (pointing to CPU1),
- then reading the policy's auto_sel register means reading
CPU1's CPPC register, which was not updated

So using the already present online()/offline() callbacks should be
enough. It would also be good to check that, for a policy, all the
CPPC registers are identical.

Note: having per-CPU CPPC registers + SHARED_TYPE_ANY
policy is theoretically valid, but unsupported FWIU.

Let me know if it makes sense or not,

Regards,

Pierre


> Thank you,
> Sumit Gupta
>

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

* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
  2026-06-11 14:31           ` Pierre Gondois
@ 2026-06-15 18:23             ` Sumit Gupta
  0 siblings, 0 replies; 12+ messages in thread
From: Sumit Gupta @ 2026-06-15 18:23 UTC (permalink / raw)
  To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1,
	zhanjie9, mario.limonciello, saket.dumbre
  Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, linux-pm,
	linux-kernel, acpica-devel, linux-acpi, sumitg

Hi Pierre,


On 11/06/26 20:01, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 6/9/26 10:53, Sumit Gupta wrote:
>>
>> On 28/05/26 17:37, Pierre Gondois wrote:
>>> External email: Use caution opening links or attachments
>>>
>>>
>>> Hello Sumit,
>>>>
>>>> Hi Pierre,
>>>>
>>>> Thanks for the review and the complementary patch.
>>>> Going point by point:
>>>>
>>>> 1. Rollback for a partially applied multiple CPU write in
>>>>     store_ospm_nominal_freq(): Agreed, will add into v4.
>>>>
>>>> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence
>>>>     checks that rely on it: I'd skip these as the register is
>>>> write-only
>>>>     as per spec.
>>>>
>>> NIT:
>>> IIUC having a write-only register doesn't mean we cannot read it.
>>> Cf. cppc_get_desired_perf()
>>
>>
>> Good point.
>> v5 reads the register via a new cppc_get_ospm_nominal_perf().
>> So, show() returns the register value or "<unsupported>",
>> and dropped the cache/bool.
>>
>>
>>>
>>>> 3. Initializing the register at startup and restoring at exit: In
>>>> v3, we
>>>>     dropped the unconditional cpu_init write so user values would
>>>>     survive CPU hotplug. The spec also makes the explicit init
>>>>     unnecessary: "If this register is not provided, then OSPM must
>>>>     assume that the OSPM Nominal Performance value is equal to
>>>>     the Nominal Performance value.". The unwritten default already
>>>>     looks well defined.
>>>
>>> The concern I had was for the scenario where:
>>>
>>> - the driver is loaded
>>>
>>> - the user sets an ospm_nominal_freq value
>>>
>>> - the driver is unloaded
>>>
>>> In such case, the ospm_nominal_freq value will still be set to a
>>> non-default value. The modifications suggested previously would
>>> allow to handle that case to come back to the default value.
>>>
>>> FWIU, we have:
>>>
>>> +------+     +---------+     +-----------+     +------+
>>> | User | <-> | CPPC    | <-> | CPPC      | <-> | CPPC |
>>> +------+     | driver  |     | reg       |     | HW   |
>>>              +---------+     | interface |     | reg  |
>>>                              +-----------+     +------+
>>>
>>> So if we want to handle:
>>>
>>> - the case described above
>>>
>>> - the case you mentioned, i.e. hot-plugging CPUs
>>>
>>> maybe the scratch values should be stored along the CPPC register
>>> interface. This would allow to handle complex cases where CPUs
>>> are hotplugged and the driver is loaded/unloaded ?
>>>
>>> Note: the same kind of scenario should apply to the auto_sel register
>>>
>>
>> Right.
>> After unload, the register keeps the user set value instead of the
>> firmware value. In a follow-up, I will restore the firmware value on
>> unload and reapply the user value across hotplug, grouping the
>> OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP).
>> On my test platforms the registers survive hotplug, but that isn't
>> guaranteed in general.
>>
>> I think it's better to keep the saved state in the cppc_cpufreq driver
>> rather than the CPPC register interface. intel_pstate and amd-pstate
>> do the same.
>> For reapply, will use a CPU hotplug callback rather than ->online/
>> ->offline hooks. Those are only called when a policy gains its first
>> online CPU or loses its last one. cppc_cpufreq also has shared
>> (SHARED_TYPE_ANY) policy, offlining and onlining a single CPU
>> keeps the policy active, so neither hook is called for it. A per-CPU
>> hotplug callback is needed to cover that case.
>>
>> Let me know if you have other thoughts.
>>
> Is it necessary to have cpuhp callbacks ?
>
> The cppc_cpufreq driver should only support:
> - SHARED_TYPE_HW/NONE: in such case, the polity should
> only have one CPU
> - SHARED_TYPE_ANY: in such case, configuring any CPU should
> configure all the CPUs of the policy. The only issue is that
> we currently don't know whether the CPUs in the policy
> share the same CPPC registers or have individual registers.
> I think it is currently strongly assumed there is only one
> registers for all the CPUs.
>
> For instance, if:
> - CPU0/1 are in the same perf. domain
> - don't have the same CPPC registers
> - have a SHARED_TYPE_ANY policy (i.e. writing to any
> of CPU0/1's CPPC registers triggers an update of the
> hardware for the 2 CPUs)
> - policy->cpu = 0
> then:
> - enabling auto_sel for the policy means configuring
> CPU0's CPPC register
> - if CPU0 is offlined, policy->cpu is updated to 1 (pointing to CPU1),
> - then reading the policy's auto_sel register means reading
> CPU1's CPPC register, which was not updated
>
> So using the already present online()/offline() callbacks should be
> enough. It would also be good to check that, for a policy, all the
> CPPC registers are identical.
>
> Note: having per-CPU CPPC registers + SHARED_TYPE_ANY
> policy is theoretically valid, but unsupported FWIU.
>
> Let me know if it makes sense or not,
>
> Regards,
>
> Pierre
>


Agreed, the cpuhp callback isn't needed.
For a SHARED_TYPE_ANY policy cppc_cpufreq treats the control registers
as equivalent across the policy's CPUs. A single CPU offline/online
within the policy therefore doesn't lose the value. Only a full teardown
and bring-up can lose value on the platforms that reset the register on
hotplug. That path already goes through ->exit/->init, so reapplying the
saved value from ->init() covers it without adding callbacks.

We can add a check that guards this assumption in cppc_cpufreq_cpu_init,
for the CPUFREQ_SHARED_TYPE_ANY case. Compare the control registers each
CPU in policy->cpus exposes (excluding per-CPU feedback counters) and
warn on a mismatch rather than failing init. Since it concerns existing
behavior, the change can be a separate independent patch. It flags the
per CPU registers case you described. Does that match what you had in mind?

Thank you,
Sumit Gupta



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

end of thread, other threads:[~2026-06-15 18:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta
2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta
2026-05-26 17:38   ` Rafael J. Wysocki
2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
2026-05-21 16:58   ` Pierre Gondois
2026-05-26 18:24     ` Sumit Gupta
2026-05-28 12:07       ` Pierre Gondois
2026-06-09  8:53         ` Sumit Gupta
2026-06-11 14:31           ` Pierre Gondois
2026-06-15 18:23             ` Sumit Gupta
2026-05-22 17:20   ` Rafael J. Wysocki
2026-05-26 19:02     ` Sumit Gupta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome