drives/ioexpander: add support to PI4IOE IO Expander

Adds support for the PIO4IOE IO Expander, more specifically the PIO4IOE5V6408 version.

Assisted-by: Cursor IDE agents
Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
Filipe Cavalcanti 2026-07-24 08:12:46 -03:00 committed by Xiang Xiao
parent 93b2798e30
commit 270d1f32f9
5 changed files with 1010 additions and 0 deletions

View file

@ -55,6 +55,10 @@ if(CONFIG_IOEXPANDER)
list(APPEND SRCS pca9538.c)
endif()
if(CONFIG_IOEXPANDER_PI4IOE5V6408)
list(APPEND SRCS pi4ioe5v6408.c)
endif()
if(CONFIG_IOEXPANDER_PCA9557)
list(APPEND SRCS pca9557.c)
endif()

View file

@ -312,6 +312,34 @@ config PCA9538_RETRY
endif # IOEXPANDER_PCA9538
config IOEXPANDER_PI4IOE5V6408
bool "PI4IOE5V6408 I2C IO expander"
default n
depends on I2C
---help---
Enable support for the Diodes Incorporated PI4IOE5V6408
8-bit I2C I/O expander.
if IOEXPANDER_PI4IOE5V6408
config PI4IOE5V6408_INT_ENABLE
bool "Enable PI4IOE5V6408 interrupt support"
default n
select IOEXPANDER_INT_ENABLE
select SCHED_HPWORK
---help---
Enable the PI4IOE5V6408 interrupt callback interface. The board
must provide hooks for the device's active-low interrupt pin.
config PI4IOE5V6408_INT_NCALLBACKS
int "Max number of interrupt callbacks"
default 4
depends on PI4IOE5V6408_INT_ENABLE
---help---
This is the maximum number of interrupt callbacks supported.
endif # IOEXPANDER_PI4IOE5V6408
config IOEXPANDER_PCA9557
bool "PCA9557 I2C IO expander"
default n

View file

@ -54,6 +54,10 @@ ifeq ($(CONFIG_IOEXPANDER_PCA9538),y)
CSRCS += pca9538.c
endif
ifeq ($(CONFIG_IOEXPANDER_PI4IOE5V6408),y)
CSRCS += pi4ioe5v6408.c
endif
ifeq ($(CONFIG_IOEXPANDER_PCA9557),y)
CSRCS += pca9557.c
endif

View file

