diff --git a/drivers/ioexpander/tca64xx.c b/drivers/ioexpander/tca64xx.c index e882bf26d25..3a9922721bc 100644 --- a/drivers/ioexpander/tca64xx.c +++ b/drivers/ioexpander/tca64xx.c @@ -919,7 +919,7 @@ static int tca64_multireadpin(FAR struct ioexpander_dev_s *dev, #ifdef CONFIG_TCA64XX_INT_ENABLE /* Update the input status with the 32 bits read from the expander */ - tca64_int_update(priv, pinset, ~0); + tca64_int_update(priv, pinset, PINSET_ALL); #endif errout_with_lock: @@ -971,11 +971,10 @@ static int tca64_attach(FAR struct ioexpander_dev_s *dev, ioe_pinset_t pinset, priv->cb[i].pinset = pinset; priv->cb[i].cbfunc = callback; ret = OK; + break; } } - /* Add this callback to the table */ - tca64_unlock(priv); return ret; } @@ -1003,10 +1002,12 @@ static void tca64_int_update(void *handle, ioe_pinset_t input, /* Check the changed bits from last read */ input = (priv->input & ~mask) | (input & mask); - diff = priv->input ^ input; + diff = priv->input ^ input; - if (!diff) + if (diff == 0) { + /* Nothing has changed */ + leave_critical_section(flags); return; } @@ -1031,7 +1032,7 @@ static void tca64_int_update(void *handle, ioe_pinset_t input, { /* Level triggered. Set intstat if in match level type. */ - if (((input & 1) != 0 && TCA64_LEVEL_HIGH(priv, pin)) || + if (((input & 1) != 0 && TCA64_LEVEL_HIGH(priv, pin)) || ((input & 1) == 0 && TCA64_LEVEL_LOW(priv, pin))) { priv->intstat |= 1 << pin; @@ -1093,7 +1094,7 @@ static void tca64_register_update(FAR struct tca64_dev_s *priv) /* Update the input status with the 32 bits read from the expander */ - tca64_int_update(priv, pinset, ~0); + tca64_int_update(priv, pinset, PINSET_ALL); } #endif @@ -1119,6 +1120,10 @@ static void tca64_irqworker(void *arg) DEBUGASSERT(priv != NULL && priv->config != NULL); + /* Get exclusive access to read inputs and assess pending interrupts. */ + + tca64_lock(priv); + /* Read the input register for pin 0 through the number of supported pins. * * The Input Port Register reflects the incoming logic levels of the pins, @@ -1136,26 +1141,31 @@ static void tca64_irqworker(void *arg) { gpioerr("ERROR: Failed to read input %u registers at %u: %d\n", nregs, regaddr, ret); - return; + tca64_unlock(priv); + goto errout_with_restart; } /* Update the input status with the 32 bits read from the expander */ - tca64_int_update(priv, pinset, ~0); + tca64_int_update(priv, pinset, PINSET_ALL); + + /* Sample and clear the pending interrupts. */ + + pinset = priv->intstat; + priv->intstat = 0; + tca64_unlock(priv); /* Perform pin interrupt callbacks */ for (i = 0; i < CONFIG_TCA64XX_INT_NCALLBACKS; i++) { - /* Is this entry valid (i.e., callback attached)? If so, did andy of - * the requested pin interrupts occur? - */ + /* Is this entry valid (i.e., callback attached)? */ if (priv->cb[i].cbfunc != NULL) { /* Did any of the requested pin interrupts occur? */ - ioe_pinset_t match = priv->intstat & priv->cb[i].pinset; + ioe_pinset_t match = pinset & priv->cb[i].pinset; if (match != 0) { /* Yes.. perform the callback */ @@ -1165,7 +1175,7 @@ static void tca64_irqworker(void *arg) } } - priv->intstat = 0; +errout_with_restart: #ifdef CONFIG_TCA64XX_INT_POLL /* Check for pending interrupts */ @@ -1332,6 +1342,12 @@ FAR struct ioexpander_dev_s *tca64_initialize(FAR struct i2c_master_s *i2c, priv->config = config; #ifdef CONFIG_TCA64XX_INT_ENABLE + /* Initial interrupt state: Edge triggered on both edges */ + + priv->trigger = PINSET_ALL; /* All edge triggered */ + priv->level[0] = PINSET_ALL; /* All rising edge */ + priv->level[1] = PINSET_ALL; /* All falling edge */ + #ifdef CONFIG_TCA64XX_INT_POLL /* Set up a timer to poll for missed interrupts */ diff --git a/drivers/ioexpander/tca64xx.h b/drivers/ioexpander/tca64xx.h index b1dd69118bc..704fbc8c81b 100644 --- a/drivers/ioexpander/tca64xx.h +++ b/drivers/ioexpander/tca64xx.h @@ -192,18 +192,18 @@ #define TCA64XX_POLLDELAY (CONFIG_TCA64XX_INT_POLLDELAY / USEC_PER_TICK) #define TCA64_LEVEL_SENSITIVE(d,p) \ - (((d)->trigger & (1 << (p))) == 0) + (((d)->trigger & ((ioe_pinset_t)1 << (p))) == 0) #define TCA64_LEVEL_HIGH(d,p) \ - (((d)->level[0] & (1 << (p))) != 0) + (((d)->level[0] & ((ioe_pinset_t)1 << (p))) != 0) #define TCA64_LEVEL_LOW(d,p) \ - (((d)->level[1] & (1 << (p))) != 0) + (((d)->level[1] & ((ioe_pinset_t)1 << (p))) != 0) #define TCA64_EDGE_SENSITIVE(d,p) \ - (((d)->trigger & (1 << (p))) != 0) + (((d)->trigger & ((ioe_pinset_t)1 << (p))) != 0) #define TCA64_EDGE_RISING(d,p) \ - (((d)->level[0] & (1 << (p))) != 0) + (((d)->level[0] & ((ioe_pinset_t)1 << (p))) != 0) #define TCA64_EDGE_FALLING(d,p) \ - (((d)->level[1] & (1 << (p))) != 0) + (((d)->level[1] & ((ioe_pinset_t)1 << (p))) != 0) #define TCA64_EDGE_BOTH(d,p) \ (TCA64_LEVEL_RISING(d,p) && TCA64_LEVEL_FALLING(d,p)) @@ -246,11 +246,11 @@ struct tca64_dev_s uint8_t addr; /* TCA64xx I2C address */ sem_t exclsem; /* Mutual exclusion */ +#ifdef CONFIG_IOEXPANDER_INT_ENABLE #ifdef CONFIG_TCA64XX_INT_POLL WDOG_ID wdog; /* Timer used to poll for missed interrupts */ #endif -#ifdef CONFIG_IOEXPANDER_INT_ENABLE ioe_pinset_t input; /* Last input registeres */ ioe_pinset_t intstat; /* Pending interrupts */ ioe_pinset_t trigger; /* Bit encoded: 0=level 1=edge */ diff --git a/include/nuttx/ioexpander/ioexpander.h b/include/nuttx/ioexpander/ioexpander.h index e844d984da6..6557f9f7bec 100644 --- a/include/nuttx/ioexpander/ioexpander.h +++ b/include/nuttx/ioexpander/ioexpander.h @@ -66,17 +66,19 @@ /* Pin options */ -#define IOEXPANDER_OPTION_INVERT 1 /* Set the "active" level for a pin */ -# define IOEXPANDER_VAL_NORMAL 0 /* Normal, no inversion */ -# define IOEXPANDER_VAL_INVERT 1 /* Inverted */ +#define IOEXPANDER_OPTION_INVERT 1 /* Set the "active" level for a pin */ +# define IOEXPANDER_VAL_NORMAL 0 /* Normal, no inversion */ +# define IOEXPANDER_VAL_INVERT 1 /* Inverted */ -#define IOEXPANDER_OPTION_INTCFG 2 /* Configure interrupt for a pin */ -# define IOEXPANDER_VAL_LEVEL (1 << 0) /* xx1 Interrupt on level (vs. edge) */ -# define IOEXPANDER_VAL_HIGH (1 << 2) /* 001 Interrupt on high level */ -# define IOEXPANDER_VAL_LOW (3 << 2) /* 011 Interrupt on low level */ -# define IOEXPANDER_VAL_RISING (2 << 2) /* 010 Interrupt on rising edge */ -# define IOEXPANDER_VAL_FALLING (4 << 2) /* 100 Interrupt on falling edge */ -# define IOEXPANDER_VAL_BOTH (6 << 2) /* 110 Interrupt on both edges */ +#define IOEXPANDER_OPTION_INTCFG 2 /* Configure interrupt for a pin */ +# define IOEXPANDER_VAL_LEVEL 1 /* xx1 Interrupt on level (vs. edge) */ +# define IOEXPANDER_VAL_HIGH 1 /* 001 Interrupt on high level */ +# define IOEXPANDER_VAL_LOW 3 /* 011 Interrupt on low level */ +# define IOEXPANDER_VAL_RISING 2 /* 010 Interrupt on rising edge */ +# define IOEXPANDER_VAL_FALLING 4 /* 100 Interrupt on falling edge */ +# define IOEXPANDER_VAL_BOTH 6 /* 110 Interrupt on both edges */ + +#define PINSET_ALL (~((ioe_pinset_t)0)) /* Access macros ************************************************************/