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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.