Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers/tca64xx: Add support for PCAL6416 #312

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 141 additions & 7 deletions drivers/ioexpander/tca64xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
},
};

Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Copy link

@jlaitine jlaitine Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no error handling now if PU/PD setting is attempted for other parts than the one which has the register. You should propably add "define TCA6424_INVALID_REG 0xff" or such in header, and make tca64_pdenable_reg return that if part->tp_pu_select is 0.

Then you can check the regaddr here (!= TCA6424_INVALID_REG) and bail out if the part doesn't support PU/PD....

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be fixed now.

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, &regval, 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, &regval, 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, &regval, 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, &regval, 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, &regval, 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, &regval, 1);
}
}

errout_with_lock:
nxmutex_unlock(&priv->lock);
return ret;
Expand Down
18 changes: 18 additions & 0 deletions drivers/ioexpander/tca64xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/nuttx/ioexpander/tca64xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum tca64xx_part_e
TCA6408_PART = 0,
TCA6416_PART,
TCA6424_PART,
PCAL6416A_PART,
TCA64_NPARTS
};

Expand Down
Loading