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

"Rafael J. Wysocki" <[email protected]> Mon, 1 Jun 2026 19:37:45 +0200
Newsgroups dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm
Message-ID <CAJZ5v0icPAmaiThjgUTxWRymNGBQFarodurJhODFOkLKpVwtOQ@mail.gmail.com>
On Wed, May 27, 2026 at 9:47 PM Sumit Gupta <[email protected]> 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 <[email protected]>

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

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

> ---
>  .../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(+)
>
> 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:       [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
> 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 15a728dea911..5c54af1655b5 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -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,
>  };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 9b18fb9aab7c..14c6d5d0ac12 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -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
>