diff mbox

[v2] gpio: omap: Fix lost edge interrupts

Message ID 20170926113203.2bhqq6ini7dtk4l2@lenoch (mailing list archive)
State New, archived
Headers show

Commit Message

Ladislav Michl Sept. 26, 2017, 11:32 a.m. UTC
From: Grygorii Strashko <grygorii.strashko@ti.com>

Use handle_simple_irq and clear edge interrupts early in
omap_gpio_irq_handler to avoid loosing interrupts.

Cc: stable@vger.kernel.org
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
---
 drivers/gpio/gpio-omap.c |   22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

 Changes:
 -v2: use handle_simple_irq to handle edge interrupts

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Grygorii Strashko Sept. 26, 2017, 3:54 p.m. UTC | #1
cc: Linus, linux-gpio

On 09/26/2017 06:32 AM, Ladislav Michl wrote:
> From: Grygorii Strashko <grygorii.strashko@ti.com>
> 
> Use handle_simple_irq and clear edge interrupts early in
> omap_gpio_irq_handler to avoid loosing interrupts.

Acked-by: Grygorii Strashko <grygorii.strashko@ti.com>

not sure, but you might need to re-send it with proper cc list.

> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
> ---
>   drivers/gpio/gpio-omap.c |   22 +++++++++++++---------
>   1 file changed, 13 insertions(+), 9 deletions(-)
> 
>   Changes:
>   -v2: use handle_simple_irq to handle edge interrupts
> 
> diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
> index dbf869fb63ce..0c8005285124 100644
> --- a/drivers/gpio/gpio-omap.c
> +++ b/drivers/gpio/gpio-omap.c
> @@ -518,7 +518,13 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
>   	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
>   		irq_set_handler_locked(d, handle_level_irq);
>   	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
> -		irq_set_handler_locked(d, handle_edge_irq);
> +		/*
> +		 * Edge IRQs are already cleared/acked in irq_handler and
> +		 * not need to be masked, as result handle_edge_irq()
> +		 * logic is excessed here and may cause lose of interrupts.
> +		 * So just use handle_simple_irq.
> +		 */
> +		irq_set_handler_locked(d, handle_simple_irq);
>   
>   	return 0;
>   
> @@ -678,7 +684,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
>   static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
>   {
>   	void __iomem *isr_reg = NULL;
> -	u32 isr;
> +	u32 enabled, isr, level_mask;
>   	unsigned int bit;
>   	struct gpio_bank *bank = gpiobank;
>   	unsigned long wa_lock_flags;
> @@ -691,23 +697,21 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
>   	pm_runtime_get_sync(bank->chip.parent);
>   
>   	while (1) {
> -		u32 isr_saved, level_mask = 0;
> -		u32 enabled;
> -
>   		raw_spin_lock_irqsave(&bank->lock, lock_flags);
>   
>   		enabled = omap_get_gpio_irqbank_mask(bank);
> -		isr_saved = isr = readl_relaxed(isr_reg) & enabled;
> +		isr = readl_relaxed(isr_reg) & enabled;
>   
>   		if (bank->level_mask)
>   			level_mask = bank->level_mask & enabled;
> +		else
> +			level_mask = 0;
>   
>   		/* clear edge sensitive interrupts before handler(s) are
>   		called so that we don't miss any interrupt occurred while
>   		executing them */
> -		omap_disable_gpio_irqbank(bank, isr_saved & ~level_mask);
> -		omap_clear_gpio_irqbank(bank, isr_saved & ~level_mask);
> -		omap_enable_gpio_irqbank(bank, isr_saved & ~level_mask);
> +		if (isr & ~level_mask)
> +			omap_clear_gpio_irqbank(bank, isr & ~level_mask);
>   
>   		raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
>   
>
diff mbox

Patch

diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index dbf869fb63ce..0c8005285124 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -518,7 +518,13 @@  static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
 	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
 		irq_set_handler_locked(d, handle_level_irq);
 	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
-		irq_set_handler_locked(d, handle_edge_irq);
+		/*
+		 * Edge IRQs are already cleared/acked in irq_handler and
+		 * not need to be masked, as result handle_edge_irq()
+		 * logic is excessed here and may cause lose of interrupts.
+		 * So just use handle_simple_irq.
+		 */
+		irq_set_handler_locked(d, handle_simple_irq);
 
 	return 0;
 
@@ -678,7 +684,7 @@  static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
 {
 	void __iomem *isr_reg = NULL;
-	u32 isr;
+	u32 enabled, isr, level_mask;
 	unsigned int bit;
 	struct gpio_bank *bank = gpiobank;
 	unsigned long wa_lock_flags;
@@ -691,23 +697,21 @@  static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
 	pm_runtime_get_sync(bank->chip.parent);
 
 	while (1) {
-		u32 isr_saved, level_mask = 0;
-		u32 enabled;
-
 		raw_spin_lock_irqsave(&bank->lock, lock_flags);
 
 		enabled = omap_get_gpio_irqbank_mask(bank);
-		isr_saved = isr = readl_relaxed(isr_reg) & enabled;
+		isr = readl_relaxed(isr_reg) & enabled;
 
 		if (bank->level_mask)
 			level_mask = bank->level_mask & enabled;
+		else
+			level_mask = 0;
 
 		/* clear edge sensitive interrupts before handler(s) are
 		called so that we don't miss any interrupt occurred while
 		executing them */
-		omap_disable_gpio_irqbank(bank, isr_saved & ~level_mask);
-		omap_clear_gpio_irqbank(bank, isr_saved & ~level_mask);
-		omap_enable_gpio_irqbank(bank, isr_saved & ~level_mask);
+		if (isr & ~level_mask)
+			omap_clear_gpio_irqbank(bank, isr & ~level_mask);
 
 		raw_spin_unlock_irqrestore(&bank->lock, lock_flags);