| From c5bccccf0ed9c3f515416f49500a215a93a38ef3 Mon Sep 17 00:00:00 2001 |
| From: Paul Cercueil <paul@crapouillou.net> |
| Date: Mon, 22 Jun 2020 23:45:47 +0200 |
| Subject: [PATCH] pinctrl: ingenic: Enhance support for IRQ_TYPE_EDGE_BOTH |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| commit 1c95348ba327fe8621d3680890c2341523d3524a upstream. |
| |
| Ingenic SoCs don't natively support registering an interrupt for both |
| rising and falling edges. This has to be emulated in software. |
| |
| Until now, this was emulated by switching back and forth between |
| IRQ_TYPE_EDGE_RISING and IRQ_TYPE_EDGE_FALLING according to the level of |
| the GPIO. While this worked most of the time, when used with GPIOs that |
| need debouncing, some events would be lost. For instance, between the |
| time a falling-edge interrupt happens and the interrupt handler |
| configures the hardware for rising-edge, the level of the pin may have |
| already risen, and the rising-edge event is lost. |
| |
| To address that issue, instead of switching back and forth between |
| IRQ_TYPE_EDGE_RISING and IRQ_TYPE_EDGE_FALLING, we now switch back and |
| forth between IRQ_TYPE_LEVEL_LOW and IRQ_TYPE_LEVEL_HIGH. Since we |
| always switch in the interrupt handler, they actually permit to detect |
| level changes. In the example above, if the pin level rises before |
| switching the IRQ type from IRQ_TYPE_LEVEL_LOW to IRQ_TYPE_LEVEL_HIGH, |
| a new interrupt will raise as soon as the handler exits, and the |
| rising-edge event will be properly detected. |
| |
| Fixes: e72394e2ea19 ("pinctrl: ingenic: Merge GPIO functionality") |
| Reported-by: João Henrique <johnnyonflame@hotmail.com> |
| Signed-off-by: Paul Cercueil <paul@crapouillou.net> |
| Tested-by: João Henrique <johnnyonflame@hotmail.com> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20200622214548.265417-1-paul@crapouillou.net |
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> |
| |
| diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c |
| index c8ef3b91a730..0e95c247def6 100644 |
| --- a/drivers/pinctrl/pinctrl-ingenic.c |
| +++ b/drivers/pinctrl/pinctrl-ingenic.c |
| @@ -873,9 +873,9 @@ static void ingenic_gpio_irq_ack(struct irq_data *irqd) |
| */ |
| high = ingenic_gpio_get_value(jzgc, irq); |
| if (high) |
| - irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING); |
| + irq_set_type(jzgc, irq, IRQ_TYPE_LEVEL_LOW); |
| else |
| - irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING); |
| + irq_set_type(jzgc, irq, IRQ_TYPE_LEVEL_HIGH); |
| } |
| |
| if (jzgc->jzpc->version >= ID_JZ4770) |
| @@ -911,7 +911,7 @@ static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) |
| */ |
| bool high = ingenic_gpio_get_value(jzgc, irqd->hwirq); |
| |
| - type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; |
| + type = high ? IRQ_TYPE_LEVEL_LOW : IRQ_TYPE_LEVEL_HIGH; |
| } |
| |
| irq_set_type(jzgc, irqd->hwirq, type); |
| -- |
| 2.27.0 |
| |