Skip to content

Commit 014a17d

Browse files
Ernest Van HoeckeBartosz Golaszewski
authored andcommitted
gpio: pca953x: handle short interrupt pulses on PCAL devices
GPIO drivers with latch input support may miss short pulses on input pins even when input latching is enabled. The generic interrupt logic in the pca953x driver reports interrupts by comparing the current input value against the previously sampled one and only signals an event when a level change is observed between two reads. For short pulses, the first edge is captured when the input register is read, but if the signal returns to its previous level before the read, the second edge is not observed. As a result, successive pulses can produce identical input values at read time and no level change is detected, causing interrupts to be missed. Below timing diagram shows this situation where the top signal is the input pin level and the bottom signal indicates the latched value. ─────┐ ┌──*───────────────┐ ┌──*─────────────────┐ ┌──*─── │ │ . │ │ . │ │ . │ │ │ │ │ │ │ │ │ └──*──┘ │ └──*──┘ │ └──*──┘ │ Input │ │ │ │ │ │ ▼ │ ▼ │ ▼ │ IRQ │ IRQ │ IRQ │ . . . ─────┐ .┌──────────────┐ .┌────────────────┐ .┌── │ │ │ │ │ │ │ │ │ │ │ │ └────────*┘ └────────*┘ └────────*┘ Latched │ │ │ ▼ ▼ ▼ READ 0 READ 0 READ 0 NO CHANGE NO CHANGE PCAL variants provide an interrupt status register that records which pins triggered an interrupt, but the status and input registers cannot be read atomically. The interrupt status is only cleared when the input port is read, and the input value must also be read to determine the triggering edge. If another interrupt occurs on a different line after the status register has been read but before the input register is sampled, that event will not be reflected in the earlier status snapshot, so relying solely on the interrupt status register is also insufficient. Support for input latching and interrupt status handling was previously added by [1], but the interrupt status-based logic was reverted by [2] due to these issues. This patch addresses the original problem by combining both sources of information. Events indicated by the interrupt status register are merged with events detected through the existing level-change logic. As a result: * short pulses, whose second edges are invisible, are detected via the interrupt status register, and * interrupts that occur between the status and input reads are still caught by the generic level-change logic. This significantly improves robustness on devices that signal interrupts as short pulses, while avoiding the issues that led to the earlier reversion. In practice, even if only the first edge of a pulse is observable, the interrupt is reliably detected. This fixes missed interrupts from an Ilitek touch controller with its interrupt line connected to a PCAL6416A, where active-low pulses are approximately 200 us long. [1] commit 44896be ("gpio: pca953x: add PCAL9535 interrupt support for Galileo Gen2") [2] commit d6179f6 ("gpio: pca953x: Improve interrupt support") Fixes: d6179f6 ("gpio: pca953x: Improve interrupt support") Signed-off-by: Ernest Van Hoecke <ernest.vanhoecke@toradex.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20251217153050.142057-1-ernestvanhoecke@gmail.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent a7ac22d commit 014a17d

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

drivers/gpio/gpio-pca953x.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,14 +943,35 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
943943
DECLARE_BITMAP(old_stat, MAX_LINE);
944944
DECLARE_BITMAP(cur_stat, MAX_LINE);
945945
DECLARE_BITMAP(new_stat, MAX_LINE);
946+
DECLARE_BITMAP(int_stat, MAX_LINE);
946947
DECLARE_BITMAP(trigger, MAX_LINE);
947948
DECLARE_BITMAP(edges, MAX_LINE);
948949
int ret;
949950

951+
if (chip->driver_data & PCA_PCAL) {
952+
/* Read INT_STAT before it is cleared by the input-port read. */
953+
ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, int_stat);
954+
if (ret)
955+
return false;
956+
}
957+
950958
ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
951959
if (ret)
952960
return false;
953961

962+
if (chip->driver_data & PCA_PCAL) {
963+
/* Detect short pulses via INT_STAT. */
964+
bitmap_and(trigger, int_stat, chip->irq_mask, gc->ngpio);
965+
966+
/* Apply filter for rising/falling edge selection. */
967+
bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise,
968+
cur_stat, gc->ngpio);
969+
970+
bitmap_and(int_stat, new_stat, trigger, gc->ngpio);
971+
} else {
972+
bitmap_zero(int_stat, gc->ngpio);
973+
}
974+
954975
/* Remove output pins from the equation */
955976
pca953x_read_regs(chip, chip->regs->direction, reg_direction);
956977

@@ -964,14 +985,16 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
964985

965986
if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) &&
966987
bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) {
967-
if (bitmap_empty(trigger, gc->ngpio))
988+
if (bitmap_empty(trigger, gc->ngpio) &&
989+
bitmap_empty(int_stat, gc->ngpio))
968990
return false;
969991
}
970992

971993
bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);
972994
bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio);
973995
bitmap_or(edges, old_stat, cur_stat, gc->ngpio);
974996
bitmap_and(pending, edges, trigger, gc->ngpio);
997+
bitmap_or(pending, pending, int_stat, gc->ngpio);
975998

976999
bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio);
9771000
bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);

0 commit comments

Comments
 (0)