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

From: Christian Loehle

Date: Mon Jul 27 2026 - 10:03:53 EST


On 7/17/26 22:53, 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/cpuX/cpufreq/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
> the policy->cpu. The register is assumed shared across the
> policy->cpus.
>
> On read, the current register value is returned, or
> "<unsupported>" if the platform does not implement the register.
>
> Also add the register to the OSPM-set register save/restore
> table, so its value survives CPU hotplug and reverts to the
> firmware value on driver unload, like the other registers in
> the table.
>
> Signed-off-by: Sumit Gupta <sumitg@xxxxxxxxxx>
> ---
> .../ABI/testing/sysfs-devices-system-cpu | 26 ++++++++++
> drivers/acpi/cppc_acpi.c | 32 +++++++++++++
> drivers/cpufreq/cppc_cpufreq.c | 47 +++++++++++++++++++
> include/acpi/cppc_acpi.h | 10 ++++
> 4 files changed, 115 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 a7fec6c93178..681d4fd40c11 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);

It's a write-only register, we need to track everything in the driver.

> +
> /**
> * 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 9c88512d635c..eb6746810fa6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -37,6 +37,7 @@ enum cppc_saved_reg_id {
> CPPC_SAVED_AUTO_SEL,
> CPPC_SAVED_EPP,
> CPPC_SAVED_AUTO_ACT_WINDOW,
> + CPPC_SAVED_OSPM_NOMINAL_PERF,
> CPPC_NR_SAVED_REGS,
> };
>
> @@ -55,6 +56,9 @@ static const struct cppc_saved_reg cppc_saved_regs[CPPC_NR_SAVED_REGS] = {
> [CPPC_SAVED_AUTO_ACT_WINDOW] = {
> cppc_get_auto_act_window, cppc_set_auto_act_window,
> },
> + [CPPC_SAVED_OSPM_NOMINAL_PERF] = {
> + cppc_get_ospm_nominal_perf, cppc_set_ospm_nominal_perf,
> + },
> };
>
> /*
> @@ -1166,11 +1170,53 @@ 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 freq_khz;
> + u32 perf;
> + int ret;
> +
> + ret = kstrtouint(buf, 0, &freq_khz);
> + if (ret)
> + return ret;
> +
> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> + if (perf < cpu_data->perf_caps.lowest_perf ||
> + perf > cpu_data->perf_caps.nominal_perf)
> + return -EINVAL;
> +
> + /* The register is assumed shared across the policy's CPUs. */
> + ret = cppc_set_ospm_nominal_perf(policy->cpu, perf);
> + if (ret)
> + return ret;
> +
> + 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,
> @@ -1178,6 +1224,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 cd07e1e92bf4..4c9e59643cc0 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -180,6 +180,8 @@ extern int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val);
> extern int cppc_get_epp_perf(int cpunum, u64 *epp_perf);
> extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable);
> extern int cppc_set_epp(int cpu, u64 epp_val);
> +extern int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf);
> +extern 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);
> @@ -268,6 +270,14 @@ static inline int cppc_set_epp(int cpu, u64 epp_val)
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline 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;