Re: [PATCH v5] ACPI: CPPC: Add ospm_nominal_perf support
Sumit Gupta <[email protected]> Thu, 9 Jul 2026 22:31:36 +0530
| Newsgroups | dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
On 01/07/26 21:53, Pierre Gondois wrote: > External email: Use caution opening links or attachments > > > 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<[email protected]> >> --- >> 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/[email protected]/ >> >> v3:https://lore.kernel.org/lkml/[email protected]/ >> >> v2:https://lore.kernel.org/lkml/[email protected]/ >> >> v1:https://lore.kernel.org/lkml/[email protected]/ >> >> >> .../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: [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 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 Will send the mismatch check for the SHARED_TYPE_ANY case as a separate patch, as it is a pre-existing issue. > - set the ospm_nominal_freq value only for policy->cpu > (and remove the rollback mechanism) Yes, will write only policy->cpu and drop the rollback assuming a shared register. > > 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 Agreed. I will add an "effective nominal" (ospm_nominal_freq when set, else caps->nominal_perf) and use it everywhere. Also will clear boost_supported when it is raised back to highest_perf. Thanks, Sumit