linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/7] Enhanced autonomous selection and API
@ 2025-08-23 20:01 Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
                   ` (6 more replies)
  0 siblings, 7 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

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

It adds support for below:
- Expose sysfs to read/write the Mininum/Maximum Performance Register
  and update the policy min/max accordingly.
    /sys/.../cpufreq/policy*/min_perf and max_perf

- Expose sysfs to read/write the Performance Limited Register.
    /sys/.../cpufreq/policy*/perf_limited

- When toggling autonomous selection, synchronize the policy limits
  by updating the policy min/max.

- System-wide autonomous mode configuration via 'auto_sel_mode' boot
  parameter. Mode can be switched dynamically on individual CPUs.

- Rename APIs to improve the inconsistent naming for clarity.

The patches are grouped as below:
- Patch 1: Improvement for clarity. Can be applied independently.
- Patch 2: Extend existing APIs. Can be applied independently.
- Patch 3: Sysfs to update min/max_perf. Can be applied independently.
- Patch 4: Sysfs to update perf_limited. Can be applied independently.
- Patch 5: Update policy min/max on auto_select. Depends on 'Patch 3'.
- Patch 6: add syfs documentation. Depends on 'Patch 3 and 4'.
- Patch 7: Boot Parameter Support. Depends on 'Patch 3 and 5'.

---
v1[1] -> v2:
- Move CPC register set sysfs from acpi_cppc to cpufreq directory.
- No sysfs to set auto_sel and epp. They were merged from diff series.
- Remove 'cppc_cpufreq_epp' instance of the 'cppc_cpufreq' driver.
- Synchronize perf_min/max with policy min/max.
- Update policy min/max Toggling auto_select.
- add sysfs to update the perf_limited register.

Sumit Gupta (7):
  ACPI: CPPC: add perf control read API and clarify naming
  ACPI: CPPC: extend APIs to support auto_sel and epp
  ACPI: CPPC: add APIs and sysfs interface for min/max_perf
  ACPI: CPPC: add APIs and sysfs interface for perf_limited register
  cpufreq: CPPC: update policy min/max when toggling auto_select
  cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  cpufreq: CPPC: add autonomous mode boot parameter support

 .../ABI/testing/sysfs-devices-system-cpu      |  43 ++
 .../admin-guide/kernel-parameters.txt         |  12 +
 drivers/acpi/cppc_acpi.c                      | 208 ++++++++-
 drivers/cpufreq/amd-pstate.c                  |   2 +-
 drivers/cpufreq/cppc_cpufreq.c                | 400 +++++++++++++++++-
 include/acpi/cppc_acpi.h                      |  51 ++-
 6 files changed, 668 insertions(+), 48 deletions(-)

[1] https://lore.kernel.org/lkml/20250211103737.447704-1-sumitg@nvidia.com/

-- 
2.34.1


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

* [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-25 18:33   ` Rafael J. Wysocki
  2025-08-25 23:41   ` kernel test robot
  2025-08-23 20:01 ` [PATCH v2 2/7] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

Add cppc_get_perf_ctrls() to read performance control register values.
Rename existing APIs for clarity as:
- To distinguish between:
  - Feedback counters (fb_ctrs): Read-only performance monitoring data.
  - Performance controls (perf_ctrls): Read-write config registers.
- cppc_set_epp_perf() updates both EPP and Autonomous Selection.

API's renamed:
- cppc_set_perf() to cppc_set_perf_ctrls().
- cppc_get_perf_ctrs() to cppc_get_perf_fb_ctrs().
- cppc_get_perf_ctrs_sample() to cppc_get_perf_fb_ctrs_sample().
- cppc_set_epp_perf() to cppc_set_epp_and_autosel().

Remove redundant energy_perf field from 'struct cppc_perf_caps' since
the same information is available in 'struct cppc_perf_ctrls' which is
actively used.

