* [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload
@ 2026-07-24 21:59 Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 1/4] cpufreq: CPPC: Keep the policy across CPU hotplug Sumit Gupta
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Sumit Gupta @ 2026-07-24 21:59 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
ionela.voinescu, zhenglifeng1, zhanjie9, lenb, saket.dumbre,
ray.huang, mario.limonciello, perry.yuan, kprateek.nayak,
linux-kernel, linux-pm, linux-acpi, acpica-devel, linux-tegra
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, mochs, bbasu,
sumitg
This series keeps the CPPC cpufreq policy alive across CPU hotplug and
preserves the OSPM-set CPPC registers (Energy Performance Preference,
Autonomous Activity Window, Autonomous Selection) set via sysfs or the
autonomous boot parameter.
Without online()/offline() callbacks, the core tears a policy down when
its last CPU goes offline and rebuilds it on the way back, re-reading the
CPPC capabilities each time. The values written to these registers can
be lost:
- Across CPU hotplug or suspend/resume: the platform may reset them
while the CPU is offline.
- On driver unload: the driver-written value is left in the register
instead of returning to its pre-driver state.
Handle these with:
- Patch 1: adds online()/offline() callbacks so the core keeps the policy
alive across CPU hotplug instead of tearing it down and rebuilding it.
- Patch 2: makes the autonomous selection register helpers take a u64.
- Patch 3: adds a table-driven mechanism that captures each register's
firmware value, restores it when the policy goes down, and reapplies
the OSPM-set value when the policy comes back online.
- Patch 4: extends the same save/restore to system suspend/resume.
v2[2] -> v3:
- Patch 1: online() now re-establishes the desired/min/max tuple.
- Patch 2: make cppc_get/set_auto_sel() take a u64 arg.
- Patch 3: skip restoring/reapplying a reg whose value couldn't be read.
- Patch 3: split save_req_and_restore_firmware_regs() so patch 4 can
reuse save_requested_regs() for save-only suspend.
- New patch 4: preserve the OSPM-set registers across suspend/resume.
Sumit Gupta (4):
cpufreq: CPPC: Keep the policy across CPU hotplug
ACPI: CPPC: Make autonomous selection helpers take a u64
cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload
cpufreq: CPPC: Preserve OSPM-set registers across suspend/resume
drivers/acpi/cppc_acpi.c | 20 +--
drivers/cpufreq/amd-pstate.c | 2 +-
drivers/cpufreq/cppc_cpufreq.c | 223 ++++++++++++++++++++++++++++++++-
include/acpi/cppc_acpi.h | 8 +-
4 files changed, 230 insertions(+), 23 deletions(-)
[1] v1: https://lore.kernel.org/lkml/20260623095403.3407436-1-sumitg@nvidia.com/
[2] v2: https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 1/4] cpufreq: CPPC: Keep the policy across CPU hotplug
2026-07-24 21:59 [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
@ 2026-07-24 21:59 ` Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 2/4] ACPI: CPPC: Make autonomous selection helpers take a u64 Sumit Gupta
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Sumit Gupta @ 2026-07-24 21:59 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
ionela.voinescu, zhenglifeng1, zhanjie9, lenb, saket.dumbre,
ray.huang, mario.limonciello, perry.yuan, kprateek.nayak,
linux-kernel, linux-pm, linux-acpi, acpica-devel, linux-tegra
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, mochs, bbasu,
sumitg
Without online()/offline() callbacks, the cpufreq core fully tears
down a policy during exit() when its last online CPU is offlined, and
rebuilds it during init() when it comes back.
Add lightweight online()/offline() callbacks so the core instead keeps
the policy live and reuses the driver's cpu_data across CPU hotplug.
This avoids re-reading the CPPC capabilities on every offline/online,
making CPU hotplug faster.
From online(), re-enable CPPC, as the platform may have disabled it
while the CPU was offline. Since the policy is no longer rebuilt via
init(), online() also reprograms the CPPC performance controls
(desired/min/max).
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 44 ++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index f6cea0c54dd9..6dc59f99d880 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -722,6 +722,48 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
}
+/*
+ * With offline() defined, the cpufreq core keeps the policy alive when
+ * a CPU is hotplugged out.
+ */
+static int cppc_cpufreq_cpu_offline(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+/*
+ * Re-enable CPPC when the policy's CPU comes back online, since the platform
+ * may have disabled it while the CPU was offline.
+ */
+static int cppc_cpufreq_cpu_online(struct cpufreq_policy *policy)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int cpu = policy->cpu;
+ int ret;
+
+ ret = cppc_set_enable(cpu, true);
+ if (ret && ret != -EOPNOTSUPP)
+ pr_warn("Failed to re-enable CPPC for CPU%d (%d)\n", cpu, ret);
+
+ /*
+ * The platform may reset the controls while the CPU is offline, so
+ * recompute min/max, clamp desired_perf into range, and reprogram them.
+ */
+ cppc_cpufreq_update_perf_limits(cpu_data, policy);
+
+ cpu_data->perf_ctrls.desired_perf =
+ clamp_t(u32, cpu_data->perf_ctrls.desired_perf,
+ cpu_data->perf_ctrls.min_perf,
+ cpu_data->perf_ctrls.max_perf);
+
+ ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+ if (ret)
+ pr_debug("Failed to reapply perf request on CPU%d (%d)\n",
+ cpu, ret);
+
+ return 0;
+}
+
static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct cppc_cpudata *cpu_data = policy->driver_data;
@@ -1034,6 +1076,8 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
.fast_switch = cppc_cpufreq_fast_switch,
.init = cppc_cpufreq_cpu_init,
.exit = cppc_cpufreq_cpu_exit,
+ .online = cppc_cpufreq_cpu_online,
+ .offline = cppc_cpufreq_cpu_offline,
.set_boost = cppc_cpufreq_set_boost,
.attr = cppc_cpufreq_attr,
.name = "cppc_cpufreq",
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/4] ACPI: CPPC: Make autonomous selection helpers take a u64
2026-07-24 21:59 [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 1/4] cpufreq: CPPC: Keep the policy across CPU hotplug Sumit Gupta
@ 2026-07-24 21:59 ` Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 3/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 4/4] cpufreq: CPPC: Preserve OSPM-set registers across suspend/resume Sumit Gupta
3 siblings, 0 replies; 5+ messages in thread
From: Sumit Gupta @ 2026-07-24 21:59 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
ionela.voinescu, zhenglifeng1, zhanjie9, lenb, saket.dumbre,
ray.huang, mario.limonciello, perry.yuan, kprateek.nayak,
linux-kernel, linux-pm, linux-acpi, acpica-devel, linux-tegra
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, mochs, bbasu,
sumitg
cppc_get_auto_sel()/cppc_set_auto_sel() use a bool, unlike the other
CPPC register get/set helpers which use a u64.
The next patch in this series saves and restores the OSPM-set registers
across CPU hotplug and driver unload through a common table of register
get/set helpers that all take a u64. The bool autonomous selection
helpers cannot be added to that table.
Change cppc_get_auto_sel()/cppc_set_auto_sel() to take a u64 so the
autonomous selection register fits alongside the others, and update
their callers.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 20 ++++----------------
drivers/cpufreq/amd-pstate.c | 2 +-
drivers/cpufreq/cppc_cpufreq.c | 4 ++--
include/acpi/cppc_acpi.h | 8 ++++----
4 files changed, 11 insertions(+), 23 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9f572f481241..ffadd2ca26b1 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1757,23 +1757,11 @@ EXPORT_SYMBOL_GPL(cppc_set_auto_act_window);
/**
* cppc_get_auto_sel() - Read autonomous selection register.
* @cpu: CPU from which to read register.
- * @enable: Return address.
+ * @enable: Return address, set to 0 or 1.
*/
-int cppc_get_auto_sel(int cpu, bool *enable)
+int cppc_get_auto_sel(int cpu, u64 *enable)
{
- u64 auto_sel;
- int ret;
-
- if (enable == NULL)
- return -EINVAL;
-
- ret = cppc_get_reg_val(cpu, AUTO_SEL_ENABLE, &auto_sel);
- if (ret)
- return ret;
-
- *enable = (bool)auto_sel;
-
- return 0;
+ return cppc_get_reg_val(cpu, AUTO_SEL_ENABLE, enable);
}
EXPORT_SYMBOL_GPL(cppc_get_auto_sel);
@@ -1782,7 +1770,7 @@ EXPORT_SYMBOL_GPL(cppc_get_auto_sel);
* @cpu : CPU to which to write register.
* @enable : the desired value of autonomous selection resiter to be updated.
*/
-int cppc_set_auto_sel(int cpu, bool enable)
+int cppc_set_auto_sel(int cpu, u64 enable)
{
return cppc_set_reg_val(cpu, AUTO_SEL_ENABLE, enable);
}
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index f8d8288d644f..0f99bb86c3a3 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -507,7 +507,7 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
struct cppc_perf_caps cppc_perf;
union perf_cached perf = READ_ONCE(cpudata->perf);
u64 numerator;
- bool auto_sel;
+ u64 auto_sel;
int ret = cppc_get_perf_caps(cpudata->cpu, &cppc_perf);
if (ret)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 6dc59f99d880..34cdba00e61a 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -893,7 +893,7 @@ static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf)
static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
{
- bool val;
+ u64 val;
int ret;
ret = cppc_get_auto_sel(policy->cpu, &val);
@@ -905,7 +905,7 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
if (ret)
return ret;
- return sysfs_emit(buf, "%d\n", val);
+ return sysfs_emit(buf, "%llu\n", val);
}
static ssize_t store_auto_select(struct cpufreq_policy *policy,
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 8693890a7275..f4da73876287 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -182,8 +182,8 @@ extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool e
extern int cppc_set_epp(int cpu, u64 epp_val);
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);
-extern int cppc_set_auto_sel(int cpu, bool enable);
+extern int cppc_get_auto_sel(int cpu, u64 *enable);
+extern int cppc_set_auto_sel(int cpu, u64 enable);
extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
@@ -274,11 +274,11 @@ static inline int cppc_set_auto_act_window(int cpu, u64 auto_act_window)
{
return -EOPNOTSUPP;
}
-static inline int cppc_get_auto_sel(int cpu, bool *enable)
+static inline int cppc_get_auto_sel(int cpu, u64 *enable)
{
return -EOPNOTSUPP;
}
-static inline int cppc_set_auto_sel(int cpu, bool enable)
+static inline int cppc_set_auto_sel(int cpu, u64 enable)
{
return -EOPNOTSUPP;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 3/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload
2026-07-24 21:59 [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 1/4] cpufreq: CPPC: Keep the policy across CPU hotplug Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 2/4] ACPI: CPPC: Make autonomous selection helpers take a u64 Sumit Gupta
@ 2026-07-24 21:59 ` Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 4/4] cpufreq: CPPC: Preserve OSPM-set registers across suspend/resume Sumit Gupta
3 siblings, 0 replies; 5+ messages in thread
From: Sumit Gupta @ 2026-07-24 21:59 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
ionela.voinescu, zhenglifeng1, zhanjie9, lenb, saket.dumbre,
ray.huang, mario.limonciello, perry.yuan, kprateek.nayak,
linux-kernel, linux-pm, linux-acpi, acpica-devel, linux-tegra
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, mochs, bbasu,
sumitg
Values written to OSPM-set CPPC registers (via sysfs or the autonomous
boot parameter) can be lost in two ways:
- Across CPU hotplug: the platform may reset a CPU's registers while it
is offline.
- On driver unload: the value the driver wrote is left in the register
instead of returning to its pre-driver state.
Add a small table-driven mechanism that handles both:
- On init(), capture each register's firmware value before the
driver programs anything.
- On offline(), read back each register's current value (whatever was
last set via sysfs or the boot parameter) so it can be reapplied, then
restore the firmware value.
- On online(), reapply the value captured at offline() after the
performance request is re-established.
Keep Autonomous Selection (auto_sel) last in the table so that, on
online(), its saved value is reapplied after the other registers that
shape its behaviour.
Cover the Autonomous Selection (auto_sel), Energy Performance Preference
(EPP) and Autonomous Activity Window (auto_act_window) registers.
Suggested-by: Pierre Gondois <pierre.gondois@arm.com>
Link: https://lore.kernel.org/all/86780f97-29ee-4a72-b311-38c89434b707@arm.com/
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
1 file changed, 157 insertions(+)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 34cdba00e61a..8a13ec49eb9d 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -28,6 +28,150 @@
static struct cpufreq_driver cppc_cpufreq_driver;
+/*
+ * OSPM-set CPPC registers tracked for save/restore. A value set via sysfs or
+ * the autonomous boot parameter is reapplied from online() across CPU
+ * hotplug, and the firmware value is restored from offline().
+ *
+ * Autonomous Selection (auto_sel) is kept last so its saved value is
+ * reapplied after the other registers that shape its behaviour.
+ */
+enum cppc_saved_reg_id {
+ CPPC_SAVED_EPP,
+ CPPC_SAVED_AUTO_ACT_WINDOW,
+ CPPC_SAVED_AUTO_SEL,
+ CPPC_NR_SAVED_REGS,
+};
+
+struct cppc_saved_reg {
+ int (*get)(int cpu, u64 *val);
+ int (*set)(int cpu, u64 val);
+};
+
+static const struct cppc_saved_reg cppc_saved_regs[CPPC_NR_SAVED_REGS] = {
+ [CPPC_SAVED_EPP] = {
+ cppc_get_epp_perf, cppc_set_epp,
+ },
+ [CPPC_SAVED_AUTO_ACT_WINDOW] = {
+ cppc_get_auto_act_window, cppc_set_auto_act_window,
+ },
+ [CPPC_SAVED_AUTO_SEL] = {
+ cppc_get_auto_sel, cppc_set_auto_sel,
+ },
+};
+
+/*
+ * Per-policy saved state for each register in cppc_saved_regs[]:
+ * firmware_val - value before the driver touched it, captured at init()
+ * and restored while the policy is offline. U64_MAX if it
+ * could not be read
+ * requested_val - value in effect when the policy last went offline,
+ * reapplied at online(). U64_MAX if none
+ */
+struct cppc_saved_state {
+ u64 firmware_val;
+ u64 requested_val;
+};
+
+static DEFINE_PER_CPU(struct cppc_saved_state[CPPC_NR_SAVED_REGS], cppc_saved_state);
+
+/*
+ * Return this policy's saved state. Each policy keeps a single copy, stored in
+ * the per-CPU variable of the first CPU it manages. related_cpus (the policy's
+ * full set of CPUs) never changes while it exists, so this CPU (unlike
+ * policy->cpu) stays the same across CPU hotplug, and every callback reaches
+ * the same copy.
+ */
+static struct cppc_saved_state *cppc_cpufreq_policy_saved_state(struct cpufreq_policy *policy)
+{
+ const struct cpumask *policy_cpus = policy->related_cpus;
+
+ /*
+ * related_cpus is empty until the core fills it in after init(). Until
+ * then, fall back to policy->cpus, which has the same first CPU.
+ */
+ if (cpumask_empty(policy_cpus))
+ policy_cpus = policy->cpus;
+
+ return per_cpu(cppc_saved_state, cpumask_first(policy_cpus));
+}
+
+/*
+ * Capture each register's firmware value before the driver programs anything.
+ */
+static void cppc_cpufreq_save_firmware_regs(struct cpufreq_policy *policy)
+{
+ struct cppc_saved_state *st = cppc_cpufreq_policy_saved_state(policy);
+ unsigned int cpu = policy->cpu;
+ u64 val;
+ int i;
+
+ for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
+ if (cppc_saved_regs[i].get(cpu, &val))
+ val = U64_MAX;
+ st[i].firmware_val = val;
+ st[i].requested_val = U64_MAX;
+ }
+}
+
+/*
+ * Save each register's current value so it can later be reapplied.
+ */
+static void cppc_cpufreq_save_requested_regs(struct cpufreq_policy *policy)
+{
+ struct cppc_saved_state *st = cppc_cpufreq_policy_saved_state(policy);
+ unsigned int cpu = policy->cpu;
+ u64 val;
+ int i;
+
+ for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
+ if (cppc_saved_regs[i].get(cpu, &val))
+ val = U64_MAX;
+ st[i].requested_val = val;
+ }
+}
+
+/*
+ * Restore each register's firmware value, leaving the platform in its
+ * pre-driver state.
+ */
+static void cppc_cpufreq_restore_firmware_regs(struct cpufreq_policy *policy)
+{
+ struct cppc_saved_state *st = cppc_cpufreq_policy_saved_state(policy);
+ unsigned int cpu = policy->cpu;
+ int i, ret;
+
+ for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
+ if (st[i].firmware_val == U64_MAX)
+ continue;
+
+ ret = cppc_saved_regs[i].set(cpu, st[i].firmware_val);
+ if (ret && ret != -EOPNOTSUPP)
+ pr_debug("Failed to restore firmware reg %d on CPU%u (%d)\n",
+ i, cpu, ret);
+ }
+}
+
+/*
+ * Reapply each register's requested value that offline() saved.
+ */
+static void cppc_cpufreq_reapply_requested_regs(struct cpufreq_policy *policy)
+{
+ struct cppc_saved_state *st = cppc_cpufreq_policy_saved_state(policy);
+ unsigned int cpu = policy->cpu;
+ int i, ret;
+
+ for (i = 0; i < CPPC_NR_SAVED_REGS; i++) {
+ if (st[i].requested_val == U64_MAX)
+ continue;
+
+ ret = cppc_saved_regs[i].set(cpu, st[i].requested_val);
+ if (ret && ret != -EOPNOTSUPP)
+ pr_debug("Failed to reapply saved reg %d on CPU%u (%d)\n",
+ i, cpu, ret);
+ }
+}
+
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
static enum {
FIE_UNSET = -1,
@@ -707,6 +851,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
cpu_data->perf_ctrls.desired_perf = caps->highest_perf;
+ cppc_cpufreq_save_firmware_regs(policy);
+
ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
if (ret) {
pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
@@ -725,15 +871,24 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
/*
* With offline() defined, the cpufreq core keeps the policy alive when
* a CPU is hotplugged out.
+ *
+ * Save each register's current value so online() can reapply it, then
+ * restore the firmware value, leaving the platform in its pre-driver state.
*/
static int cppc_cpufreq_cpu_offline(struct cpufreq_policy *policy)
{
+ cppc_cpufreq_save_requested_regs(policy);
+ cppc_cpufreq_restore_firmware_regs(policy);
+
return 0;
}
/*
* Re-enable CPPC when the policy's CPU comes back online, since the platform
* may have disabled it while the CPU was offline.
+ *
+ * offline() reset the registers to their firmware values, so reapply the
+ * OSPM-set values it saved.
*/
static int cppc_cpufreq_cpu_online(struct cpufreq_policy *policy)
{
@@ -761,6 +916,8 @@ static int cppc_cpufreq_cpu_online(struct cpufreq_policy *policy)
pr_debug("Failed to reapply perf request on CPU%d (%d)\n",
cpu, ret);
+ cppc_cpufreq_reapply_requested_regs(policy);
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 4/4] cpufreq: CPPC: Preserve OSPM-set registers across suspend/resume
2026-07-24 21:59 [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
` (2 preceding siblings ...)
2026-07-24 21:59 ` [PATCH v3 3/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
@ 2026-07-24 21:59 ` Sumit Gupta
3 siblings, 0 replies; 5+ messages in thread
From: Sumit Gupta @ 2026-07-24 21:59 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, christian.loehle,
ionela.voinescu, zhenglifeng1, zhanjie9, lenb, saket.dumbre,
ray.huang, mario.limonciello, perry.yuan, kprateek.nayak,
linux-kernel, linux-pm, linux-acpi, acpica-devel, linux-tegra
Cc: treding, jonathanh, vsethi, ksitaraman, sanjayc, mochs, bbasu,
sumitg
The previous patch preserves the OSPM-set registers across CPU hotplug,
but system suspend/resume is a separate path. On platforms that reset
those registers or the performance controls across suspend, the values
are lost.
Reuse the same save/restore mechanism for suspend/resume:
- suspend() only saves the current OSPM-set values. It does not revert
them to firmware, since a later offline() already does that when
system suspend offlines the secondary CPUs.
- resume() needs the same steps as online(), so .resume points at
online() directly.
Suggested-by: Christian Loehle <christian.loehle@arm.com>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 8a13ec49eb9d..be2d873ccd84 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -921,6 +921,22 @@ static int cppc_cpufreq_cpu_online(struct cpufreq_policy *policy)
return 0;
}
+/*
+ * suspend() only saves the OSPM-set values. It does not revert them to the
+ * firmware values.
+ *
+ * This is because system suspend may later offline the secondary CPUs.
+ * That runs offline(), which saves the values and then reverts to firmware.
+ * If suspend() had already reverted, offline() would save the firmware value
+ * instead of the user's request, and the request would be lost.
+ */
+static int cppc_cpufreq_cpu_suspend(struct cpufreq_policy *policy)
+{
+ cppc_cpufreq_save_requested_regs(policy);
+
+ return 0;
+}
+
static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct cppc_cpudata *cpu_data = policy->driver_data;
@@ -1235,6 +1251,8 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
.exit = cppc_cpufreq_cpu_exit,
.online = cppc_cpufreq_cpu_online,
.offline = cppc_cpufreq_cpu_offline,
+ .suspend = cppc_cpufreq_cpu_suspend,
+ .resume = cppc_cpufreq_cpu_online,
.set_boost = cppc_cpufreq_set_boost,
.attr = cppc_cpufreq_attr,
.name = "cppc_cpufreq",
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-24 22:00 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 21:59 [PATCH v3 0/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 1/4] cpufreq: CPPC: Keep the policy across CPU hotplug Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 2/4] ACPI: CPPC: Make autonomous selection helpers take a u64 Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 3/4] cpufreq: CPPC: Preserve OSPM-set registers across hotplug and unload Sumit Gupta
2026-07-24 21:59 ` [PATCH v3 4/4] cpufreq: CPPC: Preserve OSPM-set registers across suspend/resume Sumit Gupta
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.