[PATCH] gpio: pca953x: Add support for level-triggered interrupts

Potin Lai posted 1 patch 9 months ago
There is a newer version of this series
drivers/gpio/gpio-pca953x.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
[PATCH] gpio: pca953x: Add support for level-triggered interrupts
Posted by Potin Lai 9 months ago
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>
Re: [PATCH] gpio: pca953x: Add support for level-triggered interrupts
Posted by kernel test robot 9 months ago
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
Re: [PATCH] gpio: pca953x: Add support for level-triggered interrupts
Posted by kernel test robot 9 months ago
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