This patch was already picked up for 4.9, but it can help a lot of users in the meantime. Signed-off-by: Marcos Paulo de Souza <marcos.souza.org@xxxxxxxxx> --- PatchList.txt | 1 + i8042-skip-selftest-asus-laptops.patch | 373 +++++++++++++++++++++++++++++++++ kernel.spec | 2 + 3 files changed, 376 insertions(+) create mode 100644 i8042-skip-selftest-asus-laptops.patch diff --git a/PatchList.txt b/PatchList.txt index 3a7fd73..3cce44a 100644 --- a/PatchList.txt +++ b/PatchList.txt @@ -76,3 +76,4 @@ firmware-Drop-WARN-from-usermodehelper_read_trylock-.patch drm-i915-turn-off-wc-mmaps.patch +i8042-skip-selftest-asus-laptops.patch diff --git a/i8042-skip-selftest-asus-laptops.patch b/i8042-skip-selftest-asus-laptops.patch new file mode 100644 index 0000000..351556e --- /dev/null +++ b/i8042-skip-selftest-asus-laptops.patch @@ -0,0 +1,373 @@ +From 930e19248e9b61da36c967687ca79c4d5f977919 Mon Sep 17 00:00:00 2001 +From: Marcos Paulo de Souza <marcos.souza.org@xxxxxxxxx> +Date: Sat, 1 Oct 2016 12:07:35 -0700 +Subject: Input: i8042 - skip selftest on ASUS laptops + +On suspend/resume cycle, selftest is executed to reset i8042 controller. +But when this is done in Asus devices, subsequent calls to detect/init +functions to elantech driver fails. Skipping selftest fixes this problem. + +An easier step to reproduce this problem is adding i8042.reset=1 as a +kernel parameter. On Asus laptops, it'll make the system to start with the +touchpad already stuck, since psmouse_probe forcibly calls the selftest +function. + +This patch was inspired by John Hiesey's change[1], but, since this problem +affects a lot of models of Asus, let's avoid running selftests on them. + +All models affected by this problem: +A455LD +K401LB +K501LB +K501LX +R409L +V502LX +X302LA +X450LCP +X450LD +X455LAB +X455LDB +X455LF +Z450LA + +[1]: https://marc.info/?l=linux-input&m=144312209020616&w=2 + +Fixes: "ETPS/2 Elantech Touchpad dies after resume from suspend" +(https://bugzilla.kernel.org/show_bug.cgi?id=107971) + +Signed-off-by: Marcos Paulo de Souza <marcos.souza.org@xxxxxxxxx> +Cc: stable@xxxxxxxxxxxxxxx +Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> +--- + Documentation/kernel-parameters.txt | 9 +++- + drivers/input/serio/i8042-io.h | 2 +- + drivers/input/serio/i8042-ip22io.h | 2 +- + drivers/input/serio/i8042-ppcio.h | 2 +- + drivers/input/serio/i8042-sparcio.h | 2 +- + drivers/input/serio/i8042-unicore32io.h | 2 +- + drivers/input/serio/i8042-x86ia64io.h | 96 +++++++++++++++++++++++++++++++-- + drivers/input/serio/i8042.c | 55 +++++++++++++++---- + 8 files changed, 150 insertions(+), 20 deletions(-) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 0b3de80..3475b32 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -1409,7 +1409,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX + controllers + i8042.notimeout [HW] Ignore timeout condition signalled by controller +- i8042.reset [HW] Reset the controller during init and cleanup ++ i8042.reset [HW] Reset the controller during init, cleanup and ++ suspend-to-ram transitions, only during s2r ++ transitions, or never reset ++ Format: { 1 | Y | y | 0 | N | n } ++ 1, Y, y: always reset controller ++ 0, N, n: don't ever reset controller ++ Default: only on s2r transitions on x86; most other ++ architectures force reset to be always executed + i8042.unlock [HW] Unlock (ignore) the keylock + i8042.kbdreset [HW] Reset device connected to KBD port + +diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h +index a5eed2a..34da81c 100644 +--- a/drivers/input/serio/i8042-io.h ++++ b/drivers/input/serio/i8042-io.h +@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void) + return -EBUSY; + #endif + +- i8042_reset = 1; ++ i8042_reset = I8042_RESET_ALWAYS; + return 0; + } + +diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h +index ee1ad27..08a1c10 100644 +--- a/drivers/input/serio/i8042-ip22io.h ++++ b/drivers/input/serio/i8042-ip22io.h +@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) + return -EBUSY; + #endif + +- i8042_reset = 1; ++ i8042_reset = I8042_RESET_ALWAYS; + + return 0; + } +diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h +index f708c75..1aabea4 100644 +--- a/drivers/input/serio/i8042-ppcio.h ++++ b/drivers/input/serio/i8042-ppcio.h +@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val) + + static inline int i8042_platform_init(void) + { +- i8042_reset = 1; ++ i8042_reset = I8042_RESET_ALWAYS; + return 0; + } + +diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h +index afcd1c1..6231d63 100644 +--- a/drivers/input/serio/i8042-sparcio.h ++++ b/drivers/input/serio/i8042-sparcio.h +@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void) + } + } + +- i8042_reset = 1; ++ i8042_reset = I8042_RESET_ALWAYS; + + return 0; + } +diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h +index 73f5cc1..4557475 100644 +--- a/drivers/input/serio/i8042-unicore32io.h ++++ b/drivers/input/serio/i8042-unicore32io.h +@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) + if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) + return -EBUSY; + +- i8042_reset = 1; ++ i8042_reset = I8042_RESET_ALWAYS; + return 0; + } + +diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h +index 68f5f4a..f4bfb4b 100644 +--- a/drivers/input/serio/i8042-x86ia64io.h ++++ b/drivers/input/serio/i8042-x86ia64io.h +@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { + { } + }; + ++/* ++ * On some Asus laptops, just running self tests cause problems. ++ */ ++static const struct dmi_system_id i8042_dmi_noselftest_table[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "R409L"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"), ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"), ++ }, ++ }, ++ { } ++}; + static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { + { + /* MSI Wind U-100 */ +@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void) + return retval; + + #if defined(__ia64__) +- i8042_reset = true; ++ i8042_reset = I8042_RESET_ALWAYS; + #endif + + #ifdef CONFIG_X86 +- if (dmi_check_system(i8042_dmi_reset_table)) +- i8042_reset = true; ++ /* Honor module parameter when value is not default */ ++ if (i8042_reset == I8042_RESET_DEFAULT) { ++ if (dmi_check_system(i8042_dmi_reset_table)) ++ i8042_reset = I8042_RESET_ALWAYS; ++ ++ if (dmi_check_system(i8042_dmi_noselftest_table)) ++ i8042_reset = I8042_RESET_NEVER; ++ } + + if (dmi_check_system(i8042_dmi_noloop_table)) + i8042_noloop = true; +diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c +index b4d3408..674a760 100644 +--- a/drivers/input/serio/i8042.c ++++ b/drivers/input/serio/i8042.c +@@ -48,9 +48,39 @@ static bool i8042_unlock; + module_param_named(unlock, i8042_unlock, bool, 0); + MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); + +-static bool i8042_reset; +-module_param_named(reset, i8042_reset, bool, 0); +-MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); ++enum i8042_controller_reset_mode { ++ I8042_RESET_NEVER, ++ I8042_RESET_ALWAYS, ++ I8042_RESET_ON_S2RAM, ++#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM ++}; ++static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; ++static int i8042_set_reset(const char *val, const struct kernel_param *kp) ++{ ++ enum i8042_controller_reset_mode *arg = kp->arg; ++ int error; ++ bool reset; ++ ++ if (val) { ++ error = kstrtobool(val, &reset); ++ if (error) ++ return error; ++ } else { ++ reset = true; ++ } ++ ++ *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; ++ return 0; ++} ++ ++static const struct kernel_param_ops param_ops_reset_param = { ++ .flags = KERNEL_PARAM_OPS_FL_NOARG, ++ .set = i8042_set_reset, ++}; ++#define param_check_reset_param(name, p) \ ++ __param_check(name, p, enum i8042_controller_reset_mode) ++module_param_named(reset, i8042_reset, reset_param, 0); ++MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); + + static bool i8042_direct; + module_param_named(direct, i8042_direct, bool, 0); +@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void) + * Reset the controller and reset CRT to the original value set by BIOS. + */ + +-static void i8042_controller_reset(bool force_reset) ++static void i8042_controller_reset(bool s2r_wants_reset) + { + i8042_flush(); + +@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset) + * Reset the controller if requested. + */ + +- if (i8042_reset || force_reset) ++ if (i8042_reset == I8042_RESET_ALWAYS || ++ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { + i8042_controller_selftest(); ++ } + + /* + * Restore the original control register setting. +@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void) + * before suspending. + */ + +-static int i8042_controller_resume(bool force_reset) ++static int i8042_controller_resume(bool s2r_wants_reset) + { + int error; + +@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset) + if (error) + return error; + +- if (i8042_reset || force_reset) { ++ if (i8042_reset == I8042_RESET_ALWAYS || ++ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { + error = i8042_controller_selftest(); + if (error) + return error; +@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev) + + static int i8042_pm_resume(struct device *dev) + { +- bool force_reset; ++ bool want_reset; + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { +@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev) + * off control to the platform firmware, otherwise we can simply restore + * the mode. + */ +- force_reset = pm_resume_via_firmware(); ++ want_reset = pm_resume_via_firmware(); + +- return i8042_controller_resume(force_reset); ++ return i8042_controller_resume(want_reset); + } + + static int i8042_pm_thaw(struct device *dev) +@@ -1481,7 +1514,7 @@ static int __init i8042_probe(struct platform_device *dev) + + i8042_platform_device = dev; + +- if (i8042_reset) { ++ if (i8042_reset == I8042_RESET_ALWAYS) { + error = i8042_controller_selftest(); + if (error) + return error; +-- +cgit v0.12 + diff --git a/kernel.spec b/kernel.spec index b54e34f..00ffcd9 100644 --- a/kernel.spec +++ b/kernel.spec @@ -593,6 +593,8 @@ Patch502: firmware-Drop-WARN-from-usermodehelper_read_trylock-.patch # Patch503: drm-i915-turn-off-wc-mmaps.patch +Patch504: i8042-skip-selftest-asus-laptops.patch + Patch508: kexec-uefi-copy-secure_boot-flag-in-boot-params.patch #CVE-2016-3134 rhbz 1317383 1317384 -- 2.7.4 _______________________________________________ kernel mailing list -- kernel@xxxxxxxxxxxxxxxxxxxxxxx To unsubscribe send an email to kernel-leave@xxxxxxxxxxxxxxxxxxxxxxx