[ltp] Keyboard hang on resume from RAM

Daniel Stodden linux-thinkpad@linux-thinkpad.org
Sun, 03 Apr 2005 11:34:38 +0200


--=-r2CPI4ZasjhCzoH8WyQK
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

On Wed, 2005-03-16 at 22:04 +0100, Daniel Stodden wrote:
> hi all.
> 
> regarding a well-known problem described on
> http://mailman.linux-thinkpad.org/pipermail/linux-thinkpad/2005-February/023962.html
> 
> as well as referring to my previous mail from today:
> http://mailman.linux-thinkpad.org/pipermail/linux-thinkpad/2005-March/025090.html
> 
> problem: resume from RAM leaves the AT keyboard interface in a corrupted
> state. typical log entry:
> Mar 16 13:41:37 localhost kernel: atkbd.c: Keyboard on isa0060/serio0
> reports too many keys pressed.
> 
> the problem seems to be that many distributions configure acpid to
> access /proc/acpi/sleep directly. though, this does not take the current
> set of blacklisted modules, most importantly the usb host interfaces
> into account. that used to work for me at least until 2.6.9, iirc 2.6.10
> as well. before, i could keep uhci_hcd and ehci_hcd installed and resume
> from S3 without problems.
> 
> seems it finally ceased to work on 2.6.11, causing an early hang on
> resume.

okay, this is hopefully my last report regarding that issue.

after another 10 days of uptime with many happy suspends and resumes i
can say that 1) my original recipe described above is rubbish (sorry,
marc) and 2) the problem was a completely different one.

just adhering to the list of blacklisted modules more carefully seemed
to fix the issue for about three days. that turned out to be just by
coincidence. later, the machine showed the same behavior as before, from
time to time leaving me with a keyboard controller hang.

so, after messing around with the i8042 (AT keyboard and mouse) driver
parameters for a while, i've contacted vojtech pavlik, current
maintainer of the linux input core and surrounding driver hierarchy.
turned out he had a fix for that issue already around.

the problem is in the i8042 driver, up to including 2.6.11.x, probably
to be around up to 2.6.12. the resume function expects to be able to
read a chip control register just after chip resume, resulting in
corrupted data.

i've reassembled his original patch (patch-i8042.c-bk.diff) to match
2.6.11.4 (patch-i8042.c-2.6.11.4.diff), see the attachments below.

according to vojtech, this fix is "post 2.6.12". for people bound to
unpatched stock or distributor kernels for whichever reasons: passing
i8042.reset at boot time _may_ help, but probably won't. 
passing atkbd.reset as suggested on some other places on the web
definitely won't work. the problem is not in the AT keyboard driver, but
the controller driver below.

regards,
daniel

--=-r2CPI4ZasjhCzoH8WyQK
Content-Disposition: attachment; filename=patch-i8042.c-bk.diff
Content-Type: text/x-patch; name=patch-i8042.c-bk.diff; charset=UTF-8
Content-Transfer-Encoding: 7bit

diff -Nru a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
--- a/drivers/input/serio/i8042.c       2005-03-21 10:32:57 +01:00
+++ b/drivers/input/serio/i8042.c       2005-03-21 10:32:57 +01:00
@@ -698,6 +698,26 @@
        i8042_interrupt(0, NULL, NULL);
 }
 
+static int i8042_ctl_test(void)
+{
+       unsigned char param;
+
+       if (!i8042_reset)
+               return 0;
+
+       if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
+               printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
+               return -1;
+       }
+
+       if (param != I8042_RET_CTL_TEST) {
+               printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
+                        param, I8042_RET_CTL_TEST);
+               return -1;
+       }
+
+       return 0;
+}
 
 /*
  * i8042_controller init initializes the i8042 controller, and,
@@ -719,21 +739,8 @@
                return -1;
        }
 
-       if (i8042_reset) {
-
-               unsigned char param;
-
-               if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
-                       printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
-                       return -1;
-               }
-
-               if (param != I8042_RET_CTL_TEST) {
-                       printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
-                                param, I8042_RET_CTL_TEST);
-                       return -1;
-               }
-       }
+       if (i8042_ctl_test())
+               return -1;
 
 /*
  * Save the CTR for restoral on unload / reboot.
@@ -808,9 +815,7 @@
  * Reset the controller if requested.
  */
 
-       if (i8042_reset)
-               if (i8042_command(&param, I8042_CMD_CTL_TEST))
-                       printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
+       i8042_ctl_test();
 
 /*
  * Disable MUX mode if present.
@@ -922,8 +927,11 @@
        if (level != RESUME_ENABLE)
                return 0;
 
-       if (i8042_controller_init()) {
-               printk(KERN_ERR "i8042: resume failed\n");
+       if (i8042_ctl_test())
+               return -1;
+
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+               printk(KERN_ERR "i8042: Can't write CTR\n");
                return -1;
        }


--=-r2CPI4ZasjhCzoH8WyQK
Content-Disposition: attachment; filename=patch-i8042.c-2.6.11.4.diff
Content-Type: text/x-patch; name=patch-i8042.c-2.6.11.4.diff; charset=UTF-8
Content-Transfer-Encoding: 7bit

--- i8042.c.orig	2005-03-21 14:27:29.000000000 +0100
+++ i8042.c	2005-03-21 14:35:21.000000000 +0100
@@ -672,6 +672,26 @@
 	i8042_interrupt(0, NULL, NULL);
 }
 
+static int i8042_ctl_test(void)
+{
+       unsigned char param;
+
+       if (!i8042_reset)
+               return 0;
+
+       if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
+               printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
+               return -1;
+       }
+
+       if (param != I8042_RET_CTL_TEST) {
+               printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
+                        param, I8042_RET_CTL_TEST);
+               return -1;
+       }
+
+       return 0;
+}
 
 /*
  * i8042_controller init initializes the i8042 controller, and,
@@ -690,21 +710,8 @@
 
 	i8042_flush();
 
-	if (i8042_reset) {
-
-		unsigned char param;
-
-		if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
-			printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
-			return -1;
-		}
-
-		if (param != I8042_RET_CTL_TEST) {
-			printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
-				 param, I8042_RET_CTL_TEST);
-			return -1;
-		}
-	}
+        if (i8042_ctl_test())
+                return -1;
 
 /*
  * Save the CTR for restoral on unload / reboot.
@@ -779,9 +786,7 @@
  * Reset the controller if requested.
  */
 
-	if (i8042_reset)
-		if (i8042_command(&param, I8042_CMD_CTL_TEST))
-			printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
+        i8042_ctl_test();
 
 /*
  * Disable MUX mode if present.
@@ -891,9 +896,11 @@
 	if (level != RESUME_ENABLE)
 		return 0;
 
-	if (i8042_controller_init()) {
-		printk(KERN_ERR "i8042: resume failed\n");
+        if (i8042_ctl_test())
 		return -1;
+
+        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+                printk(KERN_ERR "i8042: Can't write CTR\n");
 	}
 
 	if (i8042_mux_present)

--=-r2CPI4ZasjhCzoH8WyQK--