drivers/gpio/gpio-pca953x.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-)
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>
---
Adds support for level-triggered interrupts in the PCA953x GPIO
expander driver.
---
drivers/gpio/gpio-pca953x.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index be4c9981ebc4..2c4034b39aff 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -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;
if (chip->driver_data & PCA_PCAL) {
@@ -875,13 +884,25 @@ 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 (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
+ 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);
}
---
base-commit: 2014c95afecee3e76ca4a56956a936e23283f05b
change-id: 20250318-gpio-pca953x-level-triggered-irq-5cf7f1cab561
Best regards,
--
Potin Lai <potin.lai.pt@gmail.com>
Hi Potin,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 2014c95afecee3e76ca4a56956a936e23283f05b]
url: https://github.com/intel-lab-lkp/linux/commits/Potin-Lai/gpio-pca953x-Add-support-for-level-triggered-interrupts/20250318-004441
base: 2014c95afecee3e76ca4a56956a936e23283f05b
patch link: https://lore.kernel.org/r/20250318-gpio-pca953x-level-triggered-irq-v1-1-0c4943d92425%40gmail.com
patch subject: [PATCH] gpio: pca953x: Add support for level-triggered interrupts
config: arm-randconfig-001-20250318 (https://download.01.org/0day-ci/archive/20250318/202503181425.OndgH7Dh-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250318/202503181425.OndgH7Dh-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503181425.OndgH7Dh-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/gpio/gpio-pca953x.c: In function 'pca953x_irq_pending':
>> drivers/gpio/gpio-pca953x.c:887:13: warning: the comparison will always evaluate as 'true' for the address of 'irq_trig_level_high' will never be NULL [-Waddress]
887 | if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
| ^
In file included from include/linux/atomic.h:5,
from drivers/gpio/gpio-pca953x.c:11:
drivers/gpio/gpio-pca953x.c:218:24: note: 'irq_trig_level_high' declared here
218 | DECLARE_BITMAP(irq_trig_level_high, MAX_LINE);
| ^~~~~~~~~~~~~~~~~~~
include/linux/types.h:11:23: note: in definition of macro 'DECLARE_BITMAP'
11 | unsigned long name[BITS_TO_LONGS(bits)]
| ^~~~
>> drivers/gpio/gpio-pca953x.c:887:43: warning: the comparison will always evaluate as 'true' for the address of 'irq_trig_level_low' will never be NULL [-Waddress]
887 | if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
| ^
drivers/gpio/gpio-pca953x.c:219:24: note: 'irq_trig_level_low' declared here
219 | DECLARE_BITMAP(irq_trig_level_low, MAX_LINE);
| ^~~~~~~~~~~~~~~~~~
include/linux/types.h:11:23: note: in definition of macro 'DECLARE_BITMAP'
11 | unsigned long name[BITS_TO_LONGS(bits)]
| ^~~~
vim +887 drivers/gpio/gpio-pca953x.c
841
842 static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pending)
843 {
844 struct gpio_chip *gc = &chip->gpio_chip;
845 DECLARE_BITMAP(reg_direction, MAX_LINE);
846 DECLARE_BITMAP(old_stat, MAX_LINE);
847 DECLARE_BITMAP(cur_stat, MAX_LINE);
848 DECLARE_BITMAP(new_stat, MAX_LINE);
849 DECLARE_BITMAP(trigger, MAX_LINE);
850 DECLARE_BITMAP(edges, MAX_LINE);
851 int ret;
852
853 if (chip->driver_data & PCA_PCAL) {
854 /* Read the current interrupt status from the device */
855 ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger);
856 if (ret)
857 return false;
858
859 /* Check latched inputs and clear interrupt status */
860 ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
861 if (ret)
862 return false;
863
864 /* Apply filter for rising/falling edge selection */
865 bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, cur_stat, gc->ngpio);
866
867 bitmap_and(pending, new_stat, trigger, gc->ngpio);
868
869 return !bitmap_empty(pending, gc->ngpio);
870 }
871
872 ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
873 if (ret)
874 return false;
875
876 /* Remove output pins from the equation */
877 pca953x_read_regs(chip, chip->regs->direction, reg_direction);
878
879 bitmap_copy(old_stat, chip->irq_stat, gc->ngpio);
880
881 bitmap_and(new_stat, cur_stat, reg_direction, gc->ngpio);
882 bitmap_xor(cur_stat, new_stat, old_stat, gc->ngpio);
883 bitmap_and(trigger, cur_stat, chip->irq_mask, gc->ngpio);
884
885 bitmap_copy(chip->irq_stat, new_stat, gc->ngpio);
886
> 887 if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
888 if (bitmap_empty(trigger, gc->ngpio))
889 return false;
890 }
891
892 bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);
893 bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio);
894 bitmap_or(edges, old_stat, cur_stat, gc->ngpio);
895 bitmap_and(pending, edges, trigger, gc->ngpio);
896
897 bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio);
898 bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);
899 bitmap_or(pending, pending, cur_stat, gc->ngpio);
900
901 bitmap_complement(cur_stat, new_stat, gc->ngpio);
902 bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio);
903 bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio);
904 bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio);
905 bitmap_or(pending, pending, old_stat, gc->ngpio);
906
907 return !bitmap_empty(pending, gc->ngpio);
908 }
909
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Potin,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 2014c95afecee3e76ca4a56956a936e23283f05b]
url: https://github.com/intel-lab-lkp/linux/commits/Potin-Lai/gpio-pca953x-Add-support-for-level-triggered-interrupts/20250318-004441
base: 2014c95afecee3e76ca4a56956a936e23283f05b
patch link: https://lore.kernel.org/r/20250318-gpio-pca953x-level-triggered-irq-v1-1-0c4943d92425%40gmail.com
patch subject: [PATCH] gpio: pca953x: Add support for level-triggered interrupts
config: x86_64-buildonly-randconfig-002-20250318 (https://download.01.org/0day-ci/archive/20250318/202503181231.ZqKVm0Z8-lkp@intel.com/config)
compiler: clang version 20.1.0 (https://github.com/llvm/llvm-project 24a30daaa559829ad079f2ff7f73eb4e18095f88)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250318/202503181231.ZqKVm0Z8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503181231.ZqKVm0Z8-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/gpio/gpio-pca953x.c:887:43: warning: address of array 'chip->irq_trig_level_low' will always evaluate to 'true' [-Wpointer-bool-conversion]
887 | if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
| ~~~~~~~^~~~~~~~~~~~~~~~~~
>> drivers/gpio/gpio-pca953x.c:887:13: warning: address of array 'chip->irq_trig_level_high' will always evaluate to 'true' [-Wpointer-bool-conversion]
887 | if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
| ~~~~~~~^~~~~~~~~~~~~~~~~~~
2 warnings generated.
vim +887 drivers/gpio/gpio-pca953x.c
841
842 static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pending)
843 {
844 struct gpio_chip *gc = &chip->gpio_chip;
845 DECLARE_BITMAP(reg_direction, MAX_LINE);
846 DECLARE_BITMAP(old_stat, MAX_LINE);
847 DECLARE_BITMAP(cur_stat, MAX_LINE);
848 DECLARE_BITMAP(new_stat, MAX_LINE);
849 DECLARE_BITMAP(trigger, MAX_LINE);
850 DECLARE_BITMAP(edges, MAX_LINE);
851 int ret;
852
853 if (chip->driver_data & PCA_PCAL) {
854 /* Read the current interrupt status from the device */
855 ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger);
856 if (ret)
857 return false;
858
859 /* Check latched inputs and clear interrupt status */
860 ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
861 if (ret)
862 return false;
863
864 /* Apply filter for rising/falling edge selection */
865 bitmap_replace(new_stat, chip->irq_trig_fall, chip->irq_trig_raise, cur_stat, gc->ngpio);
866
867 bitmap_and(pending, new_stat, trigger, gc->ngpio);
868
869 return !bitmap_empty(pending, gc->ngpio);
870 }
871
872 ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
873 if (ret)
874 return false;
875
876 /* Remove output pins from the equation */
877 pca953x_read_regs(chip, chip->regs->direction, reg_direction);
878
879 bitmap_copy(old_stat, chip->irq_stat, gc->ngpio);
880
881 bitmap_and(new_stat, cur_stat, reg_direction, gc->ngpio);
882 bitmap_xor(cur_stat, new_stat, old_stat, gc->ngpio);
883 bitmap_and(trigger, cur_stat, chip->irq_mask, gc->ngpio);
884
885 bitmap_copy(chip->irq_stat, new_stat, gc->ngpio);
886
> 887 if (!chip->irq_trig_level_high && !chip->irq_trig_level_low) {
888 if (bitmap_empty(trigger, gc->ngpio))
889 return false;
890 }
891
892 bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio);
893 bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio);
894 bitmap_or(edges, old_stat, cur_stat, gc->ngpio);
895 bitmap_and(pending, edges, trigger, gc->ngpio);
896
897 bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio);
898 bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);
899 bitmap_or(pending, pending, cur_stat, gc->ngpio);
900
901 bitmap_complement(cur_stat, new_stat, gc->ngpio);
902 bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio);
903 bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio);
904 bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio);
905 bitmap_or(pending, pending, old_stat, gc->ngpio);
906
907 return !bitmap_empty(pending, gc->ngpio);
908 }
909
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.