diff options
author | Marcos Paulo de Souza <marcos.souza.org@gmail.com> | 2016-10-01 15:07:35 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2016-10-11 19:58:25 -0400 |
commit | 930e19248e9b61da36c967687ca79c4d5f977919 (patch) | |
tree | 40a11f8280090ffe6e5cc97b8283f6458d48874d /drivers/input/serio | |
parent | 4e1bff07d7e7bc9fc3fc62100dcb8f708e1e96d2 (diff) |
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@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/serio')
-rw-r--r-- | drivers/input/serio/i8042-io.h | 2 | ||||
-rw-r--r-- | drivers/input/serio/i8042-ip22io.h | 2 | ||||
-rw-r--r-- | drivers/input/serio/i8042-ppcio.h | 2 | ||||
-rw-r--r-- | drivers/input/serio/i8042-sparcio.h | 2 | ||||
-rw-r--r-- | drivers/input/serio/i8042-unicore32io.h | 2 | ||||
-rw-r--r-- | drivers/input/serio/i8042-x86ia64io.h | 96 | ||||
-rw-r--r-- | drivers/input/serio/i8042.c | 55 |
7 files changed, 142 insertions, 19 deletions
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index a5eed2ade53d..34da81c006b6 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) | |||
81 | return -EBUSY; | 81 | return -EBUSY; |
82 | #endif | 82 | #endif |
83 | 83 | ||
84 | i8042_reset = 1; | 84 | i8042_reset = I8042_RESET_ALWAYS; |
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | 87 | ||
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h index ee1ad27d6ed0..08a1c10a1448 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) | |||
61 | return -EBUSY; | 61 | return -EBUSY; |
62 | #endif | 62 | #endif |
63 | 63 | ||
64 | i8042_reset = 1; | 64 | i8042_reset = I8042_RESET_ALWAYS; |
65 | 65 | ||
66 | return 0; | 66 | return 0; |
67 | } | 67 | } |
diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index f708c75d16f1..1aabea43329e 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) | |||
44 | 44 | ||
45 | static inline int i8042_platform_init(void) | 45 | static inline int i8042_platform_init(void) |
46 | { | 46 | { |
47 | i8042_reset = 1; | 47 | i8042_reset = I8042_RESET_ALWAYS; |
48 | return 0; | 48 | return 0; |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index afcd1c1a05b2..6231d63860ee 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) | |||
130 | } | 130 | } |
131 | } | 131 | } |
132 | 132 | ||
133 | i8042_reset = 1; | 133 | i8042_reset = I8042_RESET_ALWAYS; |
134 | 134 | ||
135 | return 0; | 135 | return 0; |
136 | } | 136 | } |
diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h index 73f5cc124a36..455747552f85 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) | |||
61 | if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) | 61 | if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) |
62 | return -EBUSY; | 62 | return -EBUSY; |
63 | 63 | ||
64 | i8042_reset = 1; | 64 | i8042_reset = I8042_RESET_ALWAYS; |
65 | return 0; | 65 | return 0; |
66 | } | 66 | } |
67 | 67 | ||
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 68f5f4a0f1e7..f4bfb4b2d50a 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[] = { | |||
510 | { } | 510 | { } |
511 | }; | 511 | }; |
512 | 512 | ||
513 | /* | ||
514 | * On some Asus laptops, just running self tests cause problems. | ||
515 | */ | ||
516 | static const struct dmi_system_id i8042_dmi_noselftest_table[] = { | ||
517 | { | ||
518 | .matches = { | ||
519 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
520 | DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"), | ||
521 | }, | ||
522 | }, | ||
523 | { | ||
524 | .matches = { | ||
525 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
526 | DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"), | ||
527 | }, | ||
528 | }, | ||
529 | { | ||
530 | .matches = { | ||
531 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
532 | DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"), | ||
533 | }, | ||
534 | }, | ||
535 | { | ||
536 | .matches = { | ||
537 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
538 | DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"), | ||
539 | }, | ||
540 | }, | ||
541 | { | ||
542 | .matches = { | ||
543 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
544 | DMI_MATCH(DMI_PRODUCT_NAME, "R409L"), | ||
545 | }, | ||
546 | }, | ||
547 | { | ||
548 | .matches = { | ||
549 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
550 | DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"), | ||
551 | }, | ||
552 | }, | ||
553 | { | ||
554 | .matches = { | ||
555 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
556 | DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"), | ||
557 | }, | ||
558 | }, | ||
559 | { | ||
560 | .matches = { | ||
561 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
562 | DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), | ||
563 | }, | ||
564 | }, | ||
565 | { | ||
566 | .matches = { | ||
567 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
568 | DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"), | ||
569 | }, | ||
570 | }, | ||
571 | { | ||
572 | .matches = { | ||
573 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
574 | DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"), | ||
575 | }, | ||
576 | }, | ||
577 | { | ||
578 | .matches = { | ||
579 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
580 | DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"), | ||
581 | }, | ||
582 | }, | ||
583 | { | ||
584 | .matches = { | ||
585 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
586 | DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"), | ||
587 | }, | ||
588 | }, | ||
589 | { | ||
590 | .matches = { | ||
591 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
592 | DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"), | ||
593 | }, | ||
594 | }, | ||
595 | { } | ||
596 | }; | ||
513 | static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { | 597 | static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { |
514 | { | 598 | { |
515 | /* MSI Wind U-100 */ | 599 | /* MSI Wind U-100 */ |
@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void) | |||
1072 | return retval; | 1156 | return retval; |
1073 | 1157 | ||
1074 | #if defined(__ia64__) | 1158 | #if defined(__ia64__) |
1075 | i8042_reset = true; | 1159 | i8042_reset = I8042_RESET_ALWAYS; |
1076 | #endif | 1160 | #endif |
1077 | 1161 | ||
1078 | #ifdef CONFIG_X86 | 1162 | #ifdef CONFIG_X86 |
1079 | if (dmi_check_system(i8042_dmi_reset_table)) | 1163 | /* Honor module parameter when value is not default */ |
1080 | i8042_reset = true; | 1164 | if (i8042_reset == I8042_RESET_DEFAULT) { |
1165 | if (dmi_check_system(i8042_dmi_reset_table)) | ||
1166 | i8042_reset = I8042_RESET_ALWAYS; | ||
1167 | |||
1168 | if (dmi_check_system(i8042_dmi_noselftest_table)) | ||
1169 | i8042_reset = I8042_RESET_NEVER; | ||
1170 | } | ||
1081 | 1171 | ||
1082 | if (dmi_check_system(i8042_dmi_noloop_table)) | 1172 | if (dmi_check_system(i8042_dmi_noloop_table)) |
1083 | i8042_noloop = true; | 1173 | i8042_noloop = true; |
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index b4d34086e73f..674a760f5221 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c | |||
@@ -48,9 +48,39 @@ static bool i8042_unlock; | |||
48 | module_param_named(unlock, i8042_unlock, bool, 0); | 48 | module_param_named(unlock, i8042_unlock, bool, 0); |
49 | MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); | 49 | MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); |
50 | 50 | ||
51 | static bool i8042_reset; | 51 | enum i8042_controller_reset_mode { |
52 | module_param_named(reset, i8042_reset, bool, 0); | 52 | I8042_RESET_NEVER, |
53 | MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); | 53 | I8042_RESET_ALWAYS, |
54 | I8042_RESET_ON_S2RAM, | ||
55 | #define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM | ||
56 | }; | ||
57 | static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; | ||
58 | static int i8042_set_reset(const char *val, const struct kernel_param *kp) | ||
59 | { | ||
60 | enum i8042_controller_reset_mode *arg = kp->arg; | ||
61 | int error; | ||
62 | bool reset; | ||
63 | |||
64 | if (val) { | ||
65 | error = kstrtobool(val, &reset); | ||
66 | if (error) | ||
67 | return error; | ||
68 | } else { | ||
69 | reset = true; | ||
70 | } | ||
71 | |||
72 | *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static const struct kernel_param_ops param_ops_reset_param = { | ||
77 | .flags = KERNEL_PARAM_OPS_FL_NOARG, | ||
78 | .set = i8042_set_reset, | ||
79 | }; | ||
80 | #define param_check_reset_param(name, p) \ | ||
81 | __param_check(name, p, enum i8042_controller_reset_mode) | ||
82 | module_param_named(reset, i8042_reset, reset_param, 0); | ||
83 | MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); | ||
54 | 84 | ||
55 | static bool i8042_direct; | 85 | static bool i8042_direct; |
56 | module_param_named(direct, i8042_direct, bool, 0); | 86 | module_param_named(direct, i8042_direct, bool, 0); |
@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void) | |||
1019 | * Reset the controller and reset CRT to the original value set by BIOS. | 1049 | * Reset the controller and reset CRT to the original value set by BIOS. |
1020 | */ | 1050 | */ |
1021 | 1051 | ||
1022 | static void i8042_controller_reset(bool force_reset) | 1052 | static void i8042_controller_reset(bool s2r_wants_reset) |
1023 | { | 1053 | { |
1024 | i8042_flush(); | 1054 | i8042_flush(); |
1025 | 1055 | ||
@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset) | |||
1044 | * Reset the controller if requested. | 1074 | * Reset the controller if requested. |
1045 | */ | 1075 | */ |
1046 | 1076 | ||
1047 | if (i8042_reset || force_reset) | 1077 | if (i8042_reset == I8042_RESET_ALWAYS || |
1078 | (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { | ||
1048 | i8042_controller_selftest(); | 1079 | i8042_controller_selftest(); |
1080 | } | ||
1049 | 1081 | ||
1050 | /* | 1082 | /* |
1051 | * Restore the original control register setting. | 1083 | * Restore the original control register setting. |
@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void) | |||
1110 | * before suspending. | 1142 | * before suspending. |
1111 | */ | 1143 | */ |
1112 | 1144 | ||
1113 | static int i8042_controller_resume(bool force_reset) | 1145 | static int i8042_controller_resume(bool s2r_wants_reset) |
1114 | { | 1146 | { |
1115 | int error; | 1147 | int error; |
1116 | 1148 | ||
@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset) | |||
1118 | if (error) | 1150 | if (error) |
1119 | return error; | 1151 | return error; |
1120 | 1152 | ||
1121 | if (i8042_reset || force_reset) { | 1153 | if (i8042_reset == I8042_RESET_ALWAYS || |
1154 | (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { | ||
1122 | error = i8042_controller_selftest(); | 1155 | error = i8042_controller_selftest(); |
1123 | if (error) | 1156 | if (error) |
1124 | return error; | 1157 | return error; |
@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev) | |||
1195 | 1228 | ||
1196 | static int i8042_pm_resume(struct device *dev) | 1229 | static int i8042_pm_resume(struct device *dev) |
1197 | { | 1230 | { |
1198 | bool force_reset; | 1231 | bool want_reset; |
1199 | int i; | 1232 | int i; |
1200 | 1233 | ||
1201 | for (i = 0; i < I8042_NUM_PORTS; i++) { | 1234 | for (i = 0; i < I8042_NUM_PORTS; i++) { |
@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev) | |||
1218 | * off control to the platform firmware, otherwise we can simply restore | 1251 | * off control to the platform firmware, otherwise we can simply restore |
1219 | * the mode. | 1252 | * the mode. |
1220 | */ | 1253 | */ |
1221 | force_reset = pm_resume_via_firmware(); | 1254 | want_reset = pm_resume_via_firmware(); |
1222 | 1255 | ||
1223 | return i8042_controller_resume(force_reset); | 1256 | return i8042_controller_resume(want_reset); |
1224 | } | 1257 | } |
1225 | 1258 | ||
1226 | static int i8042_pm_thaw(struct device *dev) | 1259 | static int i8042_pm_thaw(struct device *dev) |
@@ -1481,7 +1514,7 @@ static int __init i8042_probe(struct platform_device *dev) | |||
1481 | 1514 | ||
1482 | i8042_platform_device = dev; | 1515 | i8042_platform_device = dev; |
1483 | 1516 | ||
1484 | if (i8042_reset) { | 1517 | if (i8042_reset == I8042_RESET_ALWAYS) { |
1485 | error = i8042_controller_selftest(); | 1518 | error = i8042_controller_selftest(); |
1486 | if (error) | 1519 | if (error) |
1487 | return error; | 1520 | return error; |