aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTomas Hallenberg <tomas.hallenberg@pelagicore.com>2010-10-27 18:33:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-27 21:03:07 -0400
commit76d800a5b6e198c4fda07b88bb42a545709f193b (patch)
treee1d16296c54137cbaedfbde59502fa28143755fc /drivers
parent09cd9527d621640d4dd231dff77b681e711d8e4b (diff)
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')
-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 {
47 spinlock_t lock; /* mutual exclusion */ 47 spinlock_t lock; /* mutual exclusion */
48 struct gpio_chip gpio; 48 struct gpio_chip gpio;
49 int irq_base; 49 int irq_base;
50 unsigned long last_ier;
50}; 51};
51 52
52static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, 53static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
@@ -112,16 +113,24 @@ static void timbgpio_irq_disable(unsigned irq)
112{ 113{
113 struct timbgpio *tgpio = get_irq_chip_data(irq); 114 struct timbgpio *tgpio = get_irq_chip_data(irq);
114 int offset = irq - tgpio->irq_base; 115 int offset = irq - tgpio->irq_base;
116 unsigned long flags;
115 117
116 timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); 118 spin_lock_irqsave(&tgpio->lock, flags);
119 tgpio->last_ier &= ~(1 << offset);
120 iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
121 spin_unlock_irqrestore(&tgpio->lock, flags);
117} 122}
118 123
119static void timbgpio_irq_enable(unsigned irq) 124static void timbgpio_irq_enable(unsigned irq)
120{ 125{
121 struct timbgpio *tgpio = get_irq_chip_data(irq); 126 struct timbgpio *tgpio = get_irq_chip_data(irq);
122 int offset = irq - tgpio->irq_base; 127 int offset = irq - tgpio->irq_base;
128 unsigned long flags;
123 129
124 timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); 130 spin_lock_irqsave(&tgpio->lock, flags);
131 tgpio->last_ier |= 1 << offset;
132 iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
133 spin_unlock_irqrestore(&tgpio->lock, flags);
125} 134}
126 135
127static int timbgpio_irq_type(unsigned irq, unsigned trigger) 136static int timbgpio_irq_type(unsigned irq, unsigned trigger)
@@ -194,8 +203,16 @@ static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
194 ipr = ioread32(tgpio->membase + TGPIO_IPR); 203 ipr = ioread32(tgpio->membase + TGPIO_IPR);
195 iowrite32(ipr, tgpio->membase + TGPIO_ICR); 204 iowrite32(ipr, tgpio->membase + TGPIO_ICR);
196 205
206 /*
207 * Some versions of the hardware trash the IER register if more than
208 * one interrupt is received simultaneously.
209 */
210 iowrite32(0, tgpio->membase + TGPIO_IER);
211
197 for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) 212 for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio)
198 generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); 213 generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
214
215 iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER);
199} 216}
200 217
201static struct irq_chip timbgpio_irqchip = { 218static struct irq_chip timbgpio_irqchip = {