Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework
@ 2026-07-23 20:12 K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 1/9] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar,
	Jonathan Corbet, Shuah Khan, Kalpana Shetty
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

This is essentially a combined v2 of the following series:

  https://lore.kernel.org/lkml/20260715074822.16324-1-kprateek.nayak@amd.com/
  https://lore.kernel.org/lkml/20260630185904.5602-1-kprateek.nayak@amd.com/

The first two patches fix BIOS min perf setting and its persistence
across kexec. The next set of patches rework the Dynamic EPP feature
into an energy_performance_preference mode.

Unit tests are extended to cover the new dynamic_epp mode and also the
floor perf (CPPC Performance Priority) feature for completeness.

Patches are based on:

  git.kernel.org/pub/scm/linux/kernel/git/superm1/linux.git bleeding-edge

at commit 39c0cf62fc78 ("cpufreq/amd-pstate: handle missing policy in
dynamic EPP callbacks")

Everyone has been Cc'd on the cover letter. Respective maintainers have
been Cc'd on the relevant patches to reduce noise. Lists get the full
series.

---
Changelog rfc v1..v2:

o Simplified bios_min_perf handover during kexec by removing the
  defensive check (Mario).

o Removed the amd_dynamic_epp kernel cmdline and the dynamic_epp sysfs
  file as they are now redundant.

o Updated the documentation.

o Updated unit test to account for dynamic_epp removal.

o Added unit test for floor frequency. (Kalpana)

Individual patches have breief changelogs attached. Reviews and testing
is highly appreciated.
-- 
K Prateek Nayak (9):
  cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
  cpufreq/amd-pstate: Remove the defensive check for bios_min_perf
  cpufreq/amd-pstate: Extract platform profile to EPP conversion into a
    helper
  cpufreq/amd-pstate: Add dynamic EPP as an
    "energy_performance_preference" mode
  cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp"
    sysfs
  Documentation/amd-pstate: Update dynamic_epp documentation with new
    behavior
  cpufreq/amd-pstate: Reduce the scope of exported symbols
  cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode
  cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority

 Documentation/admin-guide/pm/amd-pstate.rst |  46 ++--
 drivers/cpufreq/amd-pstate-ut.c             | 128 +++++++++--
 drivers/cpufreq/amd-pstate.c                | 240 ++++++++++----------
 drivers/cpufreq/amd-pstate.h                |   9 +
 4 files changed, 253 insertions(+), 170 deletions(-)


base-commit: 39c0cf62fc7851a17782e7efe8dfb2948739c681
-- 
2.34.1


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

* [PATCH v2 1/9] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf K Prateek Nayak
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

amd_pstate_update_min_max_limit() sets the min_limit_perf to the
nominal_perf to avoid frequency throttling when the system is idling.

This was found to be an ideal default but is suboptimal for users who
have profiled their workload at different operating frequencies and have
configured the optimal idling frequency via bios_min_perf.

Use the bios_min_perf (if configured) as the min_limit_perf when running
with performance governor. In absence of bios_min_perf, continue using
nominal_perf as the default min_limit_perf to avoid throttling.

Fixes: 608a76b65288 ("cpufreq/amd-pstate: Add support for the "Requested CPU Min frequency" BIOS option")
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Picked up tag from Mario. (Thanks a ton!)
---
 drivers/cpufreq/amd-pstate.c | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index b357189f4f8e..1157f05df10f 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -699,9 +699,12 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
 	WRITE_ONCE(cpudata->max_limit_freq, policy->max);
 
 	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
+		u8 min_limit_perf = perf.bios_min_perf ?: perf.nominal_perf;
+		u32 min_limit_freq;
+
 		/*
-		 * For performance policy, set MinPerf to nominal_perf rather than
-		 * highest_perf or lowest_nonlinear_perf.
+		 * For performance policy, set MinPerf to nominal_perf / bios_min_perf
+		 * rather than highest_perf or lowest_nonlinear_perf.
 		 *
 		 * Per commit 0c411b39e4f4c, using highest_perf was observed
 		 * to cause frequency throttling on power-limited platforms, leading to
@@ -709,11 +712,17 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
 		 * performance too much for HPC workloads requiring high frequency
 		 * operation and minimal wakeup latency from idle states.
 		 *
-		 * nominal_perf therefore provides a balance by avoiding throttling
-		 * while still maintaining enough performance for HPC workloads.
+		 * nominal_perf therefore provides a balanced default by avoiding
+		 * throttling while still maintaining enough performance for HPC
+		 * workloads when bios_min_perf is not available.
+		 *
+		 * When bios_min_perf is available, users have profiled their workloads
+		 * to understand the best idling frequency. Use that instead.
 		 */
-		perf.min_limit_perf = min(perf.nominal_perf, perf.max_limit_perf);
-		WRITE_ONCE(cpudata->min_limit_freq, min(cpudata->nominal_freq, cpudata->max_limit_freq));
+		min_limit_freq = perf_to_freq(perf, cpudata->nominal_freq, min_limit_perf);
+		perf.min_limit_perf = min_limit_perf;
+
+		WRITE_ONCE(cpudata->min_limit_freq, min(min_limit_freq, cpudata->max_limit_freq));
 	} else {
 		perf.min_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->min);
 		WRITE_ONCE(cpudata->min_limit_freq, policy->min);
