Skip to content

Commit

Permalink
ARM: OMAP: GPIO IRQ lazy IRQ disable fix
Browse files Browse the repository at this point in the history
- The current OMAP GPIO IRQ framework doesn't use the do_edge_IRQ,
  do_level_IRQ handlers, but instead calls do_simple_IRQ. This
  doesn't handle disabled interrupts properly, so drivers will
  still get interrupts after calling disable_irq. The patch solves
  this by respecting the irq_desc.disable_depth and irq_desc.running
  counters.  When one of these is non-zero the handler is not called,
  the interrupt is masked and marked as pending. The pending interrupt
  will be serviced when the running handler returns. This is according
  to the same semantics as the standard do_edge_IRQ and do_level_IRQ
  handlers have, so one day we should use them instead of do_simple_IRQ.

- Process only interrupts that are not masked. The ISR may contain
  pending interrupts that are masked these shouldn't be processed.

- Move the bank IRQ unmasking out of the IRQ dispatch loop. If there
  are further iterations we shouldn't unmask it if there are level
  triggered interrupts pending.

Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: Juha Yrjola <juha.yrjola@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
  • Loading branch information
Imre Deak authored and Tony Lindgren committed Jun 26, 2006
1 parent 7876284 commit ea6dedd
Showing 1 changed file with 79 additions and 9 deletions.
88 changes: 79 additions & 9 deletions arch/arm/plat-omap/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,34 @@ static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
_clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio));
}

static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank)
{
void __iomem *reg = bank->base;

switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_GPIO_MASKIT;
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_INT_MASK;
break;
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_IRQENABLE1;
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_INT_MASK;
break;
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_IRQENABLE1;
break;
default:
BUG();
return 0;
}

return __raw_readl(reg);
}

static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable)
{
void __iomem *reg = bank->base;
Expand Down Expand Up @@ -736,6 +764,8 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
u32 isr;
unsigned int gpio_irq;
struct gpio_bank *bank;
u32 retrigger = 0;
int unmasked = 0;

desc->chip->ack(irq);

Expand All @@ -760,18 +790,22 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
#endif
while(1) {
u32 isr_saved, level_mask = 0;
u32 enabled;

isr_saved = isr = __raw_readl(isr_reg);
enabled = _get_gpio_irqbank_mask(bank);
isr_saved = isr = __raw_readl(isr_reg) & enabled;

if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO))
isr &= 0x0000ffff;

if (cpu_is_omap24xx())
if (cpu_is_omap24xx()) {
level_mask =
__raw_readl(bank->base +
OMAP24XX_GPIO_LEVELDETECT0) |
__raw_readl(bank->base +
OMAP24XX_GPIO_LEVELDETECT1);
level_mask &= enabled;
}

/* clear edge sensitive interrupts before handler(s) are
called so that we don't miss any interrupt occurred while
Expand All @@ -782,19 +816,54 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,

/* if there is only edge sensitive GPIO pin interrupts
configured, we could unmask GPIO bank interrupt immediately */
if (!level_mask)
if (!level_mask && !unmasked) {
unmasked = 1;
desc->chip->unmask(irq);
}

isr |= retrigger;
retrigger = 0;
if (!isr)
break;

gpio_irq = bank->virtual_irq_start;
for (; isr != 0; isr >>= 1, gpio_irq++) {
struct irqdesc *d;
int irq_mask;
if (!(isr & 1))
continue;
d = irq_desc + gpio_irq;
/* Don't run the handler if it's already running
* or was disabled lazely.
*/
if (unlikely((d->disable_depth || d->running))) {
irq_mask = 1 <<
(gpio_irq - bank->virtual_irq_start);
/* The unmasking will be done by
* enable_irq in case it is disabled or
* after returning from the handler if
* it's already running.
*/
_enable_gpio_irqbank(bank, irq_mask, 0);
if (!d->disable_depth) {
/* Level triggered interrupts
* won't ever be reentered
*/
BUG_ON(level_mask & irq_mask);
d->pending = 1;
}
continue;
}
d->running = 1;
desc_handle_irq(gpio_irq, d, regs);
d->running = 0;
if (unlikely(d->pending && !d->disable_depth)) {
irq_mask = 1 <<
(gpio_irq - bank->virtual_irq_start);
d->pending = 0;
_enable_gpio_irqbank(bank, irq_mask, 1);
retrigger |= irq_mask;
}
}

if (cpu_is_omap24xx()) {
Expand All @@ -804,13 +873,14 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
_enable_gpio_irqbank(bank, isr_saved & level_mask, 1);
}

/* if bank has any level sensitive GPIO pin interrupt
configured, we must unmask the bank interrupt only after
handler(s) are executed in order to avoid spurious bank
interrupt */
if (level_mask)
desc->chip->unmask(irq);
}
/* if bank has any level sensitive GPIO pin interrupt
configured, we must unmask the bank interrupt only after
handler(s) are executed in order to avoid spurious bank
interrupt */
if (!unmasked)
desc->chip->unmask(irq);

}

static void gpio_ack_irq(unsigned int irq)
Expand Down

0 comments on commit ea6dedd

Please sign in to comment.