From 76d800a5b6e198c4fda07b88bb42a545709f193b Mon Sep 17 00:00:00 2001 From: Tomas Hallenberg Date: Wed, 27 Oct 2010 15:33:17 -0700 Subject: [PATCH] gpio: timbgpio: use a copy of the IER register to avoid it being trashed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some versions of the hardware can trash the IER register if simultaneous interrupts occur. This patch works around it by using a local copy of the register and restoring it after every interrupt. Signed-off-by: Tomas Hallenberg Acked-by: Richard Röjfors Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/timbgpio.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c index ddd053108a1..45293662e95 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/timbgpio.c @@ -47,6 +47,7 @@ struct timbgpio { spinlock_t lock; /* mutual exclusion */ struct gpio_chip gpio; int irq_base; + unsigned long last_ier; }; static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, @@ -112,16 +113,24 @@ static void timbgpio_irq_disable(unsigned irq) { struct timbgpio *tgpio = get_irq_chip_data(irq); int offset = irq - tgpio->irq_base; + unsigned long flags; - timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier &= ~(1 << offset); + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); } static void timbgpio_irq_enable(unsigned irq) { struct timbgpio *tgpio = get_irq_chip_data(irq); int offset = irq - tgpio->irq_base; + unsigned long flags; - timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier |= 1 << offset; + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); } static int timbgpio_irq_type(unsigned irq, unsigned trigger) @@ -194,8 +203,16 @@ static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) ipr = ioread32(tgpio->membase + TGPIO_IPR); iowrite32(ipr, tgpio->membase + TGPIO_ICR); + /* + * Some versions of the hardware trash the IER register if more than + * one interrupt is received simultaneously. + */ + iowrite32(0, tgpio->membase + TGPIO_IER); + for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); + + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); } static struct irq_chip timbgpio_irqchip = {