All existing callers are updated to maintain compatibility.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c       | 95 +++++++++++++++++++++++++++++-----
 drivers/cpufreq/amd-pstate.c   |  2 +-
 drivers/cpufreq/cppc_cpufreq.c | 26 +++++-----
 include/acpi/cppc_acpi.h       | 18 ++++---
 4 files changed, 106 insertions(+), 35 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 6b649031808f..24baaa298af3 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -58,7 +58,7 @@ struct cppc_pcc_data {
 	/*
 	 * Lock to provide controlled access to the PCC channel.
 	 *
-	 * For performance critical usecases(currently cppc_set_perf)
+	 * For performance critical usecases(currently cppc_set_perf_ctrls)
 	 *	We need to take read_lock and check if channel belongs to OSPM
 	 * before reading or writing to PCC subspace
 	 *	We need to take write_lock before transferring the channel
@@ -182,8 +182,8 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, guaranteed_perf);
 show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);
 show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);
 
-show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
-show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
+show_cppc_data(cppc_get_perf_fb_ctrs, cppc_perf_fb_ctrs, reference_perf);
+show_cppc_data(cppc_get_perf_fb_ctrs, cppc_perf_fb_ctrs, wraparound_time);
 
 /* Check for valid access_width, otherwise, fallback to using bit_width */
 #define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width)
@@ -202,7 +202,7 @@ static ssize_t show_feedback_ctrs(struct kobject *kobj,
 	struct cppc_perf_fb_ctrs fb_ctrs = {0};
 	int ret;
 
-	ret = cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+	ret = cppc_get_perf_fb_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
 	if (ret)
 		return ret;
 
@@ -1427,7 +1427,7 @@ EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
  *
  * CPPC has flexibility about how CPU performance counters are accessed.
  * One of the choices is PCC regions, which can have a high access latency. This
- * routine allows callers of cppc_get_perf_ctrs() to know this ahead of time.
+ * routine allows callers of cppc_get_perf_fb_ctrs() to know this ahead of time.
  *
  * Return: true if any of the counters are in PCC regions, false otherwise
  */
@@ -1465,13 +1465,13 @@ bool cppc_perf_ctrs_in_pcc(void)
 EXPORT_SYMBOL_GPL(cppc_perf_ctrs_in_pcc);
 
 /**
- * cppc_get_perf_ctrs - Read a CPU's performance feedback counters.
+ * cppc_get_perf_fb_ctrs - Read a CPU's performance feedback counters.
  * @cpunum: CPU from which to read counters.
  * @perf_fb_ctrs: ptr to cppc_perf_fb_ctrs. See cppc_acpi.h
  *
  * Return: 0 for success with perf_fb_ctrs populated else -ERRNO.
  */
-int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
+int cppc_get_perf_fb_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 {
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
 	struct cpc_register_resource *delivered_reg, *reference_reg,
@@ -1542,13 +1542,13 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 		up_write(&pcc_ss_data->pcc_lock);
 	return ret;
 }
-EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
+EXPORT_SYMBOL_GPL(cppc_get_perf_fb_ctrs);
 
 /*
  * Set Energy Performance Preference Register value through
  * Performance Controls Interface
  */
-int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
+int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
 {
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
 	struct cpc_register_resource *epp_set_reg;
@@ -1599,7 +1599,7 @@ int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
 
 	return ret;
 }
-EXPORT_SYMBOL_GPL(cppc_set_epp_perf);
+EXPORT_SYMBOL_GPL(cppc_set_epp_and_autosel);
 
 /**
  * cppc_set_epp() - Write the EPP register.
@@ -1731,15 +1731,82 @@ 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_ctrls - 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_ctrls(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cpc_register_resource *desired_perf_reg, *min_perf_reg, *max_perf_reg,
+				     *energy_perf_reg;
+	u64 max, min, desired_perf, energy_perf;
+	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;
+	}
+
+	desired_perf_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+	min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
+	max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
+	energy_perf_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
+
+	/* Are any of the regs PCC ?*/
+	if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) ||
+	    CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg)) {
+		if (pcc_ss_id < 0) {
+			pr_debug("Invalid pcc_ss_id\n");
+			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;
+
+out_err:
+	if (regs_in_pcc)
+		up_write(&pcc_ss_data->pcc_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf_ctrls);
 
 /**
- * cppc_set_perf - Set a CPU's performance controls.
+ * cppc_set_perf_ctrls - Set a CPU's performance controls.
  * @cpu: CPU for which to set performance controls.
  * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
  *
  * Return: 0 for success, -ERRNO otherwise.
  */
-int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+int cppc_set_perf_ctrls(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 {
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
 	struct cpc_register_resource *desired_reg, *min_perf_reg, *max_perf_reg;
@@ -1803,7 +1870,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	/*
 	 * This is Phase-II where we transfer the ownership of PCC to Platform
 	 *
-	 * Short Summary: Basically if we think of a group of cppc_set_perf
+	 * Short Summary: Basically if we think of a group of cppc_set_perf_ctrls
 	 * requests that happened in short overlapping interval. The last CPU to
 	 * come out of Phase-I will enter Phase-II and ring the doorbell.
 	 *
@@ -1862,7 +1929,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	}
 	return ret;
 }
-EXPORT_SYMBOL_GPL(cppc_set_perf);
+EXPORT_SYMBOL_GPL(cppc_set_perf_ctrls);
 
 /**
  * cppc_get_transition_latency - returns frequency transition latency in ns
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index bbc27ef9edf7..b98539d1a6aa 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -355,7 +355,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp)
 		return 0;
 
 	perf_ctrls.energy_perf = epp;
-	ret = cppc_set_epp_perf(cpudata->cpu, &perf_ctrls, 1);
+	ret = cppc_set_epp_and_autosel(cpudata->cpu, &perf_ctrls, 1);
 	if (ret) {
 		pr_debug("failed to set energy perf value (%d)\n", ret);
 		return ret;
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index ecbeb12f46e6..e4666836306d 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -81,7 +81,7 @@ static void cppc_scale_freq_workfn(struct kthread_work *work)
 	cppc_fi = container_of(work, struct cppc_freq_invariance, work);
 	cpu_data = cppc_fi->cpu_data;
 
-	if (cppc_get_perf_ctrs(cppc_fi->cpu, &fb_ctrs)) {
+	if (cppc_get_perf_fb_ctrs(cppc_fi->cpu, &fb_ctrs)) {
 		pr_warn("%s: failed to read perf counters\n", __func__);
 		return;
 	}
@@ -115,7 +115,7 @@ static void cppc_scale_freq_tick(void)
 	struct cppc_freq_invariance *cppc_fi = &per_cpu(cppc_freq_inv, smp_processor_id());
 
 	/*
-	 * cppc_get_perf_ctrs() can potentially sleep, call that from the right
+	 * cppc_get_perf_fb_ctrs() can potentially sleep, call that from the right
 	 * context.
 	 */
 	irq_work_queue(&cppc_fi->irq_work);
@@ -141,7 +141,7 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
 		kthread_init_work(&cppc_fi->work, cppc_scale_freq_workfn);
 		init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
 
-		ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
+		ret = cppc_get_perf_fb_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
 		if (ret) {
 			pr_warn("%s: failed to read perf counters for cpu:%d: %d\n",
 				__func__, cpu, ret);
@@ -271,7 +271,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 	freqs.new = target_freq;
 
 	cpufreq_freq_transition_begin(policy, &freqs);
-	ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
 	cpufreq_freq_transition_end(policy, &freqs, ret != 0);
 
 	if (ret)
@@ -291,7 +291,7 @@ static unsigned int cppc_cpufreq_fast_switch(struct cpufreq_policy *policy,
 
 	desired_perf = cppc_khz_to_perf(&cpu_data->perf_caps, target_freq);
 	cpu_data->perf_ctrls.desired_perf = desired_perf;
-	ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
 
 	if (ret) {
 		pr_debug("Failed to set target on CPU:%d. ret:%d\n",
@@ -640,7 +640,7 @@ 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);
+	ret = cppc_set_perf_ctrls(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);
@@ -666,7 +666,7 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 
 	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
 
-	ret = cppc_set_perf(cpu, &cpu_data->perf_ctrls);
+	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
 	if (ret)
 		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
 			 caps->lowest_perf, cpu, ret);
@@ -705,19 +705,19 @@ static int cppc_perf_from_fbctrs(struct cppc_perf_fb_ctrs *fb_ctrs_t0,
 	return (reference_perf * delta_delivered) / delta_reference;
 }
 
-static int cppc_get_perf_ctrs_sample(int cpu,
-				     struct cppc_perf_fb_ctrs *fb_ctrs_t0,
-				     struct cppc_perf_fb_ctrs *fb_ctrs_t1)
+static int cppc_get_perf_fb_ctrs_sample(int cpu,
+					struct cppc_perf_fb_ctrs *fb_ctrs_t0,
+					struct cppc_perf_fb_ctrs *fb_ctrs_t1)
 {
 	int ret;
 
-	ret = cppc_get_perf_ctrs(cpu, fb_ctrs_t0);
+	ret = cppc_get_perf_fb_ctrs(cpu, fb_ctrs_t0);
 	if (ret)
 		return ret;
 
 	udelay(2); /* 2usec delay between sampling */
 
-	return cppc_get_perf_ctrs(cpu, fb_ctrs_t1);
+	return cppc_get_perf_fb_ctrs(cpu, fb_ctrs_t1);
 }
 
 static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
@@ -735,7 +735,7 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
 
 	cpufreq_cpu_put(policy);
 
-	ret = cppc_get_perf_ctrs_sample(cpu, &fb_ctrs_t0, &fb_ctrs_t1);
+	ret = cppc_get_perf_fb_ctrs_sample(cpu, &fb_ctrs_t0, &fb_ctrs_t1);
 	if (ret) {
 		if (ret == -EFAULT)
 			/* Any of the associated CPPC regs is 0. */
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 20f3d62e7a16..2f2dbeeced65 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -119,7 +119,6 @@ struct cppc_perf_caps {
 	u32 lowest_nonlinear_perf;
 	u32 lowest_freq;
 	u32 nominal_freq;
-	u32 energy_perf;
 	bool auto_sel;
 };
 
@@ -150,8 +149,9 @@ struct cppc_cpudata {
 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_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
+extern int cppc_get_perf_fb_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
+extern int cppc_get_perf_ctrls(int cpu, struct cppc_perf_ctrls *perf_ctrls);
+extern int cppc_set_perf_ctrls(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);
 extern bool cppc_perf_ctrs_in_pcc(void);
@@ -166,7 +166,7 @@ extern bool cpc_supported_by_cpu(void);
 extern int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val);
 extern int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val);
 extern int cppc_get_epp_perf(int cpunum, u64 *epp_perf);
-extern int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable);
+extern int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable);
 extern int cppc_set_epp(int cpu, u64 epp_val);
 extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
 extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
@@ -188,11 +188,15 @@ static inline int cppc_get_highest_perf(int cpunum, u64 *highest_perf)
 {
 	return -EOPNOTSUPP;
 }
-static inline int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
+static inline int cppc_get_perf_fb_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_perf_ctrls(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 {
 	return -EOPNOTSUPP;
 }
-static inline int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+static inline int cppc_set_perf_ctrls(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 {
 	return -EOPNOTSUPP;
 }
@@ -232,7 +236,7 @@ static inline int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
 {
 	return -EOPNOTSUPP;
 }
-static inline int cppc_set_epp_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
+static inline int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool enable)
 {
 	return -EOPNOTSUPP;
 }
-- 
2.34.1


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

* [PATCH v2 2/7] ACPI: CPPC: extend APIs to support auto_sel and epp
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 3/7] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

Add read of auto_sel in cppc_get_perf_caps().
Add write of auto_sel and epp in cppc_set_epp_and_autosel().

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 24baaa298af3..fbcfbe4bcbf0 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1344,8 +1344,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
 	struct cpc_register_resource *highest_reg, *lowest_reg,
 		*lowest_non_linear_reg, *nominal_reg, *guaranteed_reg,
-		*low_freq_reg = NULL, *nom_freq_reg = NULL;
-	u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0;
+		*low_freq_reg = NULL, *nom_freq_reg = NULL, *auto_sel_reg = NULL;
+	u64 high, low, guaranteed, nom, min_nonlinear, low_f = 0, nom_f = 0, auto_sel = 0;
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
 	int ret = 0, regs_in_pcc = 0;
@@ -1362,11 +1362,12 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
 	nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
 	guaranteed_reg = &cpc_desc->cpc_regs[GUARANTEED_PERF];
+	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
 
 	/* Are any of the regs PCC ?*/
 	if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
 		CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
-		CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
+		CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg) || CPC_IN_PCC(auto_sel_reg)) {
 		if (pcc_ss_id < 0) {
 			pr_debug("Invalid pcc_ss_id\n");
 			return -ENODEV;
@@ -1414,6 +1415,9 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
 	perf_caps->lowest_freq = low_f;
 	perf_caps->nominal_freq = nom_f;
 
+	if (CPC_SUPPORTED(auto_sel_reg))
+		cpc_read(cpunum, auto_sel_reg, &auto_sel);
+	perf_caps->auto_sel = (bool)auto_sel;
 
 out_err:
 	if (regs_in_pcc)
@@ -1555,6 +1559,8 @@ int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool e
 	struct cpc_register_resource *auto_sel_reg;
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
+	bool autosel_support_in_ffh_or_sysmem;
+	bool epp_support_in_ffh_or_sysmem;
 	int ret;
 
 	if (!cpc_desc) {
@@ -1565,6 +1571,11 @@ int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool e
 	auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
 	epp_set_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
 
+	epp_support_in_ffh_or_sysmem = CPC_SUPPORTED(epp_set_reg) &&
+				(CPC_IN_FFH(epp_set_reg) || CPC_IN_SYSTEM_MEMORY(epp_set_reg));
+	autosel_support_in_ffh_or_sysmem = CPC_SUPPORTED(auto_sel_reg) &&
+				(CPC_IN_FFH(auto_sel_reg) || CPC_IN_SYSTEM_MEMORY(auto_sel_reg));
+
 	if (CPC_IN_PCC(epp_set_reg) || CPC_IN_PCC(auto_sel_reg)) {
 		if (pcc_ss_id < 0) {
 			pr_debug("Invalid pcc_ss_id for CPU:%d\n", cpu);
@@ -1590,8 +1601,19 @@ int cppc_set_epp_and_autosel(int cpu, struct cppc_perf_ctrls *perf_ctrls, bool e
 		ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE);
 		up_write(&pcc_ss_data->pcc_lock);
 	} else if (osc_cpc_flexible_adr_space_confirmed &&
-		   CPC_SUPPORTED(epp_set_reg) && CPC_IN_FFH(epp_set_reg)) {
-		ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+		   epp_support_in_ffh_or_sysmem && autosel_support_in_ffh_or_sysmem) {
+			ret = cpc_write(cpu, auto_sel_reg, enable);
+			if (ret) {
+				pr_debug("Failed to write auto_sel=%d for CPU:%d\n", enable, cpu);
+				return ret;
+			}
+
+			ret = cpc_write(cpu, epp_set_reg, perf_ctrls->energy_perf);
+			if (ret) {
+				pr_debug("Failed to write energy_perf=%u for CPU:%d\n",
+					 perf_ctrls->energy_perf, cpu);
+				return ret;
+			}
 	} else {
 		ret = -ENOTSUPP;
 		pr_debug("_CPC in PCC and _CPC in FFH are not supported\n");
-- 
2.34.1


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

* [PATCH v2 3/7] ACPI: CPPC: add APIs and sysfs interface for min/max_perf
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 2/7] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 4/7] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

CPPC allows platforms to specify minimum and maximum performance
limits that constrain the operating range for CPU performance scaling
when Autonomous Selection is enabled. These limits can be dynamically
adjusted to implement power management policies or workload-specific
optimizations.

Add cppc_get_min_perf() and cppc_set_min_perf() functions to read and
write the MIN_PERF register, allowing dynamic adjustment of the minimum
performance floor.

Add cppc_get_max_perf() and cppc_set_max_perf() functions to read and
write the MAX_PERF register, enabling dynamic ceiling control for
maximum performance.

Expose these capabilities through cpufreq sysfs attributes:
- /sys/.../cpufreq/policy*/min_perf: Read/write min performance limit
- /sys/.../cpufreq/policy*/max_perf: Read/write max performance limit

Also update EPP constants for better clarity:
- Rename CPPC_ENERGY_PERF_MAX to CPPC_EPP_ENERGY_EFFICIENCY_PREF
- Add CPPC_EPP_PERFORMANCE_PREF for the performance-oriented setting

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c       |  55 ++++++++++++-
 drivers/cpufreq/cppc_cpufreq.c | 142 +++++++++++++++++++++++++++++++++
 include/acpi/cppc_acpi.h       |  23 +++++-
 3 files changed, 218 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index fbcfbe4bcbf0..dd06bad26670 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1630,7 +1630,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_and_autosel);
  */
 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);
@@ -1753,6 +1753,59 @@ int cppc_set_enable(int cpu, bool enable)
 	return cppc_set_reg_val(cpu, ENABLE, enable);
 }
 EXPORT_SYMBOL_GPL(cppc_set_enable);
+
+/**
+ * cppc_get_min_perf - Get the min performance register value.
+ * @cpu: CPU from which to get min performance.
+ * @min_perf: Return address.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_min_perf(int cpu, u64 *min_perf)
+{
+	return cppc_get_reg_val(cpu, MIN_PERF, min_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_min_perf);
+
+/**
+ * cppc_set_min_perf() - Write the min performance register.
+ * @cpu: CPU on which to write register.
+ * @min_perf: Value to write to the MIN_PERF register.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_set_min_perf(int cpu, u64 min_perf)
+{
+	return cppc_set_reg_val(cpu, MIN_PERF, min_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_min_perf);
+
+/**
+ * cppc_get_max_perf - Get the max performance register value.
+ * @cpu: CPU from which to get max performance.
+ * @max_perf: Return address.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_max_perf(int cpu, u64 *max_perf)
+{
+	return cppc_get_reg_val(cpu, MAX_PERF, max_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_get_max_perf);
+
+/**
+ * cppc_set_max_perf() - Write the max performance register.
+ * @cpu: CPU on which to write register.
+ * @max_perf: Value to write to the MAX_PERF register.
+ *
+ * Return: 0 for success, -EIO otherwise.
+ */
+int cppc_set_max_perf(int cpu, u64 max_perf)
+{
+	return cppc_set_reg_val(cpu, MAX_PERF, max_perf);
+}
+EXPORT_SYMBOL_GPL(cppc_set_max_perf);
+
 /**
  * cppc_get_perf_ctrls - Get a CPU's performance controls.
  * @cpu: CPU for which to get performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index e4666836306d..0d3cfede5915 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -38,6 +38,8 @@ static enum {
 module_param(fie_disabled, int, 0444);
 MODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)");
 
+static DEFINE_MUTEX(cppc_cpufreq_update_autosel_config_lock);
+
 /* Frequency invariance support */
 struct cppc_freq_invariance {
 	int cpu;
@@ -572,6 +574,74 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy)
 	policy->driver_data = NULL;
 }
 
+static int cppc_cpufreq_set_min_perf(struct cpufreq_policy *policy, u64 val,
+				     bool update_reg, bool update_policy)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+	unsigned int cpu = policy->cpu;
+	unsigned int min_freq;
+	u32 min_perf;
+	int ret;
+
+	mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
+
+	min_perf = clamp(val, cpu_data->perf_caps.lowest_perf, cpu_data->perf_caps.highest_perf);
+	if (update_reg) {
+		ret = cppc_set_min_perf(policy->cpu, min_perf);
+		if (ret)
+			goto out;
+		cpu_data->perf_ctrls.min_perf = min_perf;
+	}
+
+	if (update_policy) {
+		min_freq = cppc_perf_to_khz(caps, min_perf);
+		ret = freq_qos_update_request(policy->min_freq_req, min_freq);
+		if (ret < 0)
+			pr_warn("Failed to update min freq constraint for CPU%d: %d\n", cpu, ret);
+		else
+			ret = 0;
+	}
+
+out:
+	mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
+	return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+
+static int cppc_cpufreq_set_max_perf(struct cpufreq_policy *policy, u64 val,
+				     bool update_reg, bool update_policy)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	struct cppc_perf_caps *caps = &cpu_data->perf_caps;
+	unsigned int cpu = policy->cpu;
+	unsigned int max_freq;
+	u32 max_perf;
+	int ret;
+
+	mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
+
+	max_perf = clamp(val, cpu_data->perf_caps.lowest_perf, cpu_data->perf_caps.highest_perf);
+	if (update_reg) {
+		ret = cppc_set_max_perf(policy->cpu, max_perf);
+		if (ret)
+			goto out;
+		cpu_data->perf_ctrls.max_perf = max_perf;
+	}
+
+	if (update_policy) {
+		max_freq = cppc_perf_to_khz(caps, max_perf);
+		ret = freq_qos_update_request(policy->max_freq_req, max_freq);
+		if (ret < 0)
+			pr_warn("Failed to update max freq constraint for CPU%d: %d\n", cpu, ret);
+		else
+			ret = 0;
+	}
+
+out:
+	mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
+	return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+
 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	unsigned int cpu = policy->cpu;
@@ -892,16 +962,88 @@ static ssize_t store_energy_performance_preference_val(struct cpufreq_policy *po
 	return count;
 }
 
+static ssize_t show_min_perf(struct cpufreq_policy *policy, char *buf)
+{
+	u64 val;
+	int ret;
+
+	ret = cppc_get_min_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);
+}
+
+static ssize_t store_min_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	u64 val;
+	int ret;
+
+	ret = kstrtou64(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = cppc_cpufreq_set_min_perf(policy, val, true, cpu_data->perf_caps.auto_sel);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t show_max_perf(struct cpufreq_policy *policy, char *buf)
+{
+	u64 val;
+	int ret;
+
+	ret = cppc_get_max_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);
+}
+
+static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	u64 val;
+	int ret;
+
+	ret = kstrtou64(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = cppc_cpufreq_set_max_perf(policy, val, true, cpu_data->perf_caps.auto_sel);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
 cpufreq_freq_attr_ro(freqdomain_cpus);
 cpufreq_freq_attr_rw(auto_select);
 cpufreq_freq_attr_rw(auto_act_window);
 cpufreq_freq_attr_rw(energy_performance_preference_val);
+cpufreq_freq_attr_rw(min_perf);
+cpufreq_freq_attr_rw(max_perf);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
 	&freqdomain_cpus,
 	&auto_select,
 	&auto_act_window,
 	&energy_performance_preference_val,
+	&min_perf,
+	&max_perf,
 	NULL,
 };
 
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 2f2dbeeced65..494cb3bd8eba 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -39,7 +39,8 @@
 /* CPPC_AUTO_ACT_WINDOW_MAX_SIG is 127, so 128 and 129 will decay to 127 when writing */
 #define CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH 129
 
