summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Berger <opendmb@gmail.com>2017-10-24 12:54:47 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-11-21 09:24:17 +0100
commitdabaf70e3ad6ec7d327bfa98916163985a103e4a (patch)
treea206f839a7c8b79061bee3719620f60ff342d66d
parenta8ba76fc043e1a34e98055f6e818c67ddc3c05c2 (diff)
gpio: brcmstb: release the bgpio lock during irq handlers
commit 142c168e0e50164e67c9399c28dedd65a307cfe5 upstream. The basic memory-mapped GPIO controller lock must be released before calling the registered GPIO interrupt handlers to allow the interrupt handlers to access the hardware. Examples of why a GPIO interrupt handler might want to access the GPIO hardware include an interrupt that is configured to trigger on rising and falling edges that needs to read the current level of the input to know how to respond, or an interrupt that causes a change in a GPIO output in the same bank. If the lock is not released before enterring the handler the hardware accesses will deadlock when they attempt to grab the lock. Since the lock is only needed to protect the calculation of unmasked pending interrupts create a dedicated function to perform this and hide the complexity. Fixes: 19a7b6940b78 ("gpio: brcmstb: Add interrupt and wakeup source support") Signed-off-by: Doug Berger <opendmb@gmail.com> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Acked-by: Gregory Fong <gregory.0xf0@gmail.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/gpio/gpio-brcmstb.c21
1 files changed, 16 insertions, 5 deletions
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index dd0308cc8bb0..bfc9b1afa388 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -63,6 +63,21 @@ brcmstb_gpio_gc_to_priv(struct gpio_chip *gc)
return bank->parent_priv;
}
+static unsigned long
+brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank)
+{
+ void __iomem *reg_base = bank->parent_priv->reg_base;
+ unsigned long status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
+ status = bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) &
+ bank->gc.read_reg(reg_base + GIO_MASK(bank->id));
+ spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
+
+ return status;
+}
+
static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
unsigned int offset, bool enable)
{
@@ -205,11 +220,8 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
struct irq_domain *irq_domain = bank->gc.irqdomain;
void __iomem *reg_base = priv->reg_base;
unsigned long status;
- unsigned long flags;
- spin_lock_irqsave(&bank->gc.bgpio_lock, flags);
- while ((status = bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) &
- bank->gc.read_reg(reg_base + GIO_MASK(bank->id)))) {
+ while ((status = brcmstb_gpio_get_active_irqs(bank))) {
int bit;
for_each_set_bit(bit, &status, 32) {
@@ -224,7 +236,6 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
generic_handle_irq(irq_find_mapping(irq_domain, bit));
}
}
- spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags);
}
/* Each UPG GIO block has one IRQ for all banks */