Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
From: Rafael J. Wysocki
Date: Fri May 22 2026 - 13:29:02 EST
On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@xxxxxxxxxx> 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@xxxxxxxxxx>
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@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 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
>
>