-- 
2.34.1


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

* [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 1/9] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 21:17   ` Mario Limonciello
  2026-07-23 20:12 ` [PATCH v2 3/9] cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper K Prateek Nayak
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Initialization of bios_min_perf (BIOS Requested CPU Min Freq.) only
succeeds when the driver init finds the CPPC_REQ MSRs to have all 0s
except for MIN_PERF bits.

A kexec puts the driver through the suspend path which, although resets
the min_perf back to bios_min_perf, keeps the rest of the CPPR_REQ
intact with the last value at the time of suspend.

The defensive check for bios_min_perf exists to prevent the min perf
from last CPPC_REQ being incorrectly considered as bios_min_perf when
a kexec switches from an older kernel running the version of driver
which is not aware of bios_min_perf to a newer one.

This scenario is extremely unlikely and Mario suggested it is better to
simplify the initialization rather than complicating the suspend resume
paths.

Drop the defensive check for bios_min_perf initialization and add a
debug message to dump the BIOS Requested Min Freq. to console leaving
enough breadcrumbs for debug if a situation so arises.

Suggested-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Reworked based on review comments from Mario.
---
 drivers/cpufreq/amd-pstate.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 1157f05df10f..02308e95636e 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -462,7 +462,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 {
 	union perf_cached perf = READ_ONCE(cpudata->perf);
 	u64 cap1, numerator, cppc_req;
-	u8 min_perf;
 
 	int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
 				     &cap1);
@@ -478,16 +477,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 		return ret;
 
 	WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
-	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
-
-	/*
-	 * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
-	 * indication that the min_perf value is the one specified through the BIOS option
-	 */
-	cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
-
-	if (!cppc_req)
-		perf.bios_min_perf = min_perf;
 
 	perf.highest_perf = numerator;
 	perf.max_limit_perf = numerator;
@@ -495,6 +484,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 	perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
 	perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
 	perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
+	perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
 	WRITE_ONCE(cpudata->perf, perf);
 	WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
 	WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
@@ -1043,6 +1033,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
 		return -EINVAL;
 	}
 
+	if (perf.bios_min_perf) {
+		u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf);
+
+		pr_debug("Found Requested CPU Min Frequency of %uMHz on CPU%d\n",
+			 bios_min_freq, cpudata->cpu);
+	}
+
 	return 0;
 }
 
-- 
2.34.1


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

* [PATCH v2 3/9] cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 1/9] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 4/9] cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode K Prateek Nayak
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Avoid duplication by extracting the switch case that derives EPP based
on platform profile into the amd_pstate_get_epp_from_platform_profile()
helper.

No functional changes intended.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Picked up tag from Mario. (Thanks a ton!)
---
 drivers/cpufreq/amd-pstate.c | 67 +++++++++++++++++-------------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 02308e95636e..7d1feed0e11e 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1188,6 +1188,24 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
 	return NOTIFY_OK;
 }
 
+static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy,
+						    enum platform_profile_option profile)
+{
+	switch (profile) {
+	case PLATFORM_PROFILE_PERFORMANCE:
+		return AMD_CPPC_EPP_PERFORMANCE;
+	case PLATFORM_PROFILE_BALANCED:
+		return amd_pstate_get_balanced_epp(policy);
+	case PLATFORM_PROFILE_LOW_POWER:
+		return AMD_CPPC_EPP_POWERSAVE;
+	default:
+		break;
+	}
+
+	pr_err("Unknown Platform Profile %d\n", profile);
+	return -EOPNOTSUPP;
+}
+
 static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices)
 {
 	set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
@@ -1213,31 +1231,19 @@ static int amd_pstate_profile_set(struct device *dev,
 	struct amd_cpudata *cpudata = dev_get_drvdata(dev);
 	struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
 	int ret;
+	u8 epp;
 
 	if (!policy)
 		return -ENODEV;
 
-	switch (profile) {
-	case PLATFORM_PROFILE_LOW_POWER:
-		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE);
-		if (ret)
-			return ret;
-		break;
-	case PLATFORM_PROFILE_BALANCED:
-		ret = amd_pstate_set_epp(policy,
-					 amd_pstate_get_balanced_epp(policy));
-		if (ret)
-			return ret;
-		break;
-	case PLATFORM_PROFILE_PERFORMANCE:
-		ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE);
-		if (ret)
-			return ret;
-		break;
-	default:
-		pr_err("Unknown Platform Profile %d\n", profile);
-		return -EOPNOTSUPP;
-	}
+	ret = amd_pstate_get_epp_from_platform_profile(policy, profile);
+	if (ret < 0)
+		return ret;
+
+	epp = (u8)ret;
+	ret = amd_pstate_set_epp(policy, epp);
+	if (ret)
+		return ret;
 
 	cpudata->current_profile = profile;
 
@@ -1271,20 +1277,11 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 	int ret;
 	u8 epp;
 
