Input: i8042 - try disabling and re-enabling AUX port at close
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 10 Sep 2009 02:08:15 +0000 (19:08 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 11 Sep 2009 05:11:35 +0000 (22:11 -0700)
Ever since we switched from having a polling timer to registering IRQ
handlers for both keyboard and AUX ports at the driver registration
time, on certain boxes probing for a mouse results in keyboard
stopping working. The only real difference between old and new way is
that before we disabled ports after unsuccessful probe whereas now we
leave them as is. Try to emulate the old behavior by disabling and
immediately re-enabling AUX and KBD ports when corresponding serio
port is being closed.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/serio/i8042.c

index b53a015..8aaf8fc 100644 (file)
@@ -264,6 +264,49 @@ static int i8042_aux_write(struct serio *serio, unsigned char c)
                                        I8042_CMD_MUX_SEND + port->mux);
 }
 
+
+/*
+ * i8042_aux_close attempts to clear AUX or KBD port state by disabling
+ * and then re-enabling it.
+ */
+
+static void i8042_port_close(struct serio *serio)
+{
+       int irq_bit;
+       int disable_bit;
+       const char *port_name;
+
+       if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) {
+               irq_bit = I8042_CTR_AUXINT;
+               disable_bit = I8042_CTR_AUXDIS;
+               port_name = "AUX";
+       } else {
+               irq_bit = I8042_CTR_KBDINT;
+               disable_bit = I8042_CTR_KBDDIS;
+               port_name = "KBD";
+       }
+
+       i8042_ctr &= ~irq_bit;
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+               printk(KERN_WARNING
+                       "i8042.c: Can't write CTR while closing %s port.\n",
+                       port_name);
+
+       udelay(50);
+
+       i8042_ctr &= ~disable_bit;
+       i8042_ctr |= irq_bit;
+       if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+               printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n",
+                       port_name);
+
+       /*
+        * See if there is any data appeared while we were messing with
+        * port state.
+        */
+       i8042_interrupt(0, NULL);
+}
+
 /*
  * i8042_start() is called by serio core when port is about to finish
  * registering. It will mark port as existing so i8042_interrupt can
@@ -393,7 +436,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 }
 
 /*
- * i8042_enable_kbd_port enables keybaord port on chip
+ * i8042_enable_kbd_port enables keyboard port on chip
  */
 
 static int i8042_enable_kbd_port(void)
@@ -841,6 +884,9 @@ static void i8042_controller_reset(void)
        i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
        i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
 
+       if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
+               printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n");
+
 /*
  * Disable MUX mode if present.
  */
@@ -1026,6 +1072,7 @@ static int __devinit i8042_create_kbd_port(void)
        serio->write            = i8042_dumbkbd ? NULL : i8042_kbd_write;
        serio->start            = i8042_start;
        serio->stop             = i8042_stop;
+       serio->close            = i8042_port_close;
        serio->port_data        = port;
        serio->dev.parent       = &i8042_platform_device->dev;
        strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
@@ -1056,6 +1103,7 @@ static int __devinit i8042_create_aux_port(int idx)
        if (idx < 0) {
                strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
                strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+               serio->close = i8042_port_close;
        } else {
                snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
                snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);