diff --git a/drivers/ioexpander/tca64xx.c b/drivers/ioexpander/tca64xx.c index c2fd74cb9a155..858892471deed 100644 --- a/drivers/ioexpander/tca64xx.c +++ b/drivers/ioexpander/tca64xx.c @@ -51,6 +51,9 @@ static uint8_t tca64_input_reg(FAR struct tca64_dev_s *priv, uint8_t pin); static uint8_t tca64_output_reg(FAR struct tca64_dev_s *priv, uint8_t pin); static uint8_t tca64_polarity_reg(FAR struct tca64_dev_s *priv, uint8_t pin); static uint8_t tca64_config_reg(FAR struct tca64_dev_s *priv, uint8_t pin); +static uint8_t tca64_pdenable_reg(FAR struct tca64_dev_s *priv, uint8_t pin); +static uint8_t tca64_pdselect_reg(FAR struct tca64_dev_s *priv, uint8_t pin); + static int tca64_getreg(FAR struct tca64_dev_s *priv, uint8_t regaddr, FAR uint8_t *regval, unsigned int count); static int tca64_putreg(struct tca64_dev_s *priv, uint8_t regaddr, @@ -132,6 +135,8 @@ static const struct tca64_part_s g_tca64_parts[TCA64_NPARTS] = TCA6408_OUTPUT_REG, TCA6408_POLARITY_REG, TCA6408_CONFIG_REG, + TCA64XX_INVALID_REG, + TCA64XX_INVALID_REG, }, { TCA6416_PART, @@ -140,6 +145,8 @@ static const struct tca64_part_s g_tca64_parts[TCA64_NPARTS] = TCA6416_OUTPUT0_REG, TCA6416_POLARITY0_REG, TCA6416_CONFIG0_REG, + TCA64XX_INVALID_REG, + TCA64XX_INVALID_REG, }, { TCA6424_PART, @@ -148,6 +155,18 @@ static const struct tca64_part_s g_tca64_parts[TCA64_NPARTS] = TCA6424_OUTPUT0_REG, TCA6424_POLARITY0_REG, TCA6424_CONFIG0_REG, + TCA64XX_INVALID_REG, + TCA64XX_INVALID_REG, + }, + { + PCAL6416A_PART, + MIN(PCAL6416A_NR_GPIOS, CONFIG_IOEXPANDER_NPINS), + PCAL6416A_INPUT0_REG, + PCAL6416A_OUTPUT0_REG, + PCAL6416A_POLARITY0_REG, + PCAL6416A_CONFIG0_REG, + PCAL6416A_PU_ENABLE0_REG, + PCAL6416A_PUPD_SELECT0_REG, }, }; @@ -254,6 +273,50 @@ static uint8_t tca64_config_reg(FAR struct tca64_dev_s *priv, uint8_t pin) return reg + (pin >> 3); } +/**************************************************************************** + * Name: tca64_pdenable_reg + * + * Description: + * Return the address of the pu/pd enable register for the specified pin. + * + ****************************************************************************/ + +static uint8_t tca64_pdenable_reg(FAR struct tca64_dev_s *priv, uint8_t pin) +{ + FAR const struct tca64_part_s *part = tca64_getpart(priv); + uint8_t reg = part->tp_puenable; + + if (reg == TCA64XX_INVALID_REG) + { + return reg; + } + + DEBUGASSERT(pin <= part->tp_ngpios); + return reg + (pin >> 3); +} + +/**************************************************************************** + * Name: tca64_pdselect_reg + * + * Description: + * Return the address of the pu/pd selection register for the specified pin. + * + ****************************************************************************/ + +static uint8_t tca64_pdselect_reg(FAR struct tca64_dev_s *priv, uint8_t pin) +{ + FAR const struct tca64_part_s *part = tca64_getpart(priv); + uint8_t reg = part->tp_pu_select; + + if (reg == TCA64XX_INVALID_REG) + { + return reg; + } + + DEBUGASSERT(pin <= part->tp_ngpios); + return reg + (pin >> 3); +} + /**************************************************************************** * Name: tca64_getreg * @@ -373,12 +436,6 @@ static int tca64_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, uint8_t regval; int ret; - if (direction != IOEXPANDER_DIRECTION_IN && - direction != IOEXPANDER_DIRECTION_OUT) - { - return -EINVAL; - } - DEBUGASSERT(priv != NULL && priv->config != NULL && pin < CONFIG_IOEXPANDER_NPINS); @@ -409,7 +466,9 @@ static int tca64_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, /* Set the pin direction in the I/O Expander */ - if (direction == IOEXPANDER_DIRECTION_IN) + if ((direction == IOEXPANDER_DIRECTION_IN) || + (direction == IOEXPANDER_DIRECTION_IN_PULLDOWN) || + (direction == IOEXPANDER_DIRECTION_IN_PULLUP)) { /* Configure pin as input. If a bit in the configuration register is * set to 1, the corresponding port pin is enabled as an input with a @@ -439,6 +498,81 @@ static int tca64_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, regaddr, ret); } + if ((direction == IOEXPANDER_DIRECTION_IN_PULLDOWN) || + (direction == IOEXPANDER_DIRECTION_IN_PULLUP)) + { + regaddr = tca64_pdenable_reg(priv, pin); + if (regaddr == TCA64XX_INVALID_REG) + { + gpioerr("ERROR: This part does not support PU/PD settings\n"); + ret = -EINVAL; + goto errout_with_lock; + } + + ret = tca64_getreg(priv, regaddr, ®val, 1); + if (ret < 0) + { + gpioerr("ERROR: Failed to read pu config register at %u: %d\n", + regaddr, ret); + goto errout_with_lock; + } + + regval |= (1 << (pin & 7)); + + ret = tca64_putreg(priv, regaddr, ®val, 1); + if (ret < 0) + { + gpioerr("ERROR: Failed to write pu config register at %u: %d\n", + regaddr, ret); + goto errout_with_lock; + } + + regaddr = tca64_pdselect_reg(priv, pin); + ret = tca64_getreg(priv, regaddr, ®val, 1); + if (ret < 0) + { + gpioerr("ERROR: Failed to read pu select register at %u: %d\n", + regaddr, ret); + goto errout_with_lock; + } + + if (direction == IOEXPANDER_DIRECTION_IN_PULLUP) + { + regval |= (1 << (pin & 7)); + } + else + { + regval &= ~(1 << (pin & 7)); + } + + ret = tca64_putreg(priv, regaddr, ®val, 1); + if (ret < 0) + { + gpioerr("ERROR: Failed to write pu select register at %u: %d\n", + regaddr, ret); + goto errout_with_lock; + } + } + else if (direction == IOEXPANDER_DIRECTION_IN) + { + /* Disable pu/pd if that is available */ + + regaddr = tca64_pdenable_reg(priv, pin); + if (regaddr != TCA64XX_INVALID_REG) + { + ret = tca64_getreg(priv, regaddr, ®val, 1); + if (ret < 0) + { + gpioerr("ERROR: Failed to read pu config register at %u: %d\n", + regaddr, ret); + goto errout_with_lock; + } + + regval &= ~(1 << (pin & 7)); + ret = tca64_putreg(priv, regaddr, ®val, 1); + } + } + errout_with_lock: nxmutex_unlock(&priv->lock); return ret; diff --git a/drivers/ioexpander/tca64xx.h b/drivers/ioexpander/tca64xx.h index bcec8af971a9f..41888d1ed15ba 100644 --- a/drivers/ioexpander/tca64xx.h +++ b/drivers/ioexpander/tca64xx.h @@ -97,6 +97,8 @@ /* TCA64XX Parts ************************************************************/ +#define TCA64XX_INVALID_REG 0xff + #define TCA6408_INPUT_REG 0x00 #define TCA6408_OUTPUT_REG 0x01 #define TCA6408_POLARITY_REG 0x02 @@ -132,6 +134,20 @@ #define TCA64XX_NR_GPIO_MAX TCA6424_NR_GPIOS +#define PCAL6416A_INPUT0_REG 0x00 +#define PCAL6416A_INPUT1_REG 0x01 +#define PCAL6416A_OUTPUT0_REG 0x02 +#define PCAL6416A_OUTPUT1_REG 0x03 +#define PCAL6416A_POLARITY0_REG 0x04 +#define PCAL6416A_POLARITY1_REG 0x05 +#define PCAL6416A_CONFIG0_REG 0x06 +#define PCAL6416A_CONFIG1_REG 0x07 +#define PCAL6416A_PU_ENABLE0_REG 0x46 +#define PCAL6416A_PU_ENABLE1_REG 0x47 +#define PCAL6416A_PUPD_SELECT0_REG 0x48 +#define PCAL6416A_PUPD_SELECT1_REG 0x49 +#define PCAL6416A_NR_GPIOS 16 + /* 1us (datasheet: reset pulse duration (Tw) is 4ns */ #define TCA64XX_TW 1 @@ -181,6 +197,8 @@ struct tca64_part_s uint8_t tp_output; /* Address of first output register */ uint8_t tp_polarity; /* Address of first polarity register */ uint8_t tp_config; /* Address of first configuration register */ + uint8_t tp_puenable; + uint8_t tp_pu_select; }; #ifdef CONFIG_IOEXPANDER_INT_ENABLE diff --git a/include/nuttx/ioexpander/tca64xx.h b/include/nuttx/ioexpander/tca64xx.h index 96f7582675ba8..95892c6910d1a 100644 --- a/include/nuttx/ioexpander/tca64xx.h +++ b/include/nuttx/ioexpander/tca64xx.h @@ -60,6 +60,7 @@ enum tca64xx_part_e TCA6408_PART = 0, TCA6416_PART, TCA6424_PART, + PCAL6416A_PART, TCA64_NPARTS };