-	switch (cpudata->current_profile) {
-	case PLATFORM_PROFILE_PERFORMANCE:
-		epp = AMD_CPPC_EPP_PERFORMANCE;
-		break;
-	case PLATFORM_PROFILE_LOW_POWER:
-		epp = AMD_CPPC_EPP_POWERSAVE;
-		break;
-	case PLATFORM_PROFILE_BALANCED:
-		epp = amd_pstate_get_balanced_epp(policy);
-		break;
-	default:
-		pr_err("Unknown Platform Profile %d\n", cpudata->current_profile);
-		return -EOPNOTSUPP;
-	}
+	ret = amd_pstate_get_epp_from_platform_profile(policy, cpudata->current_profile);
+	if (ret < 0)
+		return ret;
+
+	epp = (u8)ret;
 	ret = amd_pstate_set_epp(policy, epp);
 	if (ret)
 		return ret;
-- 
2.34.1


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

* [PATCH v2 4/9] cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (2 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 3/9] cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 5/9] cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs K Prateek Nayak
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Conver the global "dynamic_epp" toggle into a per-CPU
"energy_performance_preference" mode "dynamic" that allows toggling the
functionality of "dynamic_epp" at a per-CPU level.

Instead of being a system-wide toggle, users can opt into the
functionality of dynamic EPP on a per-CPU basis by switching to the
powersave governor and selecting the "dynamic" mode from the available
performance preferences.

Unlike the previous implementation that had to check for driver mode
before toggling on the functionality, block writes to certain sysfs
files, potentially disallow policy change, etc. the per-CPU toggle fits
naturally into the intended design and provides more granular control to
the user.

The dynamic_epp file is now redundant as the option to toggle it on is
controlled via energy_performance_preference, and the dynamic_epp file
will be removed in the subsequent commit.

Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Removed "dynamic_epp" global control related changes since it will be
  removed in the subsequent commit.
---
 drivers/cpufreq/amd-pstate.c | 57 +++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 8 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 7d1feed0e11e..fc90e4108c22 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -106,6 +106,7 @@ static struct quirk_entry *quirks;
  *	3		balance_power
  *	4		power
  *	5		custom (for raw EPP values)
+ *	6		dynamic (platform profile driven selection)
  */
 enum energy_perf_value_index {
 	EPP_INDEX_DEFAULT = 0,
@@ -114,6 +115,7 @@ enum energy_perf_value_index {
 	EPP_INDEX_BALANCE_POWERSAVE,
 	EPP_INDEX_POWERSAVE,
 	EPP_INDEX_CUSTOM,
+	EPP_INDEX_DYNAMIC,
 	EPP_INDEX_MAX,
 };
 
@@ -124,6 +126,7 @@ static const char * const energy_perf_strings[] = {
 	[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
 	[EPP_INDEX_POWERSAVE] = "power",
 	[EPP_INDEX_CUSTOM] = "custom",
+	[EPP_INDEX_DYNAMIC] = "dynamic",
 };
 static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
 
@@ -134,7 +137,7 @@ static unsigned int epp_values[] = {
 	[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
 	[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
 };
-static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1);
+static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2);
 
 typedef int (*cppc_mode_transition_fn)(int);
 
@@ -1274,6 +1277,7 @@ EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp);
 static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
+	u64 prev = READ_ONCE(cpudata->cppc_req_cached);
 	int ret;
 	u8 epp;
 
@@ -1314,6 +1318,9 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 cleanup:
 	amd_pstate_clear_dynamic_epp(policy);
 
+	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, prev);
+	/* Restore previous EPP if toggling Dynamic EPP failed. */
+	amd_pstate_set_epp(policy, epp);
 	return ret;
 }
 
@@ -1384,7 +1391,7 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy,
 static ssize_t show_energy_performance_available_preferences(
 				struct cpufreq_policy *policy, char *buf)
 {
-	int offset = 0, i;
+	int i, offset = 0;
 	struct amd_cpudata *cpudata = policy->driver_data;
 
 	if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
@@ -1407,11 +1414,6 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
 	bool raw_epp = false;
 	u8 epp;
 
-	if (cpudata->dynamic_epp) {
-		pr_debug("EPP cannot be set when dynamic EPP is enabled\n");
-		return -EBUSY;
-	}
-
 	/*
 	 * if the value matches a number, use that, otherwise see if
 	 * matches an index in the energy_perf_strings array
@@ -1422,6 +1424,23 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
 		ret = sysfs_match_string(energy_perf_strings, buf);
 		if (ret < 0 || ret == EPP_INDEX_CUSTOM)
 			return -EINVAL;
+
+		if (ret == EPP_INDEX_DYNAMIC) {
+			/*
+			 * Dynamic EPP was already enabled for this CPU.
+			 * Nothing to do.
+			 */
+			if (cpudata->dynamic_epp)
+				return count;
+
+			cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
+			ret = amd_pstate_set_dynamic_epp(policy);
+			if (ret)
+				return ret;
+
+			return count;
+		}
+
 		if (ret)
 			epp = epp_values[ret];
 		else
@@ -1433,6 +1452,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
 		return -EBUSY;
 	}
 
+	/*
+	 * Dynamic EPP was enabled previously!
+	 * Switch back to the static EPP mode.
+	 */
+	if (cpudata->dynamic_epp)
+		amd_pstate_clear_dynamic_epp(policy);
+
 	ret = amd_pstate_set_epp(policy, epp);
 	if (ret)
 		return ret;
