Skip to content

Commit

Permalink
Input: winbond-cir - fix suspend/resume
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Härdeman authored and Dmitry Torokhov committed Feb 24, 2010
1 parent eb083ba commit 197d4db
Showing 1 changed file with 104 additions and 109 deletions.
213 changes: 104 additions & 109 deletions drivers/input/misc/winbond-cir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -1006,7 +1007,6 @@ wbcir_irq_handler(int irqno, void *cookie)
}

wbcir_reset_irdata(data);
data->idle_count = 0;
}

out:
Expand All @@ -1018,7 +1018,7 @@ wbcir_irq_handler(int irqno, void *cookie)

/*****************************************************************************
*
* SUSPEND/RESUME FUNCTIONS
* SETUP/INIT/SUSPEND/RESUME FUNCTIONS
*
*****************************************************************************/

Expand Down Expand Up @@ -1197,7 +1197,16 @@ wbcir_shutdown(struct pnp_dev *device)
}

/* 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
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit 197d4db

Please sign in to comment.