Re: [PATCH] gpio:mcp23s08 Fixed missing interrupts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 14/02/2017 00:25, Robert Middleton wrote:
On Mon, Feb 13, 2017 at 12:15 AM, Phil Reid <preid@xxxxxxxxxxxxxxxxx> wrote:
G'day Rob,

I've tested this and it works for my use case.
Which has rising & falling irqs and not very fast inputs.

A couple of comment below. I'm no expert on the kernel standards thou.

I've also cc-dd the gpio maintainers. See MAINTAINERS file for a list people
to copy for different subfolder etc.


On 12/02/2017 04:02, robert.middleton@xxxxxxxxxx wrote:

From: Robert Middleton <robert.middleton@xxxxxxxxxx>

When an interrupt occurs on an MCP23S08 chip, the INTF register will only
contain one bit as causing the interrupt.  If more than two pins change at
the same time on the chip, this causes one of the pins to not be reported.
This patch fixes the logic for checking if a pin has changed, so that
multiple pins will always cause more than one change.


Comments need to be wrapped to 75 chars.
Also needs signed-by. "git format-patch -s" will do it for you.


Ah, I didn't realize that the signed-by was required(I was assuming
signed-by was
only for the actual kernel maintainers).


---
 drivers/gpio/gpio-mcp23s08.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 99d37b5..b7ceee3 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -337,7 +337,7 @@ mcp23s08_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
 static irqreturn_t mcp23s08_irq(int irq, void *data)
 {
        struct mcp23s08 *mcp = data;
-       int intcap, intf, i;
+       int intcap, intf, i, gpio, gpio_cache;
        unsigned int child_irq;

        mutex_lock(&mcp->lock);
@@ -356,14 +356,30 @@ static irqreturn_t mcp23s08_irq(int irq, void *data)
        }

        mcp->cache[MCP_INTCAP] = intcap;
+
+       /* This clears the interrupt */

Only for no s18 chips. But it should matter for them.


+       gpio = mcp->ops->read(mcp, MCP_GPIO);
+       if (gpio < 0) {
+               mutex_unlock(&mcp->lock);
+               return IRQ_HANDLED;
+       }
+       gpio_cache = mcp->cache[MCP_GPIO];
+       mcp->cache[MCP_GPIO] = gpio;
        mutex_unlock(&mcp->lock);


        for (i = 0; i < mcp->chip.ngpio; i++) {
-               if ((BIT(i) & mcp->cache[MCP_INTF]) &&
-                   ((BIT(i) & intcap & mcp->irq_rise) ||
-                    (mcp->irq_fall & ~intcap & BIT(i)) ||
-                    (BIT(i) & mcp->cache[MCP_INTCON]))) {
+               /* We must check all of the inputs on the chip,
+                * otherwise we may not notice a change on >=2 pins
+                */
+               int bit_changed = (BIT(i) & gpio_cache) !=
+                       (BIT(i) & mcp->cache[MCP_GPIO]);
+               int defval_changed = (BIT(i) & mcp->cache[MCP_GPIO]) !=
+                       (BIT(i) & mcp->cache[MCP_DEFVAL]);
+               if ((bit_changed && (BIT(i) & mcp->irq_rise)) ||
+                       (bit_changed && (~BIT(i) & mcp->irq_fall)) ||
+                       ((BIT(i) & mcp->cache[MCP_INTCON]) &&

trailing space on this line.
I'm not sure what the formatting should be here, but most other kernel code
I've looked
using space to align to the open bracket of the if statement when the
condition exceeds
one line.

+                       defval_changed)) {
                        child_irq = irq_find_mapping(mcp->chip.irqdomain,
i);
                        handle_nested_irq(child_irq);
                }


bit_changed / defval_changed could be of type bool to be clearer that it's
meant to
be a bool result. These should also be defined at the top of the function I
believe.


I forgot that the kernel actually has a 'bool' type; I was thinking it was
C89 only.

Are variables always supposed to be at the start of a function?  I'm wondering
because since they are used only in that one block, it doesn't make sense
to me at least that you would want them as function-level variables.

I pretty sure the logic will trigger a interrupt on both a rising and
falling edge of the input
even if only falling edge is requested. But I may be missing something
obvious.


I think that you're correct here actually; I thought I had tested this
by setting
the 'rising'/'falling' edge in sysfs, but testing it again just now I
get changes in
userspace on both turning it on and off.  I'll look back into it today/tomorrow.

Also what is the impact to short interrupt events. Should the INTCAP / INTF
flags still be consider as well.
ie. A short pulse on input which may happen before GPIO input register be
read, but captured by INTCAP
be consider as well. This change may break systems relying on that
behaviour.


The reason that I wasn't able to compare against the INTF register was because
since only 1 bit is ever set, you don't get more than 1 pin change event.  For
example, if pin 1 changes, INTF would read 0x0001.
If both pins 1 and 2 change at the same time, INTF would read either 0x0002
or 0x0001, it wouldn't have both set.  From the testing that I did, it
looked like
INTCAP would contain the proper data, but I wasn't sure if that would always
be the case.

My reasoning for reading the GPIO input register was to clear the interrupt as
soon as possible, so that if there was a short interrupt the kernel would get
the interrupt again.  I'm not sure if that is the proper approach; I have been
told before that in the IRQ handler you should get the data and clear the
interrupt as fast as possible in order to wait for more interrupts.

I'm suggest combining the two approaches.
If the pin toggles quickly an you only read the GPIO input to determine which pins triggered
the irq you may miss calling the nested irq handler.
So you'd need to consider the INTF / INTCAP state and after than also check GPIO state.
eg:
Pin trigger int. (example high)
INTF / INTCAP get set.
Pin returns to previous state (example low)
ISR handler run and reads the the GPIO.
No IRQ would be detected if only the GPIO state is considered.

But if you also look at INTF INTCAP you could trigger both a rising and falling irq.

Considering that the chip is SPI / I2c it can be a relatively long time before the handler
can actually read the GPIO state.


FYI: You can run scripts/checkpatch.pl against you patch to identify some of
the problems highlighted above.

not related to your changes: this function always returns IRQ_HANDLED in all
cases.
Other GPIO drivers return IRQ_NONE if no handler was called or in some case
if an error occurred
in reading the state. I'm not sure what the correct behaviour is supposed to
be.
Docs suggest IRQ_NONE helps the kernel detect bad irq.
Someone more knowledgeable may be able to offer a hint on that one.
I can do a patch if this needs changing.



--
Regards
Phil Reid

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



[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux