[ARM] 5308/1: Fix Viper ISA IRQ handling

The ISA IRQ renumbering broke the Viper ISA code in interesting ways.
It originally assumed that ISA interrupt were numbered in the order that
is defined by the CPLD registers. Unfortunately, this is no longer the
case.

Furthermore, the viper_irq_handler() function being a chained IRQ
handler, it must ACK the interrupt by itself, or the handler will be
immediately reentered, with the expected damages.

This fix was made possible thanks to the help of David Raeman, who
provided debug information and tested each version of this patch.

Tested-by: David Raeman <david.raeman@gmail.com>

Signed-off-by: Marc Zyngier <maz@misterjones.org>
Acked-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Marc Zyngier 2008-10-15 12:54:05 +01:00 committed by Russell King
parent af2010daf7
commit a9ff8f6462

View File

@ -204,25 +204,54 @@ static void viper_set_core_cpu_voltage(unsigned long khz, int force)
/* Interrupt handling */ /* Interrupt handling */
static unsigned long viper_irq_enabled_mask; static unsigned long viper_irq_enabled_mask;
static const int viper_isa_irqs[] = { 3, 4, 5, 6, 7, 10, 11, 12, 9, 14, 15 };
static const int viper_isa_irq_map[] = {
0, /* ISA irq #0, invalid */
0, /* ISA irq #1, invalid */
0, /* ISA irq #2, invalid */
1 << 0, /* ISA irq #3 */
1 << 1, /* ISA irq #4 */
1 << 2, /* ISA irq #5 */
1 << 3, /* ISA irq #6 */
1 << 4, /* ISA irq #7 */
0, /* ISA irq #8, invalid */
1 << 8, /* ISA irq #9 */
1 << 5, /* ISA irq #10 */
1 << 6, /* ISA irq #11 */
1 << 7, /* ISA irq #12 */
0, /* ISA irq #13, invalid */
1 << 9, /* ISA irq #14 */
1 << 10, /* ISA irq #15 */
};
static inline int viper_irq_to_bitmask(unsigned int irq)
{
return viper_isa_irq_map[irq - PXA_ISA_IRQ(0)];
}
static inline int viper_bit_to_irq(int bit)
{
return viper_isa_irqs[bit] + PXA_ISA_IRQ(0);
}
static void viper_ack_irq(unsigned int irq) static void viper_ack_irq(unsigned int irq)
{ {
int viper_irq = irq - PXA_ISA_IRQ(0); int viper_irq = viper_irq_to_bitmask(irq);
if (viper_irq < 8) if (viper_irq & 0xff)
VIPER_LO_IRQ_STATUS = 1 << viper_irq; VIPER_LO_IRQ_STATUS = viper_irq;
else else
VIPER_HI_IRQ_STATUS = 1 << (viper_irq - 8); VIPER_HI_IRQ_STATUS = (viper_irq >> 8);
} }
static void viper_mask_irq(unsigned int irq) static void viper_mask_irq(unsigned int irq)
{ {
viper_irq_enabled_mask &= ~(1 << (irq - PXA_ISA_IRQ(0))); viper_irq_enabled_mask &= ~(viper_irq_to_bitmask(irq));
} }
static void viper_unmask_irq(unsigned int irq) static void viper_unmask_irq(unsigned int irq)
{ {
viper_irq_enabled_mask |= (1 << (irq - PXA_ISA_IRQ(0))); viper_irq_enabled_mask |= viper_irq_to_bitmask(irq);
} }
static inline unsigned long viper_irq_pending(void) static inline unsigned long viper_irq_pending(void)
@ -237,8 +266,12 @@ static void viper_irq_handler(unsigned int irq, struct irq_desc *desc)
pending = viper_irq_pending(); pending = viper_irq_pending();
do { do {
/* we're in a chained irq handler,
* so ack the interrupt by hand */
GEDR(VIPER_CPLD_GPIO) = GPIO_bit(VIPER_CPLD_GPIO);
if (likely(pending)) { if (likely(pending)) {
irq = PXA_ISA_IRQ(0) + __ffs(pending); irq = viper_bit_to_irq(__ffs(pending));
generic_handle_irq(irq); generic_handle_irq(irq);
} }
pending = viper_irq_pending(); pending = viper_irq_pending();
@ -254,15 +287,14 @@ static struct irq_chip viper_irq_chip = {
static void __init viper_init_irq(void) static void __init viper_init_irq(void)
{ {
const int isa_irqs[] = { 3, 4, 5, 6, 7, 10, 11, 12, 9, 14, 15 }; int level;
int irq;
int isa_irq; int isa_irq;
pxa25x_init_irq(); pxa25x_init_irq();
/* setup ISA IRQs */ /* setup ISA IRQs */
for (irq = 0; irq < ARRAY_SIZE(isa_irqs); irq++) { for (level = 0; level < ARRAY_SIZE(viper_isa_irqs); level++) {
isa_irq = isa_irqs[irq]; isa_irq = viper_bit_to_irq(level);
set_irq_chip(isa_irq, &viper_irq_chip); set_irq_chip(isa_irq, &viper_irq_chip);
set_irq_handler(isa_irq, handle_edge_irq); set_irq_handler(isa_irq, handle_edge_irq);
set_irq_flags(isa_irq, IRQF_VALID | IRQF_PROBE); set_irq_flags(isa_irq, IRQF_VALID | IRQF_PROBE);