Re: [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve

From: Derek J. Clark

Date: Tue May 12 2026 - 11:15:39 EST


On May 10, 2026 11:19:30 PM PDT, Ahmed Yaseen <yaseen@xxxxxxxxx> wrote:
>On models flagged with requires_fan_curve in the DMI power_data table
>(28 entries), the BIOS ACPI method SPLX only writes PPT values to the
>EC when the fan mode is set to Manual (FANM=4). FANM is set to 4 by
>the DEFC method when a custom fan curve is written. Without an active
>custom fan curve, the WMI DEVS call returns success but the firmware
>silently ignores the PPT value, so userspace observes no effect from
>its write.
>
>Gate writes to ASUS_WMI_DEVID_PPT_{PL1_SPL,PL2_SPPT,PL3_FPPT,APU_SPPT,
>PLAT_SPPT} on a check of asus_wmi_custom_fan_curve_is_enabled(), and
>return -ENODEV with a pr_warn() when no fan curve is active on an
>affected model. Export the helper from asus-wmi so asus-armoury can
>call it across module boundaries.
>
>Signed-off-by: Ahmed Yaseen <yaseen@xxxxxxxxx>
>---
> drivers/platform/x86/asus-armoury.c | 20 ++++++++++++++++
> drivers/platform/x86/asus-wmi.c | 28 ++++++++++++++++++++++
> include/linux/platform_data/x86/asus-wmi.h | 5 ++++
> 3 files changed, 53 insertions(+)
>
>diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
>index 5b0987ccc270..01e552573674 100644
>--- a/drivers/platform/x86/asus-armoury.c
>+++ b/drivers/platform/x86/asus-armoury.c
>@@ -93,6 +93,8 @@ struct asus_armoury_priv {
>
> u32 mini_led_dev_id;
> u32 gpu_mux_dev_id;
>+
>+ bool requires_fan_curve;
> };
>
> static struct asus_armoury_priv asus_armoury = {
>@@ -216,6 +218,22 @@ static int armoury_set_devstate(struct kobj_attribute *attr,
> u32 result;
> int err;
>
>+ /* On some models, PPT changes require an active fan curve */
>+ if (asus_armoury.requires_fan_curve) {
>+ switch (dev_id) {
>+ case ASUS_WMI_DEVID_PPT_PL1_SPL:
>+ case ASUS_WMI_DEVID_PPT_PL2_SPPT:
>+ case ASUS_WMI_DEVID_PPT_PL3_FPPT:
>+ case ASUS_WMI_DEVID_PPT_APU_SPPT:
>+ case ASUS_WMI_DEVID_PPT_PLAT_SPPT:
>+ if (!asus_wmi_custom_fan_curve_is_enabled()) {
>+ pr_warn("PPT change requires an active fan curve on this model. Enable a custom fan curve first.\n");
>+ return -ENODEV;

-EBUSY is more appropriate IMO. It lets userspace know the device isn't ready for the operation, not that it doesn't exist.

- Derek
>+ }
>+ break;
>+ }
>+ }
>+
> /*
> * Prevent developers from bricking devices or issuing dangerous
> * commands that can be difficult or impossible to recover from.
>@@ -1002,6 +1020,8 @@ static void init_rog_tunables(void)
> return;
> }
>
>+ asus_armoury.requires_fan_curve = power_data->requires_fan_curve;
>+
> /* Initialize AC power tunables */
> ac_limits = power_data->ac_data;
> if (ac_limits) {
>diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
>index 80144c412b90..b05218d31d90 100644
>--- a/drivers/platform/x86/asus-wmi.c
>+++ b/drivers/platform/x86/asus-wmi.c
>@@ -341,6 +341,9 @@ struct asus_wmi {
> /* Global to allow setting externally without requiring driver data */
> static enum asus_ally_mcu_hack use_ally_mcu_hack = ASUS_WMI_ALLY_MCU_HACK_INIT;
>
>+/* Global asus_wmi instance for use by exported functions */
>+static struct asus_wmi *asus_wmi_instance;
>+
> #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS)
> static void asus_wmi_show_deprecated(void)
> {
>@@ -4001,6 +4004,28 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
> return 0;
> }
>
>+/*
>+ * Returns true if at least one custom fan curve is active
>+ *
>+ * Used by asus-armoury to check if PPT writes will be accepted by the BIOS
>+ * on models that require an active fan curve for TDP changes.
>+ */
>+bool asus_wmi_custom_fan_curve_is_enabled(void)
>+{
>+ struct asus_wmi *asus = asus_wmi_instance;
>+ struct fan_curve_data *curves;
>+
>+ if (!asus)
>+ return false;
>+
>+ curves = asus->custom_fan_curves;
>+
>+ return (asus->cpu_fan_curve_available && curves[FAN_CURVE_DEV_CPU].enabled) ||
>+ (asus->gpu_fan_curve_available && curves[FAN_CURVE_DEV_GPU].enabled) ||
>+ (asus->mid_fan_curve_available && curves[FAN_CURVE_DEV_MID].enabled);
>+}
>+EXPORT_SYMBOL_NS_GPL(asus_wmi_custom_fan_curve_is_enabled, "ASUS_WMI");
>+
> /* Throttle thermal policy ****************************************************/
> static int throttle_thermal_policy_write(struct asus_wmi *asus)
> {
>@@ -5156,6 +5181,8 @@ static int asus_wmi_add(struct platform_device *pdev)
>
> asus_wmi_debugfs_init(asus);
>
>+ asus_wmi_instance = asus;
>+
> return 0;
>
> fail_wmi_handler:
>@@ -5199,6 +5226,7 @@ static void asus_wmi_remove(struct platform_device *device)
> throttle_thermal_policy_set_default(asus);
> asus_wmi_battery_exit(asus);
>
>+ asus_wmi_instance = NULL;
> kfree(asus);
> }
>
>diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
>index 554f41b827e1..5d57293ced6c 100644
>--- a/include/linux/platform_data/x86/asus-wmi.h
>+++ b/include/linux/platform_data/x86/asus-wmi.h
>@@ -196,6 +196,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> int asus_hid_register_listener(struct asus_hid_listener *cdev);
> void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
> int asus_hid_event(enum asus_hid_event event);
>+bool asus_wmi_custom_fan_curve_is_enabled(void);
> #else
> static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
> {
>@@ -227,6 +228,10 @@ static inline int asus_hid_event(enum asus_hid_event event)
> {
> return -ENODEV;
> }
>+static inline bool asus_wmi_custom_fan_curve_is_enabled(void)
>+{
>+ return false;
>+}
> #endif
>
> #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */