diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index e7c2fa3a2fe..e5294dcc42b 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -78,6 +78,13 @@ config MCP23X17_INT_ENABLE ---help--- Enable driver interrupt functionality +config MCP23X17_INT_MIRROR + bool "Enable MCP23x17 Interrupt Mirror" + default n + select MCP23X17_INT_ENABLE + ---help--- + Enable driver interrupt mirror functionality + config MCP23X17_INT_NCALLBACKS int "Max number of interrupt callbacks" default 4 diff --git a/drivers/ioexpander/mcp23x17.c b/drivers/ioexpander/mcp23x17.c index 34b864ff6dc..ed6a272cdd2 100644 --- a/drivers/ioexpander/mcp23x17.c +++ b/drivers/ioexpander/mcp23x17.c @@ -312,6 +312,7 @@ static int mcp23x17_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, int ret; if (direction != IOEXPANDER_DIRECTION_IN && + direction != IOEXPANDER_DIRECTION_IN_PULLUP && direction != IOEXPANDER_DIRECTION_OUT) { return -EINVAL; @@ -326,7 +327,17 @@ static int mcp23x17_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, } ret = mcp23x17_setbit(priv, MCP23X17_IODIRA, pin, - (direction == IOEXPANDER_DIRECTION_IN)); + (direction == IOEXPANDER_DIRECTION_IN) || + (direction == IOEXPANDER_DIRECTION_IN_PULLUP)); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return ret; + } + + ret = mcp23x17_setbit(priv, MCP23X17_GPPUA, pin, + (direction == IOEXPANDER_DIRECTION_IN_PULLUP)); + nxmutex_unlock(&priv->lock); return ret; } @@ -746,6 +757,8 @@ static FAR void *mcp23x17_attach(FAR struct ioexpander_dev_s *dev, { FAR struct mcp23x17_dev_s *priv = (FAR struct mcp23x17_dev_s *)dev; FAR void *handle = NULL; + uint8_t addr = MCP23X17_GPINTENA; + uint8_t buf[3]; int i; int ret; @@ -754,7 +767,25 @@ static FAR void *mcp23x17_attach(FAR struct ioexpander_dev_s *dev, ret = nxmutex_lock(&priv->lock); if (ret < 0) { - return ret; + return NULL; + } + + ret = mcp23x17_writeread(priv, &addr, 1, &buf[1], 2); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return NULL; + } + + buf[0] = addr; + buf[1] |= (uint8_t)(pinset & 0x00ff); + buf[2] |= (uint8_t)((pinset & 0xff00) >> 8); + + ret = mcp23x17_write(priv, buf, 3); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return NULL; } /* Find and available in entry in the callback table */ @@ -806,7 +837,7 @@ static int mcp23x17_detach(FAR struct ioexpander_dev_s *dev, DEBUGASSERT(priv != NULL && cb != NULL); DEBUGASSERT((uintptr_t)cb >= (uintptr_t)&priv->cb[0] && (uintptr_t)cb <= - (uintptr_t)&priv->cb[CONFIG_TCA64XX_INT_NCALLBACKS - 1]); + (uintptr_t)&priv->cb[CONFIG_MCP23X17_INT_NCALLBACKS - 1]); UNUSED(priv); cb->pinset = 0; @@ -827,26 +858,20 @@ static int mcp23x17_detach(FAR struct ioexpander_dev_s *dev, static void mcp23x17_irqworker(void *arg) { FAR struct mcp23x17_dev_s *priv = (FAR struct mcp23x17_dev_s *)arg; - uint8_t addr = MCP23X17_GPIOA; + uint8_t addr = MCP23X17_INTFA; uint8_t buf[2]; ioe_pinset_t pinset; int ret; int i; - /* Read inputs */ + /* Read interrupt flags */ ret = mcp23x17_writeread(priv, &addr, 1, buf, 2); if (ret == OK) { -#ifdef CONFIG_MCP23X17_SHADOW_MODE - /* Don't forget to update the shadow registers at this point */ - - priv->sreg[addr] = buf[0]; - priv->sreg[addr + 1] = buf[1]; -#endif /* Create a 16-bit pinset */ - pinset = ((unsigned int)buf[0] << 8) | buf[1]; + pinset = ((unsigned int)buf[1] << 8) | buf[0]; /* Perform pin interrupt callbacks */ @@ -870,6 +895,19 @@ static void mcp23x17_irqworker(void *arg) } } } + + /* Read GPIOs to clear interrupt condition */ + + addr = MCP23X17_INTCAPA; + + mcp23x17_writeread(priv, &addr, 1, buf, 2); + +#ifdef CONFIG_MCP23X17_SHADOW_MODE + /* Don't forget to update the shadow registers at this point */ + + priv->sreg[addr] = buf[0]; + priv->sreg[addr + 1] = buf[1]; +#endif } /* Re-enable interrupts */ @@ -934,6 +972,9 @@ FAR struct ioexpander_dev_s *mcp23x17_initialize( FAR struct mcp23x17_config_s *config) { FAR struct mcp23x17_dev_s *priv; +#ifdef CONFIG_MCP23X17_INT_MIRROR + uint8_t buf[3]; +#endif DEBUGASSERT(i2cdev != NULL && config != NULL); @@ -967,6 +1008,14 @@ FAR struct ioexpander_dev_s *mcp23x17_initialize( priv->config = config; #ifdef CONFIG_MCP23X17_INT_ENABLE + +#ifdef CONFIG_MCP23X17_INT_MIRROR + buf[0] = MCP23X17_IOCON; + buf[1] = 0x40; + buf[2] = 0x40; + mcp23x17_write(priv, buf, 3); +#endif + priv->config->attach(priv->config, mcp23x17_interrupt, priv); priv->config->enable(priv->config, TRUE); #endif diff --git a/drivers/ioexpander/mcp23x17.h b/drivers/ioexpander/mcp23x17.h index ee87278af86..dd2463ee558 100644 --- a/drivers/ioexpander/mcp23x17.h +++ b/drivers/ioexpander/mcp23x17.h @@ -152,6 +152,10 @@ struct mcp23x17_dev_s struct ioexpander_dev_s dev; /* Nested structure to allow casting * as public gpio expander. */ +#ifdef CONFIG_MCP23X17_MULTIPLE + FAR struct mcp23x17_dev_s *flink; /* Supports a singly linked list of drivers */ +#endif + FAR struct mcp23x17_config_s *config; /* Board configuration data */ FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */ mutex_t lock; /* Mutual exclusion */