Re: [PATCH v3 2/2] ACPI: CPPC: Add ospm_nominal_perf support
Pierre Gondois <[email protected]> Thu, 21 May 2026 18:58:28 +0200
| Newsgroups | dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> > --- > .../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: [email protected] > +Description: OSPM Nominal Performance (kHz) > + > + OSPM uses this attribute to request a nominal performance > + level lower than the platform-reported nominal. The > + platform treats performance above this level as boost > + and below as throttle for power and thermal decisions. > + > + Read returns the last written value in kHz, or 0 if no > + value has been written. Write a kHz value in the range > + [lowest_freq, nominal_freq]. > + > + This file is only present if the cppc-cpufreq driver is > + in use. > + > What: /sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1} > Date: August 2008 > KernelVersion: 2.6.27 > diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c > index c76cfafa3589..ad6ece16c30d 100644 > --- a/drivers/acpi/cppc_acpi.c > +++ b/drivers/acpi/cppc_acpi.c > @@ -1682,6 +1682,41 @@ int cppc_set_epp(int cpu, u64 epp_val) > } > EXPORT_SYMBOL_GPL(cppc_set_epp); > > +/** > + * cppc_set_ospm_nominal_perf() - Write OSPM Nominal Performance register. > + * @cpu: CPU on which to write register. > + * @ospm_nominal_perf: Value to write to the OSPM Nominal Performance register. > + * > + * OSPM Nominal Performance conveys the desired nominal performance level > + * at which the platform may run. Per ACPI 6.6, s8.4.6.1.2.6, the value > + * must lie within [Lowest Performance, Nominal Performance] and may be > + * set independently of Minimum, Maximum and Desired performance. > + * > + * Return: 0 on success or negative error code. > + */ > +int cppc_set_ospm_nominal_perf(int cpu, u64 ospm_nominal_perf) > +{ > + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); > + struct cppc_perf_caps caps; > + int ret; > + > + if (!cpc_desc) { > + pr_debug("No CPC descriptor for CPU:%d\n", cpu); > + return -ENODEV; > + } > + > + ret = cppc_get_perf_caps(cpu, &caps); > + if (ret) > + return ret; > + > + if (ospm_nominal_perf < caps.lowest_perf || > + ospm_nominal_perf > caps.nominal_perf) > + return -EINVAL; > + > + return cppc_set_reg_val(cpu, OSPM_NOMINAL_PERF, ospm_nominal_perf); > +} > +EXPORT_SYMBOL_GPL(cppc_set_ospm_nominal_perf); > + > /** > * cppc_get_auto_act_window() - Read autonomous activity window register. > * @cpu: CPU from which to read register. > diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c > index 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;
0001-ACPI-CPPC-Add-ospm_nominal_perf-support-additional-c.patch
(text/x-patch, 9.1 KB)
From 28a1fd8e94f860a3bbf135779fa399621e9355ad Mon Sep 17 00:00:00 2001 From: Pierre Gondois <[email protected]> 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 <[email protected]> --- .../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