diff options
Diffstat (limited to 'drivers/gpio/gpio-mcp23s08.c')
-rw-r--r-- | drivers/gpio/gpio-mcp23s08.c | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index bdb692345428..2a57d024481d 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c | |||
@@ -270,8 +270,10 @@ mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) | |||
270 | static irqreturn_t mcp23s08_irq(int irq, void *data) | 270 | static irqreturn_t mcp23s08_irq(int irq, void *data) |
271 | { | 271 | { |
272 | struct mcp23s08 *mcp = data; | 272 | struct mcp23s08 *mcp = data; |
273 | int intcap, intf, i; | 273 | int intcap, intf, i, gpio, gpio_orig, intcap_mask; |
274 | unsigned int child_irq; | 274 | unsigned int child_irq; |
275 | bool intf_set, intcap_changed, gpio_bit_changed, | ||
276 | defval_changed, gpio_set; | ||
275 | 277 | ||
276 | mutex_lock(&mcp->lock); | 278 | mutex_lock(&mcp->lock); |
277 | if (mcp_read(mcp, MCP_INTF, &intf) < 0) { | 279 | if (mcp_read(mcp, MCP_INTF, &intf) < 0) { |
@@ -287,14 +289,67 @@ static irqreturn_t mcp23s08_irq(int irq, void *data) | |||
287 | } | 289 | } |
288 | 290 | ||
289 | mcp->cache[MCP_INTCAP] = intcap; | 291 | mcp->cache[MCP_INTCAP] = intcap; |
292 | |||
293 | /* This clears the interrupt(configurable on S18) */ | ||
294 | if (mcp_read(mcp, MCP_GPIO, &gpio) < 0) { | ||
295 | mutex_unlock(&mcp->lock); | ||
296 | return IRQ_HANDLED; | ||
297 | } | ||
298 | gpio_orig = mcp->cache[MCP_GPIO]; | ||
299 | mcp->cache[MCP_GPIO] = gpio; | ||
290 | mutex_unlock(&mcp->lock); | 300 | mutex_unlock(&mcp->lock); |
291 | 301 | ||
302 | if (mcp->cache[MCP_INTF] == 0) { | ||
303 | /* There is no interrupt pending */ | ||
304 | return IRQ_HANDLED; | ||
305 | } | ||
306 | |||
307 | dev_dbg(mcp->chip.parent, | ||
308 | "intcap 0x%04X intf 0x%04X gpio_orig 0x%04X gpio 0x%04X\n", | ||
309 | intcap, intf, gpio_orig, gpio); | ||
292 | 310 | ||
293 | for (i = 0; i < mcp->chip.ngpio; i++) { | 311 | for (i = 0; i < mcp->chip.ngpio; i++) { |
294 | if ((BIT(i) & mcp->cache[MCP_INTF]) && | 312 | /* We must check all of the inputs on the chip, |
295 | ((BIT(i) & intcap & mcp->irq_rise) || | 313 | * otherwise we may not notice a change on >=2 pins. |
296 | (mcp->irq_fall & ~intcap & BIT(i)) || | 314 | * |
297 | (BIT(i) & mcp->cache[MCP_INTCON]))) { | 315 | * On at least the mcp23s17, INTCAP is only updated |
316 | * one byte at a time(INTCAPA and INTCAPB are | ||
317 | * not written to at the same time - only on a per-bank | ||
318 | * basis). | ||
319 | * | ||
320 | * INTF only contains the single bit that caused the | ||
321 | * interrupt per-bank. On the mcp23s17, there is | ||
322 | * INTFA and INTFB. If two pins are changed on the A | ||
323 | * side at the same time, INTF will only have one bit | ||
324 | * set. If one pin on the A side and one pin on the B | ||
325 | * side are changed at the same time, INTF will have | ||
326 | * two bits set. Thus, INTF can't be the only check | ||
327 | * to see if the input has changed. | ||
328 | */ | ||
329 | |||
330 | intf_set = BIT(i) & mcp->cache[MCP_INTF]; | ||
331 | if (i < 8 && intf_set) | ||
332 | intcap_mask = 0x00FF; | ||
333 | else if (i >= 8 && intf_set) | ||
334 | intcap_mask = 0xFF00; | ||
335 | else | ||
336 | intcap_mask = 0x00; | ||
337 | |||
338 | intcap_changed = (intcap_mask & | ||
339 | (BIT(i) & mcp->cache[MCP_INTCAP])) != | ||
340 | (intcap_mask & (BIT(i) & gpio_orig)); | ||
341 | gpio_set = BIT(i) & mcp->cache[MCP_GPIO]; | ||
342 | gpio_bit_changed = (BIT(i) & gpio_orig) != | ||
343 | (BIT(i) & mcp->cache[MCP_GPIO]); | ||
344 | defval_changed = (BIT(i) & mcp->cache[MCP_INTCON]) && | ||
345 | ((BIT(i) & mcp->cache[MCP_GPIO]) != | ||
346 | (BIT(i) & mcp->cache[MCP_DEFVAL])); | ||
347 | |||
348 | if (((gpio_bit_changed || intcap_changed) && | ||
349 | (BIT(i) & mcp->irq_rise) && gpio_set) || | ||
350 | ((gpio_bit_changed || intcap_changed) && | ||
351 | (BIT(i) & mcp->irq_fall) && !gpio_set) || | ||
352 | defval_changed) { | ||
298 | child_irq = irq_find_mapping(mcp->chip.irqdomain, i); | 353 | child_irq = irq_find_mapping(mcp->chip.irqdomain, i); |
299 | handle_nested_irq(child_irq); | 354 | handle_nested_irq(child_irq); |
300 | } | 355 | } |