From: Sumit Gupta <sumitg@nvidia.com>
To: <rafael@kernel.org>, <viresh.kumar@linaro.org>,
<pierre.gondois@arm.com>, <christian.loehle@arm.com>,
<ionela.voinescu@arm.com>, <zhenglifeng1@huawei.com>,
<zhanjie9@hisilicon.com>, <lenb@kernel.org>,
<mario.limonciello@amd.com>, <maciej.wieczor-retman@intel.com>,
<pawel.chmielewski@intel.com>, <linux-pm@vger.kernel.org>,
<linux-acpi@vger.kernel.org>, <acpica-devel@lists.linux.dev>,
<linux-kernel@vger.kernel.org>
Cc: <linux-tegra@vger.kernel.org>, <treding@nvidia.com>,
<jonathanh@nvidia.com>, <vsethi@nvidia.com>,
<ksitaraman@nvidia.com>, <sanjayc@nvidia.com>, <mochs@nvidia.com>,
<bbasu@nvidia.com>, <sumitg@nvidia.com>
Subject: [PATCH v8 2/3] cpufreq: CPPC: Add ospm_nominal_freq attribute
Date: Fri, 30 Oct 2026 22:57:44 +0530 [thread overview]
Message-ID: <20261030172745.2045743-3-sumitg@nvidia.com> (raw)
In-Reply-To: <20261030172745.2045743-1-sumitg@nvidia.com>
OSPM Nominal Performance (ACPI 6.6, Section 8.4.6.1.2.6) lets the OS
request a nominal performance level below the platform-reported one. The
platform treats performance above that level as boosted and below it as
throttled. A lower value moves that boundary down, so more levels count
as boost.
Expose it as a per-policy cpufreq attribute in kHz, matching the unit
convention of the other frequency attributes:
/sys/devices/system/cpu/cpuX/cpufreq/ospm_nominal_freq
Writes are converted with cppc_khz_to_perf() and rejected unless they
fall in [Lowest Performance, Nominal Performance]. The register cannot
be read back, so a read returns the last value written, or "<unset>"
before the first write.
Also track the register in the OSPM-set save/restore table, so a
requested value survives CPU hotplug and suspend/resume. The store
handler records the request rather than init() capturing a firmware
value, so the driver writes the register only once the OS has set it.
CPU offline and driver unload write the platform-reported Nominal
Performance to the register.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
.../ABI/testing/sysfs-devices-system-cpu | 27 +++++
drivers/cpufreq/cppc_cpufreq.c | 101 ++++++++++++++++--
2 files changed, 121 insertions(+), 7 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 545687ebadcc..6af7fa86e44c 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -350,6 +350,33 @@ 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: October 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.
+
+ Write a value in kHz, between the frequencies corresponding to
+ Lowest Performance and Nominal Performance.
+
+ The register cannot be read back, so a read returns the last
+ value written, rounded to a performance level. It returns
+ "<unset>" before the first write. Reads and writes fail with
+ -EOPNOTSUPP if the platform does not implement the register.
+
+ Note that tasks may be migrated from one CPU to another by the
+ scheduler's load-balancing algorithm, and if different OSPM
+ Nominal Performance values are set for those CPUs (through
+ different cpufreq policies), that may lead to undesirable
+ outcomes. To avoid such issues it is better to set the same
+ value across all policies, or to pin every task potentially
+ sensitive to it to a specific CPU.
+
+ This file is only present if the cppc-cpufreq driver is in use.
+
What: /sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
Date: August 2008
KernelVersion: 2.6.27
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 76cb803617f5..662397526433 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -34,11 +34,13 @@ static struct cpufreq_driver cppc_cpufreq_driver;
* reapplied from online() across CPU hotplug, and the firmware value is
* restored from offline().
*
- * Autonomous Selection (auto_sel) is kept first, as the registers after it
- * only have meaning while it is enabled.
+ * Autonomous Selection (auto_sel) splits the list: the registers before it are
+ * independent of it, and those after it have meaning only while autonomous
+ * selection is enabled. Place a new register on the matching side.
*/
enum cppc_saved_reg_id {
- CPPC_SAVED_AUTO_SEL,
+ CPPC_SAVED_OSPM_NOMINAL_PERF,
+ CPPC_SAVED_AUTO_SEL, /* Entries below need auto_sel enabled. */
CPPC_SAVED_EPP,
CPPC_SAVED_AUTO_ACT_WINDOW,
CPPC_NR_SAVED_REGS,
@@ -51,6 +53,11 @@ struct cppc_saved_reg {
};
static const struct cppc_saved_reg cppc_saved_regs[CPPC_NR_SAVED_REGS] = {
+ /* The register is write-only, so the driver tracks the requested value. */
+ [CPPC_SAVED_OSPM_NOMINAL_PERF] = {
+ .name = "ospm_nominal_perf",
+ .set = cppc_set_ospm_nominal_perf,
+ },
[CPPC_SAVED_AUTO_SEL] = {
.name = "auto_sel",
.get = cppc_get_auto_sel,
@@ -83,6 +90,9 @@ enum cppc_saved_type {
* CPPC_SAVED_REQUESTED - value the OS asked for, reapplied at online().
* It starts as the firmware value and changes only
* when a write from the OS succeeds.
+ *
+ * A write-only register cannot be read back. init() leaves both values unset,
+ * and its store handler sets them.
*/
struct cppc_policy_state {
u64 regs[CPPC_NR_SAVED_REGS][CPPC_NR_SAVED_TYPES];
@@ -135,11 +145,19 @@ static int cppc_cpufreq_save_firmware_regs(struct cpufreq_policy *policy)
int i, ret;
for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
- ret = cppc_saved_regs[i].get(cpu, &val);
- if (ret == -EOPNOTSUPP)
+ const struct cppc_saved_reg *reg = &cppc_saved_regs[i];
+
+ if (!reg->get) {
+ /* A write-only register has no value to read. */
val = U64_MAX;
- else if (ret)
- return ret;
+ } else {
+ ret = reg->get(cpu, &val);
+ /* Leave a register the platform does not implement unset. */
+ if (ret == -EOPNOTSUPP)
+ val = U64_MAX;
+ else if (ret)
+ return ret;
+ }
st->regs[i][CPPC_SAVED_FIRMWARE] = val;
st->regs[i][CPPC_SAVED_REQUESTED] = val;
@@ -198,6 +216,12 @@ static void cppc_cpufreq_apply_saved_regs(struct cpufreq_policy *policy,
u64 auto_sel, val;
int i;
+ /* Registers before auto_sel do not depend on it. */
+ for (i = 0; i < CPPC_SAVED_AUTO_SEL; i++) {
+ val = st->regs[i][saved_type];
+ cppc_cpufreq_write_saved_reg(cpu, i, val, saved_type);
+ }
+
auto_sel = st->regs[CPPC_SAVED_AUTO_SEL][saved_type];
if (auto_sel)
@@ -1470,11 +1494,73 @@ static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited)
CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered,
cppc_set_perf_limited)
+static ssize_t show_ospm_nominal_freq(struct cpufreq_policy *policy, char *buf)
+{
+ const struct cppc_policy_state *st = cppc_cpufreq_policy_state(policy);
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u64 perf;
+
+ if (!cppc_ospm_nominal_perf_supported(policy->cpu))
+ return -EOPNOTSUPP;
+
+ /* The register cannot be read back, so report the tracked request. */
+ perf = st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_REQUESTED];
+ if (perf == U64_MAX)
+ return sysfs_emit(buf, "<unset>\n");
+
+ return sysfs_emit(buf, "%u\n",
+ cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+static ssize_t store_ospm_nominal_freq(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ struct cppc_policy_state *st;
+ unsigned int freq_khz;
+ u32 perf;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &freq_khz);
+ if (ret)
+ return ret;
+
+ perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+ if (perf < cpu_data->perf_caps.lowest_perf ||
+ perf > cpu_data->perf_caps.nominal_perf)
+ return -EINVAL;
+
+ ret = cppc_set_ospm_nominal_perf(policy->cpu, perf);
+ if (ret)
+ return ret;
+
+ /*
+ * The OS can write this register but cannot read it back.
+ *
+ * The register lets the OS ask for a nominal level below the platform
+ * Nominal Performance. offline() and driver unload write the platform
+ * Nominal in place of the requested value. This removes the lower
+ * level the OS asked for. The value from before the first OS write
+ * cannot be read, so the driver cannot restore it. online() writes
+ * the requested value again.
+ *
+ * Set both values here and not at init(). The driver then writes the
+ * register only after the OS has set it.
+ */
+ st = cppc_cpufreq_policy_state(policy);
+ st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_REQUESTED] = perf;
+ st->regs[CPPC_SAVED_OSPM_NOMINAL_PERF][CPPC_SAVED_FIRMWARE] =
+ cpu_data->perf_caps.nominal_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,
@@ -1482,6 +1568,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
&auto_act_window,
&energy_performance_preference_val,
&perf_limited,
+ &ospm_nominal_freq,
NULL,
};
--
2.34.1
next prev parent reply other threads:[~2026-10-30 17:29 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 17:27 [PATCH v8 0/3] ACPI / cpufreq: CPPC: Add ospm_nominal_perf support Sumit Gupta
2026-10-30 17:27 ` [PATCH v8 1/3] ACPI: " Sumit Gupta
2026-10-30 17:27 ` Sumit Gupta [this message]
2026-10-30 17:27 ` [PATCH v8 3/3] cpufreq: CPPC: Reflect the OSPM nominal in boost and limits Sumit Gupta
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030172745.2045743-3-sumitg@nvidia.com \
--to=sumitg@nvidia.com \
--cc=acpica-devel@lists.linux.dev \
--cc=bbasu@nvidia.com \
--cc=christian.loehle@arm.com \
--cc=ionela.voinescu@arm.com \
--cc=jonathanh@nvidia.com \
--cc=ksitaraman@nvidia.com \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=maciej.wieczor-retman@intel.com \
--cc=mario.limonciello@amd.com \
--cc=mochs@nvidia.com \
--cc=pawel.chmielewski@intel.com \
--cc=pierre.gondois@arm.com \
--cc=rafael@kernel.org \
--cc=sanjayc@nvidia.com \
--cc=treding@nvidia.com \
--cc=viresh.kumar@linaro.org \
--cc=vsethi@nvidia.com \
--cc=zhanjie9@hisilicon.com \
--cc=zhenglifeng1@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox