Re: [PATCH v5] ACPI: CPPC: Add ospm_nominal_perf support
From: Pierre Gondois
Date: Wed Jul 01 2026 - 12:26:35 EST
Hello Sumit,
On 6/15/26 20:59, 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.
On read, the current register value is returned, or
"<unsupported>" if the platform does not implement the register.
Signed-off-by: Sumit Gupta<sumitg@xxxxxxxxxx>
---
Patch 1 of the v4 series ("ACPI: CPPC: Add support for CPPC v4") is
already applied, so this contains only patch 2.
Changes in v5:
- Add cppc_get_ospm_nominal_perf() to read the register directly.
- Drop the cppc_cpudata cache variables ospm_nominal_perf/_set.
- Show_ospm_nominal_freq() returns register value or "<unsupported>".
- Register rollback reads the register too.
- Move range check into the sysfs store from cppc_set_ospm_nominal_perf()
- ABI doc: update read description and add a task-migration note.
v4:https://lore.kernel.org/lkml/20260527194626.185286-1-sumitg@xxxxxxxxxx/
v3:https://lore.kernel.org/lkml/20260514194822.1841748-1-sumitg@xxxxxxxxxx/
v2:https://lore.kernel.org/lkml/20260430142430.755437-1-sumitg@xxxxxxxxxx/
v1:https://lore.kernel.org/lkml/20260427051823.280419-1-sumitg@xxxxxxxxxx/
.../ABI/testing/sysfs-devices-system-cpu | 26 ++++++++
drivers/acpi/cppc_acpi.c | 32 +++++++++
drivers/cpufreq/cppc_cpufreq.c | 65 +++++++++++++++++++
include/acpi/cppc_acpi.h | 10 +++
4 files changed, 133 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 82d10d556cc8..a8d592c08823 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -346,6 +346,32 @@ 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@xxxxxxxxxxxxxxx
+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 current value in kHz, or "<unsupported>"
+ if the platform does not implement the register. Write a
+ kHz value in the range [lowest_freq, nominal_freq].
+
+ Note that tasks may be migrated from one CPU to another
+ by the scheduler's load-balancing algorithm, and if
+ different OSPM Nominal Performance values are set for
+ those CPUs (through different cpufreq policies), that may
+ lead to undesirable outcomes. To avoid such issues it is
+ better to set the same value across all policies, or to
+ pin every task potentially sensitive to it to a specific
+ CPU.
+
+ This file is only present if the cppc-cpufreq driver is
+ in use.
+
What: /sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
Date: August 2008
KernelVersion: 2.6.27
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9f572f481241..1fcc22a10b4c 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1685,6 +1685,38 @@ int cppc_set_epp(int cpu, u64 epp_val)
}
EXPORT_SYMBOL_GPL(cppc_set_epp);
+/**
+ * cppc_set_ospm_nominal_perf() - Write OSPM Nominal Performance register.
+ * @cpu: CPU on which to write register.
+ * @ospm_nominal_perf: Value to write to the OSPM Nominal Performance register.
+ *
+ * OSPM Nominal Performance conveys the desired nominal performance level
+ * at which the platform may run. Per ACPI 6.6, s8.4.6.1.2.6, the value
+ * must lie within [Lowest Performance, Nominal Performance] and may be
+ * set independently of Minimum, Maximum and Desired performance. The
+ * caller is responsible for validating the range.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
+{
+ return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf);
+
+/**
+ * cppc_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 f6cea0c54dd9..d160ceced7d9 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1011,11 +1011,75 @@ 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;
+ 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;
+
+ return sysfs_emit(buf, "%u\n",
+ cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int sib, freq_khz, failing_cpu = 0;
+ u64 prev_perf;
+ u32 perf;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &freq_khz);
+ if (ret)
+ return ret;
+
+ perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+ if (perf < cpu_data->perf_caps.lowest_perf ||
+ perf > cpu_data->perf_caps.nominal_perf)
+ return -EINVAL;
+
Sorry I didn't notice in v3, but this function is the only
one setting the registers for all CPUs in the policy.
In v2, the interface was per-CPU. The main issue with that
is that we need a mechanism to aggregate values of CPUs
in the same policy.
In v3/v4/v5, the interface was per-policy, but the
ospm_nominal_freq was set for all CPUs CPPC regs individually.
If we do that, the sequence:
- unplug a CPU from a policy
- set ospm_nominal_freq for the policy
- hotplug back the CPU
might cause inconsistencies in the CPPC regs.
Maybe it is better to continue with the assumption that all
CPUs in the same policy have the same interface ? This
assumption seems to be made for auto_sel and other regs.
If this makes sense, would it be possible to:
- add such check and warn if it is not the case
- set the ospm_nominal_freq value only for policy->cpu
(and remove the rollback mechanism)
When updating the ospm_nominal_freq, we should also:
- update policy->cpuinfo.max_freq if boost=0
- refresh the freq. limits
- set policy->boost_supported if it is not already the case
Also, update cppc_cpufreq_set_boost() should rely on
ospm_nominal_freq instead of caps->nominal_perf as a reference.
Please let me know if this is ok
Regards,
Pierre