@@ -1450,7 +1476,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
 
 	epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
 
-	if (cpudata->raw_epp)
+	if (!cpudata->dynamic_epp && cpudata->raw_epp)
 		return sysfs_emit(buf, "%u\n", epp);
 
 	switch (epp) {
@@ -1470,6 +1496,9 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
 		return -EINVAL;
 	}
 
+	if (cpudata->dynamic_epp)
+		return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]);
+
 	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
 }
 EXPORT_SYMBOL_GPL(show_energy_performance_preference);
@@ -2030,6 +2059,18 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)
 	if (!policy->cpuinfo.max_freq)
 		return -ENODEV;
 
+	/* Must be a switch between PERFORMANCE and POWERSAVE */
+	if (cpudata->policy != policy->policy) {
+		/*
+		 * Disable dynamic_epp when switching
+		 * out of CPUFREQ_POLICY_POWERSAVE.
+		 */
+		if (cpudata->dynamic_epp) {
+			WARN_ON_ONCE(cpudata->policy != CPUFREQ_POLICY_POWERSAVE);
+			amd_pstate_clear_dynamic_epp(policy);
+		}
+	}
+
 	cpudata->policy = policy->policy;
 
 	ret = amd_pstate_epp_update_limit(policy, true);
-- 
2.34.1


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

* [PATCH v2 5/9] cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (3 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 4/9] cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 6/9] Documentation/amd-pstate: Update dynamic_epp documentation with new behavior K Prateek Nayak
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Since dynamic_epp has been converted to an
"energy_performance_preference", toggling the feature via the sysfs file
or the kernel cmdline is now redundant.

Remove the sysfs file and the "amd_dynamic_epp" cmdline and only depend
on "energy_performance_preference" to toggle dynamic_epp.

Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Removed "dynamic_epp" gloabal control, the "amd_synamic_epp" cmdline,
  and the "fdynamic_epp" sysfs since they are all redundant after the
  rework.
---
 drivers/cpufreq/amd-pstate.c | 55 +-----------------------------------
 1 file changed, 1 insertion(+), 54 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index fc90e4108c22..92da8937df04 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -87,7 +87,6 @@ static struct cpufreq_driver amd_pstate_driver;
 static struct cpufreq_driver amd_pstate_epp_driver;
 static int cppc_state = AMD_PSTATE_UNDEFINED;
 static bool amd_pstate_prefcore = true;
-static bool dynamic_epp;
 static struct quirk_entry *quirks;
 
 /*
@@ -1834,50 +1833,12 @@ static ssize_t prefcore_show(struct device *dev,
 	return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
 }
 
-static ssize_t dynamic_epp_show(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp));
-}
-
-static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b,
-				 const char *buf, size_t count)
-{
-	bool enabled;
-	int ret;
-
-	ret = kstrtobool(buf, &enabled);
-	if (ret)
-		return ret;
-
-	guard(mutex)(&amd_pstate_driver_lock);
-
-	if (cppc_state != AMD_PSTATE_ACTIVE) {
-		pr_debug("dynamic_epp can only be toggled in active mode\n");
-		return -EINVAL;
-	}
-
-	/* Nothing to do */
-	if (dynamic_epp == enabled)
-		return count;
-
-	/* reinitialize with desired dynamic EPP value */
-	dynamic_epp = enabled;
-	ret = amd_pstate_change_driver_mode(cppc_state);
-	if (ret)
-		dynamic_epp = false;
-
-	return ret ? ret : count;
-}
-
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(prefcore);
-static DEVICE_ATTR_RW(dynamic_epp);
 
 static struct attribute *pstate_global_attributes[] = {
 	&dev_attr_status.attr,
 	&dev_attr_prefcore.attr,
-	&dev_attr_dynamic_epp.attr,
 	NULL
 };
 
@@ -1984,10 +1945,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 		cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
 	}
 
-	if (dynamic_epp)
-		ret = amd_pstate_set_dynamic_epp(policy);
-	else
-		ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc);
+	ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc);
 	if (ret)
 		goto free_cpudata1;
 
@@ -2398,19 +2356,8 @@ static int __init amd_prefcore_param(char *str)
 	return 0;
 }
 
-static int __init amd_dynamic_epp_param(char *str)
-{
-	if (!strcmp(str, "disable"))
-		dynamic_epp = false;
-	if (!strcmp(str, "enable"))
-		dynamic_epp = true;
-
-	return 0;
-}
-
 early_param("amd_pstate", amd_pstate_param);
 early_param("amd_prefcore", amd_prefcore_param);
-early_param("amd_dynamic_epp", amd_dynamic_epp_param);
 
 MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
 MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");
-- 
2.34.1


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

* [PATCH v2 6/9] Documentation/amd-pstate: Update dynamic_epp documentation with new behavior
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (4 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 5/9] cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 7/9] cpufreq/amd-pstate: Reduce the scope of exported symbols K Prateek Nayak
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar,
	Jonathan Corbet, Shuah Khan
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Update the admin-guide for dynamic_epp describing the latest integration
into energy_performance_preference selections.

Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Updated documentation based on current state of rework.
---
 Documentation/admin-guide/pm/amd-pstate.rst | 46 ++++++++++-----------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst
index a95e2ebce005..b0f1626bd783 100644
--- a/Documentation/admin-guide/pm/amd-pstate.rst
+++ b/Documentation/admin-guide/pm/amd-pstate.rst
@@ -317,7 +317,10 @@ These profiles represent different hints that are provided
 to the low-level firmware about the user's desired energy vs efficiency
 tradeoff.  ``default`` represents the epp value is set by platform
 firmware. ``custom`` designates that integer values 0-255 may be written
-as well.  This attribute is read-only.
+as well. ``dynamic`` designates that the EPP is modified dynamically
+by the platform profile and the power supply status. See ``Dynamic energy
+performance profile`` section below to know more about the ``dynamic``
+mode. This attribute is read-only.
 
 ``energy_performance_preference``
 
@@ -326,13 +329,11 @@ and user can change current preference according to energy or performance needs
 Coarse named profiles are available in the attribute
 ``energy_performance_available_preferences``.
 Users can also write individual integer values between 0 to 255.
-When dynamic EPP is enabled, writes to energy_performance_preference are blocked
-even when EPP feature is enabled by platform firmware. Lower epp values shift the bias
-towards improved performance while a higher epp value shifts the bias towards
-power-savings. The exact impact can change from one platform to the other.
-If a valid integer was last written, then a number will be returned on future reads.
-If a valid string was last written then a string will be returned on future reads.
-This attribute is read-write.
+Lower epp values shift the bias towards improved performance while a higher epp
+value shifts the bias towards power-savings. The exact impact can change from
+one platform to the other. If a valid integer was last written, then a number
+will be returned on future reads. If a valid string was last written then a
+string will be returned on future reads. This attribute is read-write.
 
 ``boost``
 The `boost` sysfs attribute provides control over the CPU core
@@ -356,21 +357,20 @@ Other performance and frequency values can be read back from
 Dynamic energy performance profile
 ==================================
 The amd-pstate driver supports dynamically selecting the energy performance
-profile based on whether the machine is running on AC or DC power.
-
-Whether this behavior is enabled by default depends on the kernel command line option
-``amd_dynamic_epp`` is set. This behavior can also be overridden
-at runtime by the sysfs file ``/sys/devices/system/cpu/amd_pstate/dynamic_epp``.
-
-When set to enabled, the driver will select a different energy performance
-profile when the machine is running on battery or AC power. The driver will
-also register with the platform profile handler to receive notifications of
-user desired power state and react to those.
-When set to disabled, the driver will not change the energy performance profile
-based on the power source and will not react to user desired power state.
-
-Attempting to manually write to the ``energy_performance_preference`` sysfs
-file will fail when ``dynamic_epp`` is enabled.
+profile based on the system profile and the current power source in active mode.
+
+The ``dynamic`` mode is listed in
+``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_available_preferences``
+when available while running under the ``powersave`` governor. The ``dynamic``
+mode can be toggled on by writing the same to the sysfs file
+``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_preference`` when
+available.
+
+When ``energy_performance_preference`` is set to ``dynamic``, the driver will
+select a different energy performance profile when the machine is running on
+battery or AC power. The driver will also register with the platform profile
+handler to receive notifications of user desired power state and react to
+those.
 
 ``amd-pstate`` vs ``acpi-cpufreq``
 ======================================
-- 
2.34.1


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

* [PATCH v2 7/9] cpufreq/amd-pstate: Reduce the scope of exported symbols
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (5 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 6/9] Documentation/amd-pstate: Update dynamic_epp documentation with new behavior K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 8/9] cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 9/9] cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority K Prateek Nayak
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Symbols exported by amd-pstate.c are ever only needed for amd-pstate-ut.
Introduce EXPORT_SYMBOL_FOR_PSTATE_UT() to export these symbols
selectively to "amd-pstate-ut" namespace as opposed to all GPL modules.

No functional changes intended.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Collected tag from Mario. (Thanks a ton!)
---
 drivers/cpufreq/amd-pstate.c | 14 +++++++-------
 drivers/cpufreq/amd-pstate.h |  7 +++++++
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 92da8937df04..b875e92103e1 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -75,7 +75,7 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode)
 		mode = AMD_PSTATE_UNDEFINED;
 	return amd_pstate_mode_string[mode];
 }
-EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string);
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_mode_string);
 
 struct quirk_entry {
 	u32 nominal_freq;
@@ -1271,7 +1271,7 @@ void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
 	kfree(cpudata->profile_name);
 	cpudata->dynamic_epp = false;
 }
-EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp);
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_clear_dynamic_epp);
 
 static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
 {
@@ -1466,7 +1466,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
 
 	return count;
 }
-EXPORT_SYMBOL_GPL(store_energy_performance_preference);
+EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference);
 
 ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
 {
@@ -1500,7 +1500,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
 
 	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
 }
-EXPORT_SYMBOL_GPL(show_energy_performance_preference);
+EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
 
 static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
 					   const char *buf, size_t count)
@@ -1600,7 +1600,7 @@ struct freq_attr **amd_pstate_get_current_attrs(void)
 		return NULL;
 	return current_pstate_driver->attr;
 }
-EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs);
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_current_attrs);
 
 static struct freq_attr **get_freq_attrs(void)
 {
@@ -1785,7 +1785,7 @@ int amd_pstate_get_status(void)
 {
 	return cppc_state;
 }
-EXPORT_SYMBOL_GPL(amd_pstate_get_status);
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
 
 int amd_pstate_update_status(const char *buf, size_t size)
 {
@@ -1805,7 +1805,7 @@ int amd_pstate_update_status(const char *buf, size_t size)
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(amd_pstate_update_status);
+EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status);
 
 static ssize_t status_show(struct device *dev,
 			   struct device_attribute *attr, char *buf)
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index 23e8baa05849..edd697a5e29f 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -11,6 +11,13 @@
 #include <linux/pm_qos.h>
 #include <linux/platform_profile.h>
 
+#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT)
+#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \
+	EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut")
+#else
+#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol)
+#endif
+
 /*********************************************************************
  *                        AMD P-state INTERFACE                       *
  *********************************************************************/
-- 
2.34.1


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

* [PATCH v2 8/9] cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (6 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 7/9] cpufreq/amd-pstate: Reduce the scope of exported symbols K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  2026-07-23 20:12 ` [PATCH v2 9/9] cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority K Prateek Nayak
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Extend the EPP unit test to cover the "dynamic" epp mode. Since
"dynamic_epp" is no longer a system-wide toggle, remove the legacy
"dynamic_epp" bits from the unit test.

Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
Changelog rfc v1..v2:

o Simplified some aspect of test now that dynamic_epp global knob is
  removed.
---
 drivers/cpufreq/amd-pstate-ut.c | 45 ++++++++++++++++++---------------
 1 file changed, 24 insertions(+), 21 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
index 2142838ad6cc..d1f730e3502b 100644
--- a/drivers/cpufreq/amd-pstate-ut.c
+++ b/drivers/cpufreq/amd-pstate-ut.c
@@ -275,6 +275,7 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
 static int amd_pstate_ut_epp(u32 index)
 {
 	static const char * const epp_strings[] = {
+		"dynamic",
 		"power",
 		"balance_power",
 		"balance_performance",
@@ -282,10 +283,10 @@ static int amd_pstate_ut_epp(u32 index)
 	};
 	char *buf __free(cleanup_page) = NULL;
 	struct cpufreq_policy *policy = NULL;
+	unsigned long orig_dynamic_epp = 0;
 	enum amd_pstate_mode orig_mode;
 	struct amd_cpudata *cpudata;
 	unsigned long orig_policy;
-	bool orig_dynamic_epp;
 	int ret, cpu = 0;
 	u16 epp;
 	int i;
@@ -294,9 +295,11 @@ static int amd_pstate_ut_epp(u32 index)
 	if (!policy)
 		return -ENODEV;
 
-	cpudata = policy->driver_data;
 	orig_mode = amd_pstate_get_status();
-	orig_dynamic_epp = cpudata->dynamic_epp;
+	if (policy->driver_data) {
+		cpudata = policy->driver_data;
+		orig_dynamic_epp = cpudata->dynamic_epp;
+	}
 
 	/* Drop reference before potential driver change. */
 	cpufreq_cpu_put(policy);
@@ -321,16 +324,6 @@ static int amd_pstate_ut_epp(u32 index)
 	orig_policy = cpudata->policy;
 	cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
 
-	/*
-	 * Disable dynamic EPP before running test. If "orig_dynamic_epp" is
-	 * true, the  driver will do a redundant switch at the end and there
-	 * is no need for enabling it again at the end of the test.
-	 */
-	if (cpudata->dynamic_epp) {
-		pr_debug("Dynamic EPP is enabled, disabling it\n");
-		amd_pstate_clear_dynamic_epp(policy);
-	}
-
 	for (epp = 0; epp <= U8_MAX; epp++) {
 		u8 val;
 
@@ -367,6 +360,11 @@ static int amd_pstate_ut_epp(u32 index)
 		if (ret < 0)
 			goto out;
 		strreplace(buf, '\n', '\0');
+		/*
+		 * "dynamic" mode reports the EPP as "dynamic(profile:X)"
+		 * Trim at "(" and just compare tie the epp string.
+		 */
+		strreplace(buf, '(', '\0');
 
 		if (strcmp(buf, epp_strings[i])) {
 			pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
@@ -380,18 +378,23 @@ static int amd_pstate_ut_epp(u32 index)
 out:
 	if (policy) {
 		cpudata->policy = orig_policy;
+		/*
+		 * If the driver had enabled dynamic_epp to brgin with,
+		 * restore it here before dropping policy reference.
+		 */
+		if (orig_dynamic_epp) {
+			int ret2;
+
+			ret2 = store_energy_performance_preference(policy,
+								  epp_strings[0],
+								  strlen(epp_strings[0]));
+			if (!ret && (ret2 < 0))
+				ret = ret2;
+		}
 		up_write(&policy->rwsem);
 		cpufreq_cpu_put(policy);
 	}
 
-	if (orig_dynamic_epp) {
-		int ret2;
-
-		ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE);
-		if (!ret && ret2)
-			ret = ret2;
-	}
-
 	if (orig_mode != amd_pstate_get_status()) {
 		int ret2;
 
-- 
2.34.1


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

* [PATCH v2 9/9] cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority
  2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
                   ` (7 preceding siblings ...)
  2026-07-23 20:12 ` [PATCH v2 8/9] cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode K Prateek Nayak
@ 2026-07-23 20:12 ` K Prateek Nayak
  8 siblings, 0 replies; 11+ messages in thread
From: K Prateek Nayak @ 2026-07-23 20:12 UTC (permalink / raw)
  To: Mario Limonciello, Huang Rui, Rafael J. Wysocki, Viresh Kumar,
	Kalpana Shetty
  Cc: Mario Limonciello, Perry Yuan, K Prateek Nayak, linux-pm,
	linux-doc, linux-kernel

Add a unit test for CPPC Performance Priority that modifies the floor
perf and confirms if the modification was successful similar to the
energy_performance_preference unit test.

On platforms that do not support X86_FEATURE_CPPC_PERF_PRIO, the test
returns -EOPNOTSUPP and amd_pstate_ut_check_floor_freq is marked as
"skipped".

Suggested-by: Kalpana Shetty <kalpana.shetty@amd.com>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
---
changelog rfc v1..v2:

o New patch.
---
 drivers/cpufreq/amd-pstate-ut.c | 83 ++++++++++++++++++++++++++++++++-
 drivers/cpufreq/amd-pstate.c    |  7 +--
 drivers/cpufreq/amd-pstate.h    |  2 +
 3 files changed, 87 insertions(+), 5 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
index d1f730e3502b..6a17941380c2 100644
--- a/drivers/cpufreq/amd-pstate-ut.c
+++ b/drivers/cpufreq/amd-pstate-ut.c
@@ -59,6 +59,7 @@ static int amd_pstate_ut_check_freq(u32 index);
 static int amd_pstate_ut_epp(u32 index);
 static int amd_pstate_ut_check_driver(u32 index);
 static int amd_pstate_ut_check_freq_attrs(u32 index);
+static int amd_pstate_ut_check_floor_freq(u32 index);
 
 static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
 	{"amd_pstate_ut_acpi_cpc_valid",    amd_pstate_ut_acpi_cpc_valid   },
@@ -68,6 +69,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
 	{"amd_pstate_ut_epp",               amd_pstate_ut_epp              },
 	{"amd_pstate_ut_check_driver",      amd_pstate_ut_check_driver     },
 	{"amd_pstate_ut_check_freq_attrs",  amd_pstate_ut_check_freq_attrs },
+	{"amd_pstate_ut_check_floor_freq",  amd_pstate_ut_check_floor_freq },
 };
 
 static bool test_in_list(const char *list, const char *name)