-#define CPPC_ENERGY_PERF_MAX	(0xFF)
+#define CPPC_EPP_PERFORMANCE_PREF		0x00
+#define CPPC_EPP_ENERGY_EFFICIENCY_PREF		0xFF
 
 /* Each register has the folowing format. */
 struct cpc_reg {
@@ -172,6 +173,10 @@ extern int cppc_get_auto_act_window(int cpu, u64 *auto_act_window);
 extern int cppc_set_auto_act_window(int cpu, u64 auto_act_window);
 extern int cppc_get_auto_sel(int cpu, bool *enable);
 extern int cppc_set_auto_sel(int cpu, bool enable);
+extern int cppc_get_min_perf(int cpu, u64 *min_perf);
+extern int cppc_set_min_perf(int cpu, u64 min_perf);
+extern int cppc_get_max_perf(int cpu, u64 *max_perf);
+extern int cppc_set_max_perf(int cpu, u64 max_perf);
 extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
 extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
 extern int amd_detect_prefcore(bool *detected);
@@ -264,6 +269,22 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_min_perf(int cpu, u64 *min_perf)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_min_perf(int cpu, u64 min_perf)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_get_max_perf(int cpu, u64 *max_perf)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_max_perf(int cpu, u64 max_perf)
+{
+	return -EOPNOTSUPP;
+}
 static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
 {
 	return -ENODEV;
-- 
2.34.1


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

* [PATCH v2 4/7] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
                   ` (2 preceding siblings ...)
  2025-08-23 20:01 ` [PATCH v2 3/7] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 5/7] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

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

The Performance Limited register indicates to the OS that an
unpredictable event (like thermal throttling) has limited processor
performance. This register is sticky and remains set until reset or
OS clears it by writing 0.

The interface is exposed as:
 /sys/devices/system/cpu/cpuX/cpufreq/perf_limited

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

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index dd06bad26670..8cfed2d786ef 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1806,6 +1806,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
 }
 EXPORT_SYMBOL_GPL(cppc_set_max_perf);
 
+/**
+ * cppc_get_perf_limited - Get the Performance Limited register value.
+ * @cpu: CPU from which to get Performance Limited register.
+ * @perf_limited: Pointer to store the Performance Limited value.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+	return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
+
+/**
+ * cppc_set_perf_limited() - Write the Performance Limited register.
+ * @cpu: CPU on which to write register.
+ * @perf_limited: Value to write to the perf_limited register.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+	return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
+
 /**
  * cppc_get_perf_ctrls - Get a CPU's performance controls.
  * @cpu: CPU for which to get performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 0d3cfede5915..d9aae1ec26e1 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1030,12 +1030,47 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
 	return count;
 }
 
+static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
+{
+	u64 val;
+	int ret;
+
+	ret = cppc_get_perf_limited(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);
+}
+
+static ssize_t store_perf_limited(struct cpufreq_policy *policy,
+				  const char *buf, size_t count)
+{
+	u64 val;
+	int ret;
+
+	ret = kstrtou64(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = cppc_set_perf_limited(policy->cpu, val);
+	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);
+cpufreq_freq_attr_rw(perf_limited);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
 	&freqdomain_cpus,
@@ -1044,6 +1079,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 494cb3bd8eba..bdf8ce62338c 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
 extern int cppc_set_min_perf(int cpu, u64 min_perf);
 extern int cppc_get_max_perf(int cpu, u64 *max_perf);
 extern int cppc_set_max_perf(int cpu, u64 max_perf);
+extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
+extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
 extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
 extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
 extern int amd_detect_prefcore(bool *detected);
@@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+	return -EOPNOTSUPP;
+}
 static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
 {
 	return -ENODEV;
-- 
2.34.1


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

* [PATCH v2 5/7] cpufreq: CPPC: update policy min/max when toggling auto_select
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
                   ` (3 preceding siblings ...)
  2025-08-23 20:01 ` [PATCH v2 4/7] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
  2025-08-23 20:01 ` [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
  6 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

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

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

When auto_select is enabled, preserve the current min/max performance
values to maintain user-configured limits. When disabled, the hardware
operates in a default mode where the OS directly controls performance,
so update the policy limits accordingly.

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

diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index d9aae1ec26e1..5e1bbb5f67b8 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -880,6 +880,10 @@ static ssize_t show_auto_select(struct cpufreq_policy *policy, char *buf)
 static ssize_t store_auto_select(struct cpufreq_policy *policy,
 				 const char *buf, size_t count)
 {
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	unsigned int cpu = policy->cpu;
+	bool update_reg = false;
+	u32 min_perf, max_perf;
 	bool val;
 	int ret;
 
@@ -887,9 +891,48 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
 	if (ret)
 		return ret;
 
-	ret = cppc_set_auto_sel(policy->cpu, val);
-	if (ret)
+	mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
+	if (val) {
+		/* Enabling auto_select: set current user-configured limits */
+		min_perf = cpu_data->perf_ctrls.min_perf;
+		max_perf = cpu_data->perf_ctrls.max_perf;
+		update_reg = true;
+	} else {
+		/*
+		 * Disabling auto_select: set defaults for OS control.
+		 * Use lowest_nonlinear_perf as minimum to avoid very low frequencies
+		 * and nominal_perf as maximum for balanced operation.
+		 */
+		min_perf = cpu_data->perf_caps.lowest_nonlinear_perf;
+		max_perf = cpu_data->perf_caps.nominal_perf;
+	}
+
+	ret = cppc_set_auto_sel(cpu, val);
+	if (ret) {
+		pr_warn("failed to set auto_sel for cpu:%d (%d)\n", cpu, ret);
+		mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
+		return ret;
+	}
+	cpu_data->perf_caps.auto_sel = val;
+	mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
+
+	/*
+	 * On enabling auto_select: set min/max_perf register and update policy.
+	 * On disabling auto_select: update only policy.
+	 */
+	ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg, true);
+	if (ret) {
+		pr_warn("failed to %s update min policy for cpu:%d (%d)\n",
+			val > 0 ? "set min_perf and" : "", cpu, ret);
 		return ret;
+	}
+
+	ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg, true);
+	if (ret) {
+		pr_warn("failed to %s update max policy for cpu:%d (%d)\n",
+			val > 0 ? "set max_perf and" : "", cpu, ret);
+		return ret;
+	}
 
 	return count;
 }
-- 
2.34.1


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

