Skip to contentSkip to conversation

ACPI: CPPC: Add CPPC v4 support (ACPI 6.6)

PATCH Applied Sumit Gupta 4 participants 12 messages May 27 – Jun 9

Subsystems (5)
lore
Share & tools
lore

Sumit Gupta

author reviewer
#0 lore raw

v4 cover ACPI: CPPC: Add CPPC v4 support (ACPI 6.6)

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.

---
v3[3] -> v4:
- Patch 2:
- Add a bool cpu_data->ospm_nominal_perf_set to distinguish an unwritten cache from a valid perf=0 write.
- Switch freq_khz to unsigned int and use kstrtouint().
- Add rollback in store_ospm_nominal_freq() to restore the previous cached value on a partial multi-CPU write failure.

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                | 60 +++++++++++++++++++
 include/acpi/cppc_acpi.h                      | 20 ++++++-
 4 files changed, 147 insertions(+), 8 deletions(-)

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/[email protected]/
[3] https://lore.kernel.org/lkml/[email protected]/

Signature · 1 line
2.34.1
author reviewer
#1 lore raw

PATCH v4 1/2 ACPI: CPPC: Add support for CPPC v4

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.

---
 drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------
 include/acpi/cppc_acpi.h |  8 ++++++--
 2 files changed, 23 insertions(+), 8 deletions(-)
drivers/acpi/cppc_acpi.c +17 -6
index f370be8715ae..c76cfafa3589 100644
@@ -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);
include/acpi/cppc_acpi.h +6 -2
index ee8352830c32..9b18fb9aab7c 100644
@@ -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
@@ -110,6 +112,8 @@ enum cppc_regs {
 	REFERENCE_PERF,
 	LOWEST_FREQ,
 	NOMINAL_FREQ,
+	OSPM_NOMINAL_PERF,
+	RESOURCE_PRIORITY,
 };
 
 /*
Signature · 1 line
2.34.1
#2 lore raw

Re: [PATCH v4 1/2] ACPI: CPPC: Add support for CPPC v4

Quoted 15 lines · Sumit Gupta

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.

Quoted 6 lines

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

This patch is good to go AFAICS, while there are still comments on the second one in the series.

Do you want me to apply this one separately?

Quoted 97 lines
---
 drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------
 include/acpi/cppc_acpi.h |  8 ++++++--
 2 files changed, 23 insertions(+), 8 deletions(-)
drivers/acpi/cppc_acpi.c +17 -6
index f370be8715ae..c76cfafa3589 100644
@@ -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);
include/acpi/cppc_acpi.h +6 -3
index ee8352830c32..9b18fb9aab7c 100644
@@ -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
@@ -110,6 +112,8 @@ enum cppc_regs {
        REFERENCE_PERF,
        LOWEST_FREQ,
        NOMINAL_FREQ,
+       OSPM_NOMINAL_PERF,
+       RESOURCE_PRIORITY,
 };
 
 /*
--

2.34.1

author reviewer
#3 lore raw

Re: [PATCH v4 1/2] ACPI: CPPC: Add support for CPPC v4

Hi Rafael,

Quoted 21 lines · Rafael J. Wysocki

External email: Use caution opening links or attachments

Quoted 17 lines · Sumit Gupta

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.

Quoted 7 lines
Quoted 3 lines

This patch is good to go AFAICS, while there are still comments on the second one in the series.

Do you want me to apply this one separately?

Yes please. Patch 1 is independent of patch 2.
I will send patch 2 separately as a v5 after addressing the review comments.

Thank you,
Sumit Gupta

Quoted 98 lines
Quoted 98 lines
---
  drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------
  include/acpi/cppc_acpi.h |  8 ++++++--
  2 files changed, 23 insertions(+), 8 deletions(-)
drivers/acpi/cppc_acpi.c +17 -6
index f370be8715ae..c76cfafa3589 100644
@@ -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);
include/acpi/cppc_acpi.h +6 -3
index ee8352830c32..9b18fb9aab7c 100644
@@ -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
@@ -110,6 +112,8 @@ enum cppc_regs {
         REFERENCE_PERF,
         LOWEST_FREQ,
         NOMINAL_FREQ,
+       OSPM_NOMINAL_PERF,
+       RESOURCE_PRIORITY,
  };
 
  /*
--

2.34.1

maintainer Applied
#4 lore raw

Re: [PATCH v4 1/2] ACPI: CPPC: Add support for CPPC v4

Quoted 29 lines · Sumit Gupta

Hi Rafael,

Quoted 24 lines · Rafael J. Wysocki

External email: Use caution opening links or attachments

Quoted 20 lines · Sumit Gupta

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.

Quoted 8 lines
Quoted 4 lines

This patch is good to go AFAICS, while there are still comments on the second one in the series.

Do you want me to apply this one separately?

Yes please. Patch 1 is independent of patch 2.
I will send patch 2 separately as a v5 after addressing the review comments.

Applied as 7.2 material, thanks!

author reviewer
#5 lore raw

PATCH v4 2/2 ACPI: CPPC: Add ospm_nominal_perf support

In reply to Sumit Gupta

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).

---
 .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
 drivers/acpi/cppc_acpi.c                      | 35 +++++++++++
 drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++
 include/acpi/cppc_acpi.h                      | 12 ++++
 4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +17 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -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:	[email protected]
+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
drivers/acpi/cppc_acpi.c +35 -0
index c76cfafa3589..ad6ece16c30d 100644
@@ -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.
drivers/cpufreq/cppc_cpufreq.c +60 -0

77 lines not on this page · raw message

include/acpi/cppc_acpi.h +12 -0
index 9b18fb9aab7c..14c6d5d0ac12 100644
@@ -154,6 +154,13 @@ 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).
+	 * ospm_nominal_perf_set is true once userspace has written, so an
+	 * unwritten cache is distinguishable from a valid 0 perf write.
+	 */
+	u32 ospm_nominal_perf;
+	bool ospm_nominal_perf_set;
 };
 
 #ifdef CONFIG_ACPI_CPPC_LIB
@@ -181,6 +188,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);
@@ -267,6 +275,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;
Signature · 1 line
2.34.1
#6 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

Hello Sumit,

Quoted 111 lines · Sumit Gupta

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).

---
  .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
  drivers/acpi/cppc_acpi.c                      | 35 +++++++++++
  drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++
  include/acpi/cppc_acpi.h                      | 12 ++++
  4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +17 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -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:	[email protected]
+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
drivers/acpi/cppc_acpi.c +35 -0
index c76cfafa3589..ad6ece16c30d 100644
@@ -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.
drivers/cpufreq/cppc_cpufreq.c +0 -0
index 15a728dea911..5c54af1655b5 100644

drivers/cpufreq/cppc_cpufreq.c static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited) · line 1148

Quoted 10 lines
drivers/cpufreq/cppc_cpufreq.c +7 -0
@@ -1139,11 +1139,70 @@ static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited)
  CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered,
  			 cppc_set_perf_limited)
 
+static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	unsigned int freq_khz;
+
+	if (!cpu_data->ospm_nominal_perf_set)
+		return sysfs_emit(buf, "0\n");

The questions on v3 might be more relevant,
but for instance here, the ospm_nominal_perf value is not 0, the hardware register might contain any value.

So reading the register might be more meaningful than returning 0.

author reviewer
#7 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

Quoted 138 lines · Pierre Gondois

External email: Use caution opening links or attachments

Hello Sumit,

Quoted 117 lines · Sumit Gupta

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).

---
  .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++ drivers/acpi/cppc_acpi.c                      | 35 +++++++++++ drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++ include/acpi/cppc_acpi.h                      | 12 ++++

  4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +0 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -346,6 +346,23 @@ Description:     Performance Limited
 

              This file is only present if the cppc-cpufreq driver is in use.

Documentation/ABI/testing/sysfs-devices-system-cpu +17 -0
+What: /sys/devices/system/cpu/cpuX/cpufreq/ospm_nominal_freq
+Date:                May 2026
+Contact:     [email protected]
+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

drivers/acpi/cppc_acpi.c +2 -0
index c76cfafa3589..ad6ece16c30d 100644
@@ -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.
+ */

drivers/acpi/cppc_acpi.c +20 -0
+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);

drivers/acpi/cppc_acpi.c +3 -0
+}
+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.

drivers/cpufreq/cppc_cpufreq.c +0 -0
index 15a728dea911..5c54af1655b5 100644
@@ -1139,11 +1139,70 @@ static int cppc_get_perf_limited_filtered(int 
 cpu, u64 *perf_limited)
   CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered,

drivers/cpufreq/cppc_cpufreq.c static int cppc_get_perf_limited_filtered(int · line 1150

Quoted 10 lines
drivers/cpufreq/cppc_cpufreq.c +7 -0
                        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_set)
+             return sysfs_emit(buf, "0\n");

