Message ID | 20250226010129.32043-3-luke@ljones.dev (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | hid-asus: asus-wmi: refactor Ally suspend/resume | expand |
On 2/25/2025 17:01, Luke Jones wrote: > From: "Luke D. Jones" <luke@ljones.dev> > > Adjust how the CSEE direct call hack is used. > > The results of months of testing combined with help from ASUS to > determine the actual cause of suspend issues has resulted in this > refactoring which immensely improves the reliability for devices which > do not have the following minimum MCU FW version: > - ROG Ally X: 313 > - ROG Ally 1: 319 > > For MCU FW versions that match the minimum or above the CSEE hack is > disabled and mcu_powersave set to on by default as there are no > negatives beyond a slightly slower device reinitialization due to the > MCU being powered off. > > As this is set only at module load time, it is still possible for > mcu_powersave sysfs attributes to change it at runtime if so desired. > > Signed-off-by: Luke D. Jones <luke@ljones.dev> > --- > drivers/hid/hid-asus.c | 4 + > drivers/platform/x86/asus-wmi.c | 124 ++++++++++++++------- > include/linux/platform_data/x86/asus-wmi.h | 15 +++ > 3 files changed, 104 insertions(+), 39 deletions(-) > > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c > index 3cec622b6e68..c6b94f3d0fd9 100644 > --- a/drivers/hid/hid-asus.c > +++ b/drivers/hid/hid-asus.c > @@ -620,6 +620,9 @@ static void validate_mcu_fw_version(struct hid_device *hdev, int idProduct) > hid_warn(hdev, > "The MCU firmware version must be %d or greater to avoid issues with suspend.\n", > min_version); > + } else { > + set_ally_mcu_hack(false); > + set_ally_mcu_powersave(true); > } > } > > @@ -1426,4 +1429,5 @@ static struct hid_driver asus_driver = { > }; > module_hid_driver(asus_driver); > > +MODULE_IMPORT_NS("ASUS_WMI"); > MODULE_LICENSE("GPL"); > diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c > index 38ef778e8c19..9dba88a29e2c 100644 > --- a/drivers/platform/x86/asus-wmi.c > +++ b/drivers/platform/x86/asus-wmi.c > @@ -142,16 +142,20 @@ module_param(fnlock_default, bool, 0444); > #define ASUS_MINI_LED_2024_STRONG 0x01 > #define ASUS_MINI_LED_2024_OFF 0x02 > > -/* Controls the power state of the USB0 hub on ROG Ally which input is on */ > #define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE" > -/* 300ms so far seems to produce a reliable result on AC and battery */ > -#define ASUS_USB0_PWR_EC0_CSEE_WAIT 1500 > +/* > + * The period required to wait after screen off/on/s2idle.check in MS. > + * Time here greatly impacts the wake behaviour. Used in suspend/wake. > + */ > +#define ASUS_USB0_PWR_EC0_CSEE_WAIT 600 > +#define ASUS_USB0_PWR_EC0_CSEE_OFF 0xB7 > +#define ASUS_USB0_PWR_EC0_CSEE_ON 0xB8 > > static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; > > static int throttle_thermal_policy_write(struct asus_wmi *); > > -static const struct dmi_system_id asus_ally_mcu_quirk[] = { > +static const struct dmi_system_id asus_rog_ally_device[] = { > { > .matches = { > DMI_MATCH(DMI_BOARD_NAME, "RC71L"), > @@ -274,9 +278,6 @@ struct asus_wmi { > u32 tablet_switch_dev_id; > bool tablet_switch_inverted; > > - /* The ROG Ally device requires the MCU USB device be disconnected before suspend */ > - bool ally_mcu_usb_switch; > - > enum fan_type fan_type; > enum fan_type gpu_fan_type; > enum fan_type mid_fan_type; > @@ -335,6 +336,9 @@ struct asus_wmi { > struct asus_wmi_driver *driver; > }; > > +/* Global to allow setting externally without requiring driver data */ > +static bool use_ally_mcu_hack; > + > /* WMI ************************************************************************/ > > static int asus_wmi_evaluate_method3(u32 method_id, > @@ -549,7 +553,7 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) > return 0; > } > > -static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, > +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, > u32 *retval) > { > return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, > @@ -1343,6 +1347,38 @@ static ssize_t nv_temp_target_show(struct device *dev, > static DEVICE_ATTR_RW(nv_temp_target); > > /* Ally MCU Powersave ********************************************************/ > + > +/* > + * The HID driver needs to check MCU version and set this to false if the MCU FW > + * version is >= the minimum requirements. New FW do not need the hacks. > + */ > +void set_ally_mcu_hack(bool enabled) > +{ > + use_ally_mcu_hack = enabled; > + pr_info("Disabled Ally MCU suspend quirks"); Shouldn't this message change when set_ally_mcu_hack() is called with different values? Also is pr_info() the right level? Or should be this be pr_debug()? > +} > +EXPORT_SYMBOL_NS_GPL(set_ally_mcu_hack, "ASUS_WMI"); > + > +/* > + * mcu_powersave should be enabled always, as it is fixed in MCU FW versions: > + * - v313 for Ally X > + * - v319 for Ally 1 > + * The HID driver checks MCU versions and so should set this if requirements match > + */ > +void set_ally_mcu_powersave(bool enabled) > +{ > + int result, err; > + > + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, enabled, &result); > + if (err) > + pr_warn("Failed to set MCU powersave: %d\n", err); > + if (result > 1) > + pr_warn("Failed to set MCU powersave (result): 0x%x\n", result); Don't you want to add some return statements for these two blocks? Otherwise you're goign to have pr_warn() saying it didn't work followed by pr_info() saying it did leading to confusion. > + > + pr_info("Set mcu_powersave to enabled"); This is a bit noisy, no? Is this better for pr_debug()? > +} > +EXPORT_SYMBOL_NS_GPL(set_ally_mcu_powersave, "ASUS_WMI"); > + > static ssize_t mcu_powersave_show(struct device *dev, > struct device_attribute *attr, char *buf) > { > @@ -4711,6 +4747,18 @@ static int asus_wmi_add(struct platform_device *pdev) > if (err) > goto fail_platform; > > + use_ally_mcu_hack = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) > + && dmi_check_system(asus_rog_ally_device); > + if (use_ally_mcu_hack && dmi_match(DMI_BOARD_NAME, "RC71")) { > + /* > + * These steps ensure the device is in a valid good state, this is > + * especially important for the Ally 1 after a reboot. > + */ > + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, > + ASUS_USB0_PWR_EC0_CSEE_ON); > + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); > + } > + > /* ensure defaults for tunables */ > asus->ppt_pl2_sppt = 5; > asus->ppt_pl1_spl = 5; > @@ -4723,8 +4771,6 @@ static int asus_wmi_add(struct platform_device *pdev) > asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU); > asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU); > asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); > - asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) > - && dmi_check_system(asus_ally_mcu_quirk); > > if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE)) > asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE; > @@ -4910,34 +4956,6 @@ static int asus_hotk_resume(struct device *device) > return 0; > } > > -static int asus_hotk_resume_early(struct device *device) > -{ > - struct asus_wmi *asus = dev_get_drvdata(device); > - > - if (asus->ally_mcu_usb_switch) { > - /* sleep required to prevent USB0 being yanked then reappearing rapidly */ > - if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8))) > - dev_err(device, "ROG Ally MCU failed to connect USB dev\n"); > - else > - msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); > - } > - return 0; > -} > - > -static int asus_hotk_prepare(struct device *device) > -{ > - struct asus_wmi *asus = dev_get_drvdata(device); > - > - if (asus->ally_mcu_usb_switch) { > - /* sleep required to ensure USB0 is disabled before sleep continues */ > - if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7))) > - dev_err(device, "ROG Ally MCU failed to disconnect USB dev\n"); > - else > - msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); > - } > - return 0; > -} > - > static int asus_hotk_restore(struct device *device) > { > struct asus_wmi *asus = dev_get_drvdata(device); > @@ -4978,11 +4996,34 @@ static int asus_hotk_restore(struct device *device) > return 0; > } > > +static void asus_ally_s2idle_restore(void) > +{ > + if (use_ally_mcu_hack) { > + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, > + ASUS_USB0_PWR_EC0_CSEE_ON); > + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); > + } > +} > + > +static int asus_hotk_prepare(struct device *device) > +{ > + if (use_ally_mcu_hack) { > + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, > + ASUS_USB0_PWR_EC0_CSEE_OFF); > + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); > + } > + return 0; > +} > + > +/* Use only for Ally devices due to the wake_on_ac */ > +static struct acpi_s2idle_dev_ops asus_ally_s2idle_dev_ops = { > + .restore = asus_ally_s2idle_restore, > +}; > + > static const struct dev_pm_ops asus_pm_ops = { > .thaw = asus_hotk_thaw, > .restore = asus_hotk_restore, > .resume = asus_hotk_resume, > - .resume_early = asus_hotk_resume_early, > .prepare = asus_hotk_prepare, > }; > > @@ -5010,6 +5051,10 @@ static int asus_wmi_probe(struct platform_device *pdev) > return ret; > } > > + ret = acpi_register_lps0_dev(&asus_ally_s2idle_dev_ops); > + if (ret) > + pr_warn("failed to register LPS0 sleep handler in asus-wmi\n"); > + > return asus_wmi_add(pdev); > } > > @@ -5042,6 +5087,7 @@ EXPORT_SYMBOL_GPL(asus_wmi_register_driver); > > void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) > { > + acpi_unregister_lps0_dev(&asus_ally_s2idle_dev_ops); > platform_device_unregister(driver->platform_device); > platform_driver_unregister(&driver->platform_driver); > used = false; > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h > index 783e2a336861..a32cb8865b2f 100644 > --- a/include/linux/platform_data/x86/asus-wmi.h > +++ b/include/linux/platform_data/x86/asus-wmi.h > @@ -158,8 +158,23 @@ > #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F > > #if IS_REACHABLE(CONFIG_ASUS_WMI) > +void set_ally_mcu_hack(bool enabled); > +void set_ally_mcu_powersave(bool enabled); > +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); > int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); > #else > +static inline void set_ally_mcu_hack(bool enabled) > +{ > + return -ENODEV; > +} > +static inline void set_ally_mcu_powersave(bool enabled) > +{ > + return -ENODEV; > +} > +static inline int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) > +{ > + return -ENODEV; > +} > static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, > u32 *retval) > {
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 3cec622b6e68..c6b94f3d0fd9 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -620,6 +620,9 @@ static void validate_mcu_fw_version(struct hid_device *hdev, int idProduct) hid_warn(hdev, "The MCU firmware version must be %d or greater to avoid issues with suspend.\n", min_version); + } else { + set_ally_mcu_hack(false); + set_ally_mcu_powersave(true); } } @@ -1426,4 +1429,5 @@ static struct hid_driver asus_driver = { }; module_hid_driver(asus_driver); +MODULE_IMPORT_NS("ASUS_WMI"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 38ef778e8c19..9dba88a29e2c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -142,16 +142,20 @@ module_param(fnlock_default, bool, 0444); #define ASUS_MINI_LED_2024_STRONG 0x01 #define ASUS_MINI_LED_2024_OFF 0x02 -/* Controls the power state of the USB0 hub on ROG Ally which input is on */ #define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE" -/* 300ms so far seems to produce a reliable result on AC and battery */ -#define ASUS_USB0_PWR_EC0_CSEE_WAIT 1500 +/* + * The period required to wait after screen off/on/s2idle.check in MS. + * Time here greatly impacts the wake behaviour. Used in suspend/wake. + */ +#define ASUS_USB0_PWR_EC0_CSEE_WAIT 600 +#define ASUS_USB0_PWR_EC0_CSEE_OFF 0xB7 +#define ASUS_USB0_PWR_EC0_CSEE_ON 0xB8 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static int throttle_thermal_policy_write(struct asus_wmi *); -static const struct dmi_system_id asus_ally_mcu_quirk[] = { +static const struct dmi_system_id asus_rog_ally_device[] = { { .matches = { DMI_MATCH(DMI_BOARD_NAME, "RC71L"), @@ -274,9 +278,6 @@ struct asus_wmi { u32 tablet_switch_dev_id; bool tablet_switch_inverted; - /* The ROG Ally device requires the MCU USB device be disconnected before suspend */ - bool ally_mcu_usb_switch; - enum fan_type fan_type; enum fan_type gpu_fan_type; enum fan_type mid_fan_type; @@ -335,6 +336,9 @@ struct asus_wmi { struct asus_wmi_driver *driver; }; +/* Global to allow setting externally without requiring driver data */ +static bool use_ally_mcu_hack; + /* WMI ************************************************************************/ static int asus_wmi_evaluate_method3(u32 method_id, @@ -549,7 +553,7 @@ static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) return 0; } -static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) { return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, @@ -1343,6 +1347,38 @@ static ssize_t nv_temp_target_show(struct device *dev, static DEVICE_ATTR_RW(nv_temp_target); /* Ally MCU Powersave ********************************************************/ + +/* + * The HID driver needs to check MCU version and set this to false if the MCU FW + * version is >= the minimum requirements. New FW do not need the hacks. + */ +void set_ally_mcu_hack(bool enabled) +{ + use_ally_mcu_hack = enabled; + pr_info("Disabled Ally MCU suspend quirks"); +} +EXPORT_SYMBOL_NS_GPL(set_ally_mcu_hack, "ASUS_WMI"); + +/* + * mcu_powersave should be enabled always, as it is fixed in MCU FW versions: + * - v313 for Ally X + * - v319 for Ally 1 + * The HID driver checks MCU versions and so should set this if requirements match + */ +void set_ally_mcu_powersave(bool enabled) +{ + int result, err; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, enabled, &result); + if (err) + pr_warn("Failed to set MCU powersave: %d\n", err); + if (result > 1) + pr_warn("Failed to set MCU powersave (result): 0x%x\n", result); + + pr_info("Set mcu_powersave to enabled"); +} +EXPORT_SYMBOL_NS_GPL(set_ally_mcu_powersave, "ASUS_WMI"); + static ssize_t mcu_powersave_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -4711,6 +4747,18 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + use_ally_mcu_hack = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) + && dmi_check_system(asus_rog_ally_device); + if (use_ally_mcu_hack && dmi_match(DMI_BOARD_NAME, "RC71")) { + /* + * These steps ensure the device is in a valid good state, this is + * especially important for the Ally 1 after a reboot. + */ + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, + ASUS_USB0_PWR_EC0_CSEE_ON); + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); + } + /* ensure defaults for tunables */ asus->ppt_pl2_sppt = 5; asus->ppt_pl1_spl = 5; @@ -4723,8 +4771,6 @@ static int asus_wmi_add(struct platform_device *pdev) asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU); asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU); asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); - asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) - && dmi_check_system(asus_ally_mcu_quirk); if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE)) asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE; @@ -4910,34 +4956,6 @@ static int asus_hotk_resume(struct device *device) return 0; } -static int asus_hotk_resume_early(struct device *device) -{ - struct asus_wmi *asus = dev_get_drvdata(device); - - if (asus->ally_mcu_usb_switch) { - /* sleep required to prevent USB0 being yanked then reappearing rapidly */ - if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8))) - dev_err(device, "ROG Ally MCU failed to connect USB dev\n"); - else - msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); - } - return 0; -} - -static int asus_hotk_prepare(struct device *device) -{ - struct asus_wmi *asus = dev_get_drvdata(device); - - if (asus->ally_mcu_usb_switch) { - /* sleep required to ensure USB0 is disabled before sleep continues */ - if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7))) - dev_err(device, "ROG Ally MCU failed to disconnect USB dev\n"); - else - msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); - } - return 0; -} - static int asus_hotk_restore(struct device *device) { struct asus_wmi *asus = dev_get_drvdata(device); @@ -4978,11 +4996,34 @@ static int asus_hotk_restore(struct device *device) return 0; } +static void asus_ally_s2idle_restore(void) +{ + if (use_ally_mcu_hack) { + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, + ASUS_USB0_PWR_EC0_CSEE_ON); + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); + } +} + +static int asus_hotk_prepare(struct device *device) +{ + if (use_ally_mcu_hack) { + acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, + ASUS_USB0_PWR_EC0_CSEE_OFF); + msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); + } + return 0; +} + +/* Use only for Ally devices due to the wake_on_ac */ +static struct acpi_s2idle_dev_ops asus_ally_s2idle_dev_ops = { + .restore = asus_ally_s2idle_restore, +}; + static const struct dev_pm_ops asus_pm_ops = { .thaw = asus_hotk_thaw, .restore = asus_hotk_restore, .resume = asus_hotk_resume, - .resume_early = asus_hotk_resume_early, .prepare = asus_hotk_prepare, }; @@ -5010,6 +5051,10 @@ static int asus_wmi_probe(struct platform_device *pdev) return ret; } + ret = acpi_register_lps0_dev(&asus_ally_s2idle_dev_ops); + if (ret) + pr_warn("failed to register LPS0 sleep handler in asus-wmi\n"); + return asus_wmi_add(pdev); } @@ -5042,6 +5087,7 @@ EXPORT_SYMBOL_GPL(asus_wmi_register_driver); void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) { + acpi_unregister_lps0_dev(&asus_ally_s2idle_dev_ops); platform_device_unregister(driver->platform_device); platform_driver_unregister(&driver->platform_driver); used = false; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 783e2a336861..a32cb8865b2f 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -158,8 +158,23 @@ #define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F #if IS_REACHABLE(CONFIG_ASUS_WMI) +void set_ally_mcu_hack(bool enabled); +void set_ally_mcu_powersave(bool enabled); +int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); #else +static inline void set_ally_mcu_hack(bool enabled) +{ + return -ENODEV; +} +static inline void set_ally_mcu_powersave(bool enabled) +{ + return -ENODEV; +} +static inline int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) +{ + return -ENODEV; +} static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) {