linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 00/11] Enhanced autonomous selection and improvements
@ 2025-12-23 12:12 Sumit Gupta
  2025-12-23 12:12 ` [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
                   ` (10 more replies)
  0 siblings, 11 replies; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:12 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

This patch series enhances the ACPI CPPC CPUFREQ driver with
comprehensive support for autonomous performance selection, expanded
runtime control interfaces and improvements.

CPPC autonomous mode (auto_sel) enables hardware-driven CPU performance
scaling using Energy Performance Preference (EPP) hints, without OS
governor intervention. Currently, there's limited runtime control and
visibility into CPPC performance registers when using autonomous mode.

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.
3. Keeping policy limits synchronized with hardware registers to ensure
   consistent behavior between HW state and userspace view.
4. Providing boot parameter for system-wide autonomous mode enablement.

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, 3 & 4: Improvements. Can be applied independently.
- Patch 5: ACPI APIs and sysfs for min/max_perf. Independent.
- Patch 6: ACPI APIs and sysfs for perf_limited. Independent.
- Patch 7: Add ABI documentation. Depends on Patch 5 and 6.
- Patch 8: Sync policy when min/max_perf updated. Depends on Patch 5.
- Patch 9: Sync policy when toggling auto_select. Depends on Patch 8.
- Patch 10: Make scaling_min/max_freq read-only. Depends on Patch 9.
- Patch 11: Boot parameter support. Depends on Patch 9.

Patches 1-7 can be applied first if they are Ok.

---
v4[4] -> v5:
- patch2: new patch to clean up cppc_perf_caps and cppc_perf_ctrls.
- patch3: moved cppc_get_perf() call from v4's patch8 to here.
- patch5: fix ABI documentation format (kernel test robot report).
- patch6: update perf_limited logic to only allow clearing set bits.
- patch8: new patch to sync policy limits when updating min/max_perf,
  update_policy parameter moved from v4's patch5.
- patch9: refactored from v4's patch7, added update_reg parameter,
  handle -EOPNOTSUPP for auto_sel (platforms with fixed auto_sel).
- patch10: new to make scaling_min/max_freq read-only in auto_sel mode.
- patch11 (v4's patch8): moved boot param handling to cpu_init,
  handle -EOPNOTSUPP for EPP (platforms without EPP support).
- general code improvements, kernel-doc updates, reduced redundancies.

[4] has detailed changelog of previous versions.

Sumit Gupta (11):
  cpufreq: CPPC: Add generic helpers for sysfs show/store
  ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
  ACPI: CPPC: Add cppc_get_perf() API to read performance controls
  ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp
  ACPI: CPPC: add APIs and sysfs interface for min/max_perf
  ACPI: CPPC: add APIs and sysfs interface for perf_limited
  cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  cpufreq: CPPC: sync policy limits when updating min/max_perf
  cpufreq: CPPC: sync policy limits when toggling auto_select
  cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel
    enabled
  cpufreq: CPPC: add autonomous mode boot parameter support

 .../ABI/testing/sysfs-devices-system-cpu      |  42 ++
 .../admin-guide/kernel-parameters.txt         |  13 +
 drivers/acpi/cppc_acpi.c                      | 214 ++++++++-
 drivers/cpufreq/cppc_cpufreq.c                | 434 ++++++++++++++++--
 include/acpi/cppc_acpi.h                      |  46 +-
 5 files changed, 712 insertions(+), 37 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/

-- 
2.34.1


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
@ 2025-12-23 12:12 ` Sumit Gupta
  2025-12-25  3:41   ` zhenglifeng (A)
  2025-12-23 12:12 ` [PATCH v5 02/11] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:12 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add generic show/store helper functions for u64 sysfs attributes:
- cppc_cpufreq_sysfs_show_u64()
- cppc_cpufreq_sysfs_store_u64()

Refactor auto_act_window and energy_performance_preference_val
attributes to use these helpers, eliminating code duplication.

No functional changes.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 71 +++++++++++++++-------------------
 1 file changed, 31 insertions(+), 40 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 9eac77c4f294..7c26ce554e29 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -831,72 +831,63 @@ 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)
+static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
 {
-	u64 val;
-	int ret;
-
-	ret = cppc_get_epp_perf(policy->cpu, &val);
-
-	/* show "<unsupported>" when this register is not supported by cpc */
-	if (ret == -EOPNOTSUPP)
-		return sysfs_emit(buf, "<unsupported>\n");
-
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%llu\n", val);
+	return cppc_cpufreq_sysfs_show_u64(policy->cpu,
+					   cppc_get_auto_act_window, buf);
 }
 
-static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
-						       const char *buf, size_t count)
+static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
+				     const char *buf, size_t count)
 {
-	u64 val;
-	int ret;
-
-	ret = kstrtou64(buf, 0, &val);
-	if (ret)
-		return ret;
+	return cppc_cpufreq_sysfs_store_u64(policy->cpu,
+					    cppc_set_auto_act_window,
+					    buf, count);
+}
 
-	ret = cppc_set_epp(policy->cpu, val);
-	if (ret)
-		return ret;
+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);
+}
 
-	return count;
+static ssize_t
+store_energy_performance_preference_val(struct cpufreq_policy *policy,
+					const char *buf, size_t count)
+{
+	return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp,
+					    buf, count);
 }
 
 cpufreq_freq_attr_ro(freqdomain_cpus);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 02/11] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
  2025-12-23 12:12 ` [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2025-12-23 12:12 ` Sumit Gupta
  2025-12-23 12:12 ` [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:12 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

- 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.
---
 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 13fa81504844..a090b010f5f1 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] 23+ messages in thread

* [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
  2025-12-23 12:12 ` [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
  2025-12-23 12:12 ` [PATCH v5 02/11] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
@ 2025-12-23 12:12 ` Sumit Gupta
  2025-12-25  8:21   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp Sumit Gupta
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:12 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add cppc_get_perf() function to read values of performance control
registers including desired_perf, min_perf, max_perf, 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.

Also call cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
perf_ctrls with current hardware register values during cpufreq
initialization for each CPU policy.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c       | 79 ++++++++++++++++++++++++++++++++++
 drivers/cpufreq/cppc_cpufreq.c |  6 +++
 include/acpi/cppc_acpi.h       |  5 +++
 3 files changed, 90 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index e66e20d1f31b..a4e89fe6aab5 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1732,6 +1732,85 @@ int cppc_set_enable(int cpu, bool enable)
 	return cppc_set_reg_val(cpu, ENABLE, enable);
 }
 EXPORT_SYMBOL_GPL(cppc_set_enable);
+/**
+ * cppc_get_perf - Get a CPU's performance controls.
+ * @cpu: CPU for which to get performance controls.
+ * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
+ *
+ * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
+ */
+int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cpc_register_resource *desired_perf_reg,
+				     *min_perf_reg, *max_perf_reg,
+				     *energy_perf_reg, *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.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 7c26ce554e29..a87e7bb2e2f1 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -562,6 +562,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 a090b010f5f1..12a1dc31bf2a 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -150,6 +150,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);
@@ -191,6 +192,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] 23+ messages in thread

* [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (2 preceding siblings ...)
  2025-12-23 12:12 ` [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-25  3:56   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

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.

Also update EPP constants for better clarity:
- Add CPPC_EPP_PERFORMANCE_PREF (0x00) for performance preference
- Add CPPC_EPP_ENERGY_EFFICIENCY_PREF (0xFF) for energy efficiency

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c | 35 +++++++++++++++++++++++++++++------
 include/acpi/cppc_acpi.h |  3 ++-
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index a4e89fe6aab5..403ee988a8c6 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1556,6 +1556,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) {
@@ -1566,6 +1568,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);
@@ -1590,14 +1597,30 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
 		/* after writing CPC, transfer the ownership of PCC to platform */
 		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
 		up_write(&pcc_ss_data->pcc_lock);
-	} else if (osc_cpc_flexible_adr_space_confirmed &&
-		   CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
-		ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+	} else if (osc_cpc_flexible_adr_space_confirmed) {
+		if (!epp_ffh_sysmem && !autosel_ffh_sysmem) {
+			ret = -EOPNOTSUPP;
+		} else {
+			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");
+		ret = -EOPNOTSUPP;
 	}
 
+	if (ret == -EOPNOTSUPP)
+		pr_debug("CPU%d: _CPC not in PCC/FFH/SystemMemory\n", cpu);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
@@ -1609,7 +1632,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 12a1dc31bf2a..2860a0252313 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] 23+ messages in thread

* [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (3 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-25  9:03   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add cppc_get/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 | 160 +++++++++++++++++++++++++++++++++
 include/acpi/cppc_acpi.h       |  20 +++++
 3 files changed, 224 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 403ee988a8c6..9f28c20d902d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1742,6 +1742,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 a87e7bb2e2f1..1e282dfabc76 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_update_autosel_config_lock);
+
 #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
 static enum {
 	FIE_UNSET = -1,
@@ -538,6 +540,46 @@ static void populate_efficiency_class(void)
 }
 #endif
 
+/**
+ * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
+ * @policy: cpufreq policy
+ * @val: performance value to set
+ * @is_min: true for min_perf, false for max_perf
+ */
+static int cppc_cpufreq_set_mperf_limit(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("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
+				is_min ? "min" : "max", (u64)perf, cpu, ret);
+		return ret;
+	}
+
+	if (is_min)
+		cpu_data->perf_ctrls.min_perf = perf;
+	else
+		cpu_data->perf_ctrls.max_perf = perf;
+
+	return 0;
+}
+
+#define cppc_cpufreq_set_min_perf(policy, val) \
+	cppc_cpufreq_set_mperf_limit(policy, val, true)
+
+#define cppc_cpufreq_set_max_perf(policy, val) \
+	cppc_cpufreq_set_mperf_limit(policy, val, false)
+
 static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
 {
 	struct cppc_cpudata *cpu_data;
@@ -896,16 +938,134 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
 					    buf, count);
 }
 
+/**
+ * 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;
+	u64 perf;
+	int ret;
+
+	ret = cppc_get_min_perf(policy->cpu, &perf);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
+
+	/* Convert performance to frequency (kHz) for user */
+	return sysfs_emit(buf, "%u\n",
+			  cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+/**
+ * store_min_perf - Set minimum performance from frequency (kHz)
+ * @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_update_autosel_config_lock);
+	ret = cppc_cpufreq_set_min_perf(policy, perf);
+	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;
+	u64 perf;
+	int ret;
+
+	ret = cppc_get_max_perf(policy->cpu, &perf);
+	if (ret == -EOPNOTSUPP)
+		return sysfs_emit(buf, "<unsupported>\n");
+	if (ret)
+		return ret;
+
+	/* Convert performance to frequency (kHz) for user */
+	return sysfs_emit(buf, "%u\n",
+			  cppc_perf_to_khz(&cpu_data->perf_caps, perf));
+}
+
+/**
+ * store_max_perf - Set maximum performance from frequency (kHz)
+ * @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_update_autosel_config_lock);
+	ret = cppc_cpufreq_set_max_perf(policy, perf);
+	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 2860a0252313..a49b50bddaf9 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -173,6 +173,10 @@ extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
 extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
 extern int cppc_get_auto_sel(int cpu, bool *enable);
 extern int cppc_set_auto_sel(int cpu, bool enable);
+extern int cppc_get_min_perf(int cpu, u64 *min_perf);
+extern int cppc_set_min_perf(int cpu, 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);
@@ -265,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_min_perf(int cpu, 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] 23+ messages in thread

* [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (4 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-25 12:06   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add sysfs interface to read/write the Performance Limited register.

The Performance Limited register indicates to the OS that an
unpredictable event (like thermal throttling) has limited processor
performance. 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 1 to clear bit 0
  - Write 2 to clear bit 1
  - Write 3 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 | 31 +++++++++++++++++++
 include/acpi/cppc_acpi.h       | 15 +++++++++
 3 files changed, 102 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9f28c20d902d..ffd866c1c00d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1786,6 +1786,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, &current_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 1e282dfabc76..1f8825006940 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1052,12 +1052,42 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
 	return count;
 }
 
+/**
+ * show_perf_limited - Show Performance Limited register status
+ * @policy: cpufreq policy
+ * @buf: buffer to write the value to
+ *
+ * Read the Performance Limited register to check if platform throttling
+ * (thermal/power/current limits) occurred.
+ */
+static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
+{
+	return cppc_cpufreq_sysfs_show_u64(policy->cpu,
+					   cppc_get_perf_limited, buf);
+}
+
+/**
+ * store_perf_limited - Clear Performance Limited register bits
+ * @policy: cpufreq policy
+ * @buf: buffer containing the bitmask of bits to clear
+ * @count: number of bytes in buf
+ *
+ * Write 1 to clear bit 0, 2 to clear bit 1, or 3 to clear both.
+ */
+static ssize_t store_perf_limited(struct cpufreq_policy *policy,
+				  const char *buf, size_t count)
+{
+	return cppc_cpufreq_sysfs_store_u64(policy->cpu,
+					    cppc_set_perf_limited, buf, count);
+}
+
 cpufreq_freq_attr_ro(freqdomain_cpus);
 cpufreq_freq_attr_rw(auto_select);
 cpufreq_freq_attr_rw(auto_act_window);
 cpufreq_freq_attr_rw(energy_performance_preference_val);
 cpufreq_freq_attr_rw(min_perf);
 cpufreq_freq_attr_rw(max_perf);
+cpufreq_freq_attr_rw(perf_limited);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
 	&freqdomain_cpus,
@@ -1066,6 +1096,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 a49b50bddaf9..57e04326a4b6 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;
@@ -177,6 +182,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);
@@ -285,6 +292,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] 23+ messages in thread