The questions on v3 might be more relevant,
but for instance here, the ospm_nominal_perf value is not 0, the hardware register might contain any value.

Quoted 4 lines

So reading the register might be more meaningful than returning 0.

Agreed.
In v5, reading the register instead of caching:

- Added cppc_get_ospm_nominal_perf().
- show_ospm_nominal_freq() now returns the current register value, or "<unsupported>" on -EOPNOTSUPP.
- Dropped the cpu_data->ospm_nominal_perf cache and the ospm_nominal_perf_set bool entirely.

This also follows your earlier point that being write-only doesn't mean we can't read it (cppc_get_desired_perf() being the precedent).

Thank you,
Sumit Gupta

maintainer
#8 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

In reply to Sumit Gupta
Quoted 47 lines · Sumit Gupta

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).

---
 .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
 drivers/acpi/cppc_acpi.c                      | 35 +++++++++++
 drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++
 include/acpi/cppc_acpi.h                      | 12 ++++
 4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +7 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -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:	[email protected]
+Description:	OSPM Nominal Performance (kHz)
+
+		OSPM uses this attribute to request a nominal performance
+		level lower than the platform-reported nominal. The

Documentation/ABI/testing/sysfs-devices-system-cpu Description: Performance Limited · line 365

Quoted 10 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +10 -0
+		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.
+

Given that this value, based also on firmware behavior, can create vast asymmetries between CPUs, which the scheduler would be unaware of I wonder if this warrants a similar disclaimer like intel_pstate carries for their per-CPU EPP:
https://www.kernel.org/doc/html/v7.0/admin-guide/pm/intel_pstate.html#energy-vs-performance-hints

"[Note that tasks may by migrated from one CPU to another by the scheduler’s load-balancing algorithm and if different energy vs performance hints are set for those CPUs, that may lead to undesirable outcomes. To avoid such issues it is better to set the same energy vs performance hint for all CPUs or to pin every task potentially sensitive to them to a specific CPU.]"

author reviewer
#9 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

Quoted 66 lines · Christian Loehle

External email: Use caution opening links or attachments

Quoted 47 lines · Sumit Gupta

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).

---
  .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
  drivers/acpi/cppc_acpi.c                      | 35 +++++++++++
  drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++
  include/acpi/cppc_acpi.h                      | 12 ++++
  4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +7 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -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:     [email protected]
+Description: OSPM Nominal Performance (kHz)
+
+             OSPM uses this attribute to request a nominal performance
+             level lower than the platform-reported nominal. The

Documentation/ABI/testing/sysfs-devices-system-cpu Description: Performance Limited · line 365

Quoted 10 lines
Documentation/ABI/testing/sysfs-devices-system-cpu +10 -0
+             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.
+

Given that this value, based also on firmware behavior, can create vast asymmetries between CPUs, which the scheduler would be unaware of I wonder if this warrants a similar disclaimer like intel_pstate carries for their per-CPU EPP:
https://www.kernel.org/doc/html/v7.0/admin-guide/pm/intel_pstate.html#energy-vs-performance-hints

Quoted 1 line

"[Note that tasks may by migrated from one CPU to another by the scheduler’s load-balancing algorithm and if different energy vs performance hints are set for those CPUs, that may lead to undesirable outcomes. To avoid such issues it is better to set the same energy vs performance hint for all CPUs or to pin every task potentially sensitive to them to a specific CPU.]"

Good point.
In v5 added below to the sysfs ABI doc, modeled on similar note:

  Note that tasks may be migrated from one CPU to another by the scheduler's load-balancing algorithm, and if different OSPM Nominal Performance values are set for those CPUs, that may lead to undesirable outcomes. To avoid such issues it is better to set the same value across all policies, or to pin every potentially sensitive task to a specific CPU.

Thank you,
Sumit Gupta

#10 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

In reply to Sumit Gupta
Quoted 21 lines · Sumit Gupta

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.

Quoted 5 lines

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).

There is some sashiko.dev feedback on this one that is valid AFAICS:

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

Quoted 204 lines
---
 .../ABI/testing/sysfs-devices-system-cpu      | 17 ++++++
 drivers/acpi/cppc_acpi.c                      | 35 +++++++++++
 drivers/cpufreq/cppc_cpufreq.c                | 60 +++++++++++++++++++
 include/acpi/cppc_acpi.h                      | 12 ++++
 4 files changed, 124 insertions(+)
Documentation/ABI/testing/sysfs-devices-system-cpu +17 -0
index 82d10d556cc8..ac1bf1b89ac4 100644
@@ -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:       [email protected]
+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
drivers/acpi/cppc_acpi.c +35 -0
index c76cfafa3589..ad6ece16c30d 100644
@@ -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.
drivers/cpufreq/cppc_cpufreq.c +60 -0
index 15a728dea911..5c54af1655b5 100644
@@ -1139,11 +1139,70 @@ static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited)
 CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered,
                         cppc_set_perf_limited)
 
+static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
+{
+       struct cppc_cpudata *cpu_data = policy->driver_data;
+       unsigned int freq_khz;
+
+       if (!cpu_data->ospm_nominal_perf_set)
+               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;
+       bool prev_set = cpu_data->ospm_nominal_perf_set;
+       u32 prev_perf = cpu_data->ospm_nominal_perf;
+       unsigned int sib, freq_khz, failing_cpu = 0;
+       u32 perf;
+       int ret;
+
+       ret = kstrtouint(buf, 0, &freq_khz);
+       if (ret)
+               return ret;
+
+       perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+
+       for_each_cpu(sib, policy->cpus) {
+               ret = cppc_set_ospm_nominal_perf(sib, perf);
+               if (ret) {
+                       failing_cpu = sib;
+                       goto rollback;
+               }
+       }
+
+       cpu_data->ospm_nominal_perf = perf;
+       cpu_data->ospm_nominal_perf_set = true;
+       return count;
+
+rollback:
+       /*
+        * Restore the previous value on siblings already updated.
+        * for_each_cpu() iterates in CPU-id order, so siblings before
+        * @failing_cpu were updated successfully. Skip restore when
+        * there is no prior known-good value (first-ever write failed).
+        */
+       if (prev_set) {
+               for_each_cpu(sib, policy->cpus) {
+                       if (sib == failing_cpu)
+                               break;
+                       cppc_set_ospm_nominal_perf(sib, prev_perf);
+               }
+       }
+       return ret;
+}
+
 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,
@@ -1151,6 +1210,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
        &auto_act_window,
        &energy_performance_preference_val,
        &perf_limited,
+       &ospm_nominal_freq,
        NULL,
 };
 
include/acpi/cppc_acpi.h +12 -1
index 9b18fb9aab7c..14c6d5d0ac12 100644
@@ -154,6 +154,13 @@ 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).
+        * ospm_nominal_perf_set is true once userspace has written, so an
+        * unwritten cache is distinguishable from a valid 0 perf write.
+        */
+       u32 ospm_nominal_perf;
+       bool ospm_nominal_perf_set;
 };
 
 #ifdef CONFIG_ACPI_CPPC_LIB
@@ -181,6 +188,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);
@@ -267,6 +275,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

author reviewer
#11 lore raw

Re: [PATCH v4 2/2] ACPI: CPPC: Add ospm_nominal_perf support

Quoted 24 lines · Rafael J. Wysocki

External email: Use caution opening links or attachments

Quoted 20 lines · Sumit Gupta

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.

Quoted 8 lines
Quoted 5 lines

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).

There is some sashiko.dev feedback on this one that is valid AFAICS:

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

Thanks for sharing. Will address all four in v5.

Quoted 2 lines

Could this validation reject valid frequencies on systems with CPPC favored cores?

1.
In v5, moved the range check into store_ospm_nominal_freq() in cppc_cpufreq.c, where it uses the same cpu_data->perf_caps as the kHz->perf conversion. OSPM Nominal is treated as a single per-policy value. It's validated once against the policy caps and applied to all CPUs in policy->cpus. This avoids validating against a sibling's separately-read caps.

Quoted 3 lines

When the platform's ACPI _CPC table does not support this register, will this unconditionally return '0' instead of an error like -EOPNOTSUPP?

2.
In v5, added cppc_get_ospm_nominal_perf() and dropped the cached value. show() now reads the register and returns "<unsupported>" on -EOPNOTSUPP.

Quoted 1 line

Are newly onlined CPUs missing this cached ospm_nominal_perf value?

3.
show() now reads the register directly and not cached value, so it always reflects true per-CPU state.

Quoted 2 lines

Does skipping this rollback loop when prev_set is false leave the CPUs in a desynchronized state?

In v5, the cache and ospm_nominal_perf_set bool are removed and show() reads the register, so a partial write is no longer hidden. sysfs shows the real per-CPU value, not '0').
store() reads the current value before writing and restores it on already updated CPUs if a sibling write fails, so there is no first write skip.

Thank you,
Sumit Gupta
....