@ -0,0 +1,864 @@
/****************************************************************************
* drivers/ioexpander/pi4ioe5v6408.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <nuttx/debug.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/ioexpander/ioexpander.h>
#include <nuttx/ioexpander/pi4ioe5v6408.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/signal.h>
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
# include <nuttx/wqueue.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PI4IOE5V6408_REG_CHIP_ID_CTRL 0x01
#define PI4IOE5V6408_REG_DIRECTION 0x03
#define PI4IOE5V6408_REG_OUTPUT 0x05
#define PI4IOE5V6408_REG_OUTPUT_HIGH_Z 0x07
#define PI4IOE5V6408_REG_INPUT_DEFAULT 0x09
#define PI4IOE5V6408_REG_PULL_ENABLE 0x0b
#define PI4IOE5V6408_REG_PULL_SELECT 0x0d
#define PI4IOE5V6408_REG_INPUT 0x0f
#define PI4IOE5V6408_REG_INT_MASK 0x11
#define PI4IOE5V6408_REG_INT_STATUS 0x13
#define PI4IOE5V6408_SOFT_RESET 0xff
#define PI4IOE5V6408_RESET_DELAY_US 1000
#define PI4IOE5V6408_INT_MASK_DEFAULT 0xff
#define PI4IOE5V6408_IN_DEF_DEFAULT 0x00
/****************************************************************************
* Private Types
****************************************************************************/
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
struct pi4ioe5v6408_callback_s
{
ioe_pinset_t pinset;
ioe_callback_t callback;
FAR void *arg;
};
#endif
struct pi4ioe5v6408_dev_s
{
struct ioexpander_dev_s dev;
FAR struct i2c_master_s *i2c;
FAR struct pi4ioe5v6408_config_s *config;
mutex_t lock;
uint8_t direction;
uint8_t output;
uint8_t high_impedance;
uint8_t pull_enable;
uint8_t pull_select;
uint8_t input_default;
uint8_t interrupt_mask;
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
struct work_s work;
struct pi4ioe5v6408_callback_s
callbacks[CONFIG_PI4IOE5V6408_INT_NCALLBACKS];
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int pi4ioe5v6408_direction(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int direction);
static int pi4ioe5v6408_option(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int option, FAR void *value);
static int pi4ioe5v6408_writepin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, bool value);
static int pi4ioe5v6408_readpin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value);
static int pi4ioe5v6408_readbuf(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value);
#ifdef CONFIG_IOEXPANDER_MULTIPIN
static int pi4ioe5v6408_multiwritepin(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR const bool *values, int count);
static int pi4ioe5v6408_multireadpin(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR bool *values, int count);
static int pi4ioe5v6408_multireadbuf(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR bool *values, int count);
#endif
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
static FAR void *pi4ioe5v6408_attach(FAR struct ioexpander_dev_s *dev,
ioe_pinset_t pinset,
ioe_callback_t callback, FAR void *arg);
static int pi4ioe5v6408_detach(FAR struct ioexpander_dev_s *dev,
FAR void *handle);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static const struct ioexpander_ops_s g_pi4ioe5v6408_ops =
{
pi4ioe5v6408_direction,
pi4ioe5v6408_option,
pi4ioe5v6408_writepin,
pi4ioe5v6408_readpin,
pi4ioe5v6408_readbuf
#ifdef CONFIG_IOEXPANDER_MULTIPIN
, pi4ioe5v6408_multiwritepin
, pi4ioe5v6408_multireadpin
, pi4ioe5v6408_multireadbuf
#endif
#ifdef CONFIG_IOEXPANDER_INT_ENABLE
# ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
, pi4ioe5v6408_attach
, pi4ioe5v6408_detach
# else
, NULL
, NULL
# endif
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int pi4ioe5v6408_write(FAR struct pi4ioe5v6408_dev_s *priv,
uint8_t reg, uint8_t value)
{
struct i2c_config_s config;
uint8_t buffer[2];
int ret;
config.frequency = priv->config->frequency;
config.address = priv->config->address;
config.addrlen = 7;
buffer[0] = reg;
buffer[1] = value;
ret = i2c_write(priv->i2c, &config, buffer, sizeof(buffer));
if (ret < 0)
{
gpioerr("ERROR: PI4IOE5V6408 write reg 0x%02x val 0x%02x failed: %d\n",
reg, value, ret);
}
return ret;
}
static int pi4ioe5v6408_read(FAR struct pi4ioe5v6408_dev_s *priv,
uint8_t reg, FAR uint8_t *value)
{
struct i2c_config_s config;
int ret;
config.frequency = priv->config->frequency;
config.address = priv->config->address;
config.addrlen = 7;
ret = i2c_writeread(priv->i2c, &config, &reg, 1, value, 1);
if (ret < 0)
{
gpioerr("ERROR: PI4IOE5V6408 read reg 0x%02x failed: %d\n",
reg, ret);
}
return ret;
}
static int pi4ioe5v6408_update(FAR struct pi4ioe5v6408_dev_s *priv,
uint8_t reg, FAR uint8_t *shadow,
uint8_t mask, bool set)
{
uint8_t value;
int ret;
value = set ? (*shadow | mask) : (*shadow & ~mask);
/* Skip the I2C write when the register is unchanged. */
if (value == *shadow)
{
return OK;
}
ret = pi4ioe5v6408_write(priv, reg, value);
if (ret >= 0)
{
*shadow = value;
}
return ret;
}
static int pi4ioe5v6408_checkpin(uint8_t pin)
{
return pin < PI4IOE5V6408_NPINS ? OK : -ENXIO;
}
static int pi4ioe5v6408_setpull(FAR struct pi4ioe5v6408_dev_s *priv,
uint8_t pin, int direction)
{
uint8_t mask = 1 << pin;
bool enable;
bool pullup;
int ret;
enable = direction == IOEXPANDER_DIRECTION_IN_PULLUP ||
direction == IOEXPANDER_DIRECTION_IN_PULLDOWN;
pullup = direction == IOEXPANDER_DIRECTION_IN_PULLUP;
if (enable)
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_PULL_SELECT,
&priv->pull_select, mask, pullup);
if (ret < 0)
{
return ret;
}
}
return pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_PULL_ENABLE,
&priv->pull_enable, mask, enable);
}
static int pi4ioe5v6408_direction(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int direction)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
uint8_t mask;
bool output;
int ret;
ret = pi4ioe5v6408_checkpin(pin);
if (ret < 0)
{
return ret;
}
if (direction != IOEXPANDER_DIRECTION_IN &&
direction != IOEXPANDER_DIRECTION_IN_PULLUP &&
direction != IOEXPANDER_DIRECTION_IN_PULLDOWN &&
direction != IOEXPANDER_DIRECTION_OUT)
{
return -ENOTSUP;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
mask = 1 << pin;
output = direction == IOEXPANDER_DIRECTION_OUT;
/* Leave pull registers at reset defaults when configuring
* an output. Only input modes with explicit pull request change them.
*/
if (!output)
{
ret = pi4ioe5v6408_setpull(priv, pin, direction);
}
else
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_OUTPUT_HIGH_Z,
&priv->high_impedance, mask, false);
}
if (ret >= 0)
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_DIRECTION,
&priv->direction, mask, output);
}
nxmutex_unlock(&priv->lock);
return ret;
}
static int pi4ioe5v6408_option(FAR struct ioexpander_dev_s *dev,
uint8_t pin, int option, FAR void *value)
{
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
uintptr_t setting = (uintptr_t)value;
uint8_t mask;
#endif
int ret;
ret = pi4ioe5v6408_checkpin(pin);
if (ret < 0)
{
return ret;
}
#ifndef CONFIG_PI4IOE5V6408_INT_ENABLE
UNUSED(dev);
UNUSED(option);
UNUSED(value);
return -ENOTSUP;
#else
if (option != IOEXPANDER_OPTION_INTCFG &&
option != IOEXPANDER_OPTION_SETMASK)
{
return -ENOTSUP;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
mask = 1 << pin;
if (option == IOEXPANDER_OPTION_SETMASK)
{
if (setting != IOEXPANDER_MASK_DISABLE &&
setting != IOEXPANDER_MASK_ENABLE)
{
ret = -EINVAL;
}
else
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_INT_MASK,
&priv->interrupt_mask, mask,
setting == IOEXPANDER_MASK_ENABLE);
}
}
else if (setting == IOEXPANDER_VAL_DISABLE)
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_INT_MASK,
&priv->interrupt_mask, mask, true);
}
else if (setting == IOEXPANDER_VAL_HIGH ||
setting == IOEXPANDER_VAL_RISING ||
setting == IOEXPANDER_VAL_LOW ||
setting == IOEXPANDER_VAL_FALLING)
{
/* The chip compares the input against INPUT_DEFAULT. A default low
* detects high/rising and a default high detects low/falling.
*/
bool default_high = setting == IOEXPANDER_VAL_LOW ||
setting == IOEXPANDER_VAL_FALLING;
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_INPUT_DEFAULT,
&priv->input_default, mask, default_high);
if (ret >= 0)
{
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_INT_MASK,
&priv->interrupt_mask, mask, false);
}
}
else
{
ret = -ENOTSUP;
}
nxmutex_unlock(&priv->lock);
return ret;
#endif
}
static int pi4ioe5v6408_writepin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, bool value)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
int ret;
ret = pi4ioe5v6408_checkpin(pin);
if (ret < 0)
{
return ret;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_update(priv, PI4IOE5V6408_REG_OUTPUT,
&priv->output, 1 << pin, value);
nxmutex_unlock(&priv->lock);
return ret;
}
static int pi4ioe5v6408_readpin(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
uint8_t input;
int ret;
if (value == NULL)
{
return -EINVAL;
}
ret = pi4ioe5v6408_checkpin(pin);
if (ret < 0)
{
return ret;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_read(priv, PI4IOE5V6408_REG_INPUT, &input);
if (ret >= 0)
{
*value = (input & (1 << pin)) != 0;
}
nxmutex_unlock(&priv->lock);
return ret;
}
static int pi4ioe5v6408_readbuf(FAR struct ioexpander_dev_s *dev,
uint8_t pin, FAR bool *value)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
int ret;
if (value == NULL)
{
return -EINVAL;
}
ret = pi4ioe5v6408_checkpin(pin);
if (ret < 0)
{
return ret;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
*value = (priv->output & (1 << pin)) != 0;
nxmutex_unlock(&priv->lock);
return OK;
}
#ifdef CONFIG_IOEXPANDER_MULTIPIN
static int pi4ioe5v6408_multiwritepin(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR const bool *values, int count)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
uint8_t output;
int ret;
int i;
if (pins == NULL || values == NULL || count < 0)
{
return -EINVAL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
output = priv->output;
for (i = 0; i < count; i++)
{
ret = pi4ioe5v6408_checkpin(pins[i]);
if (ret < 0)
{
nxmutex_unlock(&priv->lock);
return ret;
}
if (values[i])
{
output |= 1 << pins[i];
}
else
{
output &= ~(1 << pins[i]);
}
}
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_OUTPUT, output);
if (ret >= 0)
{
priv->output = output;
}
nxmutex_unlock(&priv->lock);
return ret;
}
static int pi4ioe5v6408_multiread(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR bool *values, int count, bool buffered)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
uint8_t data;
int ret;
int i;
if (pins == NULL || values == NULL || count < 0)
{
return -EINVAL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
if (buffered)
{
data = priv->output;
}
else
{
ret = pi4ioe5v6408_read(priv, PI4IOE5V6408_REG_INPUT, &data);
if (ret < 0)
{
nxmutex_unlock(&priv->lock);
return ret;
}
}
for (i = 0; i < count; i++)
{
ret = pi4ioe5v6408_checkpin(pins[i]);
if (ret < 0)
{
nxmutex_unlock(&priv->lock);
return ret;
}
values[i] = (data & (1 << pins[i])) != 0;
}
nxmutex_unlock(&priv->lock);
return OK;
}
static int pi4ioe5v6408_multireadpin(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR bool *values, int count)
{
return pi4ioe5v6408_multiread(dev, pins, values, count, false);
}
static int pi4ioe5v6408_multireadbuf(FAR struct ioexpander_dev_s *dev,
FAR const uint8_t *pins,
FAR bool *values, int count)
{
return pi4ioe5v6408_multiread(dev, pins, values, count, true);
}
#endif
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
static void pi4ioe5v6408_irqworker(FAR void *arg)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)arg;
uint8_t status = 0;
int ret;
int i;
ret = nxmutex_lock(&priv->lock);
if (ret >= 0)
{
ret = pi4ioe5v6408_read(priv, PI4IOE5V6408_REG_INT_STATUS, &status);
nxmutex_unlock(&priv->lock);
}
if (ret >= 0)
{
for (i = 0; i < CONFIG_PI4IOE5V6408_INT_NCALLBACKS; i++)
{
ioe_pinset_t match = priv->callbacks[i].pinset & status;
if (match != 0 && priv->callbacks[i].callback != NULL)
{
priv->callbacks[i].callback(&priv->dev, match,
priv->callbacks[i].arg);
}
}
}
priv->config->enable(priv->config, true);
}
static int pi4ioe5v6408_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)arg;
int ret;
if (work_available(&priv->work))
{
priv->config->enable(priv->config, false);
ret = work_queue(HPWORK, &priv->work, pi4ioe5v6408_irqworker,
priv, 0);
if (ret < 0)
{
priv->config->enable(priv->config, true);
return ret;
}
}
return OK;
}
static FAR void *pi4ioe5v6408_attach(FAR struct ioexpander_dev_s *dev,
ioe_pinset_t pinset,
ioe_callback_t callback, FAR void *arg)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
FAR void *handle = NULL;
int ret;
int i;
if (callback == NULL || pinset == 0)
{
return NULL;
}
pinset &= (1 << PI4IOE5V6408_NPINS) - 1;
if (pinset == 0)
{
return NULL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return NULL;
}
for (i = 0; i < CONFIG_PI4IOE5V6408_INT_NCALLBACKS; i++)
{
if (priv->callbacks[i].callback == NULL)
{
priv->callbacks[i].pinset = pinset;
priv->callbacks[i].callback = callback;
priv->callbacks[i].arg = arg;
handle = &priv->callbacks[i];
break;
}
}
nxmutex_unlock(&priv->lock);
return handle;
}
static int pi4ioe5v6408_detach(FAR struct ioexpander_dev_s *dev,
FAR void *handle)
{
FAR struct pi4ioe5v6408_dev_s *priv =
(FAR struct pi4ioe5v6408_dev_s *)dev;
FAR struct pi4ioe5v6408_callback_s *callback =
(FAR struct pi4ioe5v6408_callback_s *)handle;
int ret;
if ((uintptr_t)callback < (uintptr_t)&priv->callbacks[0] ||
(uintptr_t)callback >=
(uintptr_t)&priv->callbacks[CONFIG_PI4IOE5V6408_INT_NCALLBACKS])
{
return -EINVAL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
callback->pinset = 0;
callback->callback = NULL;
callback->arg = NULL;
nxmutex_unlock(&priv->lock);
return OK;
}
#endif
static int pi4ioe5v6408_reset(FAR struct pi4ioe5v6408_dev_s *priv)
{
uint8_t chip_id;
int ret;
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_CHIP_ID_CTRL,
PI4IOE5V6408_SOFT_RESET);
if (ret < 0)
{
return ret;
}
/* Give the chip time to leave reset before the follow-up register
* burst (avoids intermittent -EIO).
*/
nxsig_usleep(PI4IOE5V6408_RESET_DELAY_US);
/* Reading CHIP_ID_CTRL clears the reset interrupt indication. */
ret = pi4ioe5v6408_read(priv, PI4IOE5V6408_REG_CHIP_ID_CTRL, &chip_id);
if (ret < 0)
{
return ret;
}
gpioinfo("PI4IOE5V6408 chip ID 0x%02x\n", chip_id);
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_DIRECTION,
PI4IOE5V6408_DIR_DEFAULT);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_OUTPUT_HIGH_Z,
PI4IOE5V6408_HIGHZ_DEFAULT);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_PULL_SELECT,
PI4IOE5V6408_PULLSEL_DEFAULT);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_PULL_ENABLE,
PI4IOE5V6408_PULLEN_DEFAULT);
if (ret < 0)
{
return ret;
}
ret = pi4ioe5v6408_write(priv, PI4IOE5V6408_REG_OUTPUT,
PI4IOE5V6408_OUT_DEFAULT);
if (ret < 0)
{
return ret;
}
priv->direction = PI4IOE5V6408_DIR_DEFAULT;
priv->high_impedance = PI4IOE5V6408_HIGHZ_DEFAULT;
priv->pull_select = PI4IOE5V6408_PULLSEL_DEFAULT;
priv->pull_enable = PI4IOE5V6408_PULLEN_DEFAULT;
priv->output = PI4IOE5V6408_OUT_DEFAULT;
priv->input_default = PI4IOE5V6408_IN_DEF_DEFAULT;
priv->interrupt_mask = PI4IOE5V6408_INT_MASK_DEFAULT;
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
FAR struct ioexpander_dev_s *
pi4ioe5v6408_initialize(FAR struct i2c_master_s *i2c,
FAR struct pi4ioe5v6408_config_s *config)
{
FAR struct pi4ioe5v6408_dev_s *priv;
int ret;
DEBUGASSERT(i2c != NULL && config != NULL);
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
DEBUGASSERT(config->attach != NULL && config->enable != NULL);
#endif
priv = kmm_zalloc(sizeof(*priv));
if (priv == NULL)
{
gpioerr("ERROR: Failed to allocate PI4IOE5V6408 instance\n");
return NULL;
}
priv->dev.ops = &g_pi4ioe5v6408_ops;
priv->i2c = i2c;
priv->config = config;
nxmutex_init(&priv->lock);
ret = pi4ioe5v6408_reset(priv);
if (ret < 0)
{
nxmutex_destroy(&priv->lock);
kmm_free(priv);
return NULL;
}
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
ret = config->attach(config, pi4ioe5v6408_interrupt, priv);
if (ret < 0)
{
nxmutex_destroy(&priv->lock);
kmm_free(priv);
return NULL;
}
config->enable(config, true);
#endif
return &priv->dev;
}

