* [PATCH v3 0/1] asus-armoury: gate PPT writes on active fan curves
@ 2026-05-19 18:12 Ahmed Yaseen
2026-05-19 18:12 ` [PATCH v3 1/1] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
0 siblings, 1 reply; 3+ messages in thread
From: Ahmed Yaseen @ 2026-05-19 18:12 UTC (permalink / raw)
To: Corentin Chary, Luke D . Jones, Denis Benato, Hans de Goede,
Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mario Limonciello,
Ahmed Yaseen
My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
On 30 ASUS ROG laptop models flagged with requires_fan_curve in the
asus-armoury DMI power_data table, the BIOS ACPI method SPLX silently
discards Package Power Tracking (PPT) writes unless 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. Until then, the WMI DEVS call returns
success but the firmware ignores the value, so userspace sees no
effect from writes to ppt_pl1_spl, ppt_pl2_sppt, ppt_pl3_fppt,
ppt_apu_sppt or ppt_platform_sppt.
The requires_fan_curve flag has existed in the per-model power_data
entries for some time but was never read. This patch wires it up:
Patch 1: Adds the actual gate. Exports
asus_wmi_custom_fan_curve_is_enabled() from asus-wmi so
asus-armoury can query fan-curve state across module boundaries,
and returns -EBUSY with a pr_warn_once() from the PPT write path on
affected models when no fan curve is active.
Testing:
Verified on G835LW (ROG Strix SCAR 18 2025, requires_fan_curve=true):
- With no fan curve active, writes to ppt_pl1_spl return -EBUSY
and the cached value is unchanged.
- With pwm1_enable=1 on /sys/class/hwmon/.../asus_custom_fan_curve,
PPT writes succeed and readback matches.
- The pr_warn_once fires on the first rejected write and stays
silent on subsequent ones, as expected.
I do not have access to the other 29 affected models. Testers from
any of these would be appreciated: FX507VI, FX507VV, FX507Z,
GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
GU605CR, GU605CW, GU605CX, GU605M, GU605MU, G513I, G513QM,
G513QY, G513R, G614FP, G614J, G615LR, G634J, G713PV, G733C,
G733P, G814J, G834J, G835LR.
Changes since v2:
- Drop the new global asus_wmi_instance and reach the asus_wmi
instance through the existing asus_ref reference (already shared
with hid-asus), so it's accessed under its spinlock
- Use pr_warn_once instead of pr_warn (Suggested by Mario Limonciello)
- Drop the requires_fan_curve sysfs attribute (patch 2) so we're
not locked into the ABI before a kernel-managed 'custom' platform
profile design lands (Suggested by Derek J. Clark)
Changes since v1:
- Return EBUSY instead of ENODEV for userspace clarity
(Suggested by Derek J. Clark)
- Update ABI Documents to reflect the change to EBUSY
Ahmed Yaseen (1):
platform/x86: asus-armoury: gate PPT writes behind active fan curve
drivers/platform/x86/asus-armoury.c | 20 ++++++++++++++++++
drivers/platform/x86/asus-wmi.c | 24 ++++++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 5 +++++
3 files changed, 49 insertions(+)
--
2.54.0
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH v3 1/1] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-19 18:12 [PATCH v3 0/1] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
@ 2026-05-19 18:12 ` Ahmed Yaseen
2026-06-13 14:21 ` Denis Benato
0 siblings, 1 reply; 3+ messages in thread
From: Ahmed Yaseen @ 2026-05-19 18:12 UTC (permalink / raw)
To: Corentin Chary, Luke D . Jones, Denis Benato, Hans de Goede,
Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mario Limonciello,
Ahmed Yaseen
On models flagged with requires_fan_curve in the DMI power_data table
(30 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 -EBUSY with a pr_warn_once() 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@ghoul.dev>
---
drivers/platform/x86/asus-armoury.c | 20 ++++++++++++++++++
drivers/platform/x86/asus-wmi.c | 24 ++++++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 5 +++++
3 files changed, 49 insertions(+)
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
index 5b0987ccc270..f094b70fa1f0 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_once("PPT change requires an active fan curve on this model. Enable a custom fan curve first.\n");
+ return -EBUSY;
+ }
+ 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..e4c1716d9b30 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -4001,6 +4001,30 @@ 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 fan_curve_data *curves;
+ struct asus_wmi *asus;
+
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ asus = asus_ref.asus;
+ 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)
{
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 */
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v3 1/1] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-19 18:12 ` [PATCH v3 1/1] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
@ 2026-06-13 14:21 ` Denis Benato
0 siblings, 0 replies; 3+ messages in thread
From: Denis Benato @ 2026-06-13 14:21 UTC (permalink / raw)
To: Ahmed Yaseen, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mario Limonciello
On 5/19/26 20:12, Ahmed Yaseen wrote:
> On models flagged with requires_fan_curve in the DMI power_data table
> (30 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 -EBUSY with a pr_warn_once() 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.
Reviewed-by: Denis Benato <denis.benato@linux.dev>
> Signed-off-by: Ahmed Yaseen <yaseen@ghoul.dev>
> ---
> drivers/platform/x86/asus-armoury.c | 20 ++++++++++++++++++
> drivers/platform/x86/asus-wmi.c | 24 ++++++++++++++++++++++
> include/linux/platform_data/x86/asus-wmi.h | 5 +++++
> 3 files changed, 49 insertions(+)
>
> diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
> index 5b0987ccc270..f094b70fa1f0 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_once("PPT change requires an active fan curve on this model. Enable a custom fan curve first.\n");
> + return -EBUSY;
> + }
> + 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..e4c1716d9b30 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -4001,6 +4001,30 @@ 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 fan_curve_data *curves;
> + struct asus_wmi *asus;
> +
> + guard(spinlock_irqsave)(&asus_ref.lock);
> + asus = asus_ref.asus;
> + 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)
> {
> 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 */
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-13 14:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-19 18:12 [PATCH v3 0/1] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
2026-05-19 18:12 ` [PATCH v3 1/1] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
2026-06-13 14:21 ` Denis Benato
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.