@@ -560,6 +562,77 @@ static int amd_pstate_ut_check_freq_attrs(u32 index)
 	return ret;
 }
 
+static int amd_pstate_ut_check_floor_freq(u32 index)
+{
+	struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
+	char *buf __free(cleanup_page) = NULL;
+	unsigned int orig_floor_freq;
+	unsigned int floor_freq;
+	int ret, cpu = 0;
+
+	if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO))
+		return -EOPNOTSUPP;
+
+	policy = cpufreq_cpu_get(cpu);
+	if (!policy || !policy->driver_data)
+		return -ENODEV;
+
+	buf = (char *)__get_free_page(GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	guard(rwsem_write)(&policy->rwsem);
+
+	/* Retrieve original floor frequency */
+	memset(buf, 0, PAGE_SIZE);
+	ret = show_amd_pstate_floor_freq(policy, buf);
+	if (ret < 0)
+		return ret;
+
+	ret = kstrtou32(buf, 0, &orig_floor_freq);
+	if (ret)
+		return ret;
+
+	memset(buf, 0, PAGE_SIZE);
+	snprintf(buf, PAGE_SIZE, "%u", policy->cpuinfo.min_freq);
+
+	/* Set floor frequency to cpuinfo.min_freq */
+	ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf));
+	if (ret < 0) {
+		pr_err("Failed to set floor frequency to %s\n", buf);
+		return ret;
+	}
+
+	memset(buf, 0, PAGE_SIZE);
+	ret = show_amd_pstate_floor_freq(policy, buf);
+	if (ret < 0)
+		return ret;
+
+	strreplace(buf, '\n', '\0');
+	ret = kstrtou32(buf, 0, &floor_freq);
+	if (ret)
+		return ret;
+
+	/* Confirm sysfs reflects the change correctly. */
+	if (floor_freq != policy->cpuinfo.min_freq) {
+		pr_err("Floor frequency value mismatch: %u != %u\n",
+		       floor_freq, policy->cpuinfo.min_freq);
+		return -EINVAL;
+	}
+
+	memset(buf, 0, PAGE_SIZE);
+	snprintf(buf, PAGE_SIZE, "%u", orig_floor_freq);
+
+	/* Restore the original value. */
+	ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf));
+	if (ret < 0) {
+		pr_err("Failed to restore floor frequency to %s\n", buf);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int __init amd_pstate_ut_init(void)
 {
 	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
@@ -578,10 +651,16 @@ static int __init amd_pstate_ut_init(void)
 
 		ret = amd_pstate_ut_cases[i].func(i);
 
-		if (ret)
+		if (ret) {
+			/* Platform does not support the feature being tested. */
+			if (ret == -EOPNOTSUPP) {
+				pr_err("%-4d %-20s\t skipped!\n", i+1, amd_pstate_ut_cases[i].name);
+				continue;
+			}
 			pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
-		else
+		} else {
 			pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
+		}
 	}
 
 	return 0;
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index b875e92103e1..a42899722a21 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1502,8 +1502,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
 }
 EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
 
