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

Sumit Gupta <[email protected]>
Newsgroups dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm,org.kernel.vger.linux-tegra
Message-ID <[email protected]>
Hi Zhongqiu,


On 28/07/26 18:54, Zhongqiu Han wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sumit,
>
> On 7/18/2026 5:53 AM, 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 <[email protected]>
>> ---
>>   .../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:     [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 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);
>> +
>>   /**
>>    * 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));
>
> In patch 2/2, cppc_cpufreq_get_effective_nominal() reads the OSPM
> Nominal into ospm_nominal via the output pointer of
> cppc_get_ospm_nominal_perf(policy->cpu, &ospm_nominal), then checks
> ospm_nominal for zero ("A zero value means OSPM has not selected a
> nominal level") and substitutes the platform Nominal in that case.
>
> However, show_ospm_nominal_freq() passes the value read through the same
> output pointer straight into cppc_perf_to_khz() without the equivalent
> zero check. This looks a bit inconsistent — is it intentional, or
> should show() apply the same "zero means not selected" handling?
>

Thanks for pointing this out.
In v7, I will make ospm_nominal_freq as write-only and remove sysfs
show() and cppc_get_ospm_nominal_perf(), eliminating this inconsistency.
The effective nominal calculation will use the last successfully
written value, falling back to the platform Nominal Performance if none
has been written.

Thanks,
Sumit


>
>> +}
>> +
>> +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;
>
>
> -- 
> Thx and BRs,
> Zhongqiu Han
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.