* [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (5 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-24 18:32   ` kernel test robot
  2025-12-26  0:20   ` Bagas Sanjaya
  2025-12-23 12:13 ` [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf Sumit Gupta
                   ` (3 subsequent siblings)
  10 siblings, 2 replies; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add sysfs interfaces for Minimum Performance, Maximum Performance
and Performance Limited Register in the cppc_cpufreq driver.

Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../ABI/testing/sysfs-devices-system-cpu      | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 3a05604c21bf..b022cbf46adc 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -327,6 +327,48 @@ 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:
+		  1 = clear bit 0 (desired performance excursion)
+		  2 = clear bit 1 (minimum performance excursion)
+		  3 = 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] 23+ messages in thread

* [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (6 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-25 13:56   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select Sumit Gupta
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

When min_perf or max_perf is updated via sysfs in autonomous mode, the
policy frequency limits should also be updated to reflect the new
performance bounds.

Add @update_policy parameter to cppc_cpufreq_set_mperf_limit() to
control whether policy constraints are synced with HW registers.
The policy is updated only when autonomous selection is enabled to
keep SW limits in sync with HW.

This ensures that scaling_min_freq and scaling_max_freq values remain
consistent with the actual min/max_perf register values when operating
in autonomous mode.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 35 ++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 1f8825006940..0202c7b823e6 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -544,14 +544,20 @@ static void populate_efficiency_class(void)
  * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
  * @policy: cpufreq policy
  * @val: performance value to set
+ * @update_policy: whether to update policy constraints
  * @is_min: true for min_perf, false for max_perf
+ *
+ * When @update_policy is true, updates cpufreq policy frequency limits.
+ * @update_policy is false during cpu_init when policy isn't fully set up.
  */
 static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
-					bool is_min)
+					bool update_policy, bool is_min)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
 	unsigned int cpu = policy->cpu;
+	struct freq_qos_request *req;
+	unsigned int freq;
 	u32 perf;
 	int ret;
 
@@ -571,15 +577,26 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
 	else
 		cpu_data->perf_ctrls.max_perf = perf;
 
+	if (update_policy) {
+		freq = cppc_perf_to_khz(caps, perf);
+		req = is_min ? policy->min_freq_req : policy->max_freq_req;
+
+		ret = freq_qos_update_request(req, freq);
+		if (ret < 0) {
+			pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n",
+				is_min ? "min" : "max", cpu, ret);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
-#define cppc_cpufreq_set_min_perf(policy, val) \
-	cppc_cpufreq_set_mperf_limit(policy, val, true)
-
-#define cppc_cpufreq_set_max_perf(policy, val) \
-	cppc_cpufreq_set_mperf_limit(policy, val, false)
+#define cppc_cpufreq_set_min_perf(policy, val, update_policy) \
+	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, true)
 
+#define cppc_cpufreq_set_max_perf(policy, val, update_policy) \
+	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, false)
 static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
 {
 	struct cppc_cpudata *cpu_data;
@@ -988,7 +1005,8 @@ static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf,
 	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
 
 	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
-	ret = cppc_cpufreq_set_min_perf(policy, perf);
+	ret = cppc_cpufreq_set_min_perf(policy, perf,
+					cpu_data->perf_ctrls.auto_sel);
 	if (ret)
 		return ret;
 
@@ -1045,7 +1063,8 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
 	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
 
 	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
-	ret = cppc_cpufreq_set_max_perf(policy, perf);
+	ret = cppc_cpufreq_set_max_perf(policy, perf,
+					cpu_data->perf_ctrls.auto_sel);
 	if (ret)
 		return ret;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (7 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-26  2:55   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled Sumit Gupta
  2025-12-23 12:13 ` [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

When CPPC autonomous selection (auto_select) is enabled or disabled,
the policy min/max frequency limits should be updated appropriately to
reflect the new operating mode.

Currently, toggling auto_select only changes the hardware register but
doesn't update the cpufreq policy constraints, which can lead to
inconsistent behavior between the hardware state and the policy limits
visible to userspace.

Add cppc_cpufreq_update_autosel_config() function to handle the
auto_select toggle by syncing min/max_perf values with policy
constraints. When enabling auto_sel, restore preserved min/max_perf
values to policy limits. When disabling, reset policy to defaults
while preserving hardware register values for later use.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 112 +++++++++++++++++++++++++++------
 1 file changed, 92 insertions(+), 20 deletions(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 0202c7b823e6..b1f570d6de34 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -544,14 +544,20 @@ static void populate_efficiency_class(void)
  * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
  * @policy: cpufreq policy
  * @val: performance value to set
+ * @update_reg: whether to update hardware register
  * @update_policy: whether to update policy constraints
  * @is_min: true for min_perf, false for max_perf
  *
+ * When @update_reg is true, writes to HW registers and preserves values.
  * When @update_policy is true, updates cpufreq policy frequency limits.
+ *
+ * @update_reg is false when disabling auto_sel to preserve HW values.
+ * The preserved value is used on next enabling of the autonomous mode.
  * @update_policy is false during cpu_init when policy isn't fully set up.
  */
 static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
-					bool update_policy, bool is_min)
+					bool update_reg, bool update_policy,
+					bool is_min)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
@@ -563,19 +569,22 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
 
 	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("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
-				is_min ? "min" : "max", (u64)perf, cpu, ret);
-		return ret;
-	}
+	if (update_reg) {
+		ret = is_min ? cppc_set_min_perf(cpu, perf) :
+			       cppc_set_max_perf(cpu, perf);
+		if (ret) {
+			if (ret != -EOPNOTSUPP)
+				pr_warn("CPU%d: set %s_perf=%llu failed (%d)\n",
+					cpu, is_min ? "min" : "max",
+					(u64)perf, ret);
+			return ret;
+		}
 
-	if (is_min)
-		cpu_data->perf_ctrls.min_perf = perf;
-	else
-		cpu_data->perf_ctrls.max_perf = perf;
+		if (is_min)
+			cpu_data->perf_ctrls.min_perf = perf;
+		else
+			cpu_data->perf_ctrls.max_perf = perf;
+	}
 
 	if (update_policy) {
 		freq = cppc_perf_to_khz(caps, perf);
@@ -592,11 +601,74 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
 	return 0;
 }
 
-#define cppc_cpufreq_set_min_perf(policy, val, update_policy) \
-	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, true)
+#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy)     \
+	cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy,  \
+				     true)
+
+#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy)     \
+	cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy,  \
+				     false)
+
+/**
+ * cppc_cpufreq_update_autosel_config - Update autonomous selection config
+ * @policy: cpufreq policy
+ * @is_auto_sel: enable/disable autonomous selection
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
+					      bool is_auto_sel)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+	u64 min_perf = caps->lowest_nonlinear_perf;
+	u64 max_perf = caps->nominal_perf;
+	unsigned int cpu = policy->cpu;
+	bool update_reg = is_auto_sel;
+	bool update_policy = true;
+	int ret;
+
+	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
+
+	if (is_auto_sel) {
+		/* Use preserved values if available, else use defaults */
+		if (cpu_data->perf_ctrls.min_perf)
+			min_perf = cpu_data->perf_ctrls.min_perf;
+		if (cpu_data->perf_ctrls.max_perf)
+			max_perf = cpu_data->perf_ctrls.max_perf;
+	}
+
+	/*
+	 * Set min/max performance and update policy constraints.
+	 *   When enabling: update both HW registers and policy.
+	 *   When disabling: update policy only, preserve HW registers.
+	 * Continue even if min/max are not supported, as EPP and autosel
+	 * might still be supported.
+	 */
+	ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg,
+					update_policy);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+
+	ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg,
+					update_policy);
+	if (ret && ret != -EOPNOTSUPP)
+		return ret;
+
+	/* Update auto_sel register */
+	ret = cppc_set_auto_sel(cpu, is_auto_sel);
+	if (ret && ret != -EOPNOTSUPP) {
+		pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n",
+			is_auto_sel, cpu, ret);
+		return ret;
+	}
+	if (!ret)
+		cpu_data->perf_ctrls.auto_sel = is_auto_sel;
+
+	return 0;
+}
+
 
-#define cppc_cpufreq_set_max_perf(policy, val, update_policy) \
-	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, false)
 static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
 {
 	struct cppc_cpudata *cpu_data;
@@ -889,7 +961,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
 	if (ret)
 		return ret;
 
-	ret = cppc_set_auto_sel(policy->cpu, val);
+	ret = cppc_cpufreq_update_autosel_config(policy, val);
 	if (ret)
 		return ret;
 
@@ -1005,7 +1077,7 @@ static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf,
 	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
 
 	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
-	ret = cppc_cpufreq_set_min_perf(policy, perf,
+	ret = cppc_cpufreq_set_min_perf(policy, perf, true,
 					cpu_data->perf_ctrls.auto_sel);
 	if (ret)
 		return ret;
@@ -1063,7 +1135,7 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
 	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
 
 	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
-	ret = cppc_cpufreq_set_max_perf(policy, perf,
+	ret = cppc_cpufreq_set_max_perf(policy, perf, true,
 					cpu_data->perf_ctrls.auto_sel);
 	if (ret)
 		return ret;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (8 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-26  3:26   ` zhenglifeng (A)
  2025-12-23 12:13 ` [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

When autonomous selection (auto_sel) is enabled, the hardware controls
performance within min_perf/max_perf register bounds making the
scaling_min/max_freq effectively read-only.

Enforce this by setting policy limits to min/max_perf bounds in
cppc_verify_policy(). Users must use min_perf/max_perf sysfs interfaces
to change performance limits in autonomous mode.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/cpufreq/cppc_cpufreq.c | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index b1f570d6de34..b3da263c18b0 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -305,7 +305,37 @@ static unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy,
 
 static int cppc_verify_policy(struct cpufreq_policy_data *policy)
 {
-	cpufreq_verify_within_cpu_limits(policy);
+	unsigned int min_freq = policy->cpuinfo.min_freq;
+	unsigned int max_freq = policy->cpuinfo.max_freq;
+	struct cpufreq_policy *cpu_policy;
+	struct cppc_cpudata *cpu_data;
+	struct cppc_perf_caps *caps;
+
+	cpu_policy = cpufreq_cpu_get(policy->cpu);
+	if (!cpu_policy)
+		return -ENODEV;
+
+	cpu_data = cpu_policy->driver_data;
+	caps = &cpu_data->perf_caps;
+
+	if (cpu_data->perf_ctrls.auto_sel) {
+		u32 min_perf, max_perf;
+
+		/*
+		 * Set policy limits to HW min/max_perf bounds. In autonomous
+		 * mode, scaling_min/max_freq is effectively read-only.
+		 */
+		min_perf = cpu_data->perf_ctrls.min_perf ?:
+			   caps->lowest_nonlinear_perf;
+		max_perf = cpu_data->perf_ctrls.max_perf ?: caps->nominal_perf;
+
+		policy->min = cppc_perf_to_khz(caps, min_perf);
+		policy->max = cppc_perf_to_khz(caps, max_perf);
+	} else {
+		cpufreq_verify_within_limits(policy, min_freq, max_freq);
+	}
+
+	cpufreq_cpu_put(cpu_policy);
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
                   ` (9 preceding siblings ...)
  2025-12-23 12:13 ` [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled Sumit Gupta
@ 2025-12-23 12:13 ` Sumit Gupta
  2025-12-26  8:03   ` zhenglifeng (A)
  10 siblings, 1 reply; 23+ messages in thread
From: Sumit Gupta @ 2025-12-23 12:13 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu, sumitg

Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
autonomous performance selection on all CPUs at system startup without
requiring runtime sysfs manipulation. When autonomous mode is enabled,
the hardware automatically adjusts CPU performance based on workload
demands using Energy Performance Preference (EPP) hints.

When auto_sel_mode=1:
- All CPUs are configured for autonomous operation during init
- EPP is set to performance preference (0x0) by default
- Min/max performance bounds use defaults or already set values
- CPU frequency scaling is handled by hardware instead of OS governor

The boot parameter is applied only during first policy initialization.
User's runtime sysfs configuration is preserved across hotplug.

For Documentation/:
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../admin-guide/kernel-parameters.txt         | 13 +++
 drivers/cpufreq/cppc_cpufreq.c                | 85 +++++++++++++++++--
 2 files changed, 90 insertions(+), 8 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index aab72efa1acd..450f0b0225dc 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1035,6 +1035,19 @@ Kernel parameters
 			Format:
 			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
 
+	cppc_cpufreq.auto_sel_mode=
+			[CPU_FREQ] Enable ACPI CPPC autonomous performance
+			selection. When enabled, hardware automatically adjusts
+			CPU frequency on all CPUs based on workload demands.
+			In Autonomous mode, Energy Performance Preference (EPP)
+			hints guide hardware toward performance (0x0) or energy
+			efficiency (0xff).
+			Requires ACPI CPPC autonomous selection register support.
+			Format: <bool>
+			Default: 0 (disabled)
+			0: use cpufreq governors
+			1: enable if supported by hardware
+
 	cpuidle.off=1	[CPU_IDLE]
 			disable the cpuidle sub-system
 
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index b3da263c18b0..8c6869e68504 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -30,6 +30,9 @@ static struct cpufreq_driver cppc_cpufreq_driver;
 
 static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
 
+/* Autonomous Selection boot parameter */
+static bool auto_sel_mode;
+
 #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
 static enum {
 	FIE_UNSET = -1,
@@ -643,11 +646,16 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
  * cppc_cpufreq_update_autosel_config - Update autonomous selection config
  * @policy: cpufreq policy
  * @is_auto_sel: enable/disable autonomous selection
+ * @epp_val: EPP value (used only if update_epp true)
+ * @update_epp: whether to update EPP register
+ * @update_policy: whether to update policy constraints
  *
  * Return: 0 on success, negative error code on failure
  */
 static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
-					      bool is_auto_sel)
+					      bool is_auto_sel, u32 epp_val,
+					      bool update_epp,
+					      bool update_policy)
 {
 	struct cppc_cpudata *cpu_data = policy->driver_data;
 	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
@@ -655,7 +663,6 @@ static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
 	u64 max_perf = caps->nominal_perf;
 	unsigned int cpu = policy->cpu;
 	bool update_reg = is_auto_sel;
-	bool update_policy = true;
 	int ret;
 
 	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
@@ -685,6 +692,17 @@ static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
 	if (ret && ret != -EOPNOTSUPP)
 		return ret;
 
+	/* Update EPP register */
+	if (update_epp) {
+		ret = cppc_set_epp(cpu, epp_val);
+		if (ret && ret != -EOPNOTSUPP) {
+			pr_warn("Failed to set EPP for CPU%d (%d)\n", cpu, ret);
+			return ret;
+		}
+		if (!ret)
+			cpu_data->perf_ctrls.energy_perf = epp_val;
+	}
+
 	/* Update auto_sel register */
 	ret = cppc_set_auto_sel(cpu, is_auto_sel);
 	if (ret && ret != -EOPNOTSUPP) {
@@ -816,11 +834,54 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 	policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
 	cpu_data->perf_ctrls.desired_perf =  caps->highest_perf;
 
-	ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
-	if (ret) {
-		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
-			 caps->highest_perf, cpu, ret);
-		goto out;
+	/*
+	 * Enable autonomous mode on first init if boot param is set.
+	 * Check last_governor to detect first init and skip if auto_sel
+	 * is already enabled.
+	 */
+	if (auto_sel_mode && policy->last_governor[0] == '\0' &&
+	    !cpu_data->perf_ctrls.auto_sel) {
+		/* Enable CPPC - optional register, some platforms need it */
+		ret = cppc_set_enable(cpu, true);
+		if (ret) {
+			if (ret == -EOPNOTSUPP)
+				pr_debug("CPPC enable not supported CPU%d\n",
+					 cpu);
+			else
+				pr_warn("Failed enable CPPC CPU%d (%d)\n",
+					cpu, ret);
+		}
+
+		/*
+		 * Enable autonomous mode; Pass false for update_policy to avoid
+		 * updating policy limits prematurely as they are not yet fully setup.
+		 */
+		ret = cppc_cpufreq_update_autosel_config(policy,
+							 true,  /* is_auto_sel */
+							 CPPC_EPP_PERFORMANCE_PREF,
+							 true,  /* update_epp */
+							 false); /* update_policy */
+		if (ret)
+			pr_warn("Failed autonomous config CPU%d (%d)\n",
+				cpu, ret);
+	}
+
+	/* If auto mode is enabled, sync policy limits with HW registers */
+	if (cpu_data->perf_ctrls.auto_sel) {
+		policy->min = cppc_perf_to_khz(caps,
+					       cpu_data->perf_ctrls.min_perf ?:
+					       caps->lowest_nonlinear_perf);
+		policy->max = cppc_perf_to_khz(caps,
+					       cpu_data->perf_ctrls.max_perf ?:
+					       caps->nominal_perf);
+	} else {
+		/* Standard mode: governors control frequency */
+		ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+		if (ret) {
+			pr_debug("Err setting perf value:%d CPU:%d ret:%d\n",
+				 caps->highest_perf, cpu, ret);
+			goto out;
+		}
 	}
 
 	cppc_cpufreq_cpu_fie_init(policy);
@@ -991,7 +1052,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
 	if (ret)
 		return ret;
 
-	ret = cppc_cpufreq_update_autosel_config(policy, val);
+	ret = cppc_cpufreq_update_autosel_config(policy, val, 0, false, true);
 	if (ret)
 		return ret;
 
@@ -1253,10 +1314,18 @@ static int __init cppc_cpufreq_init(void)
 
 static void __exit cppc_cpufreq_exit(void)
 {
+	unsigned int cpu;
+
+	for_each_present_cpu(cpu)
+		cppc_set_auto_sel(cpu, false);
+
 	cpufreq_unregister_driver(&cppc_cpufreq_driver);
 	cppc_freq_invariance_exit();
 }
 
+module_param(auto_sel_mode, bool, 0000);
+MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
+
 module_exit(cppc_cpufreq_exit);
 MODULE_AUTHOR("Ashwin Chaugule");
 MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-12-23 12:13 ` [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-12-24 18:32   ` kernel test robot
  2025-12-26  0:20   ` Bagas Sanjaya
  1 sibling, 0 replies; 23+ messages in thread
From: kernel test robot @ 2025-12-24 18:32 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: oe-kbuild-all, linux-tegra, treding, jonathanh, vsethi,
	ksitaraman, sanjayc, nhartman, bbasu, sumitg

Hi Sumit,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/bleeding-edge linus/master v6.19-rc2 next-20251219]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Sumit-Gupta/cpufreq-CPPC-Add-generic-helpers-for-sysfs-show-store/20251224-001833
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20251223121307.711773-8-sumitg%40nvidia.com
patch subject: [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
reproduce: (https://download.01.org/0day-ci/archive/20251224/202512241930.heuhsbt9-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512241930.heuhsbt9-lkp@intel.com/

All warnings (new ones prefixed by >>):

   Using alabaster theme
   ERROR: Cannot find file ./include/linux/pci.h
   WARNING: No kernel-doc for file ./include/linux/pci.h
   ERROR: Cannot find file ./include/linux/mod_devicetable.h
   WARNING: No kernel-doc for file ./include/linux/mod_devicetable.h
>> Documentation/ABI/testing/sysfs-devices-system-cpu:356: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils]
   ERROR: Cannot find file ./include/linux/bootconfig.h
   WARNING: No kernel-doc for file ./include/linux/bootconfig.h
   ERROR: Cannot find file ./include/linux/pstore_zone.h
   ERROR: Cannot find file ./include/linux/pstore_zone.h
   WARNING: No kernel-doc for file ./include/linux/pstore_zone.h


vim +356 Documentation/ABI/testing/sysfs-devices-system-cpu

   347	
   348			Read/write a frequency value in kHz from/to this file. This
   349			file sets the maximum performance level (as frequency) at
   350			which the platform may run. The frequency value is internally
   351			converted to a performance value and must be in the range
   352			[cpuinfo_min_freq, cpuinfo_max_freq], inclusive.
   353	
   354			This file is only present if the cppc-cpufreq driver is in use.
   355	
 > 356	What:		/sys/devices/system/cpu/cpuX/cpufreq/perf_limited
   357	Date:		February 2026
   358	Contact:	linux-pm@vger.kernel.org
   359	Description:	Performance Limited
   360	
   361			Read to check if platform throttling (thermal/power/current
   362			limits) caused delivered performance to fall below the
   363			requested level. A non-zero value indicates throttling occurred.
   364	
   365			Write the bitmask of bits to clear:
   366			  1 = clear bit 0 (desired performance excursion)
   367			  2 = clear bit 1 (minimum performance excursion)
   368			  3 = clear both bits
   369			The platform sets these bits; OSPM can only clear them.
   370	
   371			This file is only present if the cppc-cpufreq driver is in use.
   372	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store
  2025-12-23 12:12 ` [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
@ 2025-12-25  3:41   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25  3:41 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:12, Sumit Gupta wrote:
> Add generic show/store helper functions for u64 sysfs attributes:
> - cppc_cpufreq_sysfs_show_u64()
> - cppc_cpufreq_sysfs_store_u64()
> 
> Refactor auto_act_window and energy_performance_preference_val
> attributes to use these helpers, eliminating code duplication.

How about adding a macro to generate show_xxx and store_xxx in one line?

Just a suggestion.

> 
> No functional changes.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/cpufreq/cppc_cpufreq.c | 71 +++++++++++++++-------------------
>  1 file changed, 31 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 9eac77c4f294..7c26ce554e29 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -831,72 +831,63 @@ 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)
> +static ssize_t show_auto_act_window(struct cpufreq_policy *policy, char *buf)
>  {
> -	u64 val;
> -	int ret;
> -
> -	ret = cppc_get_epp_perf(policy->cpu, &val);
> -
> -	/* show "<unsupported>" when this register is not supported by cpc */
> -	if (ret == -EOPNOTSUPP)
> -		return sysfs_emit(buf, "<unsupported>\n");
> -
> -	if (ret)
> -		return ret;
> -
> -	return sysfs_emit(buf, "%llu\n", val);
> +	return cppc_cpufreq_sysfs_show_u64(policy->cpu,
> +					   cppc_get_auto_act_window, buf);
>  }
>  
> -static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *policy,
> -						       const char *buf, size_t count)
> +static ssize_t store_auto_act_window(struct cpufreq_policy *policy,
> +				     const char *buf, size_t count)
>  {
> -	u64 val;
> -	int ret;
> -
> -	ret = kstrtou64(buf, 0, &val);
> -	if (ret)
> -		return ret;
> +	return cppc_cpufreq_sysfs_store_u64(policy->cpu,
> +					    cppc_set_auto_act_window,
> +					    buf, count);
> +}
>  
> -	ret = cppc_set_epp(policy->cpu, val);
> -	if (ret)
> -		return ret;
> +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);
> +}
>  
> -	return count;
> +static ssize_t
> +store_energy_performance_preference_val(struct cpufreq_policy *policy,
> +					const char *buf, size_t count)
> +{
> +	return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_epp,
> +					    buf, count);
>  }
>  
>  cpufreq_freq_attr_ro(freqdomain_cpus);


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp
  2025-12-23 12:13 ` [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp Sumit Gupta
@ 2025-12-25  3:56   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25  3:56 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, 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.
> 
> Also update EPP constants for better clarity:
> - Add CPPC_EPP_PERFORMANCE_PREF (0x00) for performance preference
> - Add CPPC_EPP_ENERGY_EFFICIENCY_PREF (0xFF) for energy efficiency
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/acpi/cppc_acpi.c | 35 +++++++++++++++++++++++++++++------
>  include/acpi/cppc_acpi.h |  3 ++-
>  2 files changed, 31 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index a4e89fe6aab5..403ee988a8c6 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1556,6 +1556,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) {
> @@ -1566,6 +1568,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);
> @@ -1590,14 +1597,30 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
>  		/* after writing CPC, transfer the ownership of PCC to platform */
>  		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
>  		up_write(&pcc_ss_data->pcc_lock);
> -	} else if (osc_cpc_flexible_adr_space_confirmed &&
> -		   CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
> -		ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
> +	} else if (osc_cpc_flexible_adr_space_confirmed) {
> +		if (!epp_ffh_sysmem && !autosel_ffh_sysmem) {
> +			ret = -EOPNOTSUPP;
> +		} else {

I think this can do the same thing:

	} else if (osc_cpc_flexible_adr_space_confirmed &&
		   (epp_ffh_sysmem || autosel_ffh_sysmem)) {

and reduce the levels of indentation.

> +			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");
> +		ret = -EOPNOTSUPP;
>  	}
>  
> +	if (ret == -EOPNOTSUPP)
> +		pr_debug("CPU%d: _CPC not in PCC/FFH/SystemMemory\n", cpu);
> +
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(cppc_set_epp_perf);

The code below is unrelated to the code above and should ideally be
separated into a separate patch I think.

> @@ -1609,7 +1632,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 12a1dc31bf2a..2860a0252313 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 {


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls
  2025-12-23 12:12 ` [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
@ 2025-12-25  8:21   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25  8:21 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:12, 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.
> 
> Also call cppc_get_perf() in cppc_cpufreq_get_cpu_data() to initialize
> perf_ctrls with current hardware register values during cpufreq
> initialization for each CPU policy.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/acpi/cppc_acpi.c       | 79 ++++++++++++++++++++++++++++++++++
>  drivers/cpufreq/cppc_cpufreq.c |  6 +++
>  include/acpi/cppc_acpi.h       |  5 +++
>  3 files changed, 90 insertions(+)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index e66e20d1f31b..a4e89fe6aab5 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1732,6 +1732,85 @@ int cppc_set_enable(int cpu, bool enable)
>  	return cppc_set_reg_val(cpu, ENABLE, enable);
>  }
>  EXPORT_SYMBOL_GPL(cppc_set_enable);
> +/**
> + * cppc_get_perf - Get a CPU's performance controls.
> + * @cpu: CPU for which to get performance controls.
> + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
> + *
> + * Return: 0 for success with perf_ctrls, -ERRNO otherwise.
> + */
> +int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
> +{
> +	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
> +	struct cpc_register_resource *desired_perf_reg,
> +				     *min_perf_reg, *max_perf_reg,
> +				     *energy_perf_reg, *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.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 7c26ce554e29..a87e7bb2e2f1 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -562,6 +562,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;
> +	}
> +

If you really need energy_perf and auto_sel in cpu_data->perf_ctrls, they
should be updated whenever they are set, i.e., in store_auto_select() and
store_energy_performance_preference_val().

>  	return cpu_data;
>  
>  free_mask:
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index a090b010f5f1..12a1dc31bf2a 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -150,6 +150,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);
> @@ -191,6 +192,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] 23+ messages in thread

* Re: [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
  2025-12-23 12:13 ` [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2025-12-25  9:03   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25  9:03 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, 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 | 160 +++++++++++++++++++++++++++++++++
>  include/acpi/cppc_acpi.h       |  20 +++++
>  3 files changed, 224 insertions(+)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 403ee988a8c6..9f28c20d902d 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1742,6 +1742,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 a87e7bb2e2f1..1e282dfabc76 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_update_autosel_config_lock);
> +
>  #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>  static enum {
>  	FIE_UNSET = -1,
> @@ -538,6 +540,46 @@ static void populate_efficiency_class(void)
>  }
>  #endif
>  
> +/**
> + * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
> + * @policy: cpufreq policy
> + * @val: performance value to set
> + * @is_min: true for min_perf, false for max_perf
> + */
> +static int cppc_cpufreq_set_mperf_limit(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("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
> +				is_min ? "min" : "max", (u64)perf, cpu, ret);
> +		return ret;
> +	}
> +
> +	if (is_min)
> +		cpu_data->perf_ctrls.min_perf = perf;
> +	else
> +		cpu_data->perf_ctrls.max_perf = perf;
> +
> +	return 0;
> +}
> +
> +#define cppc_cpufreq_set_min_perf(policy, val) \
> +	cppc_cpufreq_set_mperf_limit(policy, val, true)
> +
> +#define cppc_cpufreq_set_max_perf(policy, val) \
> +	cppc_cpufreq_set_mperf_limit(policy, val, false)
> +
>  static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>  {
>  	struct cppc_cpudata *cpu_data;
> @@ -896,16 +938,134 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
>  					    buf, count);
>  }
>  
> +/**
> + * 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;
> +	u64 perf;
> +	int ret;
> +
> +	ret = cppc_get_min_perf(policy->cpu, &perf);
> +	if (ret == -EOPNOTSUPP)
> +		return sysfs_emit(buf, "<unsupported>\n");
> +	if (ret)
> +		return ret;
> +
> +	/* Convert performance to frequency (kHz) for user */
> +	return sysfs_emit(buf, "%u\n",
> +			  cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_min_perf - Set minimum performance from frequency (kHz)
> + * @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_update_autosel_config_lock);
> +	ret = cppc_cpufreq_set_min_perf(policy, perf);

Clamping value, calling cppc_set_min_perf(), setting
cpu_data->perf_ctrls.min_perf. These things can be accomplished with three
or four more lines of code here. I don't think
cppc_cpufreq_set_mperf_limit() is necessary. Same as in store_max_perf().

> +	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;
> +	u64 perf;
> +	int ret;
> +
> +	ret = cppc_get_max_perf(policy->cpu, &perf);
> +	if (ret == -EOPNOTSUPP)
> +		return sysfs_emit(buf, "<unsupported>\n");
> +	if (ret)
> +		return ret;
> +
> +	/* Convert performance to frequency (kHz) for user */
> +	return sysfs_emit(buf, "%u\n",
> +			  cppc_perf_to_khz(&cpu_data->perf_caps, perf));
> +}
> +
> +/**
> + * store_max_perf - Set maximum performance from frequency (kHz)
> + * @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_update_autosel_config_lock);
> +	ret = cppc_cpufreq_set_max_perf(policy, perf);
> +	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 2860a0252313..a49b50bddaf9 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -173,6 +173,10 @@ extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
>  extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
>  extern int cppc_get_auto_sel(int cpu, bool *enable);
>  extern int cppc_set_auto_sel(int cpu, bool enable);
> +extern int cppc_get_min_perf(int cpu, u64 *min_perf);
> +extern int cppc_set_min_perf(int cpu, 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);
> @@ -265,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
>  {
>  	return -EOPNOTSUPP;
>  }
> +static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_min_perf(int cpu, 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] 23+ messages in thread

* Re: [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited
  2025-12-23 12:13 ` [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
@ 2025-12-25 12:06   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25 12:06 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, 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 1 to clear bit 0
>   - Write 2 to clear bit 1
>   - Write 3 to clear both bits

It's a bit odd that users write a 1 to and then read a 0 from the sysfs
file. I think it is better to seperate these two bits, as two sysfs files.
Then users can write '0' or 'clear' or others into them to clear each bit.

> 
> 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 | 31 +++++++++++++++++++
>  include/acpi/cppc_acpi.h       | 15 +++++++++
>  3 files changed, 102 insertions(+)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 9f28c20d902d..ffd866c1c00d 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1786,6 +1786,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, &current_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 1e282dfabc76..1f8825006940 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1052,12 +1052,42 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
>  	return count;
>  }
>  
> +/**
> + * show_perf_limited - Show Performance Limited register status
> + * @policy: cpufreq policy
> + * @buf: buffer to write the value to
> + *
> + * Read the Performance Limited register to check if platform throttling
> + * (thermal/power/current limits) occurred.
> + */
> +static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
> +{
> +	return cppc_cpufreq_sysfs_show_u64(policy->cpu,
> +					   cppc_get_perf_limited, buf);
> +}
> +
> +/**
> + * store_perf_limited - Clear Performance Limited register bits
> + * @policy: cpufreq policy
> + * @buf: buffer containing the bitmask of bits to clear
> + * @count: number of bytes in buf
> + *
> + * Write 1 to clear bit 0, 2 to clear bit 1, or 3 to clear both.
> + */
> +static ssize_t store_perf_limited(struct cpufreq_policy *policy,
> +				  const char *buf, size_t count)
> +{
> +	return cppc_cpufreq_sysfs_store_u64(policy->cpu,
> +					    cppc_set_perf_limited, buf, count);
> +}
> +
>  cpufreq_freq_attr_ro(freqdomain_cpus);
>  cpufreq_freq_attr_rw(auto_select);
>  cpufreq_freq_attr_rw(auto_act_window);
>  cpufreq_freq_attr_rw(energy_performance_preference_val);
>  cpufreq_freq_attr_rw(min_perf);
>  cpufreq_freq_attr_rw(max_perf);
> +cpufreq_freq_attr_rw(perf_limited);
>  
>  static struct freq_attr *cppc_cpufreq_attr[] = {
>  	&freqdomain_cpus,
> @@ -1066,6 +1096,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 a49b50bddaf9..57e04326a4b6 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;
> @@ -177,6 +182,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);
> @@ -285,6 +292,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] 23+ messages in thread