-static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
-					   const char *buf, size_t count)
+ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
@@ -1526,13 +1525,15 @@ static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
 
 	return ret ?: count;
 }
+EXPORT_SYMBOL_FOR_PSTATE_UT(store_amd_pstate_floor_freq);
 
-static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
+ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 
 	return sysfs_emit(buf, "%u\n", cpudata->floor_freq);
 }
+EXPORT_SYMBOL_FOR_PSTATE_UT(show_amd_pstate_floor_freq);
 
 static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf)
 {
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index edd697a5e29f..f8e2f6ba1534 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -160,6 +160,8 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
 				    const char *buf, size_t count);
 ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf);
 void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy);
+ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count);
+ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf);
 
 struct freq_attr;
 
-- 
2.34.1


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

* Re: [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf
  2026-07-23 20:12 ` [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf K Prateek Nayak
@ 2026-07-23 21:17   ` Mario Limonciello
  0 siblings, 0 replies; 11+ messages in thread
From: Mario Limonciello @ 2026-07-23 21:17 UTC (permalink / raw)
  To: K Prateek Nayak, Huang Rui, Rafael J. Wysocki, Viresh Kumar
  Cc: Mario Limonciello, Perry Yuan, linux-pm, linux-doc, linux-kernel



On 7/23/26 15:12, K Prateek Nayak wrote:
> Initialization of bios_min_perf (BIOS Requested CPU Min Freq.) only
> succeeds when the driver init finds the CPPC_REQ MSRs to have all 0s
> except for MIN_PERF bits.
> 
> A kexec puts the driver through the suspend path which, although resets
> the min_perf back to bios_min_perf, keeps the rest of the CPPR_REQ
> intact with the last value at the time of suspend.
> 
> The defensive check for bios_min_perf exists to prevent the min perf
> from last CPPC_REQ being incorrectly considered as bios_min_perf when
> a kexec switches from an older kernel running the version of driver
> which is not aware of bios_min_perf to a newer one.
> 
> This scenario is extremely unlikely and Mario suggested it is better to
> simplify the initialization rather than complicating the suspend resume
> paths.
> 
> Drop the defensive check for bios_min_perf initialization and add a
> debug message to dump the BIOS Requested Min Freq. to console leaving
> enough breadcrumbs for debug if a situation so arises.
> 
> Suggested-by: Mario Limonciello <mario.limonciello@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
> ---
> Changelog rfc v1..v2:
> 
> o Reworked based on review comments from Mario.
> ---
>   drivers/cpufreq/amd-pstate.c | 19 ++++++++-----------
>   1 file changed, 8 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 1157f05df10f..02308e95636e 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -462,7 +462,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   {
>   	union perf_cached perf = READ_ONCE(cpudata->perf);
>   	u64 cap1, numerator, cppc_req;
> -	u8 min_perf;
>   
>   	int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
>   				     &cap1);
> @@ -478,16 +477,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   		return ret;
>   
>   	WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
> -	min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
> -
> -	/*
> -	 * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
> -	 * indication that the min_perf value is the one specified through the BIOS option
> -	 */
> -	cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
> -
> -	if (!cppc_req)
> -		perf.bios_min_perf = min_perf;
>   
>   	perf.highest_perf = numerator;
>   	perf.max_limit_perf = numerator;
> @@ -495,6 +484,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
>   	perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
>   	perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
>   	perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
> +	perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
>   	WRITE_ONCE(cpudata->perf, perf);
>   	WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
>   	WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
> @@ -1043,6 +1033,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
>   		return -EINVAL;
>   	}
>   
> +	if (perf.bios_min_perf) {
> +		u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf);
> +
> +		pr_debug("Found Requested CPU Min Frequency of %uMHz on CPU%d\n",
> +			 bios_min_freq, cpudata->cpu);
> +	}
> +
>   	return 0;
>   }
>   


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

end of thread, other threads:[~2026-07-23 21:18 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 20:12 [PATCH v2 0/9] cpufreq/amd-pstate: BIOS min perf fixes and Dynamic EPP rework K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 1/9] cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 2/9] cpufreq/amd-pstate: Remove the defensive check for bios_min_perf K Prateek Nayak
2026-07-23 21:17   ` Mario Limonciello
2026-07-23 20:12 ` [PATCH v2 3/9] cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 4/9] cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 5/9] cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 6/9] Documentation/amd-pstate: Update dynamic_epp documentation with new behavior K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 7/9] cpufreq/amd-pstate: Reduce the scope of exported symbols K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 8/9] cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode K Prateek Nayak
2026-07-23 20:12 ` [PATCH v2 9/9] cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority K Prateek Nayak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox