diff mbox

gpio-omap: Edge interrupts stall

Message ID 20170321001732.gs525wb5klq7fuq7@lenoch (mailing list archive)
State New, archived
Headers show

Commit Message

Ladislav Michl March 21, 2017, 12:17 a.m. UTC
On Mon, Mar 20, 2017 at 04:16:33PM -0500, Grygorii Strashko wrote:
> 
> 
> On 03/20/2017 06:21 AM, Ladislav Michl wrote:
> > On Thu, Mar 16, 2017 at 12:17:45PM -0700, Tony Lindgren wrote:
> >> Hmm maybe we need to flush posted writes when re-enabling the GPIO interrupts?
> >>
> >> Below is an untested patch that might help if that's the case.
> > 
> > Unfortunately that's not the case. Even writing set&clear version of
> > interrupt demux handler did not make it any better. And idea from
> > ancient patch I initially sent is not easily extensible when we
> > need to trigger interrupt on both edges. So far I'm clueless...
> 
> So, just to be sure - can you reproduce it with LKML?

Do you mean mainline kernel? I'm on 4.11-rc3 now...

> As per code, the possible problem could be with double acking of edge irqs
> (theoretically):
> - omap_gpio_irq_handler
>   - "isr" = read irq status
>   - omap_clear_gpio_irqbank(bank, isr_saved & ~level_mask); --- clear edge status, so new irq can be accepted
>   - loop while "isr"
> 	generic_handle_irq()
> 	 - handle_edge_irq()
> 	    - desc->irq_data.chip->irq_ack(&desc->irq_data); 
> 		- omap_gpio_ack_irq()
> it might be that at this moment edge IRQ was triggered again and it will be cleared.
> 
> just as an experiment, could you try to update omap_gpio_ack_irq()
> as below:
> 
> if (irq type is not edge)
> 	omap_clear_gpio_irqstatus(bank, offset);

Rewritten as:


And that did the trick. So far I tried IR decoder and decoding sometimes
fails, but I cannot say anything certain until I check with scope (which
I do tomorrow)

Thank you,
	ladis
--
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

Tony Lindgren March 22, 2017, 5:17 p.m. UTC | #1
* Ladislav Michl <ladis@linux-mips.org> [170320 17:19]:
> On Mon, Mar 20, 2017 at 04:16:33PM -0500, Grygorii Strashko wrote:
> > 
> > 
> > On 03/20/2017 06:21 AM, Ladislav Michl wrote:
> > > On Thu, Mar 16, 2017 at 12:17:45PM -0700, Tony Lindgren wrote:
> > >> Hmm maybe we need to flush posted writes when re-enabling the GPIO interrupts?
> > >>
> > >> Below is an untested patch that might help if that's the case.
> > > 
> > > Unfortunately that's not the case. Even writing set&clear version of
> > > interrupt demux handler did not make it any better. And idea from
> > > ancient patch I initially sent is not easily extensible when we
> > > need to trigger interrupt on both edges. So far I'm clueless...
> > 
> > So, just to be sure - can you reproduce it with LKML?
> 
> Do you mean mainline kernel? I'm on 4.11-rc3 now...
> 
> > As per code, the possible problem could be with double acking of edge irqs
> > (theoretically):
> > - omap_gpio_irq_handler
> >   - "isr" = read irq status
> >   - omap_clear_gpio_irqbank(bank, isr_saved & ~level_mask); --- clear edge status, so new irq can be accepted
> >   - loop while "isr"
> > 	generic_handle_irq()
> > 	 - handle_edge_irq()
> > 	    - desc->irq_data.chip->irq_ack(&desc->irq_data); 
> > 		- omap_gpio_ack_irq()
> > it might be that at this moment edge IRQ was triggered again and it will be cleared.
> > 
> > just as an experiment, could you try to update omap_gpio_ack_irq()
> > as below:
> > 
> > if (irq type is not edge)
> > 	omap_clear_gpio_irqstatus(bank, offset);
> 
> Rewritten as:
> 
> diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
> index efc85a279d54..9381763e1fec 100644
> --- a/drivers/gpio/gpio-omap.c
> +++ b/drivers/gpio/gpio-omap.c
> @@ -806,7 +806,8 @@ static void omap_gpio_ack_irq(struct irq_data *d)
>  	struct gpio_bank *bank = omap_irq_data_get_bank(d);
>  	unsigned offset = d->hwirq;
>  
> -	omap_clear_gpio_irqstatus(bank, offset);
> +	if (bank->level_mask & BIT(offset))
> +		omap_clear_gpio_irqstatus(bank, offset);
>  }
>  
>  static void omap_gpio_mask_irq(struct irq_data *d)
> 
> And that did the trick. So far I tried IR decoder and decoding sometimes
> fails, but I cannot say anything certain until I check with scope (which
> I do tomorrow)

