[PATCH 3/4] cpufreq: amd-pstate-ut: Tolerate aliased EPP preferences in the EPP test
From: Mario Limonciello
Date: Mon Sep 21 2026 - 15:03:56 EST
amd_pstate_ut_epp() writes each named EPP preference and requires it to
read back as the same string. That assumes a one-to-one mapping between
named preferences and raw EPP values, which does not hold on all
platforms. Zen6 client, for example, programs the same raw EPP value for
more than one named preference (power and balance_power are both 64 on
performance cores), so show_energy_performance_preference() reports the
first name matching that value and the string comparison fails:
amd_pstate_ut: String EPP value mismatch: balance_power != power
amd_pstate_ut: 5 amd_pstate_ut_epp fail: -22!
Instead of comparing the strings, record the raw EPP value programmed by
the written preference, then re-write whatever name show() reported and
confirm it programs the same raw EPP value. This tolerates several
preferences aliasing to one value while still catching a genuinely
inconsistent show()/store() mapping.
Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx>
---
drivers/cpufreq/amd-pstate-ut.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c
index c2c1a166b3a9e..f5888beb767a8 100644
--- a/drivers/cpufreq/amd-pstate-ut.c
+++ b/drivers/cpufreq/amd-pstate-ut.c
@@ -372,11 +372,14 @@ static int amd_pstate_ut_epp(u32 index)
}
for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
+ u8 want_epp, got_epp;
+
memset(buf, 0, PAGE_SIZE);
snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
ret = store_energy_performance_preference(policy, buf, strlen(buf));
if (ret < 0)
goto out;
+ want_epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
memset(buf, 0, PAGE_SIZE);
ret = show_energy_performance_preference(policy, buf);
@@ -385,12 +388,27 @@ static int amd_pstate_ut_epp(u32 index)
strreplace(buf, '\n', '\0');
/*
* "dynamic" mode reports the EPP as "dynamic(profile:X)"
- * Trim at "(" and just compare tie the epp string.
+ * Trim at "(" and just keep the preference name.
*/
strreplace(buf, '(', '\0');
- if (strcmp(buf, epp_strings[i])) {
- pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
+ /*
+ * The preference read back may legitimately differ from the one
+ * written: some platforms (e.g. Zen6) program the same raw EPP
+ * value for more than one named preference, so show() reports the
+ * first name that matches that value. Rather than compare the
+ * strings, re-write whatever name was reported and confirm it
+ * programs the same raw EPP value. This verifies the show()/store()
+ * mapping stays consistent while tolerating such aliasing.
+ */
+ ret = store_energy_performance_preference(policy, buf, strlen(buf));
+ if (ret < 0)
+ goto out;
+ got_epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
+
+ if (want_epp != got_epp) {
+ pr_err("EPP mapping inconsistent: %s programmed %u but %s programmed %u\n",
+ epp_strings[i], want_epp, buf, got_epp);
ret = -EINVAL;
goto out;
}
--
2.43.0