summaryrefslogtreecommitdiff
path: root/drivers/gpio/timbgpio.c
diff options
context:
space:
mode:
authorTomas Hallenberg <tomas.hallenberg@pelagicore.com>2010-10-27 15:33:17 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-27 18:03:07 -0700
commit76d800a5b6e198c4fda07b88bb42a545709f193b (patch)
treee1d16296c54137cbaedfbde59502fa28143755fc /drivers/gpio/timbgpio.c
parent09cd9527d621640d4dd231dff77b681e711d8e4b (diff)
downloadlwn-76d800a5b6e198c4fda07b88bb42a545709f193b.tar.gz
lwn-76d800a5b6e198c4fda07b88bb42a545709f193b.zip
gpio: timbgpio: use a copy of the IER register to avoid it being trashed
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 <tomas.hallenberg@pelagicore.com> Acked-by: Richard Röjfors <richard.rojfors@pelagicore.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/gpio/timbgpio.c')
-rw-r--r--drivers/gpio/timbgpio.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c
index ddd053108a13..45293662e950 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 = {