* [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
                   ` (4 preceding siblings ...)
  2025-08-23 20:01 ` [PATCH v2 5/7] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-24  0:08   ` Randy Dunlap
  2025-08-23 20:01 ` [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
  6 siblings, 1 reply; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

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

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../ABI/testing/sysfs-devices-system-cpu      | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index ab8cd337f43a..4bce0dbc48c9 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -327,6 +327,49 @@ 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:           Aug 2025
+Contact:        linux-pm@vger.kernel.org
+Description:    Minimum Performance
+
+		Read/write a 32 bits value from/to this file. This file
+		conveys the minimum performance level at which the platform
+		may run. Minimum performance may be set to any performance
+		value in the range [Lowest Performance, Highest Performance],
+		inclusive but must be set to a value that is less than or
+		equal to that specified by the Maximum Performance Register.
+
+		Writing to this file only has meaning when Autonomous Selection
+		is enabled.
+
+		This file is only present if the cppc-cpufreq driver is in use.
+
+What:           /sys/devices/system/cpu/cpuX/cpufreq/max_perf
+Date:           Aug 2025
+Contact:        linux-pm@vger.kernel.org
+Description:    Minimum Performance
+
+		Read/write a 32 bits value from/to this file. This file conveys
+		the maximum performance level at which the platform may run.
+		Maximum performance may be set to any performance value in the
+		range [Lowest Performance, Highest Performance], inclusive.
+
+		Writing to this file only has meaning when Autonomous Selection is
+		enabled.
+
+		This file is only present if the cppc-cpufreq driver is in use.
+
+What:           /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
+Date:           Aug 2025
+Contact:        linux-pm@vger.kernel.org
+Description:    Minimum Performance
+
+		Read/write a 32 bits value from/to this file. This file indicates
+		to OSPM that an unpredictable event has limited processor
+		performance, and the delivered performance may be less than
+		desired/minimum performance.
+
+		This file is only present if the cppc-cpufreq driver is in use.
 
 What:		/sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
 Date:		August 2008
-- 
2.34.1


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

* [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
                   ` (5 preceding siblings ...)
  2025-08-23 20:01 ` [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-08-23 20:01 ` Sumit Gupta
  2025-08-24  0:08   ` Randy Dunlap
  2025-08-25 18:40   ` Mario Limonciello
  6 siblings, 2 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-08-23 20:01 UTC (permalink / raw)
  To: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg

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

This parameter allows to configure the autonomous mode on all CPUs
without requiring runtime sysfs manipulation if the 'auto_sel' register
is present.

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

Also ensure that when autonomous mode is active, the set_target callback
returns early since hardware controls frequency scaling directly.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 .../admin-guide/kernel-parameters.txt         |  12 ++
 drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
 2 files changed, 168 insertions(+), 15 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 86f395f2933b..ea58deb88c36 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -911,6 +911,18 @@
 			Format:
 			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
 
+	cppc_cpufreq.auto_sel_mode=
+			[CPU_FREQ] Autonomous Performance Level Selection.
+			When Autonomous selection is enabled, then the hardware is
+			allowed to autonomously select the CPU frequency.
+			In Autonomous mode, Energy Performance Preference(EPP)
+			provides input to the hardware to favour performance (0x0)
+			or energy efficiency (0xff).
+			Format: <bool>
+			Default: disabled.
+			0: force disabled
+			1: force enabled
+
 	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 5e1bbb5f67b8..bbf654c56ff9 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -27,6 +27,8 @@
 #include <acpi/cppc_acpi.h>
 
 static struct cpufreq_driver cppc_cpufreq_driver;
+/* Autonomous Selection */
+static bool auto_sel_mode;
 
 #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
 static enum {
@@ -272,6 +274,14 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 	freqs.old = policy->cur;
 	freqs.new = target_freq;
 
+	/*
+	 * In autonomous mode, hardware handles frequency scaling directly
+	 * based on workload demands and EPP hints, so OS frequency requests
+	 * are not needed.
+	 */
+	if (cpu_data->perf_caps.auto_sel)
+		return ret;
+
 	cpufreq_freq_transition_begin(policy, &freqs);
 	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
 	cpufreq_freq_transition_end(policy, &freqs, ret != 0);
@@ -555,6 +565,12 @@ static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
 		goto free_mask;
 	}
 
+	ret = cppc_get_perf_ctrls(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:
@@ -642,6 +658,79 @@ static int cppc_cpufreq_set_max_perf(struct cpufreq_policy *policy, u64 val,
 	return (ret == -EOPNOTSUPP) ? 0 : ret;
 }
 
+static int cppc_cpufreq_update_autosel_epp(struct cpufreq_policy *policy, int auto_sel, u32 epp)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	unsigned int cpu = policy->cpu;
+	int ret;
+
+	pr_debug("cpu%d: curr epp:%u, curr mode:%u, new epp:%u, new mode:%d\n", cpu,
+		 cpu_data->perf_ctrls.energy_perf, cpu_data->perf_caps.auto_sel, epp, auto_sel);
+
+	mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
+
+	ret = cppc_set_epp(cpu, epp);
+	if (ret) {
+		pr_warn("failed to set energy_perf for cpu:%d (%d)\n", cpu, ret);
+		goto out;
+	}
+	cpu_data->perf_ctrls.energy_perf = epp;
+
+	ret = cppc_set_auto_sel(cpu, auto_sel);
+	if (ret) {
+		pr_warn("failed to set auto_sel for cpu:%d (%d)\n", cpu, ret);
+		return ret;
+	}
+	cpu_data->perf_caps.auto_sel = auto_sel;
+
+out:
+	mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
+	return ret;
+}
+
+static int cppc_cpufreq_update_autosel_mperf_ctrls(struct cpufreq_policy *policy, u32 min_p,
+						   u32 max_p, bool update_reg, bool update_policy)
+{
+	struct cppc_cpudata *cpu_data = policy->driver_data;
+	unsigned int cpu = policy->cpu;
+	int ret;
+
+	pr_debug("cpu%d: curr max_perf:%u, curr min_perf:%u, new max_perf:%u, new min_perf:%u\n",
+		 cpu, cpu_data->perf_ctrls.max_perf, cpu_data->perf_ctrls.min_perf, max_p, min_p);
+
+	ret = cppc_cpufreq_set_min_perf(policy, min_p, update_reg, update_policy);
+	if (ret) {
+		pr_debug("failed to set min_perf for cpu:%d (%d)\n", cpu, ret);
+		return ret;
+	}
+
+	ret = cppc_cpufreq_set_max_perf(policy, max_p, update_reg, update_policy);
+	if (ret) {
+		pr_debug("failed to set max_perf for cpu:%d (%d)\n", cpu, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int cppc_cpufreq_update_autosel_configs(struct cpufreq_policy *policy, int auto_sel,
+					       u32 epp, u32 min_perf, u32 max_perf,
+					       bool update_reg, bool update_policy)
+{
+	int ret;
+
+	ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, max_perf,
+						      update_reg, update_policy);
+	if (ret)
+		return ret;
+
+	ret = cppc_cpufreq_update_autosel_epp(policy, auto_sel, epp);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
 	unsigned int cpu = policy->cpu;
@@ -710,11 +799,28 @@ 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_ctrls(cpu, &cpu_data->perf_ctrls);
-	if (ret) {
-		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
-			 caps->highest_perf, cpu, ret);
-		goto out;
+	if (cpu_data->perf_caps.auto_sel) {
+		ret = cppc_set_enable(cpu, true);
+		if (ret) {
+			pr_err("Failed to enable CPPC on cpu%d (%d)\n", cpu, ret);
+			goto out;
+		}
+
+		ret = cppc_cpufreq_update_autosel_configs(policy, true,
+							  CPPC_EPP_PERFORMANCE_PREF,
+							  caps->lowest_nonlinear_perf,
+							  caps->nominal_perf, true, false);
+		if (ret) {
+			pr_debug("Failed to update autosel configs on CPU%d(%d)\n", cpu, ret);
+			goto out;
+		}
+	} else {
+		ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
+		if (ret) {
+			pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
+				 caps->highest_perf, cpu, ret);
+			goto out;
+		}
 	}
 
 	cppc_cpufreq_cpu_fie_init(policy);
@@ -736,6 +842,13 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 
 	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
 
+	if (cpu_data->perf_caps.auto_sel) {
+		ret = cppc_cpufreq_update_autosel_epp(policy, false,
+						      CPPC_EPP_ENERGY_EFFICIENCY_PREF);
+		if (ret)
+			return;
+	}
+
 	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
 	if (ret)
 		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
@@ -920,17 +1033,10 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
 	 * On enabling auto_select: set min/max_perf register and update policy.
 	 * On disabling auto_select: update only policy.
 	 */
-	ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg, true);
-	if (ret) {
-		pr_warn("failed to %s update min policy for cpu:%d (%d)\n",
-			val > 0 ? "set min_perf and" : "", cpu, ret);
-		return ret;
-	}
-
-	ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg, true);
+	ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, max_perf, update_reg, true);
 	if (ret) {
-		pr_warn("failed to %s update max policy for cpu:%d (%d)\n",
-			val > 0 ? "set max_perf and" : "", cpu, ret);
+		pr_warn("failed to %s update policy for cpu:%d (%d)\n",
+			val > 0 ? "set min/max_perf and" : "", cpu, ret);
 		return ret;
 	}
 
@@ -1139,13 +1245,44 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
 	.name = "cppc_cpufreq",
 };
 
+static void cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64 epp)
+{
+	int cpu, ret;
+
+	for_each_present_cpu(cpu) {
+		ret = cppc_set_epp(cpu, epp);
+		if (ret)
+			pr_debug("failed to set energy_perf for cpu:%d (%d)\n", cpu, ret);
+
+		ret = cppc_set_auto_sel(cpu, auto_sel);
+		if (ret)
+			pr_debug("failed to set auto_sel for cpu:%d (%d)\n", cpu, ret);
+	}
+}
+
 static int __init cppc_cpufreq_init(void)
 {
+	bool auto_sel;
 	int ret;
 
 	if (!acpi_cpc_valid())
 		return -ENODEV;
 
+	if (auto_sel_mode) {
+		/*
+		 * Check if autonomous selection is supported by testing CPU 0.
+		 * If supported, enable autonomous mode on all CPUs.
+		 */
+		ret = cppc_get_auto_sel(0, &auto_sel);
+		if (!ret) {
+			pr_info("Enabling autonomous mode on all CPUs\n");
+			cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
+		} else {
+			pr_warn("Autonomous selection not supported, disabling auto_sel_mode\n");
+			auto_sel_mode = false;
+		}
+	}
+
 	cppc_freq_invariance_init();
 	populate_efficiency_class();
 
@@ -1160,8 +1297,12 @@ static void __exit cppc_cpufreq_exit(void)
 {
 	cpufreq_unregister_driver(&cppc_cpufreq_driver);
 	cppc_freq_invariance_exit();
+	auto_sel_mode = 0;
 }
 
+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] 19+ messages in thread

* Re: [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-08-23 20:01 ` [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
@ 2025-08-24  0:08   ` Randy Dunlap
  2025-09-01 13:12     ` Sumit Gupta
  0 siblings, 1 reply; 19+ messages in thread
From: Randy Dunlap @ 2025-08-24  0:08 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu



On 8/23/25 1:01 PM, Sumit Gupta wrote:
> Add sysfs interfaces for Minimum Performance, Maximum Performance
> and Performance Limited Register in the cppc_cpufreq driver.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  .../ABI/testing/sysfs-devices-system-cpu      | 43 +++++++++++++++++++
>  1 file changed, 43 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index ab8cd337f43a..4bce0dbc48c9 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -327,6 +327,49 @@ 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:           Aug 2025
> +Contact:        linux-pm@vger.kernel.org
> +Description:    Minimum Performance

Preferably these 4 lines above use tab(s) after the ':' for indentation
instead of spaces. (in 3 places, i.e., each sysfs file entry).

> +
> +		Read/write a 32 bits value from/to this file. This file
> +		conveys the minimum performance level at which the platform
> +		may run. Minimum performance may be set to any performance
> +		value in the range [Lowest Performance, Highest Performance],
> +		inclusive but must be set to a value that is less than or
> +		equal to that specified by the Maximum Performance Register.
> +
> +		Writing to this file only has meaning when Autonomous Selection
> +		is enabled.
> +
> +		This file is only present if the cppc-cpufreq driver is in use.
> +
> +What:           /sys/devices/system/cpu/cpuX/cpufreq/max_perf
> +Date:           Aug 2025
> +Contact:        linux-pm@vger.kernel.org
> +Description:    Minimum Performance
> +
> +		Read/write a 32 bits value from/to this file. This file conveys
> +		the maximum performance level at which the platform may run.
> +		Maximum performance may be set to any performance value in the
> +		range [Lowest Performance, Highest Performance], inclusive.
> +
> +		Writing to this file only has meaning when Autonomous Selection is
> +		enabled.
> +
> +		This file is only present if the cppc-cpufreq driver is in use.
> +
> +What:           /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
> +Date:           Aug 2025
> +Contact:        linux-pm@vger.kernel.org
> +Description:    Minimum Performance
> +
> +		Read/write a 32 bits value from/to this file. This file indicates
> +		to OSPM that an unpredictable event has limited processor
> +		performance, and the delivered performance may be less than
> +		desired/minimum performance.
> +
> +		This file is only present if the cppc-cpufreq driver is in use.
>  
>  What:		/sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
>  Date:		August 2008

Reviewed-by: Randy Dunlap <rdunlap@infradead.org>

Thanks.

-- 
~Randy

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

* Re: [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-08-23 20:01 ` [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
@ 2025-08-24  0:08   ` Randy Dunlap
  2025-09-01 13:18     ` Sumit Gupta
  2025-08-25 18:40   ` Mario Limonciello
  1 sibling, 1 reply; 19+ messages in thread
From: Randy Dunlap @ 2025-08-24  0:08 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu



On 8/23/25 1:01 PM, Sumit Gupta wrote:
> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
> autonomous performance selection at system startup. When autonomous mode
> is enabled, the hardware automatically adjusts CPU performance based on
> workload demands using Energy Performance Preference (EPP) hints from
> the OS.
> 
> This parameter allows to configure the autonomous mode on all CPUs
> without requiring runtime sysfs manipulation if the 'auto_sel' register
> is present.
> 
> When auto_sel_mode=1:
> - All CPUs are configured for autonomous operation during driver init
> - EPP is set to performance preference (0x0) by default
> - Min/max performance bounds use defaults
> - CPU frequency scaling is handled by hardware rather than OS
> 
> Also ensure that when autonomous mode is active, the set_target callback
> returns early since hardware controls frequency scaling directly.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>



> ---
>  .../admin-guide/kernel-parameters.txt         |  12 ++
>  drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
>  2 files changed, 168 insertions(+), 15 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 86f395f2933b..ea58deb88c36 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -911,6 +911,18 @@
>  			Format:
>  			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>  
> +	cppc_cpufreq.auto_sel_mode=
> +			[CPU_FREQ] Autonomous Performance Level Selection.
> +			When Autonomous selection is enabled, then the hardware is
> +			allowed to autonomously select the CPU frequency.
> +			In Autonomous mode, Energy Performance Preference(EPP)
> +			provides input to the hardware to favour performance (0x0)
> +			or energy efficiency (0xff).
> +			Format: <bool>
> +			Default: disabled.
> +			0: force disabled
> +			1: force enabled
> +
>  	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 5e1bbb5f67b8..bbf654c56ff9 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c

[snip]

>  
> +module_param(auto_sel_mode, bool, 0000);

Hm. Is this changed to readable at some point or
does it stay invisible?

> +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");

For Documentation/:
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>

Thanks.

-- 
~Randy

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

* Re: [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
  2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
@ 2025-08-25 18:33   ` Rafael J. Wysocki
  2025-09-01 13:46     ` Sumit Gupta
  2025-08-25 23:41   ` kernel test robot
  1 sibling, 1 reply; 19+ messages in thread
From: Rafael J. Wysocki @ 2025-08-25 18:33 UTC (permalink / raw)
  To: Sumit Gupta
  Cc: rafael, viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
	sanjayc, bbasu

On Sat, Aug 23, 2025 at 10:02 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
> Add cppc_get_perf_ctrls() to read performance control register values.
> Rename existing APIs for clarity as:
> - To distinguish between:
>   - Feedback counters (fb_ctrs): Read-only performance monitoring data.
>   - Performance controls (perf_ctrls): Read-write config registers.
> - cppc_set_epp_perf() updates both EPP and Autonomous Selection.
>
> API's renamed:
> - cppc_set_perf() to cppc_set_perf_ctrls().
> - cppc_get_perf_ctrs() to cppc_get_perf_fb_ctrs().
> - cppc_get_perf_ctrs_sample() to cppc_get_perf_fb_ctrs_sample().
> - cppc_set_epp_perf() to cppc_set_epp_and_autosel().

> Remove redundant energy_perf field from 'struct cppc_perf_caps' since
> the same information is available in 'struct cppc_perf_ctrls' which is
> actively used.
>
> All existing callers are updated to maintain compatibility.

First, this is too much in one patch IMV and second, I honestly don't
see a reason for the renames above.

This generally makes tracking the code changes history harder.

Thanks!

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

* Re: [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-08-23 20:01 ` [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
  2025-08-24  0:08   ` Randy Dunlap
@ 2025-08-25 18:40   ` Mario Limonciello
  2025-09-01 13:37     ` Sumit Gupta
  1 sibling, 1 reply; 19+ messages in thread
From: Mario Limonciello @ 2025-08-25 18:40 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu

On 8/23/2025 3:01 PM, Sumit Gupta wrote:
> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
> autonomous performance selection at system startup. When autonomous mode
> is enabled, the hardware automatically adjusts CPU performance based on
> workload demands using Energy Performance Preference (EPP) hints from
> the OS.
> 
> This parameter allows to configure the autonomous mode on all CPUs
> without requiring runtime sysfs manipulation if the 'auto_sel' register
> is present.
> 
> When auto_sel_mode=1:
> - All CPUs are configured for autonomous operation during driver init
> - EPP is set to performance preference (0x0) by default
> - Min/max performance bounds use defaults
> - CPU frequency scaling is handled by hardware rather than OS
> 
> Also ensure that when autonomous mode is active, the set_target callback
> returns early since hardware controls frequency scaling directly.
> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>   .../admin-guide/kernel-parameters.txt         |  12 ++
>   drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
>   2 files changed, 168 insertions(+), 15 deletions(-)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index 86f395f2933b..ea58deb88c36 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -911,6 +911,18 @@
>   			Format:
>   			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>   
> +	cppc_cpufreq.auto_sel_mode=
> +			[CPU_FREQ] Autonomous Performance Level Selection.
> +			When Autonomous selection is enabled, then the hardware is
> +			allowed to autonomously select the CPU frequency.
> +			In Autonomous mode, Energy Performance Preference(EPP)
> +			provides input to the hardware to favour performance (0x0)
> +			or energy efficiency (0xff).
> +			Format: <bool>
> +			Default: disabled.
> +			0: force disabled
> +			1: force enabled

I don't think you can actually force enable.  If the hardware doesn't 
support it, setting 1 won't do anything.

IoW really setting 1 is "enable if supported".

> +
>   	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 5e1bbb5f67b8..bbf654c56ff9 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -27,6 +27,8 @@
>   #include <acpi/cppc_acpi.h>
>   
>   static struct cpufreq_driver cppc_cpufreq_driver;
> +/* Autonomous Selection */
> +static bool auto_sel_mode;
>   
>   #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>   static enum {
> @@ -272,6 +274,14 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
>   	freqs.old = policy->cur;
>   	freqs.new = target_freq;
>   
> +	/*
> +	 * In autonomous mode, hardware handles frequency scaling directly
> +	 * based on workload demands and EPP hints, so OS frequency requests
> +	 * are not needed.
> +	 */
> +	if (cpu_data->perf_caps.auto_sel)
> +		return ret;
> +
>   	cpufreq_freq_transition_begin(policy, &freqs);
>   	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>   	cpufreq_freq_transition_end(policy, &freqs, ret != 0);
> @@ -555,6 +565,12 @@ static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>   		goto free_mask;
>   	}
>   
> +	ret = cppc_get_perf_ctrls(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:
> @@ -642,6 +658,79 @@ static int cppc_cpufreq_set_max_perf(struct cpufreq_policy *policy, u64 val,
>   	return (ret == -EOPNOTSUPP) ? 0 : ret;
>   }
>   
> +static int cppc_cpufreq_update_autosel_epp(struct cpufreq_policy *policy, int auto_sel, u32 epp)
> +{
> +	struct cppc_cpudata *cpu_data = policy->driver_data;
> +	unsigned int cpu = policy->cpu;
> +	int ret;
> +
> +	pr_debug("cpu%d: curr epp:%u, curr mode:%u, new epp:%u, new mode:%d\n", cpu,
> +		 cpu_data->perf_ctrls.energy_perf, cpu_data->perf_caps.auto_sel, epp, auto_sel);
> +
> +	mutex_lock(&cppc_cpufreq_update_autosel_config_lock);

As I noticed below a case you missed the mutex unlock, this feels like a 
good candidate for

guard(mutex)();

> +
> +	ret = cppc_set_epp(cpu, epp);
> +	if (ret) {
> +		pr_warn("failed to set energy_perf for cpu:%d (%d)\n", cpu, ret);
> +		goto out;
> +	}
> +	cpu_data->perf_ctrls.energy_perf = epp;
> +
> +	ret = cppc_set_auto_sel(cpu, auto_sel);
> +	if (ret) {
> +		pr_warn("failed to set auto_sel for cpu:%d (%d)\n", cpu, ret);
> +		return ret;

Looks like a case that you didn't unlock the mutex.

> +	}
> +	cpu_data->perf_caps.auto_sel = auto_sel;
> +
> +out:
> +	mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
> +	return ret;
> +}
> +
> +static int cppc_cpufreq_update_autosel_mperf_ctrls(struct cpufreq_policy *policy, u32 min_p,
> +						   u32 max_p, bool update_reg, bool update_policy)
> +{
> +	struct cppc_cpudata *cpu_data = policy->driver_data;
> +	unsigned int cpu = policy->cpu;
> +	int ret;
> +
> +	pr_debug("cpu%d: curr max_perf:%u, curr min_perf:%u, new max_perf:%u, new min_perf:%u\n",
> +		 cpu, cpu_data->perf_ctrls.max_perf, cpu_data->perf_ctrls.min_perf, max_p, min_p);
> +
> +	ret = cppc_cpufreq_set_min_perf(policy, min_p, update_reg, update_policy);
> +	if (ret) {
> +		pr_debug("failed to set min_perf for cpu:%d (%d)\n", cpu, ret);
> +		return ret;
> +	}
> +
> +	ret = cppc_cpufreq_set_max_perf(policy, max_p, update_reg, update_policy);
> +	if (ret) {
> +		pr_debug("failed to set max_perf for cpu:%d (%d)\n", cpu, ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int cppc_cpufreq_update_autosel_configs(struct cpufreq_policy *policy, int auto_sel,
> +					       u32 epp, u32 min_perf, u32 max_perf,
> +					       bool update_reg, bool update_policy)
> +{
> +	int ret;
> +
> +	ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, max_perf,
> +						      update_reg, update_policy);
> +	if (ret)
> +		return ret;
> +
> +	ret = cppc_cpufreq_update_autosel_epp(policy, auto_sel, epp);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
>   static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>   {
>   	unsigned int cpu = policy->cpu;
> @@ -710,11 +799,28 @@ 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_ctrls(cpu, &cpu_data->perf_ctrls);
> -	if (ret) {
> -		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
> -			 caps->highest_perf, cpu, ret);
> -		goto out;
> +	if (cpu_data->perf_caps.auto_sel) {
> +		ret = cppc_set_enable(cpu, true);
> +		if (ret) {
> +			pr_err("Failed to enable CPPC on cpu%d (%d)\n", cpu, ret);
> +			goto out;
> +		}
> +
> +		ret = cppc_cpufreq_update_autosel_configs(policy, true,
> +							  CPPC_EPP_PERFORMANCE_PREF,
> +							  caps->lowest_nonlinear_perf,
> +							  caps->nominal_perf, true, false);
> +		if (ret) {
> +			pr_debug("Failed to update autosel configs on CPU%d(%d)\n", cpu, ret);
> +			goto out;
> +		}
> +	} else {
> +		ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
> +		if (ret) {
> +			pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
> +				 caps->highest_perf, cpu, ret);
> +			goto out;
> +		}
>   	}
>   
>   	cppc_cpufreq_cpu_fie_init(policy);
> @@ -736,6 +842,13 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
>   
>   	cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
>   
> +	if (cpu_data->perf_caps.auto_sel) {
> +		ret = cppc_cpufreq_update_autosel_epp(policy, false,
> +						      CPPC_EPP_ENERGY_EFFICIENCY_PREF);
> +		if (ret)
> +			return;
> +	}
> +
>   	ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>   	if (ret)
>   		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
> @@ -920,17 +1033,10 @@ static ssize_t store_auto_select(struct cpufreq_policy *policy,
>   	 * On enabling auto_select: set min/max_perf register and update policy.
>   	 * On disabling auto_select: update only policy.
>   	 */
> -	ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg, true);
> -	if (ret) {
> -		pr_warn("failed to %s update min policy for cpu:%d (%d)\n",
> -			val > 0 ? "set min_perf and" : "", cpu, ret);
> -		return ret;
> -	}
> -
> -	ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg, true);
> +	ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, max_perf, update_reg, true);
>   	if (ret) {
> -		pr_warn("failed to %s update max policy for cpu:%d (%d)\n",
> -			val > 0 ? "set max_perf and" : "", cpu, ret);
> +		pr_warn("failed to %s update policy for cpu:%d (%d)\n",
> +			val > 0 ? "set min/max_perf and" : "", cpu, ret);
>   		return ret;
>   	}
>   
> @@ -1139,13 +1245,44 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
>   	.name = "cppc_cpufreq",
>   };
>   
> +static void cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64 epp)
> +{
> +	int cpu, ret;
> +
> +	for_each_present_cpu(cpu) {
> +		ret = cppc_set_epp(cpu, epp);
> +		if (ret)
> +			pr_debug("failed to set energy_perf for cpu:%d (%d)\n", cpu, ret);
> +
> +		ret = cppc_set_auto_sel(cpu, auto_sel);
> +		if (ret)
> +			pr_debug("failed to set auto_sel for cpu:%d (%d)\n", cpu, ret);
> +	}
> +}
> +
>   static int __init cppc_cpufreq_init(void)
>   {
> +	bool auto_sel;
>   	int ret;
>   
>   	if (!acpi_cpc_valid())
>   		return -ENODEV;
>   
> +	if (auto_sel_mode) {
> +		/*
> +		 * Check if autonomous selection is supported by testing CPU 0.
> +		 * If supported, enable autonomous mode on all CPUs.
> +		 */
> +		ret = cppc_get_auto_sel(0, &auto_sel);
> +		if (!ret) {
> +			pr_info("Enabling autonomous mode on all CPUs\n");
> +			cppc_cpufreq_set_epp_autosel_allcpus(true, CPPC_EPP_PERFORMANCE_PREF);
> +		} else {
> +			pr_warn("Autonomous selection not supported, disabling auto_sel_mode\n");
> +			auto_sel_mode = false;
> +		}
> +	}
> +
>   	cppc_freq_invariance_init();
>   	populate_efficiency_class();
>   
> @@ -1160,8 +1297,12 @@ static void __exit cppc_cpufreq_exit(void)
>   {
>   	cpufreq_unregister_driver(&cppc_cpufreq_driver);
>   	cppc_freq_invariance_exit();
> +	auto_sel_mode = 0;
>   }
>   
> +module_param(auto_sel_mode, bool, 0000);
> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level Selection");

Why default to disabled?  As a precaution?  We enable EPP by default in 
the *-pstate drivers if the hardware supports it, I would think it makes 
sense here too.

> +
>   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] 19+ messages in thread

* Re: [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
  2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
  2025-08-25 18:33   ` Rafael J. Wysocki
@ 2025-08-25 23:41   ` kernel test robot
  1 sibling, 0 replies; 19+ messages in thread
From: kernel test robot @ 2025-08-25 23:41 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel
  Cc: llvm, oe-kbuild-all, linux-tegra, treding, jonathanh, vsethi,
	ksitaraman, sanjayc, bbasu, sumitg

Hi Sumit,

kernel test robot noticed the following build errors:

[auto build test ERROR on rafael-pm/linux-next]
[also build test ERROR on rafael-pm/bleeding-edge linus/master v6.17-rc3 next-20250825]
[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/ACPI-CPPC-add-perf-control-read-API-and-clarify-naming/20250824-040531
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20250823200121.1320197-2-sumitg%40nvidia.com
patch subject: [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20250826/202508260711.I7imWLTG-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250826/202508260711.I7imWLTG-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/202508260711.I7imWLTG-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/cpufreq/amd-pstate.c:524:8: error: call to undeclared function 'cppc_set_perf'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     524 |         ret = cppc_set_perf(cpudata->cpu, &perf_ctrls);
         |               ^
   drivers/cpufreq/amd-pstate.c:524:8: note: did you mean 'cppc_set_epp'?
   include/acpi/cppc_acpi.h:170:12: note: 'cppc_set_epp' declared here
     170 | extern int cppc_set_epp(int cpu, u64 epp_val);
         |            ^
   1 error generated.


vim +/cppc_set_perf +524 drivers/cpufreq/amd-pstate.c

e059c184da47e9 Huang Rui         2021-12-24  480  
77fbea69b0ffad Mario Limonciello 2024-12-09  481  static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
555bbe67a622b2 Dhananjay Ugwekar 2025-02-05  482  			     u8 des_perf, u8 max_perf, u8 epp, bool fast_switch)
e059c184da47e9 Huang Rui         2021-12-24  483  {
77fbea69b0ffad Mario Limonciello 2024-12-09  484  	struct amd_cpudata *cpudata = policy->driver_data;
e059c184da47e9 Huang Rui         2021-12-24  485  	struct cppc_perf_ctrls perf_ctrls;
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  486  	u64 value, prev;
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  487  	int ret;
e059c184da47e9 Huang Rui         2021-12-24  488  
fff395796917ac Mario Limonciello 2024-12-09  489  	if (cppc_state == AMD_PSTATE_ACTIVE) {
77fbea69b0ffad Mario Limonciello 2024-12-09  490  		int ret = shmem_set_epp(policy, epp);
fff395796917ac Mario Limonciello 2024-12-09  491  
fff395796917ac Mario Limonciello 2024-12-09  492  		if (ret)
fff395796917ac Mario Limonciello 2024-12-09  493  			return ret;
fff395796917ac Mario Limonciello 2024-12-09  494  	}
fff395796917ac Mario Limonciello 2024-12-09  495  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  496  	value = prev = READ_ONCE(cpudata->cppc_req_cached);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  497  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  498  	value &= ~(AMD_CPPC_MAX_PERF_MASK | AMD_CPPC_MIN_PERF_MASK |
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  499  		   AMD_CPPC_DES_PERF_MASK | AMD_CPPC_EPP_PERF_MASK);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  500  	value |= FIELD_PREP(AMD_CPPC_MAX_PERF_MASK, max_perf);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  501  	value |= FIELD_PREP(AMD_CPPC_DES_PERF_MASK, des_perf);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  502  	value |= FIELD_PREP(AMD_CPPC_MIN_PERF_MASK, min_perf);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  503  	value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  504  
77fbea69b0ffad Mario Limonciello 2024-12-09  505  	if (trace_amd_pstate_epp_perf_enabled()) {
77fbea69b0ffad Mario Limonciello 2024-12-09  506  		union perf_cached perf = READ_ONCE(cpudata->perf);
77fbea69b0ffad Mario Limonciello 2024-12-09  507  
77fbea69b0ffad Mario Limonciello 2024-12-09  508  		trace_amd_pstate_epp_perf(cpudata->cpu,
77fbea69b0ffad Mario Limonciello 2024-12-09  509  					  perf.highest_perf,
77fbea69b0ffad Mario Limonciello 2024-12-09  510  					  epp,
77fbea69b0ffad Mario Limonciello 2024-12-09  511  					  min_perf,
77fbea69b0ffad Mario Limonciello 2024-12-09  512  					  max_perf,
77fbea69b0ffad Mario Limonciello 2024-12-09  513  					  policy->boost_enabled,
77fbea69b0ffad Mario Limonciello 2024-12-09  514  					  value != prev);
77fbea69b0ffad Mario Limonciello 2024-12-09  515  	}
77fbea69b0ffad Mario Limonciello 2024-12-09  516  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  517  	if (value == prev)
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  518  		return 0;
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  519  
e059c184da47e9 Huang Rui         2021-12-24  520  	perf_ctrls.max_perf = max_perf;
e059c184da47e9 Huang Rui         2021-12-24  521  	perf_ctrls.min_perf = min_perf;
e059c184da47e9 Huang Rui         2021-12-24  522  	perf_ctrls.desired_perf = des_perf;
e059c184da47e9 Huang Rui         2021-12-24  523  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09 @524  	ret = cppc_set_perf(cpudata->cpu, &perf_ctrls);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  525  	if (ret)
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  526  		return ret;
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  527  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  528  	WRITE_ONCE(cpudata->cppc_req_cached, value);
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  529  
9f5daa2f2f6ddd Mario Limonciello 2024-12-09  530  	return 0;
e059c184da47e9 Huang Rui         2021-12-24  531  }
e059c184da47e9 Huang Rui         2021-12-24  532  

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

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

* Re: [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited
  2025-08-24  0:08   ` Randy Dunlap
@ 2025-09-01 13:12     ` Sumit Gupta
  0 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-09-01 13:12 UTC (permalink / raw)
  To: Randy Dunlap, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg


On 24/08/25 05:38, Randy Dunlap wrote:
>
> On 8/23/25 1:01 PM, Sumit Gupta wrote:
>> Add sysfs interfaces for Minimum Performance, Maximum Performance
>> and Performance Limited Register in the cppc_cpufreq driver.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>>   .../ABI/testing/sysfs-devices-system-cpu      | 43 +++++++++++++++++++
>>   1 file changed, 43 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> index ab8cd337f43a..4bce0dbc48c9 100644
>> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
>> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> @@ -327,6 +327,49 @@ 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:           Aug 2025
>> +Contact:        linux-pm@vger.kernel.org
>> +Description:    Minimum Performance
> Preferably these 4 lines above use tab(s) after the ':' for indentation
> instead of spaces. (in 3 places, i.e., each sysfs file entry).

Sorry for replying late as I was on vacation.

Sure, will replace spaces with tabs in v3 and add your 'Reviewed-by'.


Thank you,

Sumit Gupta


>> +
>> +		Read/write a 32 bits value from/to this file. This file
>> +		conveys the minimum performance level at which the platform
>> +		may run. Minimum performance may be set to any performance
>> +		value in the range [Lowest Performance, Highest Performance],
>> +		inclusive but must be set to a value that is less than or
>> +		equal to that specified by the Maximum Performance Register.
>> +
>> +		Writing to this file only has meaning when Autonomous Selection
>> +		is enabled.
>> +
>> +		This file is only present if the cppc-cpufreq driver is in use.
>> +
>> +What:           /sys/devices/system/cpu/cpuX/cpufreq/max_perf
>> +Date:           Aug 2025
>> +Contact:        linux-pm@vger.kernel.org
>> +Description:    Minimum Performance
>> +
>> +		Read/write a 32 bits value from/to this file. This file conveys
>> +		the maximum performance level at which the platform may run.
>> +		Maximum performance may be set to any performance value in the
>> +		range [Lowest Performance, Highest Performance], inclusive.
>> +
>> +		Writing to this file only has meaning when Autonomous Selection is
>> +		enabled.
>> +
>> +		This file is only present if the cppc-cpufreq driver is in use.
>> +
>> +What:           /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
>> +Date:           Aug 2025
>> +Contact:        linux-pm@vger.kernel.org
>> +Description:    Minimum Performance
>> +
>> +		Read/write a 32 bits value from/to this file. This file indicates
>> +		to OSPM that an unpredictable event has limited processor
>> +		performance, and the delivered performance may be less than
>> +		desired/minimum performance.
>> +
>> +		This file is only present if the cppc-cpufreq driver is in use.
>>   
>>   What:		/sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1}
>>   Date:		August 2008
> Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
>
> Thanks.
>

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

* Re: [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-08-24  0:08   ` Randy Dunlap
@ 2025-09-01 13:18     ` Sumit Gupta
  0 siblings, 0 replies; 19+ messages in thread
From: Sumit Gupta @ 2025-09-01 13:18 UTC (permalink / raw)
  To: Randy Dunlap, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg


On 24/08/25 05:38, Randy Dunlap wrote:
>
> On 8/23/25 1:01 PM, Sumit Gupta wrote:
>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>> autonomous performance selection at system startup. When autonomous mode
>> is enabled, the hardware automatically adjusts CPU performance based on
>> workload demands using Energy Performance Preference (EPP) hints from
>> the OS.
>>
>> This parameter allows to configure the autonomous mode on all CPUs
>> without requiring runtime sysfs manipulation if the 'auto_sel' register
>> is present.
>>
>> When auto_sel_mode=1:
>> - All CPUs are configured for autonomous operation during driver init
>> - EPP is set to performance preference (0x0) by default
>> - Min/max performance bounds use defaults
>> - CPU frequency scaling is handled by hardware rather than OS
>>
>> Also ensure that when autonomous mode is active, the set_target callback
>> returns early since hardware controls frequency scaling directly.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>
>
>> ---
>>   .../admin-guide/kernel-parameters.txt         |  12 ++
>>   drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
>>   2 files changed, 168 insertions(+), 15 deletions(-)
>>
>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>> index 86f395f2933b..ea58deb88c36 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -911,6 +911,18 @@
>>   			Format:
>>   			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>>   
>> +	cppc_cpufreq.auto_sel_mode=
>> +			[CPU_FREQ] Autonomous Performance Level Selection.
>> +			When Autonomous selection is enabled, then the hardware is
>> +			allowed to autonomously select the CPU frequency.
>> +			In Autonomous mode, Energy Performance Preference(EPP)
>> +			provides input to the hardware to favour performance (0x0)
>> +			or energy efficiency (0xff).
>> +			Format: <bool>
>> +			Default: disabled.
>> +			0: force disabled
>> +			1: force enabled
>> +
>>   	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 5e1bbb5f67b8..bbf654c56ff9 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
> [snip]
>
>>   
>> +module_param(auto_sel_mode, bool, 0000);
> Hm. Is this changed to readable at some point or
> does it stay invisible?

Yes, it stays invisible. I am not sure about future as of now.

Thank you,
Sumit Gupta
>
>> +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");
> For Documentation/:
> Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
>
> Thanks.
>

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

* Re: [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-08-25 18:40   ` Mario Limonciello
@ 2025-09-01 13:37     ` Sumit Gupta
  2025-09-02 19:48       ` Mario Limonciello
  0 siblings, 1 reply; 19+ messages in thread
From: Sumit Gupta @ 2025-09-01 13:37 UTC (permalink / raw)
  To: Mario Limonciello, rafael, viresh.kumar, lenb, robert.moore,
	corbet, pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu, sumitg


On 26/08/25 00:10, Mario Limonciello wrote:
> External email: Use caution opening links or attachments
>
>
> On 8/23/2025 3:01 PM, Sumit Gupta wrote:
>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>> autonomous performance selection at system startup. When autonomous mode
>> is enabled, the hardware automatically adjusts CPU performance based on
>> workload demands using Energy Performance Preference (EPP) hints from
>> the OS.
>>
>> This parameter allows to configure the autonomous mode on all CPUs
>> without requiring runtime sysfs manipulation if the 'auto_sel' register
>> is present.
>>
>> When auto_sel_mode=1:
>> - All CPUs are configured for autonomous operation during driver init
>> - EPP is set to performance preference (0x0) by default
>> - Min/max performance bounds use defaults
>> - CPU frequency scaling is handled by hardware rather than OS
>>
>> Also ensure that when autonomous mode is active, the set_target callback
>> returns early since hardware controls frequency scaling directly.
>>
>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>> ---
>>   .../admin-guide/kernel-parameters.txt         |  12 ++
>>   drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
>>   2 files changed, 168 insertions(+), 15 deletions(-)
>>
>> diff --git a/Documentation/admin-guide/kernel-parameters.txt 
>> b/Documentation/admin-guide/kernel-parameters.txt
>> index 86f395f2933b..ea58deb88c36 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -911,6 +911,18 @@
>>                       Format:
>> <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>>
>> +     cppc_cpufreq.auto_sel_mode=
>> +                     [CPU_FREQ] Autonomous Performance Level Selection.
>> +                     When Autonomous selection is enabled, then the 
>> hardware is
>> +                     allowed to autonomously select the CPU frequency.
>> +                     In Autonomous mode, Energy Performance 
>> Preference(EPP)
>> +                     provides input to the hardware to favour 
>> performance (0x0)
>> +                     or energy efficiency (0xff).
>> +                     Format: <bool>
>> +                     Default: disabled.
>> +                     0: force disabled
>> +                     1: force enabled
>
> I don't think you can actually force enable.  If the hardware doesn't
> support it, setting 1 won't do anything.
>
> IoW really setting 1 is "enable if supported".
>

Yes, will change in v3.


>> +
>>       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 5e1bbb5f67b8..bbf654c56ff9 100644
>> --- a/drivers/cpufreq/cppc_cpufreq.c
>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>> @@ -27,6 +27,8 @@
>>   #include <acpi/cppc_acpi.h>
>>
>>   static struct cpufreq_driver cppc_cpufreq_driver;
>> +/* Autonomous Selection */
>> +static bool auto_sel_mode;
>>
>>   #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>>   static enum {
>> @@ -272,6 +274,14 @@ static int cppc_cpufreq_set_target(struct 
>> cpufreq_policy *policy,
>>       freqs.old = policy->cur;
>>       freqs.new = target_freq;
>>
>> +     /*
>> +      * In autonomous mode, hardware handles frequency scaling directly
>> +      * based on workload demands and EPP hints, so OS frequency 
>> requests
>> +      * are not needed.
>> +      */
>> +     if (cpu_data->perf_caps.auto_sel)
>> +             return ret;
>> +
>>       cpufreq_freq_transition_begin(policy, &freqs);
>>       ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>>       cpufreq_freq_transition_end(policy, &freqs, ret != 0);
>> @@ -555,6 +565,12 @@ static struct cppc_cpudata 
>> *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>>               goto free_mask;
>>       }
>>
>> +     ret = cppc_get_perf_ctrls(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:
>> @@ -642,6 +658,79 @@ static int cppc_cpufreq_set_max_perf(struct 
>> cpufreq_policy *policy, u64 val,
>>       return (ret == -EOPNOTSUPP) ? 0 : ret;
>>   }
>>
>> +static int cppc_cpufreq_update_autosel_epp(struct cpufreq_policy 
>> *policy, int auto_sel, u32 epp)
>> +{
>> +     struct cppc_cpudata *cpu_data = policy->driver_data;
>> +     unsigned int cpu = policy->cpu;
>> +     int ret;
>> +
>> +     pr_debug("cpu%d: curr epp:%u, curr mode:%u, new epp:%u, new 
>> mode:%d\n", cpu,
>> +              cpu_data->perf_ctrls.energy_perf, 
>> cpu_data->perf_caps.auto_sel, epp, auto_sel);
>> +
>> +     mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
>
> As I noticed below a case you missed the mutex unlock, this feels like a
> good candidate for
>
> guard(mutex)();
>

I will check about 'guard(mutex)()' and change accordingly in v3.

>> +
>> +     ret = cppc_set_epp(cpu, epp);
>> +     if (ret) {
>> +             pr_warn("failed to set energy_perf for cpu:%d (%d)\n", 
>> cpu, ret);
>> +             goto out;
>> +     }
>> +     cpu_data->perf_ctrls.energy_perf = epp;
>> +
>> +     ret = cppc_set_auto_sel(cpu, auto_sel);
>> +     if (ret) {
>> +             pr_warn("failed to set auto_sel for cpu:%d (%d)\n", 
>> cpu, ret);
>> +             return ret;
>
> Looks like a case that you didn't unlock the mutex.

ACK.

>
>> +     }
>> +     cpu_data->perf_caps.auto_sel = auto_sel;
>> +
>> +out:
>> + mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
>> +     return ret;
>> +}
>> +
>> +static int cppc_cpufreq_update_autosel_mperf_ctrls(struct 
>> cpufreq_policy *policy, u32 min_p,
>> +                                                u32 max_p, bool 
>> update_reg, bool update_policy)
>> +{
>> +     struct cppc_cpudata *cpu_data = policy->driver_data;
>> +     unsigned int cpu = policy->cpu;
>> +     int ret;
>> +
>> +     pr_debug("cpu%d: curr max_perf:%u, curr min_perf:%u, new 
>> max_perf:%u, new min_perf:%u\n",
>> +              cpu, cpu_data->perf_ctrls.max_perf, 
>> cpu_data->perf_ctrls.min_perf, max_p, min_p);
>> +
>> +     ret = cppc_cpufreq_set_min_perf(policy, min_p, update_reg, 
>> update_policy);
>> +     if (ret) {
>> +             pr_debug("failed to set min_perf for cpu:%d (%d)\n", 
>> cpu, ret);
>> +             return ret;
>> +     }
>> +
>> +     ret = cppc_cpufreq_set_max_perf(policy, max_p, update_reg, 
>> update_policy);
>> +     if (ret) {
>> +             pr_debug("failed to set max_perf for cpu:%d (%d)\n", 
>> cpu, ret);
>> +             return ret;
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static int cppc_cpufreq_update_autosel_configs(struct cpufreq_policy 
>> *policy, int auto_sel,
>> +                                            u32 epp, u32 min_perf, 
>> u32 max_perf,
>> +                                            bool update_reg, bool 
>> update_policy)
>> +{
>> +     int ret;
>> +
>> +     ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, 
>> max_perf,
>> +                                                   update_reg, 
>> update_policy);
>> +     if (ret)
>> +             return ret;
>> +
>> +     ret = cppc_cpufreq_update_autosel_epp(policy, auto_sel, epp);
>> +     if (ret)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>>   static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>>   {
>>       unsigned int cpu = policy->cpu;
>> @@ -710,11 +799,28 @@ 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_ctrls(cpu, &cpu_data->perf_ctrls);
>> -     if (ret) {
>> -             pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
>> -                      caps->highest_perf, cpu, ret);
>> -             goto out;
>> +     if (cpu_data->perf_caps.auto_sel) {
>> +             ret = cppc_set_enable(cpu, true);
>> +             if (ret) {
>> +                     pr_err("Failed to enable CPPC on cpu%d (%d)\n", 
>> cpu, ret);
>> +                     goto out;
>> +             }
>> +
>> +             ret = cppc_cpufreq_update_autosel_configs(policy, true,
>> + CPPC_EPP_PERFORMANCE_PREF,
>> + caps->lowest_nonlinear_perf,
>> + caps->nominal_perf, true, false);
>> +             if (ret) {
>> +                     pr_debug("Failed to update autosel configs on 
>> CPU%d(%d)\n", cpu, ret);
>> +                     goto out;
>> +             }
>> +     } else {
>> +             ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>> +             if (ret) {
>> +                     pr_debug("Err setting perf value:%d on CPU:%d. 
>> ret:%d\n",
>> +                              caps->highest_perf, cpu, ret);
>> +                     goto out;
>> +             }
>>       }
>>
>>       cppc_cpufreq_cpu_fie_init(policy);
>> @@ -736,6 +842,13 @@ static void cppc_cpufreq_cpu_exit(struct 
>> cpufreq_policy *policy)
>>
>>       cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
>>
>> +     if (cpu_data->perf_caps.auto_sel) {
>> +             ret = cppc_cpufreq_update_autosel_epp(policy, false,
>> + CPPC_EPP_ENERGY_EFFICIENCY_PREF);
>> +             if (ret)
>> +                     return;
>> +     }
>> +
>>       ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>>       if (ret)
>>               pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
>> @@ -920,17 +1033,10 @@ static ssize_t store_auto_select(struct 
>> cpufreq_policy *policy,
>>        * On enabling auto_select: set min/max_perf register and 
>> update policy.
>>        * On disabling auto_select: update only policy.
>>        */
>> -     ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg, 
>> true);
>> -     if (ret) {
>> -             pr_warn("failed to %s update min policy for cpu:%d 
>> (%d)\n",
>> -                     val > 0 ? "set min_perf and" : "", cpu, ret);
>> -             return ret;
>> -     }
>> -
>> -     ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg, 
>> true);
>> +     ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, 
>> max_perf, update_reg, true);
>>       if (ret) {
>> -             pr_warn("failed to %s update max policy for cpu:%d 
>> (%d)\n",
>> -                     val > 0 ? "set max_perf and" : "", cpu, ret);
>> +             pr_warn("failed to %s update policy for cpu:%d (%d)\n",
>> +                     val > 0 ? "set min/max_perf and" : "", cpu, ret);
>>               return ret;
>>       }
>>
>> @@ -1139,13 +1245,44 @@ static struct cpufreq_driver 
>> cppc_cpufreq_driver = {
>>       .name = "cppc_cpufreq",
>>   };
>>
>> +static void cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64 
>> epp)
>> +{
>> +     int cpu, ret;
>> +
>> +     for_each_present_cpu(cpu) {
>> +             ret = cppc_set_epp(cpu, epp);
>> +             if (ret)
>> +                     pr_debug("failed to set energy_perf for cpu:%d 
>> (%d)\n", cpu, ret);
>> +
>> +             ret = cppc_set_auto_sel(cpu, auto_sel);
>> +             if (ret)
>> +                     pr_debug("failed to set auto_sel for cpu:%d 
>> (%d)\n", cpu, ret);
>> +     }
>> +}
>> +
>>   static int __init cppc_cpufreq_init(void)
>>   {
>> +     bool auto_sel;
>>       int ret;
>>
>>       if (!acpi_cpc_valid())
>>               return -ENODEV;
>>
>> +     if (auto_sel_mode) {
>> +             /*
>> +              * Check if autonomous selection is supported by 
>> testing CPU 0.
>> +              * If supported, enable autonomous mode on all CPUs.
>> +              */
>> +             ret = cppc_get_auto_sel(0, &auto_sel);
>> +             if (!ret) {
>> +                     pr_info("Enabling autonomous mode on all CPUs\n");
>> +                     cppc_cpufreq_set_epp_autosel_allcpus(true, 
>> CPPC_EPP_PERFORMANCE_PREF);
>> +             } else {
>> +                     pr_warn("Autonomous selection not supported, 
>> disabling auto_sel_mode\n");
>> +                     auto_sel_mode = false;
>> +             }
>> +     }
>> +
>>       cppc_freq_invariance_init();
>>       populate_efficiency_class();
>>
>> @@ -1160,8 +1297,12 @@ static void __exit cppc_cpufreq_exit(void)
>>   {
>>       cpufreq_unregister_driver(&cppc_cpufreq_driver);
>>       cppc_freq_invariance_exit();
>> +     auto_sel_mode = 0;
>>   }
>>
>> +module_param(auto_sel_mode, bool, 0000);
>> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level 
>> Selection");
>
> Why default to disabled?  As a precaution?  We enable EPP by default in
> the *-pstate drivers if the hardware supports it, I would think it makes
> sense here too.
>

Kept disabled by default both as precaution and to enable it based on 
preference.
Someone may want to try different config values during bringup, 
verification etc.

Thank you,
Sumit Gupta

>> +
>>   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] 19+ messages in thread

* Re: [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
  2025-08-25 18:33   ` Rafael J. Wysocki
@ 2025-09-01 13:46     ` Sumit Gupta
  2025-09-01 19:00       ` Rafael J. Wysocki
  0 siblings, 1 reply; 19+ messages in thread
From: Sumit Gupta @ 2025-09-01 13:46 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: viresh.kumar, lenb, robert.moore, corbet, pierre.gondois,
	zhenglifeng1, ray.huang, gautham.shenoy, mario.limonciello,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel, linux-tegra, treding, jonathanh, vsethi, ksitaraman,
	sanjayc, bbasu, sumitg


On 26/08/25 00:03, Rafael J. Wysocki wrote:
> External email: Use caution opening links or attachments
>
>
> On Sat, Aug 23, 2025 at 10:02 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>> Add cppc_get_perf_ctrls() to read performance control register values.
>> Rename existing APIs for clarity as:
>> - To distinguish between:
>>    - Feedback counters (fb_ctrs): Read-only performance monitoring data.
>>    - Performance controls (perf_ctrls): Read-write config registers.
>> - cppc_set_epp_perf() updates both EPP and Autonomous Selection.
>>
>> API's renamed:
>> - cppc_set_perf() to cppc_set_perf_ctrls().
>> - cppc_get_perf_ctrs() to cppc_get_perf_fb_ctrs().
>> - cppc_get_perf_ctrs_sample() to cppc_get_perf_fb_ctrs_sample().
>> - cppc_set_epp_perf() to cppc_set_epp_and_autosel().
>> Remove redundant energy_perf field from 'struct cppc_perf_caps' since
>> the same information is available in 'struct cppc_perf_ctrls' which is
>> actively used.
>>
>> All existing callers are updated to maintain compatibility.
> First, this is too much in one patch IMV and second, I honestly don't
> see a reason for the renames above.
>
> This generally makes tracking the code changes history harder.
>
> Thanks!

Did the renaming for clarity and better readability.
If we don't want to do that then i can drop the renaming and keep other 
changes.
Also, split this patch into two as below:
         Patch1: Add cppc_get_perf() API.

         Patch2:
           - Update both EPP and Autonomous Selection in 
cppc_set_epp_perf().
           - Remove redundant energy_perf field from 'struct 
cppc_perf_caps'.

Thank you,
Sumit Gupta



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

* Re: [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming
  2025-09-01 13:46     ` Sumit Gupta
@ 2025-09-01 19:00       ` Rafael J. Wysocki
  0 siblings, 0 replies; 19+ messages in thread
From: Rafael J. Wysocki @ 2025-09-01 19:00 UTC (permalink / raw)
  To: Sumit Gupta
  Cc: Rafael J. Wysocki, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	mario.limonciello, perry.yuan, linux-pm, linux-acpi, linux-doc,
	acpica-devel, linux-kernel, linux-tegra, treding, jonathanh,
	vsethi, ksitaraman, sanjayc, bbasu

On Mon, Sep 1, 2025 at 3:46 PM Sumit Gupta <sumitg@nvidia.com> wrote:
>
>
> On 26/08/25 00:03, Rafael J. Wysocki wrote:
> > External email: Use caution opening links or attachments
> >
> >
> > On Sat, Aug 23, 2025 at 10:02 PM Sumit Gupta <sumitg@nvidia.com> wrote:
> >> Add cppc_get_perf_ctrls() to read performance control register values.
> >> Rename existing APIs for clarity as:
> >> - To distinguish between:
> >>    - Feedback counters (fb_ctrs): Read-only performance monitoring data.
> >>    - Performance controls (perf_ctrls): Read-write config registers.
> >> - cppc_set_epp_perf() updates both EPP and Autonomous Selection.
> >>
> >> API's renamed:
> >> - cppc_set_perf() to cppc_set_perf_ctrls().
> >> - cppc_get_perf_ctrs() to cppc_get_perf_fb_ctrs().
> >> - cppc_get_perf_ctrs_sample() to cppc_get_perf_fb_ctrs_sample().
> >> - cppc_set_epp_perf() to cppc_set_epp_and_autosel().
> >> Remove redundant energy_perf field from 'struct cppc_perf_caps' since
> >> the same information is available in 'struct cppc_perf_ctrls' which is
> >> actively used.
> >>
> >> All existing callers are updated to maintain compatibility.
> >
> > First, this is too much in one patch IMV and second, I honestly don't
> > see a reason for the renames above.
> >
> > This generally makes tracking the code changes history harder.
> >
> > Thanks!
>
> Did the renaming for clarity and better readability.
> If we don't want to do that then i can drop the renaming and keep other
> changes.

Please do.

> Also, split this patch into two as below:
>          Patch1: Add cppc_get_perf() API.
>
>          Patch2:
>            - Update both EPP and Autonomous Selection in
> cppc_set_epp_perf().
>            - Remove redundant energy_perf field from 'struct
> cppc_perf_caps'.

Sounds reasonable to me.

Thanks!

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

* Re: [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support
  2025-09-01 13:37     ` Sumit Gupta
@ 2025-09-02 19:48       ` Mario Limonciello
  0 siblings, 0 replies; 19+ messages in thread
From: Mario Limonciello @ 2025-09-02 19:48 UTC (permalink / raw)
  To: Sumit Gupta, rafael, viresh.kumar, lenb, robert.moore, corbet,
	pierre.gondois, zhenglifeng1, ray.huang, gautham.shenoy,
	perry.yuan, linux-pm, linux-acpi, linux-doc, acpica-devel,
	linux-kernel
  Cc: linux-tegra, treding, jonathanh, vsethi, ksitaraman, sanjayc,
	bbasu

On 9/1/2025 8:37 AM, Sumit Gupta wrote:
> 
> On 26/08/25 00:10, Mario Limonciello wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> On 8/23/2025 3:01 PM, Sumit Gupta wrote:
>>> Add kernel boot parameter 'cppc_cpufreq.auto_sel_mode' to enable CPPC
>>> autonomous performance selection at system startup. When autonomous mode
>>> is enabled, the hardware automatically adjusts CPU performance based on
>>> workload demands using Energy Performance Preference (EPP) hints from
>>> the OS.
>>>
>>> This parameter allows to configure the autonomous mode on all CPUs
>>> without requiring runtime sysfs manipulation if the 'auto_sel' register
>>> is present.
>>>
>>> When auto_sel_mode=1:
>>> - All CPUs are configured for autonomous operation during driver init
>>> - EPP is set to performance preference (0x0) by default
>>> - Min/max performance bounds use defaults
>>> - CPU frequency scaling is handled by hardware rather than OS
>>>
>>> Also ensure that when autonomous mode is active, the set_target callback
>>> returns early since hardware controls frequency scaling directly.
>>>
>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>> ---
>>>   .../admin-guide/kernel-parameters.txt         |  12 ++
>>>   drivers/cpufreq/cppc_cpufreq.c                | 171 ++++++++++++++++--
>>>   2 files changed, 168 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/ 
>>> Documentation/admin-guide/kernel-parameters.txt
>>> index 86f395f2933b..ea58deb88c36 100644
>>> --- a/Documentation/admin-guide/kernel-parameters.txt
>>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>>> @@ -911,6 +911,18 @@
>>>                       Format:
>>> <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
>>>
>>> +     cppc_cpufreq.auto_sel_mode=
>>> +                     [CPU_FREQ] Autonomous Performance Level Selection.
>>> +                     When Autonomous selection is enabled, then the 
>>> hardware is
>>> +                     allowed to autonomously select the CPU frequency.
>>> +                     In Autonomous mode, Energy Performance 
>>> Preference(EPP)
>>> +                     provides input to the hardware to favour 
>>> performance (0x0)
>>> +                     or energy efficiency (0xff).
>>> +                     Format: <bool>
>>> +                     Default: disabled.
>>> +                     0: force disabled
>>> +                     1: force enabled
>>
>> I don't think you can actually force enable.  If the hardware doesn't
>> support it, setting 1 won't do anything.
>>
>> IoW really setting 1 is "enable if supported".
>>
> 
> Yes, will change in v3.
> 
> 
>>> +
>>>       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 5e1bbb5f67b8..bbf654c56ff9 100644
>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>> @@ -27,6 +27,8 @@
>>>   #include <acpi/cppc_acpi.h>
>>>
>>>   static struct cpufreq_driver cppc_cpufreq_driver;
>>> +/* Autonomous Selection */
>>> +static bool auto_sel_mode;
>>>
>>>   #ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
>>>   static enum {
>>> @@ -272,6 +274,14 @@ static int cppc_cpufreq_set_target(struct 
>>> cpufreq_policy *policy,
>>>       freqs.old = policy->cur;
>>>       freqs.new = target_freq;
>>>
>>> +     /*
>>> +      * In autonomous mode, hardware handles frequency scaling directly
>>> +      * based on workload demands and EPP hints, so OS frequency 
>>> requests
>>> +      * are not needed.
>>> +      */
>>> +     if (cpu_data->perf_caps.auto_sel)
>>> +             return ret;
>>> +
>>>       cpufreq_freq_transition_begin(policy, &freqs);
>>>       ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>>>       cpufreq_freq_transition_end(policy, &freqs, ret != 0);
>>> @@ -555,6 +565,12 @@ static struct cppc_cpudata 
>>> *cppc_cpufreq_get_cpu_data(unsigned int cpu)
>>>               goto free_mask;
>>>       }
>>>
>>> +     ret = cppc_get_perf_ctrls(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:
>>> @@ -642,6 +658,79 @@ static int cppc_cpufreq_set_max_perf(struct 
>>> cpufreq_policy *policy, u64 val,
>>>       return (ret == -EOPNOTSUPP) ? 0 : ret;
>>>   }
>>>
>>> +static int cppc_cpufreq_update_autosel_epp(struct cpufreq_policy 
>>> *policy, int auto_sel, u32 epp)
>>> +{
>>> +     struct cppc_cpudata *cpu_data = policy->driver_data;
>>> +     unsigned int cpu = policy->cpu;
>>> +     int ret;
>>> +
>>> +     pr_debug("cpu%d: curr epp:%u, curr mode:%u, new epp:%u, new 
>>> mode:%d\n", cpu,
>>> +              cpu_data->perf_ctrls.energy_perf, cpu_data- 
>>> >perf_caps.auto_sel, epp, auto_sel);
>>> +
>>> +     mutex_lock(&cppc_cpufreq_update_autosel_config_lock);
>>
>> As I noticed below a case you missed the mutex unlock, this feels like a
>> good candidate for
>>
>> guard(mutex)();
>>
> 
> I will check about 'guard(mutex)()' and change accordingly in v3.
> 
>>> +
>>> +     ret = cppc_set_epp(cpu, epp);
>>> +     if (ret) {
>>> +             pr_warn("failed to set energy_perf for cpu:%d (%d)\n", 
>>> cpu, ret);
>>> +             goto out;
>>> +     }
>>> +     cpu_data->perf_ctrls.energy_perf = epp;
>>> +
>>> +     ret = cppc_set_auto_sel(cpu, auto_sel);
>>> +     if (ret) {
>>> +             pr_warn("failed to set auto_sel for cpu:%d (%d)\n", 
>>> cpu, ret);
>>> +             return ret;
>>
>> Looks like a case that you didn't unlock the mutex.
> 
> ACK.
> 
>>
>>> +     }
>>> +     cpu_data->perf_caps.auto_sel = auto_sel;
>>> +
>>> +out:
>>> + mutex_unlock(&cppc_cpufreq_update_autosel_config_lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int cppc_cpufreq_update_autosel_mperf_ctrls(struct 
>>> cpufreq_policy *policy, u32 min_p,
>>> +                                                u32 max_p, bool 
>>> update_reg, bool update_policy)
>>> +{
>>> +     struct cppc_cpudata *cpu_data = policy->driver_data;
>>> +     unsigned int cpu = policy->cpu;
>>> +     int ret;
>>> +
>>> +     pr_debug("cpu%d: curr max_perf:%u, curr min_perf:%u, new 
>>> max_perf:%u, new min_perf:%u\n",
>>> +              cpu, cpu_data->perf_ctrls.max_perf, cpu_data- 
>>> >perf_ctrls.min_perf, max_p, min_p);
>>> +
>>> +     ret = cppc_cpufreq_set_min_perf(policy, min_p, update_reg, 
>>> update_policy);
>>> +     if (ret) {
>>> +             pr_debug("failed to set min_perf for cpu:%d (%d)\n", 
>>> cpu, ret);
>>> +             return ret;
>>> +     }
>>> +
>>> +     ret = cppc_cpufreq_set_max_perf(policy, max_p, update_reg, 
>>> update_policy);
>>> +     if (ret) {
>>> +             pr_debug("failed to set max_perf for cpu:%d (%d)\n", 
>>> cpu, ret);
>>> +             return ret;
>>> +     }
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static int cppc_cpufreq_update_autosel_configs(struct cpufreq_policy 
>>> *policy, int auto_sel,
>>> +                                            u32 epp, u32 min_perf, 
>>> u32 max_perf,
>>> +                                            bool update_reg, bool 
>>> update_policy)
>>> +{
>>> +     int ret;
>>> +
>>> +     ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, 
>>> max_perf,
>>> +                                                   update_reg, 
>>> update_policy);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     ret = cppc_cpufreq_update_autosel_epp(policy, auto_sel, epp);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>>   static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>>>   {
>>>       unsigned int cpu = policy->cpu;
>>> @@ -710,11 +799,28 @@ 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_ctrls(cpu, &cpu_data->perf_ctrls);
>>> -     if (ret) {
>>> -             pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
>>> -                      caps->highest_perf, cpu, ret);
>>> -             goto out;
>>> +     if (cpu_data->perf_caps.auto_sel) {
>>> +             ret = cppc_set_enable(cpu, true);
>>> +             if (ret) {
>>> +                     pr_err("Failed to enable CPPC on cpu%d (%d)\n", 
>>> cpu, ret);
>>> +                     goto out;
>>> +             }
>>> +
>>> +             ret = cppc_cpufreq_update_autosel_configs(policy, true,
>>> + CPPC_EPP_PERFORMANCE_PREF,
>>> + caps->lowest_nonlinear_perf,
>>> + caps->nominal_perf, true, false);
>>> +             if (ret) {
>>> +                     pr_debug("Failed to update autosel configs on 
>>> CPU%d(%d)\n", cpu, ret);
>>> +                     goto out;
>>> +             }
>>> +     } else {
>>> +             ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>>> +             if (ret) {
>>> +                     pr_debug("Err setting perf value:%d on CPU:%d. 
>>> ret:%d\n",
>>> +                              caps->highest_perf, cpu, ret);
>>> +                     goto out;
>>> +             }
>>>       }
>>>
>>>       cppc_cpufreq_cpu_fie_init(policy);
>>> @@ -736,6 +842,13 @@ static void cppc_cpufreq_cpu_exit(struct 
>>> cpufreq_policy *policy)
>>>
>>>       cpu_data->perf_ctrls.desired_perf = caps->lowest_perf;
>>>
>>> +     if (cpu_data->perf_caps.auto_sel) {
>>> +             ret = cppc_cpufreq_update_autosel_epp(policy, false,
>>> + CPPC_EPP_ENERGY_EFFICIENCY_PREF);
>>> +             if (ret)
>>> +                     return;
>>> +     }
>>> +
>>>       ret = cppc_set_perf_ctrls(cpu, &cpu_data->perf_ctrls);
>>>       if (ret)
>>>               pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
>>> @@ -920,17 +1033,10 @@ static ssize_t store_auto_select(struct 
>>> cpufreq_policy *policy,
>>>        * On enabling auto_select: set min/max_perf register and 
>>> update policy.
>>>        * On disabling auto_select: update only policy.
>>>        */
>>> -     ret = cppc_cpufreq_set_min_perf(policy, min_perf, update_reg, 
>>> true);
>>> -     if (ret) {
>>> -             pr_warn("failed to %s update min policy for cpu:%d 
>>> (%d)\n",
>>> -                     val > 0 ? "set min_perf and" : "", cpu, ret);
>>> -             return ret;
>>> -     }
>>> -
>>> -     ret = cppc_cpufreq_set_max_perf(policy, max_perf, update_reg, 
>>> true);
>>> +     ret = cppc_cpufreq_update_autosel_mperf_ctrls(policy, min_perf, 
>>> max_perf, update_reg, true);
>>>       if (ret) {
>>> -             pr_warn("failed to %s update max policy for cpu:%d 
>>> (%d)\n",
>>> -                     val > 0 ? "set max_perf and" : "", cpu, ret);
>>> +             pr_warn("failed to %s update policy for cpu:%d (%d)\n",
>>> +                     val > 0 ? "set min/max_perf and" : "", cpu, ret);
>>>               return ret;
>>>       }
>>>
>>> @@ -1139,13 +1245,44 @@ static struct cpufreq_driver 
>>> cppc_cpufreq_driver = {
>>>       .name = "cppc_cpufreq",
>>>   };
>>>
>>> +static void cppc_cpufreq_set_epp_autosel_allcpus(bool auto_sel, u64 
>>> epp)
>>> +{
>>> +     int cpu, ret;
>>> +
>>> +     for_each_present_cpu(cpu) {
>>> +             ret = cppc_set_epp(cpu, epp);
>>> +             if (ret)
>>> +                     pr_debug("failed to set energy_perf for cpu:%d 
>>> (%d)\n", cpu, ret);
>>> +
>>> +             ret = cppc_set_auto_sel(cpu, auto_sel);
>>> +             if (ret)
>>> +                     pr_debug("failed to set auto_sel for cpu:%d 
>>> (%d)\n", cpu, ret);
>>> +     }
>>> +}
>>> +
>>>   static int __init cppc_cpufreq_init(void)
>>>   {
>>> +     bool auto_sel;
>>>       int ret;
>>>
>>>       if (!acpi_cpc_valid())
>>>               return -ENODEV;
>>>
>>> +     if (auto_sel_mode) {
>>> +             /*
>>> +              * Check if autonomous selection is supported by 
>>> testing CPU 0.
>>> +              * If supported, enable autonomous mode on all CPUs.
>>> +              */
>>> +             ret = cppc_get_auto_sel(0, &auto_sel);
>>> +             if (!ret) {
>>> +                     pr_info("Enabling autonomous mode on all CPUs\n");
>>> +                     cppc_cpufreq_set_epp_autosel_allcpus(true, 
>>> CPPC_EPP_PERFORMANCE_PREF);
>>> +             } else {
>>> +                     pr_warn("Autonomous selection not supported, 
>>> disabling auto_sel_mode\n");
>>> +                     auto_sel_mode = false;
>>> +             }
>>> +     }
>>> +
>>>       cppc_freq_invariance_init();
>>>       populate_efficiency_class();
>>>
>>> @@ -1160,8 +1297,12 @@ static void __exit cppc_cpufreq_exit(void)
>>>   {
>>>       cpufreq_unregister_driver(&cppc_cpufreq_driver);
>>>       cppc_freq_invariance_exit();
>>> +     auto_sel_mode = 0;
>>>   }
>>>
>>> +module_param(auto_sel_mode, bool, 0000);
>>> +MODULE_PARM_DESC(auto_sel_mode, "Enable Autonomous Performance Level 
>>> Selection");
>>
>> Why default to disabled?  As a precaution?  We enable EPP by default in
>> the *-pstate drivers if the hardware supports it, I would think it makes
>> sense here too.
>>
> 
> Kept disabled by default both as precaution and to enable it based on 
> preference.
> Someone may want to try different config values during bringup, 
> verification etc.

I personally don't think that bringup software should dictate the steady 
state decision.  Bringup can trivially have a custom kernel command line 
that disables it.

Otherwise this means that the "worries of stability" translate into a 
custom kernel command line option on all production hardware.

So I feel once you're happy with it on a variety of hardware you should 
consider a patch for enabling it by default at some point.




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

end of thread, other threads:[~2025-09-02 19:48 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-23 20:01 [PATCH v2 0/7] Enhanced autonomous selection and API Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 1/7] ACPI: CPPC: add perf control read API and clarify naming Sumit Gupta
2025-08-25 18:33   ` Rafael J. Wysocki
2025-09-01 13:46     ` Sumit Gupta
2025-09-01 19:00       ` Rafael J. Wysocki
2025-08-25 23:41   ` kernel test robot
2025-08-23 20:01 ` [PATCH v2 2/7] ACPI: CPPC: extend APIs to support auto_sel and epp Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 3/7] ACPI: CPPC: add APIs and sysfs interface for min/max_perf Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 4/7] ACPI: CPPC: add APIs and sysfs interface for perf_limited register Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 5/7] cpufreq: CPPC: update policy min/max when toggling auto_select Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 6/7] cpufreq: CPPC: Add sysfs for min/max_perf and perf_limited Sumit Gupta
2025-08-24  0:08   ` Randy Dunlap
2025-09-01 13:12     ` Sumit Gupta
2025-08-23 20:01 ` [PATCH v2 7/7] cpufreq: CPPC: add autonomous mode boot parameter support Sumit Gupta
2025-08-24  0:08   ` Randy Dunlap
2025-09-01 13:18     ` Sumit Gupta
2025-08-25 18:40   ` Mario Limonciello
2025-09-01 13:37     ` Sumit Gupta
2025-09-02 19:48       ` Mario Limonciello

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).