Input: i8042 - fix AUX IRQ delivery check
[powerpc.git] / drivers / input / serio / i8042.c
index 1bb0c76..c3fdfc1 100644 (file)
@@ -106,9 +106,10 @@ static unsigned char i8042_ctr;
 static unsigned char i8042_mux_present;
 static unsigned char i8042_kbd_irq_registered;
 static unsigned char i8042_aux_irq_registered;
+static unsigned char i8042_suppress_kbd_ack;
 static struct platform_device *i8042_platform_device;
 
-static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t i8042_interrupt(int irq, void *dev_id);
 
 /*
  * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
@@ -254,25 +255,10 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
 static int i8042_aux_write(struct serio *serio, unsigned char c)
 {
        struct i8042_port *port = serio->port_data;
-       int retval;
-
-/*
- * Send the byte out.
- */
-
-       if (port->mux == -1)
-               retval = i8042_command(&c, I8042_CMD_AUX_SEND);
-       else
-               retval = i8042_command(&c, I8042_CMD_MUX_SEND + port->mux);
 
-/*
- * Make sure the interrupt happens and the character is received even
- * in the case the IRQ isn't wired, so that we can receive further
- * characters later.
- */
-
-       i8042_interrupt(0, NULL, NULL);
-       return retval;
+       return i8042_command(&c, port->mux == -1 ?
+                                       I8042_CMD_AUX_SEND :
+                                       I8042_CMD_MUX_SEND + port->mux);
 }
 
 /*
@@ -309,14 +295,14 @@ static void i8042_stop(struct serio *serio)
  * to the upper layers.
  */
 
-static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t i8042_interrupt(int irq, void *dev_id)
 {
        struct i8042_port *port;
        unsigned long flags;
        unsigned char str, data;
        unsigned int dfl;
        unsigned int port_no;
-       int ret;
+       int ret = 1;
 
        spin_lock_irqsave(&i8042_lock, flags);
        str = i8042_read_status();
@@ -336,23 +322,27 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                dfl = 0;
                if (str & I8042_STR_MUXERR) {
                        dbg("MUX error, status is %02x, data is %02x", str, data);
-                       switch (data) {
-                               default:
 /*
  * When MUXERR condition is signalled the data register can only contain
  * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
- * it is not always the case. Some KBC just get confused which port the
- * data came from and signal error leaving the data intact. They _do not_
- * revert to legacy mode (actually I've never seen KBC reverting to legacy
- * mode yet, when we see one we'll add proper handling).
- * Anyway, we will assume that the data came from the same serio last byte
+ * it is not always the case. Some KBCs also report 0xfc when there is
+ * nothing connected to the port while others sometimes get confused which
+ * port the data came from and signal error leaving the data intact. They
+ * _do not_ revert to legacy mode (actually I've never seen KBC reverting
+ * to legacy mode yet, when we see one we'll add proper handling).
+ * Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
+ * rest assume that the data came from the same serio last byte
  * was transmitted (if transmission happened not too long ago).
  */
+
+                       switch (data) {
+                               default:
                                        if (time_before(jiffies, last_transmit + HZ/10)) {
                                                str = last_str;
                                                break;
                                        }
                                        /* fall through - report timeout */
+                               case 0xfc:
                                case 0xfd:
                                case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
                                case 0xff: dfl = SERIO_PARITY;  data = 0xfe; break;
@@ -378,10 +368,16 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
            dfl & SERIO_PARITY ? ", bad parity" : "",
            dfl & SERIO_TIMEOUT ? ", timeout" : "");
 
+       if (unlikely(i8042_suppress_kbd_ack))
+               if (port_no == I8042_KBD_PORT_NO &&
+                   (data == 0xfa || data == 0xfe)) {
+                       i8042_suppress_kbd_ack--;
+                       goto out;
+               }
+
        if (likely(port->exists))
-               serio_interrupt(port->serio, data, dfl, regs);
+               serio_interrupt(port->serio, data, dfl);
 
-       ret = 1;
  out:
        return IRQ_RETVAL(ret);
 }
@@ -519,7 +515,7 @@ static int __devinit i8042_check_mux(void)
 static struct completion i8042_aux_irq_delivered __devinitdata;
 static int i8042_irq_being_tested __devinitdata;
 
-static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id)
 {
        unsigned long flags;
        unsigned char str, data;
@@ -547,6 +543,7 @@ static int __devinit i8042_check_aux(void)
 {
        int retval = -1;
        int irq_registered = 0;
+       int aux_loop_broken = 0;
        unsigned long flags;
        unsigned char param;
 
@@ -576,6 +573,8 @@ static int __devinit i8042_check_aux(void)
                if (i8042_command(&param, I8042_CMD_AUX_TEST) ||
                    (param && param != 0xfa && param != 0xff))
                        return -1;
+
+               aux_loop_broken = 1;
        }
 
 /*
@@ -599,7 +598,7 @@ static int __devinit i8042_check_aux(void)
  * used it for a PCI card or somethig else.
  */
 
-       if (i8042_noloop) {
+       if (i8042_noloop || aux_loop_broken) {
 /*
  * Without LOOP command we can't test AUX IRQ delivery. Assume the port
  * is working and hope we are right.
@@ -842,11 +841,14 @@ static long i8042_panic_blink(long count)
        led ^= 0x01 | 0x04;
        while (i8042_read_status() & I8042_STR_IBF)
                DELAY;
+       dbg("%02x -> i8042 (panic blink)", 0xed);
+       i8042_suppress_kbd_ack = 2;
        i8042_write_data(0xed); /* set leds */
        DELAY;
        while (i8042_read_status() & I8042_STR_IBF)
                DELAY;
        DELAY;
+       dbg("%02x -> i8042 (panic blink)", led);
        i8042_write_data(led);
        DELAY;
        last_blink = count;
@@ -905,7 +907,7 @@ static int i8042_resume(struct platform_device *dev)
        if (i8042_ports[I8042_KBD_PORT_NO].serio)
                i8042_enable_kbd_port();
 
-       i8042_interrupt(0, NULL, NULL);
+       i8042_interrupt(0, NULL);
 
        return 0;
 }