]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ACPI: CPPC: add APIs and sysfs interface for perf_limited
authorSumit Gupta <sumitg@nvidia.com>
Fri, 6 Feb 2026 14:26:57 +0000 (19:56 +0530)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 27 Feb 2026 19:50:42 +0000 (20:50 +0100)
Add sysfs interface to read/write the Performance Limited register.

The Performance Limited register indicates to the OS that an
unpredictable event (like thermal throttling) has limited processor
performance. It contains two sticky bits set by the platform:
  - Bit 0 (Desired_Excursion): Set when delivered performance is
    constrained below desired performance. Not used when Autonomous
    Selection is enabled.
  - Bit 1 (Minimum_Excursion): Set when delivered performance is
    constrained below minimum performance.

These bits remain set until OSPM explicitly clears them. The write
operation accepts a bitmask of bits to clear:
  - Write 0x1 to clear bit 0
  - Write 0x2 to clear bit 1
  - Write 0x3 to clear both bits

This enables users to detect if platform throttling impacted a workload.
Users clear the register before execution, run the workload, then check
afterward - if set, hardware throttling occurred during that time window.

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

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Reviewed-by: Pierre Gondois <pierre.gondois@arm.com>
Reviewed-by: Lifeng Zheng <zhenglifeng1@huawei.com>
Link: https://patch.msgid.link/20260206142658.72583-7-sumitg@nvidia.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/cppc_acpi.c
drivers/cpufreq/cppc_cpufreq.c
include/acpi/cppc_acpi.h

index 94a7ffa8be3c375e5561e058ca25d680b1eb7599..53a6ffd995a1ab0f353b7564c80700742e3757f2 100644 (file)
@@ -1978,6 +1978,62 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 }
 EXPORT_SYMBOL_GPL(cppc_set_perf);
 
+/**
+ * cppc_get_perf_limited - Get the Performance Limited register value.
+ * @cpu: CPU from which to get Performance Limited register.
+ * @perf_limited: Pointer to store the Performance Limited value.
+ *
+ * The returned value contains sticky status bits indicating platform-imposed
+ * performance limitations.
+ *
+ * Return: 0 for success, -EIO on failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+       return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
+
+/**
+ * cppc_set_perf_limited() - Clear bits in the Performance Limited register.
+ * @cpu: CPU on which to write register.
+ * @bits_to_clear: Bitmask of bits to clear in the perf_limited register.
+ *
+ * The Performance Limited register contains two sticky bits set by platform:
+ *   - Bit 0 (Desired_Excursion): Set when delivered performance is constrained
+ *     below desired performance. Not used when Autonomous Selection is enabled.
+ *   - Bit 1 (Minimum_Excursion): Set when delivered performance is constrained
+ *     below minimum performance.
+ *
+ * These bits are sticky and remain set until OSPM explicitly clears them.
+ * This function only allows clearing bits (the platform sets them).
+ *
+ * Return: 0 for success, -EINVAL for invalid bits, -EIO on register
+ *         access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_set_perf_limited(int cpu, u64 bits_to_clear)
+{
+       u64 current_val, new_val;
+       int ret;
+
+       /* Only bits 0 and 1 are valid */
+       if (bits_to_clear & ~CPPC_PERF_LIMITED_MASK)
+               return -EINVAL;
+
+       if (!bits_to_clear)
+               return 0;
+
+       ret = cppc_get_perf_limited(cpu, &current_val);
+       if (ret)
+               return ret;
+
+       /* Clear the specified bits */
+       new_val = current_val & ~bits_to_clear;
+
+       return cppc_set_reg_val(cpu, PERF_LIMITED, new_val);
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
+
 /**
  * cppc_get_transition_latency - returns frequency transition latency in ns
  * @cpu_num: CPU number for per_cpu().
index 8a8cf76828ee23539d7f7f469a741b006e6b4093..94d489a4c90d1b10eefd9e2329fab2fb3341bc13 100644 (file)
@@ -985,16 +985,21 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy,
        return count;
 }
 
+CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited,
+                        cppc_set_perf_limited)
+
 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(perf_limited);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
        &freqdomain_cpus,
        &auto_select,
        &auto_act_window,
        &energy_performance_preference_val,
+       &perf_limited,
        NULL,
 };
 
index 3fc796c0d9022b7938545e6048e2a5bf6cc3273d..f7afa20b8ad9db55fe5c6e0f8f988359d744085b 100644 (file)
 #define CPPC_EPP_PERFORMANCE_PREF              0x00
 #define CPPC_EPP_ENERGY_EFFICIENCY_PREF                0xFF
 
+#define CPPC_PERF_LIMITED_DESIRED_EXCURSION    BIT(0)
+#define CPPC_PERF_LIMITED_MINIMUM_EXCURSION    BIT(1)
+#define CPPC_PERF_LIMITED_MASK         (CPPC_PERF_LIMITED_DESIRED_EXCURSION | \
+                                        CPPC_PERF_LIMITED_MINIMUM_EXCURSION)
+
 /* Each register has the folowing format. */
 struct cpc_reg {
        u8 descriptor;
@@ -174,6 +179,8 @@ 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_perf_limited(int cpu, u64 *perf_limited);
+extern int cppc_set_perf_limited(int cpu, u64 bits_to_clear);
 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);
@@ -270,6 +277,14 @@ static inline int cppc_set_auto_sel(int cpu, bool enable)
 {
        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 bits_to_clear)
+{
+       return -EOPNOTSUPP;
+}
 static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
 {
        return -ENODEV;