Skip to content

Commit

Permalink
gpio: timbgpio: use a copy of the IER register to avoid it being trashed
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tomas Hallenberg authored and Linus Torvalds committed Oct 28, 2010
1 parent 09cd952 commit 76d800a
Showing 1 changed file with 19 additions and 2 deletions.
21 changes: 19 additions & 2 deletions drivers/gpio/timbgpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down

0 comments on commit 76d800a

Please sign in to comment.