Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
From: Sumit Gupta
Date: Mon Dec 01 2025 - 09:10:29 EST
On 11/5/25 12:38, Sumit Gupta wrote:
Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPCDo we need to take the mutex ? Or is it reserved to auto_sel ?
autonomous performance selection at system startup. When autonomous
mode
is enabled, the hardware automatically adjusts CPU performance based on
workload demands using Energy Performance Preference (EPP) hints.
This parameter allows to configure the autonomous mode on all CPUs
without requiring runtime sysfs manipulation if the 'auto_sel' register
is present.
When auto_sel_mode=1:
- All CPUs are configured for autonomous operation during module init
- EPP is set to performance preference (0x0) by default
- Min/max performance bounds use defaults
- CPU frequency scaling is handled by hardware instead of OS governor
For Documentation/:
Reviewed-by: Randy Dunlap<rdunlap@xxxxxxxxxxxxx>
Signed-off-by: Sumit Gupta<sumitg@xxxxxxxxxx>
---
.../admin-guide/kernel-parameters.txt | 12 ++
drivers/cpufreq/cppc_cpufreq.c | 197
+++++++++++++++---
2 files changed, 182 insertions(+), 27 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt
b/Documentation/admin-guide/kernel-parameters.txt
index b8f8f5d74093..048f84008a7e 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -929,6 +929,18 @@
Format:
<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
+ cppc_cpufreq.auto_sel_mode=
+ [CPU_FREQ] Enable ACPI CPPC autonomous
performance selection.
+ When enabled, hardware automatically adjusts
CPU frequency
+ on all CPUs based on workload demands. In
Autonomous mode,
+ Energy Performance Preference(EPP) hints guide
hardware
+ toward performance(0x0) or energy efficiency
(0xff).
+ Requires ACPI CPPC autonomous selection
register support.
+ Format: <bool>
+ Default: 0 (disabled)
+ 0: use cpufreq governors
+ 1: enable if supoorted by hardware
+
cpuidle.off=1 [CPU_IDLE]
disable the cpuidle sub-system
diff --git a/drivers/cpufreq/cppc_cpufreq.c
b/drivers/cpufreq/cppc_cpufreq.c
index d1b44beaddda..0a55ab011317 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -28,8 +28,12 @@
#include <acpi/cppc_acpi.h>
static struct cpufreq_driver cppc_cpufreq_driver;
+
static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
+/* Autonomous Selection */
+static bool auto_sel_mode;
+
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
static enum {
FIE_UNSET = -1,
@@ -272,8 +276,13 @@ static int cppc_cpufreq_set_target(struct
cpufreq_policy *policy,
freqs.old = policy->cur;
freqs.new = target_freq;
+ /*
+ * In autonomous selection mode, hardware handles frequency
scaling directly
+ * based on workload and EPP hints. So, skip the OS frequency
set requests.
+ */
cpufreq_freq_transition_begin(policy, &freqs);
- ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+ if (!cpu_data->perf_caps.auto_sel)
+ ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
cpufreq_freq_transition_end(policy, &freqs, ret != 0);
if (ret)
@@ -565,6 +574,12 @@ static struct cppc_cpudata
*cppc_cpufreq_get_cpu_data(unsigned int cpu)
goto free_mask;
}
+ ret = cppc_get_perf(cpu, &cpu_data->perf_ctrls);
+ if (ret) {
+ pr_debug("Err reading CPU%d perf ctrls:ret:%d\n", cpu,
ret);
+ goto free_mask;
+ }
+
return cpu_data;
free_mask:
@@ -666,11 +681,81 @@ static int
cppc_cpufreq_update_autosel_val(struct cpufreq_policy *policy, bool a
return 0;
}
+static int cppc_cpufreq_update_epp_val(struct cpufreq_policy
*policy, u32 epp)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int cpu = policy->cpu;
+ int ret;
+
+ pr_debug("cpu%d, eppcurr:%u,new:%u\n", cpu,
cpu_data->perf_ctrls.energy_perf, epp);
+
+ guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
Will move this to parent function.
Explained more in reply of the previous patch '7/8'.
+The CPPC enable register is optional.
+ ret = cppc_set_epp(cpu, epp);
+ if (ret) {
+ pr_warn("failed to set energy_perf forcpu:%d (%d)\n",
cpu, ret);
+ return ret;
+ }
+ cpu_data->perf_ctrls.energy_perf = epp;
+
+ return 0;
+}
+
+/**
+ * cppc_cpufreq_update_autosel_config - Update Autonomous selection
configuration
+ * @policy: cpufreq policy for the CPU
+ * @min_perf: minimum performance value to set
+ * @max_perf: maximum performance value to set
+ * @auto_sel: autonomous selection mode enable/disable (also
controls min/max perf reg updates)
+ * @epp_val: energy performance preference value
+ * @update_epp: whether to update EPP register
+ * @update_policy: whether to update policy constraints
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy
*policy,
+ u64 min_perf, u64
max_perf, bool auto_sel,
+ u32 epp_val, bool
update_epp, bool update_policy)
+{
+ const unsigned int cpu = policy->cpu;
+ int ret;
+
+ /*
+ * Set min/max performance registers and update policy
constraints.
+ * When enabling: update both registers and policy.
+ * When disabling: update policy only.
+ * Continue even if min/max are not supported, as EPP and autosel
+ * might still be supported.
+ */
+ ret = cppc_cpufreq_set_min_perf(policy, min_perf, auto_sel,
update_policy);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ ret = cppc_cpufreq_set_max_perf(policy, max_perf, auto_sel,
update_policy);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
+ if (update_epp) {
+ ret = cppc_cpufreq_update_epp_val(policy, epp_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = cppc_cpufreq_update_autosel_val(policy, auto_sel);
+ if (ret)
+ return ret;
+
+ pr_debug("Updated autonomous config [%llu-%llu] for CPU%d\n",
min_perf, max_perf, cpu);
+
+ return 0;
+}
+
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
struct cppc_cpudata *cpu_data;
struct cppc_perf_caps *caps;
+ u64 min_perf, max_perf;
int ret;
cpu_data = cppc_cpufreq_get_cpu_data(cpu);
@@ -734,11 +819,31 @@ 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;
- ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
- if (ret) {
- pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
- caps->highest_perf, cpu, ret);
- goto out;
+ if (cpu_data->perf_caps.auto_sel) {
+ ret = cppc_set_enable(cpu, true);
However this doesn't mean CPPC is not working.
Ya, changed this in v5.
+ if (ret) {Isn't the EPP optional ?
+ pr_err("Failed to enable CPPC on cpu%d
(%d)\n", cpu, ret);
+ goto out;
+ }
+
+ min_perf = cpu_data->perf_ctrls.min_perf ?
+ cpu_data->perf_ctrls.min_perf :
caps->lowest_nonlinear_perf;
+ max_perf = cpu_data->perf_ctrls.max_perf ?
+ cpu_data->perf_ctrls.max_perf :
caps->nominal_perf;
+
+ ret = cppc_cpufreq_update_autosel_config(policy,
min_perf, max_perf, true,
+ CPPC_EPP_PERFORMANCE_PREF, true, false);
+ if (ret) {
+ cppc_set_enable(cpu, false);
+ goto out;
+ }
+ } else {
+ ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+ if (ret) {
+ pr_debug("Err setting perf value:%d on CPU:%d.
ret:%d\n",
+ caps->highest_perf, cpu, ret);
+ goto out;
+ }
}
cppc_cpufreq_cpu_fie_init(policy);
@@ -910,7 +1015,6 @@ static int
cppc_cpufreq_update_auto_select(struct cpufreq_policy *policy, bool e
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
u64 min_perf = caps->lowest_nonlinear_perf;
u64 max_perf = caps->nominal_perf;
- int ret;
if (enable) {
if (cpu_data->perf_ctrls.min_perf)
@@ -919,26 +1023,8 @@ static int
cppc_cpufreq_update_auto_select(struct cpufreq_policy *policy, bool e
max_perf = cpu_data->perf_ctrls.max_perf;
}
- /*
- * Set min/max performance registers and update policy
constraints.
- * When enabling: update both registers and policy.
- * When disabling: update policy only.
- * Continue even if min/max are not supported, as EPP and autosel
- * might still be supported.
- */
- ret = cppc_cpufreq_set_min_perf(policy, min_perf, enable, true);
- if (ret && ret != -EOPNOTSUPP)
- return ret;
-
- ret = cppc_cpufreq_set_max_perf(policy, max_perf, enable, true);
- if (ret && ret != -EOPNOTSUPP)
- return ret;
-
- ret = cppc_cpufreq_update_autosel_val(policy, enable);
- if (ret)
- return ret;
-
- return 0;
+ return cppc_cpufreq_update_autosel_config(policy, min_perf,
max_perf, enable,
+ 0, false, true);
}
static ssize_t store_auto_select(struct cpufreq_policy *policy,
const char *buf, size_t count)
@@ -1146,13 +1232,61 @@ static struct cpufreq_driver
cppc_cpufreq_driver = {
.name = "cppc_cpufreq",
};
+static int cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64
epp)
+{
+ int cpu, ret;
+
+ for_each_present_cpu(cpu) {
+ ret = cppc_set_epp(cpu, epp);
Moving this to cppc_cpufreq_cpu_init in v5. Will add handling for
EOPNOTSUPP.
If autonomous selection is available but not EPP, we will bail out.
I couldn't find in spec that EPP is mandatory when auto_select is
enabled.
I was thinking about the case where the platform:
- supports auto_sel
- doesn't support EPP
Then won't this function return an error code and not set auto_sel even
though
we could have enabled it (without setting the EPP value) ?
Ya, right. For that will handle EOPNOTSUPP after reading EPP.
Thank you,
Sumit Gupta
+ if (ret) {
+ pr_warn("Failed to set EPP on CPU%d (%d)\n",
cpu, ret);
+ goto disable_all;
+ }
+
+ ret = cppc_set_auto_sel(cpu, auto_sel);
Also, it is possible that a platform only supports autonomous selection.
In this case, writing to auto_sel will fail, but auto_sel is still
relevant.
I am not sure if we will have such platform which only supports
Autonomous
mode and has auto_sel as read only. Will add handling for EOPNOTSUPP
if we
have such cases as the cppc_get_reg_val() will returns this error.
Thank you,
Sumit Gupta
....