This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 270d1f32f923f69c5f3017165057811aac065945 Author: Filipe Cavalcanti <[email protected]> AuthorDate: Fri Jul 24 08:12:46 2026 -0300 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 <[email protected]> --- drivers/ioexpander/CMakeLists.txt | 4 + drivers/ioexpander/Kconfig | 28 ++ drivers/ioexpander/Make.defs | 4 + drivers/ioexpander/pi4ioe5v6408.c | 864 ++++++++++++++++++++++++++++++++ include/nuttx/ioexpander/pi4ioe5v6408.h | 110 ++++ 5 files changed, 1010 insertions(+) diff --git a/drivers/ioexpander/CMakeLists.txt b/drivers/ioexpander/CMakeLists.txt index 23e712e1d35..34bde646335 100644 --- a/drivers/ioexpander/CMakeLists.txt +++ b/drivers/ioexpander/CMakeLists.txt @@ -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() diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index 1bd2cd89283..0de2ee4e56e 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -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 diff --git a/drivers/ioexpander/Make.defs b/drivers/ioexpander/Make.defs index 96cdc9cb45d..2c949558bb2 100644 --- a/drivers/ioexpander/Make.defs +++ b/drivers/ioexpander/Make.defs @@ -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 diff --git a/drivers/ioexpander/pi4ioe5v6408.c b/drivers/ioexpander/pi4ioe5v6408.c new file mode 100644 index 00000000000..c6820e9ec39 --- /dev/null +++ b/drivers/ioexpander/pi4ioe5v6408.c @@ -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, ®, 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; +} diff --git a/include/nuttx/ioexpander/pi4ioe5v6408.h b/include/nuttx/ioexpander/pi4ioe5v6408.h new file mode 100644 index 00000000000..dcb9358c0f3 --- /dev/null +++ b/include/nuttx/ioexpander/pi4ioe5v6408.h @@ -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 */
