* [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6)
@ 2026-05-14 19:48 Sumit Gupta
2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta
2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta
0 siblings, 2 replies; 12+ messages in thread
From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1,
zhanjie9, mario.limonciello, saket.dumbre, linux-acpi,
linux-kernel, linux-pm, acpica-devel
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg
Add initial kernel support for CPPC v4 (ACPI 6.6, Section 8.4.6),
which extends the _CPC package from 23 to 25 entries with two
optional fields:
- OSPM Nominal Performance (8.4.6.1.2.6): register used by OSPM
to tell the platform what it considers nominal. The platform
classifies performance above this as boost and below as
throttle for power/thermal decisions.
- Resource Priority (8.4.6.1.2.7): Package of Resource Priority
Register Descriptor sub-packages. Full parsing is not yet
implemented; such entries are marked as unsupported.
Patch 1: Add v4 _CPC parsing - validate the 25-entry layout,
restrict the Resource Priority package fallback to that slot
only, and mark the two new registers optional.
Patch 2: Add per-policy sysfs attribute ospm_nominal_freq (kHz)
under cpufreq. Writes are converted to perf and applied to every
CPU in policy->cpus.
---
v2[2] -> v3:
- Patch 1:
- Accept ACPI_TYPE_PACKAGE only at RESOURCE_PRIORITY; abort otherwise.
- Added Reviewed-by from Pierre Gondois.
- Patch 2:
- Sysfs moved from per-CPU acpi_cppc to per-policy cpufreq.
- Read ospm_nominal_freq sysfs input in kHz and convert to perf.
- Cached ospm_nominal_perf moved from cpc_desc to cppc_cpudata.
- Validate input against [Lowest Performance, Nominal Performance]
in cppc_set_ospm_nominal_perf().
- Dropped unconditional init in cppc_cpufreq_cpu_init().
- Remove to skip write if cache matches in cppc_set_ospm_nominal_perf.
- Early -ENODEV return when no CPC descriptor.
- Added ABI documentation at
Documentation/ABI/testing/sysfs-devices-system-cpu.
v1[1] -> v2:
- Patch 1: Added Reviewed-by from Mario Limonciello.
- Patch 2:
- Make ospm_nominal_perf sysfs read-write; cache last write in
cpc_desc and skip redundant register writes.
- Validate input in cppc_set_ospm_nominal_perf.
Sumit Gupta (2):
ACPI: CPPC: Add support for CPPC v4
ACPI: CPPC: Add ospm_nominal_perf support
.../ABI/testing/sysfs-devices-system-cpu | 17 ++++++
drivers/acpi/cppc_acpi.c | 58 +++++++++++++++++--
drivers/cpufreq/cppc_cpufreq.c | 40 +++++++++++++
include/acpi/cppc_acpi.h | 15 ++++-
4 files changed, 122 insertions(+), 8 deletions(-)
[1] https://lore.kernel.org/lkml/20260427051823.280419-1-sumitg@nvidia.com/
[2] https://lore.kernel.org/lkml/20260430142430.755437-1-sumitg@nvidia.com/
--
2.34.1
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta @ 2026-05-14 19:48 ` Sumit Gupta 2026-05-26 17:38 ` Rafael J. Wysocki 2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta 1 sibling, 1 reply; 12+ messages in thread From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw) To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg, Mario Limonciello CPPC v4 (ACPI 6.6, Section 8.4.6) adds two optional entries to the _CPC package: 1. OSPM Nominal Performance (8.4.6.1.2.6): A write-only register that lets OSPM inform the platform what it considers nominal performance. The platform classifies performance above this level as boost and below as throttle for its power/thermal decisions. 2. Resource Priority (8.4.6.1.2.7): A Package of Resource Priority Register Descriptor sub-packages that allow OSPM to set relative priority among processors for shared resources (boost, throttle, L2/L3 cache, memory bandwidth). Parsing the full structure is not yet supported; such entries are marked as unsupported. Add v4 _CPC table parsing (25 entries) and update REG_OPTIONAL to mark the two new registers as optional. Signed-off-by: Sumit Gupta <sumitg@nvidia.com> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> --- drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------ include/acpi/cppc_acpi.h | 8 ++++++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f370be8715ae..c76cfafa3589 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -134,7 +134,7 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); * cpc_regs[] with the corresponding index. 0 means mandatory and 1 * means optional. */ -#define REG_OPTIONAL (0x1FC7D0) +#define REG_OPTIONAL (0x7FC7D0) /* * Use the index of the register in per-cpu cpc_regs[] to check if @@ -751,18 +751,19 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* * Disregard _CPC if the number of entries in the return package is not * as expected, but support future revisions being proper supersets of - * the v3 and only causing more entries to be returned by _CPC. + * the v4 and only causing more entries to be returned by _CPC. */ if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) || (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) || - (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) { + (cpc_rev == CPPC_V4_REV && num_ent != CPPC_V4_NUM_ENT) || + (cpc_rev > CPPC_V4_REV && num_ent <= CPPC_V4_NUM_ENT)) { pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n", num_ent, pr->id); goto out_free; } - if (cpc_rev > CPPC_V3_REV) { - num_ent = CPPC_V3_NUM_ENT; - cpc_rev = CPPC_V3_REV; + if (cpc_rev > CPPC_V4_REV) { + num_ent = CPPC_V4_NUM_ENT; + cpc_rev = CPPC_V4_REV; } cpc_ptr->num_entries = num_ent; @@ -845,6 +846,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); + } else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { + /* + * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a + * Package of Resource Priority Register Descriptor sub-packages. + * Parsing the full structure is not yet supported. + * Mark the register as unsupported for now. + */ + pr_debug("CPU:%d Resource Priority not supported\n", pr->id); + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; + cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0; } else { pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n", i, pr->id); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index d1f02ceec4f9..8693890a7275 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -17,16 +17,18 @@ #include <acpi/pcc.h> #include <acpi/processor.h> -/* CPPCv2 and CPPCv3 support */ +/* CPPCv2, CPPCv3 and CPPCv4 support */ #define CPPC_V2_REV 2 #define CPPC_V3_REV 3 +#define CPPC_V4_REV 4 #define CPPC_V2_NUM_ENT 21 #define CPPC_V3_NUM_ENT 23 +#define CPPC_V4_NUM_ENT 25 #define PCC_CMD_COMPLETE_MASK (1 << 0) #define PCC_ERROR_MASK (1 << 2) -#define MAX_CPC_REG_ENT 21 +#define MAX_CPC_REG_ENT 23 /* CPPC specific PCC commands. */ #define CMD_READ 0 @@ -109,6 +111,8 @@ enum cppc_regs { REFERENCE_PERF, LOWEST_FREQ, NOMINAL_FREQ, + OSPM_NOMINAL_PERF, + RESOURCE_PRIORITY, }; /* -- 2.34.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta @ 2026-05-26 17:38 ` Rafael J. Wysocki 0 siblings, 0 replies; 12+ messages in thread From: Rafael J. Wysocki @ 2026-05-26 17:38 UTC (permalink / raw) To: Sumit Gupta, mario.limonciello Cc: viresh.kumar, pierre.gondois, zhenglifeng1, zhanjie9, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel, treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, Mario Limonciello On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> wrote: > > CPPC v4 (ACPI 6.6, Section 8.4.6) adds two optional entries to the > _CPC package: > > 1. OSPM Nominal Performance (8.4.6.1.2.6): A write-only register that > lets OSPM inform the platform what it considers nominal performance. > The platform classifies performance above this level as boost and > below as throttle for its power/thermal decisions. > > 2. Resource Priority (8.4.6.1.2.7): A Package of Resource Priority > Register Descriptor sub-packages that allow OSPM to set relative > priority among processors for shared resources (boost, throttle, > L2/L3 cache, memory bandwidth). Parsing the full structure is not > yet supported; such entries are marked as unsupported. > > Add v4 _CPC table parsing (25 entries) and update REG_OPTIONAL to > mark the two new registers as optional. > > Signed-off-by: Sumit Gupta <sumitg@nvidia.com> > Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> > Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> I can apply this one if there are no objections. The other patch in the series seems to require more work though. Mario, does your follow-up series depend on both patches in this one? > --- > drivers/acpi/cppc_acpi.c | 23 +++++++++++++++++------ > include/acpi/cppc_acpi.h | 8 ++++++-- > 2 files changed, 23 insertions(+), 8 deletions(-) > > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index f370be8715ae..c76cfafa3589 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -134,7 +134,7 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); > * cpc_regs[] with the corresponding index. 0 means mandatory and 1 > * means optional. > */ > -#define REG_OPTIONAL (0x1FC7D0) > +#define REG_OPTIONAL (0x7FC7D0) > > /* > * Use the index of the register in per-cpu cpc_regs[] to check if > @@ -751,18 +751,19 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) > /* > * Disregard _CPC if the number of entries in the return package is not > * as expected, but support future revisions being proper supersets of > - * the v3 and only causing more entries to be returned by _CPC. > + * the v4 and only causing more entries to be returned by _CPC. > */ > if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) || > (cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) || > - (cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) { > + (cpc_rev == CPPC_V4_REV && num_ent != CPPC_V4_NUM_ENT) || > + (cpc_rev > CPPC_V4_REV && num_ent <= CPPC_V4_NUM_ENT)) { > pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n", > num_ent, pr->id); > goto out_free; > } > - if (cpc_rev > CPPC_V3_REV) { > - num_ent = CPPC_V3_NUM_ENT; > - cpc_rev = CPPC_V3_REV; > + if (cpc_rev > CPPC_V4_REV) { > + num_ent = CPPC_V4_NUM_ENT; > + cpc_rev = CPPC_V4_REV; > } > > cpc_ptr->num_entries = num_ent; > @@ -845,6 +846,16 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) > > cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; > memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); > + } else if (cpc_obj->type == ACPI_TYPE_PACKAGE && (i - 2) == RESOURCE_PRIORITY) { > + /* > + * ACPI 6.6, s8.4.6.1.2.7 defines Resource Priority as a > + * Package of Resource Priority Register Descriptor sub-packages. > + * Parsing the full structure is not yet supported. > + * Mark the register as unsupported for now. > + */ > + pr_debug("CPU:%d Resource Priority not supported\n", pr->id); > + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; > + cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = 0; > } else { > pr_debug("Invalid entry type (%d) in _CPC for CPU:%d\n", > i, pr->id); > diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h > index d1f02ceec4f9..8693890a7275 100644 > --- a/include/acpi/cppc_acpi.h > +++ b/include/acpi/cppc_acpi.h > @@ -17,16 +17,18 @@ > #include <acpi/pcc.h> > #include <acpi/processor.h> > > -/* CPPCv2 and CPPCv3 support */ > +/* CPPCv2, CPPCv3 and CPPCv4 support */ > #define CPPC_V2_REV 2 > #define CPPC_V3_REV 3 > +#define CPPC_V4_REV 4 > #define CPPC_V2_NUM_ENT 21 > #define CPPC_V3_NUM_ENT 23 > +#define CPPC_V4_NUM_ENT 25 > > #define PCC_CMD_COMPLETE_MASK (1 << 0) > #define PCC_ERROR_MASK (1 << 2) > > -#define MAX_CPC_REG_ENT 21 > +#define MAX_CPC_REG_ENT 23 > > /* CPPC specific PCC commands. */ > #define CMD_READ 0 > @@ -109,6 +111,8 @@ enum cppc_regs { > REFERENCE_PERF, > LOWEST_FREQ, > NOMINAL_FREQ, > + OSPM_NOMINAL_PERF, > + RESOURCE_PRIORITY, > }; > > /* > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta 2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta @ 2026-05-14 19:48 ` Sumit Gupta 2026-05-21 16:58 ` Pierre Gondois 2026-05-22 17:20 ` Rafael J. Wysocki 1 sibling, 2 replies; 12+ messages in thread From: Sumit Gupta @ 2026-05-14 19:48 UTC (permalink / raw) To: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg 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@nvidia.com> --- .../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@vger.kernel.org +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 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta @ 2026-05-21 16:58 ` Pierre Gondois 2026-05-26 18:24 ` Sumit Gupta 2026-05-22 17:20 ` Rafael J. Wysocki 1 sibling, 1 reply; 12+ messages in thread From: Pierre Gondois @ 2026-05-21 16:58 UTC (permalink / raw) To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu [-- Attachment #1: Type: text/plain, Size: 17680 bytes --] Hello Sumit, I copy-pasted and attached a complementary patch just to show what I meant by caching the ospm_nominal_perf value at driver init/exit. This should also allow to handle the cases where a user tries to store a new value for a policy with multiple CPUs, one of the CPUs and we need to rollback. I think another issue that should be solved is that: - we don't the value of ospm_nominal_perf when loading the driver - we leave the register in an unknown state if we unload/reload the driver. Note: IMO caching values like in the patch would also benefit to the auto_sel register. ------ If you think the patch helps, feel free to take it. Otherwise please let me know the issues it would raise. Regards, Pierre On 5/14/26 21:48, 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. > > 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@nvidia.com> > --- > .../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@vger.kernel.org > +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; diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index ac1bf1b89ac4d..f3d357917242a 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -356,9 +356,7 @@Description: OSPM Nominal Performance (kHz) 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]. +Values are in kHz and in the range [lowest_freq, nominal_freq]. This file is only present if the cppc-cpufreq driver is in use. diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 97218c4ac162b..01542184e69b2 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1717,6 +1717,19 @@int cppc_set_ospm_nominal_perf(int cpu, u64 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 6379b7ceee347..1a51017a81630 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -28,6 +28,9 @@ static struct cpufreq_driver cppc_cpufreq_driver; +/* Values at driver init. */ +static u64 scratch_ospm_nominal_perf[NR_CPUS]; + #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE static enum { FIE_UNSET = -1, @@ -642,7 +645,7 @@static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) { - unsigned int cpu = policy->cpu; +unsigned int sib, cpu = policy->cpu; struct cppc_cpudata *cpu_data; struct cppc_perf_caps *caps; int ret; @@ -704,6 +707,34 @@static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) if (caps->highest_perf > caps->nominal_perf) policy->boost_supported = true; +/* + * CPUs are expected to have: + * ospm_nominal_perf = nominal_perf + * at policy init. + */ +cpu_data->ospm_nominal_perf = caps->nominal_perf; + +for_each_cpu(sib, policy->cpus) { +u64 ospm_nominal_perf; + +ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf); +if (ret == -EOPNOTSUPP) { +continue; +} else if (ret) { +pr_err("CPU%d: Could not read ospm_nominal_perf value\n", +sib); +goto out; +} + +if (ospm_nominal_perf != caps->nominal_perf) { +pr_err("Incoherent ospm_nominal_perf values: " +"scratch_value=%llu expected_value=%u\n", +scratch_ospm_nominal_perf[sib], +caps->nominal_perf); +goto out; +} +} + /* Set policy->cur to max now. The governors will adjust later. */ policy->cur = cppc_perf_to_khz(caps, caps->highest_perf); cpu_data->perf_ctrls.desired_perf = caps->highest_perf; @@ -727,9 +758,17 @@static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct cppc_cpudata *cpu_data = policy->driver_data; struct cppc_perf_caps *caps = &cpu_data->perf_caps; - unsigned int cpu = policy->cpu; +unsigned int sib, cpu = policy->cpu; int ret; +/* + * CPUs are expected to have: + * ospm_nominal_perf = nominal_perf + * at policy init, so come back to that state. + */ +for_each_cpu(sib, policy->cpus) +cppc_set_ospm_nominal_perf(sib, caps->nominal_perf); + cppc_cpufreq_cpu_fie_exit(policy); cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; @@ -989,9 +1028,20 @@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; +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; - if (!cpu_data->ospm_nominal_perf) - return sysfs_emit(buf, "0\n"); +if (perf != cpu_data->ospm_nominal_perf) { +pr_warn("ospm_nominal_freq value is not coherent: " +"register=%llu scratch_value=%u\n", +perf, cpu_data->ospm_nominal_perf); +} freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps, cpu_data->ospm_nominal_perf); @@ -1002,7 +1052,7 @@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; +unsigned int sib, failing_cpu; u64 freq_khz; u32 perf; int ret; @@ -1015,12 +1065,23 @@static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy, for_each_cpu(sib, policy->cpus) { ret = cppc_set_ospm_nominal_perf(sib, perf); - if (ret) - return ret; +if (ret) { +failing_cpu = sib; +goto error; +} } cpu_data->ospm_nominal_perf = perf; return count; + +error: +for_each_cpu(sib, policy->cpus) { +if (sib == failing_cpu) +break; +cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf); +} + +return ret; } cpufreq_freq_attr_ro(freqdomain_cpus); @@ -1053,6 +1114,64 @@static struct cpufreq_driver cppc_cpufreq_driver = { .name = "cppc_cpufreq", }; +static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void) +{ +int cpu; + +for_each_present_cpu(cpu) { +if (scratch_ospm_nominal_perf[cpu] == U64_MAX) +continue; + +/* Reset ospm_nominal_perf to the value present at init. */ +cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]); +} +} + +static int __init cppc_cpufreq_ospm_nominal_perf_init(void) +{ +int ret, cpu; + +ret = 0; + +for_each_present_cpu(cpu) { +u64 nominal_perf; + +/* + * If one CPU failed, scratch_ospm_nominal_perf still needs + * to be initialized. + */ +if (ret) { +scratch_ospm_nominal_perf[cpu] = U64_MAX; +continue; +} + +ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]); +if (ret) { +if (ret == -EOPNOTSUPP) { +/* Valid reason to fail. Continue to iterate. */ +ret = 0; +} + +scratch_ospm_nominal_perf[cpu] = U64_MAX; +continue; +} + +/* Set ospm_nominal_perf to nominal_perf before policy init. */ +ret = cppc_get_nominal_perf(cpu, &nominal_perf); +if (ret) +continue; + +ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf); +if (ret) +continue; +} + +if (ret) +cppc_cpufreq_ospm_nominal_perf_exit(); + +return ret; +} + static int __init cppc_cpufreq_init(void) { int ret; @@ -1063,9 +1182,17 @@static int __init cppc_cpufreq_init(void) cppc_freq_invariance_init(); populate_efficiency_class(); +ret = cppc_cpufreq_ospm_nominal_perf_init(); +if (ret) { +cppc_freq_invariance_exit(); +return ret; +} + ret = cpufreq_register_driver(&cppc_cpufreq_driver); - if (ret) +if (ret) { +cppc_cpufreq_ospm_nominal_perf_exit(); cppc_freq_invariance_exit(); +} return ret; } @@ -1074,6 +1201,7 @@static void __exit cppc_cpufreq_exit(void) { cpufreq_unregister_driver(&cppc_cpufreq_driver); cppc_freq_invariance_exit(); +cppc_cpufreq_ospm_nominal_perf_exit(); } module_exit(cppc_cpufreq_exit); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 0b1dcdbea10a2..07e6598fa837a 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -183,6 +183,7 @@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); @@ -273,6 +274,10 @@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; [-- Attachment #2: 0001-ACPI-CPPC-Add-ospm_nominal_perf-support-additional-c.patch --] [-- Type: text/x-patch, Size: 9277 bytes --] From 28a1fd8e94f860a3bbf135779fa399621e9355ad Mon Sep 17 00:00:00 2001 From: Pierre Gondois <pierre.gondois@arm.com> Date: Thu, 21 May 2026 14:53:46 +0200 Subject: [PATCH] ACPI: CPPC: Add ospm_nominal_perf support additional changes Complementary modifications to: - "ACPI: CPPC: Add ospm_nominal_perf support" Signed-off-by: Pierre Gondois <pierre.gondois@arm.com> --- .../ABI/testing/sysfs-devices-system-cpu | 4 +- drivers/acpi/cppc_acpi.c | 13 ++ drivers/cpufreq/cppc_cpufreq.c | 144 +++++++++++++++++- include/acpi/cppc_acpi.h | 5 + 4 files changed, 155 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index ac1bf1b89ac4d..f3d357917242a 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -356,9 +356,7 @@ Description: OSPM Nominal Performance (kHz) 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]. + Values are in kHz and in the range [lowest_freq, nominal_freq]. This file is only present if the cppc-cpufreq driver is in use. diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 97218c4ac162b..01542184e69b2 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1717,6 +1717,19 @@ int cppc_set_ospm_nominal_perf(int cpu, u64 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 6379b7ceee347..1a51017a81630 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -28,6 +28,9 @@ static struct cpufreq_driver cppc_cpufreq_driver; +/* Values at driver init. */ +static u64 scratch_ospm_nominal_perf[NR_CPUS]; + #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE static enum { FIE_UNSET = -1, @@ -642,7 +645,7 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) { - unsigned int cpu = policy->cpu; + unsigned int sib, cpu = policy->cpu; struct cppc_cpudata *cpu_data; struct cppc_perf_caps *caps; int ret; @@ -704,6 +707,34 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) if (caps->highest_perf > caps->nominal_perf) policy->boost_supported = true; + /* + * CPUs are expected to have: + * ospm_nominal_perf = nominal_perf + * at policy init. + */ + cpu_data->ospm_nominal_perf = caps->nominal_perf; + + for_each_cpu(sib, policy->cpus) { + u64 ospm_nominal_perf; + + ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf); + if (ret == -EOPNOTSUPP) { + continue; + } else if (ret) { + pr_err("CPU%d: Could not read ospm_nominal_perf value\n", + sib); + goto out; + } + + if (ospm_nominal_perf != caps->nominal_perf) { + pr_err("Incoherent ospm_nominal_perf values: " + "scratch_value=%llu expected_value=%u\n", + scratch_ospm_nominal_perf[sib], + caps->nominal_perf); + goto out; + } + } + /* Set policy->cur to max now. The governors will adjust later. */ policy->cur = cppc_perf_to_khz(caps, caps->highest_perf); cpu_data->perf_ctrls.desired_perf = caps->highest_perf; @@ -727,9 +758,17 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct cppc_cpudata *cpu_data = policy->driver_data; struct cppc_perf_caps *caps = &cpu_data->perf_caps; - unsigned int cpu = policy->cpu; + unsigned int sib, cpu = policy->cpu; int ret; + /* + * CPUs are expected to have: + * ospm_nominal_perf = nominal_perf + * at policy init, so come back to that state. + */ + for_each_cpu(sib, policy->cpus) + cppc_set_ospm_nominal_perf(sib, caps->nominal_perf); + cppc_cpufreq_cpu_fie_exit(policy); cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; @@ -989,9 +1028,20 @@ 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; + 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; - if (!cpu_data->ospm_nominal_perf) - return sysfs_emit(buf, "0\n"); + if (perf != cpu_data->ospm_nominal_perf) { + pr_warn("ospm_nominal_freq value is not coherent: " + "register=%llu scratch_value=%u\n", + perf, cpu_data->ospm_nominal_perf); + } freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps, cpu_data->ospm_nominal_perf); @@ -1002,7 +1052,7 @@ 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; + unsigned int sib, failing_cpu; u64 freq_khz; u32 perf; int ret; @@ -1015,12 +1065,23 @@ static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy, for_each_cpu(sib, policy->cpus) { ret = cppc_set_ospm_nominal_perf(sib, perf); - if (ret) - return ret; + if (ret) { + failing_cpu = sib; + goto error; + } } cpu_data->ospm_nominal_perf = perf; return count; + +error: + for_each_cpu(sib, policy->cpus) { + if (sib == failing_cpu) + break; + cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf); + } + + return ret; } cpufreq_freq_attr_ro(freqdomain_cpus); @@ -1053,6 +1114,64 @@ static struct cpufreq_driver cppc_cpufreq_driver = { .name = "cppc_cpufreq", }; +static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (scratch_ospm_nominal_perf[cpu] == U64_MAX) + continue; + + /* Reset ospm_nominal_perf to the value present at init. */ + cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]); + } +} + +static int __init cppc_cpufreq_ospm_nominal_perf_init(void) +{ + int ret, cpu; + + ret = 0; + + for_each_present_cpu(cpu) { + u64 nominal_perf; + + /* + * If one CPU failed, scratch_ospm_nominal_perf still needs + * to be initialized. + */ + if (ret) { + scratch_ospm_nominal_perf[cpu] = U64_MAX; + continue; + } + + ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]); + if (ret) { + if (ret == -EOPNOTSUPP) { + /* Valid reason to fail. Continue to iterate. */ + ret = 0; + } + + scratch_ospm_nominal_perf[cpu] = U64_MAX; + continue; + } + + /* Set ospm_nominal_perf to nominal_perf before policy init. */ + ret = cppc_get_nominal_perf(cpu, &nominal_perf); + if (ret) + continue; + + ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf); + if (ret) + continue; + } + + if (ret) + cppc_cpufreq_ospm_nominal_perf_exit(); + + return ret; +} + static int __init cppc_cpufreq_init(void) { int ret; @@ -1063,9 +1182,17 @@ static int __init cppc_cpufreq_init(void) cppc_freq_invariance_init(); populate_efficiency_class(); + ret = cppc_cpufreq_ospm_nominal_perf_init(); + if (ret) { + cppc_freq_invariance_exit(); + return ret; + } + ret = cpufreq_register_driver(&cppc_cpufreq_driver); - if (ret) + if (ret) { + cppc_cpufreq_ospm_nominal_perf_exit(); cppc_freq_invariance_exit(); + } return ret; } @@ -1074,6 +1201,7 @@ static void __exit cppc_cpufreq_exit(void) { cpufreq_unregister_driver(&cppc_cpufreq_driver); cppc_freq_invariance_exit(); + cppc_cpufreq_ospm_nominal_perf_exit(); } module_exit(cppc_cpufreq_exit); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 0b1dcdbea10a2..07e6598fa837a 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -183,6 +183,7 @@ 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); @@ -273,6 +274,10 @@ 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; -- 2.43.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-21 16:58 ` Pierre Gondois @ 2026-05-26 18:24 ` Sumit Gupta 2026-05-28 12:07 ` Pierre Gondois 0 siblings, 1 reply; 12+ messages in thread From: Sumit Gupta @ 2026-05-26 18:24 UTC (permalink / raw) To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg On 21/05/26 22:28, Pierre Gondois wrote: > External email: Use caution opening links or attachments > > > Hello Sumit, > > I copy-pasted and attached a complementary patch just to show > what I meant by caching the ospm_nominal_perf value at driver > init/exit. > > This should also allow to handle the cases where a user tries > to store a new value for a policy with multiple CPUs, > one of the CPUs and we need to rollback. > > I think another issue that should be solved is that: > > - we don't the value of ospm_nominal_perf when loading the driver > > - we leave the register in an unknown state if we unload/reload > the driver. > > Note: > IMO caching values like in the patch would also benefit to the > auto_sel register. > > ------ > > If you think the patch helps, feel free to take it. Otherwise > please let me know the issues it would raise. > > Regards, > > Pierre Hi Pierre, Thanks for the review and the complementary patch. Going point by point: 1. Rollback for a partially applied multiple CPU write in store_ospm_nominal_freq(): Agreed, will add into v4. 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence checks that rely on it: I'd skip these as the register is write-only as per spec. 3. Initializing the register at startup and restoring at exit: In v3, we dropped the unconditional cpu_init write so user values would survive CPU hotplug. The spec also makes the explicit init unnecessary: "If this register is not provided, then OSPM must assume that the OSPM Nominal Performance value is equal to the Nominal Performance value.". The unwritten default already looks well defined. 4. pr_warn() in show_ospm_nominal_freq() on HW vs cache mismatch: Skipping it since it relies on (2). So in v4, will pick up the rollback and leave the rest as is. Happy to discuss further if you think differently. Thank you, Sumit Gupta > > > On 5/14/26 21:48, 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. >> >> 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@nvidia.com> >> --- >> .../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@vger.kernel.org >> +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; > > > diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu > b/Documentation/ABI/testing/sysfs-devices-system-cpu > index ac1bf1b89ac4d..f3d357917242a 100644 > --- a/Documentation/ABI/testing/sysfs-devices-system-cpu > +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu > @@ -356,9 +356,7 @@Description: OSPM Nominal Performance (kHz) > 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]. > +Values are in kHz and in the range [lowest_freq, nominal_freq]. > > This file is only present if the cppc-cpufreq driver is > in use. > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index 97218c4ac162b..01542184e69b2 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -1717,6 +1717,19 @@int cppc_set_ospm_nominal_perf(int cpu, u64 > 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 6379b7ceee347..1a51017a81630 100644 > --- a/drivers/cpufreq/cppc_cpufreq.c > +++ b/drivers/cpufreq/cppc_cpufreq.c > @@ -28,6 +28,9 @@ > > static struct cpufreq_driver cppc_cpufreq_driver; > > +/* Values at driver init. */ > +static u64 scratch_ospm_nominal_perf[NR_CPUS]; > + > #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE > static enum { > FIE_UNSET = -1, > @@ -642,7 +645,7 @@static void cppc_cpufreq_put_cpu_data(struct > cpufreq_policy *policy) > > static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) > { > - unsigned int cpu = policy->cpu; > +unsigned int sib, cpu = policy->cpu; > struct cppc_cpudata *cpu_data; > struct cppc_perf_caps *caps; > int ret; > @@ -704,6 +707,34 @@static int cppc_cpufreq_cpu_init(struct > cpufreq_policy *policy) > if (caps->highest_perf > caps->nominal_perf) > policy->boost_supported = true; > > +/* > + * CPUs are expected to have: > + * ospm_nominal_perf = nominal_perf > + * at policy init. > + */ > +cpu_data->ospm_nominal_perf = caps->nominal_perf; > + > +for_each_cpu(sib, policy->cpus) { > +u64 ospm_nominal_perf; > + > +ret = cppc_get_ospm_nominal_perf(sib, &ospm_nominal_perf); > +if (ret == -EOPNOTSUPP) { > +continue; > +} else if (ret) { > +pr_err("CPU%d: Could not read ospm_nominal_perf value\n", > +sib); > +goto out; > +} > + > +if (ospm_nominal_perf != caps->nominal_perf) { > +pr_err("Incoherent ospm_nominal_perf values: " > +"scratch_value=%llu expected_value=%u\n", > +scratch_ospm_nominal_perf[sib], > +caps->nominal_perf); > +goto out; > +} > +} > + > /* Set policy->cur to max now. The governors will adjust later. */ > policy->cur = cppc_perf_to_khz(caps, caps->highest_perf); > cpu_data->perf_ctrls.desired_perf = caps->highest_perf; > @@ -727,9 +758,17 @@static void cppc_cpufreq_cpu_exit(struct > cpufreq_policy *policy) > { > struct cppc_cpudata *cpu_data = policy->driver_data; > struct cppc_perf_caps *caps = &cpu_data->perf_caps; > - unsigned int cpu = policy->cpu; > +unsigned int sib, cpu = policy->cpu; > int ret; > > +/* > + * CPUs are expected to have: > + * ospm_nominal_perf = nominal_perf > + * at policy init, so come back to that state. > + */ > +for_each_cpu(sib, policy->cpus) > +cppc_set_ospm_nominal_perf(sib, caps->nominal_perf); > + > cppc_cpufreq_cpu_fie_exit(policy); > > cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; > @@ -989,9 +1028,20 @@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; > +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; > > - if (!cpu_data->ospm_nominal_perf) > - return sysfs_emit(buf, "0\n"); > +if (perf != cpu_data->ospm_nominal_perf) { > +pr_warn("ospm_nominal_freq value is not coherent: " > +"register=%llu scratch_value=%u\n", > +perf, cpu_data->ospm_nominal_perf); > +} > > freq_khz = cppc_perf_to_khz(&cpu_data->perf_caps, > cpu_data->ospm_nominal_perf); > @@ -1002,7 +1052,7 @@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; > +unsigned int sib, failing_cpu; > u64 freq_khz; > u32 perf; > int ret; > @@ -1015,12 +1065,23 @@static ssize_t store_ospm_nominal_freq(struct > cpufreq_policy *policy, > > for_each_cpu(sib, policy->cpus) { > ret = cppc_set_ospm_nominal_perf(sib, perf); > - if (ret) > - return ret; > +if (ret) { > +failing_cpu = sib; > +goto error; > +} > } > > cpu_data->ospm_nominal_perf = perf; > return count; > + > +error: > +for_each_cpu(sib, policy->cpus) { > +if (sib == failing_cpu) > +break; > +cppc_set_ospm_nominal_perf(sib, cpu_data->ospm_nominal_perf); > +} > + > +return ret; > } > > cpufreq_freq_attr_ro(freqdomain_cpus); > @@ -1053,6 +1114,64 @@static struct cpufreq_driver cppc_cpufreq_driver > = { > .name = "cppc_cpufreq", > }; > > +static void __exit cppc_cpufreq_ospm_nominal_perf_exit(void) > +{ > +int cpu; > + > +for_each_present_cpu(cpu) { > +if (scratch_ospm_nominal_perf[cpu] == U64_MAX) > +continue; > + > +/* Reset ospm_nominal_perf to the value present at init. */ > +cppc_set_ospm_nominal_perf(cpu, scratch_ospm_nominal_perf[cpu]); > +} > +} > + > +static int __init cppc_cpufreq_ospm_nominal_perf_init(void) > +{ > +int ret, cpu; > + > +ret = 0; > + > +for_each_present_cpu(cpu) { > +u64 nominal_perf; > + > +/* > + * If one CPU failed, scratch_ospm_nominal_perf still needs > + * to be initialized. > + */ > +if (ret) { > +scratch_ospm_nominal_perf[cpu] = U64_MAX; > +continue; > +} > + > +ret = cppc_get_ospm_nominal_perf(cpu, &scratch_ospm_nominal_perf[cpu]); > +if (ret) { > +if (ret == -EOPNOTSUPP) { > +/* Valid reason to fail. Continue to iterate. */ > +ret = 0; > +} > + > +scratch_ospm_nominal_perf[cpu] = U64_MAX; > +continue; > +} > + > +/* Set ospm_nominal_perf to nominal_perf before policy init. */ > +ret = cppc_get_nominal_perf(cpu, &nominal_perf); > +if (ret) > +continue; > + > +ret = cppc_set_ospm_nominal_perf(cpu, nominal_perf); > +if (ret) > +continue; > +} > + > +if (ret) > +cppc_cpufreq_ospm_nominal_perf_exit(); > + > +return ret; > +} > + > static int __init cppc_cpufreq_init(void) > { > int ret; > @@ -1063,9 +1182,17 @@static int __init cppc_cpufreq_init(void) > cppc_freq_invariance_init(); > populate_efficiency_class(); > > +ret = cppc_cpufreq_ospm_nominal_perf_init(); > +if (ret) { > +cppc_freq_invariance_exit(); > +return ret; > +} > + > ret = cpufreq_register_driver(&cppc_cpufreq_driver); > - if (ret) > +if (ret) { > +cppc_cpufreq_ospm_nominal_perf_exit(); > cppc_freq_invariance_exit(); > +} > > return ret; > } > @@ -1074,6 +1201,7 @@static void __exit cppc_cpufreq_exit(void) > { > cpufreq_unregister_driver(&cppc_cpufreq_driver); > cppc_freq_invariance_exit(); > +cppc_cpufreq_ospm_nominal_perf_exit(); > } > > module_exit(cppc_cpufreq_exit); > diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h > index 0b1dcdbea10a2..07e6598fa837a 100644 > --- a/include/acpi/cppc_acpi.h > +++ b/include/acpi/cppc_acpi.h > @@ -183,6 +183,7 @@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); > @@ -273,6 +274,10 @@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; > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-26 18:24 ` Sumit Gupta @ 2026-05-28 12:07 ` Pierre Gondois 2026-06-09 8:53 ` Sumit Gupta 0 siblings, 1 reply; 12+ messages in thread From: Pierre Gondois @ 2026-05-28 12:07 UTC (permalink / raw) To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu Hello Sumit, > > Hi Pierre, > > Thanks for the review and the complementary patch. > Going point by point: > > 1. Rollback for a partially applied multiple CPU write in > store_ospm_nominal_freq(): Agreed, will add into v4. > > 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence > checks that rely on it: I'd skip these as the register is write-only > as per spec. > NIT: IIUC having a write-only register doesn't mean we cannot read it. Cf. cppc_get_desired_perf() > 3. Initializing the register at startup and restoring at exit: In v3, we > dropped the unconditional cpu_init write so user values would > survive CPU hotplug. The spec also makes the explicit init > unnecessary: "If this register is not provided, then OSPM must > assume that the OSPM Nominal Performance value is equal to > the Nominal Performance value.". The unwritten default already > looks well defined. The concern I had was for the scenario where: - the driver is loaded - the user sets an ospm_nominal_freq value - the driver is unloaded In such case, the ospm_nominal_freq value will still be set to a non-default value. The modifications suggested previously would allow to handle that case to come back to the default value. FWIU, we have: +------+ +---------+ +-----------+ +------+ | User | <-> | CPPC | <-> | CPPC | <-> | CPPC | +------+ | driver | | reg | | HW | +---------+ | interface | | reg | +-----------+ +------+ So if we want to handle: - the case described above - the case you mentioned, i.e. hot-plugging CPUs maybe the scratch values should be stored along the CPPC register interface. This would allow to handle complex cases where CPUs are hotplugged and the driver is loaded/unloaded ? Note: the same kind of scenario should apply to the auto_sel register > 4. pr_warn() in show_ospm_nominal_freq() on HW vs cache > mismatch: Skipping it since it relies on (2). > > So in v4, will pick up the rollback and leave the rest as is. > Happy to discuss further if you think differently. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-28 12:07 ` Pierre Gondois @ 2026-06-09 8:53 ` Sumit Gupta 2026-06-11 14:31 ` Pierre Gondois 0 siblings, 1 reply; 12+ messages in thread From: Sumit Gupta @ 2026-06-09 8:53 UTC (permalink / raw) To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg On 28/05/26 17:37, Pierre Gondois wrote: > External email: Use caution opening links or attachments > > > Hello Sumit, >> >> Hi Pierre, >> >> Thanks for the review and the complementary patch. >> Going point by point: >> >> 1. Rollback for a partially applied multiple CPU write in >> store_ospm_nominal_freq(): Agreed, will add into v4. >> >> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence >> checks that rely on it: I'd skip these as the register is write-only >> as per spec. >> > NIT: > IIUC having a write-only register doesn't mean we cannot read it. > Cf. cppc_get_desired_perf() Good point. v5 reads the register via a new cppc_get_ospm_nominal_perf(). So, show() returns the register value or "<unsupported>", and dropped the cache/bool. > >> 3. Initializing the register at startup and restoring at exit: In v3, we >> dropped the unconditional cpu_init write so user values would >> survive CPU hotplug. The spec also makes the explicit init >> unnecessary: "If this register is not provided, then OSPM must >> assume that the OSPM Nominal Performance value is equal to >> the Nominal Performance value.". The unwritten default already >> looks well defined. > > The concern I had was for the scenario where: > > - the driver is loaded > > - the user sets an ospm_nominal_freq value > > - the driver is unloaded > > In such case, the ospm_nominal_freq value will still be set to a > non-default value. The modifications suggested previously would > allow to handle that case to come back to the default value. > > FWIU, we have: > > +------+ +---------+ +-----------+ +------+ > | User | <-> | CPPC | <-> | CPPC | <-> | CPPC | > +------+ | driver | | reg | | HW | > +---------+ | interface | | reg | > +-----------+ +------+ > > So if we want to handle: > > - the case described above > > - the case you mentioned, i.e. hot-plugging CPUs > > maybe the scratch values should be stored along the CPPC register > interface. This would allow to handle complex cases where CPUs > are hotplugged and the driver is loaded/unloaded ? > > Note: the same kind of scenario should apply to the auto_sel register > Right. After unload, the register keeps the user set value instead of the firmware value. In a follow-up, I will restore the firmware value on unload and reapply the user value across hotplug, grouping the OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP). On my test platforms the registers survive hotplug, but that isn't guaranteed in general. I think it's better to keep the saved state in the cppc_cpufreq driver rather than the CPPC register interface. intel_pstate and amd-pstate do the same. For reapply, will use a CPU hotplug callback rather than ->online/ ->offline hooks. Those are only called when a policy gains its first online CPU or loses its last one. cppc_cpufreq also has shared (SHARED_TYPE_ANY) policy, offlining and onlining a single CPU keeps the policy active, so neither hook is called for it. A per-CPU hotplug callback is needed to cover that case. Let me know if you have other thoughts. Thank you, Sumit Gupta ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-06-09 8:53 ` Sumit Gupta @ 2026-06-11 14:31 ` Pierre Gondois 2026-06-15 18:23 ` Sumit Gupta 0 siblings, 1 reply; 12+ messages in thread From: Pierre Gondois @ 2026-06-11 14:31 UTC (permalink / raw) To: Sumit Gupta, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, linux-pm, linux-kernel, acpica-devel, linux-acpi On 6/9/26 10:53, Sumit Gupta wrote: > > On 28/05/26 17:37, Pierre Gondois wrote: >> External email: Use caution opening links or attachments >> >> >> Hello Sumit, >>> >>> Hi Pierre, >>> >>> Thanks for the review and the complementary patch. >>> Going point by point: >>> >>> 1. Rollback for a partially applied multiple CPU write in >>> store_ospm_nominal_freq(): Agreed, will add into v4. >>> >>> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence >>> checks that rely on it: I'd skip these as the register is >>> write-only >>> as per spec. >>> >> NIT: >> IIUC having a write-only register doesn't mean we cannot read it. >> Cf. cppc_get_desired_perf() > > > Good point. > v5 reads the register via a new cppc_get_ospm_nominal_perf(). > So, show() returns the register value or "<unsupported>", > and dropped the cache/bool. > > >> >>> 3. Initializing the register at startup and restoring at exit: In >>> v3, we >>> dropped the unconditional cpu_init write so user values would >>> survive CPU hotplug. The spec also makes the explicit init >>> unnecessary: "If this register is not provided, then OSPM must >>> assume that the OSPM Nominal Performance value is equal to >>> the Nominal Performance value.". The unwritten default already >>> looks well defined. >> >> The concern I had was for the scenario where: >> >> - the driver is loaded >> >> - the user sets an ospm_nominal_freq value >> >> - the driver is unloaded >> >> In such case, the ospm_nominal_freq value will still be set to a >> non-default value. The modifications suggested previously would >> allow to handle that case to come back to the default value. >> >> FWIU, we have: >> >> +------+ +---------+ +-----------+ +------+ >> | User | <-> | CPPC | <-> | CPPC | <-> | CPPC | >> +------+ | driver | | reg | | HW | >> +---------+ | interface | | reg | >> +-----------+ +------+ >> >> So if we want to handle: >> >> - the case described above >> >> - the case you mentioned, i.e. hot-plugging CPUs >> >> maybe the scratch values should be stored along the CPPC register >> interface. This would allow to handle complex cases where CPUs >> are hotplugged and the driver is loaded/unloaded ? >> >> Note: the same kind of scenario should apply to the auto_sel register >> > > Right. > After unload, the register keeps the user set value instead of the > firmware value. In a follow-up, I will restore the firmware value on > unload and reapply the user value across hotplug, grouping the > OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP). > On my test platforms the registers survive hotplug, but that isn't > guaranteed in general. > > I think it's better to keep the saved state in the cppc_cpufreq driver > rather than the CPPC register interface. intel_pstate and amd-pstate > do the same. > For reapply, will use a CPU hotplug callback rather than ->online/ > ->offline hooks. Those are only called when a policy gains its first > online CPU or loses its last one. cppc_cpufreq also has shared > (SHARED_TYPE_ANY) policy, offlining and onlining a single CPU > keeps the policy active, so neither hook is called for it. A per-CPU > hotplug callback is needed to cover that case. > > Let me know if you have other thoughts. > Is it necessary to have cpuhp callbacks ? The cppc_cpufreq driver should only support: - SHARED_TYPE_HW/NONE: in such case, the polity should only have one CPU - SHARED_TYPE_ANY: in such case, configuring any CPU should configure all the CPUs of the policy. The only issue is that we currently don't know whether the CPUs in the policy share the same CPPC registers or have individual registers. I think it is currently strongly assumed there is only one registers for all the CPUs. For instance, if: - CPU0/1 are in the same perf. domain - don't have the same CPPC registers - have a SHARED_TYPE_ANY policy (i.e. writing to any of CPU0/1's CPPC registers triggers an update of the hardware for the 2 CPUs) - policy->cpu = 0 then: - enabling auto_sel for the policy means configuring CPU0's CPPC register - if CPU0 is offlined, policy->cpu is updated to 1 (pointing to CPU1), - then reading the policy's auto_sel register means reading CPU1's CPPC register, which was not updated So using the already present online()/offline() callbacks should be enough. It would also be good to check that, for a policy, all the CPPC registers are identical. Note: having per-CPU CPPC registers + SHARED_TYPE_ANY policy is theoretically valid, but unsupported FWIU. Let me know if it makes sense or not, Regards, Pierre > Thank you, > Sumit Gupta > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-06-11 14:31 ` Pierre Gondois @ 2026-06-15 18:23 ` Sumit Gupta 0 siblings, 0 replies; 12+ messages in thread From: Sumit Gupta @ 2026-06-15 18:23 UTC (permalink / raw) To: Pierre Gondois, rafael, viresh.kumar, lenb, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, linux-pm, linux-kernel, acpica-devel, linux-acpi, sumitg Hi Pierre, On 11/06/26 20:01, Pierre Gondois wrote: > External email: Use caution opening links or attachments > > > On 6/9/26 10:53, Sumit Gupta wrote: >> >> On 28/05/26 17:37, Pierre Gondois wrote: >>> External email: Use caution opening links or attachments >>> >>> >>> Hello Sumit, >>>> >>>> Hi Pierre, >>>> >>>> Thanks for the review and the complementary patch. >>>> Going point by point: >>>> >>>> 1. Rollback for a partially applied multiple CPU write in >>>> store_ospm_nominal_freq(): Agreed, will add into v4. >>>> >>>> 2. cppc_get_ospm_nominal_perf() and the show/init/exit coherence >>>> checks that rely on it: I'd skip these as the register is >>>> write-only >>>> as per spec. >>>> >>> NIT: >>> IIUC having a write-only register doesn't mean we cannot read it. >>> Cf. cppc_get_desired_perf() >> >> >> Good point. >> v5 reads the register via a new cppc_get_ospm_nominal_perf(). >> So, show() returns the register value or "<unsupported>", >> and dropped the cache/bool. >> >> >>> >>>> 3. Initializing the register at startup and restoring at exit: In >>>> v3, we >>>> dropped the unconditional cpu_init write so user values would >>>> survive CPU hotplug. The spec also makes the explicit init >>>> unnecessary: "If this register is not provided, then OSPM must >>>> assume that the OSPM Nominal Performance value is equal to >>>> the Nominal Performance value.". The unwritten default already >>>> looks well defined. >>> >>> The concern I had was for the scenario where: >>> >>> - the driver is loaded >>> >>> - the user sets an ospm_nominal_freq value >>> >>> - the driver is unloaded >>> >>> In such case, the ospm_nominal_freq value will still be set to a >>> non-default value. The modifications suggested previously would >>> allow to handle that case to come back to the default value. >>> >>> FWIU, we have: >>> >>> +------+ +---------+ +-----------+ +------+ >>> | User | <-> | CPPC | <-> | CPPC | <-> | CPPC | >>> +------+ | driver | | reg | | HW | >>> +---------+ | interface | | reg | >>> +-----------+ +------+ >>> >>> So if we want to handle: >>> >>> - the case described above >>> >>> - the case you mentioned, i.e. hot-plugging CPUs >>> >>> maybe the scratch values should be stored along the CPPC register >>> interface. This would allow to handle complex cases where CPUs >>> are hotplugged and the driver is loaded/unloaded ? >>> >>> Note: the same kind of scenario should apply to the auto_sel register >>> >> >> Right. >> After unload, the register keeps the user set value instead of the >> firmware value. In a follow-up, I will restore the firmware value on >> unload and reapply the user value across hotplug, grouping the >> OSPM-set registers together (ospm_nominal_perf, auto_sel and EPP). >> On my test platforms the registers survive hotplug, but that isn't >> guaranteed in general. >> >> I think it's better to keep the saved state in the cppc_cpufreq driver >> rather than the CPPC register interface. intel_pstate and amd-pstate >> do the same. >> For reapply, will use a CPU hotplug callback rather than ->online/ >> ->offline hooks. Those are only called when a policy gains its first >> online CPU or loses its last one. cppc_cpufreq also has shared >> (SHARED_TYPE_ANY) policy, offlining and onlining a single CPU >> keeps the policy active, so neither hook is called for it. A per-CPU >> hotplug callback is needed to cover that case. >> >> Let me know if you have other thoughts. >> > Is it necessary to have cpuhp callbacks ? > > The cppc_cpufreq driver should only support: > - SHARED_TYPE_HW/NONE: in such case, the polity should > only have one CPU > - SHARED_TYPE_ANY: in such case, configuring any CPU should > configure all the CPUs of the policy. The only issue is that > we currently don't know whether the CPUs in the policy > share the same CPPC registers or have individual registers. > I think it is currently strongly assumed there is only one > registers for all the CPUs. > > For instance, if: > - CPU0/1 are in the same perf. domain > - don't have the same CPPC registers > - have a SHARED_TYPE_ANY policy (i.e. writing to any > of CPU0/1's CPPC registers triggers an update of the > hardware for the 2 CPUs) > - policy->cpu = 0 > then: > - enabling auto_sel for the policy means configuring > CPU0's CPPC register > - if CPU0 is offlined, policy->cpu is updated to 1 (pointing to CPU1), > - then reading the policy's auto_sel register means reading > CPU1's CPPC register, which was not updated > > So using the already present online()/offline() callbacks should be > enough. It would also be good to check that, for a policy, all the > CPPC registers are identical. > > Note: having per-CPU CPPC registers + SHARED_TYPE_ANY > policy is theoretically valid, but unsupported FWIU. > > Let me know if it makes sense or not, > > Regards, > > Pierre > Agreed, the cpuhp callback isn't needed. For a SHARED_TYPE_ANY policy cppc_cpufreq treats the control registers as equivalent across the policy's CPUs. A single CPU offline/online within the policy therefore doesn't lose the value. Only a full teardown and bring-up can lose value on the platforms that reset the register on hotplug. That path already goes through ->exit/->init, so reapplying the saved value from ->init() covers it without adding callbacks. We can add a check that guards this assumption in cppc_cpufreq_cpu_init, for the CPUFREQ_SHARED_TYPE_ANY case. Compare the control registers each CPU in policy->cpus exposes (excluding per-CPU feedback counters) and warn on a mismatch rather than failing init. Since it concerns existing behavior, the change can be a separate independent patch. It flags the per CPU registers case you described. Does that match what you had in mind? Thank you, Sumit Gupta ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta 2026-05-21 16:58 ` Pierre Gondois @ 2026-05-22 17:20 ` Rafael J. Wysocki 2026-05-26 19:02 ` Sumit Gupta 1 sibling, 1 reply; 12+ messages in thread From: Rafael J. Wysocki @ 2026-05-22 17:20 UTC (permalink / raw) To: Sumit Gupta Cc: rafael, viresh.kumar, lenb, pierre.gondois, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel, treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> 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@nvidia.com> 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@vger.kernel.org > +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 > > ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support 2026-05-22 17:20 ` Rafael J. Wysocki @ 2026-05-26 19:02 ` Sumit Gupta 0 siblings, 0 replies; 12+ messages in thread From: Sumit Gupta @ 2026-05-26 19:02 UTC (permalink / raw) To: Rafael J. Wysocki Cc: viresh.kumar, lenb, pierre.gondois, zhenglifeng1, zhanjie9, mario.limonciello, saket.dumbre, linux-acpi, linux-kernel, linux-pm, acpica-devel, treding, jonathanh, vsethi, ksitaraman, sanjayc, bbasu, sumitg On 22/05/26 22:50, Rafael J. Wysocki wrote: > External email: Use caution opening links or attachments > > > On Thu, May 14, 2026 at 9:49 PM Sumit Gupta <sumitg@nvidia.com> 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@nvidia.com> > 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. Hi Rafael, Sure, going through the Sashiko comments: Patch 1: - Comments #1 and #2 were also raised in the earlier Sashiko review. They are pre-existing issues with rare occurrence. Will address them in a separate hardening patch. Patch 2: > Is it possible for a platform to define lowest_perf as 0? .... Will fix by adding a bool cpu_data->ospm_nominal_perf_set to distinguish an unwritten cpu_data->ospm_nominal_perf cache from a valid 0 perf write. > Does passing the 64-bit freq_khz to cppc_khz_to_perf() cause a silent > truncation? .... In v4, will switch freq_khz to 'unsigned int' and use kstrtouint() instead of kstrtou64(). > If cppc_set_ospm_nominal_perf() succeeds for the first CPU but fails on > a subsequent sibling, does this leave the policy out of sync? In v4, will add rollback in store_ospm_nominal_freq() to restore the previous value on siblings already updated before the failure. Thank you, Sumit Gupta > >> --- >> .../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@vger.kernel.org >> +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 >> >> ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-15 18:23 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-14 19:48 [PATCH v3 0/2] ACPI: CPPC: Add CPPC v4 support (ACPI 6.6) Sumit Gupta 2026-05-14 19:48 ` [PATCH v3 1/2] ACPI: CPPC: Add support for CPPC v4 Sumit Gupta 2026-05-26 17:38 ` Rafael J. Wysocki 2026-05-14 19:48 ` [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support Sumit Gupta 2026-05-21 16:58 ` Pierre Gondois 2026-05-26 18:24 ` Sumit Gupta 2026-05-28 12:07 ` Pierre Gondois 2026-06-09 8:53 ` Sumit Gupta 2026-06-11 14:31 ` Pierre Gondois 2026-06-15 18:23 ` Sumit Gupta 2026-05-22 17:20 ` Rafael J. Wysocki 2026-05-26 19:02 ` Sumit Gupta
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox