]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
gpio: pca953x: Add support for level-triggered interrupts
authorPotin Lai <potin.lai.pt@gmail.com>
Wed, 9 Apr 2025 15:37:30 +0000 (23:37 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 17 Jan 2026 15:31:22 +0000 (16:31 +0100)
[ Upstream commit 417b0f8d08f878615de9481c6e8827fbc8b57ed2 ]

Adds support for level-triggered interrupts in the PCA953x GPIO
expander driver. Previously, the driver only supported edge-triggered
interrupts, which could lead to missed events in scenarios where an
interrupt condition persists until it is explicitly cleared.

By enabling level-triggered interrupts, the driver can now detect and
respond to sustained interrupt conditions more reliably.

Signed-off-by: Potin Lai <potin.lai.pt@gmail.com>
Link: https://lore.kernel.org/r/20250409-gpio-pca953x-level-triggered-irq-v3-1-7f184d814934@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Stable-dep-of: 014a17deb412 ("gpio: pca953x: handle short interrupt pulses on PCAL devices")
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/gpio/gpio-pca953x.c

index bb7c1bf5f856e3d392ca0ff4d5498bfb1da4cea0..76879dc6461c4cd7a12752aeaaa98f143747fd9b 100644 (file)
@@ -215,6 +215,8 @@ struct pca953x_chip {
        DECLARE_BITMAP(irq_stat, MAX_LINE);
        DECLARE_BITMAP(irq_trig_raise, MAX_LINE);
        DECLARE_BITMAP(irq_trig_fall, MAX_LINE);
+       DECLARE_BITMAP(irq_trig_level_high, MAX_LINE);
+       DECLARE_BITMAP(irq_trig_level_low, MAX_LINE);
 #endif
        atomic_t wakeup_path;
 
@@ -773,6 +775,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
        pca953x_read_regs(chip, chip->regs->direction, reg_direction);
 
        bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
+       bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio);
+       bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio);
        bitmap_complement(reg_direction, reg_direction, gc->ngpio);
        bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio);
 
@@ -790,13 +794,15 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type)
        struct device *dev = &chip->client->dev;
        irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       if (!(type & IRQ_TYPE_EDGE_BOTH)) {
+       if (!(type & IRQ_TYPE_SENSE_MASK)) {
                dev_err(dev, "irq %d: unsupported type %d\n", d->irq, type);
                return -EINVAL;
        }
 
        assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING);
        assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING);
+       assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW);
+       assign_bit(hwirq, chip->irq_trig_level_high, type & IRQ_TYPE_LEVEL_HIGH);
 
        return 0;
 }
@@ -809,6 +815,8 @@ static void pca953x_irq_shutdown(struct irq_data *d)
 
        clear_bit(hwirq, chip->irq_trig_raise);
        clear_bit(hwirq, chip->irq_trig_fall);
+       clear_bit(hwirq, chip->irq_trig_level_low);
+       clear_bit(hwirq, chip->irq_trig_level_high);
 }
 
 static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p)
@@ -839,6 +847,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
        DECLARE_BITMAP(cur_stat, MAX_LINE);
        DECLARE_BITMAP(new_stat, MAX_LINE);
        DECLARE_BITMAP(trigger, MAX_LINE);
+       DECLARE_BITMAP(edges, MAX_LINE);
        int ret;
 
        ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
@@ -856,13 +865,26 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
 
        bitmap_copy(chip->irq_stat, new_stat, gc->ngpio);
 
-       if (bitmap_empty(trigger, gc->ngpio))
-               return false;
+       if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) &&
+           bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) {
+               if (bitmap_empty(trigger, gc->ngpio))
+                       return false;
+       }
 
        bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);
        bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio);
-       bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio);
-       bitmap_and(pending, new_stat, trigger, gc->ngpio);
+       bitmap_or(edges, old_stat, cur_stat, gc->ngpio);
+       bitmap_and(pending, edges, trigger, gc->ngpio);
+
+       bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio);
+       bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);
+       bitmap_or(pending, pending, cur_stat, gc->ngpio);
+
+       bitmap_complement(cur_stat, new_stat, gc->ngpio);
+       bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio);
+       bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio);
+       bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio);
+       bitmap_or(pending, pending, old_stat, gc->ngpio);
 
        return !bitmap_empty(pending, gc->ngpio);
 }