* [PATCH v6 0/9] Enhanced autonomous selection and improvements
@ 2026-01-20 14:56 Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
` (8 more replies)
0 siblings, 9 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
As discussed in [6], v5 is split into two parts. This is part 1
(v5 patches 1-7) and can be applied independently. I will follow up
separately with part 2 (v5 patches 8-11).
This patch series adds sysfs interfaces for CPPC min_perf, max_perf,
and perf_limited registers, along with supporting ACPI APIs and
improvements for the cppc_cpufreq driver.
CPPC autonomous mode (auto_sel) enables hardware-driven CPU performance
scaling using Energy Performance Preference (EPP) hints. Currently,
there's limited runtime control and visibility into CPPC performance
registers.
This series addresses these gaps by:
1. Exposing min_perf/max_perf registers via sysfs (as frequency in kHz)
to allow fine-grained performance bounds control in autonomous mode.
2. Exposing perf_limited register to detect and clear throttling events.
It also includes code improvements: generic sysfs helpers, struct
cleanup, new APIs for reading performance controls, and extended
epp_perf support.
The patches are grouped as below:
- Patch 1: Generic sysfs helpers (refactoring, independent).
- Patch 2-4: Code improvements (can be applied independently).
- Patch 5: Extend cppc_set_epp_perf for FFH/SystemMemory (independent).
- Patch 6-7: APIs and sysfs for min/max_perf, perf_limited (independent).
- Patch 8: ABI documentation (depends on patch 6-7).
- Patch 9: Update cached perf_ctrls on sysfs write (depends on patch 6).
---
v5[5] -> v6:
- Split patchset as described above.
- patch1: added CPPC_CPUFREQ_ATTR_RW_U64 macro for sysfs attributes.
- patch3: new patch to rename EPP constants for clarity.
- patch5: simplified else block in cppc_set_epp_perf().
- patch6: fallback to caps values if registers are uninitialized.
- patch7: use CPPC_CPUFREQ_ATTR_RW_U64 macro for perf_limited sysfs.
- patch9: new patch to update cached perf_ctrls on sysfs write.
- Renamed mutex to cppc_cpufreq_autonomous_lock.
Sumit Gupta (9):
cpufreq: CPPC: Add generic helpers for sysfs show/store
ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
ACPI: CPPC: Rename EPP constants for clarity
ACPI: CPPC: Add cppc_get_perf() API to read performance controls
ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
ACPI: CPPC: add APIs and sysfs interface for min/max_perf
ACPI: CPPC: add APIs and sysfs interface for perf_limited
cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
cpufreq: CPPC: Update cached perf_ctrls on sysfs write
.../ABI/testing/sysfs-devices-system-cpu | 44 ++++
drivers/acpi/cppc_acpi.c | 206 +++++++++++++++-
drivers/cpufreq/cppc_cpufreq.c | 224 ++++++++++++++++--
include/acpi/cppc_acpi.h | 46 +++-
4 files changed, 491 insertions(+), 29 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/
[4] https://lore.kernel.org/lkml/20251105113844.4086250-1-sumitg@nvidia.com/
[5] https://lore.kernel.org/lkml/20251223121307.711773-1-sumitg@nvidia.com/
[6] https://lore.kernel.org/lkml/66f58f43-631b-40a0-8d42-4e90cd24b757@arm.com/
--
2.34.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 8:27 ` zhenglifeng (A)
2026-01-27 16:24 ` Rafael J. Wysocki
2026-01-20 14:56 ` [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
` (7 subsequent siblings)
8 siblings, 2 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Add generic helper functions for u64 sysfs attributes that follow the
common pattern of calling CPPC get/set APIs:
- cppc_cpufreq_sysfs_show_u64(): reads value and handles -EOPNOTSUPP
- cppc_cpufreq_sysfs_store_u64(): parses input and calls set function
Add CPPC_CPUFREQ_ATTR_RW_U64() macro to generate show/store functions
using these helpers, reducing boilerplate for simple attributes.
Convert auto_act_window and energy_performance_preference_val to use
the new macro.
No functional changes.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 69 ++++++++++++----------------------
1 file changed, 25 insertions(+), 44 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 36e8a75a37f1..c95dcd7719c3 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -863,73 +863,54 @@ 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((int)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)
-{
- 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");
-
- if (ret)
- return ret;
-
- return sysfs_emit(buf, "%llu\n", val);
+#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
+static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
+{ \
+ return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
+} \
+static ssize_t store_##_name(struct cpufreq_policy *policy, \
+ const char *buf, size_t count) \
+{ \
+ return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
+ buf, count); \
}
-static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
- const char *buf, size_t count)
-{
- u64 val;
- int ret;
+CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
+ cppc_set_auto_act_window)
- ret = kstrtou64(buf, 0, &val);
- if (ret)
- return ret;
-
- ret = cppc_set_epp(policy->cpu, val);
- if (ret)
- return ret;
-
- return count;
-}
+CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
+ cppc_get_epp_perf, cppc_set_epp)
cpufreq_freq_attr_ro(freqdomain_cpus);
cpufreq_freq_attr_rw(auto_select);
--
2.34.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 8:28 ` zhenglifeng (A)
2026-01-27 16:27 ` Rafael J. Wysocki
2026-01-20 14:56 ` [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity Sumit Gupta
` (6 subsequent siblings)
8 siblings, 2 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
- Remove redundant energy_perf field from 'struct cppc_perf_caps' as
the same is available in 'struct cppc_perf_ctrls' which is used.
- Move the 'auto_sel' field from 'struct cppc_perf_caps' to
'struct cppc_perf_ctrls' as it represents a control register.
Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
include/acpi/cppc_acpi.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 4bcdcaf8bf2c..6573a759eb8d 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -119,8 +119,6 @@ struct cppc_perf_caps {
u32 lowest_nonlinear_perf;
u32 lowest_freq;
u32 nominal_freq;
- u32 energy_perf;
- bool auto_sel;
};
struct cppc_perf_ctrls {
@@ -128,6 +126,7 @@ struct cppc_perf_ctrls {
u32 min_perf;
u32 desired_perf;
u32 energy_perf;
+ bool auto_sel;
};
struct cppc_perf_fb_ctrs {
--
2.34.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 8:31 ` zhenglifeng (A)
2026-01-20 14:56 ` [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
` (5 subsequent siblings)
8 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Update EPP (Energy Performance Preference) constants for better clarity:
- Add CPPC_EPP_PERFORMANCE_PREF (0x00) for performance preference
- Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF (0xFF)
for energy efficiency
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 2 +-
include/acpi/cppc_acpi.h | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 22d7fd669a6c..a09bdabaa804 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1615,7 +1615,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);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 6573a759eb8d..4d644f03098e 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 {
--
2.34.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (2 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 8:56 ` zhenglifeng (A)
2026-01-20 14:56 ` [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory Sumit Gupta
` (4 subsequent siblings)
8 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, 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, energy_perf,
and auto_sel.
This provides a read interface to complement the existing
cppc_set_perf() write interface for performance control registers.
Note that auto_sel is read by cppc_get_perf() but not written by
cppc_set_perf() to avoid unintended mode changes during performance
updates. It can be updated with existing dedicated cppc_set_auto_sel()
API.
Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
perf_ctrls with current hardware register values during cpufreq
policy initialization.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 80 ++++++++++++++++++++++++++++++++++
drivers/cpufreq/cppc_cpufreq.c | 6 +++
include/acpi/cppc_acpi.h | 5 +++
3 files changed, 91 insertions(+)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index a09bdabaa804..de35aeb07833 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
+ u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 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];
+ auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
+
+ /* 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) ||
+ CPC_IN_PCC(auto_sel_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) {
+ 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;
+
+ if (CPC_SUPPORTED(auto_sel_reg))
+ cpc_read(cpu, auto_sel_reg, &auto_sel);
+ perf_ctrls->auto_sel = (bool)auto_sel;
+
+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.
* @cpu: CPU for which to set performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index c95dcd7719c3..229880c4eedb 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -594,6 +594,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:
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 4d644f03098e..3fc796c0d902 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);
@@ -193,6 +194,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] 38+ messages in thread
* [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (3 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 9:18 ` zhenglifeng (A)
2026-01-20 14:56 ` [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
` (3 subsequent siblings)
8 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Extend cppc_set_epp_perf() to write both auto_sel and energy_perf
registers when they are in FFH or SystemMemory address space.
This keeps the behavior consistent with PCC case where both registers
are already updated together, but was missing for FFH/SystemMemory.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index de35aeb07833..45c6bd6ec24b 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1562,6 +1562,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_ffh_sysmem;
+ bool epp_ffh_sysmem;
int ret;
if (!cpc_desc) {
@@ -1572,6 +1574,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_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) &&
+ (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
+ autosel_ffh_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);
@@ -1597,11 +1604,22 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
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);
+ (epp_ffh_sysmem || autosel_ffh_sysmem)) {
+ if (autosel_ffh_sysmem) {
+ ret = cpc_write(cpu, auto_sel_reg, enable);
+ if (ret)
+ return ret;
+ }
+
+ if (epp_ffh_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");
+ pr_debug("_CPC in PCC/FFH/SystemMemory are not supported\n");
}
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (4 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 11:36 ` Pierre Gondois
2026-01-22 12:35 ` zhenglifeng (A)
2026-01-20 14:56 ` [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
` (2 subsequent siblings)
8 siblings, 2 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, 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/set_min_perf() and cppc_get/set_max_perf() APIs to read and
write the MIN_PERF and MAX_PERF registers.
Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
to expose these controls to userspace. The sysfs values are in frequency
(kHz) for consistency with other cpufreq sysfs files.
A mutex is used to serialize sysfs store operations to ensure hardware
register writes and perf_ctrls updates are atomic.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/acpi/cppc_acpi.c | 44 +++++++++
drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
include/acpi/cppc_acpi.h | 20 +++++
3 files changed, 221 insertions(+)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 45c6bd6ec24b..46bf45f8b0f3 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
}
EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
+/**
+ * cppc_get_min_perf - Read minimum performance register.
+ * @cpu: CPU from which to read register.
+ * @min_perf: Return address.
+ */
+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 minimum performance register.
+ * @cpu: CPU to which to write register.
+ * @min_perf: the desired minimum performance value to be updated.
+ */
+int cppc_set_min_perf(int cpu, u32 min_perf)
+{
+ return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_min_perf);
+
+/**
+ * cppc_get_max_perf - Read maximum performance register.
+ * @cpu: CPU from which to read register.
+ * @max_perf: Return address.
+ */
+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 maximum performance register.
+ * @cpu: CPU to which to write register.
+ * @max_perf: the desired maximum performance value to be updated.
+ */
+int cppc_set_max_perf(int cpu, u32 max_perf)
+{
+ return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_max_perf);
+
/**
* cppc_set_enable - Set to enable CPPC on the processor by writing the
* Continuous Performance Control package EnableRegister field.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 229880c4eedb..66e183b45fb0 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -28,6 +28,8 @@
static struct cpufreq_driver cppc_cpufreq_driver;
+static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
+
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
static enum {
FIE_UNSET = -1,
@@ -570,6 +572,35 @@ static void populate_efficiency_class(void)
}
#endif
+/* Set min/max performance HW register and cache the value */
+static int cppc_cpufreq_set_mperf_reg(struct cpufreq_policy *policy,
+ u64 val, 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;
+ u32 perf;
+ int ret;
+
+ perf = clamp(val, caps->lowest_perf, caps->highest_perf);
+
+ ret = is_min ? cppc_set_min_perf(cpu, perf) :
+ cppc_set_max_perf(cpu, perf);
+ if (ret) {
+ if (ret != -EOPNOTSUPP)
+ pr_warn("CPU%d: set %s_perf=%u failed (%d)\n",
+ cpu, is_min ? "min" : "max", perf, ret);
+ return ret;
+ }
+
+ if (is_min)
+ cpu_data->perf_ctrls.min_perf = perf;
+ else
+ cpu_data->perf_ctrls.max_perf = perf;
+
+ return 0;
+}
+
static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
{
struct cppc_cpudata *cpu_data;
@@ -918,16 +949,142 @@ CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
cppc_get_epp_perf, cppc_set_epp)
+/**
+ * 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).
+ */
+static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+ 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;
+
+ /* Use lowest_perf if register is uninitialized (0) */
+ if (perf == 0)
+ perf = caps->lowest_perf;
+
+ /* Convert performance to frequency (kHz) for user */
+ return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
+}
+
+/**
+ * store_min_perf - Set minimum performance from frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer containing the frequency value
+ * @count: size of @buf
+ *
+ * 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);
+
+ guard(mutex)(&cppc_cpufreq_autonomous_lock);
+ ret = cppc_cpufreq_set_mperf_reg(policy, perf, true);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/**
+ * 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).
+ */
+static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+ 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;
+
+ /* Use highest_perf if register is uninitialized or out of range */
+ if (perf == 0 || perf > caps->highest_perf)
+ perf = caps->highest_perf;
+
+ /* Convert performance to frequency (kHz) for user */
+ return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
+}
+
+/**
+ * store_max_perf - Set maximum performance from frequency (kHz)
+ * @policy: cpufreq policy
+ * @buf: buffer containing the frequency value
+ * @count: size of @buf
+ *
+ * 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);
+
+ guard(mutex)(&cppc_cpufreq_autonomous_lock);
+ ret = cppc_cpufreq_set_mperf_reg(policy, perf, false);
+ 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 3fc796c0d902..b358440cd0e2 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -174,6 +174,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, u32 min_perf);
+extern int cppc_get_max_perf(int cpu, u64 *max_perf);
+extern int cppc_set_max_perf(int cpu, u32 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);
@@ -270,6 +274,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, u32 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, u32 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] 38+ messages in thread
* [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (5 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-22 11:51 ` Pierre Gondois
2026-01-20 14:56 ` [PATCH v6 8/9] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 9/9] cpufreq: CPPC: Update cached perf_ctrls on sysfs write Sumit Gupta
8 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, 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. It contains two sticky bits set by the platform:
- Bit 0 (Desired_Excursion): Set when delivered performance is
constrained below desired performance. Not used when Autonomous
Selection is enabled.
- Bit 1 (Minimum_Excursion): Set when delivered performance is
constrained below minimum performance.
These bits remain set until OSPM explicitly clears them. The write
operation accepts a bitmask of bits to clear:
- Write 0x1 to clear bit 0
- Write 0x2 to clear bit 1
- Write 0x3 to clear both bits
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 occurred during that time window.
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 | 56 ++++++++++++++++++++++++++++++++++
drivers/cpufreq/cppc_cpufreq.c | 5 +++
include/acpi/cppc_acpi.h | 15 +++++++++
3 files changed, 76 insertions(+)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 46bf45f8b0f3..b46f22f58f56 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1787,6 +1787,62 @@ int cppc_set_max_perf(int cpu, u32 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.
+ *
+ * The returned value contains sticky status bits indicating platform-imposed
+ * performance limitations.
+ *
+ * Return: 0 for success, -EIO on 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() - Clear bits in the Performance Limited register.
+ * @cpu: CPU on which to write register.
+ * @bits_to_clear: Bitmask of bits to clear in the perf_limited register.
+ *
+ * The Performance Limited register contains two sticky bits set by platform:
+ * - Bit 0 (Desired_Excursion): Set when delivered performance is constrained
+ * below desired performance. Not used when Autonomous Selection is enabled.
+ * - Bit 1 (Minimum_Excursion): Set when delivered performance is constrained
+ * below minimum performance.
+ *
+ * These bits are sticky and remain set until OSPM explicitly clears them.
+ * This function only allows clearing bits (the platform sets them).
+ *
+ * Return: 0 for success, -EINVAL for invalid bits, -EIO on register
+ * access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
+{
+ u64 current_val, new_val;
+ int ret;
+
+ /* Only bits 0 and 1 are valid */
+ if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK)
+ return -EINVAL;
+
+ if (!bits_to_clear)
+ return 0;
+
+ ret = cppc_get_perf_limited(cpu, ¤t_val);
+ if (ret)
+ return ret;
+
+ /* Clear the specified bits */
+ new_val = current_val & ~bits_to_clear;
+
+ return cppc_set_reg_val(cpu, PERF_LIMITED, new_val);
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
+
/**
* cppc_set_enable - Set to enable CPPC on the processor by writing the
* Continuous Performance Control package EnableRegister field.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 66e183b45fb0..afb2cdb67a2f 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1071,12 +1071,16 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
return count;
}
+CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
+ cppc_set_perf_limited)
+
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,
@@ -1085,6 +1089,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 b358440cd0e2..f3a04ccd10b7 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -42,6 +42,11 @@
#define CPPC_EPP_PERFORMANCE_PREF 0x00
#define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF
+#define CPPC_PERF_LIMITED_DESIRED_EXCURSION BIT(0)
+#define CPPC_PERF_LIMITED_MINIMUM_EXCURSION BIT(1)
+#define CPPC_PERF_LIMITED_MASK (CPPC_PERF_LIMITED_DESIRED_EXCURSION | \
+ CPPC_PERF_LIMITED_MINIMUM_EXCURSION)
+
/* Each register has the folowing format. */
struct cpc_reg {
u8 descriptor;
@@ -178,6 +183,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
extern int cppc_set_min_perf(int cpu, u32 min_perf);
extern int cppc_get_max_perf(int cpu, u64 *max_perf);
extern int cppc_set_max_perf(int cpu, u32 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);
@@ -290,6 +297,14 @@ static inline int cppc_set_max_perf(int cpu, u32 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] 38+ messages in thread
* [PATCH v6 8/9] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (6 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 9/9] cpufreq: CPPC: Update cached perf_ctrls on sysfs write Sumit Gupta
8 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, 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 | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 3a05604c21bf..0b0c07c074bf 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -327,6 +327,50 @@ 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: February 2026
+Contact: linux-pm@vger.kernel.org
+Description: Minimum Performance Frequency
+
+ 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.
+
+What: /sys/devices/system/cpu/cpuX/cpufreq/max_perf
+Date: February 2026
+Contact: linux-pm@vger.kernel.org
+Description: Maximum Performance Frequency
+
+ Read/write a frequency value in kHz from/to this file. This
+ file sets the maximum 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.
+
+What: /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
+Date: February 2026
+Contact: linux-pm@vger.kernel.org
+Description: Performance Limited
+
+ Read to check if platform throttling (thermal/power/current
+ limits) caused delivered performance to fall below the
+ requested level. A non-zero value indicates throttling occurred.
+
+ Write the bitmask of bits to clear:
+
+ - 0x1 = clear bit 0 (desired performance excursion)
+ - 0x2 = clear bit 1 (minimum performance excursion)
+ - 0x3 = clear both bits
+
+ The platform sets these bits; OSPM can only clear them.
+
+ 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] 38+ messages in thread
* [PATCH v6 9/9] cpufreq: CPPC: Update cached perf_ctrls on sysfs write
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
` (7 preceding siblings ...)
2026-01-20 14:56 ` [PATCH v6 8/9] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2026-01-20 14:56 ` Sumit Gupta
8 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-20 14:56 UTC (permalink / raw)
To: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
Update the cached perf_ctrls values when writing via sysfs to keep
them in sync with hardware registers:
- store_auto_select(): update perf_ctrls.auto_sel
- store_energy_performance_preference_val(): update perf_ctrls.energy_perf
Add mutex protection to these functions to ensure hardware register
writes and cache updates are atomic.
This ensures consistent cached values after sysfs writes, which
complements the cppc_get_perf() initialization during policy setup.
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
drivers/cpufreq/cppc_cpufreq.c | 35 ++++++++++++++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index afb2cdb67a2f..946fb93179d7 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -886,6 +886,7 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
static ssize_t store_auto_select(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
bool val;
int ret;
@@ -893,10 +894,14 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
if (ret)
return ret;
+ guard(mutex)(&cppc_cpufreq_autonomous_lock);
+
ret = cppc_set_auto_sel(policy->cpu, val);
if (ret)
return ret;
+ cpu_data->perf_ctrls.auto_sel = val;
+
return count;
}
@@ -946,8 +951,34 @@ static ssize_t store_##_name(struct cpufreq_policy *policy, \
CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
cppc_set_auto_act_window)
-CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
- cppc_get_epp_perf, cppc_set_epp)
+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)
+{
+ struct cppc_cpudata *cpu_data = policy->driver_data;
+ u64 val;
+ int ret;
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&cppc_cpufreq_autonomous_lock);
+
+ ret = cppc_set_epp(policy->cpu, val);
+ if (ret)
+ return ret;
+
+ cpu_data->perf_ctrls.energy_perf = val;
+
+ return count;
+}
/**
* show_min_perf - Show minimum performance as frequency (kHz)
--
2.34.1
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2026-01-22 8:27 ` zhenglifeng (A)
2026-01-27 16:24 ` Rafael J. Wysocki
1 sibling, 0 replies; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 8:27 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> Add generic helper functions for u64 sysfs attributes that follow the
> common pattern of calling CPPC get/set APIs:
> - cppc_cpufreq_sysfs_show_u64(): reads value and handles -EOPNOTSUPP
> - cppc_cpufreq_sysfs_store_u64(): parses input and calls set function
>
> Add CPPC_CPUFREQ_ATTR_RW_U64() macro to generate show/store functions
> using these helpers, reducing boilerplate for simple attributes.
>
> Convert auto_act_window and energy_performance_preference_val to use
> the new macro.
>
> No functional changes.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/cpufreq/cppc_cpufreq.c | 69 ++++++++++++----------------------
> 1 file changed, 25 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 36e8a75a37f1..c95dcd7719c3 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -863,73 +863,54 @@ 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((int)cpu, &val);
>
> - /* show "<unsupported>" when this register is not supported by cpc */
> if (ret == -EOPNOTSUPP)
> return sysfs_emit(buf, "<unsupported>\n");
> -
I think it is better to keep this blank line. Apart from this
Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
> 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)
> -{
> - 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");
> -
> - if (ret)
> - return ret;
> -
> - return sysfs_emit(buf, "%llu\n", val);
> +#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
> +static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
> +{ \
> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
> +} \
> +static ssize_t store_##_name(struct cpufreq_policy *policy, \
> + const char *buf, size_t count) \
> +{ \
> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
> + buf, count); \
> }
>
> -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
> - const char *buf, size_t count)
> -{
> - u64 val;
> - int ret;
> +CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
> + cppc_set_auto_act_window)
>
> - ret = kstrtou64(buf, 0, &val);
> - if (ret)
> - return ret;
> -
> - ret = cppc_set_epp(policy->cpu, val);
> - if (ret)
> - return ret;
> -
> - return count;
> -}
> +CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
> + cppc_get_epp_perf, cppc_set_epp)
>
> cpufreq_freq_attr_ro(freqdomain_cpus);
> cpufreq_freq_attr_rw(auto_select);
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
2026-01-20 14:56 ` [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
@ 2026-01-22 8:28 ` zhenglifeng (A)
2026-01-27 16:27 ` Rafael J. Wysocki
1 sibling, 0 replies; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 8:28 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
> the same is available in 'struct cppc_perf_ctrls' which is used.
> - Move the 'auto_sel' field from 'struct cppc_perf_caps' to
> 'struct cppc_perf_ctrls' as it represents a control register.
>
> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> include/acpi/cppc_acpi.h | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 4bcdcaf8bf2c..6573a759eb8d 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -119,8 +119,6 @@ struct cppc_perf_caps {
> u32 lowest_nonlinear_perf;
> u32 lowest_freq;
> u32 nominal_freq;
> - u32 energy_perf;
> - bool auto_sel;
> };
>
> struct cppc_perf_ctrls {
> @@ -128,6 +126,7 @@ struct cppc_perf_ctrls {
> u32 min_perf;
> u32 desired_perf;
> u32 energy_perf;
> + bool auto_sel;
> };
>
> struct cppc_perf_fb_ctrs {
Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity
2026-01-20 14:56 ` [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity Sumit Gupta
@ 2026-01-22 8:31 ` zhenglifeng (A)
0 siblings, 0 replies; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 8:31 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> Update EPP (Energy Performance Preference) constants for better clarity:
> - Add CPPC_EPP_PERFORMANCE_PREF (0x00) for performance preference
> - Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF (0xFF)
> for energy efficiency
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 2 +-
> include/acpi/cppc_acpi.h | 3 ++-
> 2 files changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 22d7fd669a6c..a09bdabaa804 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1615,7 +1615,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);
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 6573a759eb8d..4d644f03098e 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 {
Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-20 14:56 ` [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2026-01-22 8:56 ` zhenglifeng (A)
2026-01-22 11:30 ` Pierre Gondois
2026-01-24 20:05 ` Sumit Gupta
0 siblings, 2 replies; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 8:56 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> Add cppc_get_perf() function to read values of performance control
> registers including desired_perf, min_perf, max_perf, energy_perf,
> and auto_sel.
>
> This provides a read interface to complement the existing
> cppc_set_perf() write interface for performance control registers.
>
> Note that auto_sel is read by cppc_get_perf() but not written by
> cppc_set_perf() to avoid unintended mode changes during performance
> updates. It can be updated with existing dedicated cppc_set_auto_sel()
> API.
>
> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
> perf_ctrls with current hardware register values during cpufreq
> policy initialization.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 80 ++++++++++++++++++++++++++++++++++
> drivers/cpufreq/cppc_cpufreq.c | 6 +++
> include/acpi/cppc_acpi.h | 5 +++
> 3 files changed, 91 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index a09bdabaa804..de35aeb07833 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 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];
> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
> +
> + /* 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) ||
> + CPC_IN_PCC(auto_sel_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) {
> + 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;
desired_perf_reg is not an optional one, so it has to be supported.
> +
> + if (CPC_SUPPORTED(energy_perf_reg))
> + cpc_read(cpu, energy_perf_reg, &energy_perf);
> + perf_ctrls->energy_perf = energy_perf;
> +
> + if (CPC_SUPPORTED(auto_sel_reg))
> + cpc_read(cpu, auto_sel_reg, &auto_sel);
> + perf_ctrls->auto_sel = (bool)auto_sel;
> +
> +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.
> * @cpu: CPU for which to set performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index c95dcd7719c3..229880c4eedb 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -594,6 +594,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:
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 4d644f03098e..3fc796c0d902 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);
> @@ -193,6 +194,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] 38+ messages in thread
* Re: [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
2026-01-20 14:56 ` [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory Sumit Gupta
@ 2026-01-22 9:18 ` zhenglifeng (A)
2026-01-24 20:08 ` Sumit Gupta
0 siblings, 1 reply; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 9:18 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> Extend cppc_set_epp_perf() to write both auto_sel and energy_perf
> registers when they are in FFH or SystemMemory address space.
>
> This keeps the behavior consistent with PCC case where both registers
> are already updated together, but was missing for FFH/SystemMemory.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++++++---
> 1 file changed, 21 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index de35aeb07833..45c6bd6ec24b 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1562,6 +1562,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_ffh_sysmem;
> + bool epp_ffh_sysmem;
> int ret;
>
> if (!cpc_desc) {
> @@ -1572,6 +1574,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_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) &&
> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
> + autosel_ffh_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);
> @@ -1597,11 +1604,22 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
> 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);
> + (epp_ffh_sysmem || autosel_ffh_sysmem)) {
> + if (autosel_ffh_sysmem) {
> + ret = cpc_write(cpu, auto_sel_reg, enable);
> + if (ret)
> + return ret;
> + }
> +
> + if (epp_ffh_sysmem) {
> + ret = cpc_write(cpu, epp_set_reg,
> + perf_ctrls->energy_perf);
> + if (ret)
> + return ret;
> + }
Don't know if such a scenario exists, but if one of them is in PCC and the
other is in FFH or system memory, only the one in PCC will be updated
based on your modifications.
> } else {
> ret = -ENOTSUPP;
> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
> + pr_debug("_CPC in PCC/FFH/SystemMemory are not supported\n");
> }
>
> return ret;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-22 8:56 ` zhenglifeng (A)
@ 2026-01-22 11:30 ` Pierre Gondois
2026-01-22 11:42 ` zhenglifeng (A)
2026-01-24 20:05 ` Sumit Gupta
1 sibling, 1 reply; 38+ messages in thread
From: Pierre Gondois @ 2026-01-22 11:30 UTC (permalink / raw)
To: zhenglifeng (A), Sumit Gupta
Cc: rafael, viresh.kumar, ionela.voinescu, lenb, robert.moore, corbet,
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
On 1/22/26 09:56, zhenglifeng (A) wrote:
> On 2026/1/20 22:56, Sumit Gupta wrote:
>> Add cppc_get_perf() function to read values of performance control
>> registers including desired_perf, min_perf, max_perf, energy_perf,
>> and auto_sel.
>>
>> This provides a read interface to complement the existing
>> cppc_set_perf() write interface for performance control registers.
>>
>> Note that auto_sel is read by cppc_get_perf() but not written by
>> cppc_set_perf() to avoid unintended mode changes during performance
>> updates. It can be updated with existing dedicated cppc_set_auto_sel()
>> API.
>>
>> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
>> perf_ctrls with current hardware register values during cpufreq
>> policy initialization.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 80 ++++++++++++++++++++++++++++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 6 +++
>> include/acpi/cppc_acpi.h | 5 +++
>> 3 files changed, 91 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index a09bdabaa804..de35aeb07833 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 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];
>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>> +
>> + /* 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) ||
>> + CPC_IN_PCC(auto_sel_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) {
>> + 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;
> desired_perf_reg is not an optional one, so it has to be supported.
The ACPI spec seems to say it is optional under some circumstances.
Cf:
"""
This register is optional when OSPM
indicates support for CPPC2 in the platform-wide _OSC
capabilities and the Autonomous Selection Enable reg-
ister is Integer 1
"""
Maybe it is ok to let this check here and to a more complex verification
in acpi_cppc_processor_probe()
>
>> +
>> + if (CPC_SUPPORTED(energy_perf_reg))
>> + cpc_read(cpu, energy_perf_reg, &energy_perf);
>> + perf_ctrls->energy_perf = energy_perf;
>> +
>> + if (CPC_SUPPORTED(auto_sel_reg))
>> + cpc_read(cpu, auto_sel_reg, &auto_sel);
>> + perf_ctrls->auto_sel = (bool)auto_sel;
>> +
>> +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.
>> * @cpu: CPU for which to set performance controls.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> index c95dcd7719c3..229880c4eedb 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -594,6 +594,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:
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 4d644f03098e..3fc796c0d902 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);
>> @@ -193,6 +194,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] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-20 14:56 ` [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2026-01-22 11:36 ` Pierre Gondois
2026-01-24 20:32 ` Sumit Gupta
2026-01-22 12:35 ` zhenglifeng (A)
1 sibling, 1 reply; 38+ messages in thread
From: Pierre Gondois @ 2026-01-22 11:36 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, zhenglifeng1, ionela.voinescu,
lenb, robert.moore, corbet, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 1/20/26 15:56, Sumit Gupta wrote:
> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read and
> write the MIN_PERF and MAX_PERF registers.
>
> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
> to expose these controls to userspace. The sysfs values are in frequency
> (kHz) for consistency with other cpufreq sysfs files.
>
> A mutex is used to serialize sysfs store operations to ensure hardware
> register writes and perf_ctrls updates are atomic.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 44 +++++++++
> drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
> include/acpi/cppc_acpi.h | 20 +++++
> 3 files changed, 221 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 45c6bd6ec24b..46bf45f8b0f3 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
> }
> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>
> +/**
> + * cppc_get_min_perf - Read minimum performance register.
> + * @cpu: CPU from which to read register.
> + * @min_perf: Return address.
> + */
> +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 minimum performance register.
> + * @cpu: CPU to which to write register.
> + * @min_perf: the desired minimum performance value to be updated.
> + */
> +int cppc_set_min_perf(int cpu, u32 min_perf)
> +{
> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
> +
> +/**
> + * cppc_get_max_perf - Read maximum performance register.
> + * @cpu: CPU from which to read register.
> + * @max_perf: Return address.
> + */
> +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 maximum performance register.
> + * @cpu: CPU to which to write register.
> + * @max_perf: the desired maximum performance value to be updated.
> + */
> +int cppc_set_max_perf(int cpu, u32 max_perf)
> +{
> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
> +
> /**
> * cppc_set_enable - Set to enable CPPC on the processor by writing the
> * Continuous Performance Control package EnableRegister field.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 229880c4eedb..66e183b45fb0 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -28,6 +28,8 @@
>
> static struct cpufreq_driver cppc_cpufreq_driver;
>
> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
> +
Shouldn't concurrent access be handled by the policy->rwsem ?
I think this can be checked using either:
- lockdep_assert_held_write(&policy->rwsem)
- lockdep_assert_held_read(&policy->rwsem)
in store/show_max_perf() for instance.
> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
> static enum {
> FIE_UNSET = -1,
> @@ -570,6 +572,35 @@ static void populate_efficiency_class(void)
> }
> #endif
>
> +/* Set min/max performance HW register and cache the value */
> +static int cppc_cpufreq_set_mperf_reg(struct cpufreq_policy *policy,
> + u64 val, 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;
> + u32 perf;
> + int ret;
> +
> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
> +
> + ret = is_min ? cppc_set_min_perf(cpu, perf) :
> + cppc_set_max_perf(cpu, perf);
> + if (ret) {
> + if (ret != -EOPNOTSUPP)
> + pr_warn("CPU%d: set %s_perf=%u failed (%d)\n",
> + cpu, is_min ? "min" : "max", perf, ret);
> + return ret;
> + }
> +
> + if (is_min)
> + cpu_data->perf_ctrls.min_perf = perf;
> + else
> + cpu_data->perf_ctrls.max_perf = perf;
> +
> + return 0;
> +}
> +
> static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
> {
> struct cppc_cpudata *cpu_data;
> @@ -918,16 +949,142 @@ CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
> CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
> cppc_get_epp_perf, cppc_set_epp)
>
> +/**
> + * 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).
> + */
> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + 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;
> +
> + /* Use lowest_perf if register is uninitialized (0) */
> + if (perf == 0)
> + perf = caps->lowest_perf;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
> +}
> +
> +/**
> + * store_min_perf - Set minimum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer containing the frequency value
> + * @count: size of @buf
> + *
> + * 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);
> +
> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, true);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +/**
> + * 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).
> + */
> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + 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;
> +
> + /* Use highest_perf if register is uninitialized or out of range */
> + if (perf == 0 || perf > caps->highest_perf)
> + perf = caps->highest_perf;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
> +}
> +
> +/**
> + * store_max_perf - Set maximum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer containing the frequency value
> + * @count: size of @buf
> + *
> + * 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);
> +
> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, false);
> + 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 3fc796c0d902..b358440cd0e2 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -174,6 +174,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, u32 min_perf);
> +extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> +extern int cppc_set_max_perf(int cpu, u32 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);
> @@ -270,6 +274,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, u32 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, u32 max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-22 11:30 ` Pierre Gondois
@ 2026-01-22 11:42 ` zhenglifeng (A)
0 siblings, 0 replies; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 11:42 UTC (permalink / raw)
To: Pierre Gondois, Sumit Gupta
Cc: rafael, viresh.kumar, ionela.voinescu, lenb, robert.moore, corbet,
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
On 2026/1/22 19:30, Pierre Gondois wrote:
>
> On 1/22/26 09:56, zhenglifeng (A) wrote:
>> On 2026/1/20 22:56, Sumit Gupta wrote:
>>> +
>>> + if (CPC_SUPPORTED(desired_perf_reg))
>>> + cpc_read(cpu, desired_perf_reg, &desired_perf);
>>> + perf_ctrls->desired_perf = desired_perf;
>> desired_perf_reg is not an optional one, so it has to be supported.
>
> The ACPI spec seems to say it is optional under some circumstances.
>
> Cf:
> """
> This register is optional when OSPM
> indicates support for CPPC2 in the platform-wide _OSC
> capabilities and the Autonomous Selection Enable reg-
> ister is Integer 1
> """
>
>
> Maybe it is ok to let this check here and to a more complex verification
> in acpi_cppc_processor_probe()
Right. My mistake.
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited
2026-01-20 14:56 ` [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
@ 2026-01-22 11:51 ` Pierre Gondois
2026-01-24 21:04 ` Sumit Gupta
0 siblings, 1 reply; 38+ messages in thread
From: Pierre Gondois @ 2026-01-22 11:51 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, zhenglifeng1, ionela.voinescu,
lenb, robert.moore, corbet, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 1/20/26 15:56, 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. It contains two sticky bits set by the platform:
> - Bit 0 (Desired_Excursion): Set when delivered performance is
> constrained below desired performance. Not used when Autonomous
> Selection is enabled.
> - Bit 1 (Minimum_Excursion): Set when delivered performance is
> constrained below minimum performance.
>
> These bits remain set until OSPM explicitly clears them. The write
> operation accepts a bitmask of bits to clear:
> - Write 0x1 to clear bit 0
> - Write 0x2 to clear bit 1
> - Write 0x3 to clear both bits
>
> 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 occurred during that time window.
>
> 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 | 56 ++++++++++++++++++++++++++++++++++
> drivers/cpufreq/cppc_cpufreq.c | 5 +++
> include/acpi/cppc_acpi.h | 15 +++++++++
> 3 files changed, 76 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 46bf45f8b0f3..b46f22f58f56 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1787,6 +1787,62 @@ int cppc_set_max_perf(int cpu, u32 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.
> + *
> + * The returned value contains sticky status bits indicating platform-imposed
> + * performance limitations.
> + *
> + * Return: 0 for success, -EIO on 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() - Clear bits in the Performance Limited register.
> + * @cpu: CPU on which to write register.
> + * @bits_to_clear: Bitmask of bits to clear in the perf_limited register.
> + *
> + * The Performance Limited register contains two sticky bits set by platform:
> + * - Bit 0 (Desired_Excursion): Set when delivered performance is constrained
> + * below desired performance. Not used when Autonomous Selection is enabled.
> + * - Bit 1 (Minimum_Excursion): Set when delivered performance is constrained
> + * below minimum performance.
> + *
> + * These bits are sticky and remain set until OSPM explicitly clears them.
> + * This function only allows clearing bits (the platform sets them).
> + *
> + * Return: 0 for success, -EINVAL for invalid bits, -EIO on register
> + * access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
> +{
> + u64 current_val, new_val;
> + int ret;
> +
> + /* Only bits 0 and 1 are valid */
> + if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK)
> + return -EINVAL;
> +
> + if (!bits_to_clear)
> + return 0;
> +
> + ret = cppc_get_perf_limited(cpu, ¤t_val);
> + if (ret)
> + return ret;
> +
> + /* Clear the specified bits */
> + new_val = current_val & ~bits_to_clear;
> +
> + return cppc_set_reg_val(cpu, PERF_LIMITED, new_val);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
> +
> /**
> * cppc_set_enable - Set to enable CPPC on the processor by writing the
> * Continuous Performance Control package EnableRegister field.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 66e183b45fb0..afb2cdb67a2f 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1071,12 +1071,16 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
> return count;
> }
>
> +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
> + cppc_set_perf_limited)
> +
> 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);
If the OS wants to get regular feedback about whether the platform had
to limit
the perf. level, it will likely try to frequently probe the register.
In order to see new events, the register must be cleared. So:
- is it a good idea to allow users to write this register ?
- is it useful to expose this register if the OS frequently clears it ?
I think the functions are useful, it might just be questionable to expose
the register in the sysfs.
>
> static struct freq_attr *cppc_cpufreq_attr[] = {
> &freqdomain_cpus,
> @@ -1085,6 +1089,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 b358440cd0e2..f3a04ccd10b7 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -42,6 +42,11 @@
> #define CPPC_EPP_PERFORMANCE_PREF 0x00
> #define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF
>
> +#define CPPC_PERF_LIMITED_DESIRED_EXCURSION BIT(0)
> +#define CPPC_PERF_LIMITED_MINIMUM_EXCURSION BIT(1)
> +#define CPPC_PERF_LIMITED_MASK (CPPC_PERF_LIMITED_DESIRED_EXCURSION | \
> + CPPC_PERF_LIMITED_MINIMUM_EXCURSION)
> +
> /* Each register has the folowing format. */
> struct cpc_reg {
> u8 descriptor;
> @@ -178,6 +183,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
> extern int cppc_set_min_perf(int cpu, u32 min_perf);
> extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> extern int cppc_set_max_perf(int cpu, u32 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);
> @@ -290,6 +297,14 @@ static inline int cppc_set_max_perf(int cpu, u32 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] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-20 14:56 ` [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2026-01-22 11:36 ` Pierre Gondois
@ 2026-01-22 12:35 ` zhenglifeng (A)
2026-01-24 20:52 ` Sumit Gupta
1 sibling, 1 reply; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-22 12:35 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/20 22:56, Sumit Gupta wrote:
> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read and
> write the MIN_PERF and MAX_PERF registers.
>
> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
> to expose these controls to userspace. The sysfs values are in frequency
> (kHz) for consistency with other cpufreq sysfs files.
>
> A mutex is used to serialize sysfs store operations to ensure hardware
> register writes and perf_ctrls updates are atomic.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/acpi/cppc_acpi.c | 44 +++++++++
> drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
> include/acpi/cppc_acpi.h | 20 +++++
> 3 files changed, 221 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 45c6bd6ec24b..46bf45f8b0f3 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
> }
> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>
> +/**
> + * cppc_get_min_perf - Read minimum performance register.
> + * @cpu: CPU from which to read register.
> + * @min_perf: Return address.
> + */
> +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 minimum performance register.
> + * @cpu: CPU to which to write register.
> + * @min_perf: the desired minimum performance value to be updated.
> + */
> +int cppc_set_min_perf(int cpu, u32 min_perf)
> +{
> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
ACPI spec says it 'must be set to a value that is less than or equal to
that specified by the Maximum Performance Register'. So it may be better
to check it before setting value.
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
> +
> +/**
> + * cppc_get_max_perf - Read maximum performance register.
> + * @cpu: CPU from which to read register.
> + * @max_perf: Return address.
> + */
> +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 maximum performance register.
> + * @cpu: CPU to which to write register.
> + * @max_perf: the desired maximum performance value to be updated.
> + */
> +int cppc_set_max_perf(int cpu, u32 max_perf)
> +{
> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
> +
> /**
> * cppc_set_enable - Set to enable CPPC on the processor by writing the
> * Continuous Performance Control package EnableRegister field.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 229880c4eedb..66e183b45fb0 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -28,6 +28,8 @@
>
> static struct cpufreq_driver cppc_cpufreq_driver;
>
> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
> +
> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
> static enum {
> FIE_UNSET = -1,
> @@ -570,6 +572,35 @@ static void populate_efficiency_class(void)
> }
> #endif
>
> +/* Set min/max performance HW register and cache the value */
> +static int cppc_cpufreq_set_mperf_reg(struct cpufreq_policy *policy,
> + u64 val, 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;
> + u32 perf;
> + int ret;
> +
> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
> +
> + ret = is_min ? cppc_set_min_perf(cpu, perf) :
> + cppc_set_max_perf(cpu, perf);
> + if (ret) {
> + if (ret != -EOPNOTSUPP)
> + pr_warn("CPU%d: set %s_perf=%u failed (%d)\n",
> + cpu, is_min ? "min" : "max", perf, ret);
> + return ret;
> + }
> +
> + if (is_min)
> + cpu_data->perf_ctrls.min_perf = perf;
> + else
> + cpu_data->perf_ctrls.max_perf = perf;
> +
> + return 0;
I think cppc_set_XXX and updating cpudata->perf_ctrls.XXX can be extract
out for not only min_perf and max_perf but also auto_sel and energy_perf
and anything else in perf_ctrls.
> +}
> +
> static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
> {
> struct cppc_cpudata *cpu_data;
> @@ -918,16 +949,142 @@ CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
> CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
> cppc_get_epp_perf, cppc_set_epp)
>
> +/**
> + * 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).
> + */
> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + 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;
> +
> + /* Use lowest_perf if register is uninitialized (0) */
> + if (perf == 0)
> + perf = caps->lowest_perf;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
> +}
> +
> +/**
> + * store_min_perf - Set minimum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer containing the frequency value
> + * @count: size of @buf
> + *
> + * 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);
> +
> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, true);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +/**
> + * 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).
> + */
> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
> +{
> + struct cppc_cpudata *cpu_data = policy->driver_data;
> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> + 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;
> +
> + /* Use highest_perf if register is uninitialized or out of range */
> + if (perf == 0 || perf > caps->highest_perf)
> + perf = caps->highest_perf;
> +
> + /* Convert performance to frequency (kHz) for user */
> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
> +}
> +
> +/**
> + * store_max_perf - Set maximum performance from frequency (kHz)
> + * @policy: cpufreq policy
> + * @buf: buffer containing the frequency value
> + * @count: size of @buf
> + *
> + * 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);
> +
> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, false);
> + 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 3fc796c0d902..b358440cd0e2 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -174,6 +174,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, u32 min_perf);
> +extern int cppc_get_max_perf(int cpu, u64 *max_perf);
> +extern int cppc_set_max_perf(int cpu, u32 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);
> @@ -270,6 +274,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, u32 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, u32 max_perf)
> +{
> + return -EOPNOTSUPP;
> +}
> static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
> {
> return -ENODEV;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-22 8:56 ` zhenglifeng (A)
2026-01-22 11:30 ` Pierre Gondois
@ 2026-01-24 20:05 ` Sumit Gupta
2026-01-24 20:19 ` Sumit Gupta
1 sibling, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 20:05 UTC (permalink / raw)
To: zhenglifeng (A)
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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 22/01/26 14:26, zhenglifeng (A) wrote:
> External email: Use caution opening links or attachments
>
>
> On 2026/1/20 22:56, Sumit Gupta wrote:
>> Add cppc_get_perf() function to read values of performance control
>> registers including desired_perf, min_perf, max_perf, energy_perf,
>> and auto_sel.
>>
>> This provides a read interface to complement the existing
>> cppc_set_perf() write interface for performance control registers.
>>
>> Note that auto_sel is read by cppc_get_perf() but not written by
>> cppc_set_perf() to avoid unintended mode changes during performance
>> updates. It can be updated with existing dedicated cppc_set_auto_sel()
>> API.
>>
>> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
>> perf_ctrls with current hardware register values during cpufreq
>> policy initialization.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 80 ++++++++++++++++++++++++++++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 6 +++
>> include/acpi/cppc_acpi.h | 5 +++
>> 3 files changed, 91 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index a09bdabaa804..de35aeb07833 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 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];
>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>> +
>> + /* 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) ||
>> + CPC_IN_PCC(auto_sel_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) {
>> + 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;
> desired_perf_reg is not an optional one, so it has to be supported.
The current code handles mixed cases correctly.
When either register is in PCC, the first if block executes and calls
cpc_write() for both registers. The cpc_write() internally handles
each register's type (PCC, FFH, or SystemMemory)
Thank you,
Sumit Gupta
>> +
>> + if (CPC_SUPPORTED(energy_perf_reg))
>> + cpc_read(cpu, energy_perf_reg, &energy_perf);
>> + perf_ctrls->energy_perf = energy_perf;
>> +
>> + if (CPC_SUPPORTED(auto_sel_reg))
>> + cpc_read(cpu, auto_sel_reg, &auto_sel);
>> + perf_ctrls->auto_sel = (bool)auto_sel;
>> +
>> +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.
>> * @cpu: CPU for which to set performance controls.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> index c95dcd7719c3..229880c4eedb 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -594,6 +594,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:
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 4d644f03098e..3fc796c0d902 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);
>> @@ -193,6 +194,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] 38+ messages in thread
* Re: [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
2026-01-22 9:18 ` zhenglifeng (A)
@ 2026-01-24 20:08 ` Sumit Gupta
2026-01-26 8:10 ` zhenglifeng (A)
0 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 20:08 UTC (permalink / raw)
To: zhenglifeng (A)
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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 22/01/26 14:48, zhenglifeng (A) wrote:
> External email: Use caution opening links or attachments
>
>
> On 2026/1/20 22:56, Sumit Gupta wrote:
>> Extend cppc_set_epp_perf() to write both auto_sel and energy_perf
>> registers when they are in FFH or SystemMemory address space.
>>
>> This keeps the behavior consistent with PCC case where both registers
>> are already updated together, but was missing for FFH/SystemMemory.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++++++---
>> 1 file changed, 21 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index de35aeb07833..45c6bd6ec24b 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1562,6 +1562,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_ffh_sysmem;
>> + bool epp_ffh_sysmem;
>> int ret;
>>
>> if (!cpc_desc) {
>> @@ -1572,6 +1574,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_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) &&
>> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
>> + autosel_ffh_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);
>> @@ -1597,11 +1604,22 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>> 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);
>> + (epp_ffh_sysmem || autosel_ffh_sysmem)) {
>> + if (autosel_ffh_sysmem) {
>> + ret = cpc_write(cpu, auto_sel_reg, enable);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (epp_ffh_sysmem) {
>> + ret = cpc_write(cpu, epp_set_reg,
>> + perf_ctrls->energy_perf);
>> + if (ret)
>> + return ret;
>> + }
> Don't know if such a scenario exists, but if one of them is in PCC and the
> other is in FFH or system memory, only the one in PCC will be updated
> based on your modifications.
The current code handles mixed cases correctly.
When either register is in PCC, the first if block executes and calls
cpc_write() for both registers. The cpc_write() internally handles
each register's type (PCC, FFH, or SystemMemory)
Thank you,
Sumit Gupta
>> } else {
>> ret = -ENOTSUPP;
>> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
>> + pr_debug("_CPC in PCC/FFH/SystemMemory are not supported\n");
>> }
>>
>> return ret;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-24 20:05 ` Sumit Gupta
@ 2026-01-24 20:19 ` Sumit Gupta
2026-01-26 11:20 ` Pierre Gondois
0 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 20:19 UTC (permalink / raw)
To: zhenglifeng (A)
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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 25/01/26 01:35, Sumit Gupta wrote:
>
> On 22/01/26 14:26, zhenglifeng (A) wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 2026/1/20 22:56, Sumit Gupta wrote:
>>> Add cppc_get_perf() function to read values of performance control
>>> registers including desired_perf, min_perf, max_perf, energy_perf,
>>> and auto_sel.
>>>
>>> This provides a read interface to complement the existing
>>> cppc_set_perf() write interface for performance control registers.
>>>
>>> Note that auto_sel is read by cppc_get_perf() but not written by
>>> cppc_set_perf() to avoid unintended mode changes during performance
>>> updates. It can be updated with existing dedicated cppc_set_auto_sel()
>>> API.
>>>
>>> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
>>> perf_ctrls with current hardware register values during cpufreq
>>> policy initialization.
>>>
>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>> ---
>>> drivers/acpi/cppc_acpi.c | 80
>>> ++++++++++++++++++++++++++++++++++
>>> drivers/cpufreq/cppc_cpufreq.c | 6 +++
>>> include/acpi/cppc_acpi.h | 5 +++
>>> 3 files changed, 91 insertions(+)
>>>
>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>> index a09bdabaa804..de35aeb07833 100644
>>> --- a/drivers/acpi/cppc_acpi.c
>>> +++ b/drivers/acpi/cppc_acpi.c
>>> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
>>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0,
>>> auto_sel = 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];
>>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>>> +
>>> + /* 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) ||
>>> + CPC_IN_PCC(auto_sel_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) {
>>> + 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;
>> desired_perf_reg is not an optional one, so it has to be supported.
Please ignore my previous reply on this. Had some problem with my email
client and both mails got mixed.
The register is optional when Autonomous mode is enabled.
Seems the comment here is already resolved with Pierre's reply.
We discussed this during v4 [1] also.
[1]
https://lore.kernel.org/lkml/ccd45c1b-2f69-4725-918f-18063f00a864@nvidia.com/
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-22 11:36 ` Pierre Gondois
@ 2026-01-24 20:32 ` Sumit Gupta
2026-01-26 10:51 ` Pierre Gondois
0 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 20:32 UTC (permalink / raw)
To: Pierre Gondois, rafael, viresh.kumar, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
On 22/01/26 17:06, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 1/20/26 15:56, Sumit Gupta wrote:
>> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read and
>> write the MIN_PERF and MAX_PERF registers.
>>
>> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
>> to expose these controls to userspace. The sysfs values are in frequency
>> (kHz) for consistency with other cpufreq sysfs files.
>>
>> A mutex is used to serialize sysfs store operations to ensure hardware
>> register writes and perf_ctrls updates are atomic.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 44 +++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
>> include/acpi/cppc_acpi.h | 20 +++++
>> 3 files changed, 221 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 45c6bd6ec24b..46bf45f8b0f3 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>>
>> +/**
>> + * cppc_get_min_perf - Read minimum performance register.
>> + * @cpu: CPU from which to read register.
>> + * @min_perf: Return address.
>> + */
>> +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 minimum performance register.
>> + * @cpu: CPU to which to write register.
>> + * @min_perf: the desired minimum performance value to be updated.
>> + */
>> +int cppc_set_min_perf(int cpu, u32 min_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
>> +
>> +/**
>> + * cppc_get_max_perf - Read maximum performance register.
>> + * @cpu: CPU from which to read register.
>> + * @max_perf: Return address.
>> + */
>> +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 maximum performance register.
>> + * @cpu: CPU to which to write register.
>> + * @max_perf: the desired maximum performance value to be updated.
>> + */
>> +int cppc_set_max_perf(int cpu, u32 max_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>> +
>> /**
>> * cppc_set_enable - Set to enable CPPC on the processor by writing
>> the
>> * Continuous Performance Control package EnableRegister field.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>> b/drivers/cpufreq/cppc_cpufreq.c
>> index 229880c4eedb..66e183b45fb0 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -28,6 +28,8 @@
>>
>> static struct cpufreq_driver cppc_cpufreq_driver;
>>
>> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
>> +
>
> Shouldn't concurrent access be handled by the policy->rwsem ?
>
> I think this can be checked using either:
> - lockdep_assert_held_write(&policy->rwsem)
> - lockdep_assert_held_read(&policy->rwsem)
>
> in store/show_max_perf() for instance.
>
You're right. The cpufreq sysfs already holds policy->rwsem for
show/store callbacks. I'll remove the mutex and add lockdep
assertions for the expected locking.
--------
File: drivers/cpufreq/cpufreq.c
static ssize_t store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct cpufreq_policy *policy = to_policy(kobj);
....
guard(cpufreq_policy_write)(policy);
--------
Thank you,
Sumit Gupta
>
>> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>> static enum {
>> FIE_UNSET = -1,
>> @@ -570,6 +572,35 @@ static void populate_efficiency_class(void)
>> }
>> #endif
>>
>> +/* Set min/max performance HW register and cache the value */
>> +static int cppc_cpufreq_set_mperf_reg(struct cpufreq_policy *policy,
>> + u64 val, 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;
>> + u32 perf;
>> + int ret;
>> +
>> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
>> +
>> + ret = is_min ? cppc_set_min_perf(cpu, perf) :
>> + cppc_set_max_perf(cpu, perf);
>> + if (ret) {
>> + if (ret != -EOPNOTSUPP)
>> + pr_warn("CPU%d: set %s_perf=%u failed (%d)\n",
>> + cpu, is_min ? "min" : "max", perf, ret);
>> + return ret;
>> + }
>> +
>> + if (is_min)
>> + cpu_data->perf_ctrls.min_perf = perf;
>> + else
>> + cpu_data->perf_ctrls.max_perf = perf;
>> +
>> + return 0;
>> +}
>> +
>> static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int
>> cpu)
>> {
>> struct cppc_cpudata *cpu_data;
>> @@ -918,16 +949,142 @@ CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window,
>> cppc_get_auto_act_window,
>> CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
>> cppc_get_epp_perf, cppc_set_epp)
>>
>> +/**
>> + * 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).
>> + */
>> +static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
>> + 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;
>> +
>> + /* Use lowest_perf if register is uninitialized (0) */
>> + if (perf == 0)
>> + perf = caps->lowest_perf;
>> +
>> + /* Convert performance to frequency (kHz) for user */
>> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
>> +}
>> +
>> +/**
>> + * store_min_perf - Set minimum performance from frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer containing the frequency value
>> + * @count: size of @buf
>> + *
>> + * 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);
>> +
>> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
>> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, true);
>> + if (ret)
>> + return ret;
>> +
>> + return count;
>> +}
>> +
>> +/**
>> + * 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).
>> + */
>> +static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
>> +{
>> + struct cppc_cpudata *cpu_data = policy->driver_data;
>> + struct cppc_perf_caps *caps = &cpu_data->perf_caps;
>> + 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;
>> +
>> + /* Use highest_perf if register is uninitialized or out of
>> range */
>> + if (perf == 0 || perf > caps->highest_perf)
>> + perf = caps->highest_perf;
>> +
>> + /* Convert performance to frequency (kHz) for user */
>> + return sysfs_emit(buf, "%u\n", cppc_perf_to_khz(caps, perf));
>> +}
>> +
>> +/**
>> + * store_max_perf - Set maximum performance from frequency (kHz)
>> + * @policy: cpufreq policy
>> + * @buf: buffer containing the frequency value
>> + * @count: size of @buf
>> + *
>> + * 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);
>> +
>> + guard(mutex)(&cppc_cpufreq_autonomous_lock);
>> + ret = cppc_cpufreq_set_mperf_reg(policy, perf, false);
>> + 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 3fc796c0d902..b358440cd0e2 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -174,6 +174,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, u32 min_perf);
>> +extern int cppc_get_max_perf(int cpu, u64 *max_perf);
>> +extern int cppc_set_max_perf(int cpu, u32 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);
>> @@ -270,6 +274,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, u32 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, u32 max_perf)
>> +{
>> + return -EOPNOTSUPP;
>> +}
>> static inline int amd_get_highest_perf(unsigned int cpu, u32
>> *highest_perf)
>> {
>> return -ENODEV;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-22 12:35 ` zhenglifeng (A)
@ 2026-01-24 20:52 ` Sumit Gupta
0 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 20:52 UTC (permalink / raw)
To: zhenglifeng (A)
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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 22/01/26 18:05, zhenglifeng (A) wrote:
> External email: Use caution opening links or attachments
>
>
> On 2026/1/20 22:56, Sumit Gupta wrote:
>> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read and
>> write the MIN_PERF and MAX_PERF registers.
>>
>> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
>> to expose these controls to userspace. The sysfs values are in frequency
>> (kHz) for consistency with other cpufreq sysfs files.
>>
>> A mutex is used to serialize sysfs store operations to ensure hardware
>> register writes and perf_ctrls updates are atomic.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/acpi/cppc_acpi.c | 44 +++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 157 +++++++++++++++++++++++++++++++++
>> include/acpi/cppc_acpi.h | 20 +++++
>> 3 files changed, 221 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 45c6bd6ec24b..46bf45f8b0f3 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
>> }
>> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>>
>> +/**
>> + * cppc_get_min_perf - Read minimum performance register.
>> + * @cpu: CPU from which to read register.
>> + * @min_perf: Return address.
>> + */
>> +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 minimum performance register.
>> + * @cpu: CPU to which to write register.
>> + * @min_perf: the desired minimum performance value to be updated.
>> + */
>> +int cppc_set_min_perf(int cpu, u32 min_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
> ACPI spec says it 'must be set to a value that is less than or equal to
> that specified by the Maximum Performance Register'. So it may be better
> to check it before setting value.
Yes, I added that check in v1[1]. But missed adding it in later versions.
Will add below check in v7. Thank you for pointing.
--------
static ssize_t store_min_perf(struct cpufreq_policy *policy, const
char *buf,
size_t count)
{
.....
/* Convert frequency (kHz) to performance value */
perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
+ if (perf > cpu_data->perf_ctrls.max_perf)
+ return -EINVAL;
--------
[1] https://lore.kernel.org/lkml/20250211103737.447704-4-sumitg@nvidia.com/
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
>> +
>> +/**
>> + * cppc_get_max_perf - Read maximum performance register.
>> + * @cpu: CPU from which to read register.
>> + * @max_perf: Return address.
>> + */
>> +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 maximum performance register.
>> + * @cpu: CPU to which to write register.
>> + * @max_perf: the desired maximum performance value to be updated.
>> + */
>> +int cppc_set_max_perf(int cpu, u32 max_perf)
>> +{
>> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>> +
>> /**
>> * cppc_set_enable - Set to enable CPPC on the processor by writing the
>> * Continuous Performance Control package EnableRegister field.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> index 229880c4eedb..66e183b45fb0 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -28,6 +28,8 @@
>>
>> static struct cpufreq_driver cppc_cpufreq_driver;
>>
>> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
>> +
>> #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>> static enum {
>> FIE_UNSET = -1,
>> @@ -570,6 +572,35 @@ static void populate_efficiency_class(void)
>> }
>> #endif
>>
>> +/* Set min/max performance HW register and cache the value */
>> +static int cppc_cpufreq_set_mperf_reg(struct cpufreq_policy *policy,
>> + u64 val, 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;
>> + u32 perf;
>> + int ret;
>> +
>> + perf = clamp(val, caps->lowest_perf, caps->highest_perf);
>> +
>> + ret = is_min ? cppc_set_min_perf(cpu, perf) :
>> + cppc_set_max_perf(cpu, perf);
>> + if (ret) {
>> + if (ret != -EOPNOTSUPP)
>> + pr_warn("CPU%d: set %s_perf=%u failed (%d)\n",
>> + cpu, is_min ? "min" : "max", perf, ret);
>> + return ret;
>> + }
>> +
>> + if (is_min)
>> + cpu_data->perf_ctrls.min_perf = perf;
>> + else
>> + cpu_data->perf_ctrls.max_perf = perf;
>> +
>> + return 0;
> I think cppc_set_XXX and updating cpudata->perf_ctrls.XXX can be extract
> out for not only min_perf and max_perf but also auto_sel and energy_perf
> and anything else in perf_ctrls.
Updating cached value of auto_sel and energy_perf in patch 9.
I think the current code is simple. Adding an abstraction seems to be
overdoing it for this case.
Thank you,
Sumit Gupta
....
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited
2026-01-22 11:51 ` Pierre Gondois
@ 2026-01-24 21:04 ` Sumit Gupta
2026-01-26 11:23 ` Pierre Gondois
0 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-24 21:04 UTC (permalink / raw)
To: Pierre Gondois, rafael, viresh.kumar, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
On 22/01/26 17:21, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 1/20/26 15:56, 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. It contains two sticky bits set by the platform:
>> - Bit 0 (Desired_Excursion): Set when delivered performance is
>> constrained below desired performance. Not used when Autonomous
>> Selection is enabled.
>> - Bit 1 (Minimum_Excursion): Set when delivered performance is
>> constrained below minimum performance.
>>
>> These bits remain set until OSPM explicitly clears them. The write
>> operation accepts a bitmask of bits to clear:
>> - Write 0x1 to clear bit 0
>> - Write 0x2 to clear bit 1
>> - Write 0x3 to clear both bits
>>
>> 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 occurred during that time
>> window.
>>
>> 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 | 56 ++++++++++++++++++++++++++++++++++
>> drivers/cpufreq/cppc_cpufreq.c | 5 +++
>> include/acpi/cppc_acpi.h | 15 +++++++++
>> 3 files changed, 76 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index 46bf45f8b0f3..b46f22f58f56 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1787,6 +1787,62 @@ int cppc_set_max_perf(int cpu, u32 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.
>> + *
>> + * The returned value contains sticky status bits indicating
>> platform-imposed
>> + * performance limitations.
>> + *
>> + * Return: 0 for success, -EIO on 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() - Clear bits in the Performance Limited
>> register.
>> + * @cpu: CPU on which to write register.
>> + * @bits_to_clear: Bitmask of bits to clear in the perf_limited
>> register.
>> + *
>> + * The Performance Limited register contains two sticky bits set by
>> platform:
>> + * - Bit 0 (Desired_Excursion): Set when delivered performance is
>> constrained
>> + * below desired performance. Not used when Autonomous Selection
>> is enabled.
>> + * - Bit 1 (Minimum_Excursion): Set when delivered performance is
>> constrained
>> + * below minimum performance.
>> + *
>> + * These bits are sticky and remain set until OSPM explicitly clears
>> them.
>> + * This function only allows clearing bits (the platform sets them).
>> + *
>> + * Return: 0 for success, -EINVAL for invalid bits, -EIO on register
>> + * access failure, -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
>> +{
>> + u64 current_val, new_val;
>> + int ret;
>> +
>> + /* Only bits 0 and 1 are valid */
>> + if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK)
>> + return -EINVAL;
>> +
>> + if (!bits_to_clear)
>> + return 0;
>> +
>> + ret = cppc_get_perf_limited(cpu, ¤t_val);
>> + if (ret)
>> + return ret;
>> +
>> + /* Clear the specified bits */
>> + new_val = current_val & ~bits_to_clear;
>> +
>> + return cppc_set_reg_val(cpu, PERF_LIMITED, new_val);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
>> +
>> /**
>> * cppc_set_enable - Set to enable CPPC on the processor by writing
>> the
>> * Continuous Performance Control package EnableRegister field.
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>> b/drivers/cpufreq/cppc_cpufreq.c
>> index 66e183b45fb0..afb2cdb67a2f 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -1071,12 +1071,16 @@ static ssize_t store_max_perf(struct
>> cpufreq_policy *policy, const char *buf,
>> return count;
>> }
>>
>> +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>> + cppc_set_perf_limited)
>> +
>> 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);
>
> If the OS wants to get regular feedback about whether the platform had
> to limit
> the perf. level, it will likely try to frequently probe the register.
> In order to see new events, the register must be cleared. So:
> - is it a good idea to allow users to write this register ?
> - is it useful to expose this register if the OS frequently clears it ?
>
> I think the functions are useful, it might just be questionable to expose
> the register in the sysfs.
>
Currently the kernel doesn't automatically poll or clear perf_limited,
so sysfs exposure is for manual monitoring. I can make it read-only
but then users can only observe throttling events and can't clear
them (though bits stay sticky). So, better to expose as RW attribute.
Thank you,
Sumit Gupta
>>
>> static struct freq_attr *cppc_cpufreq_attr[] = {
>> &freqdomain_cpus,
>> @@ -1085,6 +1089,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>> &energy_performance_preference_val,
>> &min_perf,
>> &max_perf,
>> + &perf_limited,
>> NULL,
>> };
....
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
2026-01-24 20:08 ` Sumit Gupta
@ 2026-01-26 8:10 ` zhenglifeng (A)
2026-01-27 11:17 ` Sumit Gupta
0 siblings, 1 reply; 38+ messages in thread
From: zhenglifeng (A) @ 2026-01-26 8:10 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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
On 2026/1/25 4:08, Sumit Gupta wrote:
>
> On 22/01/26 14:48, zhenglifeng (A) wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 2026/1/20 22:56, Sumit Gupta wrote:
>>> Extend cppc_set_epp_perf() to write both auto_sel and energy_perf
>>> registers when they are in FFH or SystemMemory address space.
>>>
>>> This keeps the behavior consistent with PCC case where both registers
>>> are already updated together, but was missing for FFH/SystemMemory.
>>>
>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>> ---
>>> drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++++++---
>>> 1 file changed, 21 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>> index de35aeb07833..45c6bd6ec24b 100644
>>> --- a/drivers/acpi/cppc_acpi.c
>>> +++ b/drivers/acpi/cppc_acpi.c
>>> @@ -1562,6 +1562,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_ffh_sysmem;
>>> + bool epp_ffh_sysmem;
>>> int ret;
>>>
>>> if (!cpc_desc) {
>>> @@ -1572,6 +1574,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_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) &&
>>> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
>>> + autosel_ffh_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);
>>> @@ -1597,11 +1604,22 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>>> 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);
>>> + (epp_ffh_sysmem || autosel_ffh_sysmem)) {
>>> + if (autosel_ffh_sysmem) {
>>> + ret = cpc_write(cpu, auto_sel_reg, enable);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + if (epp_ffh_sysmem) {
>>> + ret = cpc_write(cpu, epp_set_reg,
>>> + perf_ctrls->energy_perf);
>>> + if (ret)
>>> + return ret;
>>> + }
>> Don't know if such a scenario exists, but if one of them is in PCC and the
>> other is in FFH or system memory, only the one in PCC will be updated
>> based on your modifications.
> The current code handles mixed cases correctly.
> When either register is in PCC, the first if block executes and calls
> cpc_write() for both registers. The cpc_write() internally handles
> each register's type (PCC, FFH, or SystemMemory)
Yes, I was wrong.
According to the first if block, cpc_wite() is OK to be called for a
register not in PCC. So it looks like this 'else if' is unnecessary. Only
CPC_SUPPORTED is needed to be checked before calling cpc_write(), isn't it?
>
>
> Thank you,
> Sumit Gupta
>
>
>
>>> } else {
>>> ret = -ENOTSUPP;
>>> - pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
>>> + pr_debug("_CPC in PCC/FFH/SystemMemory are not supported\n");
>>> }
>>>
>>> return ret;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-24 20:32 ` Sumit Gupta
@ 2026-01-26 10:51 ` Pierre Gondois
2026-01-27 11:22 ` Sumit Gupta
0 siblings, 1 reply; 38+ messages in thread
From: Pierre Gondois @ 2026-01-26 10:51 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, zhenglifeng1, ionela.voinescu,
lenb, robert.moore, corbet, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 1/24/26 21:32, Sumit Gupta wrote:
>
> On 22/01/26 17:06, Pierre Gondois wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 1/20/26 15:56, Sumit Gupta wrote:
>>> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read
>>> and
>>> write the MIN_PERF and MAX_PERF registers.
>>>
>>> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
>>> to expose these controls to userspace. The sysfs values are in
>>> frequency
>>> (kHz) for consistency with other cpufreq sysfs files.
>>>
>>> A mutex is used to serialize sysfs store operations to ensure hardware
>>> register writes and perf_ctrls updates are atomic.
>>>
>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>> ---
>>> drivers/acpi/cppc_acpi.c | 44 +++++++++
>>> drivers/cpufreq/cppc_cpufreq.c | 157
>>> +++++++++++++++++++++++++++++++++
>>> include/acpi/cppc_acpi.h | 20 +++++
>>> 3 files changed, 221 insertions(+)
>>>
>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>> index 45c6bd6ec24b..46bf45f8b0f3 100644
>>> --- a/drivers/acpi/cppc_acpi.c
>>> +++ b/drivers/acpi/cppc_acpi.c
>>> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
>>> }
>>> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>>>
>>> +/**
>>> + * cppc_get_min_perf - Read minimum performance register.
>>> + * @cpu: CPU from which to read register.
>>> + * @min_perf: Return address.
>>> + */
>>> +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 minimum performance register.
>>> + * @cpu: CPU to which to write register.
>>> + * @min_perf: the desired minimum performance value to be updated.
>>> + */
>>> +int cppc_set_min_perf(int cpu, u32 min_perf)
>>> +{
>>> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
>>> +}
>>> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
>>> +
>>> +/**
>>> + * cppc_get_max_perf - Read maximum performance register.
>>> + * @cpu: CPU from which to read register.
>>> + * @max_perf: Return address.
>>> + */
>>> +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 maximum performance register.
>>> + * @cpu: CPU to which to write register.
>>> + * @max_perf: the desired maximum performance value to be updated.
>>> + */
>>> +int cppc_set_max_perf(int cpu, u32 max_perf)
>>> +{
>>> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
>>> +}
>>> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>>> +
>>> /**
>>> * cppc_set_enable - Set to enable CPPC on the processor by
>>> writing the
>>> * Continuous Performance Control package EnableRegister field.
>>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>>> b/drivers/cpufreq/cppc_cpufreq.c
>>> index 229880c4eedb..66e183b45fb0 100644
>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>> @@ -28,6 +28,8 @@
>>>
>>> static struct cpufreq_driver cppc_cpufreq_driver;
>>>
>>> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
>>> +
>>
>> Shouldn't concurrent access be handled by the policy->rwsem ?
>>
>> I think this can be checked using either:
>> - lockdep_assert_held_write(&policy->rwsem)
>> - lockdep_assert_held_read(&policy->rwsem)
>>
>> in store/show_max_perf() for instance.
>>
>
> You're right. The cpufreq sysfs already holds policy->rwsem for
> show/store callbacks. I'll remove the mutex and add lockdep
> assertions for the expected locking.
I think it's ok not to have lockde assertions.
It seems that it is a common assumption sysfs files cannot be modified
concurrently. None of the cpufreq driver seems to use lockdep assertion.
> --------
> File: drivers/cpufreq/cpufreq.c
> static ssize_t store(struct kobject *kobj, struct attribute *attr,
> const char *buf, size_t count)
> {
> struct cpufreq_policy *policy = to_policy(kobj);
> ....
> guard(cpufreq_policy_write)(policy);
> --------
>
> Thank you,
> Sumit Gupta
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-24 20:19 ` Sumit Gupta
@ 2026-01-26 11:20 ` Pierre Gondois
2026-01-27 11:08 ` Sumit Gupta
0 siblings, 1 reply; 38+ messages in thread
From: Pierre Gondois @ 2026-01-26 11:20 UTC (permalink / raw)
To: Sumit Gupta, zhenglifeng (A)
Cc: rafael, viresh.kumar, ionela.voinescu, lenb, robert.moore, corbet,
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
On 1/24/26 21:19, Sumit Gupta wrote:
>
> On 25/01/26 01:35, Sumit Gupta wrote:
>>
>> On 22/01/26 14:26, zhenglifeng (A) wrote:
>>> External email: Use caution opening links or attachments
>>>
>>>
>>> On 2026/1/20 22:56, Sumit Gupta wrote:
>>>> Add cppc_get_perf() function to read values of performance control
>>>> registers including desired_perf, min_perf, max_perf, energy_perf,
>>>> and auto_sel.
>>>>
>>>> This provides a read interface to complement the existing
>>>> cppc_set_perf() write interface for performance control registers.
>>>>
>>>> Note that auto_sel is read by cppc_get_perf() but not written by
>>>> cppc_set_perf() to avoid unintended mode changes during performance
>>>> updates. It can be updated with existing dedicated cppc_set_auto_sel()
>>>> API.
>>>>
>>>> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
>>>> perf_ctrls with current hardware register values during cpufreq
>>>> policy initialization.
>>>>
>>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>>> ---
>>>> drivers/acpi/cppc_acpi.c | 80
>>>> ++++++++++++++++++++++++++++++++++
>>>> drivers/cpufreq/cppc_cpufreq.c | 6 +++
>>>> include/acpi/cppc_acpi.h | 5 +++
>>>> 3 files changed, 91 insertions(+)
>>>>
>>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>>> index a09bdabaa804..de35aeb07833 100644
>>>> --- a/drivers/acpi/cppc_acpi.c
>>>> +++ b/drivers/acpi/cppc_acpi.c
>>>> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
>>>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0,
>>>> auto_sel = 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];
>>>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>>>> +
>>>> + /* 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) ||
>>>> + CPC_IN_PCC(auto_sel_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) {
>>>> + 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;
>>> desired_perf_reg is not an optional one, so it has to be supported.
>
> Please ignore my previous reply on this. Had some problem with my email
> client and both mails got mixed.
>
> The register is optional when Autonomous mode is enabled.
> Seems the comment here is already resolved with Pierre's reply.
> We discussed this during v4 [1] also.
> [1]
> https://lore.kernel.org/lkml/ccd45c1b-2f69-4725-918f-18063f00a864@nvidia.com/
>
> Thank you,
> Sumit Gupta
>
>
As suggested at:
https://lore.kernel.org/all/5afea521-7d80-4e72-8809-77af60b0d957@arm.com/
Maybe it would be useful to add a patch checking this:
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index e3796b520e473..7db74e19425e6 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -854,6 +854,12 @@ int acpi_cppc_processor_probe(struct acpi_processor
*pr)
}
per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
+ if (!CPC_SUPPORTED(&cpc_ptr->cpc_regs[DESIRED_PERF]) &&
+ (!osc_sb_cppc2_support_acked ||
+ !CPC_SUPPORTED(&cpc_ptr->cpc_regs[AUTO_SEL_ENABLE])))
+ pr_warn("Desired perf. register is mandatory if CPPCV2
is not supported "
+ "or autonomous selection is disabled.\n");
+
/*
* Initialize the remaining cpc_regs as unsupported.
* Example: In case FW exposes CPPC v2, the below loop will
initialize
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited
2026-01-24 21:04 ` Sumit Gupta
@ 2026-01-26 11:23 ` Pierre Gondois
0 siblings, 0 replies; 38+ messages in thread
From: Pierre Gondois @ 2026-01-26 11:23 UTC (permalink / raw)
To: Sumit Gupta, rafael, viresh.kumar, zhenglifeng1, ionela.voinescu,
lenb, robert.moore, corbet, rdunlap, ray.huang, gautham.shenoy,
mario.limonciello, perry.yuan, zhanjie9, linux-pm, linux-acpi,
linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu
On 1/24/26 22:04, Sumit Gupta wrote:
>
> On 22/01/26 17:21, Pierre Gondois wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 1/20/26 15:56, 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. It contains two sticky bits set by the platform:
>>> - Bit 0 (Desired_Excursion): Set when delivered performance is
>>> constrained below desired performance. Not used when Autonomous
>>> Selection is enabled.
>>> - Bit 1 (Minimum_Excursion): Set when delivered performance is
>>> constrained below minimum performance.
>>>
>>> These bits remain set until OSPM explicitly clears them. The write
>>> operation accepts a bitmask of bits to clear:
>>> - Write 0x1 to clear bit 0
>>> - Write 0x2 to clear bit 1
>>> - Write 0x3 to clear both bits
>>>
>>> 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 occurred during that time
>>> window.
>>>
>>> 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 | 56
>>> ++++++++++++++++++++++++++++++++++
>>> drivers/cpufreq/cppc_cpufreq.c | 5 +++
>>> include/acpi/cppc_acpi.h | 15 +++++++++
>>> 3 files changed, 76 insertions(+)
>>>
>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>> index 46bf45f8b0f3..b46f22f58f56 100644
>>> --- a/drivers/acpi/cppc_acpi.c
>>> +++ b/drivers/acpi/cppc_acpi.c
>>> @@ -1787,6 +1787,62 @@ int cppc_set_max_perf(int cpu, u32 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.
>>> + *
>>> + * The returned value contains sticky status bits indicating
>>> platform-imposed
>>> + * performance limitations.
>>> + *
>>> + * Return: 0 for success, -EIO on 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() - Clear bits in the Performance Limited
>>> register.
>>> + * @cpu: CPU on which to write register.
>>> + * @bits_to_clear: Bitmask of bits to clear in the perf_limited
>>> register.
>>> + *
>>> + * The Performance Limited register contains two sticky bits set by
>>> platform:
>>> + * - Bit 0 (Desired_Excursion): Set when delivered performance is
>>> constrained
>>> + * below desired performance. Not used when Autonomous
>>> Selection is enabled.
>>> + * - Bit 1 (Minimum_Excursion): Set when delivered performance is
>>> constrained
>>> + * below minimum performance.
>>> + *
>>> + * These bits are sticky and remain set until OSPM explicitly
>>> clears them.
>>> + * This function only allows clearing bits (the platform sets them).
>>> + *
>>> + * Return: 0 for success, -EINVAL for invalid bits, -EIO on register
>>> + * access failure, -EOPNOTSUPP if not supported.
>>> + */
>>> +int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
>>> +{
>>> + u64 current_val, new_val;
>>> + int ret;
>>> +
>>> + /* Only bits 0 and 1 are valid */
>>> + if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK)
>>> + return -EINVAL;
>>> +
>>> + if (!bits_to_clear)
>>> + return 0;
>>> +
>>> + ret = cppc_get_perf_limited(cpu, ¤t_val);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + /* Clear the specified bits */
>>> + new_val = current_val & ~bits_to_clear;
>>> +
>>> + return cppc_set_reg_val(cpu, PERF_LIMITED, new_val);
>>> +}
>>> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
>>> +
>>> /**
>>> * cppc_set_enable - Set to enable CPPC on the processor by
>>> writing the
>>> * Continuous Performance Control package EnableRegister field.
>>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>>> b/drivers/cpufreq/cppc_cpufreq.c
>>> index 66e183b45fb0..afb2cdb67a2f 100644
>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>> @@ -1071,12 +1071,16 @@ static ssize_t store_max_perf(struct
>>> cpufreq_policy *policy, const char *buf,
>>> return count;
>>> }
>>>
>>> +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
>>> + cppc_set_perf_limited)
>>> +
>>> 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);
>>
>> If the OS wants to get regular feedback about whether the platform had
>> to limit
>> the perf. level, it will likely try to frequently probe the register.
>> In order to see new events, the register must be cleared. So:
>> - is it a good idea to allow users to write this register ?
>> - is it useful to expose this register if the OS frequently clears it ?
>>
>> I think the functions are useful, it might just be questionable to
>> expose
>> the register in the sysfs.
>>
>
> Currently the kernel doesn't automatically poll or clear perf_limited,
> so sysfs exposure is for manual monitoring. I can make it read-only
> but then users can only observe throttling events and can't clear
> them (though bits stay sticky). So, better to expose as RW attribute.
>
Ok right
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
2026-01-26 11:20 ` Pierre Gondois
@ 2026-01-27 11:08 ` Sumit Gupta
0 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-27 11:08 UTC (permalink / raw)
To: Pierre Gondois, zhenglifeng (A)
Cc: rafael, viresh.kumar, ionela.voinescu, lenb, robert.moore, corbet,
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 2026/1/20 22:56, Sumit Gupta wrote:
>>>>> Add cppc_get_perf() function to read values of performance control
>>>>> registers including desired_perf, min_perf, max_perf, energy_perf,
>>>>> and auto_sel.
>>>>>
>>>>> This provides a read interface to complement the existing
>>>>> cppc_set_perf() write interface for performance control registers.
>>>>>
>>>>> Note that auto_sel is read by cppc_get_perf() but not written by
>>>>> cppc_set_perf() to avoid unintended mode changes during performance
>>>>> updates. It can be updated with existing dedicated
>>>>> cppc_set_auto_sel()
>>>>> API.
>>>>>
>>>>> Use cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
>>>>> perf_ctrls with current hardware register values during cpufreq
>>>>> policy initialization.
>>>>>
>>>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>>>> ---
>>>>> drivers/acpi/cppc_acpi.c | 80
>>>>> ++++++++++++++++++++++++++++++++++
>>>>> drivers/cpufreq/cppc_cpufreq.c | 6 +++
>>>>> include/acpi/cppc_acpi.h | 5 +++
>>>>> 3 files changed, 91 insertions(+)
>>>>>
>>>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>>>> index a09bdabaa804..de35aeb07833 100644
>>>>> --- a/drivers/acpi/cppc_acpi.c
>>>>> +++ b/drivers/acpi/cppc_acpi.c
>>>>> @@ -1739,6 +1739,86 @@ int cppc_set_enable(int cpu, bool 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, *auto_sel_reg;
>>>>> + u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0,
>>>>> auto_sel = 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];
>>>>> + auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
>>>>> +
>>>>> + /* 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) ||
>>>>> + CPC_IN_PCC(auto_sel_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) {
>>>>> + 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;
>>>> desired_perf_reg is not an optional one, so it has to be supported.
>>
>> Please ignore my previous reply on this. Had some problem with my email
>> client and both mails got mixed.
>>
>> The register is optional when Autonomous mode is enabled.
>> Seems the comment here is already resolved with Pierre's reply.
>> We discussed this during v4 [1] also.
>> [1]
>> https://lore.kernel.org/lkml/ccd45c1b-2f69-4725-918f-18063f00a864@nvidia.com/
>>
>>
>> Thank you,
>> Sumit Gupta
>>
>>
> As suggested at:
>
> https://lore.kernel.org/all/5afea521-7d80-4e72-8809-77af60b0d957@arm.com/
>
> Maybe it would be useful to add a patch checking this:
>
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>
> index e3796b520e473..7db74e19425e6 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -854,6 +854,12 @@ int acpi_cppc_processor_probe(struct acpi_processor
> *pr)
> }
> per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
>
> + if (!CPC_SUPPORTED(&cpc_ptr->cpc_regs[DESIRED_PERF]) &&
> + (!osc_sb_cppc2_support_acked ||
> + !CPC_SUPPORTED(&cpc_ptr->cpc_regs[AUTO_SEL_ENABLE])))
> + pr_warn("Desired perf. register is mandatory if CPPCV2
> is not supported "
> + "or autonomous selection is disabled.\n");
> +
> /*
> * Initialize the remaining cpc_regs as unsupported.
> * Example: In case FW exposes CPPC v2, the below loop will
> initialize
>
Thanks for the suggestion. I will add this as a separate patch in v7.
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory
2026-01-26 8:10 ` zhenglifeng (A)
@ 2026-01-27 11:17 ` Sumit Gupta
0 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-27 11:17 UTC (permalink / raw)
To: zhenglifeng (A)
Cc: rafael, viresh.kumar, pierre.gondois, ionela.voinescu, lenb,
robert.moore, corbet, 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 2026/1/20 22:56, Sumit Gupta wrote:
>>>> Extend cppc_set_epp_perf() to write both auto_sel and energy_perf
>>>> registers when they are in FFH or SystemMemory address space.
>>>>
>>>> This keeps the behavior consistent with PCC case where both registers
>>>> are already updated together, but was missing for FFH/SystemMemory.
>>>>
>>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>>> ---
>>>> drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++++++---
>>>> 1 file changed, 21 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>>> index de35aeb07833..45c6bd6ec24b 100644
>>>> --- a/drivers/acpi/cppc_acpi.c
>>>> +++ b/drivers/acpi/cppc_acpi.c
>>>> @@ -1562,6 +1562,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_ffh_sysmem;
>>>> + bool epp_ffh_sysmem;
>>>> int ret;
>>>>
>>>> if (!cpc_desc) {
>>>> @@ -1572,6 +1574,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_ffh_sysmem = CPC_SUPPORTED(epp_set_reg) &&
>>>> + (CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
>>>> + autosel_ffh_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);
>>>> @@ -1597,11 +1604,22 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>>>> 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);
>>>> + (epp_ffh_sysmem || autosel_ffh_sysmem)) {
>>>> + if (autosel_ffh_sysmem) {
>>>> + ret = cpc_write(cpu, auto_sel_reg, enable);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + if (epp_ffh_sysmem) {
>>>> + ret = cpc_write(cpu, epp_set_reg,
>>>> + perf_ctrls->energy_perf);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>> Don't know if such a scenario exists, but if one of them is in PCC and the
>>> other is in FFH or system memory, only the one in PCC will be updated
>>> based on your modifications.
>> The current code handles mixed cases correctly.
>> When either register is in PCC, the first if block executes and calls
>> cpc_write() for both registers. The cpc_write() internally handles
>> each register's type (PCC, FFH, or SystemMemory)
> Yes, I was wrong.
>
> According to the first if block, cpc_wite() is OK to be called for a
> register not in PCC. So it looks like this 'else if' is unnecessary. Only
> CPC_SUPPORTED is needed to be checked before calling cpc_write(), isn't it?
Yes, Once 'osc_cpc_flexible_adr_space_confirmed' is removed,
cppc_set_epp_perf() can be simplified to just call cpc_write() for
supported registers and only do PCC handling when needed.
As Pierre suggested [1], I will send a separate patch set for this
cleanup after the current patch set.
[1]
https://lore.kernel.org/all/c3fd7249-3cba-43e9-85c6-eadd711c0527@nvidia.com/
Thank you,
Sumit Gupta
....
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
2026-01-26 10:51 ` Pierre Gondois
@ 2026-01-27 11:22 ` Sumit Gupta
0 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-27 11:22 UTC (permalink / raw)
To: Pierre Gondois, rafael, viresh.kumar, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, rdunlap, ray.huang,
gautham.shenoy, mario.limonciello, perry.yuan, zhanjie9, linux-pm,
linux-acpi, linux-doc, acpica-devel, linux-kernel
Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
nhartman, bbasu, sumitg
>>> On 1/20/26 15:56, Sumit Gupta wrote:
>>>> Add cppc_get/set_min_perf() and cppc_get/set_max_perf() APIs to read
>>>> and
>>>> write the MIN_PERF and MAX_PERF registers.
>>>>
>>>> Also add sysfs interfaces (min_perf, max_perf) in cppc_cpufreq driver
>>>> to expose these controls to userspace. The sysfs values are in
>>>> frequency
>>>> (kHz) for consistency with other cpufreq sysfs files.
>>>>
>>>> A mutex is used to serialize sysfs store operations to ensure hardware
>>>> register writes and perf_ctrls updates are atomic.
>>>>
>>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>>> ---
>>>> drivers/acpi/cppc_acpi.c | 44 +++++++++
>>>> drivers/cpufreq/cppc_cpufreq.c | 157
>>>> +++++++++++++++++++++++++++++++++
>>>> include/acpi/cppc_acpi.h | 20 +++++
>>>> 3 files changed, 221 insertions(+)
>>>>
>>>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>>>> index 45c6bd6ec24b..46bf45f8b0f3 100644
>>>> --- a/drivers/acpi/cppc_acpi.c
>>>> +++ b/drivers/acpi/cppc_acpi.c
>>>> @@ -1743,6 +1743,50 @@ int cppc_set_auto_sel(int cpu, bool enable)
>>>> }
>>>> EXPORT_SYMBOL_GPL(cppc_set_auto_sel);
>>>>
>>>> +/**
>>>> + * cppc_get_min_perf - Read minimum performance register.
>>>> + * @cpu: CPU from which to read register.
>>>> + * @min_perf: Return address.
>>>> + */
>>>> +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 minimum performance register.
>>>> + * @cpu: CPU to which to write register.
>>>> + * @min_perf: the desired minimum performance value to be updated.
>>>> + */
>>>> +int cppc_set_min_perf(int cpu, u32 min_perf)
>>>> +{
>>>> + return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(cppc_set_min_perf);
>>>> +
>>>> +/**
>>>> + * cppc_get_max_perf - Read maximum performance register.
>>>> + * @cpu: CPU from which to read register.
>>>> + * @max_perf: Return address.
>>>> + */
>>>> +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 maximum performance register.
>>>> + * @cpu: CPU to which to write register.
>>>> + * @max_perf: the desired maximum performance value to be updated.
>>>> + */
>>>> +int cppc_set_max_perf(int cpu, u32 max_perf)
>>>> +{
>>>> + return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>>>> +
>>>> /**
>>>> * cppc_set_enable - Set to enable CPPC on the processor by
>>>> writing the
>>>> * Continuous Performance Control package EnableRegister field.
>>>> diff --git a/drivers/cpufreq/cppc_cpufreq.c
>>>> b/drivers/cpufreq/cppc_cpufreq.c
>>>> index 229880c4eedb..66e183b45fb0 100644
>>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>>> @@ -28,6 +28,8 @@
>>>>
>>>> static struct cpufreq_driver cppc_cpufreq_driver;
>>>>
>>>> +static DEFINE_MUTEX(cppc_cpufreq_autonomous_lock);
>>>> +
>>>
>>> Shouldn't concurrent access be handled by the policy->rwsem ?
>>>
>>> I think this can be checked using either:
>>> - lockdep_assert_held_write(&policy->rwsem)
>>> - lockdep_assert_held_read(&policy->rwsem)
>>>
>>> in store/show_max_perf() for instance.
>>>
>>
>> You're right. The cpufreq sysfs already holds policy->rwsem for
>> show/store callbacks. I'll remove the mutex and add lockdep
>> assertions for the expected locking.
>
> I think it's ok not to have lockde assertions.
>
> It seems that it is a common assumption sysfs files cannot be modified
>
> concurrently. None of the cpufreq driver seems to use lockdep assertion.
>
Sure. I will just remove the mutex, keeping it consistent with other
cpufreq drivers.
Thank you,
Sumit Gupta
>
>> --------
>> File: drivers/cpufreq/cpufreq.c
>> static ssize_t store(struct kobject *kobj, struct attribute *attr,
>> const char *buf, size_t count)
>> {
>> struct cpufreq_policy *policy = to_policy(kobj);
>> ....
>> guard(cpufreq_policy_write)(policy);
>> --------
>>
>> Thank you,
>> Sumit Gupta
>>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2026-01-22 8:27 ` zhenglifeng (A)
@ 2026-01-27 16:24 ` Rafael J. Wysocki
2026-01-27 19:01 ` Sumit Gupta
1 sibling, 1 reply; 38+ messages in thread
From: Rafael J. Wysocki @ 2026-01-27 16:24 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, 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
On Tue, Jan 20, 2026 at 3:57 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
> Add generic helper functions for u64 sysfs attributes that follow the
> common pattern of calling CPPC get/set APIs:
> - cppc_cpufreq_sysfs_show_u64(): reads value and handles -EOPNOTSUPP
> - cppc_cpufreq_sysfs_store_u64(): parses input and calls set function
>
> Add CPPC_CPUFREQ_ATTR_RW_U64() macro to generate show/store functions
> using these helpers, reducing boilerplate for simple attributes.
>
> Convert auto_act_window and energy_performance_preference_val to use
> the new macro.
>
> No functional changes.
>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> drivers/cpufreq/cppc_cpufreq.c | 69 ++++++++++++----------------------
> 1 file changed, 25 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 36e8a75a37f1..c95dcd7719c3 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -863,73 +863,54 @@ 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((int)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)
> -{
> - 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");
> -
> - if (ret)
> - return ret;
> -
> - return sysfs_emit(buf, "%llu\n", val);
> +#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
> +static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
> +{ \
> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
> +} \
> +static ssize_t store_##_name(struct cpufreq_policy *policy, \
> + const char *buf, size_t count) \
> +{ \
> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
> + buf, count); \
> }
>
> -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
> - const char *buf, size_t count)
> -{
> - u64 val;
> - int ret;
> +CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
> + cppc_set_auto_act_window)
>
> - ret = kstrtou64(buf, 0, &val);
> - if (ret)
> - return ret;
> -
> - ret = cppc_set_epp(policy->cpu, val);
> - if (ret)
> - return ret;
> -
> - return count;
> -}
> +CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
> + cppc_get_epp_perf, cppc_set_epp)
>
> cpufreq_freq_attr_ro(freqdomain_cpus);
> cpufreq_freq_attr_rw(auto_select);
> --
It looks like this patch could be applied independently of the other
patches in the series.
Do you want me to do so?
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
2026-01-20 14:56 ` [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
2026-01-22 8:28 ` zhenglifeng (A)
@ 2026-01-27 16:27 ` Rafael J. Wysocki
2026-01-27 19:11 ` Sumit Gupta
1 sibling, 1 reply; 38+ messages in thread
From: Rafael J. Wysocki @ 2026-01-27 16:27 UTC (permalink / raw)
To: Sumit Gupta
Cc: rafael, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, 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
On Tue, Jan 20, 2026 at 3:57 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
> the same is available in 'struct cppc_perf_ctrls' which is used.
> - Move the 'auto_sel' field from 'struct cppc_perf_caps' to
> 'struct cppc_perf_ctrls' as it represents a control register.
>
> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
> include/acpi/cppc_acpi.h | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index 4bcdcaf8bf2c..6573a759eb8d 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -119,8 +119,6 @@ struct cppc_perf_caps {
> u32 lowest_nonlinear_perf;
> u32 lowest_freq;
> u32 nominal_freq;
> - u32 energy_perf;
> - bool auto_sel;
> };
>
> struct cppc_perf_ctrls {
> @@ -128,6 +126,7 @@ struct cppc_perf_ctrls {
> u32 min_perf;
> u32 desired_perf;
> u32 energy_perf;
> + bool auto_sel;
> };
>
> struct cppc_perf_fb_ctrs {
> --
It looks like this patch and the next one can be applied independently
of the other patches in the series.
Do you want me to do that?
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store
2026-01-27 16:24 ` Rafael J. Wysocki
@ 2026-01-27 19:01 ` Sumit Gupta
2026-01-27 20:17 ` Rafael J. Wysocki
0 siblings, 1 reply; 38+ messages in thread
From: Sumit Gupta @ 2026-01-27 19:01 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: viresh.kumar, pierre.gondois, zhenglifeng1, ionela.voinescu, lenb,
robert.moore, corbet, 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 27/01/26 21:54, Rafael J. Wysocki wrote:
> External email: Use caution opening links or attachments
>
>
> On Tue, Jan 20, 2026 at 3:57 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>> Add generic helper functions for u64 sysfs attributes that follow the
>> common pattern of calling CPPC get/set APIs:
>> - cppc_cpufreq_sysfs_show_u64(): reads value and handles -EOPNOTSUPP
>> - cppc_cpufreq_sysfs_store_u64(): parses input and calls set function
>>
>> Add CPPC_CPUFREQ_ATTR_RW_U64() macro to generate show/store functions
>> using these helpers, reducing boilerplate for simple attributes.
>>
>> Convert auto_act_window and energy_performance_preference_val to use
>> the new macro.
>>
>> No functional changes.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> drivers/cpufreq/cppc_cpufreq.c | 69 ++++++++++++----------------------
>> 1 file changed, 25 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>> index 36e8a75a37f1..c95dcd7719c3 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -863,73 +863,54 @@ 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((int)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)
>> -{
>> - 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");
>> -
>> - if (ret)
>> - return ret;
>> -
>> - return sysfs_emit(buf, "%llu\n", val);
>> +#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
>> +static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
>> +{ \
>> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
>> +} \
>> +static ssize_t store_##_name(struct cpufreq_policy *policy, \
>> + const char *buf, size_t count) \
>> +{ \
>> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
>> + buf, count); \
>> }
>>
>> -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
>> - const char *buf, size_t count)
>> -{
>> - u64 val;
>> - int ret;
>> +CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
>> + cppc_set_auto_act_window)
>>
>> - ret = kstrtou64(buf, 0, &val);
>> - if (ret)
>> - return ret;
>> -
>> - ret = cppc_set_epp(policy->cpu, val);
>> - if (ret)
>> - return ret;
>> -
>> - return count;
>> -}
>> +CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
>> + cppc_get_epp_perf, cppc_set_epp)
>>
>> cpufreq_freq_attr_ro(freqdomain_cpus);
>> cpufreq_freq_attr_rw(auto_select);
>> --
> It looks like this patch could be applied independently of the other
> patches in the series.
>
> Do you want me to do so?
Yes, this patch is independent and can be applied.
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
2026-01-27 16:27 ` Rafael J. Wysocki
@ 2026-01-27 19:11 ` Sumit Gupta
0 siblings, 0 replies; 38+ messages in thread
From: Sumit Gupta @ 2026-01-27 19:11 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: viresh.kumar, pierre.gondois, zhenglifeng1, ionela.voinescu, lenb,
robert.moore, corbet, 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 27/01/26 21:57, Rafael J. Wysocki wrote:
> External email: Use caution opening links or attachments
>
>
> On Tue, Jan 20, 2026 at 3:57 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>> - Remove redundant energy_perf field from 'struct cppc_perf_caps' as
>> the same is available in 'struct cppc_perf_ctrls' which is used.
>> - Move the 'auto_sel' field from 'struct cppc_perf_caps' to
>> 'struct cppc_perf_ctrls' as it represents a control register.
>>
>> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>> include/acpi/cppc_acpi.h | 3 +--
>> 1 file changed, 1 insertion(+), 2 deletions(-)
>>
>> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
>> index 4bcdcaf8bf2c..6573a759eb8d 100644
>> --- a/include/acpi/cppc_acpi.h
>> +++ b/include/acpi/cppc_acpi.h
>> @@ -119,8 +119,6 @@ struct cppc_perf_caps {
>> u32 lowest_nonlinear_perf;
>> u32 lowest_freq;
>> u32 nominal_freq;
>> - u32 energy_perf;
>> - bool auto_sel;
>> };
>>
>> struct cppc_perf_ctrls {
>> @@ -128,6 +126,7 @@ struct cppc_perf_ctrls {
>> u32 min_perf;
>> u32 desired_perf;
>> u32 energy_perf;
>> + bool auto_sel;
>> };
>>
>> struct cppc_perf_fb_ctrs {
>> --
> It looks like this patch and the next one can be applied independently
> of the other patches in the series.
>
> Do you want me to do that?
Yes, this patch is independent and can be applied.
Also, the 'patch 3' can be applied independently.
- patch 3: 'ACPI: CPPC: Rename EPP constants for clarity'
Thank you,
Sumit Gupta
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store
2026-01-27 19:01 ` Sumit Gupta
@ 2026-01-27 20:17 ` Rafael J. Wysocki
0 siblings, 0 replies; 38+ messages in thread
From: Rafael J. Wysocki @ 2026-01-27 20:17 UTC (permalink / raw)
To: Sumit Gupta
Cc: Rafael J. Wysocki, viresh.kumar, pierre.gondois, zhenglifeng1,
ionela.voinescu, lenb, robert.moore, corbet, 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
On Tue, Jan 27, 2026 at 8:01 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
>
> On 27/01/26 21:54, Rafael J. Wysocki wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > On Tue, Jan 20, 2026 at 3:57 PM Sumit Gupta <sumitg@nvidia.com> wrote:
> >> Add generic helper functions for u64 sysfs attributes that follow the
> >> common pattern of calling CPPC get/set APIs:
> >> - cppc_cpufreq_sysfs_show_u64(): reads value and handles -EOPNOTSUPP
> >> - cppc_cpufreq_sysfs_store_u64(): parses input and calls set function
> >>
> >> Add CPPC_CPUFREQ_ATTR_RW_U64() macro to generate show/store functions
> >> using these helpers, reducing boilerplate for simple attributes.
> >>
> >> Convert auto_act_window and energy_performance_preference_val to use
> >> the new macro.
> >>
> >> No functional changes.
> >>
> >> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> >> ---
> >> drivers/cpufreq/cppc_cpufreq.c | 69 ++++++++++++----------------------
> >> 1 file changed, 25 insertions(+), 44 deletions(-)
> >>
> >> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> >> index 36e8a75a37f1..c95dcd7719c3 100644
> >> --- a/drivers/cpufreq/cppc_cpufreq.c
> >> +++ b/drivers/cpufreq/cppc_cpufreq.c
> >> @@ -863,73 +863,54 @@ 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((int)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)
> >> -{
> >> - 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");
> >> -
> >> - if (ret)
> >> - return ret;
> >> -
> >> - return sysfs_emit(buf, "%llu\n", val);
> >> +#define CPPC_CPUFREQ_ATTR_RW_U64(_name, _get_func, _set_func) \
> >> +static ssize_t show_##_name(struct cpufreq_policy *policy, char *buf) \
> >> +{ \
> >> + return cppc_cpufreq_sysfs_show_u64(policy->cpu, _get_func, buf);\
> >> +} \
> >> +static ssize_t store_##_name(struct cpufreq_policy *policy, \
> >> + const char *buf, size_t count) \
> >> +{ \
> >> + return cppc_cpufreq_sysfs_store_u64(policy->cpu, _set_func, \
> >> + buf, count); \
> >> }
> >>
> >> -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
> >> - const char *buf, size_t count)
> >> -{
> >> - u64 val;
> >> - int ret;
> >> +CPPC_CPUFREQ_ATTR_RW_U64(auto_act_window, cppc_get_auto_act_window,
> >> + cppc_set_auto_act_window)
> >>
> >> - ret = kstrtou64(buf, 0, &val);
> >> - if (ret)
> >> - return ret;
> >> -
> >> - ret = cppc_set_epp(policy->cpu, val);
> >> - if (ret)
> >> - return ret;
> >> -
> >> - return count;
> >> -}
> >> +CPPC_CPUFREQ_ATTR_RW_U64(energy_performance_preference_val,
> >> + cppc_get_epp_perf, cppc_set_epp)
> >>
> >> cpufreq_freq_attr_ro(freqdomain_cpus);
> >> cpufreq_freq_attr_rw(auto_select);
> >> --
> > It looks like this patch could be applied independently of the other
> > patches in the series.
> >
> > Do you want me to do so?
>
> Yes, this patch is independent and can be applied.
Applied as 6.20 material, thanks!
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2026-01-27 20:17 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-20 14:56 [PATCH v6 0/9] Enhanced autonomous selection and improvements Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 1/9] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2026-01-22 8:27 ` zhenglifeng (A)
2026-01-27 16:24 ` Rafael J. Wysocki
2026-01-27 19:01 ` Sumit Gupta
2026-01-27 20:17 ` Rafael J. Wysocki
2026-01-20 14:56 ` [PATCH v6 2/9] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
2026-01-22 8:28 ` zhenglifeng (A)
2026-01-27 16:27 ` Rafael J. Wysocki
2026-01-27 19:11 ` Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 3/9] ACPI: CPPC: Rename EPP constants for clarity Sumit Gupta
2026-01-22 8:31 ` zhenglifeng (A)
2026-01-20 14:56 ` [PATCH v6 4/9] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
2026-01-22 8:56 ` zhenglifeng (A)
2026-01-22 11:30 ` Pierre Gondois
2026-01-22 11:42 ` zhenglifeng (A)
2026-01-24 20:05 ` Sumit Gupta
2026-01-24 20:19 ` Sumit Gupta
2026-01-26 11:20 ` Pierre Gondois
2026-01-27 11:08 ` Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 5/9] ACPI: CPPC: Extend cppc_set_epp_perf() for FFH/SystemMemory Sumit Gupta
2026-01-22 9:18 ` zhenglifeng (A)
2026-01-24 20:08 ` Sumit Gupta
2026-01-26 8:10 ` zhenglifeng (A)
2026-01-27 11:17 ` Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 6/9] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2026-01-22 11:36 ` Pierre Gondois
2026-01-24 20:32 ` Sumit Gupta
2026-01-26 10:51 ` Pierre Gondois
2026-01-27 11:22 ` Sumit Gupta
2026-01-22 12:35 ` zhenglifeng (A)
2026-01-24 20:52 ` Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 7/9] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
2026-01-22 11:51 ` Pierre Gondois
2026-01-24 21:04 ` Sumit Gupta
2026-01-26 11:23 ` Pierre Gondois
2026-01-20 14:56 ` [PATCH v6 8/9] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
2026-01-20 14:56 ` [PATCH v6 9/9] cpufreq: CPPC: Update cached perf_ctrls on sysfs write Sumit Gupta
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox