[PATCH] fix for certain sequnce of request_irq can cause irq storm
Andrew Lunn
andrew at lunn.ch
Sat Jul 26 10:45:06 PDT 2014
On Sat, Jul 26, 2014 at 07:56:59PM +0400, Evgeniy Dushistov wrote:
> The problem is that hardware handled by arm/plat-orion/gpio.c,
> require ack for edge irq, and no ack for level irq.
>
> The code handle this issue, by two "struct irq_chip_type" per
> one "struct irq_chip_generic". For one "struct irq_chip_generic"
> irq_ack pointer is setted, for another it is NULL.
>
> But we have only one mask_cache per two "struct irq_chip_type".
> So if we
> 1)unmask interrupt A for "edge type" trigger,
> 2)unmask interrupt B for "level type" trigger,
> 3)unmask interrupt C for "edge type",
>
> we, because of usage of generic irq_gc_mask_clr_bit/irq_gc_mask_set_bit,
> have hardware configured to trigger interrupt B on "edge type",
> because of shared mask_cache. But kernel think that B is "level type",
> so when interrupt B occur via "edge" reason, we don't ack it,
> and B triggered again and again.
Hi Evgeniy
Did you look at the way gpio-mvebu.c handles this? It is a little bit
different from your solution. I'm wondering if gpio-mvebu.c is just
different, or wrong? It does seem to be using one mask_cache for both
edge and level trigger.
Thanks
Andrew
>
> Signed-off-by: Evgeniy A. Dushistov <dushistov at mail.ru>
> ---
> arch/arm/plat-orion/gpio.c | 36 ++++++++++++++++++++++++++++++++----
> 1 file changed, 32 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
> index b61a3bc..e048f61 100644
> --- a/arch/arm/plat-orion/gpio.c
> +++ b/arch/arm/plat-orion/gpio.c
> @@ -497,6 +497,34 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
> #define orion_gpio_dbg_show NULL
> #endif
>
> +static void orion_gpio_unmask_irq(struct irq_data *d)
> +{
> + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> + struct irq_chip_type *ct = irq_data_get_chip_type(d);
> + u32 reg_val;
> + u32 mask = d->mask;
> +
> + irq_gc_lock(gc);
> + reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
> + reg_val |= mask;
> + irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
> + irq_gc_unlock(gc);
> +}
> +
> +static void orion_gpio_mask_irq(struct irq_data *d)
> +{
> + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
> + struct irq_chip_type *ct = irq_data_get_chip_type(d);
> + u32 mask = d->mask;
> + u32 reg_val;
> +
> + irq_gc_lock(gc);
> + reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
> + reg_val &= ~mask;
> + irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
> + irq_gc_unlock(gc);
> +}
> +
> void __init orion_gpio_init(struct device_node *np,
> int gpio_base, int ngpio,
> void __iomem *base, int mask_offset,
> @@ -565,8 +593,8 @@ void __init orion_gpio_init(struct device_node *np,
> ct = gc->chip_types;
> ct->regs.mask = ochip->mask_offset + GPIO_LEVEL_MASK_OFF;
> ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
> - ct->chip.irq_mask = irq_gc_mask_clr_bit;
> - ct->chip.irq_unmask = irq_gc_mask_set_bit;
> + ct->chip.irq_mask = orion_gpio_mask_irq;
> + ct->chip.irq_unmask = orion_gpio_unmask_irq;
> ct->chip.irq_set_type = gpio_irq_set_type;
> ct->chip.name = ochip->chip.label;
>
> @@ -575,8 +603,8 @@ void __init orion_gpio_init(struct device_node *np,
> ct->regs.ack = GPIO_EDGE_CAUSE_OFF;
> ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
> ct->chip.irq_ack = irq_gc_ack_clr_bit;
> - ct->chip.irq_mask = irq_gc_mask_clr_bit;
> - ct->chip.irq_unmask = irq_gc_mask_set_bit;
> + ct->chip.irq_mask = orion_gpio_mask_irq;
> + ct->chip.irq_unmask = orion_gpio_unmask_irq;
> ct->chip.irq_set_type = gpio_irq_set_type;
> ct->handler = handle_edge_irq;
> ct->chip.name = ochip->chip.label;
> --
> 1.8.5.5
>
> --
> /Evgeniy
More information about the linux-arm-kernel
mailing list