* [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves
@ 2026-05-11 6:19 Ahmed Yaseen
2026-05-11 6:19 ` [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-11 6:19 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel, asus-linux
My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
On 28 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 series 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 -ENODEV with a pr_warn() from the PPT write path on
affected models when no fan curve is active.
Patch 2: Exposes the same flag to userspace as a read-only sysfs
attribute (requires_fan_curve) so tools like asusctl and rogcc
can surface the prerequisite to the user before issuing PPT
writes. Documented in
Documentation/ABI/testing/sysfs-class-firmware-attributes.
Testing:
Verified on G835LW (ROG Strix SCAR 18 2025, requires_fan_curve=true):
- With no fan curve active, writes to ppt_pl1_spl return -ENODEV
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 fires exactly once per rejected write.
I do not have access to the other 27 affected models. Testers from
any of these would be appreciated: FX507VI, FX507VV, FX507Z,
GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
GU605CR, GU605CW, GU605CX, GU605M, G513I, G513QM, G513QY, G513R,
G614J, G615LR, G634J, G713PV, G733C, G733P, G814J, G834J, G835LR.
Ahmed Yaseen (2):
platform/x86: asus-armoury: gate PPT writes behind active fan curve
platform/x86: asus-armoury: expose requires_fan_curve via sysfs
.../testing/sysfs-class-firmware-attributes | 25 +++++++++++++
drivers/platform/x86/asus-armoury.c | 35 +++++++++++++++++++
drivers/platform/x86/asus-wmi.c | 28 +++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 5 +++
4 files changed, 93 insertions(+)
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
@ 2026-05-11 6:19 ` Ahmed Yaseen
2026-05-12 15:15 ` Derek J. Clark
2026-05-11 6:19 ` [PATCH 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
` (4 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-11 6:19 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel, asus-linux
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@ghoul.dev>
---
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;
+ }
+ 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 */
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
2026-05-11 6:19 ` [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
@ 2026-05-11 6:19 ` Ahmed Yaseen
2026-05-12 15:14 ` [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Derek J. Clark
` (3 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-11 6:19 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel, asus-linux
Expose the per-model requires_fan_curve flag as a read-only sysfs
attribute so userspace tools (asusctl, rogcc) can discover whether
the system requires an active custom fan curve for PPT changes to
take effect, and warn the user before issuing such writes.
The attribute appears at:
/sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
Document the attribute in
Documentation/ABI/testing/sysfs-class-firmware-attributes.
Signed-off-by: Ahmed Yaseen <yaseen@ghoul.dev>
---
.../testing/sysfs-class-firmware-attributes | 25 +++++++++++++++++++
drivers/platform/x86/asus-armoury.c | 15 +++++++++++
2 files changed, 40 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
index 2713efa509b4..cff90c5840db 100644
--- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
+++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
@@ -492,3 +492,28 @@ Description:
log entry size identifies audit log size for the current BIOS version.
The current size is 16 bytes but it can be up to 128 bytes long in future BIOS
versions.
+
+What: /sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
+Date: May 2026
+KernelVersion: 7.1
+Contact: platform-driver-x86@vger.kernel.org
+Description:
+ A read-only attribute that reads 1 on ASUS ROG models where the
+ BIOS requires a custom fan curve to be active before Package
+ Power Tracking (PPT) writes take effect. On affected models, the
+ BIOS silently ignores writes to ppt_pl1_spl, ppt_pl2_sppt,
+ ppt_pl3_fppt, ppt_apu_sppt and ppt_platform_sppt unless a custom
+ fan curve has been written via the asus_custom_fan_curve hwmon
+ device.
+
+ The kernel rejects PPT writes with -ENODEV on these models when
+ no fan curve is active. Userspace tools can read this attribute
+ to surface a clear prerequisite to the user instead of letting
+ the write appear to succeed at the firmware-attribute layer
+ while the BIOS discards it.
+
+ == ==============================================
+ 0 No fan curve prerequisite for PPT writes.
+ 1 A custom fan curve must be active; PPT writes
+ return -ENODEV otherwise.
+ == ==============================================
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
index 01e552573674..fde6167c7f4e 100644
--- a/drivers/platform/x86/asus-armoury.c
+++ b/drivers/platform/x86/asus-armoury.c
@@ -127,6 +127,13 @@ static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *
static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
+static ssize_t requires_fan_curve_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", asus_armoury.requires_fan_curve);
+}
+
+static struct kobj_attribute requires_fan_curve = __ATTR_RO(requires_fan_curve);
+
static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
{
return !strcmp(attr->attr.name, "gpu_mux_mode") ||
@@ -914,6 +921,12 @@ static int asus_fw_attr_add(void)
goto err_destroy_kset;
}
+ err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
+ if (err) {
+ pr_err("Failed to create requires_fan_curve attribute\n");
+ goto err_destroy_kset;
+ }
+
asus_armoury.mini_led_dev_id = 0;
if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE))
asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
@@ -987,6 +1000,7 @@ static int asus_fw_attr_add(void)
if (asus_armoury.mini_led_dev_id)
sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
err_remove_file:
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
err_destroy_kset:
kset_unregister(asus_armoury.fw_attr_kset);
@@ -1161,6 +1175,7 @@ static void __exit asus_fw_exit(void)
if (asus_armoury.mini_led_dev_id)
sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
kset_unregister(asus_armoury.fw_attr_kset);
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
2026-05-11 6:19 ` [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
2026-05-11 6:19 ` [PATCH 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
@ 2026-05-12 15:14 ` Derek J. Clark
2026-05-13 14:54 ` Ahmed Yaseen
2026-05-13 15:47 ` [PATCH v2 " Ahmed Yaseen
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: Derek J. Clark @ 2026-05-12 15:14 UTC (permalink / raw)
To: Ahmed Yaseen, platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, hansg, ilpo.jarvinen,
prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel, linux-kernel,
asus-linux, mario.limonciello
On May 10, 2026 11:19:14 PM PDT, Ahmed Yaseen <yaseen@ghoul.dev> wrote:
>My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
>
>On 28 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.
>
This very nearly touches on an issue that has been present in the asus_armoury driver for a while, and presents a possible avenue to correct it. When originally adding PPT adjustments it was discussed that doing so should be gated under the "custom" platform profile [1]. This was simple to do for the Lenovo WMI drivers because that interface includes a BIOS custom mode and requires it to be set for PPT settings to take effect. This soft "requirement" was missed when asus_armoury was accepted, and it currently allows PPT settings to override manufacturer intent. Please correct me if I'm wrong, but I believe the fan curves associated in BIOS for low-power, balanced, and performance will not automatically adjust if a user changes the TDP outside of the window. I.e. setting low power and then maximum SPL would leave the fans in a quiet mode.
Since some devices require this bit to be set, it seems like a good opportunity to gate the platform profile under custom, and use custom to set the performance mode and this bit, then allow userspace to set the curve and ppt values.
Adding Mario to CC for his SA/comments on this proposal.
- Derek
1: https://lore.kernel.org/platform-driver-x86/20241206031918.1537-17-mario.limonciello@amd.com/
>The requires_fan_curve flag has existed in the per-model power_data
>entries for some time but was never read. This series 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 -ENODEV with a pr_warn() from the PPT write path on
> affected models when no fan curve is active.
>
> Patch 2: Exposes the same flag to userspace as a read-only sysfs
> attribute (requires_fan_curve) so tools like asusctl and rogcc
> can surface the prerequisite to the user before issuing PPT
> writes. Documented in
> Documentation/ABI/testing/sysfs-class-firmware-attributes.
>
>Testing:
> Verified on G835LW (ROG Strix SCAR 18 2025, requires_fan_curve=true):
> - With no fan curve active, writes to ppt_pl1_spl return -ENODEV
> 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 fires exactly once per rejected write.
>
> I do not have access to the other 27 affected models. Testers from
> any of these would be appreciated: FX507VI, FX507VV, FX507Z,
> GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
> GU605CR, GU605CW, GU605CX, GU605M, G513I, G513QM, G513QY, G513R,
> G614J, G615LR, G634J, G713PV, G733C, G733P, G814J, G834J, G835LR.
>
>Ahmed Yaseen (2):
> platform/x86: asus-armoury: gate PPT writes behind active fan curve
> platform/x86: asus-armoury: expose requires_fan_curve via sysfs
>
> .../testing/sysfs-class-firmware-attributes | 25 +++++++++++++
> drivers/platform/x86/asus-armoury.c | 35 +++++++++++++++++++
> drivers/platform/x86/asus-wmi.c | 28 +++++++++++++++
> include/linux/platform_data/x86/asus-wmi.h | 5 +++
> 4 files changed, 93 insertions(+)
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-11 6:19 ` [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
@ 2026-05-12 15:15 ` Derek J. Clark
0 siblings, 0 replies; 12+ messages in thread
From: Derek J. Clark @ 2026-05-12 15:15 UTC (permalink / raw)
To: Ahmed Yaseen, platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, hansg, ilpo.jarvinen,
prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel, linux-kernel,
asus-linux
On May 10, 2026 11:19:30 PM PDT, Ahmed Yaseen <yaseen@ghoul.dev> 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@ghoul.dev>
>---
> 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 */
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves
2026-05-12 15:14 ` [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Derek J. Clark
@ 2026-05-13 14:54 ` Ahmed Yaseen
2026-05-19 16:05 ` Derek John Clark
0 siblings, 1 reply; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-13 14:54 UTC (permalink / raw)
To: Derek J. Clark, platform-driver-x86
Cc: corentin.chary, luke, denis.benato, hansg, ilpo.jarvinen,
prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel, linux-kernel,
mario.limonciello
On 12/05/2026 20:14, Derek J. Clark wrote:
> On May 10, 2026 11:19:14 PM PDT, Ahmed Yaseen <yaseen@ghoul.dev> wrote:
>> My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
>>
>> On 28 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.
>>
>
> This very nearly touches on an issue that has been present in the asus_armoury driver for a while, and presents a possible avenue to correct it. When originally adding PPT adjustments it was discussed that doing so should be gated under the "custom" platform profile [1]. This was simple to do for the Lenovo WMI drivers because that interface includes a BIOS custom mode and requires it to be set for PPT settings to take effect. This soft "requirement" was missed when asus_armoury was accepted, and it currently allows PPT settings to override manufacturer intent. Please correct me if I'm wrong, but I believe the fan curves associated in BIOS for low-power, balanced, and performance will not automatically adjust if a user changes the TDP outside of the window. I.e. setting low power and then maximum SPL would leave the fans in a quiet mode.
>
> Since some devices require this bit to be set, it seems like a good opportunity to gate the platform profile under custom, and use custom to set the performance mode and this bit, then allow userspace to set the curve and ppt values.
>
> Adding Mario to CC for his SA/comments on this proposal.
>
> - Derek
>
>
> 1: https://lore.kernel.org/platform-driver-x86/20241206031918.1537-17-mario.limonciello@amd.com/
About the PPT settings overriding manufacturer intent, I believe this is
a misunderstanding. I believe Derek is pointing at how currently PPT
adjustments for these models appear to apply without a custom fan curve
while manufacturer intends for the TDP to only be adjustable with a
custom fan curve applied. To clarify, for the modules that require a
custom fan curve, TDP value adjustments are currently being completely
ignored by the BIOS because the requirement for the existence of a fan
curve prevents it at a hardware level. Meanwhile on the kernel side of
things, it would appear that TDP values are actually being set, while it
is actually ignored. This is the reason for my gating custom TDP values
behind requires_fan_curve for models that require it. Please correct me
if i am understanding the previous message incorrectly though.
As for the other issue mentioned (if a user were to choose low power and
set maximum SPL), this is an issue I was not able to properly recreate.
I have attempted to do this but on my device at least, a custom TDP
cannot be set without a custom fan curve. Even in low power mode, I am
able to ramp my fans up to 100% with a custom fan curve and increasing
TDP knobs works perfectly and my fans are loud as expected. It may be
the case that due to the bug I mentioned earlier, TDP was possible to
visually max out without a custom fan curve while being in quiet mode.
However, on an older device that doesn't require a custom fan curve for
TDP might behave as Derek mentioned, as there is no hardware level
enforcement for the user to take over the fan curve before changing TDP.
I am unable to test this as I do not have such a device, but I believe
this is how the device's design is. The user can simply use custom fan
curves if their device supports it in this case. Instead, there are
min-max power limits for each power profile defined for these to prevent
damage.
As for the proposal to implement a 'custom' power profile, I believe
this needs more discussion. Unlike Lenovo, ASUS does not have this as a
native option in the BIOS, Instead ASUS ships 4 fan modes (Quiet,
Balanced, Performance, Custom) which we are already using. The long term
plan I have in mind for this is that the custom fan curve can be applied
separately to all 3 performance profiles, saved somewhere, and switched
between them by the user.
So while I can see a lot of things becoming cleaner if a custom fan mode
mapped directly to a custom power profile, too many issues come together
with it. For instance, would this new custom mode use Performance,
Balanced, or Quiet as a baseline? what about the TDP value limits that
differ per profile according to Asus Armoury Crate in Widnows? it also
causes more complications for some users that only decrease TDP to
reduce power consumption as they will then also be switched to a custom
mode by the kernel which would now apply a custom fan curve which also
needs us to create defaults for these. In the case of Lenovo, the BIOS
likely ships with these baselines per device but this is not applicable
in our case.
If we can discuss this custom mode in more depth and the pros outweigh
the cons, I believe we can discuss this for a follow-up patch
- Ahmed Yaseen
>
>> The requires_fan_curve flag has existed in the per-model power_data
>> entries for some time but was never read. This series 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 -ENODEV with a pr_warn() from the PPT write path on
>> affected models when no fan curve is active.
>>
>> Patch 2: Exposes the same flag to userspace as a read-only sysfs
>> attribute (requires_fan_curve) so tools like asusctl and rogcc
>> can surface the prerequisite to the user before issuing PPT
>> writes. Documented in
>> Documentation/ABI/testing/sysfs-class-firmware-attributes.
>>
>> Testing:
>> Verified on G835LW (ROG Strix SCAR 18 2025, requires_fan_curve=true):
>> - With no fan curve active, writes to ppt_pl1_spl return -ENODEV
>> 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 fires exactly once per rejected write.
>>
>> I do not have access to the other 27 affected models. Testers from
>> any of these would be appreciated: FX507VI, FX507VV, FX507Z,
>> GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
>> GU605CR, GU605CW, GU605CX, GU605M, G513I, G513QM, G513QY, G513R,
>> G614J, G615LR, G634J, G713PV, G733C, G733P, G814J, G834J, G835LR.
>>
>> Ahmed Yaseen (2):
>> platform/x86: asus-armoury: gate PPT writes behind active fan curve
>> platform/x86: asus-armoury: expose requires_fan_curve via sysfs
>>
>> .../testing/sysfs-class-firmware-attributes | 25 +++++++++++++
>> drivers/platform/x86/asus-armoury.c | 35 +++++++++++++++++++
>> drivers/platform/x86/asus-wmi.c | 28 +++++++++++++++
>> include/linux/platform_data/x86/asus-wmi.h | 5 +++
>> 4 files changed, 93 insertions(+)
>>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 0/2] asus-armoury: gate PPT writes on active fan curves
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
` (2 preceding siblings ...)
2026-05-12 15:14 ` [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Derek J. Clark
@ 2026-05-13 15:47 ` Ahmed Yaseen
2026-05-13 15:47 ` [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
2026-05-13 15:47 ` [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
5 siblings, 0 replies; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-13 15:47 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel
My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
On 28 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 series 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() from the PPT write path on
affected models when no fan curve is active.
Patch 2: Exposes the same flag to userspace as a read-only sysfs
attribute (requires_fan_curve) so tools like asusctl and rogcc
can surface the prerequisite to the user before issuing PPT
writes. Documented in
Documentation/ABI/testing/sysfs-class-firmware-attributes.
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 fires exactly once per rejected write.
I do not have access to the other 27 affected models. Testers from
any of these would be appreciated: FX507VI, FX507VV, FX507Z,
GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
GU605CR, GU605CW, GU605CX, GU605M, G513I, G513QM, G513QY, G513R,
G614J, G615LR, G634J, G713PV, G733C, G733P, G814J, G834J, G835LR.
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 (2):
platform/x86: asus-armoury: gate PPT writes behind active fan curve
platform/x86: asus-armoury: expose requires_fan_curve via sysfs
.../testing/sysfs-class-firmware-attributes | 25 +++++++++++++
drivers/platform/x86/asus-armoury.c | 35 +++++++++++++++++++
drivers/platform/x86/asus-wmi.c | 28 +++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 5 +++
4 files changed, 93 insertions(+)
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
` (3 preceding siblings ...)
2026-05-13 15:47 ` [PATCH v2 " Ahmed Yaseen
@ 2026-05-13 15:47 ` Ahmed Yaseen
2026-05-16 21:15 ` Mario Limonciello
2026-05-13 15:47 ` [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
5 siblings, 1 reply; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-13 15:47 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel
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 -EBUSY 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@ghoul.dev>
---
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..9b1c41a25b38 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 -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..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 */
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
` (4 preceding siblings ...)
2026-05-13 15:47 ` [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
@ 2026-05-13 15:47 ` Ahmed Yaseen
2026-05-16 21:17 ` Mario Limonciello
5 siblings, 1 reply; 12+ messages in thread
From: Ahmed Yaseen @ 2026-05-13 15:47 UTC (permalink / raw)
To: platform-driver-x86
Cc: yaseen, corentin.chary, luke, denis.benato, derekjohn.clark,
hansg, ilpo.jarvinen, prasanth.ksr, mpearson-lenovo,
Dell.Client.Kernel, linux-kernel
Expose the per-model requires_fan_curve flag as a read-only sysfs
attribute so userspace tools (asusctl, rogcc) can discover whether
the system requires an active custom fan curve for PPT changes to
take effect, and warn the user before issuing such writes.
The attribute appears at:
/sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
Document the attribute in
Documentation/ABI/testing/sysfs-class-firmware-attributes.
Signed-off-by: Ahmed Yaseen <yaseen@ghoul.dev>
---
.../testing/sysfs-class-firmware-attributes | 25 +++++++++++++++++++
drivers/platform/x86/asus-armoury.c | 15 +++++++++++
2 files changed, 40 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
index 2713efa509b4..12700a077f43 100644
--- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
+++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
@@ -492,3 +492,28 @@ Description:
log entry size identifies audit log size for the current BIOS version.
The current size is 16 bytes but it can be up to 128 bytes long in future BIOS
versions.
+
+What: /sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
+Date: May 2026
+KernelVersion: 7.1
+Contact: platform-driver-x86@vger.kernel.org
+Description:
+ A read-only attribute that reads 1 on ASUS ROG models where the
+ BIOS requires a custom fan curve to be active before Package
+ Power Tracking (PPT) writes take effect. On affected models, the
+ BIOS silently ignores writes to ppt_pl1_spl, ppt_pl2_sppt,
+ ppt_pl3_fppt, ppt_apu_sppt and ppt_platform_sppt unless a custom
+ fan curve has been written via the asus_custom_fan_curve hwmon
+ device.
+
+ The kernel rejects PPT writes with -EBUSY on these models when
+ no fan curve is active. Userspace tools can read this attribute
+ to surface a clear prerequisite to the user instead of letting
+ the write appear to succeed at the firmware-attribute layer
+ while the BIOS discards it.
+
+ == ==============================================
+ 0 No fan curve prerequisite for PPT writes.
+ 1 A custom fan curve must be active; PPT writes
+ return -EBUSY otherwise.
+ == ==============================================
diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
index 9b1c41a25b38..9b483d7e9855 100644
--- a/drivers/platform/x86/asus-armoury.c
+++ b/drivers/platform/x86/asus-armoury.c
@@ -127,6 +127,13 @@ static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *
static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
+static ssize_t requires_fan_curve_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%d\n", asus_armoury.requires_fan_curve);
+}
+
+static struct kobj_attribute requires_fan_curve = __ATTR_RO(requires_fan_curve);
+
static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
{
return !strcmp(attr->attr.name, "gpu_mux_mode") ||
@@ -914,6 +921,12 @@ static int asus_fw_attr_add(void)
goto err_destroy_kset;
}
+ err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
+ if (err) {
+ pr_err("Failed to create requires_fan_curve attribute\n");
+ goto err_destroy_kset;
+ }
+
asus_armoury.mini_led_dev_id = 0;
if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE))
asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
@@ -987,6 +1000,7 @@ static int asus_fw_attr_add(void)
if (asus_armoury.mini_led_dev_id)
sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
err_remove_file:
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
err_destroy_kset:
kset_unregister(asus_armoury.fw_attr_kset);
@@ -1161,6 +1175,7 @@ static void __exit asus_fw_exit(void)
if (asus_armoury.mini_led_dev_id)
sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
+ sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
kset_unregister(asus_armoury.fw_attr_kset);
device_destroy(&firmware_attributes_class, MKDEV(0, 0));
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve
2026-05-13 15:47 ` [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
@ 2026-05-16 21:15 ` Mario Limonciello
0 siblings, 0 replies; 12+ messages in thread
From: Mario Limonciello @ 2026-05-16 21:15 UTC (permalink / raw)
To: Ahmed Yaseen, platform-driver-x86
Cc: corentin.chary, luke, denis.benato, derekjohn.clark, hansg,
ilpo.jarvinen, prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel,
linux-kernel
On 5/13/26 10:47 AM, Ahmed Yaseen 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 -EBUSY 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@ghoul.dev>
> ---
> 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..9b1c41a25b38 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");
To avoid userspace causing spam about this - I would say pr_warn_once().
> + 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..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;
Why do you need a global?
This couldn't be part of "struct asus_wmi"?
> +
> #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 */
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs
2026-05-13 15:47 ` [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
@ 2026-05-16 21:17 ` Mario Limonciello
0 siblings, 0 replies; 12+ messages in thread
From: Mario Limonciello @ 2026-05-16 21:17 UTC (permalink / raw)
To: Ahmed Yaseen, platform-driver-x86
Cc: corentin.chary, luke, denis.benato, derekjohn.clark, hansg,
ilpo.jarvinen, prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel,
linux-kernel
On 5/13/26 10:47 AM, Ahmed Yaseen wrote:
> Expose the per-model requires_fan_curve flag as a read-only sysfs
> attribute so userspace tools (asusctl, rogcc) can discover whether
> the system requires an active custom fan curve for PPT changes to
> take effect, and warn the user before issuing such writes.
>
> The attribute appears at:
> /sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
>
> Document the attribute in
> Documentation/ABI/testing/sysfs-class-firmware-attributes.
>
> Signed-off-by: Ahmed Yaseen <yaseen@ghoul.dev>
> ---
> .../testing/sysfs-class-firmware-attributes | 25 +++++++++++++++++++
> drivers/platform/x86/asus-armoury.c | 15 +++++++++++
> 2 files changed, 40 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
> index 2713efa509b4..12700a077f43 100644
> --- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
> +++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
> @@ -492,3 +492,28 @@ Description:
> log entry size identifies audit log size for the current BIOS version.
> The current size is 16 bytes but it can be up to 128 bytes long in future BIOS
> versions.
> +
> +What: /sys/class/firmware-attributes/asus-armoury/attributes/requires_fan_curve
> +Date: May 2026
> +KernelVersion: 7.1
Right now we're submitting content for 7.2 or later, so this should be
updated.
> +Contact: platform-driver-x86@vger.kernel.org
> +Description:
> + A read-only attribute that reads 1 on ASUS ROG models where the
> + BIOS requires a custom fan curve to be active before Package
> + Power Tracking (PPT) writes take effect. On affected models, the
> + BIOS silently ignores writes to ppt_pl1_spl, ppt_pl2_sppt,
> + ppt_pl3_fppt, ppt_apu_sppt and ppt_platform_sppt unless a custom
> + fan curve has been written via the asus_custom_fan_curve hwmon
> + device.
> +
> + The kernel rejects PPT writes with -EBUSY on these models when
> + no fan curve is active. Userspace tools can read this attribute
> + to surface a clear prerequisite to the user instead of letting
> + the write appear to succeed at the firmware-attribute layer
> + while the BIOS discards it.
> +
> + == ==============================================
> + 0 No fan curve prerequisite for PPT writes.
> + 1 A custom fan curve must be active; PPT writes
> + return -EBUSY otherwise.
> + == ==============================================
> diff --git a/drivers/platform/x86/asus-armoury.c b/drivers/platform/x86/asus-armoury.c
> index 9b1c41a25b38..9b483d7e9855 100644
> --- a/drivers/platform/x86/asus-armoury.c
> +++ b/drivers/platform/x86/asus-armoury.c
> @@ -127,6 +127,13 @@ static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *
>
> static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
>
> +static ssize_t requires_fan_curve_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "%d\n", asus_armoury.requires_fan_curve);
> +}
> +
> +static struct kobj_attribute requires_fan_curve = __ATTR_RO(requires_fan_curve);
> +
> static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
> {
> return !strcmp(attr->attr.name, "gpu_mux_mode") ||
> @@ -914,6 +921,12 @@ static int asus_fw_attr_add(void)
> goto err_destroy_kset;
> }
>
> + err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
> + if (err) {
> + pr_err("Failed to create requires_fan_curve attribute\n");
> + goto err_destroy_kset;
> + }
> +
> asus_armoury.mini_led_dev_id = 0;
> if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE))
> asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> @@ -987,6 +1000,7 @@ static int asus_fw_attr_add(void)
> if (asus_armoury.mini_led_dev_id)
> sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
> err_remove_file:
> + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
> sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
> err_destroy_kset:
> kset_unregister(asus_armoury.fw_attr_kset);
> @@ -1161,6 +1175,7 @@ static void __exit asus_fw_exit(void)
> if (asus_armoury.mini_led_dev_id)
> sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
>
> + sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &requires_fan_curve.attr);
> sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
> kset_unregister(asus_armoury.fw_attr_kset);
> device_destroy(&firmware_attributes_class, MKDEV(0, 0));
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves
2026-05-13 14:54 ` Ahmed Yaseen
@ 2026-05-19 16:05 ` Derek John Clark
0 siblings, 0 replies; 12+ messages in thread
From: Derek John Clark @ 2026-05-19 16:05 UTC (permalink / raw)
To: Ahmed Yaseen
Cc: platform-driver-x86, corentin.chary, luke, denis.benato, hansg,
ilpo.jarvinen, prasanth.ksr, mpearson-lenovo, Dell.Client.Kernel,
linux-kernel, mario.limonciello
On Wed, May 13, 2026 at 7:55 AM Ahmed Yaseen <yaseen@ghoul.dev> wrote:
>
> On 12/05/2026 20:14, Derek J. Clark wrote:
> > On May 10, 2026 11:19:14 PM PDT, Ahmed Yaseen <yaseen@ghoul.dev> wrote:
> >> My first kernel patch series, posted in agreement with Denis Benato (Cc'd).
> >>
> >> On 28 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.
> >>
> >
> > This very nearly touches on an issue that has been present in the asus_armoury driver for a while, and presents a possible avenue to correct it. When originally adding PPT adjustments it was discussed that doing so should be gated under the "custom" platform profile [1]. This was simple to do for the Lenovo WMI drivers because that interface includes a BIOS custom mode and requires it to be set for PPT settings to take effect. This soft "requirement" was missed when asus_armoury was accepted, and it currently allows PPT settings to override manufacturer intent. Please correct me if I'm wrong, but I believe the fan curves associated in BIOS for low-power, balanced, and performance will not automatically adjust if a user changes the TDP outside of the window. I.e. setting low power and then maximum SPL would leave the fans in a quiet mode.
> >
> > Since some devices require this bit to be set, it seems like a good opportunity to gate the platform profile under custom, and use custom to set the performance mode and this bit, then allow userspace to set the curve and ppt values.
> >
> > Adding Mario to CC for his SA/comments on this proposal.
> >
> > - Derek
> >
> >
> > 1: https://lore.kernel.org/platform-driver-x86/20241206031918.1537-17-mario.limonciello@amd.com/
>
> About the PPT settings overriding manufacturer intent, I believe this is
> a misunderstanding. I believe Derek is pointing at how currently PPT
> adjustments for these models appear to apply without a custom fan curve
> while manufacturer intends for the TDP to only be adjustable with a
> custom fan curve applied. To clarify, for the modules that require a
> custom fan curve, TDP value adjustments are currently being completely
> ignored by the BIOS because the requirement for the existence of a fan
> curve prevents it at a hardware level. Meanwhile on the kernel side of
> things, it would appear that TDP values are actually being set, while it
> is actually ignored. This is the reason for my gating custom TDP values
> behind requires_fan_curve for models that require it. Please correct me
> if i am understanding the previous message incorrectly though.
I wasn't specifically talking about manufacturer intent, but more
trying to get to a standard way for userspace to interface with PPT
values in the Linux kernel. If each implementation requires special
handling then we have essentially failed at our job of abstracting the
hardware to userspace. Even in the case of just ASUS where having some
devices where the user needs to know to set this bool while others
don't, and then having userspace reflect that difference, is an extra
burden on users and developers. Once you add more manufacturers, some
needing custom while others don't, some with different limits per
profile, etc. it then becomes the job of userspace to abstract the
vendors & models. IMO it is much cleaner to always gate PPT values for
all hardware behind custom to provide an identical interface and
expectation to users, then you can do vendor & model specific things
(like this bool, or Lenovo's custom profile) behind that without the
user even needing to know.
> As for the other issue mentioned (if a user were to choose low power and
> set maximum SPL), this is an issue I was not able to properly recreate.
> I have attempted to do this but on my device at least, a custom TDP
> cannot be set without a custom fan curve. Even in low power mode, I am
> able to ramp my fans up to 100% with a custom fan curve and increasing
> TDP knobs works perfectly and my fans are loud as expected. It may be
> the case that due to the bug I mentioned earlier, TDP was possible to
> visually max out without a custom fan curve while being in quiet mode.
In this case I was talking about the inverse behavior, where devices
that don't require a fan curve can adjust TDP without setting a custom
fan curve.
> However, on an older device that doesn't require a custom fan curve for
> TDP might behave as Derek mentioned, as there is no hardware level
> enforcement for the user to take over the fan curve before changing TDP.
> I am unable to test this as I do not have such a device, but I believe
> this is how the device's design is. The user can simply use custom fan
> curves if their device supports it in this case. Instead, there are
> min-max power limits for each power profile defined for these to prevent
> damage.
If I remember correctly, they will continue to use the fan curve of
whatever the last set platform profile is regardless of PPT setting.
That would mean that someone could in theory select the low-power
profile and have super silent fans, but ramp up PPT values to the
highest supported limits beyond what that fan profile supports. My
concern is whether or not the BIOS will reject the TDP setting above
the selected profile, automatically switch fan profiles, or enter the
scenario I outlined.
> As for the proposal to implement a 'custom' power profile, I believe
> this needs more discussion. Unlike Lenovo, ASUS does not have this as a
> native option in the BIOS, Instead ASUS ships 4 fan modes (Quiet,
> Balanced, Performance, Custom) which we are already using. The long term
> plan I have in mind for this is that the custom fan curve can be applied
> separately to all 3 performance profiles, saved somewhere, and switched
> between them by the user.
I understand that. The idea/issue I'm pressing is the need to avoid
implementing vendor specific quirks into an interface we want to be
standardized. I.E., how do we accomplish a "custom" profile when WMI
allows us to set custom values, whether or not its on a device with a
custom setting in BIOS. I think that is a fairly straightforward
objective.
> So while I can see a lot of things becoming cleaner if a custom fan mode
> mapped directly to a custom power profile, too many issues come together
> with it. For instance, would this new custom mode use Performance,
> Balanced, or Quiet as a baseline? what about the TDP value limits that
> differ per profile according to Asus Armoury Crate in Widnows? it also
Performance would be the safe option. The more complicated option is
to have the driver know where those limit transistions are and
automatically adjust the underlying platform profile before setting
the PPT values. IMO that would also be necessary if we explicitly
don't make a custom profile as they're currently not protected from
going outside the AC limits at all. I don't believe those are built
into the BIOS but are software limits in AC. Since ASUS controls the
drivers and userspace entirely in Windows they can enforce those
limits and have safe operation. It isn't an analogous situation.
> causes more complications for some users that only decrease TDP to
> reduce power consumption as they will then also be switched to a custom
> mode by the kernel which would now apply a custom fan curve which also
> needs us to create defaults for these. In the case of Lenovo, the BIOS
Default fan curves should be much easier than TDP limits, which are
already done in asus_armoury.h, since it would just be a fan % at a
temp. You could easily have an especially conservative/safe profile to
start with and add a DMI table later if it was found to be necessary.
Userspace will then take care of modifying it.
> likely ships with these baselines per device but this is not applicable
> in our case.
>
> If we can discuss this custom mode in more depth and the pros outweigh
> the cons, I believe we can discuss this for a follow-up patch
My concern with that approach is the lack of ABI stability. Once we
make a unique firmware-attribute for userspace to target we then break
userspace to "fix" it in the future. This decreases the likelihood
that such a change can be made in the future, and I think the fact
that some ASUS devices need some form of custom setting is exactly the
catalyst we need to fix it properly now. If we wait we fall into the
trap of needing to deprecate an ABI, maintain it until the next LTS
after acceptance, etc.
- Derek
> - Ahmed Yaseen
>
> >
> >> The requires_fan_curve flag has existed in the per-model power_data
> >> entries for some time but was never read. This series 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 -ENODEV with a pr_warn() from the PPT write path on
> >> affected models when no fan curve is active.
> >>
> >> Patch 2: Exposes the same flag to userspace as a read-only sysfs
> >> attribute (requires_fan_curve) so tools like asusctl and rogcc
> >> can surface the prerequisite to the user before issuing PPT
> >> writes. Documented in
> >> Documentation/ABI/testing/sysfs-class-firmware-attributes.
> >>
> >> Testing:
> >> Verified on G835LW (ROG Strix SCAR 18 2025, requires_fan_curve=true):
> >> - With no fan curve active, writes to ppt_pl1_spl return -ENODEV
> >> 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 fires exactly once per rejected write.
> >>
> >> I do not have access to the other 27 affected models. Testers from
> >> any of these would be appreciated: FX507VI, FX507VV, FX507Z,
> >> GA402X, GA403UI, GA403UV, GA403WM, GA403WR, GA403WW, GA605W,
> >> GU605CR, GU605CW, GU605CX, GU605M, G513I, G513QM, G513QY, G513R,
> >> G614J, G615LR, G634J, G713PV, G733C, G733P, G814J, G834J, G835LR.
> >>
> >> Ahmed Yaseen (2):
> >> platform/x86: asus-armoury: gate PPT writes behind active fan curve
> >> platform/x86: asus-armoury: expose requires_fan_curve via sysfs
> >>
> >> .../testing/sysfs-class-firmware-attributes | 25 +++++++++++++
> >> drivers/platform/x86/asus-armoury.c | 35 +++++++++++++++++++
> >> drivers/platform/x86/asus-wmi.c | 28 +++++++++++++++
> >> include/linux/platform_data/x86/asus-wmi.h | 5 +++
> >> 4 files changed, 93 insertions(+)
> >>
> >
>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-05-19 16:05 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-11 6:19 [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Ahmed Yaseen
2026-05-11 6:19 ` [PATCH 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
2026-05-12 15:15 ` Derek J. Clark
2026-05-11 6:19 ` [PATCH 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
2026-05-12 15:14 ` [PATCH 0/2] asus-armoury: gate PPT writes on active fan curves Derek J. Clark
2026-05-13 14:54 ` Ahmed Yaseen
2026-05-19 16:05 ` Derek John Clark
2026-05-13 15:47 ` [PATCH v2 " Ahmed Yaseen
2026-05-13 15:47 ` [PATCH v2 1/2] platform/x86: asus-armoury: gate PPT writes behind active fan curve Ahmed Yaseen
2026-05-16 21:15 ` Mario Limonciello
2026-05-13 15:47 ` [PATCH v2 2/2] platform/x86: asus-armoury: expose requires_fan_curve via sysfs Ahmed Yaseen
2026-05-16 21:17 ` Mario Limonciello
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®