Another good code review catch by Grygorii! :)

Can you guys please add a comment there to the code when posting the
proper patch? Something like:

/*
 * Edge GPIOs are already cleared during handling, clearing
 * them here again will cause lost interrupts.
 */

And also add a proper Fixes tag so this gets propagated to the
stable kernels. It really seems we've had this for a long time.

Regards,

Tony
--
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
Ladislav Michl March 22, 2017, 10:30 p.m. UTC | #2
On Wed, Mar 22, 2017 at 10:17:14AM -0700, Tony Lindgren wrote:
> Another good code review catch by Grygorii! :)
> 
> Can you guys please add a comment there to the code when posting the
> proper patch? Something like:
> 
> /*
>  * Edge GPIOs are already cleared during handling, clearing
>  * them here again will cause lost interrupts.
>  */

Just a though, as we are acking edge irq in demux handler
handle_simple_irq should do the job as well.

> And also add a proper Fixes tag so this gets propagated to the
> stable kernels. It really seems we've had this for a long time.

Well, it makes things better, but it is not good enough, yet...
Bellow is a little test code - I'm feeding gpio159 from signal
generator and expecting to see nearly the same on gpio161.
Now, if you connect speaker to output gpio, you do not even
need a scope and you can easily debug with your ears.

	ladis

    _       ,/'
   (_).  ,/'
   __  ::
  (__)'  `\.
            `\.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/irq.h>

struct gpio_test_dev {
	int gpio_in;
	int gpio_out;
};

static struct gpio_test_dev dev;

static irqreturn_t gpio_irq(int irq, void *dev_id)
{
	struct gpio_test_dev *gpio_dev = dev_id;
	int val;

	val = gpio_get_value(gpio_dev->gpio_in);
	if (val < 0)
		goto err_get_value;

	gpio_set_value(gpio_dev->gpio_out, val);

err_get_value:
	return IRQ_HANDLED;
}

static int __init gpio_test_init(void)
{
	int rc;

	dev.gpio_in = 159;
	dev.gpio_out = 161;

	rc = gpio_request(dev.gpio_in, "gpio-in");
	if (rc < 0)
		goto err;
	rc  = gpio_direction_input(dev.gpio_in);
	if (rc < 0)
		goto err_free_input;

	rc = gpio_request(dev.gpio_out, "gpio-out");
	if (rc < 0)
		goto err_free_input;
	rc  = gpio_direction_output(dev.gpio_out, 0);
	if (rc < 0)
		goto err_free_output;

	rc = request_irq(gpio_to_irq(dev.gpio_in), gpio_irq,
			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
			"gpio-irq", &dev);
	if (rc < 0)
		goto err_free_output;

	return 0;

err_free_output:
	gpio_free(dev.gpio_out);
err_free_input:
	gpio_free(dev.gpio_in);
err:
	return rc;
}

static void __exit gpio_test_exit(void)
{
	free_irq(gpio_to_irq(dev.gpio_in), &dev);
	gpio_free(dev.gpio_in);
	gpio_free(dev.gpio_out);
}

module_init(gpio_test_init);
module_exit(gpio_test_exit);

MODULE_DESCRIPTION("GPIO interrupt test driver");
MODULE_LICENSE("GPL v2");
--
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
diff mbox

Patch

diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index efc85a279d54..9381763e1fec 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -806,7 +806,8 @@  static void omap_gpio_ack_irq(struct irq_data *d)
 	struct gpio_bank *bank = omap_irq_data_get_bank(d);
 	unsigned offset = d->hwirq;
 
-	omap_clear_gpio_irqstatus(bank, offset);
+	if (bank->level_mask & BIT(offset))
+		omap_clear_gpio_irqstatus(bank, offset);
 }
 
 static void omap_gpio_mask_irq(struct irq_data *d)