Input: winbond-cir - fix suspend/resume
David Härdeman [Wed, 24 Feb 2010 10:08:29 +0000 (02:08 -0800)]
This fixes suspend/resume problem with the driver caused by the
fact that ACPI _DIS method would completely power off the SP3
module leaving the output lines (including IRQ lines) in an
undefined state. This could cause spurious interrupts and requires
reinitializing hardware from scratch during resume.

This fixes:

http://bugzilla.kernel.org/show_bug.cgi?id=15257

Signed-off-by: David Härdeman <david@hardeman.nu>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

drivers/input/misc/winbond-cir.c

index c8f5a9a..cbec3df 100644 (file)
@@ -538,6 +538,7 @@ wbcir_reset_irdata(struct wbcir_data *data)
        data->irdata_count = 0;
        data->irdata_off = 0;
        data->irdata_error = 0;
+       data->idle_count = 0;
 }
 
 /* Adds one bit of irdata */
@@ -1006,7 +1007,6 @@ wbcir_irq_handler(int irqno, void *cookie)
                }
 
                wbcir_reset_irdata(data);
-               data->idle_count = 0;
        }
 
 out:
@@ -1018,7 +1018,7 @@ out:
 
 /*****************************************************************************
  *
- * SUSPEND/RESUME FUNCTIONS
+ * SETUP/INIT/SUSPEND/RESUME FUNCTIONS
  *
  *****************************************************************************/
 
@@ -1197,7 +1197,16 @@ finish:
        }
 
        /* Disable interrupts */
+       wbcir_select_bank(data, WBCIR_BANK_0);
        outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+
+       /*
+        * ACPI will set the HW disable bit for SP3 which means that the
+        * output signals are left in an undefined state which may cause
+        * spurious interrupts which we need to ignore until the hardware
+        * is reinitialized.
+        */
+       disable_irq(data->irq);
 }
 
 static int
@@ -1207,37 +1216,15 @@ wbcir_suspend(struct pnp_dev *device, pm_message_t state)
        return 0;
 }
 
-static int
-wbcir_resume(struct pnp_dev *device)
-{
-       struct wbcir_data *data = pnp_get_drvdata(device);
-
-       /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
-       wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
-
-       /* Clear CEIR_EN */
-       wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
-
-       /* Enable interrupts */
-       wbcir_reset_irdata(data);
-       outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
-
-       return 0;
-}
-
-
-
-/*****************************************************************************
- *
- * SETUP/INIT FUNCTIONS
- *
- *****************************************************************************/
-
 static void
-wbcir_cfg_ceir(struct wbcir_data *data)
+wbcir_init_hw(struct wbcir_data *data)
 {
        u8 tmp;
 
+       /* Disable interrupts */
+       wbcir_select_bank(data, WBCIR_BANK_0);
+       outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+
        /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
        tmp = protocol << 4;
        if (invert)
@@ -1264,6 +1251,93 @@ wbcir_cfg_ceir(struct wbcir_data *data)
         * set SP3_IRRX_SW to binary 01, helpfully not documented
         */
        outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+
+       /* Enable extended mode */
+       wbcir_select_bank(data, WBCIR_BANK_2);
+       outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
+
+       /*
+        * Configure baud generator, IR data will be sampled at
+        * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
+        *
+        * The ECIR registers include a flag to change the
+        * 24Mhz clock freq to 48Mhz.
+        *
+        * It's not documented in the specs, but fifo levels
+        * other than 16 seems to be unsupported.
+        */
+
+       /* prescaler 1.0, tx/rx fifo lvl 16 */
+       outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
+
+       /* Set baud divisor to generate one byte per bit/cell */
+       switch (protocol) {
+       case IR_PROTOCOL_RC5:
+               outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
+               break;
+       case IR_PROTOCOL_RC6:
+               outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
+               break;
+       case IR_PROTOCOL_NEC:
+               outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
+               break;
+       }
+       outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
+
+       /* Set CEIR mode */
+       wbcir_select_bank(data, WBCIR_BANK_0);
+       outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
+       inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
+       inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
+
+       /* Disable RX demod, run-length encoding/decoding, set freq span */
+       wbcir_select_bank(data, WBCIR_BANK_7);
+       outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+
+       /* Disable timer */
+       wbcir_select_bank(data, WBCIR_BANK_4);
+       outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
+
+       /* Enable MSR interrupt, Clear AUX_IRX */
+       wbcir_select_bank(data, WBCIR_BANK_5);
+       outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
+
+       /* Disable CRC */
+       wbcir_select_bank(data, WBCIR_BANK_6);
+       outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
+
+       /* Set RX/TX (de)modulation freq, not really used */
+       wbcir_select_bank(data, WBCIR_BANK_7);
+       outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+       outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+
+       /* Set invert and pin direction */
+       if (invert)
+               outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
+       else
+               outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
+
+       /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */
+       wbcir_select_bank(data, WBCIR_BANK_0);
+       outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
+
+       /* Clear AUX status bits */
+       outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
+
+       /* Enable interrupts */
+       wbcir_reset_irdata(data);
+       outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+}
+
+static int
+wbcir_resume(struct pnp_dev *device)
+{
+       struct wbcir_data *data = pnp_get_drvdata(device);
+
+       wbcir_init_hw(data);
+       enable_irq(data->irq);
+
+       return 0;
 }
 
 static int __devinit
@@ -1393,86 +1467,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 
        device_init_wakeup(&device->dev, 1);
 
-       wbcir_cfg_ceir(data);
-
-       /* Disable interrupts */
-       wbcir_select_bank(data, WBCIR_BANK_0);
-       outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
-       /* Enable extended mode */
-       wbcir_select_bank(data, WBCIR_BANK_2);
-       outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
-
-       /*
-        * Configure baud generator, IR data will be sampled at
-        * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
-        *
-        * The ECIR registers include a flag to change the
-        * 24Mhz clock freq to 48Mhz.
-        *
-        * It's not documented in the specs, but fifo levels
-        * other than 16 seems to be unsupported.
-        */
-
-       /* prescaler 1.0, tx/rx fifo lvl 16 */
-       outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
-
-       /* Set baud divisor to generate one byte per bit/cell */
-       switch (protocol) {
-       case IR_PROTOCOL_RC5:
-               outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
-               break;
-       case IR_PROTOCOL_RC6:
-               outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
-               break;
-       case IR_PROTOCOL_NEC:
-               outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
-               break;
-       }
-       outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
-
-       /* Set CEIR mode */
-       wbcir_select_bank(data, WBCIR_BANK_0);
-       outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
-       inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
-       inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
-
-       /* Disable RX demod, run-length encoding/decoding, set freq span */
-       wbcir_select_bank(data, WBCIR_BANK_7);
-       outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
-
-       /* Disable timer */
-       wbcir_select_bank(data, WBCIR_BANK_4);
-       outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
-
-       /* Enable MSR interrupt, Clear AUX_IRX */
-       wbcir_select_bank(data, WBCIR_BANK_5);
-       outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
-
-       /* Disable CRC */
-       wbcir_select_bank(data, WBCIR_BANK_6);
-       outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
-
-       /* Set RX/TX (de)modulation freq, not really used */
-       wbcir_select_bank(data, WBCIR_BANK_7);
-       outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
-       outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
-
-       /* Set invert and pin direction */
-       if (invert)
-               outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
-       else
-               outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
-
-       /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */
-       wbcir_select_bank(data, WBCIR_BANK_0);
-       outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
-
-       /* Clear AUX status bits */
-       outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
-
-       /* Enable interrupts */
-       outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+       wbcir_init_hw(data);
 
        return 0;