[PATCH v3 1/3] ACPI: CPPC: Reject desired_perf reads on ACPI 6.6+

Christian Loehle <[email protected]> Mon, 3 Aug 2026 11:02:49 +0100
Newsgroups org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm
Message-ID <18ee922e97441b99b9ab346fe559d837d05eb5e7.1785749175.git.christian.loehle@arm.com>
When CPPC feedback counters cannot provide a usable sample, cppc-cpufreq
calls cppc_get_desired_perf() because some platforms repurpose Desired
Performance to report actual delivered performance.

The fallback was added for platforms on which Desired Performance reflects
delivered performance. ACPI 6.6 defines the register as write-only, so
invoking that workaround on an ACPI 6.6 or later platform would require an
invalid register read.

Make cppc_get_desired_perf() return -EOPNOTSUPP in that case. When checking
the FADT minor revision, mask off its upper errata-generation bits and
compare only the specification minor version.

The FADT revision may not accurately describe the semantics implemented by
_CPC. If a nominally pre-6.6 platform implements Desired Performance as
write-only, a read may return zero and make cppc_cpufreq_get_rate() report
0 kHz. Treat a zero read as unusable and fall back to the cached OSPM
request, just as for a failed read.

Fixes: c47195631960 ("cppc_cpufreq: Use desired perf if feedback ctrs are 0 or unchanged")
Cc: [email protected]
Suggested-by: Sumit Gupta <[email protected]>
Signed-off-by: Christian Loehle <[email protected]>
---
 drivers/acpi/cppc_acpi.c       | 15 ++++++++++++++-
 drivers/cpufreq/cppc_cpufreq.c |  2 +-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 53d09ca98f06..6e5381f8de38 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1316,15 +1316,28 @@ static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val)
 	return cpc_write(cpu, reg, val);
 }
 
+static bool cppc_desired_perf_readable(void)
+{
+	u8 minor_revision = acpi_gbl_FADT.minor_revision & 0x0f;
+
+	return acpi_gbl_FADT.header.revision < 6 ||
+	       (acpi_gbl_FADT.header.revision == 6 && minor_revision < 6);
+}
+
 /**
  * cppc_get_desired_perf - Get the desired performance register value.
  * @cpunum: CPU from which to get desired performance.
  * @desired_perf: Return address.
  *
- * Return: 0 for success, -EIO otherwise.
+ * Return: 0 for success, -EOPNOTSUPP for ACPI 6.6 or later, and a negative
+ * errno otherwise.
  */
 int cppc_get_desired_perf(int cpunum, u64 *desired_perf)
 {
+	/* ACPI 6.6 no longer specifies Desired Performance as readable. */
+	if (!cppc_desired_perf_readable())
+		return -EOPNOTSUPP;
+
 	return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf);
 }
 EXPORT_SYMBOL_GPL(cppc_get_desired_perf);
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 6fe0e972952a..80893844353c 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -836,7 +836,7 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu)
 	 * value first as some platforms may update the actual delivered perf
 	 * there; if failed, resort to the cached desired perf.
 	 */
-	if (cppc_get_desired_perf(cpu, &delivered_perf))
+	if (cppc_get_desired_perf(cpu, &delivered_perf) || !delivered_perf)
 		delivered_perf = cpu_data->perf_ctrls.desired_perf;
 
 	return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf);
-- 
2.34.1