diff options
author | Manuel Lauss <manuel.lauss@googlemail.com> | 2009-10-14 12:22:20 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-11-02 12:00:08 +0100 |
commit | 44f2c586a3553154bc6549eb696c7716c85f910b (patch) | |
tree | 1050bd39b5fd1d67c63dddd939223b60ad7f9a44 /arch/mips | |
parent | fcc152f3bf55cec61167b173774cbf717b0ff5e4 (diff) | |
download | op-kernel-dev-44f2c586a3553154bc6549eb696c7716c85f910b.zip op-kernel-dev-44f2c586a3553154bc6549eb696c7716c85f910b.tar.gz |
MIPS: Alchemy: Fix hang with high-frequency edge interrupts
The handle_edge_irq() flowhandler disables edge int sources which occur
too fast (i.e. another edge comes in before the irq handler function
had a chance to finish). Currently, the mask_ack() callback does not
ack the edges in hardware, leading to an endless loop in the flowhandler
where it tries to shut up the irq source.
When I rewrote the alchemy IRQ code I wrongly assumed the mask_ack()
callback was only used by the level flowhandler, hence it omitted the
(at the time pointless) edge acks. Turned out I was wrong; so here
is a complete mask_ack implementation for Alchemy IC, which fixes
the above mentioned problem.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/alchemy/common/irq.c | 34 |
1 files changed, 26 insertions, 8 deletions
diff --git a/arch/mips/alchemy/common/irq.c b/arch/mips/alchemy/common/irq.c index c88c821..d670928 100644 --- a/arch/mips/alchemy/common/irq.c +++ b/arch/mips/alchemy/common/irq.c @@ -354,6 +354,28 @@ static void au1x_ic1_ack(unsigned int irq_nr) au_sync(); } +static void au1x_ic0_maskack(unsigned int irq_nr) +{ + unsigned int bit = irq_nr - AU1000_INTC0_INT_BASE; + + au_writel(1 << bit, IC0_WAKECLR); + au_writel(1 << bit, IC0_MASKCLR); + au_writel(1 << bit, IC0_RISINGCLR); + au_writel(1 << bit, IC0_FALLINGCLR); + au_sync(); +} + +static void au1x_ic1_maskack(unsigned int irq_nr) +{ + unsigned int bit = irq_nr - AU1000_INTC1_INT_BASE; + + au_writel(1 << bit, IC1_WAKECLR); + au_writel(1 << bit, IC1_MASKCLR); + au_writel(1 << bit, IC1_RISINGCLR); + au_writel(1 << bit, IC1_FALLINGCLR); + au_sync(); +} + static int au1x_ic1_setwake(unsigned int irq, unsigned int on) { unsigned int bit = irq - AU1000_INTC1_INT_BASE; @@ -379,25 +401,21 @@ static int au1x_ic1_setwake(unsigned int irq, unsigned int on) /* * irq_chips for both ICs; this way the mask handlers can be * as short as possible. - * - * NOTE: the ->ack() callback is used by the handle_edge_irq - * flowhandler only, the ->mask_ack() one by handle_level_irq, - * so no need for an irq_chip for each type of irq (level/edge). */ static struct irq_chip au1x_ic0_chip = { .name = "Alchemy-IC0", - .ack = au1x_ic0_ack, /* edge */ + .ack = au1x_ic0_ack, .mask = au1x_ic0_mask, - .mask_ack = au1x_ic0_mask, /* level */ + .mask_ack = au1x_ic0_maskack, .unmask = au1x_ic0_unmask, .set_type = au1x_ic_settype, }; static struct irq_chip au1x_ic1_chip = { .name = "Alchemy-IC1", - .ack = au1x_ic1_ack, /* edge */ + .ack = au1x_ic1_ack, .mask = au1x_ic1_mask, - .mask_ack = au1x_ic1_mask, /* level */ + .mask_ack = au1x_ic1_maskack, .unmask = au1x_ic1_unmask, .set_type = au1x_ic_settype, .set_wake = au1x_ic1_setwake, |