View file

@ -0,0 +1,110 @@
/****************************************************************************
* include/nuttx/ioexpander/pi4ioe5v6408.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_IOEXPANDER_PI4IOE5V6408_H
#define __INCLUDE_NUTTX_IOEXPANDER_PI4IOE5V6408_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/compiler.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
# include <nuttx/irq.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PI4IOE5V6408_I2C_ADDRESS_LOW 0x43
#define PI4IOE5V6408_I2C_ADDRESS_HIGH 0x44
#define PI4IOE5V6408_NPINS 8
/* Power-up / soft-reset defaults.
* Direction: 1 = output, 0 = input.
* Pull select: 1 = pull-up, 0 = pull-down.
*/
#define PI4IOE5V6408_DIR_DEFAULT 0xff
#define PI4IOE5V6408_OUT_DEFAULT 0x00
#define PI4IOE5V6408_HIGHZ_DEFAULT 0xff
#define PI4IOE5V6408_PULLEN_DEFAULT 0xff
#define PI4IOE5V6408_PULLSEL_DEFAULT 0x00
/****************************************************************************
* Public Types
****************************************************************************/
struct i2c_master_s;
struct ioexpander_dev_s;
struct pi4ioe5v6408_config_s
{
uint8_t address;
uint32_t frequency;
#ifdef CONFIG_PI4IOE5V6408_INT_ENABLE
CODE int (*attach)(FAR struct pi4ioe5v6408_config_s *config,
xcpt_t isr, FAR void *arg);
CODE void (*enable)(FAR struct pi4ioe5v6408_config_s *config, bool enable);
#endif
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
/****************************************************************************
* Name: pi4ioe5v6408_initialize
*
* Description:
* Instantiate and initialize a PI4IOE5V6408 I2C I/O expander.
*
* Input Parameters:
* i2c - I2C controller instance
* config - Persistent board configuration
*
* Returned Value:
* An ioexpander_dev_s instance on success, NULL on failure.
*
****************************************************************************/
FAR struct ioexpander_dev_s *
pi4ioe5v6408_initialize(FAR struct i2c_master_s *i2c,
FAR struct pi4ioe5v6408_config_s *config);
#ifdef __cplusplus
}
#endif
#endif /* __INCLUDE_NUTTX_IOEXPANDER_PI4IOE5V6408_H */