* [PATCH v4 0/8] Enhanced autonomous selection and improvements
@ 2025-11-05 11:38 Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
` (9 more replies)
0 siblings, 10 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
This patch series enhances the ACPI CPPC CPUFREQ driver with
comprehensive support for autonomous performance selection, expanded
runtime control interfaces and improvements.
It adds support for below:
- Expose sysfs to read/write the Minimum/Maximum Performance Registers
using frequency (kHz), with internal conversion to performance values.
Also, update the policy min/max accordingly.
/sys/.../cpufreq/policy*/min_perf and max_perf
- Expose sysfs to read/write the Performance Limited Register.
/sys/.../cpufreq/policy*/perf_limited
- When toggling autonomous selection, synchronize the policy limits
by updating the policy min/max.
- System-wide autonomous mode configuration via 'auto_sel_mode' boot
parameter. Mode can be switched dynamically on individual CPUs.
- Generic sysfs helper functions to reduce code duplication.
The patches are grouped as below:
- Patch 1, 2 & 3: Improvements. Can be applied independently.
- Patch 4: Sysfs to update min/max_perf. Can be applied independently.
- Patch 5: Sysfs to update perf_limited. Can be applied independently.
- Patch 6: add sysfs documentation. Depends on 'Patch 4 and 5'.
- Patch 7: sync policy min/max with auto_select. Depends on 'Patch 4'.
- Patch 8: Boot Parameter Support. Depends on 'Patch 4 and 7'.
---
v3[3] -> v4:
- patch1: changed param order for show/store and moved them above use.
- patch3: write epp and auto_sel registers independently.
- patch4:
- move mutex define outside macro.
- return EOPNOTSUPP error also to the caller to handle appropriately.
- change min/max_perf sysfs to accept freq, convert to perf internally
- patch6:
- update dates and corrected names in description.
- remove dependency of min/max_perf on auto_sel.
- add info about highest_perf and lowest_perf interfaces.
- update info about min/max_perf interface to read/write freq in kHz.
v2[2] -> v3:
- drop changes to rename exisiting APIs.
- add new 'patch 1' with generic helpers for sysfs show/store.
- used guard(mutex)() in place of mutex_lock/unlock.
- update kernel-parameters text to 'enable if supported by hardware'.
- disable auto_sel for all cpus if one fails in auto_sel_mode.
- use min/max_perf values if set from bootloader.
- general code improvements, error handling and reduced redundancies.
v1[1] -> v2:
- Move CPC register set sysfs from acpi_cppc to cpufreq directory.
- No sysfs to set auto_sel and epp. They were merged from diff series.
- Remove 'cppc_cpufreq_epp' instance of the 'cppc_cpufreq' driver.
- Synchronize perf_min/max with policy min/max.
- Update policy min/max Toggling auto_select.
- add sysfs to update the perf_limited register.
Sumit Gupta (8):
cpufreq: CPPC: Add generic helpers for sysfs show/store
ACPI: CPPC: Add cppc_get_perf() API to read performance controls
ACPI: CPPC: extend APIs to support auto_sel and epp
ACPI: CPPC: add APIs and sysfs interface for min/max_perf
ACPI: CPPC: add APIs and sysfs interface for perf_limited register
cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
cpufreq: CPPC: update policy min/max when toggling auto_select
cpufreq: CPPC: add autonomous mode boot parameter support
.../ABI/testing/sysfs-devices-system-cpu | 46 ++
.../admin-guide/kernel-parameters.txt | 12 +
drivers/acpi/cppc_acpi.c | 196 +++++++-
drivers/cpufreq/cppc_cpufreq.c | 425 ++++++++++++++++--
include/acpi/cppc_acpi.h | 39 +-
5 files changed, 676 insertions(+), 42 deletions(-)
[1] https://lore.kernel.org/lkml/20250211103737.447704-1-sumitg@nvidia.com/
[2] https://lore.kernel.org/lkml/20250823200121.1320197-1-sumitg@nvidia.com/
[3] https://lore.kernel.org/lkml/20251001150104.1275188-1-sumitg@nvidia.com/
--
2.34.1
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-10 10:56 ` Viresh Kumar
2025-11-05 11:38 ` [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
` (8 subsequent siblings)
9 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add generic show/store helper functions for u64 sysfs attributes:
- cppc_cpufreq_sysfs_show_u64()
- cppc_cpufreq_sysfs_store_u64()
Refactor auto_act_window and energy_performance_preference_val
attributes to use these helpers, eliminating code duplication.
No functional changes.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 57 ++++++++++++----------------------
1 file changed, 19 insertions(+), 38 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index e23d9abea135..cf3ed6489a4f 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -832,72 +832,53 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
return count;
}
-static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
+static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu, int (*get_func)(int, u64 *), char *buf)
{
u64 val;
- int ret;
-
- ret = cppc_get_auto_act_window(policy->cpu, &val);
+ int ret = get_func(cpu, &val);
- /* show "<unsupported>" when this register is not supported by cpc */
if (ret == -EOPNOTSUPP)
return sysfs_emit(buf, "<unsupported>\n");
-
if (ret)
return ret;
return sysfs_emit(buf, "%llu\n", val);
}
-static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
- const char *buf, size_t count)
+static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, int (*set_func)(int, u64),
+ const char *buf, size_t count)
{
- u64 usec;
+ u64 val;
int ret;
- ret = kstrtou64(buf, 0, &usec);
+ ret = kstrtou64(buf, 0, &val);
if (ret)
return ret;
- ret = cppc_set_auto_act_window(policy->cpu, usec);
- if (ret)
- return ret;
+ ret = set_func((int)cpu, val);
- return count;
+ return ret ? ret : count;
}
-static ssize_t show_energy_performance_preference_val(struct cpufreq_policy *policy, char *buf)
+static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
{
- u64 val;
- int ret;
-
- ret = cppc_get_epp_perf(policy->cpu, &val);
-
- /* show "<unsupported>" when this register is not supported by cpc */
- if (ret == -EOPNOTSUPP)
- return sysfs_emit(buf, "<unsupported>\n");
+ return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_auto_act_window, buf);
+}
- if (ret)
- return ret;
+static ssize_t store_auto_act_window(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_auto_act_window, buf, count);
+}
- return sysfs_emit(buf, "%llu\n", val);
+static ssize_t show_energy_performance_preference_val(struct cpufreq_policy *policy, char *buf)
+{
+ return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_epp_perf, buf);
}
static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
- u64 val;
- int ret;
-
- ret = kstrtou64(buf, 0, &val);
- if (ret)
- return ret;
-
- ret = cppc_set_epp(policy->cpu, val);
- if (ret)
- return ret;
-
- return count;
+ return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp, buf, count);
}
cpufreq_freq_attr_ro(freqdomain_cpus);
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
2025-11-05 11:38 ` [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
` (7 subsequent siblings)
9 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add cppc_get_perf() function to read values of performance control
registers including desired_perf, min_perf, max_perf, and energy_perf.
This provides a read interface to complement the existing cppc_set_perf()
write interface for performance control registers.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 73 ++++++++++++++++++++++++++++++++++++++++
include/acpi/cppc_acpi.h | 5 +++
2 files changed, 78 insertions(+)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index ab4651205e8a..05672c30187c 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1731,6 +1731,79 @@ int cppc_set_enable(int cpu, bool enable)
return cppc_set_reg_val(cpu, ENABLE, enable);
}
EXPORT_SYMBOL_GPL(cppc_set_enable);
+/**
+ * cppc_get_perf - Get a CPU's performance controls.
+ * @cpu: CPU for which to get performance controls.
+ * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
+ *
+ * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
+ */
+int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+{
+ struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+ struct cpc_register_resource *desired_perf_reg, *min_perf_reg, *max_perf_reg,
+ *energy_perf_reg;
+ u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0;
+ int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+ struct cppc_pcc_data *pcc_ss_data = NULL;
+ int ret = 0, regs_in_pcc = 0;
+
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
+
+ if (!perf_ctrls) {
+ pr_debug("Invalid perf_ctrls pointer\n");
+ return -EINVAL;
+ }
+
+ desired_perf_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+ min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
+ max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
+ energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
+
+ /* Are any of the regs PCC ?*/
+ if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
+ CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) {
+ if (pcc_ss_id < 0) {
+ pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
+ pcc_ss_data = pcc_data[pcc_ss_id];
+ regs_in_pcc = 1;
+ down_write(&pcc_ss_data->pcc_lock);
+ /* Ring doorbell once to update PCC subspace */
+ if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
+ pr_debug("Failed to send PCC command for CPU:%d, ret:%d\n", cpu, ret);
+ ret = -EIO;
+ goto out_err;
+ }
+ }
+
+ /* Read optional elements if present */
+ if (CPC_SUPPORTED(max_perf_reg))
+ cpc_read(cpu, max_perf_reg, &max);
+ perf_ctrls->max_perf = max;
+
+ if (CPC_SUPPORTED(min_perf_reg))
+ cpc_read(cpu, min_perf_reg, &min);
+ perf_ctrls->min_perf = min;
+
+ if (CPC_SUPPORTED(desired_perf_reg))
+ cpc_read(cpu, desired_perf_reg, &desired_perf);
+ perf_ctrls->desired_perf = desired_perf;
+
+ if (CPC_SUPPORTED(energy_perf_reg))
+ cpc_read(cpu, energy_perf_reg, &energy_perf);
+ perf_ctrls->energy_perf = energy_perf;
+
+out_err:
+ if (regs_in_pcc)
+ up_write(&pcc_ss_data->pcc_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf);
/**
* cppc_set_perf - Set a CPU's performance controls.
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 13fa81504844..7190afeead8b 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -151,6 +151,7 @@ extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
extern int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf);
extern int cppc_get_highest_perf(int cpunum, u64 *highest_perf);
extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
+extern int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
extern int cppc_set_enable(int cpu, bool enable);
extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
@@ -192,6 +193,10 @@ static inline int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_
{
return -EOPNOTSUPP;
}
+static inline int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+{
+ return -EOPNOTSUPP;
+}
static inline int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
{
return -EOPNOTSUPP;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-12 15:02 ` Ionela Voinescu
2025-11-27 14:54 ` Pierre Gondois
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
` (6 subsequent siblings)
9 siblings, 2 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
- Add auto_sel read support in cppc_get_perf_caps().
- Add write of both auto_sel and energy_perf in cppc_set_epp_perf().
- Remove redundant energy_perf field from 'struct cppc_perf_caps' as
the same is available in 'struct cppc_perf_ctrls' which is used.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++--------
include/acpi/cppc_acpi.h | 1 -
2 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 05672c30187c..757e8ce87e9b 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
struct cpc_register_resource *highest_reg, *lowest_reg,
*lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
- *low_freq_reg = NULL, *nom_freq_reg = NULL;
- u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
+ *low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL;
+ u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
struct cppc_pcc_data *pcc_ss_data = NULL;
int ret = 0, regs_in_pcc = 0;
@@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
+ auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
/* Are any of the regs PCC ?*/
if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
- CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
+ CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || CPC_IN_PCC(auto_sel_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
@@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
perf_caps->lowest_freq = low_f;
perf_caps->nominal_freq = nom_f;
+ if (CPC_SUPPORTED(auto_sel_reg))
+ cpc_read(cpunum, auto_sel_reg, &auto_sel);
+ perf_caps->auto_sel = (bool)auto_sel;
out_err:
if (regs_in_pcc)
@@ -1555,6 +1559,8 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
struct cpc_register_resource *auto_sel_reg;
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
struct cppc_pcc_data *pcc_ss_data = NULL;
+ bool autosel_support_in_ffh_or_sysmem;
+ bool epp_support_in_ffh_or_sysmem;
int ret;
if (!cpc_desc) {
@@ -1565,6 +1571,11 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
+ epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
+ (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
+ autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
+ (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
+
if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
@@ -1589,14 +1600,29 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
/* after writing CPC, transfer the ownership of PCC to platform */
ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
up_write(&pcc_ss_data->pcc_lock);
- } else if (osc_cpc_flexible_adr_space_confirmed &&
- CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
- ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+ } else if (osc_cpc_flexible_adr_space_confirmed) {
+ if (!epp_support_in_ffh_or_sysmem && !autosel_support_in_ffh_or_sysmem) {
+ ret = -EOPNOTSUPP;
+ } else {
+ if (autosel_support_in_ffh_or_sysmem) {
+ ret = cpc_write(cpu, auto_sel_reg, enable);
+ if (ret)
+ return ret;
+ }
+
+ if (epp_support_in_ffh_or_sysmem) {
+ ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+ if (ret)
+ return ret;
+ }
+ }
} else {
- ret = -ENOTSUPP;
- pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
+ ret = -EOPNOTSUPP;
}
+ if (ret == -EOPNOTSUPP)
+ pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 7190afeead8b..42e37a84cac9 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -119,7 +119,6 @@ struct cppc_perf_caps {
u32 lowest_nonlinear_perf;
u32 lowest_freq;
u32 nominal_freq;
- u32 energy_perf;
bool auto_sel;
};
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (2 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-06 10:30 ` kernel test robot
` (2 more replies)
2025-11-05 11:38 ` [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
` (5 subsequent siblings)
9 siblings, 3 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
CPPC allows platforms to specify minimum and maximum performance
limits that constrain the operating range for CPU performance scaling
when Autonomous Selection is enabled. These limits can be dynamically
adjusted to implement power management policies or workload-specific
optimizations.
Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
write the MIN_PERF register, allowing dynamic adjustment of the minimum
performance floor.
Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
write the MAX_PERF register, enabling dynamic ceiling control for
maximum performance.
Expose these capabilities through cpufreq sysfs attributes that accept
frequency values in kHz (which are converted to/from performance values
internally):
- /sys/.../cpufreq/policy*/min_perf: Read/write min perf as freq (kHz)
- /sys/.../cpufreq/policy*/max_perf: Read/write max perf as freq (kHz)
The frequency-based interface provides a user-friendly abstraction which
is similar to other cpufreq sysfs interfaces, while the driver handles
conversion to hardware performance values.
Also update EPP constants for better clarity:
- Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
- Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 55 ++++++++++-
drivers/cpufreq/cppc_cpufreq.c | 166 +++++++++++++++++++++++++++++++++
include/acpi/cppc_acpi.h | 23 ++++-
3 files changed, 242 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 757e8ce87e9b..ef53eb8a1feb 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1634,7 +1634,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
*/
int cppc_set_epp(int cpu, u64 epp_val)
{
- if (epp_val > CPPC_ENERGY_PERF_MAX)
+ if (epp_val > CPPC_EPP_ENERGY_EFFICIENCY_PREF)
return -EINVAL;
return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val);
@@ -1757,6 +1757,59 @@ int cppc_set_enable(int cpu, bool enable)
return cppc_set_reg_val(cpu, ENABLE, enable);
}
EXPORT_SYMBOL_GPL(cppc_set_enable);
+
+/**
+ * cppc_get_min_perf - Get the min performance register value.
+ * @cpu: CPU from which to get min performance.
+ * @min_perf: Return address.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_min_perf(int cpu, u64 *min_perf)
+{
+ return cppc_get_reg_val(cpu, MIN_PERF, min_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_min_perf);
+
+/**
+ * cppc_set_min_perf() - Write the min performance register.
+ * @cpu: CPU on which to write register.
+ * @min_perf: Value to write to the MIN_PERF register.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_set_min_perf(int cpu, u64 min_perf)
+{
+ return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_min_perf);
+
+/**
+ * cppc_get_max_perf - Get the max performance register value.
+ * @cpu: CPU from which to get max performance.
+ * @max_perf: Return address.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_max_perf(int cpu, u64 *max_perf)
+{
+ return cppc_get_reg_val(cpu, MAX_PERF, max_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_max_perf);
+
+/**
+ * cppc_set_max_perf() - Write the max performance register.
+ * @cpu: CPU on which to write register.
+ * @max_perf: Value to write to the MAX_PERF register.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_set_max_perf(int cpu, u64 max_perf)
+{
+ return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_max_perf);
+
/**
* cppc_get_perf - Get a CPU's performance controls.
* @cpu: CPU for which to get performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index cf3ed6489a4f..cde6202e9c51 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -23,10 +23,12 @@
#include <uapi/linux/sched/types.h>
#include <linux/unaligned.h>
+#include <linux/cleanup.h>
#include <acpi/cppc_acpi.h>
static struct cpufreq_driver cppc_cpufreq_driver;
+static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
static enum {
@@ -582,6 +584,68 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
policy->driver_data = NULL;
}
+/**
+ * cppc_cpufreq_set_mperf_limit - Generic function to set min/max performance limit
+ * @policy: cpufreq policy
+ * @val: performance value to set
+ * @update_reg: whether to update hardware register
+ * @update_policy: whether to update policy constraints
+ * @is_min: true for min_perf, false for max_perf
+ */
+static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
+ bool update_reg, bool update_policy, bool is_min)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+ unsigned int cpu = policy->cpu;
+ struct freq_qos_request *req;
+ unsigned int freq;
+ u32 perf;
+ int ret;
+
+ perf = clamp(val, caps->lowest_perf, caps->highest_perf);
+ freq = cppc_perf_to_khz(caps, perf);
+
+ pr_debug("cpu%d, %s_perf:%llu, update_reg:%d, update_policy:%d\n", cpu,
+ is_min ? "min" : "max", (u64)perf, update_reg, update_policy);
+
+ guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
+
+ if (update_reg) {
+ ret = is_min ? cppc_set_min_perf(cpu, perf) : cppc_set_max_perf(cpu, perf);
+ if (ret) {
+ if (ret != -EOPNOTSUPP)
+ pr_warn("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
+ is_min ? "min" : "max", (u64)perf, cpu, ret);
+ return ret;
+ }
+
+ if (is_min)
+ cpu_data->perf_ctrls.min_perf = perf;
+ else
+ cpu_data->perf_ctrls.max_perf = perf;
+ }
+
+ if (update_policy) {
+ req = is_min ? policy->min_freq_req : policy->max_freq_req;
+
+ ret = freq_qos_update_request(req, freq);
+ if (ret < 0) {
+ pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n",
+ is_min ? "min" : "max", cpu, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy) \
+ cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, true)
+
+#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \
+ cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false)
+
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
@@ -881,16 +945,118 @@ static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *po
return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp, buf, count);
}
+/**
+ * show_min_perf - Show minimum performance as frequency (kHz)
+ *
+ * Reads the MIN_PERF register and converts the performance value to
+ * frequency (kHz) for user-space consumption.
+ */
+static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u64 perf;
+ int ret;
+
+ ret = cppc_get_min_perf(policy->cpu, &perf);
+ if (ret == -EOPNOTSUPP)
+ return sysfs_emit(buf, "<unsupported>\n");
+ if (ret)
+ return ret;
+
+ /* Convert performance to frequency (kHz) for user */
+ return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+/**
+ * store_min_perf - Set minimum performance from frequency (kHz)
+ *
+ * Converts the user-provided frequency (kHz) to a performance value
+ * and writes it to the MIN_PERF register.
+ */
+static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int freq_khz;
+ u64 perf;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &freq_khz);
+ if (ret)
+ return ret;
+
+ /* Convert frequency (kHz) to performance value */
+ perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+
+ ret = cppc_cpufreq_set_min_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/**
+ * show_max_perf - Show maximum performance as frequency (kHz)
+ *
+ * Reads the MAX_PERF register and converts the performance value to
+ * frequency (kHz) for user-space consumption.
+ */
+static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u64 perf;
+ int ret;
+
+ ret = cppc_get_max_perf(policy->cpu, &perf);
+ if (ret == -EOPNOTSUPP)
+ return sysfs_emit(buf, "<unsupported>\n");
+ if (ret)
+ return ret;
+
+ /* Convert performance to frequency (kHz) for user */
+ return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+/**
+ * store_max_perf - Set maximum performance from frequency (kHz)
+ *
+ * Converts the user-provided frequency (kHz) to a performance value
+ * and writes it to the MAX_PERF register.
+ */
+static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int freq_khz;
+ u64 perf;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &freq_khz);
+ if (ret)
+ return ret;
+
+ /* Convert frequency (kHz) to performance value */
+ perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+
+ ret = cppc_cpufreq_set_max_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
+ if (ret)
+ return ret;
+
+ 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(min_perf);
+cpufreq_freq_attr_rw(max_perf);
static struct freq_attr *cppc_cpufreq_attr[] = {
&freqdomain_cpus,
&auto_select,
&auto_act_window,
&energy_performance_preference_val,
+ &min_perf,
+ &max_perf,
NULL,
};
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 42e37a84cac9..be7de1222eee 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -39,7 +39,8 @@
/* CPPC_AUTO_ACT_WINDOW_MAX_SIG is 127, so 128 and 129 will decay to 127 when writing */
#define CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH 129
-#define CPPC_ENERGY_PERF_MAX (0xFF)
+#define CPPC_EPP_PERFORMANCE_PREF 0x00
+#define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF
/* Each register has the folowing format. */
struct cpc_reg {
@@ -172,6 +173,10 @@ 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_min_perf(int cpu, u64 *min_perf);
+extern int cppc_set_min_perf(int cpu, u64 min_perf);
+extern int cppc_get_max_perf(int cpu, u64 *max_perf);
+extern int cppc_set_max_perf(int cpu, u64 max_perf);
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
extern int amd_detect_prefcore(bool *detected);
@@ -264,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
{
return -EOPNOTSUPP;
}
+static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
+{
+ return -EOPNOTSUPP;
+}
+static inline int cppc_set_min_perf(int cpu, u64 min_perf)
+{
+ return -EOPNOTSUPP;
+}
+static inline int cppc_get_max_perf(int cpu, u64 *max_perf)
+{
+ return -EOPNOTSUPP;
+}
+static inline int cppc_set_max_perf(int cpu, u64 max_perf)
+{
+ return -EOPNOTSUPP;
+}
static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
{
return -ENODEV;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (3 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-13 11:35 ` Ionela Voinescu
2025-11-27 14:54 ` Pierre Gondois
2025-11-05 11:38 ` [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
` (4 subsequent siblings)
9 siblings, 2 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add sysfs interface to read/write the Performance Limited register.
The Performance Limited register indicates to the OS that an
unpredictable event (like thermal throttling) has limited processor
performance. This register is sticky and remains set until reset or
OS clears it by writing 0.
The interface is exposed as:
/sys/devices/system/cpu/cpuX/cpufreq/perf_limited
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 26 ++++++++++++++++++++++++++
drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
include/acpi/cppc_acpi.h | 10 ++++++++++
3 files changed, 48 insertions(+)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index ef53eb8a1feb..9b8da3ef06db 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
}
EXPORT_SYMBOL_GPL(cppc_set_max_perf);
+/**
+ * cppc_get_perf_limited - Get the Performance Limited register value.
+ * @cpu: CPU from which to get Performance Limited register.
+ * @perf_limited: Pointer to store the Performance Limited value.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+ return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
+
+/**
+ * cppc_set_perf_limited() - Write the Performance Limited register.
+ * @cpu: CPU on which to write register.
+ * @perf_limited: Value to write to the perf_limited register.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+ return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
+
/**
* cppc_get_perf - Get a CPU's performance controls.
* @cpu: CPU for which to get performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index cde6202e9c51..a425ad575aa6 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
return count;
}
+static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
+{
+ return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
+}
+
+static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+ return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, 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(min_perf);
cpufreq_freq_attr_rw(max_perf);
+cpufreq_freq_attr_rw(perf_limited);
static struct freq_attr *cppc_cpufreq_attr[] = {
&freqdomain_cpus,
@@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
&energy_performance_preference_val,
&min_perf,
&max_perf,
+ &perf_limited,
NULL,
};
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index be7de1222eee..8baff46f2ac7 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
extern int cppc_set_min_perf(int cpu, u64 min_perf);
extern int cppc_get_max_perf(int cpu, u64 *max_perf);
extern int cppc_set_max_perf(int cpu, u64 max_perf);
+extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
+extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
extern int amd_detect_prefcore(bool *detected);
@@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
{
return -EOPNOTSUPP;
}
+static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+ return -EOPNOTSUPP;
+}
+static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+ return -EOPNOTSUPP;
+}
static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
{
return -ENODEV;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (4 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-13 12:41 ` Ionela Voinescu
2025-11-05 11:38 ` [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
` (3 subsequent siblings)
9 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add sysfs interfaces for Minimum Performance, Maximum Performance
and Performance Limited Register in the cppc_cpufreq driver.
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
.../ABI/testing/sysfs-devices-system-cpu | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 8aed6d94c4cd..6f1f70696000 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -327,6 +327,52 @@ Description: Energy performance preference
This file is only present if the cppc-cpufreq driver is in use.
+What: /sys/devices/system/cpu/cpuX/cpufreq/min_perf
+Date: December 2025
+Contact: linux-pm@vger.kernel.org
+Description: Minimum Performance Frequency
+
+ Read/write a frequency value in kHz from/to this file. This
+ file conveys the minimum performance level (as frequency) at
+ which the platform may run. The frequency value is internally
+ converted to a performance value and must correspond to a
+ performance level in the range [Lowest Performance, Highest
+ Performance], inclusive. The minimum must be less than or equal
+ to the maximum performance. The performance range can be checked
+ from nodes:
+ /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf
+ /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf
+
+ This file is only present if the cppc-cpufreq driver is in use.
+
+What: /sys/devices/system/cpu/cpuX/cpufreq/max_perf
+Date: December 2025
+Contact: linux-pm@vger.kernel.org
+Description: Maximum Performance Frequency
+
+ Read/write a frequency value in kHz from/to this file. This
+ file conveys the maximum performance level (as frequency) at
+ which the platform may run. The frequency value is internally
+ converted to a performance value and must correspond to a
+ performance level in the range [Lowest Performance, Highest
+ Performance], inclusive. The performance range can be checked
+ from nodes:
+ /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf
+ /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf
+
+ This file is only present if the cppc-cpufreq driver is in use.
+
+What: /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
+Date: December 2025
+Contact: linux-pm@vger.kernel.org
+Description: Performance Limited
+
+ Read/write a 32 bits value from/to this file. This file indicates
+ to OSPM that an unpredictable event has limited processor
+ performance, and the delivered performance may be less than
+ desired/minimum performance.
+
+ 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
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (5 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
2025-11-05 11:38 ` [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
` (2 subsequent siblings)
9 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
When CPPC autonomous selection (auto_select) is enabled or disabled,
the policy min/max frequency limits should be updated appropriately to
reflect the new operating mode.
Currently, toggling auto_select only changes the hardware register but
doesn't update the cpufreq policy constraints, which can lead to
inconsistent behavior between the hardware state and the policy limits
visible to userspace and other kernel components.
When auto_select is enabled, preserve the current min/max performance
values to maintain user-configured limits. When disabled, the hardware
operates in a default mode where the OS directly controls performance,
so update the policy limits accordingly.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 67 ++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 3 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index a425ad575aa6..d1b44beaddda 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -646,6 +646,26 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \
cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false)
+static int cppc_cpufreq_update_autosel_val(struct cpufreq_policy *policy, bool auto_sel)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ unsigned int cpu = policy->cpu;
+ int ret;
+
+ pr_debug("cpu%d, auto_sel curr:%u, new:%d\n", cpu, cpu_data->perf_caps.auto_sel, auto_sel);
+
+ guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
+
+ ret = cppc_set_auto_sel(cpu, auto_sel);
+ if (ret) {
+ pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n", auto_sel, cpu, ret);
+ return ret;
+ }
+ cpu_data->perf_caps.auto_sel = auto_sel;
+
+ return 0;
+}
+
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int cpu = policy->cpu;
@@ -879,8 +899,49 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
return sysfs_emit(buf, "%d\n", val);
}
-static ssize_t store_auto_select(struct cpufreq_policy *policy,
- const char *buf, size_t count)
+/**
+ * cppc_cpufreq_update_auto_select - Update autonomous selection config for policy->cpu
+ * @policy: cpufreq policy
+ * @enable: enable/disable autonomous selection
+ */
+static int cppc_cpufreq_update_auto_select(struct cpufreq_policy *policy, bool enable)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ 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)
+ min_perf = cpu_data->perf_ctrls.min_perf;
+ if (cpu_data->perf_ctrls.max_perf)
+ 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;
+}
+
+static ssize_t store_auto_select(struct cpufreq_policy *policy, const char *buf, size_t count)
{
bool val;
int ret;
@@ -889,7 +950,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
if (ret)
return ret;
- ret = cppc_set_auto_sel(policy->cpu, val);
+ ret = cppc_cpufreq_update_auto_select(policy, val);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (6 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
@ 2025-11-05 11:38 ` Sumit Gupta
2025-11-13 15:15 ` Ionela Voinescu
2025-11-27 14:53 ` Pierre Gondois
2025-11-10 11:00 ` [PATCH v4 0/8] Enhanced autonomous selection and improvements Viresh Kumar
2025-11-18 8:45 ` Jie Zhan
9 siblings, 2 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-05 11:38 UTC (permalink / raw)
To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
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@infradead.org>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
.../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, epp curr:%u, new:%u\n", cpu, cpu_data->perf_ctrls.energy_perf, epp);
+
+ guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
+
+ ret = cppc_set_epp(cpu, epp);
+ if (ret) {
+ pr_warn("failed to set energy_perf for cpu:%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);
+ if (ret) {
+ 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);
+ 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);
+ if (ret) {
+ pr_warn("Failed to set auto_sel on CPU%d (%d)\n", cpu, ret);
+ goto disable_all;
+ }
+ }
+
+ return 0;
+
+disable_all:
+ pr_warn("Disabling auto_sel for all CPUs\n");
+ for_each_present_cpu(cpu)
+ cppc_set_auto_sel(cpu, false);
+
+ return -EIO;
+}
+
static int __init cppc_cpufreq_init(void)
{
+ bool auto_sel;
int ret;
if (!acpi_cpc_valid())
return -ENODEV;
+ if (auto_sel_mode) {
+ /*
+ * Check if autonomous selection is supported by testing CPU 0.
+ * If supported, enable autonomous mode on all CPUs.
+ */
+ ret = cppc_get_auto_sel(0, &auto_sel);
+ if (!ret) {
+ pr_info("Enabling auto_sel_mode (autonomous selection mode)\n");
+ ret = cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
+ if (ret) {
+ pr_warn("Disabling auto_sel_mode, fallback to standard\n");
+ auto_sel_mode = false;
+ }
+ } else {
+ pr_warn("Disabling auto_sel_mode as not supported by hardware\n");
+ auto_sel_mode = false;
+ }
+ }
+
cppc_freq_invariance_init();
populate_efficiency_class();
@@ -1165,10 +1299,19 @@ static int __init cppc_cpufreq_init(void)
static void __exit cppc_cpufreq_exit(void)
{
+ int cpu;
+
+ for_each_present_cpu(cpu)
+ cppc_set_auto_sel(cpu, false);
+ auto_sel_mode = false;
+
cpufreq_unregister_driver(&cppc_cpufreq_driver);
cppc_freq_invariance_exit();
}
+module_param(auto_sel_mode, bool, 0000);
+MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
+
module_exit(cppc_cpufreq_exit);
MODULE_AUTHOR("Ashwin Chaugule");
MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
--
2.34.1
^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2025-11-06 10:30 ` kernel test robot
2025-11-07 10:00 ` Sumit Gupta
2025-11-13 10:56 ` Ionela Voinescu
2025-11-27 14:54 ` Pierre Gondois
2 siblings, 1 reply; 42+ messages in thread
From: kernel test robot @ 2025-11-06 10:30 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
pierre.gondois, zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: llvm, oe-kbuild-all, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, sumitg
Hi Sumit,
kernel test robot noticed the following build warnings:
[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/bleeding-edge linus/master v6.18-rc4 next-20251106]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sumit-Gupta/cpufreq-CPPC-Add-generic-helpers-for-sysfs-show-store/20251105-194715
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link: https://lore.kernel.org/r/20251105113844.4086250-5-sumitg%40nvidia.com
patch subject: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
config: riscv-defconfig (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project d2625a438020ad35330cda29c3def102c1687b1b)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511061802.lIq09jwh-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'policy' not described in 'show_min_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'buf' not described in 'show_min_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'policy' not described in 'store_min_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'buf' not described in 'store_min_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'count' not described in 'store_min_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'policy' not described in 'show_max_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'buf' not described in 'show_max_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'policy' not described in 'store_max_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'buf' not described in 'store_max_perf'
>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'count' not described in 'store_max_perf'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-06 10:30 ` kernel test robot
@ 2025-11-07 10:00 ` Sumit Gupta
2025-11-07 20:08 ` Rafael J. Wysocki
0 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-07 10:00 UTC (permalink / raw)
To: kernel test robot, rafael, viresh.kumar, lenb, robert.moore,
corbet, pierre.gondois, zhenglifeng1, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, ionela.voinescu,
zhanjie9, linux-pm, linux-acpi, linux-doc, acpica-devel,
linux-kernel, sumitg
Cc: llvm, oe-kbuild-all, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu
On 06/11/25 16:00, kernel test robot wrote:
> External email: Use caution opening links or attachments
>
>
> Hi Sumit,
>
> kernel test robot noticed the following build warnings:
>
> [auto build test WARNING on rafael-pm/linux-next]
> [also build test WARNING on rafael-pm/bleeding-edge linus/master v6.18-rc4 next-20251106]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Sumit-Gupta/cpufreq-CPPC-Add-generic-helpers-for-sysfs-show-store/20251105-194715
> base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
> patch link: https://lore.kernel.org/r/20251105113844.4086250-5-sumitg%40nvidia.com
> patch subject: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
> config: riscv-defconfig (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/config)
> compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project d2625a438020ad35330cda29c3def102c1687b1b)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202511061802.lIq09jwh-lkp@intel.com/
>
> All warnings (new ones prefixed by >>):
>
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'policy' not described in 'show_min_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'buf' not described in 'show_min_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'policy' not described in 'store_min_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'buf' not described in 'store_min_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'count' not described in 'store_min_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'policy' not described in 'show_max_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'buf' not described in 'show_max_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'policy' not described in 'store_max_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'buf' not described in 'store_max_perf'
>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'count' not described in 'store_max_perf'
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
Thank you for the report.
Below change to comments seem to be fixing this warning.
-------------------------------------------------------
/**
* show_min_perf - Show minimum performance as frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer to write the frequency value to
*
* Reads the MIN_PERF register and converts the performance value to
* frequency (kHz) for user-space consumption.
@@ -1117,6 +1119,9 @@ static ssize_t show_min_perf(struct cpufreq_policy
*policy, char *buf)
/**
* store_min_perf - Set minimum performance from frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer to write the frequency value to
+ * @count: size of @buf
*
* Converts the user-provided frequency (kHz) to a performance value
* and writes it to the MIN_PERF register.
@@ -1144,6 +1149,8 @@ static ssize_t store_min_perf(struct
cpufreq_policy *policy, const char *buf, si
/**
* show_max_perf - Show maximum performance as frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer to write the frequency value to
*
* Reads the MAX_PERF register and converts the performance value to
* frequency (kHz) for user-space consumption.
@@ -1166,6 +1173,9 @@ static ssize_t show_max_perf(struct cpufreq_policy
*policy, char *buf)
/**
* store_max_perf - Set maximum performance from frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer to write the frequency value to
+ * @count: size of @buf
-------------------------------------------------------
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-07 10:00 ` Sumit Gupta
@ 2025-11-07 20:08 ` Rafael J. Wysocki
2025-11-11 11:06 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Rafael J. Wysocki @ 2025-11-07 20:08 UTC (permalink / raw)
To: Sumit Gupta
Cc: kernel test robot, rafael, viresh.kumar, lenb, robert.moore,
corbet, pierre.gondois, zhenglifeng1, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, ionela.voinescu,
zhanjie9, linux-pm, linux-acpi, linux-doc, acpica-devel,
linux-kernel, llvm, oe-kbuild-all, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
On Fri, Nov 7, 2025 at 11:00 AM Sumit Gupta <sumitg@nvidia.com> wrote:
>
>
> On 06/11/25 16:00, kernel test robot wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > Hi Sumit,
> >
> > kernel test robot noticed the following build warnings:
> >
> > [auto build test WARNING on rafael-pm/linux-next]
> > [also build test WARNING on rafael-pm/bleeding-edge linus/master v6.18-rc4 next-20251106]
> > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > And when submitting patch, we suggest to use '--base' as documented in
> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> >
> > url: https://github.com/intel-lab-lkp/linux/commits/Sumit-Gupta/cpufreq-CPPC-Add-generic-helpers-for-sysfs-show-store/20251105-194715
> > base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
> > patch link: https://lore.kernel.org/r/20251105113844.4086250-5-sumitg%40nvidia.com
> > patch subject: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
> > config: riscv-defconfig (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/config)
> > compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project d2625a438020ad35330cda29c3def102c1687b1b)
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251106/202511061802.lIq09jwh-lkp@intel.com/reproduce)
> >
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Closes: https://lore.kernel.org/oe-kbuild-all/202511061802.lIq09jwh-lkp@intel.com/
> >
> > All warnings (new ones prefixed by >>):
> >
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'policy' not described in 'show_min_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'buf' not described in 'show_min_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'policy' not described in 'store_min_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'buf' not described in 'store_min_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'count' not described in 'store_min_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'policy' not described in 'show_max_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'buf' not described in 'show_max_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'policy' not described in 'store_max_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'buf' not described in 'store_max_perf'
> >>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'count' not described in 'store_max_perf'
> > --
> > 0-DAY CI Kernel Test Service
> > https://github.com/intel/lkp-tests/wiki
>
>
> Thank you for the report.
> Below change to comments seem to be fixing this warning.
So can you please send a new version of this patch with the changes
below folded in?
> -------------------------------------------------------
> /**
> * show_min_perf - Show minimum performance as frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer to write the frequency value to
> *
> * Reads the MIN_PERF register and converts the performance value to
> * frequency (kHz) for user-space consumption.
> @@ -1117,6 +1119,9 @@ static ssize_t show_min_perf(struct cpufreq_policy
> *policy, char *buf)
>
> /**
> * store_min_perf - Set minimum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer to write the frequency value to
> + * @count: size of @buf
> *
> * Converts the user-provided frequency (kHz) to a performance value
> * and writes it to the MIN_PERF register.
> @@ -1144,6 +1149,8 @@ static ssize_t store_min_perf(struct
> cpufreq_policy *policy, const char *buf, si
>
> /**
> * show_max_perf - Show maximum performance as frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer to write the frequency value to
> *
> * Reads the MAX_PERF register and converts the performance value to
> * frequency (kHz) for user-space consumption.
> @@ -1166,6 +1173,9 @@ static ssize_t show_max_perf(struct cpufreq_policy
> *policy, char *buf)
>
> /**
> * store_max_perf - Set maximum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer to write the frequency value to
> + * @count: size of @buf
> -------------------------------------------------------
As for the whole series, I generally need ARM folks to review it and
tell me that it is fine.
Thanks!
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store
2025-11-05 11:38 ` [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2025-11-10 10:56 ` Viresh Kumar
2025-11-11 11:20 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Viresh Kumar @ 2025-11-10 10:56 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, lenb, robert.moore, corbet, pierre.gondois, zhenglifeng1,
rdunlap, ray.huang, gautham.shenoy, mario.limonciello, perry.yuan,
ionela.voinescu, zhanjie9, linux-pm, linux-acpi, linux-doc,
acpica-devel, linux-kernel, linux-tegra, treding, jonathanh,
vsethi, ksitaraman, sanjayc, nhartman, bbasu
On 05-11-25, 17:08, Sumit Gupta wrote:
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> -static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
> +static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu, int (*get_func)(int, u64 *), char *buf)
> {
> u64 val;
> - int ret;
> -
> - ret = cppc_get_auto_act_window(policy->cpu, &val);
> + int ret = get_func(cpu, &val);
Why no casting required here, when you need it ...
> -static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
> - const char *buf, size_t count)
> +static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, int (*set_func)(int, u64),
> + const char *buf, size_t count)
> {
> - u64 usec;
> + u64 val;
> int ret;
>
> - ret = kstrtou64(buf, 0, &usec);
> + ret = kstrtou64(buf, 0, &val);
> if (ret)
> return ret;
>
> - ret = cppc_set_auto_act_window(policy->cpu, usec);
> - if (ret)
> - return ret;
> + ret = set_func((int)cpu, val);
... here ?
--
viresh
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 0/8] Enhanced autonomous selection and improvements
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (7 preceding siblings ...)
2025-11-05 11:38 ` [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
@ 2025-11-10 11:00 ` Viresh Kumar
2025-11-18 8:45 ` Jie Zhan
9 siblings, 0 replies; 42+ messages in thread
From: Viresh Kumar @ 2025-11-10 11:00 UTC (permalink / raw)
To: Sumit Gupta, pierre.gondois, zhanjie9, ionela.voinescu,
beata.michalska
Cc: rafael, lenb, robert.moore, corbet, zhenglifeng1, rdunlap,
ray.huang, gautham.shenoy, mario.limonciello, perry.yuan,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel,
linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 05-11-25, 17:08, Sumit Gupta wrote:
> This patch series enhances the ACPI CPPC CPUFREQ driver with
> comprehensive support for autonomous performance selection, expanded
> runtime control interfaces and improvements.
>
> It adds support for below:
> - Expose sysfs to read/write the Minimum/Maximum Performance Registers
> using frequency (kHz), with internal conversion to performance values.
> Also, update the policy min/max accordingly.
> /sys/.../cpufreq/policy*/min_perf and max_perf
>
> - Expose sysfs to read/write the Performance Limited Register.
> /sys/.../cpufreq/policy*/perf_limited
>
> - When toggling autonomous selection, synchronize the policy limits
> by updating the policy min/max.
>
> - System-wide autonomous mode configuration via 'auto_sel_mode' boot
> parameter. Mode can be switched dynamically on individual CPUs.
>
> - Generic sysfs helper functions to reduce code duplication.
>
> The patches are grouped as below:
> - Patch 1, 2 & 3: Improvements. Can be applied independently.
> - Patch 4: Sysfs to update min/max_perf. Can be applied independently.
> - Patch 5: Sysfs to update perf_limited. Can be applied independently.
> - Patch 6: add sysfs documentation. Depends on 'Patch 4 and 5'.
> - Patch 7: sync policy min/max with auto_select. Depends on 'Patch 4'.
> - Patch 8: Boot Parameter Support. Depends on 'Patch 4 and 7'.
Beata/Ionela/Jie, any feedback on the CPPC changes ?
--
viresh
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-07 20:08 ` Rafael J. Wysocki
@ 2025-11-11 11:06 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-11 11:06 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: kernel test robot, viresh.kumar, lenb, robert.moore, corbet,
pierre.gondois, zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel, llvm,
oe-kbuild-all, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 08/11/25 01:38, Rafael J. Wysocki wrote:
....
>
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'policy' not described in 'show_min_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:954 function parameter 'buf' not described in 'show_min_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'policy' not described in 'store_min_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'buf' not described in 'store_min_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:976 function parameter 'count' not described in 'store_min_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'policy' not described in 'show_max_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1003 function parameter 'buf' not described in 'show_max_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'policy' not described in 'store_max_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'buf' not described in 'store_max_perf'
>>>>> Warning: drivers/cpufreq/cppc_cpufreq.c:1025 function parameter 'count' not described in 'store_max_perf'
>>> --
>>> 0-DAY CI Kernel Test Service
>>> https://github.com/intel/lkp-tests/wiki
>>
>> Thank you for the report.
>> Below change to comments seem to be fixing this warning.
> So can you please send a new version of this patch with the changes
> below folded in?
Sure.
>> -------------------------------------------------------
>> /**
>> * show_min_perf - Show minimum performance as frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer to write the frequency value to
>> *
>> * Reads the MIN_PERF register and converts the performance value to
>> * frequency (kHz) for user-space consumption.
>> @@ -1117,6 +1119,9 @@ static ssize_t show_min_perf(struct cpufreq_policy
>> *policy, char *buf)
>>
>> /**
>> * store_min_perf - Set minimum performance from frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer to write the frequency value to
>> + * @count: size of @buf
>> *
>> * Converts the user-provided frequency (kHz) to a performance value
>> * and writes it to the MIN_PERF register.
>> @@ -1144,6 +1149,8 @@ static ssize_t store_min_perf(struct
>> cpufreq_policy *policy, const char *buf, si
>>
>> /**
>> * show_max_perf - Show maximum performance as frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer to write the frequency value to
>> *
>> * Reads the MAX_PERF register and converts the performance value to
>> * frequency (kHz) for user-space consumption.
>> @@ -1166,6 +1173,9 @@ static ssize_t show_max_perf(struct cpufreq_policy
>> *policy, char *buf)
>>
>> /**
>> * store_max_perf - Set maximum performance from frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer to write the frequency value to
>> + * @count: size of @buf
>> -------------------------------------------------------
> As for the whole series, I generally need ARM folks to review it and
> tell me that it is fine.
>
> Thanks!
Will wait if ARM folks have any new comment and then re-spin next version?
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store
2025-11-10 10:56 ` Viresh Kumar
@ 2025-11-11 11:20 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-11 11:20 UTC (permalink / raw)
To: Viresh Kumar
Cc: rafael, lenb, robert.moore, corbet, pierre.gondois, zhenglifeng1,
rdunlap, ray.huang, gautham.shenoy, mario.limonciello, perry.yuan,
ionela.voinescu, zhanjie9, linux-pm, linux-acpi, linux-doc,
acpica-devel, linux-kernel, linux-tegra, treding, jonathanh,
vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 10/11/25 16:26, Viresh Kumar wrote:
> External email: Use caution opening links or attachments
>
>
> On 05-11-25, 17:08, Sumit Gupta wrote:
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> -static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
>> +static ssize_t cppc_cpufreq_sysfs_show_u64(unsigned int cpu, int (*get_func)(int, u64 *), char *buf)
>> {
>> u64 val;
>> - int ret;
>> -
>> - ret = cppc_get_auto_act_window(policy->cpu, &val);
>> + int ret = get_func(cpu, &val);
> Why no casting required here, when you need it ...
Will add casting here.
int ret = get_func((int)cpu, &val);
>> -static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
>> - const char *buf, size_t count)
>> +static ssize_t cppc_cpufreq_sysfs_store_u64(unsigned int cpu, int (*set_func)(int, u64),
>> + const char *buf, size_t count)
>> {
>> - u64 usec;
>> + u64 val;
>> int ret;
>>
>> - ret = kstrtou64(buf, 0, &usec);
>> + ret = kstrtou64(buf, 0, &val);
>> if (ret)
>> return ret;
>>
>> - ret = cppc_set_auto_act_window(policy->cpu, usec);
>> - if (ret)
>> - return ret;
>> + ret = set_func((int)cpu, val);
> ... here ?
>
> --
> viresh
Checked this and the current change looks correct.
Thank you.
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp
2025-11-05 11:38 ` [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
@ 2025-11-12 15:02 ` Ionela Voinescu
2025-11-18 9:17 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
1 sibling, 1 reply; 42+ messages in thread
From: Ionela Voinescu @ 2025-11-12 15:02 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
Hi,
A small nit that applies to multiple places: let's keep the line length
under 80 characters - the lines seem easy to split.
On Wednesday 05 Nov 2025 at 17:08:39 (+0530), Sumit Gupta wrote:
> - Add auto_sel read support in cppc_get_perf_caps().
> - Add write of both auto_sel and energy_perf in cppc_set_epp_perf().
> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
> the same is available in 'struct cppc_perf_ctrls' which is used.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++--------
> include/acpi/cppc_acpi.h | 1 -
> 2 files changed, 34 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 05672c30187c..757e8ce87e9b 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
> struct cpc_register_resource *highest_reg, *lowest_reg,
> *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
> - *low_freq_reg = NULL, *nom_freq_reg = NULL;
> - u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
> + *low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL;
> + u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0;
> int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
> struct cppc_pcc_data *pcc_ss_data = NULL;
> int ret = 0, regs_in_pcc = 0;
> @@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
> nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
> guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>
> /* Are any of the regs PCC ?*/
> if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
> CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
> - CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
> + CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || CPC_IN_PCC(auto_sel_reg)) {
> if (pcc_ss_id < 0) {
> pr_debug("Invalid pcc_ss_id\n");
> return -ENODEV;
> @@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> perf_caps->lowest_freq = low_f;
> perf_caps->nominal_freq = nom_f;
>
> + if (CPC_SUPPORTED(auto_sel_reg))
> + cpc_read(cpunum, auto_sel_reg, &auto_sel);
> + perf_caps->auto_sel = (bool)auto_sel;
>
> out_err:
> if (regs_in_pcc)
> @@ -1555,6 +1559,8 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> struct cpc_register_resource *auto_sel_reg;
> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> struct cppc_pcc_data *pcc_ss_data = NULL;
> + bool autosel_support_in_ffh_or_sysmem;
> + bool epp_support_in_ffh_or_sysmem;
> int ret;
>
> if (!cpc_desc) {
> @@ -1565,6 +1571,11 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
> epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>
> + epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
> + autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
> + (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
> +
> if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
> if (pcc_ss_id < 0) {
> pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
> @@ -1589,14 +1600,29 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> /* after writing CPC, transfer the ownership of PCC to platform */
> ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
> up_write(&pcc_ss_data->pcc_lock);
> - } else if (osc_cpc_flexible_adr_space_confirmed &&
> - CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
> - ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
> + } else if (osc_cpc_flexible_adr_space_confirmed) {
> + if (!epp_support_in_ffh_or_sysmem && !autosel_support_in_ffh_or_sysmem) {
> + ret = -EOPNOTSUPP;
> + } else {
> + if (autosel_support_in_ffh_or_sysmem) {
> + ret = cpc_write(cpu, auto_sel_reg, enable);
> + if (ret)
> + return ret;
> + }
> +
> + if (epp_support_in_ffh_or_sysmem) {
> + ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
> + if (ret)
> + return ret;
> + }
Wouldn't it be more clear to have separate functions for setting auto-sel
and EPP? I think this is functionally correct, but somewhat unclear, given
the signature of the function. But I do acknowledge that the function was
like this to begin with.
> + }
> } else {
> - ret = -ENOTSUPP;
> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
> + ret = -EOPNOTSUPP;
> }
>
> + if (ret == -EOPNOTSUPP)
> + pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
This message needs updating.
Thank you,
Ionela.
> +
> return ret;
> }
> EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 7190afeead8b..42e37a84cac9 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -119,7 +119,6 @@ struct cppc_perf_caps {
> u32 lowest_nonlinear_perf;
> u32 lowest_freq;
> u32 nominal_freq;
> - u32 energy_perf;
> bool auto_sel;
> };
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2025-11-06 10:30 ` kernel test robot
@ 2025-11-13 10:56 ` Ionela Voinescu
2025-11-18 9:34 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
2 siblings, 1 reply; 42+ messages in thread
From: Ionela Voinescu @ 2025-11-13 10:56 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
Hi,
On Wednesday 05 Nov 2025 at 17:08:40 (+0530), Sumit Gupta wrote:
> CPPC allows platforms to specify minimum and maximum performance
> limits that constrain the operating range for CPU performance scaling
> when Autonomous Selection is enabled. These limits can be dynamically
> adjusted to implement power management policies or workload-specific
> optimizations.
>
> Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
> write the MIN_PERF register, allowing dynamic adjustment of the minimum
> performance floor.
>
> Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
> write the MAX_PERF register, enabling dynamic ceiling control for
> maximum performance.
>
> Expose these capabilities through cpufreq sysfs attributes that accept
> frequency values in kHz (which are converted to/from performance values
> internally):
> - /sys/.../cpufreq/policy*/min_perf: Read/write min perf as freq (kHz)
> - /sys/.../cpufreq/policy*/max_perf: Read/write max perf as freq (kHz)
>
There's a theoretical problem here for CPUFREQ_SHARED_TYPE_ANY, when
multiple CPUs share a policy, but that existed before your
patches :). Almost all of the files exposed by cppc_cpufreq should be
per-CPU and not per policy: auto_select, energy_performance_preference,
etc., and now min_perf, max_perf and perf_limited.
In practice it's likely not a problem as all CPUs that have P-State
dependencies would likely share all of these controls. But that's not
mandated by the ACPI specification.
> The frequency-based interface provides a user-friendly abstraction which
> is similar to other cpufreq sysfs interfaces, while the driver handles
> conversion to hardware performance values.
>
> Also update EPP constants for better clarity:
> - Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
> - Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 55 ++++++++++-
> drivers/cpufreq/cppc_cpufreq.c | 166 +++++++++++++++++++++++++++++++++
> include/acpi/cppc_acpi.h | 23 ++++-
> 3 files changed, 242 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 757e8ce87e9b..ef53eb8a1feb 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1634,7 +1634,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
> */
> int cppc_set_epp(int cpu, u64 epp_val)
> {
> - if (epp_val > CPPC_ENERGY_PERF_MAX)
> + if (epp_val > CPPC_EPP_ENERGY_EFFICIENCY_PREF)
> return -EINVAL;
>
> return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val);
> @@ -1757,6 +1757,59 @@ int cppc_set_enable(int cpu, bool enable)
> return cppc_set_reg_val(cpu, ENABLE, enable);
> }
> EXPORT_SYMBOL_GPL(cppc_set_enable);
> +
> +/**
> + * cppc_get_min_perf - Get the min performance register value.
> + * @cpu: CPU from which to get min performance.
> + * @min_perf: Return address.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_min_perf(int cpu, u64 *min_perf)
> +{
> + return cppc_get_reg_val(cpu, MIN_PERF, min_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_min_perf);
> +
> +/**
> + * cppc_set_min_perf() - Write the min performance register.
> + * @cpu: CPU on which to write register.
> + * @min_perf: Value to write to the MIN_PERF register.
> + *
> + * Return: 0 for success, -EIO otherwise.
> + */
> +int cppc_set_min_perf(int cpu, u64 min_perf)
> +{
> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
> +
> +/**
> + * cppc_get_max_perf - Get the max performance register value.
> + * @cpu: CPU from which to get max performance.
> + * @max_perf: Return address.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_max_perf(int cpu, u64 *max_perf)
> +{
> + return cppc_get_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_max_perf);
> +
> +/**
> + * cppc_set_max_perf() - Write the max performance register.
> + * @cpu: CPU on which to write register.
> + * @max_perf: Value to write to the MAX_PERF register.
> + *
> + * Return: 0 for success, -EIO otherwise.
> + */
> +int cppc_set_max_perf(int cpu, u64 max_perf)
> +{
> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
> +
> /**
> * cppc_get_perf - Get a CPU's performance controls.
> * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cf3ed6489a4f..cde6202e9c51 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -23,10 +23,12 @@
> #include <uapi/linux/sched/types.h>
>
> #include <linux/unaligned.h>
> +#include <linux/cleanup.h>
>
> #include <acpi/cppc_acpi.h>
>
> static struct cpufreq_driver cppc_cpufreq_driver;
> +static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
>
> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
> static enum {
> @@ -582,6 +584,68 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
> policy->driver_data = NULL;
> }
>
> +/**
> + * cppc_cpufreq_set_mperf_limit - Generic function to set min/max performance limit
> + * @policy: cpufreq policy
> + * @val: performance value to set
> + * @update_reg: whether to update hardware register
> + * @update_policy: whether to update policy constraints
> + * @is_min: true for min_perf, false for max_perf
> + */
> +static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
> + bool update_reg, bool update_policy, bool is_min)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + unsigned int cpu = policy->cpu;
> + struct freq_qos_request *req;
> + unsigned int freq;
> + u32 perf;
> + int ret;
> +
> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
> + freq = cppc_perf_to_khz(caps, perf);
> +
> + pr_debug("cpu%d, %s_perf:%llu, update_reg:%d, update_policy:%d\n", cpu,
> + is_min ? "min" : "max", (u64)perf, update_reg, update_policy);
> +
> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> +
> + if (update_reg) {
> + ret = is_min ? cppc_set_min_perf(cpu, perf) : cppc_set_max_perf(cpu, perf);
> + if (ret) {
> + if (ret != -EOPNOTSUPP)
> + pr_warn("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
> + is_min ? "min" : "max", (u64)perf, cpu, ret);
> + return ret;
> + }
> +
> + if (is_min)
> + cpu_data->perf_ctrls.min_perf = perf;
> + else
> + cpu_data->perf_ctrls.max_perf = perf;
> + }
> +
> + if (update_policy) {
> + req = is_min ? policy->min_freq_req : policy->max_freq_req;
> +
> + ret = freq_qos_update_request(req, freq);
> + if (ret < 0) {
> + pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n",
> + is_min ? "min" : "max", cpu, ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy) \
> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, true)
> +
> +#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \
> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false)
> +
> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
> {
> unsigned int cpu = policy->cpu;
> @@ -881,16 +945,118 @@ static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *po
> return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp, buf, count);
> }
>
> +/**
> + * show_min_perf - Show minimum performance as frequency (kHz)
> + *
> + * Reads the MIN_PERF register and converts the performance value to
> + * frequency (kHz) for user-space consumption.
> + */
> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + u64 perf;
> + int ret;
> +
> + ret = cppc_get_min_perf(policy->cpu, &perf);
> + if (ret == -EOPNOTSUPP)
> + return sysfs_emit(buf, "<unsupported>\n");
> + if (ret)
> + return ret;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_min_perf - Set minimum performance from frequency (kHz)
> + *
> + * Converts the user-provided frequency (kHz) to a performance value
> + * and writes it to the MIN_PERF register.
> + */
> +static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + unsigned int freq_khz;
> + u64 perf;
> + int ret;
> +
> + ret = kstrtouint(buf, 0, &freq_khz);
> + if (ret)
> + return ret;
> +
> + /* Convert frequency (kHz) to performance value */
> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> + ret = cppc_cpufreq_set_min_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +/**
> + * show_max_perf - Show maximum performance as frequency (kHz)
> + *
> + * Reads the MAX_PERF register and converts the performance value to
> + * frequency (kHz) for user-space consumption.
> + */
> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + u64 perf;
> + int ret;
> +
> + ret = cppc_get_max_perf(policy->cpu, &perf);
> + if (ret == -EOPNOTSUPP)
> + return sysfs_emit(buf, "<unsupported>\n");
> + if (ret)
> + return ret;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_max_perf - Set maximum performance from frequency (kHz)
> + *
> + * Converts the user-provided frequency (kHz) to a performance value
> + * and writes it to the MAX_PERF register.
> + */
> +static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + unsigned int freq_khz;
> + u64 perf;
> + int ret;
> +
> + ret = kstrtouint(buf, 0, &freq_khz);
> + if (ret)
> + return ret;
> +
> + /* Convert frequency (kHz) to performance value */
> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> + ret = cppc_cpufreq_set_max_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
Can you give me some details around updating the policy limits when
auto-select is true? I suppose if P-state selection is autonomous, the
policy limits should not matter, right?
Thanks,
Ionela.
> + if (ret)
> + return ret;
> +
> + 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(min_perf);
> +cpufreq_freq_attr_rw(max_perf);
>
> static struct freq_attr *cppc_cpufreq_attr[] = {
> &freqdomain_cpus,
> &auto_select,
> &auto_act_window,
> &energy_performance_preference_val,
> + &min_perf,
> + &max_perf,
> NULL,
> };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 42e37a84cac9..be7de1222eee 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -39,7 +39,8 @@
> /* CPPC_AUTO_ACT_WINDOW_MAX_SIG is 127, so 128 and 129 will decay to 127 when writing */
> #define CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH 129
>
> -#define CPPC_ENERGY_PERF_MAX (0xFF)
> +#define CPPC_EPP_PERFORMANCE_PREF 0x00
> +#define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF
>
> /* Each register has the folowing format. */
> struct cpc_reg {
> @@ -172,6 +173,10 @@ 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_min_perf(int cpu, u64 *min_perf);
> +extern int cppc_set_min_perf(int cpu, u64 min_perf);
> +extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> +extern int cppc_set_max_perf(int cpu, u64 max_perf);
> extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
> extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
> extern int amd_detect_prefcore(bool *detected);
> @@ -264,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_min_perf(int cpu, u64 min_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_max_perf(int cpu, u64 *max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_max_perf(int cpu, u64 max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
2025-11-05 11:38 ` [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
@ 2025-11-13 11:35 ` Ionela Voinescu
2025-11-18 10:20 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
1 sibling, 1 reply; 42+ messages in thread
From: Ionela Voinescu @ 2025-11-13 11:35 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
Hi,
On Wednesday 05 Nov 2025 at 17:08:41 (+0530), Sumit Gupta wrote:
> Add sysfs interface to read/write the Performance Limited register.
>
> The Performance Limited register indicates to the OS that an
> unpredictable event (like thermal throttling) has limited processor
> performance. This register is sticky and remains set until reset or
> OS clears it by writing 0.
>
> The interface is exposed as:
> /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
What is the intended use of this interface? The performance limited
register has a specific format of status bits with feedback about
performance being limited temporarily and the user can only clear it.
"Contains a resource descriptor with a single Register() descriptor
that describes the register to read to determine if performance was
limited. A nonzero value indicates performance was limited. This
register is sticky, and will remain set until reset or OSPM clears
it by writing 0. See the section “Performance Limiting” for more
details." Also, "The performance limited register should only be used
to report short term, unpredictable events (e.g., PROCHOT being
asserted)."
Therefore, I'm not seeing the value of exposing this via sysfs.
Thanks,
Ionela.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 26 ++++++++++++++++++++++++++
> drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
> include/acpi/cppc_acpi.h | 10 ++++++++++
> 3 files changed, 48 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index ef53eb8a1feb..9b8da3ef06db 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
> }
> EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>
> +/**
> + * cppc_get_perf_limited - Get the Performance Limited register value.
> + * @cpu: CPU from which to get Performance Limited register.
> + * @perf_limited: Pointer to store the Performance Limited value.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> + return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
> +
> +/**
> + * cppc_set_perf_limited() - Write the Performance Limited register.
> + * @cpu: CPU on which to write register.
> + * @perf_limited: Value to write to the perf_limited register.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> + return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
> +
> /**
> * cppc_get_perf - Get a CPU's performance controls.
> * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cde6202e9c51..a425ad575aa6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
> return count;
> }
>
> +static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
> +{
> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
> +}
> +
> +static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, 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(min_perf);
> cpufreq_freq_attr_rw(max_perf);
> +cpufreq_freq_attr_rw(perf_limited);
>
> static struct freq_attr *cppc_cpufreq_attr[] = {
> &freqdomain_cpus,
> @@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
> &energy_performance_preference_val,
> &min_perf,
> &max_perf,
> + &perf_limited,
> NULL,
> };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index be7de1222eee..8baff46f2ac7 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
> extern int cppc_set_min_perf(int cpu, u64 min_perf);
> extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> extern int cppc_set_max_perf(int cpu, u64 max_perf);
> +extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
> +extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
> extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
> extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
> extern int amd_detect_prefcore(bool *detected);
> @@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
2025-11-05 11:38 ` [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-11-13 12:41 ` Ionela Voinescu
2025-11-18 10:46 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Ionela Voinescu @ 2025-11-13 12:41 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
Hi,
On Wednesday 05 Nov 2025 at 17:08:42 (+0530), Sumit Gupta wrote:
> Add sysfs interfaces for Minimum Performance, Maximum Performance
> and Performance Limited Register in the cppc_cpufreq driver.
>
> Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> .../ABI/testing/sysfs-devices-system-cpu | 46 +++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index 8aed6d94c4cd..6f1f70696000 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -327,6 +327,52 @@ Description: Energy performance preference
>
> This file is only present if the cppc-cpufreq driver is in use.
>
> +What: /sys/devices/system/cpu/cpuX/cpufreq/min_perf
> +Date: December 2025
> +Contact: linux-pm@vger.kernel.org
> +Description: Minimum Performance Frequency
> +
> + Read/write a frequency value in kHz from/to this file. This
> + file conveys the minimum performance level (as frequency) at
> + which the platform may run. The frequency value is internally
> + converted to a performance value and must correspond to a
> + performance level in the range [Lowest Performance, Highest
> + Performance], inclusive. The minimum must be less than or equal
> + to the maximum performance. The performance range can be checked
> + from nodes:
> + /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf
> + /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf
I think information on highest/lowest performance is irrelevant here. If
the user is expected to provide a frequency value, it should only care
about it being in the range [cpuinfo_min_freq, cpuinfo_max_freq].
I think ideally all of these controls (auto-select, EPP, min, max, etc.)
would have been better placed under
/sys/devices/system/cpu/cpuX/acpi_cppc, but I suppose the intention
was/is to have all performance related controls under cpufreq. But that
means that the user should not be concerned about the underlying CPPC
scale and only use /sys/devices/system/cpu/cpuX/acpi_cppc for
information purposes.
Thanks,
Ionela.
> +
> + This file is only present if the cppc-cpufreq driver is in use.
> +
> +What: /sys/devices/system/cpu/cpuX/cpufreq/max_perf
> +Date: December 2025
> +Contact: linux-pm@vger.kernel.org
> +Description: Maximum Performance Frequency
> +
> + Read/write a frequency value in kHz from/to this file. This
> + file conveys the maximum performance level (as frequency) at
> + which the platform may run. The frequency value is internally
> + converted to a performance value and must correspond to a
> + performance level in the range [Lowest Performance, Highest
> + Performance], inclusive. The performance range can be checked
> + from nodes:
> + /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf
> + /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf
> +
> + This file is only present if the cppc-cpufreq driver is in use.
> +
> +What: /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
> +Date: December 2025
> +Contact: linux-pm@vger.kernel.org
> +Description: Performance Limited
> +
> + Read/write a 32 bits value from/to this file. This file indicates
> + to OSPM that an unpredictable event has limited processor
> + performance, and the delivered performance may be less than
> + desired/minimum performance.
> +
> + 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
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-05 11:38 ` [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
@ 2025-11-13 15:15 ` Ionela Voinescu
2025-11-26 13:32 ` Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
1 sibling, 1 reply; 42+ messages in thread
From: Ionela Voinescu @ 2025-11-13 15:15 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
Hi,
As an overall comment, there are now various functions that modify the
autonomous selection configuration in various degrees:
cppc_cpufreq_update_autosel_config, cppc_cpufreq_update_auto_select,
cppc_cpufreq_set_epp_autosel_allcpus.
Are these all really necessary? Some seem single use functions that
make the intention very confusing through the use of several bool
parameters. I think a lot of this complexity can be avoided, so
I'd recommend to re-think the software design a bit.
I've added more details below (and I've skipped review of the previous
patch).
On Wednesday 05 Nov 2025 at 17:08:44 (+0530), Sumit Gupta wrote:
> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
> 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@infradead.org>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> .../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);
"When Autonomous Selection is enabled, it is not necessary for OSPM to assess
processor workload performance demand and convey a corresponding performance
delivery request to the platform via the Desired Register. If the Desired
Performance Register exists, OSPM may provide an explicit performance
requirement hint to the platform by writing a non-zero value."
Therefore I believe it's up to the platform to decide if it wants to use
the software hint.
> 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;
> + }
> +
This belongs to patch 2/8.
> 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, epp curr:%u, new:%u\n", cpu, cpu_data->perf_ctrls.energy_perf, epp);
> +
> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> +
> + ret = cppc_set_epp(cpu, epp);
> + if (ret) {
> + pr_warn("failed to set energy_perf for cpu:%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;
> +}
I think cppc_cpufreq_update_auto_select() can be removed and
cppc_cpufreq_update_autosel_config() used in its place.
cppc_cpufreq_update_autosel_config() would not even need min/max as
arguments as they can be obtained from perf_caps (low/nominal range)
or perf_ctrls (current min/max). This would also simplify
cppc_cpufreq_cpu_init().
> +
> 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);
Isn't auto-sel enabled at this point? Also, if the auto-sel is
ACPI_TYPE_INTEGER, cppc_set_enable() will return an error,
isn't it?
> + if (ret) {
> + pr_err("Failed to enable CPPC on cpu%d (%d)\n", cpu, ret);
> + goto out;
Do you really want to bail out of the rest of the cpufreq CPU
initialisation, if only auto-select configuration fails?
> + }
> +
> + 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);
> + 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);
> + if (ret) {
> + pr_warn("Failed to set auto_sel on CPU%d (%d)\n", cpu, ret);
> + goto disable_all;
> + }
> + }
> +
> + return 0;
> +
> +disable_all:
> + pr_warn("Disabling auto_sel for all CPUs\n");
> + for_each_present_cpu(cpu)
> + cppc_set_auto_sel(cpu, false);
> +
> + return -EIO;
> +}
> +
> static int __init cppc_cpufreq_init(void)
> {
> + bool auto_sel;
> int ret;
>
> if (!acpi_cpc_valid())
> return -ENODEV;
>
> + if (auto_sel_mode) {
> + /*
> + * Check if autonomous selection is supported by testing CPU 0.
> + * If supported, enable autonomous mode on all CPUs.
> + */
> + ret = cppc_get_auto_sel(0, &auto_sel);
> + if (!ret) {
> + pr_info("Enabling auto_sel_mode (autonomous selection mode)\n");
> + ret = cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
> + if (ret) {
> + pr_warn("Disabling auto_sel_mode, fallback to standard\n");
> + auto_sel_mode = false;
> + }
> + } else {
> + pr_warn("Disabling auto_sel_mode as not supported by hardware\n");
> + auto_sel_mode = false;
> + }
> + }
> +
Why not check at cppc_cpufreq_cpu_init? In the unlikely case that one
CPU does not support it, I would recommend to issue a warning, rather
than disable auto-sel on all the other CPUs. It is possible that some
CPUs support auto-sel and they have it enabled by default without
exposing that control to the OS.
> cppc_freq_invariance_init();
> populate_efficiency_class();
>
> @@ -1165,10 +1299,19 @@ static int __init cppc_cpufreq_init(void)
>
> static void __exit cppc_cpufreq_exit(void)
> {
> + int cpu;
> +
> + for_each_present_cpu(cpu)
> + cppc_set_auto_sel(cpu, false);
> + auto_sel_mode = false;
> +
> cpufreq_unregister_driver(&cppc_cpufreq_driver);
> cppc_freq_invariance_exit();
> }
>
> +module_param(auto_sel_mode, bool, 0000);
> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
> +
> module_exit(cppc_cpufreq_exit);
> MODULE_AUTHOR("Ashwin Chaugule");
> MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 0/8] Enhanced autonomous selection and improvements
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
` (8 preceding siblings ...)
2025-11-10 11:00 ` [PATCH v4 0/8] Enhanced autonomous selection and improvements Viresh Kumar
@ 2025-11-18 8:45 ` Jie Zhan
9 siblings, 0 replies; 42+ messages in thread
From: Jie Zhan @ 2025-11-18 8:45 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
pierre.gondois, zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, ionela.voinescu, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 11/5/2025 7:38 PM, Sumit Gupta wrote:
> This patch series enhances the ACPI CPPC CPUFREQ driver with
> comprehensive support for autonomous performance selection, expanded
> runtime control interfaces and improvements.
>
> It adds support for below:
> - Expose sysfs to read/write the Minimum/Maximum Performance Registers
> using frequency (kHz), with internal conversion to performance values.
> Also, update the policy min/max accordingly.
> /sys/.../cpufreq/policy*/min_perf and max_perf
>
> - Expose sysfs to read/write the Performance Limited Register.
> /sys/.../cpufreq/policy*/perf_limited
>
> - When toggling autonomous selection, synchronize the policy limits
> by updating the policy min/max.
>
> - System-wide autonomous mode configuration via 'auto_sel_mode' boot
> parameter. Mode can be switched dynamically on individual CPUs.
>
> - Generic sysfs helper functions to reduce code duplication.
Hi Sumit,
My biggest question is what do we design for users to use all this CPPC
autosel stuff in the cppc_cpufreq driver?
Is it just exposing most of the related interfaces through sysfs and then
we let userspace to play with it (probably with a userspace tool) or
something like the amd_pstate_epp driver?
Since this is quite a large change, we may need a guideline of how it's
gonna be used so we would have a clue of how it should be made.
Cheers,
Jie
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp
2025-11-12 15:02 ` Ionela Voinescu
@ 2025-11-18 9:17 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-18 9:17 UTC (permalink / raw)
To: Ionela Voinescu
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
Hi Ionela,
On 12/11/25 20:32, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> A small nit that applies to multiple places: let's keep the line length
> under 80 characters - the lines seem easy to split.
Wrapped line length to 100 as per the max limit.
Will change to 80 chars if that's preferred.
$ grep "max_line_length =" scripts/checkpatch.pl
my $max_line_length = 100;
>
> On Wednesday 05 Nov 2025 at 17:08:39 (+0530), Sumit Gupta wrote:
>> - Add auto_sel read support in cppc_get_perf_caps().
>> - Add write of both auto_sel and energy_perf in cppc_set_epp_perf().
>> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
>> the same is available in 'struct cppc_perf_ctrls' which is used.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++--------
>> include/acpi/cppc_acpi.h | 1 -
>> 2 files changed, 34 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 05672c30187c..757e8ce87e9b 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
>> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
>> struct cpc_register_resource *highest_reg, *lowest_reg,
>> *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
>> - *low_freq_reg = NULL, *nom_freq_reg = NULL;
>> - u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
>> + *low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL;
>> + u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0;
>> int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
>> struct cppc_pcc_data *pcc_ss_data = NULL;
>> int ret = 0, regs_in_pcc = 0;
>> @@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
>> low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
>> nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
>> guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>>
>> /* Are any of the regs PCC ?*/
>> if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
>> CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
>> - CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
>> + CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || CPC_IN_PCC(auto_sel_reg)) {
>> if (pcc_ss_id < 0) {
>> pr_debug("Invalid pcc_ss_id\n");
>> return -ENODEV;
>> @@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
>> perf_caps->lowest_freq = low_f;
>> perf_caps->nominal_freq = nom_f;
>>
>> + if (CPC_SUPPORTED(auto_sel_reg))
>> + cpc_read(cpunum, auto_sel_reg, &auto_sel);
>> + perf_caps->auto_sel = (bool)auto_sel;
>>
>> out_err:
>> if (regs_in_pcc)
>> @@ -1555,6 +1559,8 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>> struct cpc_register_resource *auto_sel_reg;
>> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> struct cppc_pcc_data *pcc_ss_data = NULL;
>> + bool autosel_support_in_ffh_or_sysmem;
>> + bool epp_support_in_ffh_or_sysmem;
>> int ret;
>>
>> if (!cpc_desc) {
>> @@ -1565,6 +1571,11 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>> auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>> epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>>
>> + epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
>> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
>> + autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
>> + (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
>> +
>> if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
>> if (pcc_ss_id < 0) {
>> pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
>> @@ -1589,14 +1600,29 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>> /* after writing CPC, transfer the ownership of PCC to platform */
>> ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
>> up_write(&pcc_ss_data->pcc_lock);
>> - } else if (osc_cpc_flexible_adr_space_confirmed &&
>> - CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
>> - ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
>> + } else if (osc_cpc_flexible_adr_space_confirmed) {
>> + if (!epp_support_in_ffh_or_sysmem && !autosel_support_in_ffh_or_sysmem) {
>> + ret = -EOPNOTSUPP;
>> + } else {
>> + if (autosel_support_in_ffh_or_sysmem) {
>> + ret = cpc_write(cpu, auto_sel_reg, enable);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (epp_support_in_ffh_or_sysmem) {
>> + ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
>> + if (ret)
>> + return ret;
>> + }
> Wouldn't it be more clear to have separate functions for setting auto-sel
> and EPP? I think this is functionally correct, but somewhat unclear, given
> the signature of the function. But I do acknowledge that the function was
> like this to begin with.
In v2, changed names of some functions for better clarity. One of those
was rename cppc_set_epp_perf() to cppc_set_epp_and_autosel() to reflect
that the API updates both. Reverted those changes after suggestion from
Rafael in [1] to avoid renaming.
There are existing separate API's cppc_set_auto_sel() & cppc_set_epp()
which set the register directly.
This change keeps the behavior of cppc_set_epp_perf() consistent to update
both together as that is already happening for PCC but not for other cases.
[1]
https://lore.kernel.org/lkml/CAJZ5v0ipt=zDDORjuHzuerfLryZO5NE1RpPfnADeWEqi6kyv6Q@mail.gmail.com/
>> + }
>> } else {
>> - ret = -ENOTSUPP;
>> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
>> + ret = -EOPNOTSUPP;
>> }
>>
>> + if (ret == -EOPNOTSUPP)
>> + pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
> This message needs updating.
>
> Thank you,
> Ionela.
Sure.
Thank you,
Sumit Gupta
>> +
>> return ret;
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 7190afeead8b..42e37a84cac9 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -119,7 +119,6 @@ struct cppc_perf_caps {
>> u32 lowest_nonlinear_perf;
>> u32 lowest_freq;
>> u32 nominal_freq;
>> - u32 energy_perf;
>> bool auto_sel;
>> };
>>
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-13 10:56 ` Ionela Voinescu
@ 2025-11-18 9:34 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-18 9:34 UTC (permalink / raw)
To: Ionela Voinescu
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 13/11/25 16:26, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> On Wednesday 05 Nov 2025 at 17:08:40 (+0530), Sumit Gupta wrote:
>> CPPC allows platforms to specify minimum and maximum performance
>> limits that constrain the operating range for CPU performance scaling
>> when Autonomous Selection is enabled. These limits can be dynamically
>> adjusted to implement power management policies or workload-specific
>> optimizations.
>>
>> Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
>> write the MIN_PERF register, allowing dynamic adjustment of the minimum
>> performance floor.
>>
>> Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
>> write the MAX_PERF register, enabling dynamic ceiling control for
>> maximum performance.
>>
>> Expose these capabilities through cpufreq sysfs attributes that accept
>> frequency values in kHz (which are converted to/from performance values
>> internally):
>> - /sys/.../cpufreq/policy*/min_perf: Read/write min perf as freq (kHz)
>> - /sys/.../cpufreq/policy*/max_perf: Read/write max perf as freq (kHz)
>>
> There's a theoretical problem here for CPUFREQ_SHARED_TYPE_ANY, when
> multiple CPUs share a policy, but that existed before your
> patches :). Almost all of the files exposed by cppc_cpufreq should be
> per-CPU and not per policy: auto_select, energy_performance_preference,
> etc., and now min_perf, max_perf and perf_limited.
>
> In practice it's likely not a problem as all CPUs that have P-State
> dependencies would likely share all of these controls. But that's not
> mandated by the ACPI specification.
Will send a separate patch as improvement for the existing code.
>> The frequency-based interface provides a user-friendly abstraction which
>> is similar to other cpufreq sysfs interfaces, while the driver handles
>> conversion to hardware performance values.
>>
>> Also update EPP constants for better clarity:
>> - Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
>> - Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
......
>> +/**
>> + * store_max_perf - Set maximum performance from frequency (kHz)
>> + *
>> + * Converts the user-provided frequency (kHz) to a performance value
>> + * and writes it to the MAX_PERF register.
>> + */
>> +static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + unsigned int freq_khz;
>> + u64 perf;
>> + int ret;
>> +
>> + ret = kstrtouint(buf, 0, &freq_khz);
>> + if (ret)
>> + return ret;
>> +
>> + /* Convert frequency (kHz) to performance value */
>> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>> +
>> + ret = cppc_cpufreq_set_max_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
> Can you give me some details around updating the policy limits when
> auto-select is true? I suppose if P-state selection is autonomous, the
> policy limits should not matter, right?
>
> Thanks,
> Ionela.
Yes, the cpufreq sw policy limits don't matter to hw in autonomous mode.
This is done to notify the cpufreq framework and keep sw policy limits in
sync with the new limits in HW. This was raised by Pierre also in [1] about
cpufreq not being notified after user modifies the HW limits.
[1]
https://lore.kernel.org/lkml/b2bd3258-51bd-462a-ae29-71f1d6f823f3@nvidia.com/
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
2025-11-13 11:35 ` Ionela Voinescu
@ 2025-11-18 10:20 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-18 10:20 UTC (permalink / raw)
To: Ionela Voinescu
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 13/11/25 17:05, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> On Wednesday 05 Nov 2025 at 17:08:41 (+0530), Sumit Gupta wrote:
>> Add sysfs interface to read/write the Performance Limited register.
>>
>> The Performance Limited register indicates to the OS that an
>> unpredictable event (like thermal throttling) has limited processor
>> performance. This register is sticky and remains set until reset or
>> OS clears it by writing 0.
>>
>> The interface is exposed as:
>> /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
> What is the intended use of this interface? The performance limited
> register has a specific format of status bits with feedback about
> performance being limited temporarily and the user can only clear it.
>
> "Contains a resource descriptor with a single Register() descriptor
> that describes the register to read to determine if performance was
> limited. A nonzero value indicates performance was limited. This
> register is sticky, and will remain set until reset or OSPM clears
> it by writing 0. See the section “Performance Limiting” for more
> details." Also, "The performance limited register should only be used
> to report short term, unpredictable events (e.g., PROCHOT being
> asserted)."
>
> Therefore, I'm not seeing the value of exposing this via sysfs.
>
> Thanks,
> Ionela.
>
This enables users to detect if platform throttling impacted a workload.
Users clear the register before execution, run the workload, then check
afterward if set, hardware throttling (thermal/power/current limits)
occurred during that time window. The write operation can be restricted
to only accept 0 (clearing) to prevent writing arbitrary values.
Thank you,
Sumit Gupta
....
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
2025-11-13 12:41 ` Ionela Voinescu
@ 2025-11-18 10:46 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-18 10:46 UTC (permalink / raw)
To: Ionela Voinescu
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 13/11/25 18:11, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> On Wednesday 05 Nov 2025 at 17:08:42 (+0530), Sumit Gupta wrote:
>> Add sysfs interfaces for Minimum Performance, Maximum Performance
>> and Performance Limited Register in the cppc_cpufreq driver.
>>
>> Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> .../ABI/testing/sysfs-devices-system-cpu | 46 +++++++++++++++++++
>> 1 file changed, 46 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> index 8aed6d94c4cd..6f1f70696000 100644
>> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
>> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> @@ -327,6 +327,52 @@ Description: Energy performance preference
>>
>> This file is only present if the cppc-cpufreq driver is in use.
>>
>> +What: /sys/devices/system/cpu/cpuX/cpufreq/min_perf
>> +Date: December 2025
>> +Contact: linux-pm@vger.kernel.org
>> +Description: Minimum Performance Frequency
>> +
>> + Read/write a frequency value in kHz from/to this file. This
>> + file conveys the minimum performance level (as frequency) at
>> + which the platform may run. The frequency value is internally
>> + converted to a performance value and must correspond to a
>> + performance level in the range [Lowest Performance, Highest
>> + Performance], inclusive. The minimum must be less than or equal
>> + to the maximum performance. The performance range can be checked
>> + from nodes:
>> + /sys/devices/system/cpu/cpuX/acpi_cppc/highest_perf
>> + /sys/devices/system/cpu/cpuX/acpi_cppc/lowest_perf
> I think information on highest/lowest performance is irrelevant here. If
> the user is expected to provide a frequency value, it should only care
> about it being in the range [cpuinfo_min_freq, cpuinfo_max_freq].
Will change it as below:
Read/write a frequency value in kHz from/to this file. This
file sets the minimum performance level (as frequency) at
which the platform may run. The frequency value is internally
converted to a performance value and must be in the range
[cpuinfo_min_freq, cpuinfo_max_freq], inclusive.
This file is only present if the cppc-cpufreq driver is in use.
> I think ideally all of these controls (auto-select, EPP, min, max, etc.)
> would have been better placed under
> /sys/devices/system/cpu/cpuX/acpi_cppc, but I suppose the intention
> was/is to have all performance related controls under cpufreq. But that
> means that the user should not be concerned about the underlying CPPC
> scale and only use /sys/devices/system/cpu/cpuX/acpi_cppc for
> information purposes.
>
> Thanks,
> Ionela.
>
In v1[1], added these controls under acpi_cppc sysfs.
Later after discussion, it was decided to keep them under cpufreq sysfs
and [2] was merged first.
[1] https://lore.kernel.org/lkml/20250211103737.447704-1-sumitg@nvidia.com/
[2]
https://lore.kernel.org/all/20250507031941.2812701-1-zhenglifeng1@huawei.com/
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-13 15:15 ` Ionela Voinescu
@ 2025-11-26 13:32 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-26 13:32 UTC (permalink / raw)
To: Ionela Voinescu
Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel, linux-tegra, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, sumitg
On 13/11/25 20:45, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> As an overall comment, there are now various functions that modify the
> autonomous selection configuration in various degrees:
> cppc_cpufreq_update_autosel_config, cppc_cpufreq_update_auto_select,
> cppc_cpufreq_set_epp_autosel_allcpus.
>
> Are these all really necessary? Some seem single use functions that
> make the intention very confusing through the use of several bool
> parameters. I think a lot of this complexity can be avoided, so
> I'd recommend to re-think the software design a bit.
>
> I've added more details below (and I've skipped review of the previous
> patch).
Sure, will do the changes in v5.
> On Wednesday 05 Nov 2025 at 17:08:44 (+0530), Sumit Gupta wrote:
>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>> 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@infradead.org>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> .../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);
> "When Autonomous Selection is enabled, it is not necessary for OSPM to assess
> processor workload performance demand and convey a corresponding performance
> delivery request to the platform via the Desired Register. If the Desired
> Performance Register exists, OSPM may provide an explicit performance
> requirement hint to the platform by writing a non-zero value."
>
> Therefore I believe it's up to the platform to decide if it wants to use
> the software hint.
Ok, will remove the check in v5.
>> 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;
>> + }
>> +
> This belongs to patch 2/8.
will move in v5.
>
>> 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, epp curr:%u, new:%u\n", cpu, cpu_data->perf_ctrls.energy_perf, epp);
>> +
>> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
>> +
>> + ret = cppc_set_epp(cpu, epp);
>> + if (ret) {
>> + pr_warn("failed to set energy_perf for cpu:%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;
>> +}
> I think cppc_cpufreq_update_auto_select() can be removed and
> cppc_cpufreq_update_autosel_config() used in its place.
>
> cppc_cpufreq_update_autosel_config() would not even need min/max as
> arguments as they can be obtained from perf_caps (low/nominal range)
> or perf_ctrls (current min/max). This would also simplify
> cppc_cpufreq_cpu_init().
Will change in v5.
>> +
>> 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);
> Isn't auto-sel enabled at this point? Also, if the auto-sel is
> ACPI_TYPE_INTEGER, cppc_set_enable() will return an error,
> isn't it?
Do you mean CPPC enable rather than auto-sel?
Some platforms may require the Kernel to explicitly set the CPPC Enable
register which is of type ACPI_TYPE_BUFFER. I will add error handling for
cases where this register is not supported, not visible, or is read-only of
ACPI_TYPE_INTEGER to the Kernel.
>> + if (ret) {
>> + pr_err("Failed to enable CPPC on cpu%d (%d)\n", cpu, ret);
>> + goto out;
> Do you really want to bail out of the rest of the cpufreq CPU
> initialisation, if only auto-select configuration fails?
Will do the change in v5.
>> + }
>> +
>> + 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);
>> + 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);
>> + if (ret) {
>> + pr_warn("Failed to set auto_sel on CPU%d (%d)\n", cpu, ret);
>> + goto disable_all;
>> + }
>> + }
>> +
>> + return 0;
>> +
>> +disable_all:
>> + pr_warn("Disabling auto_sel for all CPUs\n");
>> + for_each_present_cpu(cpu)
>> + cppc_set_auto_sel(cpu, false);
>> +
>> + return -EIO;
>> +}
>> +
>> static int __init cppc_cpufreq_init(void)
>> {
>> + bool auto_sel;
>> int ret;
>>
>> if (!acpi_cpc_valid())
>> return -ENODEV;
>>
>> + if (auto_sel_mode) {
>> + /*
>> + * Check if autonomous selection is supported by testing CPU 0.
>> + * If supported, enable autonomous mode on all CPUs.
>> + */
>> + ret = cppc_get_auto_sel(0, &auto_sel);
>> + if (!ret) {
>> + pr_info("Enabling auto_sel_mode (autonomous selection mode)\n");
>> + ret = cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
>> + if (ret) {
>> + pr_warn("Disabling auto_sel_mode, fallback to standard\n");
>> + auto_sel_mode = false;
>> + }
>> + } else {
>> + pr_warn("Disabling auto_sel_mode as not supported by hardware\n");
>> + auto_sel_mode = false;
>> + }
>> + }
>> +
> Why not check at cppc_cpufreq_cpu_init? In the unlikely case that one
> CPU does not support it, I would recommend to issue a warning, rather
> than disable auto-sel on all the other CPUs. It is possible that some
> CPUs support auto-sel and they have it enabled by default without
> exposing that control to the OS.
Will move to cppc_cpufreq_cpu_init in v5.
Thank you,
Sumit Gupta
>> cppc_freq_invariance_init();
>> populate_efficiency_class();
>>
>> @@ -1165,10 +1299,19 @@ static int __init cppc_cpufreq_init(void)
>>
>> static void __exit cppc_cpufreq_exit(void)
>> {
>> + int cpu;
>> +
>> + for_each_present_cpu(cpu)
>> + cppc_set_auto_sel(cpu, false);
>> + auto_sel_mode = false;
>> +
>> cpufreq_unregister_driver(&cppc_cpufreq_driver);
>> cppc_freq_invariance_exit();
>> }
>>
>> +module_param(auto_sel_mode, bool, 0000);
>> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
>> +
>> module_exit(cppc_cpufreq_exit);
>> MODULE_AUTHOR("Ashwin Chaugule");
>> MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2025-11-05 11:38 ` [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:01 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:53 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-tegra, linux-kernel, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, gautham.shenoy, ray.huang, rdunlap,
zhenglifeng1, lenb, robert.moore, rafael, viresh.kumar, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, corbet
Hello Sumit,
Sorry for the late review.
I think it would be nice to split the patchset in 2 as handling the
auto_sel and
min/max_perf values in the cppc_cpufreq driver might lead to more
discussion.
I.e. the ACPI: CPPC: XXX patches should be straightforward to upstream.
This is just a personal opinion, feel free to ignore it.
On 11/5/25 12:38, Sumit Gupta wrote:
> Add cppc_get_perf() function to read values of performance control
> registers including desired_perf, min_perf, max_perf, and energy_perf.
>
> This provides a read interface to complement the existing cppc_set_perf()
> write interface for performance control registers.
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 73 ++++++++++++++++++++++++++++++++++++++++
> include/acpi/cppc_acpi.h | 5 +++
> 2 files changed, 78 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index ab4651205e8a..05672c30187c 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1731,6 +1731,79 @@ int cppc_set_enable(int cpu, bool enable)
> return cppc_set_reg_val(cpu, ENABLE, enable);
> }
> EXPORT_SYMBOL_GPL(cppc_set_enable);
> +/**
> + * cppc_get_perf - Get a CPU's performance controls.
> + * @cpu: CPU for which to get performance controls.
> + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
> + *
> + * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
> + */
> +int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
> +{
> + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> + struct cpc_register_resource *desired_perf_reg, *min_perf_reg, *max_perf_reg,
> + *energy_perf_reg;
> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0;
> + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
> + struct cppc_pcc_data *pcc_ss_data = NULL;
> + int ret = 0, regs_in_pcc = 0;
> +
> + if (!cpc_desc) {
> + pr_debug("No CPC descriptor for CPU:%d\n", cpu);
> + return -ENODEV;
> + }
> +
> + if (!perf_ctrls) {
> + pr_debug("Invalid perf_ctrls pointer\n");
> + return -EINVAL;
> + }
> +
> + desired_perf_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
> + min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
> + max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
> + energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
> +
> + /* Are any of the regs PCC ?*/
> + if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
> + CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) {
> + if (pcc_ss_id < 0) {
> + pr_debug("Invalid pcc_ss_id forCPU:%d\n", cpu);
> + return -ENODEV;
> + }
> + pcc_ss_data = pcc_data[pcc_ss_id];
> + regs_in_pcc = 1;
> + down_write(&pcc_ss_data->pcc_lock);
> + /* Ring doorbell once to update PCC subspace */
> + if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
> + pr_debug("Failed to send PCC command for CPU:%d, ret:%d\n", cpu, ret);
> + ret = -EIO;
> + goto out_err;
> + }
> + }
> +
> + /* Read optional elements if present */
> + if (CPC_SUPPORTED(max_perf_reg))
> + cpc_read(cpu, max_perf_reg, &max);
> + perf_ctrls->max_perf = max;
> +
> + if (CPC_SUPPORTED(min_perf_reg))
> + cpc_read(cpu, min_perf_reg, &min);
> + perf_ctrls->min_perf = min;
> +
NIT: I think the 'desired_perf_reg' register is mandatory, so the check
could be removed.
> + if (CPC_SUPPORTED(desired_perf_reg))
> + cpc_read(cpu, desired_perf_reg, &desired_perf);
> + perf_ctrls->desired_perf = desired_perf;
> +
> + if (CPC_SUPPORTED(energy_perf_reg))
> + cpc_read(cpu, energy_perf_reg, &energy_perf);
> + perf_ctrls->energy_perf = energy_perf;
> +
> +out_err:
> + if (regs_in_pcc)
> + up_write(&pcc_ss_data->pcc_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_perf);
>
> /**
> * cppc_set_perf - Set a CPU's performance controls.
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 13fa81504844..7190afeead8b 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -151,6 +151,7 @@ extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
> extern int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf);
> extern int cppc_get_highest_perf(int cpunum, u64 *highest_perf);
> extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
> +extern int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
> extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
> extern int cppc_set_enable(int cpu, bool enable);
> extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
> @@ -192,6 +193,10 @@ static inline int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
> {
> return -EOPNOTSUPP;
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select
2025-11-05 11:38 ` [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
@ 2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:08 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:53 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, mario.limonciello,
ray.huang, zhenglifeng1, corbet, robert.moore, lenb, viresh.kumar,
rafael, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
sanjayc, nhartman, bbasu, rdunlap, gautham.shenoy
On 11/5/25 12:38, Sumit Gupta wrote:
> When CPPC autonomous selection (auto_select) is enabled or disabled,
> the policy min/max frequency limits should be updated appropriately to
> reflect the new operating mode.
>
> Currently, toggling auto_select only changes the hardware register but
> doesn't update the cpufreq policy constraints, which can lead to
> inconsistent behavior between the hardware state and the policy limits
> visible to userspace and other kernel components.
>
> When auto_select is enabled, preserve the current min/max performance
> values to maintain user-configured limits. When disabled, the hardware
> operates in a default mode where the OS directly controls performance,
> so update the policy limits accordingly.
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> drivers/cpufreq/cppc_cpufreq.c | 67 ++++++++++++++++++++++++++++++++--
> 1 file changed, 64 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index a425ad575aa6..d1b44beaddda 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -646,6 +646,26 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
> #define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \
> cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false)
>
> +static int cppc_cpufreq_update_autosel_val(struct cpufreq_policy *policy, bool auto_sel)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + unsigned int cpu = policy->cpu;
> + int ret;
> +
> + pr_debug("cpu%d, auto_selcurr:%u,new:%d\n", cpu, cpu_data->perf_caps.auto_sel, auto_sel);
> +
> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
Would it be possible to explain why we need this mutex specifically for
auto_sel ?
> +
> + ret = cppc_set_auto_sel(cpu, auto_sel);
> + if (ret) {
> + pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n", auto_sel, cpu, ret);
> + return ret;
> + }
> + cpu_data->perf_caps.auto_sel = auto_sel;
> +
> + return 0;
> +}
> +
> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
> {
> unsigned int cpu = policy->cpu;
> @@ -879,8 +899,49 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
> return sysfs_emit(buf, "%d\n", val);
> }
>
> -static ssize_t store_auto_select(struct cpufreq_policy *policy,
> - const char *buf, size_t count)
> +/**
> + * cppc_cpufreq_update_auto_select - Update autonomous selection config for policy->cpu
> + * @policy: cpufreq policy
> + * @enable: enable/disable autonomous selection
> + */
> +static int cppc_cpufreq_update_auto_select(struct cpufreq_policy *policy, bool enable)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + 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)
> + min_perf = cpu_data->perf_ctrls.min_perf;
> + if (cpu_data->perf_ctrls.max_perf)
> + max_perf = cpu_data->perf_ctrls.max_perf;
> + }
I think the min/max performance values are still relevant when auto_sel is
disabled/absent. So:
- enabling/disabling autonomous selection
- setting min/max perf values
should not have any dependency I think.
> +
> + /*
> + * 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;
> +}
> +
> +static ssize_t store_auto_select(struct cpufreq_policy *policy, const char *buf, size_t count)
> {
> bool val;
> int ret;
> @@ -889,7 +950,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
> if (ret)
> return ret;
>
> - ret = cppc_set_auto_sel(policy->cpu, val);
> + ret = cppc_cpufreq_update_auto_select(policy, val);
> if (ret)
> return ret;
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-05 11:38 ` [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
2025-11-13 15:15 ` Ionela Voinescu
@ 2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:29 ` Sumit Gupta
1 sibling, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:53 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-kernel, corbet, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, ray.huang, rdunlap, zhenglifeng1, robert.moore,
lenb, viresh.kumar, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, rafael, gautham.shenoy
On 11/5/25 12:38, Sumit Gupta wrote:
> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
> 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@infradead.org>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> .../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);
Do we need to take the mutex ? Or is it reserved to auto_sel ?
> +
> + 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);
The CPPC enable register is optional.
However this doesn't mean CPPC is not working.
> + if (ret) {
> + 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);
Isn't the EPP optional ?
If autonomous selection is available but not EPP, we will bail out.
> + 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.
> + if (ret) {
> + pr_warn("Failed to set auto_sel on CPU%d (%d)\n", cpu, ret);
> + goto disable_all;
> + }
> + }
> +
> + return 0;
> +
> +disable_all:
> + pr_warn("Disabling auto_sel for all CPUs\n");
> + for_each_present_cpu(cpu)
> + cppc_set_auto_sel(cpu, false);
> +
> + return -EIO;
> +}
> +
> static int __init cppc_cpufreq_init(void)
> {
> + bool auto_sel;
> int ret;
>
> if (!acpi_cpc_valid())
> return -ENODEV;
>
> + if (auto_sel_mode) {
> + /*
> + * Check if autonomous selection is supported by testing CPU 0.
> + * If supported, enable autonomous mode on all CPUs.
> + */
> + ret = cppc_get_auto_sel(0, &auto_sel);
> + if (!ret) {
> + pr_info("Enabling auto_sel_mode (autonomous selection mode)\n");
> + ret = cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
> + if (ret) {
> + pr_warn("Disabling auto_sel_mode, fallback to standard\n");
> + auto_sel_mode = false;
> + }
> + } else {
> + pr_warn("Disabling auto_sel_mode as not supported by hardware\n");
> + auto_sel_mode = false;
> + }
> + }
> +
> cppc_freq_invariance_init();
> populate_efficiency_class();
>
> @@ -1165,10 +1299,19 @@ static int __init cppc_cpufreq_init(void)
>
> static void __exit cppc_cpufreq_exit(void)
> {
> + int cpu;
> +
> + for_each_present_cpu(cpu)
> + cppc_set_auto_sel(cpu, false);
> + auto_sel_mode = false;
> +
> cpufreq_unregister_driver(&cppc_cpufreq_driver);
> cppc_freq_invariance_exit();
> }
>
> +module_param(auto_sel_mode, bool, 0000);
> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
> +
> module_exit(cppc_cpufreq_exit);
> MODULE_AUTHOR("Ashwin Chaugule");
> MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp
2025-11-05 11:38 ` [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
2025-11-12 15:02 ` Ionela Voinescu
@ 2025-11-27 14:54 ` Pierre Gondois
2025-12-09 18:10 ` Sumit Gupta
1 sibling, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:54 UTC (permalink / raw)
To: Sumit Gupta, mario.limonciello
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, gautham.shenoy, ray.huang,
zhenglifeng1, corbet, lenb, robert.moore, viresh.kumar, rafael,
linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, rdunlap
Hello Sumit, Mario,
On 11/5/25 12:38, Sumit Gupta wrote:
> - Add auto_sel read support in cppc_get_perf_caps().
> - Add write of both auto_sel and energy_perf in cppc_set_epp_perf().
> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
> the same is available in 'struct cppc_perf_ctrls' which is used.
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++--------
> include/acpi/cppc_acpi.h | 1 -
> 2 files changed, 34 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 05672c30187c..757e8ce87e9b 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
> struct cpc_register_resource *highest_reg, *lowest_reg,
> *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
> - *low_freq_reg = NULL, *nom_freq_reg = NULL;
> - u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
> + *low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL;
> + u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0;
I am not sure this is really useful to get the auto_sel value register
in this function as:
- All of the other registers read are read-only
- The name of the function doesn't match: the autonomous selection is
not really
related to perf. capabilities
I assume this change comes from the presence of the auto_sel register in the
'struct cppc_perf_caps', but IMO this register should be placed in
another structure.
I assume this is ok to let it in 'struct cppc_perf_caps' for now, but I
think we should not
fetch the value with all the other perf. capabilities values.
> int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
> struct cppc_pcc_data *pcc_ss_data = NULL;
> int ret = 0, regs_in_pcc = 0;
> @@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
> nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
> guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>
> /* Are any of the regs PCC ?*/
> if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
> CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
> - CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
> + CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || CPC_IN_PCC(auto_sel_reg)) {
> if (pcc_ss_id < 0) {
> pr_debug("Invalid pcc_ss_id\n");
> return -ENODEV;
> @@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
> perf_caps->lowest_freq = low_f;
> perf_caps->nominal_freq = nom_f;
>
> + if (CPC_SUPPORTED(auto_sel_reg))
> + cpc_read(cpunum, auto_sel_reg, &auto_sel);
> + perf_caps->auto_sel = (bool)auto_sel;
>
> out_err:
> if (regs_in_pcc)
> @@ -1555,6 +1559,8 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> struct cpc_register_resource *auto_sel_reg;
> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> struct cppc_pcc_data *pcc_ss_data = NULL;
> + bool autosel_support_in_ffh_or_sysmem;
> + bool epp_support_in_ffh_or_sysmem;
> int ret;
>
> if (!cpc_desc) {
> @@ -1565,6 +1571,11 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
> epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>
> + epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
> + autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
> + (CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
> +
> if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
> if (pcc_ss_id < 0) {
> pr_debug("Invalid pcc_ss_id forCPU:%d\n", cpu);
> @@ -1589,14 +1600,29 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> /* after writing CPC, transfer the ownership of PCC to platform */
> ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
> up_write(&pcc_ss_data->pcc_lock);
> - } else if (osc_cpc_flexible_adr_space_confirmed &&
> - CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
> - ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
I think this is a bit out of the scope of this patchset, but I'm not
sure this is necessary
to check the value of "osc_cpc_flexible_adr_space_confirmed" here.
Indeed, acpi_cppc_processor_probe() already loops over the CPPC fields
and should detect when a field is using an address space that is not
allowed by "osc_cpc_flexible_adr_space_confirmed".
From what I understand:
- osc_cpc_flexible_adr_space_confirmed was introduced to check that CPPC
registers are in the correct address space
- this broke some amd platforms that didn't configure the _OSC method
correctly
- 8b356e536e69 ("ACPI: CPPC: Don't require _OSC if X86_FEATURE_CPPC is
supported") introduced cpc_supported_by_cpu() to bypass the check of
osc_cpc_flexible_adr_space_confirmed. Indeed, the broken amd platforms
don't configure the _OSC method, but it is possible to check if there is
CPPC support by reading an MSR register.
- an amd platform failed to set the EPP register. This seems to be due
to the EPP register being located in FFH and not in PCC. However the
handler only supported PCC at that time: 7bc1fcd39901 ("ACPI: CPPC: Add
AMD pstate energy performance preference cppc control") The bug report
thread: bugzilla.kernel.org/show_bug.cgi?id=218686
- to allow setting the EPP value when it is located in FFH, the
following patch was done: aaf21ac93909 ("ACPI: CPPC: Add support for
setting EPP register in FFH") This patch seems to have added a check
over the _OSC flexible bit value due to this comment:
https://bugzilla.kernel.org/show_bug.cgi?id=218686#c83 However the CPPC
registers are always allowed to be located in the FFH and PCC address
space. Cf: 0651ab90e4ad ("ACPI: CPPC: Check _OSC for flexible address
space")
------------
Just to summarize, I think the check over
osc_cpc_flexible_adr_space_confirmed could/should be removed. Ideally in
a separate patch.
If Mario could confirm this is correct this would be nice.
> + } else if (osc_cpc_flexible_adr_space_confirmed) {
> + if (!epp_support_in_ffh_or_sysmem && !autosel_support_in_ffh_or_sysmem) {
> + ret = -EOPNOTSUPP;
> + } else {
> + if (autosel_support_in_ffh_or_sysmem) {
> + ret = cpc_write(cpu, auto_sel_reg, enable);
> + if (ret)
> + return ret;
> + }
> +
> + if (epp_support_in_ffh_or_sysmem) {
> + ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
> + if (ret)
> + return ret;
> + }
> + }
> } else {
> - ret = -ENOTSUPP;
> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
> + ret = -EOPNOTSUPP;
> }
>
> + if (ret == -EOPNOTSUPP)
> + pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
> +
> return ret;
> }
> EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 7190afeead8b..42e37a84cac9 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -119,7 +119,6 @@ struct cppc_perf_caps {
> u32 lowest_nonlinear_perf;
> u32 lowest_freq;
> u32 nominal_freq;
> - u32 energy_perf;
> bool auto_sel;
> };
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2025-11-06 10:30 ` kernel test robot
2025-11-13 10:56 ` Ionela Voinescu
@ 2025-11-27 14:54 ` Pierre Gondois
2025-12-09 16:38 ` Sumit Gupta
2 siblings, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:54 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, mario.limonciello,
gautham.shenoy, rdunlap, zhenglifeng1, corbet, robert.moore, lenb,
viresh.kumar, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
sanjayc, nhartman, bbasu, rafael, ray.huang
On 11/5/25 12:38, Sumit Gupta wrote:
> CPPC allows platforms to specify minimum and maximum performance
> limits that constrain the operating range for CPU performance scaling
> when Autonomous Selection is enabled. These limits can be dynamically
> adjusted to implement power management policies or workload-specific
> optimizations.
>
> Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
> write the MIN_PERF register, allowing dynamic adjustment of the minimum
> performance floor.
>
> Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
> write the MAX_PERF register, enabling dynamic ceiling control for
> maximum performance.
>
> Expose these capabilities through cpufreq sysfs attributes that accept
> frequency values in kHz (which are converted to/from performance values
> internally):
> - /sys/.../cpufreq/policy*/min_perf: Read/write min perf as freq (kHz)
> - /sys/.../cpufreq/policy*/max_perf: Read/write max perf as freq (kHz)
>
> The frequency-based interface provides a user-friendly abstraction which
> is similar to other cpufreq sysfs interfaces, while the driver handles
> conversion to hardware performance values.
>
> Also update EPP constants for better clarity:
> - Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
> - Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 55 ++++++++++-
> drivers/cpufreq/cppc_cpufreq.c | 166 +++++++++++++++++++++++++++++++++
> include/acpi/cppc_acpi.h | 23 ++++-
> 3 files changed, 242 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 757e8ce87e9b..ef53eb8a1feb 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1634,7 +1634,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
> */
> int cppc_set_epp(int cpu, u64 epp_val)
> {
> - if (epp_val > CPPC_ENERGY_PERF_MAX)
> + if (epp_val > CPPC_EPP_ENERGY_EFFICIENCY_PREF)
> return -EINVAL;
>
> return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val);
> @@ -1757,6 +1757,59 @@ int cppc_set_enable(int cpu, bool enable)
> return cppc_set_reg_val(cpu, ENABLE, enable);
> }
> EXPORT_SYMBOL_GPL(cppc_set_enable);
> +
> +/**
> + * cppc_get_min_perf - Get the min performance register value.
> + * @cpu: CPU from which to get min performance.
> + * @min_perf: Return address.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_min_perf(int cpu, u64 *min_perf)
> +{
> + return cppc_get_reg_val(cpu, MIN_PERF, min_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_min_perf);
> +
> +/**
> + * cppc_set_min_perf() - Write the min performance register.
> + * @cpu: CPU on which to write register.
> + * @min_perf: Value to write to the MIN_PERF register.
> + *
> + * Return: 0 for success, -EIO otherwise.
> + */
> +int cppc_set_min_perf(int cpu, u64 min_perf)
> +{
> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
> +
> +/**
> + * cppc_get_max_perf - Get the max performance register value.
> + * @cpu: CPU from which to get max performance.
> + * @max_perf: Return address.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_max_perf(int cpu, u64 *max_perf)
> +{
> + return cppc_get_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_max_perf);
> +
> +/**
> + * cppc_set_max_perf() - Write the max performance register.
> + * @cpu: CPU on which to write register.
> + * @max_perf: Value to write to the MAX_PERF register.
> + *
> + * Return: 0 for success, -EIO otherwise.
> + */
> +int cppc_set_max_perf(int cpu, u64 max_perf)
> +{
> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
> +
> /**
> * cppc_get_perf - Get a CPU's performance controls.
> * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cf3ed6489a4f..cde6202e9c51 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -23,10 +23,12 @@
> #include <uapi/linux/sched/types.h>
>
> #include <linux/unaligned.h>
> +#include <linux/cleanup.h>
>
> #include <acpi/cppc_acpi.h>
>
> static struct cpufreq_driver cppc_cpufreq_driver;
> +static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
>
> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
> static enum {
> @@ -582,6 +584,68 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
> policy->driver_data = NULL;
> }
>
> +/**
> + * cppc_cpufreq_set_mperf_limit - Generic function to set min/max performance limit
> + * @policy: cpufreq policy
> + * @val: performance value to set
> + * @update_reg: whether to update hardware register
I m not sure I see in which case we might not want to update the
hardware register.
Isn't the min/max_perf values relevant even when autonomous selection is
disabled/absent ?
> + * @update_policy: whether to update policy constraints
> + * @is_min: true for min_perf, false for max_perf
> + */
> +static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
> + bool update_reg, bool update_policy, bool is_min)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + unsigned int cpu = policy->cpu;
> + struct freq_qos_request *req;
> + unsigned int freq;
> + u32 perf;
> + int ret;
> +
> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
> + freq = cppc_perf_to_khz(caps, perf);
> +
> + pr_debug("cpu%d, %s_perf:%llu, update_reg:%d, update_policy:%d\n", cpu,
> + is_min ? "min" : "max", (u64)perf, update_reg, update_policy);
> +
> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> +
> + if (update_reg) {
> + ret = is_min ? cppc_set_min_perf(cpu, perf) : cppc_set_max_perf(cpu, perf);
> + if (ret) {
> + if (ret != -EOPNOTSUPP)
> + pr_warn("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
> + is_min ? "min" : "max", (u64)perf, cpu, ret);
> + return ret;
> + }
> +
> + if (is_min)
> + cpu_data->perf_ctrls.min_perf = perf;
> + else
> + cpu_data->perf_ctrls.max_perf = perf;
> + }
> +
> + if (update_policy) {
> + req = is_min ? policy->min_freq_req : policy->max_freq_req;
> +
> + ret = freq_qos_update_request(req, freq);
IIUC, we are adding a qos constraint to the min_freq_req or
max_freq_req. However these constraints should match the
scaling_min/max_freq sysfs interface. So doesn't it mean that if we set
the 'max_perf', we are overwriting the the max_freq_req constraint ?
If you have frequencies between 600000:1200000 # Init state:
max_perf:1200000 scaling_max_freq:1200000 # echo 10000000 > max_perf
max_perf:1000000 scaling_max_freq:1000000 # echo 900000 >
scaling_max_freq max_perf:1000000 scaling_max_freq:900000 # echo 1200000
> scaling_max_freq max_perf:1000000 scaling_max_freq:1200000
The 2 values are not in sync. Is it the desired behaviour ?
> + if (ret < 0) {
> + pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n",
> + is_min ? "min" : "max", cpu, ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy) \
> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, true)
> +
> +#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy) \
> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy, false)
> +
> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
> {
> unsigned int cpu = policy->cpu;
> @@ -881,16 +945,118 @@ static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *po
> return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp, buf, count);
> }
>
> +/**
> + * show_min_perf - Show minimum performance as frequency (kHz)
> + *
> + * Reads the MIN_PERF register and converts the performance value to
> + * frequency (kHz) for user-space consumption.
> + */
> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + u64 perf;
> + int ret;
> +
> + ret = cppc_get_min_perf(policy->cpu, &perf);
> + if (ret == -EOPNOTSUPP)
> + return sysfs_emit(buf, "<unsupported>\n");
> + if (ret)
> + return ret;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_min_perf - Set minimum performance from frequency (kHz)
> + *
> + * Converts the user-provided frequency (kHz) to a performance value
> + * and writes it to the MIN_PERF register.
> + */
> +static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + unsigned int freq_khz;
> + u64 perf;
> + int ret;
> +
> + ret = kstrtouint(buf, 0, &freq_khz);
> + if (ret)
> + return ret;
> +
> + /* Convert frequency (kHz) to performance value */
> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> + ret = cppc_cpufreq_set_min_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +/**
> + * show_max_perf - Show maximum performance as frequency (kHz)
> + *
> + * Reads the MAX_PERF register and converts the performance value to
> + * frequency (kHz) for user-space consumption.
> + */
> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
I think it might collide with the scaling_min/max_freq.
I saw that you answered this point at:
https://lore.kernel.org/lkml/b2bd3258-51bd-462a-ae29-71f1d6f823f3@nvidia.com/
But I m not sure I understood why it is needed to have 2 interfaces.
Would it be possible to explain it again ?
I don't see any case where we would like to make a distinction between:
- scaling_max_freq, i.e. the maximal freq. the cpufreq driver is allowed
to set
- max_perf, i.e. the maximal perf. level the firmware will set
------------
Another point is that the min/max_perf interface actually uses freq. values.
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + u64 perf;
> + int ret;
> +
> + ret = cppc_get_max_perf(policy->cpu, &perf);
> + if (ret == -EOPNOTSUPP)
> + return sysfs_emit(buf, "<unsupported>\n");
> + if (ret)
> + return ret;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_max_perf - Set maximum performance from frequency (kHz)
> + *
> + * Converts the user-provided frequency (kHz) to a performance value
> + * and writes it to the MAX_PERF register.
> + */
> +static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + unsigned int freq_khz;
> + u64 perf;
> + int ret;
> +
> + ret = kstrtouint(buf, 0, &freq_khz);
> + if (ret)
> + return ret;
> +
> + /* Convert frequency (kHz) to performance value */
> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
> +
> + ret = cppc_cpufreq_set_max_perf(policy, perf, true, cpu_data->perf_caps.auto_sel);
> + if (ret)
> + return ret;
> +
> + 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(min_perf);
> +cpufreq_freq_attr_rw(max_perf);
>
> static struct freq_attr *cppc_cpufreq_attr[] = {
> &freqdomain_cpus,
> &auto_select,
> &auto_act_window,
> &energy_performance_preference_val,
> + &min_perf,
> + &max_perf,
> NULL,
> };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 42e37a84cac9..be7de1222eee 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -39,7 +39,8 @@
> /* CPPC_AUTO_ACT_WINDOW_MAX_SIG is 127, so 128 and 129 will decay to 127 when writing */
> #define CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH 129
>
> -#define CPPC_ENERGY_PERF_MAX (0xFF)
> +#define CPPC_EPP_PERFORMANCE_PREF 0x00
> +#define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF
>
> /* Each register has the folowing format. */
> struct cpc_reg {
> @@ -172,6 +173,10 @@ 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_min_perf(int cpu, u64 *min_perf);
> +extern int cppc_set_min_perf(int cpu, u64 min_perf);
> +extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> +extern int cppc_set_max_perf(int cpu, u64 max_perf);
> extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
> extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
> extern int amd_detect_prefcore(bool *detected);
> @@ -264,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_min_perf(int cpu, u64 min_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_get_max_perf(int cpu, u64 *max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_max_perf(int cpu, u64 max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
2025-11-05 11:38 ` [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
2025-11-13 11:35 ` Ionela Voinescu
@ 2025-11-27 14:54 ` Pierre Gondois
2025-12-09 17:22 ` Sumit Gupta
1 sibling, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-27 14:54 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-tegra, linux-kernel, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, gautham.shenoy, ray.huang, rdunlap,
zhenglifeng1, corbet, robert.moore, lenb, viresh.kumar, rafael,
treding, jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu
On 11/5/25 12:38, Sumit Gupta wrote:
> Add sysfs interface to read/write the Performance Limited register.
>
> The Performance Limited register indicates to the OS that an
> unpredictable event (like thermal throttling) has limited processor
> performance. This register is sticky and remains set until reset or
> OS clears it by writing 0.
>
> The interface is exposed as:
> /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 26 ++++++++++++++++++++++++++
> drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
> include/acpi/cppc_acpi.h | 10 ++++++++++
> 3 files changed, 48 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index ef53eb8a1feb..9b8da3ef06db 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
> }
> EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>
> +/**
> + * cppc_get_perf_limited - Get the Performance Limited register value.
> + * @cpu: CPU from which to get Performance Limited register.
> + * @perf_limited: Pointer to store the Performance Limited value.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> + return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
> +
> +/**
> + * cppc_set_perf_limited() - Write the Performance Limited register.
> + * @cpu: CPU on which to write register.
> + * @perf_limited: Value to write to the perf_limited register.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> + return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
There are currently only 2 bits used:
- 0 Desired_Excursion
- 1 Minimum_Excursion
It might be worth defining these bits and mask the values when trying to
set the register.
------
Also NIT:
The spec. says:
" All accesses to the Performance Limited Register must be made using
interlocked operations, by both accessing entities."
I am not sure I understand which synchronization issues are faced.
It's just to report the comment from the spec.
> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
> +
> /**
> * cppc_get_perf - Get a CPU's performance controls.
> * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cde6202e9c51..a425ad575aa6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
> return count;
> }
>
> +static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
> +{
> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
> +}
> +
> +static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, 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(min_perf);
> cpufreq_freq_attr_rw(max_perf);
> +cpufreq_freq_attr_rw(perf_limited);
>
> static struct freq_attr *cppc_cpufreq_attr[] = {
> &freqdomain_cpus,
> @@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
> &energy_performance_preference_val,
> &min_perf,
> &max_perf,
> + &perf_limited,
> NULL,
> };
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index be7de1222eee..8baff46f2ac7 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
> extern int cppc_set_min_perf(int cpu, u64 min_perf);
> extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> extern int cppc_set_max_perf(int cpu, u64 max_perf);
> +extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
> +extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
> extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
> extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
> extern int amd_detect_prefcore(bool *detected);
> @@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
> {
> return -EOPNOTSUPP;
> }
> +static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2025-11-27 14:53 ` Pierre Gondois
@ 2025-11-28 14:01 ` Sumit Gupta
2025-11-28 15:05 ` Pierre Gondois
0 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-28 14:01 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-tegra, linux-kernel, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, gautham.shenoy, ray.huang, rdunlap,
zhenglifeng1, lenb, robert.moore, rafael, viresh.kumar, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, corbet,
sumitg
Hi Pierre,
On 27/11/25 20:23, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sumit,
>
> Sorry for the late review.
> I think it would be nice to split the patchset in 2 as handling the
> auto_sel and
> min/max_perf values in the cppc_cpufreq driver might lead to more
> discussion.
> I.e. the ACPI: CPPC: XXX patches should be straightforward to upstream.
> This is just a personal opinion, feel free to ignore it.
>
I think its better to keep the changes in one patchset for better context.
I agree that the 'ACPI: CPPC: XX' patches can be applied first if ACK'ed.
Will update the cover letter in v5 to request for taking those patches
first if no new issues and send v6 with changes on the rest of patches
if any new comments on them.
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> Add cppc_get_perf() function to read values of performance control
>> registers including desired_perf, min_perf, max_perf, and energy_perf.
>>
>> This provides a read interface to complement the existing
>> cppc_set_perf()
>> write interface for performance control registers.
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 73 ++++++++++++++++++++++++++++++++++++++++
>> include/acpi/cppc_acpi.h | 5 +++
>> 2 files changed, 78 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index ab4651205e8a..05672c30187c 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1731,6 +1731,79 @@ int cppc_set_enable(int cpu, bool enable)
>> return cppc_set_reg_val(cpu, ENABLE, enable);
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_enable);
>> +/**
>> + * cppc_get_perf - Get a CPU's performance controls.
>> + * @cpu: CPU for which to get performance controls.
>> + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
>> + *
>> + * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
>> + */
>> +int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
>> +{
>> + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> + struct cpc_register_resource *desired_perf_reg, *min_perf_reg,
>> *max_perf_reg,
>> + *energy_perf_reg;
>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0;
>> + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
>> + struct cppc_pcc_data *pcc_ss_data = NULL;
>> + int ret = 0, regs_in_pcc = 0;
>> +
>> + if (!cpc_desc) {
>> + pr_debug("No CPC descriptor for CPU:%d\n", cpu);
>> + return -ENODEV;
>> + }
>> +
>> + if (!perf_ctrls) {
>> + pr_debug("Invalid perf_ctrls pointer\n");
>> + return -EINVAL;
>> + }
>> +
>> + desired_perf_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
>> + min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
>> + max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
>> + energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>> +
>> + /* Are any of the regs PCC ?*/
>> + if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
>> + CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) {
>> + if (pcc_ss_id < 0) {
>> + pr_debug("Invalid pcc_ss_id forCPU:%d\n", cpu);
>> + return -ENODEV;
>> + }
>> + pcc_ss_data = pcc_data[pcc_ss_id];
>> + regs_in_pcc = 1;
>> + down_write(&pcc_ss_data->pcc_lock);
>> + /* Ring doorbell once to update PCC subspace */
>> + if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
>> + pr_debug("Failed to send PCC command for
>> CPU:%d, ret:%d\n", cpu, ret);
>> + ret = -EIO;
>> + goto out_err;
>> + }
>> + }
>> +
>> + /* Read optional elements if present */
>> + if (CPC_SUPPORTED(max_perf_reg))
>> + cpc_read(cpu, max_perf_reg, &max);
>> + perf_ctrls->max_perf = max;
>> +
>> + if (CPC_SUPPORTED(min_perf_reg))
>> + cpc_read(cpu, min_perf_reg, &min);
>> + perf_ctrls->min_perf = min;
>> +
>
> NIT: I think the 'desired_perf_reg' register is mandatory, so the check
> could be removed.
>
>
The register is optional when Autonomous mode is enabled.
As per CPPC spec:
"This register is optional when OSPM indicates support for CPPC2 in the
platform-wide _OSC capabilities and the Autonomous Selection Enable
register is Integer 1"
Thank you,
Sumit Gupta
>> + if (CPC_SUPPORTED(desired_perf_reg))
>> + cpc_read(cpu, desired_perf_reg, &desired_perf);
>> + perf_ctrls->desired_perf = desired_perf;
>> +
>> + if (CPC_SUPPORTED(energy_perf_reg))
>> + cpc_read(cpu, energy_perf_reg, &energy_perf);
>> + perf_ctrls->energy_perf = energy_perf;
>> +
>> +out_err:
>> + if (regs_in_pcc)
>> + up_write(&pcc_ss_data->pcc_lock);
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_get_perf);
....
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select
2025-11-27 14:53 ` Pierre Gondois
@ 2025-11-28 14:08 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-11-28 14:08 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, mario.limonciello,
ray.huang, zhenglifeng1, corbet, robert.moore, lenb, viresh.kumar,
rafael, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
sanjayc, nhartman, bbasu, rdunlap, gautham.shenoy, sumitg
On 27/11/25 20:23, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> When CPPC autonomous selection (auto_select) is enabled or disabled,
>> the policy min/max frequency limits should be updated appropriately to
>> reflect the new operating mode.
>>
>> Currently, toggling auto_select only changes the hardware register but
>> doesn't update the cpufreq policy constraints, which can lead to
>> inconsistent behavior between the hardware state and the policy limits
>> visible to userspace and other kernel components.
>>
>> When auto_select is enabled, preserve the current min/max performance
>> values to maintain user-configured limits. When disabled, the hardware
>> operates in a default mode where the OS directly controls performance,
>> so update the policy limits accordingly.
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> drivers/cpufreq/cppc_cpufreq.c | 67 ++++++++++++++++++++++++++++++++--
>> 1 file changed, 64 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>> b/drivers/cpufreq/cppc_cpufreq.c
>> index a425ad575aa6..d1b44beaddda 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -646,6 +646,26 @@ static int cppc_cpufreq_set_mperf_limit(struct
>> cpufreq_policy *policy, u64 val,
>> #define cppc_cpufreq_set_max_perf(policy, val, update_reg,
>> update_policy) \
>> cppc_cpufreq_set_mperf_limit(policy, val, update_reg,
>> update_policy, false)
>>
>> +static int cppc_cpufreq_update_autosel_val(struct cpufreq_policy
>> *policy, bool auto_sel)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + unsigned int cpu = policy->cpu;
>> + int ret;
>> +
>> + pr_debug("cpu%d, auto_selcurr:%u,new:%d\n", cpu,
>> cpu_data->perf_caps.auto_sel, auto_sel);
>> +
>> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> Would it be possible to explain why we need this mutex specifically for
> auto_sel ?
Used lock to protect writes to auto_sel related registers (min_perf,
max_perf, epp, auto_sel) of a CPU from concurrent update by multiple
CPUs. Re-looked it and it seems better to take lock at parent function
and make sure all register writes happen together. That will prevent
concurrent sysfs operations from different CPUs from interleaving.
>> +
>> + ret = cppc_set_auto_sel(cpu, auto_sel);
>> + if (ret) {
>> + pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n",
>> auto_sel, cpu, ret);
>> + return ret;
>> + }
>> + cpu_data->perf_caps.auto_sel = auto_sel;
>> +
>> + return 0;
>> +}
>> +
>> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>> {
>> unsigned int cpu = policy->cpu;
>> @@ -879,8 +899,49 @@ static ssize_t show_auto_select(struct
>> cpufreq_policy *policy, char *buf)
>> return sysfs_emit(buf, "%d\n", val);
>> }
>>
>> -static ssize_t store_auto_select(struct cpufreq_policy *policy,
>> - const char *buf, size_t count)
>> +/**
>> + * cppc_cpufreq_update_auto_select - Update autonomous selection
>> config for policy->cpu
>> + * @policy: cpufreq policy
>> + * @enable: enable/disable autonomous selection
>> + */
>> +static int cppc_cpufreq_update_auto_select(struct cpufreq_policy
>> *policy, bool enable)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + 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)
>> + min_perf = cpu_data->perf_ctrls.min_perf;
>> + if (cpu_data->perf_ctrls.max_perf)
>> + max_perf = cpu_data->perf_ctrls.max_perf;
>> + }
>
> I think the min/max performance values are still relevant when
> auto_sel is
> disabled/absent. So:
> - enabling/disabling autonomous selection
> - setting min/max perf values
> should not have any dependency I think.
>
When enable auto_sel, update both the min/max_perf registers and policy
limits with respective values.
When disable, update only policy limits with the default mode values
and not update the min/max_perf registers. These register values last set
by user are kept and used to update policy limits when auto_sel is enabled
next time.
Thank you,
Sumit Gupta
>
>> +
>> + /*
>> + * 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;
>> +}
>> +
>> +static ssize_t store_auto_select(struct cpufreq_policy *policy,
>> const char *buf, size_t count)
>> {
>> bool val;
>> int ret;
>> @@ -889,7 +950,7 @@ static ssize_t store_auto_select(struct
>> cpufreq_policy *policy,
>> if (ret)
>> return ret;
>>
>> - ret = cppc_set_auto_sel(policy->cpu, val);
>> + ret = cppc_cpufreq_update_auto_select(policy, val);
>> if (ret)
>> return ret;
>>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-27 14:53 ` Pierre Gondois
@ 2025-11-28 14:29 ` Sumit Gupta
2025-11-28 15:05 ` Pierre Gondois
0 siblings, 1 reply; 42+ messages in thread
From: Sumit Gupta @ 2025-11-28 14:29 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, corbet, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, ray.huang, rdunlap, zhenglifeng1, robert.moore,
lenb, viresh.kumar, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, rafael, gautham.shenoy,
sumitg
On 27/11/25 20:23, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>> 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@infradead.org>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> .../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);
> Do we need to take the mutex ? Or is it reserved to auto_sel ?
Will move this to parent function.
Explained more in reply of the previous patch '7/8'.
>> +
>> + 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);
> The CPPC enable register is optional.
> However this doesn't mean CPPC is not working.
Ya, changed this in v5.
>> + if (ret) {
>> + 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);
> Isn't the EPP optional ?
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.
>> + 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
....
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2025-11-28 14:01 ` Sumit Gupta
@ 2025-11-28 15:05 ` Pierre Gondois
0 siblings, 0 replies; 42+ messages in thread
From: Pierre Gondois @ 2025-11-28 15:05 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-tegra, linux-kernel, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, gautham.shenoy, ray.huang, rdunlap,
zhenglifeng1, lenb, robert.moore, rafael, viresh.kumar, treding,
jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu, corbet
Hello Sumit,
On 11/28/25 15:01, Sumit Gupta wrote:
> Hi Pierre,
>
>
> On 27/11/25 20:23, Pierre Gondois wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> Hello Sumit,
>>
>> Sorry for the late review.
>> I think it would be nice to split the patchset in 2 as handling the
>> auto_sel and
>> min/max_perf values in the cppc_cpufreq driver might lead to more
>> discussion.
>> I.e. the ACPI: CPPC: XXX patches should be straightforward to upstream.
>> This is just a personal opinion, feel free to ignore it.
>>
>
> I think its better to keep the changes in one patchset for better
> context.
> I agree that the 'ACPI: CPPC: XX' patches can be applied first if ACK'ed.
> Will update the cover letter in v5 to request for taking those patches
> first if no new issues and send v6 with changes on the rest of patches
> if any new comments on them.
Ok sure
>
>
>>
>> On 11/5/25 12:38, Sumit Gupta wrote:
>>> Add cppc_get_perf() function to read values of performance control
>>> registers including desired_perf, min_perf, max_perf, and energy_perf.
>>>
>>> This provides a read interface to complement the existing
>>> cppc_set_perf()
>>> write interface for performance control registers.
>>>
>>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>>> ---
>>> drivers/acpi/cppc_acpi.c | 73
>>> ++++++++++++++++++++++++++++++++++++++++
>>> include/acpi/cppc_acpi.h | 5 +++
>>> 2 files changed, 78 insertions(+)
>>>
>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>> index ab4651205e8a..05672c30187c 100644
>>> --- a/drivers/acpi/cppc_acpi.c
>>> +++ b/drivers/acpi/cppc_acpi.c
>>> @@ -1731,6 +1731,79 @@ int cppc_set_enable(int cpu, bool enable)
>>> return cppc_set_reg_val(cpu, ENABLE, enable);
>>> }
>>> EXPORT_SYMBOL_GPL(cppc_set_enable);
>>> +/**
>>> + * cppc_get_perf - Get a CPU's performance controls.
>>> + * @cpu: CPU for which to get performance controls.
>>> + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
>>> + *
>>> + * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
>>> + */
>>> +int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
>>> +{
>>> + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>>> + struct cpc_register_resource *desired_perf_reg, *min_perf_reg,
>>> *max_perf_reg,
>>> + *energy_perf_reg;
>>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0;
>>> + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
>>> + struct cppc_pcc_data *pcc_ss_data = NULL;
>>> + int ret = 0, regs_in_pcc = 0;
>>> +
>>> + if (!cpc_desc) {
>>> + pr_debug("No CPC descriptor for CPU:%d\n", cpu);
>>> + return -ENODEV;
>>> + }
>>> +
>>> + if (!perf_ctrls) {
>>> + pr_debug("Invalid perf_ctrls pointer\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + desired_perf_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
>>> + min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
>>> + max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
>>> + energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>>> +
>>> + /* Are any of the regs PCC ?*/
>>> + if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
>>> + CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) {
>>> + if (pcc_ss_id < 0) {
>>> + pr_debug("Invalid pcc_ss_id forCPU:%d\n", cpu);
>>> + return -ENODEV;
>>> + }
>>> + pcc_ss_data = pcc_data[pcc_ss_id];
>>> + regs_in_pcc = 1;
>>> + down_write(&pcc_ss_data->pcc_lock);
>>> + /* Ring doorbell once to update PCC subspace */
>>> + if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
>>> + pr_debug("Failed to send PCC command for
>>> CPU:%d, ret:%d\n", cpu, ret);
>>> + ret = -EIO;
>>> + goto out_err;
>>> + }
>>> + }
>>> +
>>> + /* Read optional elements if present */
>>> + if (CPC_SUPPORTED(max_perf_reg))
>>> + cpc_read(cpu, max_perf_reg, &max);
>>> + perf_ctrls->max_perf = max;
>>> +
>>> + if (CPC_SUPPORTED(min_perf_reg))
>>> + cpc_read(cpu, min_perf_reg, &min);
>>> + perf_ctrls->min_perf = min;
>>> +
>>
>> NIT: I think the 'desired_perf_reg' register is mandatory, so the check
>> could be removed.
>>
>>
>
> The register is optional when Autonomous mode is enabled.
> As per CPPC spec:
> "This register is optional when OSPM indicates support for CPPC2 in the
> platform-wide _OSC capabilities and the Autonomous Selection Enable
> register is Integer 1"
Yes, right indeed,
Regards,
Pierre
>
> Thank you,
> Sumit Gupta
>
>
>>> + if (CPC_SUPPORTED(desired_perf_reg))
>>> + cpc_read(cpu, desired_perf_reg, &desired_perf);
>>> + perf_ctrls->desired_perf = desired_perf;
>>> +
>>> + if (CPC_SUPPORTED(energy_perf_reg))
>>> + cpc_read(cpu, energy_perf_reg, &energy_perf);
>>> + perf_ctrls->energy_perf = energy_perf;
>>> +
>>> +out_err:
>>> + if (regs_in_pcc)
>>> + up_write(&pcc_ss_data->pcc_lock);
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(cppc_get_perf);
>
> ....
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-28 14:29 ` Sumit Gupta
@ 2025-11-28 15:05 ` Pierre Gondois
2025-12-01 14:09 ` Sumit Gupta
0 siblings, 1 reply; 42+ messages in thread
From: Pierre Gondois @ 2025-11-28 15:05 UTC (permalink / raw)
To: Sumit Gupta
Cc: linux-kernel, corbet, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, ray.huang, rdunlap, zhenglifeng1, robert.moore,
lenb, viresh.kumar, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, rafael, gautham.shenoy
Hello Sumit,
On 11/28/25 15:29, Sumit Gupta wrote:
>
> On 27/11/25 20:23, Pierre Gondois wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 11/5/25 12:38, Sumit Gupta wrote:
>>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>>> 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@infradead.org>
>>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>>> ---
>>> .../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);
>> Do we need to take the mutex ? Or is it reserved to auto_sel ?
>
> Will move this to parent function.
> Explained more in reply of the previous patch '7/8'.
>
>>> +
>>> + 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);
>> The CPPC enable register is optional.
>> However this doesn't mean CPPC is not working.
>
> Ya, changed this in v5.
>
>>> + if (ret) {
>>> + 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);
>> Isn't the EPP optional ?
>
> 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) ?
>
>
>>> + 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
>
> ....
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support
2025-11-28 15:05 ` Pierre Gondois
@ 2025-12-01 14:09 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-12-01 14:09 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, corbet, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, ray.huang, rdunlap, zhenglifeng1, robert.moore,
lenb, viresh.kumar, linux-tegra, treding, jonathanh, vsethi,
ksitaraman, sanjayc, nhartman, bbasu, rafael, gautham.shenoy,
sumitg
>>>
>>>
>>> On 11/5/25 12:38, Sumit Gupta wrote:
>>>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>>>> 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@infradead.org>
>>>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>>>> ---
>>>> .../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);
>>> Do we need to take the mutex ? Or is it reserved to auto_sel ?
>>
>> Will move this to parent function.
>> Explained more in reply of the previous patch '7/8'.
>>
>>>> +
>>>> + 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);
>>> The CPPC enable register is optional.
>>> However this doesn't mean CPPC is not working.
>>
>> Ya, changed this in v5.
>>
>>>> + if (ret) {
>>>> + 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);
>>> Isn't the EPP optional ?
>>
>> 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
>>
>> ....
>>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2025-11-27 14:54 ` Pierre Gondois
@ 2025-12-09 16:38 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-12-09 16:38 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, mario.limonciello,
gautham.shenoy, rdunlap, zhenglifeng1, corbet, robert.moore, lenb,
viresh.kumar, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
sanjayc, nhartman, bbasu, rafael, ray.huang, sumitg
On 27/11/25 20:24, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> CPPC allows platforms to specify minimum and maximum performance
>> limits that constrain the operating range for CPU performance scaling
>> when Autonomous Selection is enabled. These limits can be dynamically
>> adjusted to implement power management policies or workload-specific
>> optimizations.
>>
>> Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
>> write the MIN_PERF register, allowing dynamic adjustment of the minimum
>> performance floor.
>>
>> Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
>> write the MAX_PERF register, enabling dynamic ceiling control for
>> maximum performance.
>>
>> Expose these capabilities through cpufreq sysfs attributes that accept
>> frequency values in kHz (which are converted to/from performance values
>> internally):
>> - /sys/.../cpufreq/policy*/min_perf: Read/write min perf as freq (kHz)
>> - /sys/.../cpufreq/policy*/max_perf: Read/write max perf as freq (kHz)
>>
>> The frequency-based interface provides a user-friendly abstraction which
>> is similar to other cpufreq sysfs interfaces, while the driver handles
>> conversion to hardware performance values.
>>
>> Also update EPP constants for better clarity:
>> - Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
>> - Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 55 ++++++++++-
>> drivers/cpufreq/cppc_cpufreq.c | 166 +++++++++++++++++++++++++++++++++
>> include/acpi/cppc_acpi.h | 23 ++++-
>> 3 files changed, 242 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 757e8ce87e9b..ef53eb8a1feb 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1634,7 +1634,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
>> */
>> int cppc_set_epp(int cpu, u64 epp_val)
>> {
>> - if (epp_val > CPPC_ENERGY_PERF_MAX)
>> + if (epp_val > CPPC_EPP_ENERGY_EFFICIENCY_PREF)
>> return -EINVAL;
>>
>> return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val);
>> @@ -1757,6 +1757,59 @@ int cppc_set_enable(int cpu, bool enable)
>> return cppc_set_reg_val(cpu, ENABLE, enable);
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_enable);
>> +
>> +/**
>> + * cppc_get_min_perf - Get the min performance register value.
>> + * @cpu: CPU from which to get min performance.
>> + * @min_perf: Return address.
>> + *
>> + * Return: 0 for success, -EIO on register access failure,
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_get_min_perf(int cpu, u64 *min_perf)
>> +{
>> + return cppc_get_reg_val(cpu, MIN_PERF, min_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_get_min_perf);
>> +
>> +/**
>> + * cppc_set_min_perf() - Write the min performance register.
>> + * @cpu: CPU on which to write register.
>> + * @min_perf: Value to write to the MIN_PERF register.
>> + *
>> + * Return: 0 for success, -EIO otherwise.
>> + */
>> +int cppc_set_min_perf(int cpu, u64 min_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
>> +
>> +/**
>> + * cppc_get_max_perf - Get the max performance register value.
>> + * @cpu: CPU from which to get max performance.
>> + * @max_perf: Return address.
>> + *
>> + * Return: 0 for success, -EIO on register access failure,
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_get_max_perf(int cpu, u64 *max_perf)
>> +{
>> + return cppc_get_reg_val(cpu, MAX_PERF, max_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_get_max_perf);
>> +
>> +/**
>> + * cppc_set_max_perf() - Write the max performance register.
>> + * @cpu: CPU on which to write register.
>> + * @max_perf: Value to write to the MAX_PERF register.
>> + *
>> + * Return: 0 for success, -EIO otherwise.
>> + */
>> +int cppc_set_max_perf(int cpu, u64 max_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>> +
>> /**
>> * cppc_get_perf - Get a CPU's performance controls.
>> * @cpu: CPU for which to get performance controls.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>> b/drivers/cpufreq/cppc_cpufreq.c
>> index cf3ed6489a4f..cde6202e9c51 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -23,10 +23,12 @@
>> #include <uapi/linux/sched/types.h>
>>
>> #include <linux/unaligned.h>
>> +#include <linux/cleanup.h>
>>
>> #include <acpi/cppc_acpi.h>
>>
>> static struct cpufreq_driver cppc_cpufreq_driver;
>> +static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
>>
>> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>> static enum {
>> @@ -582,6 +584,68 @@ static void cppc_cpufreq_put_cpu_data(struct
>> cpufreq_policy *policy)
>> policy->driver_data = NULL;
>> }
>>
>> +/**
>> + * cppc_cpufreq_set_mperf_limit - Generic function to set min/max
>> performance limit
>> + * @policy: cpufreq policy
>> + * @val: performance value to set
>> + * @update_reg: whether to update hardware register
>
> I m not sure I see in which case we might not want to update the
> hardware register.
> Isn't the min/max_perf values relevant even when autonomous selection is
> disabled/absent ?
>
Explained in reply on 'patch 7/8'. Adding here also brief info.
When disabling auto_sel, only the policy limits are reset, the
min/max_perf registers are preserved.
When re-enabled, these preserved values are restored to both
hardware reg and policy.
>
>> + * @update_policy: whether to update policy constraints
>> + * @is_min: true for min_perf, false for max_perf
>> + */
>> +static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy
>> *policy, u64 val,
>> + bool update_reg, bool
>> update_policy, bool is_min)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
>> + unsigned int cpu = policy->cpu;
>> + struct freq_qos_request *req;
>> + unsigned int freq;
>> + u32 perf;
>> + int ret;
>> +
>> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
>> + freq = cppc_perf_to_khz(caps, perf);
>> +
>> + pr_debug("cpu%d, %s_perf:%llu, update_reg:%d,
>> update_policy:%d\n", cpu,
>> + is_min ? "min" : "max", (u64)perf, update_reg,
>> update_policy);
>> +
>> + guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
>> +
>> + if (update_reg) {
>> + ret = is_min ? cppc_set_min_perf(cpu, perf) :
>> cppc_set_max_perf(cpu, perf);
>> + if (ret) {
>> + if (ret != -EOPNOTSUPP)
>> + pr_warn("Failed to set %s_perf (%llu)
>> on CPU%d (%d)\n",
>> + is_min ? "min" : "max",
>> (u64)perf, cpu, ret);
>> + return ret;
>> + }
>> +
>> + if (is_min)
>> + cpu_data->perf_ctrls.min_perf = perf;
>> + else
>> + cpu_data->perf_ctrls.max_perf = perf;
>> + }
>> +
>> + if (update_policy) {
>> + req = is_min ? policy->min_freq_req :
>> policy->max_freq_req;
>> +
>> + ret = freq_qos_update_request(req, freq);
>
> IIUC, we are adding a qos constraint to the min_freq_req or
> max_freq_req. However these constraints should match the
> scaling_min/max_freq sysfs interface. So doesn't it mean that if we set
> the 'max_perf', we are overwriting the the max_freq_req constraint ?
>
Yes.
> If you have frequencies between 600000:1200000 # Init state:
> max_perf:1200000 scaling_max_freq:1200000 # echo 10000000 > max_perf
> max_perf:1000000 scaling_max_freq:1000000 # echo 900000 >
> scaling_max_freq max_perf:1000000 scaling_max_freq:900000 # echo 1200000
> > scaling_max_freq max_perf:1000000 scaling_max_freq:1200000
>
> The 2 values are not in sync. Is it the desired behaviour ?
>
>
Making scaling_min/max_freq read-only in auto_sel mode will solve this.
We can do this by setting policy limits to min/max_perf bounds in
cppc_verify_policy() when the auto_sel is enabled.
In autonomous mode, the hardware controls performance within these
bounds, so scaling_min/max_freq is effectively read-only.
Users must use min_perf/max_perf sysfs to change limits.
Please share if you have different thoughts or another approach.
cppc_verify_policy(struct cpufreq_policy_data *policy_data)
{
...
if (caps->auto_sel) {
min_perf = cpu_data->perf_ctrls.min_perf ?:
caps->lowest_nonlinear_perf;
max_perf = cpu_data->perf_ctrls.max_perf ?: caps->nominal_perf;
/* set min/max_perf bounds (read-only behavior) */
policy_data->min = cppc_perf_to_khz(caps, min_perf);
policy_data->max = cppc_perf_to_khz(caps, max_perf);
} else {
cpufreq_verify_within_limits(policy_data, min_freq, max_freq);
}
....
}
>> + if (ret < 0) {
>> + pr_warn("Failed to update %s_freq constraint
>> for CPU%d: %d\n",
>> + is_min ? "min" : "max", cpu, ret);
>> + return ret;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +#define cppc_cpufreq_set_min_perf(policy, val, update_reg,
>> update_policy) \
>> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg,
>> update_policy, true)
>> +
>> +#define cppc_cpufreq_set_max_perf(policy, val, update_reg,
>> update_policy) \
>> + cppc_cpufreq_set_mperf_limit(policy, val, update_reg,
>> update_policy, false)
>> +
>> static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>> {
>> unsigned int cpu = policy->cpu;
>> @@ -881,16 +945,118 @@ static ssize_t
>> store_energy_performance_preference_val(struct cpufreq_policy *po
>> return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp,
>> buf, count);
>> }
>>
>> +/**
>> + * show_min_perf - Show minimum performance as frequency (kHz)
>> + *
>> + * Reads the MIN_PERF register and converts the performance value to
>> + * frequency (kHz) for user-space consumption.
>> + */
>> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + u64 perf;
>> + int ret;
>> +
>> + ret = cppc_get_min_perf(policy->cpu, &perf);
>> + if (ret == -EOPNOTSUPP)
>> + return sysfs_emit(buf, "<unsupported>\n");
>> + if (ret)
>> + return ret;
>> +
>> + /* Convert performance to frequency (kHz) for user */
>> + return sysfs_emit(buf, "%u\n",
>> cppc_perf_to_khz(&cpu_data->perf_caps, perf));
>> +}
>> +
>> +/**
>> + * store_min_perf - Set minimum performance from frequency (kHz)
>> + *
>> + * Converts the user-provided frequency (kHz) to a performance value
>> + * and writes it to the MIN_PERF register.
>> + */
>> +static ssize_t store_min_perf(struct cpufreq_policy *policy, const
>> char *buf, size_t count)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + unsigned int freq_khz;
>> + u64 perf;
>> + int ret;
>> +
>> + ret = kstrtouint(buf, 0, &freq_khz);
>> + if (ret)
>> + return ret;
>> +
>> + /* Convert frequency (kHz) to performance value */
>> + perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>> +
>> + ret = cppc_cpufreq_set_min_perf(policy, perf, true,
>> cpu_data->perf_caps.auto_sel);
>> + if (ret)
>> + return ret;
>> +
>> + return count;
>> +}
>> +
>> +/**
>> + * show_max_perf - Show maximum performance as frequency (kHz)
>> + *
>> + * Reads the MAX_PERF register and converts the performance value to
>> + * frequency (kHz) for user-space consumption.
>> + */
>> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
>
> I think it might collide with the scaling_min/max_freq.
> I saw that you answered this point at:
> https://lore.kernel.org/lkml/b2bd3258-51bd-462a-ae29-71f1d6f823f3@nvidia.com/
>
>
> But I m not sure I understood why it is needed to have 2 interfaces.
> Would it be possible to explain it again ?
Separate interface for min/max_perf are kept because we are writing
to different CPPC hardware registers with that name.
>
> I don't see any case where we would like to make a distinction between:
> - scaling_max_freq, i.e. the maximal freq. the cpufreq driver is allowed
> to set
> - max_perf, i.e. the maximal perf. level the firmware will set
>
> ------------
>
> Another point is that the min/max_perf interface actually uses freq.
> values.
Changed the min/max_perf interfaces from perf to freq to sync their scale
with other cpufreq sysfs interfaces after discussion in [1].
[1]
https://lore.kernel.org/lkml/80e16de0-63e4-4ead-9577-4ebba9b1a02d@nvidia.com/
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
2025-11-27 14:54 ` Pierre Gondois
@ 2025-12-09 17:22 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-12-09 17:22 UTC (permalink / raw)
To: Pierre Gondois
Cc: linux-tegra, linux-kernel, acpica-devel, linux-doc, linux-acpi,
linux-pm, zhanjie9, ionela.voinescu, perry.yuan,
mario.limonciello, gautham.shenoy, ray.huang, rdunlap,
zhenglifeng1, corbet, robert.moore, lenb, viresh.kumar, rafael,
treding, jonathanh, vsethi, ksitaraman, sanjayc, nhartman, bbasu,
sumitg
On 27/11/25 20:24, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> Add sysfs interface to read/write the Performance Limited register.
>>
>> The Performance Limited register indicates to the OS that an
>> unpredictable event (like thermal throttling) has limited processor
>> performance. This register is sticky and remains set until reset or
>> OS clears it by writing 0.
>>
>> The interface is exposed as:
>> /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 26 ++++++++++++++++++++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
>> include/acpi/cppc_acpi.h | 10 ++++++++++
>> 3 files changed, 48 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index ef53eb8a1feb..9b8da3ef06db 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>>
>> +/**
>> + * cppc_get_perf_limited - Get the Performance Limited register value.
>> + * @cpu: CPU from which to get Performance Limited register.
>> + * @perf_limited: Pointer to store the Performance Limited value.
>> + *
>> + * Return: 0 for success, -EIO on register access failure,
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
>> +{
>> + return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
>> +
>> +/**
>> + * cppc_set_perf_limited() - Write the Performance Limited register.
>> + * @cpu: CPU on which to write register.
>> + * @perf_limited: Value to write to the perf_limited register.
>> + *
>> + * Return: 0 for success, -EIO on register access failure,
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
>> +{
>> + return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
>> +}
>
> There are currently only 2 bits used:
> - 0 Desired_Excursion
> - 1 Minimum_Excursion
> It might be worth defining these bits and mask the values when trying to
> set the register.
>
Will do the change to allow only the clearing of these two bits and not
setting.
> ------
>
> Also NIT:
>
> The spec. says:
> " All accesses to the Performance Limited Register must be made using
> interlocked operations, by both accessing entities."
>
> I am not sure I understand which synchronization issues are faced.
> It's just to report the comment from the spec.
>
cpc_write() already has locking. So, two writes will complete without
interference.
As you suggested above, only the required register bit will be cleared
by an operation.
Thank you,
Sumit Gupta
....
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp
2025-11-27 14:54 ` Pierre Gondois
@ 2025-12-09 18:10 ` Sumit Gupta
0 siblings, 0 replies; 42+ messages in thread
From: Sumit Gupta @ 2025-12-09 18:10 UTC (permalink / raw)
To: Pierre Gondois, mario.limonciello
Cc: linux-kernel, acpica-devel, linux-doc, linux-acpi, linux-pm,
zhanjie9, ionela.voinescu, perry.yuan, gautham.shenoy, ray.huang,
zhenglifeng1, corbet, lenb, robert.moore, viresh.kumar, rafael,
linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, rdunlap, sumitg
On 27/11/25 20:24, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> Hello Sumit, Mario,
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> - Add auto_sel read support in cppc_get_perf_caps().
>> - Add write of both auto_sel and energy_perf in cppc_set_epp_perf().
>> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
>> the same is available in 'struct cppc_perf_ctrls' which is used.
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 42 ++++++++++++++++++++++++++++++++--------
>> include/acpi/cppc_acpi.h | 1 -
>> 2 files changed, 34 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 05672c30187c..757e8ce87e9b 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct
>> cppc_perf_caps *perf_caps)
>> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
>> struct cpc_register_resource *highest_reg, *lowest_reg,
>> *lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
>> - *low_freq_reg = NULL, *nom_freq_reg = NULL;
>> - u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f
>> = 0;
>> + *low_freq_reg = NULL, *nom_freq_reg = NULL,
>> *auto_sel_reg = NULL;
>> + u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f
>> = 0, auto_sel = 0;
>
> I am not sure this is really useful to get the auto_sel value register
> in this function as:
> - All of the other registers read are read-only
> - The name of the function doesn't match: the autonomous selection is
> not really
> related to perf. capabilities
>
> I assume this change comes from the presence of the auto_sel register
> in the
> 'struct cppc_perf_caps', but IMO this register should be placed in
> another structure.
>
> I assume this is ok to let it in 'struct cppc_perf_caps' for now, but I
> think we should not
> fetch the value with all the other perf. capabilities values.
>
Yes, reading it here in cppc_get_perf_caps() as it was part of
'struct cppc_perf_caps' in the existing code which i agree looks wrong.
I am adding cppc_get_perf() api to read performance controls in 'patch
2/8'.
Additionally, I can move the 'auto_sel' field from 'struct cppc_perf_caps'
to 'struct cppc_perf_ctrls' and read it within cppc_get_perf() with other
performance controls.
>
>> int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
>> struct cppc_pcc_data *pcc_ss_data = NULL;
>> int ret = 0, regs_in_pcc = 0;
>> @@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct
>> cppc_perf_caps *perf_caps)
>> low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
>> nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
>> guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>>
>> /* Are any of the regs PCC ?*/
>> if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
>> CPC_IN_PCC(lowest_non_linear_reg) ||
>> CPC_IN_PCC(nominal_reg) ||
>> - CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
>> + CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) ||
>> CPC_IN_PCC(auto_sel_reg)) {
>> if (pcc_ss_id < 0) {
>> pr_debug("Invalid pcc_ss_id\n");
>> return -ENODEV;
>> @@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct
>> cppc_perf_caps *perf_caps)
>> perf_caps->lowest_freq = low_f;
>> perf_caps->nominal_freq = nom_f;
>>
>> + if (CPC_SUPPORTED(auto_sel_reg))
>> + cpc_read(cpunum, auto_sel_reg, &auto_sel);
>> + perf_caps->auto_sel = (bool)auto_sel;
>>
>> out_err:
>> if (regs_in_pcc)
>> @@ -1555,6 +1559,8 @@ int cppc_set_epp_perf(int cpu, struct
>> cppc_perf_ctrls *perf_ctrls, bool enable)
>> struct cpc_register_resource *auto_sel_reg;
>> struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
>> struct cppc_pcc_data *pcc_ss_data = NULL;
>> + bool autosel_support_in_ffh_or_sysmem;
>> + bool epp_support_in_ffh_or_sysmem;
>> int ret;
>>
>> if (!cpc_desc) {
>> @@ -1565,6 +1571,11 @@ int cppc_set_epp_perf(int cpu, struct
>> cppc_perf_ctrls *perf_ctrls, bool enable)
>> auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>> epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
>>
>> + epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
>> + (CPC_IN_FFH(epp_set_reg) ||
>> CPC_IN_SYSTEM_MEMORY(epp_set_reg));
>> + autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
>> + (CPC_IN_FFH(auto_sel_reg) ||
>> CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
>> +
>> if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
>> if (pcc_ss_id < 0) {
>> pr_debug("Invalid pcc_ss_id forCPU:%d\n", cpu);
>> @@ -1589,14 +1600,29 @@ int cppc_set_epp_perf(int cpu, struct
>> cppc_perf_ctrls *perf_ctrls, bool enable)
>> /* after writing CPC, transfer the ownership of PCC to
>> platform */
>> ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
>> up_write(&pcc_ss_data->pcc_lock);
>> - } else if (osc_cpc_flexible_adr_space_confirmed &&
>> - CPC_SUPPORTED(epp_set_reg) &&
>> CPC_IN_FFH(epp_set_reg)) {
>> - ret = cpc_write(cpu, epp_set_reg,
>> perf_ctrls->energy_perf);
>
> I think this is a bit out of the scope of this patchset, but I'm not
> sure this is necessary
> to check the value of "osc_cpc_flexible_adr_space_confirmed" here.
> Indeed, acpi_cppc_processor_probe() already loops over the CPPC fields
> and should detect when a field is using an address space that is not
> allowed by "osc_cpc_flexible_adr_space_confirmed".
>
> From what I understand:
>
> - osc_cpc_flexible_adr_space_confirmed was introduced to check that CPPC
> registers are in the correct address space
>
> - this broke some amd platforms that didn't configure the _OSC method
> correctly
>
> - 8b356e536e69 ("ACPI: CPPC: Don't require _OSC if X86_FEATURE_CPPC is
> supported") introduced cpc_supported_by_cpu() to bypass the check of
> osc_cpc_flexible_adr_space_confirmed. Indeed, the broken amd platforms
> don't configure the _OSC method, but it is possible to check if there is
> CPPC support by reading an MSR register.
>
> - an amd platform failed to set the EPP register. This seems to be due
> to the EPP register being located in FFH and not in PCC. However the
> handler only supported PCC at that time: 7bc1fcd39901 ("ACPI: CPPC: Add
> AMD pstate energy performance preference cppc control") The bug report
> thread: bugzilla.kernel.org/show_bug.cgi?id=218686
>
> - to allow setting the EPP value when it is located in FFH, the
> following patch was done: aaf21ac93909 ("ACPI: CPPC: Add support for
> setting EPP register in FFH") This patch seems to have added a check
> over the _OSC flexible bit value due to this comment:
> https://bugzilla.kernel.org/show_bug.cgi?id=218686#c83 However the CPPC
> registers are always allowed to be located in the FFH and PCC address
> space. Cf: 0651ab90e4ad ("ACPI: CPPC: Check _OSC for flexible address
> space")
>
> ------------
>
> Just to summarize, I think the check over
> osc_cpc_flexible_adr_space_confirmed could/should be removed. Ideally in
> a separate patch.
>
> If Mario could confirm this is correct this would be nice.
>
Can send a separate patch with this change.
Thank you,
Sumit Gupta
>
>> + } else if (osc_cpc_flexible_adr_space_confirmed) {
>> + if (!epp_support_in_ffh_or_sysmem &&
>> !autosel_support_in_ffh_or_sysmem) {
>> + ret = -EOPNOTSUPP;
>> + } else {
>> + if (autosel_support_in_ffh_or_sysmem) {
>> + ret = cpc_write(cpu, auto_sel_reg,
>> enable);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (epp_support_in_ffh_or_sysmem) {
>> + ret = cpc_write(cpu, epp_set_reg,
>> perf_ctrls->energy_perf);
>> + if (ret)
>> + return ret;
>> + }
>> + }
>> } else {
>> - ret = -ENOTSUPP;
>> - pr_debug("_CPC in PCC and _CPC in FFH are not
>> supported\n");
>> + ret = -EOPNOTSUPP;
>> }
>>
>> + if (ret == -EOPNOTSUPP)
>> + pr_debug("_CPC in PCC and _CPC in FFH are not
>> supported\n");
>> +
>> return ret;
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 7190afeead8b..42e37a84cac9 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -119,7 +119,6 @@ struct cppc_perf_caps {
>> u32 lowest_nonlinear_perf;
>> u32 lowest_freq;
>> u32 nominal_freq;
>> - u32 energy_perf;
>> bool auto_sel;
>> };
>>
^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2025-12-09 18:10 UTC | newest]
Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-05 11:38 [PATCH v4 0/8] Enhanced autonomous selection and improvements Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 1/8] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2025-11-10 10:56 ` Viresh Kumar
2025-11-11 11:20 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 2/8] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:01 ` Sumit Gupta
2025-11-28 15:05 ` Pierre Gondois
2025-11-05 11:38 ` [PATCH v4 3/8] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
2025-11-12 15:02 ` Ionela Voinescu
2025-11-18 9:17 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
2025-12-09 18:10 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 4/8] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2025-11-06 10:30 ` kernel test robot
2025-11-07 10:00 ` Sumit Gupta
2025-11-07 20:08 ` Rafael J. Wysocki
2025-11-11 11:06 ` Sumit Gupta
2025-11-13 10:56 ` Ionela Voinescu
2025-11-18 9:34 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
2025-12-09 16:38 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
2025-11-13 11:35 ` Ionela Voinescu
2025-11-18 10:20 ` Sumit Gupta
2025-11-27 14:54 ` Pierre Gondois
2025-12-09 17:22 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 6/8] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
2025-11-13 12:41 ` Ionela Voinescu
2025-11-18 10:46 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 7/8] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:08 ` Sumit Gupta
2025-11-05 11:38 ` [PATCH v4 8/8] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
2025-11-13 15:15 ` Ionela Voinescu
2025-11-26 13:32 ` Sumit Gupta
2025-11-27 14:53 ` Pierre Gondois
2025-11-28 14:29 ` Sumit Gupta
2025-11-28 15:05 ` Pierre Gondois
2025-12-01 14:09 ` Sumit Gupta
2025-11-10 11:00 ` [PATCH v4 0/8] Enhanced autonomous selection and improvements Viresh Kumar
2025-11-18 8:45 ` Jie Zhan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).