* Re: [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf
  2025-12-23 12:13 ` [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf Sumit Gupta
@ 2025-12-25 13:56   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-25 13:56 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, Sumit Gupta wrote:
> When min_perf or max_perf is updated via sysfs in autonomous mode, the
> policy frequency limits should also be updated to reflect the new
> performance bounds.
> 
> Add @update_policy parameter to cppc_cpufreq_set_mperf_limit() to
> control whether policy constraints are synced with HW registers.
> The policy is updated only when autonomous selection is enabled to
> keep SW limits in sync with HW.
> 
> This ensures that scaling_min_freq and scaling_max_freq values remain
> consistent with the actual min/max_perf register values when operating
> in autonomous mode.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/cpufreq/cppc_cpufreq.c | 35 ++++++++++++++++++++++++++--------
>  1 file changed, 27 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 1f8825006940..0202c7b823e6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -544,14 +544,20 @@ static void populate_efficiency_class(void)
>   * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
>   * @policy: cpufreq policy
>   * @val: performance value to set
> + * @update_policy: whether to update policy constraints
>   * @is_min: true for min_perf, false for max_perf
> + *
> + * When @update_policy is true, updates cpufreq policy frequency limits.
> + * @update_policy is false during cpu_init when policy isn't fully set up.
>   */
>  static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
> -					bool is_min)
> +					bool update_policy, bool is_min)
>  {
>  	struct cppc_cpudata *cpu_data = policy->driver_data;
>  	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
>  	unsigned int cpu = policy->cpu;
> +	struct freq_qos_request *req;
> +	unsigned int freq;
>  	u32 perf;
>  	int ret;
>  
> @@ -571,15 +577,26 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
>  	else
>  		cpu_data->perf_ctrls.max_perf = perf;
>  
> +	if (update_policy) {
> +		freq = cppc_perf_to_khz(caps, perf);
> +		req = is_min ? policy->min_freq_req : policy->max_freq_req;
> +
> +		ret = freq_qos_update_request(req, freq);
> +		if (ret < 0) {
> +			pr_warn("Failed to update %s_freq constraint for CPU%d: %d\n",
> +				is_min ? "min" : "max", cpu, ret);
> +			return ret;
> +		}
> +	}
> +

OK. Now I see the necessity of extracting this function. But why not use
freq_khz as a input parameter and convert it to perf in this funciton,
since you need the freq here?

>  	return 0;
>  }
>  
> -#define cppc_cpufreq_set_min_perf(policy, val) \
> -	cppc_cpufreq_set_mperf_limit(policy, val, true)
> -
> -#define cppc_cpufreq_set_max_perf(policy, val) \
> -	cppc_cpufreq_set_mperf_limit(policy, val, false)
> +#define cppc_cpufreq_set_min_perf(policy, val, update_policy) \
> +	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, true)
>  
> +#define cppc_cpufreq_set_max_perf(policy, val, update_policy) \
> +	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, false)
>  static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>  {
>  	struct cppc_cpudata *cpu_data;
> @@ -988,7 +1005,8 @@ static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf,
>  	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>  
>  	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> -	ret = cppc_cpufreq_set_min_perf(policy, perf);
> +	ret = cppc_cpufreq_set_min_perf(policy, perf,
> +					cpu_data->perf_ctrls.auto_sel);
>  	if (ret)
>  		return ret;
>  
> @@ -1045,7 +1063,8 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
>  	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>  
>  	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> -	ret = cppc_cpufreq_set_max_perf(policy, perf);
> +	ret = cppc_cpufreq_set_max_perf(policy, perf,
> +					cpu_data->perf_ctrls.auto_sel);
>  	if (ret)
>  		return ret;
>  


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-12-23 12:13 ` [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
  2025-12-24 18:32   ` kernel test robot
@ 2025-12-26  0:20   ` Bagas Sanjaya
  1 sibling, 0 replies; 23+ messages in thread
From: Bagas Sanjaya @ 2025-12-26  0:20 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

[-- Attachment #1: Type: text/plain, Size: 1463 bytes --]

On Tue, Dec 23, 2025 at 05:43:03PM +0530, Sumit Gupta wrote:
> +		Write the bitmask of bits to clear:
> +		  1 = clear bit 0 (desired performance excursion)
> +		  2 = clear bit 1 (minimum performance excursion)
> +		  3 = clear both bits
> +		The platform sets these bits; OSPM can only clear them.

Sphinx reports htmldocs warning:

Documentation/ABI/testing/sysfs-devices-system-cpu:356: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils]

I have to fix up the bitmask list:

---- >8 ----
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index b022cbf46adcfe..8cda698019922b 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -363,9 +363,11 @@ Description:	Performance Limited
 		requested level. A non-zero value indicates throttling occurred.
 
 		Write the bitmask of bits to clear:
-		  1 = clear bit 0 (desired performance excursion)
-		  2 = clear bit 1 (minimum performance excursion)
-		  3 = clear both bits
+
+		- 1 = clear bit 0 (desired performance excursion)
+		- 2 = clear bit 1 (minimum performance excursion)
+		- 3 = 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.

Thanks.

-- 
An old man doll... just what I always wanted! - Clara

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select
  2025-12-23 12:13 ` [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select Sumit Gupta
@ 2025-12-26  2:55   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-26  2:55 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, Sumit Gupta wrote:
> When CPPC autonomous selection (auto_select) is enabled or disabled,
> the policy min/max frequency limits should be updated appropriately to
> reflect the new operating mode.
> 
> Currently, toggling auto_select only changes the hardware register but
> doesn't update the cpufreq policy constraints, which can lead to
> inconsistent behavior between the hardware state and the policy limits
> visible to userspace.
> 
> Add cppc_cpufreq_update_autosel_config() function to handle the
> auto_select toggle by syncing min/max_perf values with policy
> constraints. When enabling auto_sel, restore preserved min/max_perf
> values to policy limits. When disabling, reset policy to defaults
> while preserving hardware register values for later use.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/cpufreq/cppc_cpufreq.c | 112 +++++++++++++++++++++++++++------
>  1 file changed, 92 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index 0202c7b823e6..b1f570d6de34 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -544,14 +544,20 @@ static void populate_efficiency_class(void)
>   * cppc_cpufreq_set_mperf_limit - Set min/max performance limit
>   * @policy: cpufreq policy
>   * @val: performance value to set
> + * @update_reg: whether to update hardware register
>   * @update_policy: whether to update policy constraints
>   * @is_min: true for min_perf, false for max_perf
>   *
> + * When @update_reg is true, writes to HW registers and preserves values.
>   * When @update_policy is true, updates cpufreq policy frequency limits.
> + *
> + * @update_reg is false when disabling auto_sel to preserve HW values.
> + * The preserved value is used on next enabling of the autonomous mode.
>   * @update_policy is false during cpu_init when policy isn't fully set up.
>   */
>  static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
> -					bool update_policy, bool is_min)
> +					bool update_reg, bool update_policy,
> +					bool is_min)
>  {
>  	struct cppc_cpudata *cpu_data = policy->driver_data;
>  	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> @@ -563,19 +569,22 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
>  
>  	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("Failed to set %s_perf (%llu) on CPU%d (%d)\n",
> -				is_min ? "min" : "max", (u64)perf, cpu, ret);
> -		return ret;
> -	}
> +	if (update_reg) {
> +		ret = is_min ? cppc_set_min_perf(cpu, perf) :
> +			       cppc_set_max_perf(cpu, perf);
> +		if (ret) {
> +			if (ret != -EOPNOTSUPP)
> +				pr_warn("CPU%d: set %s_perf=%llu failed (%d)\n",
> +					cpu, is_min ? "min" : "max",
> +					(u64)perf, ret);
> +			return ret;
> +		}
>  
> -	if (is_min)
> -		cpu_data->perf_ctrls.min_perf = perf;
> -	else
> -		cpu_data->perf_ctrls.max_perf = perf;
> +		if (is_min)
> +			cpu_data->perf_ctrls.min_perf = perf;
> +		else
> +			cpu_data->perf_ctrls.max_perf = perf;
> +	}
>  
>  	if (update_policy) {
>  		freq = cppc_perf_to_khz(caps, perf);
> @@ -592,11 +601,74 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
>  	return 0;
>  }
>  
> -#define cppc_cpufreq_set_min_perf(policy, val, update_policy) \
> -	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, true)
> +#define cppc_cpufreq_set_min_perf(policy, val, update_reg, update_policy)     \
> +	cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy,  \
> +				     true)
> +
> +#define cppc_cpufreq_set_max_perf(policy, val, update_reg, update_policy)     \
> +	cppc_cpufreq_set_mperf_limit(policy, val, update_reg, update_policy,  \
> +				     false)
> +
> +/**
> + * cppc_cpufreq_update_autosel_config - Update autonomous selection config
> + * @policy: cpufreq policy
> + * @is_auto_sel: enable/disable autonomous selection
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
> +					      bool is_auto_sel)
> +{
> +	struct cppc_cpudata *cpu_data = policy->driver_data;
> +	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> +	u64 min_perf = caps->lowest_nonlinear_perf;
> +	u64 max_perf = caps->nominal_perf;
> +	unsigned int cpu = policy->cpu;
> +	bool update_reg = is_auto_sel;
> +	bool update_policy = true;
> +	int ret;
> +
> +	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> +
> +	if (is_auto_sel) {
> +		/* Use preserved values if available, else use defaults */
> +		if (cpu_data->perf_ctrls.min_perf)
> +			min_perf = cpu_data->perf_ctrls.min_perf;
> +		if (cpu_data->perf_ctrls.max_perf)
> +			max_perf = cpu_data->perf_ctrls.max_perf;
> +	}

So if !is_auto_sel, min_perf and max_perf reg will be set to
lowest_nonlinear_perf and nominal_perf, but perf_ctrls.min_perf and
perf_ctrls.max_perf remain the old value. A little bit strange I think. And
when this happen, min_freq_req and max_freq_req will retain the value last
set by the users through min_perf and max_perf. It's that alright?

> +
> +	/*
> +	 * Set min/max performance and update policy constraints.
> +	 *   When enabling: update both HW registers and policy.
> +	 *   When disabling: update policy only, preserve HW registers.
> +	 * Continue even if min/max are not supported, as EPP and autosel
> +	 * might still be supported.
> +	 */
> +	ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg,
> +					update_policy);
> +	if (ret && ret != -EOPNOTSUPP)
> +		return ret;
> +
> +	ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg,
> +					update_policy);
> +	if (ret && ret != -EOPNOTSUPP)
> +		return ret;
> +
> +	/* Update auto_sel register */
> +	ret = cppc_set_auto_sel(cpu, is_auto_sel);
> +	if (ret && ret != -EOPNOTSUPP) {
> +		pr_warn("Failed to set auto_sel=%d for CPU%d (%d)\n",
> +			is_auto_sel, cpu, ret);
> +		return ret;
> +	}
> +	if (!ret)
> +		cpu_data->perf_ctrls.auto_sel = is_auto_sel;
> +
> +	return 0;

Better to return ret.

> +}
> +
>  
> -#define cppc_cpufreq_set_max_perf(policy, val, update_policy) \
> -	cppc_cpufreq_set_mperf_limit(policy, val, update_policy, false)
>  static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>  {
>  	struct cppc_cpudata *cpu_data;
> @@ -889,7 +961,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
>  	if (ret)
>  		return ret;

Since you already store auto_sel value in perf_ctrls, We can compare the
new value with perf_ctrls.auto_sel here, and just return if they are the
same.

>  
> -	ret = cppc_set_auto_sel(policy->cpu, val);
> +	ret = cppc_cpufreq_update_autosel_config(policy, val);
>  	if (ret)
>  		return ret;
>  
> @@ -1005,7 +1077,7 @@ static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf,
>  	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>  
>  	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> -	ret = cppc_cpufreq_set_min_perf(policy, perf,
> +	ret = cppc_cpufreq_set_min_perf(policy, perf, true,
>  					cpu_data->perf_ctrls.auto_sel);
>  	if (ret)
>  		return ret;
> @@ -1063,7 +1135,7 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf,
>  	perf = cppc_khz_to_perf(&cpu_data->perf_caps, freq_khz);
>  
>  	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> -	ret = cppc_cpufreq_set_max_perf(policy, perf,
> +	ret = cppc_cpufreq_set_max_perf(policy, perf, true,
>  					cpu_data->perf_ctrls.auto_sel);
>  	if (ret)
>  		return ret;


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled
  2025-12-23 12:13 ` [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled Sumit Gupta
@ 2025-12-26  3:26   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-26  3:26 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, Sumit Gupta wrote:
> When autonomous selection (auto_sel) is enabled, the hardware controls
> performance within min_perf/max_perf register bounds making the
> scaling_min/max_freq effectively read-only.
> 
> Enforce this by setting policy limits to min/max_perf bounds in
> cppc_verify_policy(). Users must use min_perf/max_perf sysfs interfaces
> to change performance limits in autonomous mode.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/cpufreq/cppc_cpufreq.c | 32 +++++++++++++++++++++++++++++++-
>  1 file changed, 31 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index b1f570d6de34..b3da263c18b0 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -305,7 +305,37 @@ static unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy,
>  
>  static int cppc_verify_policy(struct cpufreq_policy_data *policy)
>  {
> -	cpufreq_verify_within_cpu_limits(policy);
> +	unsigned int min_freq = policy->cpuinfo.min_freq;
> +	unsigned int max_freq = policy->cpuinfo.max_freq;
> +	struct cpufreq_policy *cpu_policy;
> +	struct cppc_cpudata *cpu_data;
> +	struct cppc_perf_caps *caps;
> +
> +	cpu_policy = cpufreq_cpu_get(policy->cpu);

Better to use:

	struct cpufreq_policy *cpu_policy __free(put_cpufreq_policy) = cpufreq_cpu_get(policy->cpu);

> +	if (!cpu_policy)
> +		return -ENODEV;
> +
> +	cpu_data = cpu_policy->driver_data;
> +	caps = &cpu_data->perf_caps;

cpu_policy, cpu_data and cpas are only used in the if branch. Just put them
in it.

> +
> +	if (cpu_data->perf_ctrls.auto_sel) {
> +		u32 min_perf, max_perf;
> +
> +		/*
> +		 * Set policy limits to HW min/max_perf bounds. In autonomous
> +		 * mode, scaling_min/max_freq is effectively read-only.
> +		 */
> +		min_perf = cpu_data->perf_ctrls.min_perf ?:
> +			   caps->lowest_nonlinear_perf;
> +		max_perf = cpu_data->perf_ctrls.max_perf ?: caps->nominal_perf;
> +
> +		policy->min = cppc_perf_to_khz(caps, min_perf);
> +		policy->max = cppc_perf_to_khz(caps, max_perf);
> +	} else {
> +		cpufreq_verify_within_limits(policy, min_freq, max_freq);

Why not still using cpufreq_verify_within_cpu_limits()?

> +	}
> +
> +	cpufreq_cpu_put(cpu_policy);
>  	return 0;
>  }
>  


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-12-23 12:13 ` [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
@ 2025-12-26  8:03   ` zhenglifeng (A)
  0 siblings, 0 replies; 23+ messages in thread
From: zhenglifeng (A) @ 2025-12-26  8:03 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, rdunlap, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, ionela.voinescu, zhanjie9,
	linux-pm, linux-acpi, linux-doc, acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	nhartman, bbasu

On 2025/12/23 20:13, Sumit Gupta wrote:
> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
> autonomous performance selection on all CPUs at system startup without
> requiring runtime sysfs manipulation. When autonomous mode is enabled,
> the hardware automatically adjusts CPU performance based on workload
> demands using Energy Performance Preference (EPP) hints.
> 
> When auto_sel_mode=1:
> - All CPUs are configured for autonomous operation during init
> - EPP is set to performance preference (0x0) by default
> - Min/max performance bounds use defaults or already set values
> - CPU frequency scaling is handled by hardware instead of OS governor
> 
> The boot parameter is applied only during first policy initialization.
> User's runtime sysfs configuration is preserved across hotplug.
> 
> For Documentation/:
> Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  .../admin-guide/kernel-parameters.txt         | 13 +++
>  drivers/cpufreq/cppc_cpufreq.c                | 85 +++++++++++++++++--
>  2 files changed, 90 insertions(+), 8 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index aab72efa1acd..450f0b0225dc 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -1035,6 +1035,19 @@ Kernel parameters
>  			Format:
>  			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>  
> +	cppc_cpufreq.auto_sel_mode=
> +			[CPU_FREQ] Enable ACPI CPPC autonomous performance
> +			selection. When enabled, hardware automatically adjusts
> +			CPU frequency on all CPUs based on workload demands.
> +			In Autonomous mode, Energy Performance Preference (EPP)
> +			hints guide hardware toward performance (0x0) or energy
> +			efficiency (0xff).
> +			Requires ACPI CPPC autonomous selection register support.
> +			Format: <bool>
> +			Default: 0 (disabled)
> +			0: use cpufreq governors
> +			1: enable if supported by hardware
> +
>  	cpuidle.off=1	[CPU_IDLE]
>  			disable the cpuidle sub-system
>  
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index b3da263c18b0..8c6869e68504 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -30,6 +30,9 @@ static struct cpufreq_driver cppc_cpufreq_driver;
>  
>  static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
>  
> +/* Autonomous Selection boot parameter */
> +static bool auto_sel_mode;
> +
>  #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>  static enum {
>  	FIE_UNSET = -1,
> @@ -643,11 +646,16 @@ static int cppc_cpufreq_set_mperf_limit(struct cpufreq_policy *policy, u64 val,
>   * cppc_cpufreq_update_autosel_config - Update autonomous selection config
>   * @policy: cpufreq policy
>   * @is_auto_sel: enable/disable autonomous selection
> + * @epp_val: EPP value (used only if update_epp true)
> + * @update_epp: whether to update EPP register
> + * @update_policy: whether to update policy constraints
>   *
>   * Return: 0 on success, negative error code on failure
>   */
>  static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
> -					      bool is_auto_sel)
> +					      bool is_auto_sel, u32 epp_val,
> +					      bool update_epp,
> +					      bool update_policy)

cppc_cpufreq_set_mperf_limit() and cppc_cpufreq_update_autosel_config()
have too much bool input param. Just break them down into several separate
functions and call them only when needed. These two functions are now too
hard to read.

>  {
>  	struct cppc_cpudata *cpu_data = policy->driver_data;
>  	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
> @@ -655,7 +663,6 @@ static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
>  	u64 max_perf = caps->nominal_perf;
>  	unsigned int cpu = policy->cpu;
>  	bool update_reg = is_auto_sel;
> -	bool update_policy = true;
>  	int ret;
>  
>  	guard(mutex)(&cppc_cpufreq_update_autosel_config_lock);
> @@ -685,6 +692,17 @@ static int cppc_cpufreq_update_autosel_config(struct cpufreq_policy *policy,
>  	if (ret && ret != -EOPNOTSUPP)
>  		return ret;
>  
> +	/* Update EPP register */
> +	if (update_epp) {
> +		ret = cppc_set_epp(cpu, epp_val);
> +		if (ret && ret != -EOPNOTSUPP) {
> +			pr_warn("Failed to set EPP for CPU%d (%d)\n", cpu, ret);
> +			return ret;
> +		}
> +		if (!ret)
> +			cpu_data->perf_ctrls.energy_perf = epp_val;
> +	}
> +
>  	/* Update auto_sel register */
>  	ret = cppc_set_auto_sel(cpu, is_auto_sel);
>  	if (ret && ret != -EOPNOTSUPP) {
> @@ -816,11 +834,54 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>  	policy->cur = cppc_perf_to_khz(caps, caps->highest_perf);
>  	cpu_data->perf_ctrls.desired_perf =  caps->highest_perf;
>  
> -	ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
> -	if (ret) {
> -		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
> -			 caps->highest_perf, cpu, ret);
> -		goto out;
> +	/*
> +	 * Enable autonomous mode on first init if boot param is set.
> +	 * Check last_governor to detect first init and skip if auto_sel
> +	 * is already enabled.
> +	 */
> +	if (auto_sel_mode && policy->last_governor[0] == '\0' &&
> +	    !cpu_data->perf_ctrls.auto_sel) {
> +		/* Enable CPPC - optional register, some platforms need it */
> +		ret = cppc_set_enable(cpu, true);
> +		if (ret) {
> +			if (ret == -EOPNOTSUPP)
> +				pr_debug("CPPC enable not supported CPU%d\n",
> +					 cpu);
> +			else
> +				pr_warn("Failed enable CPPC CPU%d (%d)\n",
> +					cpu, ret);
> +		}
> +
> +		/*
> +		 * Enable autonomous mode; Pass false for update_policy to avoid
> +		 * updating policy limits prematurely as they are not yet fully setup.
> +		 */
> +		ret = cppc_cpufreq_update_autosel_config(policy,
> +							 true,  /* is_auto_sel */
> +							 CPPC_EPP_PERFORMANCE_PREF,
> +							 true,  /* update_epp */
> +							 false); /* update_policy */
> +		if (ret)
> +			pr_warn("Failed autonomous config CPU%d (%d)\n",
> +				cpu, ret);
> +	}
> +
> +	/* If auto mode is enabled, sync policy limits with HW registers */
> +	if (cpu_data->perf_ctrls.auto_sel) {
> +		policy->min = cppc_perf_to_khz(caps,
> +					       cpu_data->perf_ctrls.min_perf ?:
> +					       caps->lowest_nonlinear_perf);
> +		policy->max = cppc_perf_to_khz(caps,
> +					       cpu_data->perf_ctrls.max_perf ?:
> +					       caps->nominal_perf);
> +	} else {
> +		/* Standard mode: governors control frequency */
> +		ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
> +		if (ret) {
> +			pr_debug("Err setting perf value:%d CPU:%d ret:%d\n",
> +				 caps->highest_perf, cpu, ret);
> +			goto out;
> +		}
>  	}
>  
>  	cppc_cpufreq_cpu_fie_init(policy);
> @@ -991,7 +1052,7 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
>  	if (ret)
>  		return ret;
>  
> -	ret = cppc_cpufreq_update_autosel_config(policy, val);
> +	ret = cppc_cpufreq_update_autosel_config(policy, val, 0, false, true);
>  	if (ret)
>  		return ret;
>  
> @@ -1253,10 +1314,18 @@ static int __init cppc_cpufreq_init(void)
>  
>  static void __exit cppc_cpufreq_exit(void)
>  {
> +	unsigned int cpu;
> +
> +	for_each_present_cpu(cpu)
> +		cppc_set_auto_sel(cpu, false);
> +
>  	cpufreq_unregister_driver(&cppc_cpufreq_driver);
>  	cppc_freq_invariance_exit();
>  }
>  
> +module_param(auto_sel_mode, bool, 0000);
> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");
> +
>  module_exit(cppc_cpufreq_exit);
>  MODULE_AUTHOR("Ashwin Chaugule");
>  MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec");


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2025-12-26  8:03 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-23 12:12 [PATCH v5 00/11] Enhanced autonomous selection and improvements Sumit Gupta
2025-12-23 12:12 ` [PATCH v5 01/11] cpufreq: CPPC: Add generic helpers for sysfs show/store Sumit Gupta
2025-12-25  3:41   ` zhenglifeng (A)
2025-12-23 12:12 ` [PATCH v5 02/11] ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs Sumit Gupta
2025-12-23 12:12 ` [PATCH v5 03/11] ACPI: CPPC: Add cppc_get_perf() API to read performance controls Sumit Gupta
2025-12-25  8:21   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 04/11] ACPI: CPPC: Extend cppc_set_epp_perf() to support auto_sel and epp Sumit Gupta
2025-12-25  3:56   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 05/11] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2025-12-25  9:03   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 06/11] ACPI: CPPC: add APIs and sysfs interface for perf_limited Sumit Gupta
2025-12-25 12:06   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 07/11] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
2025-12-24 18:32   ` kernel test robot
2025-12-26  0:20   ` Bagas Sanjaya
2025-12-23 12:13 ` [PATCH v5 08/11] cpufreq: CPPC: sync policy limits when updating min/max_perf Sumit Gupta
2025-12-25 13:56   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 09/11] cpufreq: CPPC: sync policy limits when toggling auto_select Sumit Gupta
2025-12-26  2:55   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 10/11] cpufreq: CPPC: make scaling_min/max_freq read-only when auto_sel enabled Sumit Gupta
2025-12-26  3:26   ` zhenglifeng (A)
2025-12-23 12:13 ` [PATCH v5 11/11] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
2025-12-26  8:03   ` zhenglifeng (A)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).