Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: mvebu: Fix mask/unmask managment per irq chip type

Level IRQ handlers and edge IRQ handler are managed by tow different
sets of registers. But currently the driver uses the same mask for the
both registers. It lead to issues with the following scenario:

First, an IRQ is requested on a GPIO to be triggered on front. After,
this an other IRQ is requested for a GPIO of the same bank but
triggered on level. Then the first one will be also setup to be
triggered on level. It leads to an interrupt storm.

The different kind of handler are already associated with two
different irq chip type. With this patch the driver uses a private
mask for each one which solves this issue.

It has been tested on an Armada XP based board and on an Armada 375
board. For the both boards, with this patch is applied, there is no
such interrupt storm when running the previous scenario.

This bug was already fixed but in a different way in the legacy
version of this driver by Evgeniy Dushistov:
9ece8839b1277fb9128ff6833411614ab6c88d68 "ARM: orion: Fix for certain
sequence of request_irq can cause irq storm". The fact the new version
of the gpio drive could be affected had been discussed there:
http://thread.gmane.org/gmane.linux.ports.arm.kernel/344670/focus=364012

Reported-by: Evgeniy A. Dushistov <dushistov@mail.ru>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Cc: <stable@vger.kernel.org> # v3.7 +
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Gregory CLEMENT and committed by
Linus Walleij
61819549 177b0381

+16 -8
+16 -8
drivers/gpio/gpio-mvebu.c
··· 320 320 { 321 321 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 322 322 struct mvebu_gpio_chip *mvchip = gc->private; 323 + struct irq_chip_type *ct = irq_data_get_chip_type(d); 323 324 u32 mask = 1 << (d->irq - gc->irq_base); 324 325 325 326 irq_gc_lock(gc); 326 - gc->mask_cache &= ~mask; 327 - writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); 327 + ct->mask_cache_priv &= ~mask; 328 + 329 + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); 328 330 irq_gc_unlock(gc); 329 331 } 330 332 ··· 334 332 { 335 333 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 336 334 struct mvebu_gpio_chip *mvchip = gc->private; 335 + struct irq_chip_type *ct = irq_data_get_chip_type(d); 336 + 337 337 u32 mask = 1 << (d->irq - gc->irq_base); 338 338 339 339 irq_gc_lock(gc); 340 - gc->mask_cache |= mask; 341 - writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); 340 + ct->mask_cache_priv |= mask; 341 + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); 342 342 irq_gc_unlock(gc); 343 343 } 344 344 ··· 348 344 { 349 345 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 350 346 struct mvebu_gpio_chip *mvchip = gc->private; 347 + struct irq_chip_type *ct = irq_data_get_chip_type(d); 348 + 351 349 u32 mask = 1 << (d->irq - gc->irq_base); 352 350 353 351 irq_gc_lock(gc); 354 - gc->mask_cache &= ~mask; 355 - writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); 352 + ct->mask_cache_priv &= ~mask; 353 + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); 356 354 irq_gc_unlock(gc); 357 355 } 358 356 ··· 362 356 { 363 357 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 364 358 struct mvebu_gpio_chip *mvchip = gc->private; 359 + struct irq_chip_type *ct = irq_data_get_chip_type(d); 360 + 365 361 u32 mask = 1 << (d->irq - gc->irq_base); 366 362 367 363 irq_gc_lock(gc); 368 - gc->mask_cache |= mask; 369 - writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); 364 + ct->mask_cache_priv |= mask; 365 + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); 370 366 irq_gc_unlock(gc); 371 367 } 372 368