You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2024/01/23 20:15:31 UTC
(nuttx) branch master updated: ioexpander: add support for iC-JX expander
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 22110b3b83 ioexpander: add support for iC-JX expander
22110b3b83 is described below
commit 22110b3b83e51ee9a5858223a88d038b87ee2c05
Author: Michal Lenc <mi...@seznam.cz>
AuthorDate: Tue Jan 23 14:46:08 2024 +0100
ioexpander: add support for iC-JX expander
This commit adds basic support for iC-JX expander in SPI mode. The
expander is functional and supports both input and output pins. Further
expander functionalities (filtering, adc, interrupt support) are not
yet implemented.
Signed-off-by: Michal Lenc <mi...@seznam.cz>
---
drivers/ioexpander/Kconfig | 18 +
drivers/ioexpander/Make.defs | 4 +
drivers/ioexpander/icjx.c | 750 ++++++++++++++++++++++++++++++++++++++++
drivers/ioexpander/icjx.h | 102 ++++++
include/nuttx/ioexpander/icjx.h | 96 +++++
5 files changed, 970 insertions(+)
diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig
index 77c9b2eb96..f1f6cb9b25 100644
--- a/drivers/ioexpander/Kconfig
+++ b/drivers/ioexpander/Kconfig
@@ -56,6 +56,24 @@ config IOEXPANDER_DUMMY_INT_POLLDELAY
endif # IOEXPANDER_DUMMY
+config IOEXPANDER_ICJX
+ bool "iC-JX SPI IO expander"
+ default n
+ depends on SPI
+ ---help---
+ Enable support for the iC-JX expander from iC-Haus GmbH manufacturer.
+ SPI peripheral is used for the communication.
+
+if IOEXPANDER_ICJX
+
+config iC-JX_MULTIPLE
+ bool "Multiple iC-JX Devices"
+ default n
+ ---help---
+ Can be defined to support multiple iC-JX devices on board.
+
+endif # IOEXPANDER_ICJX
+
config IOEXPANDER_ISO1H812G
bool "ISO1H812G SPI IO expander"
default n
diff --git a/drivers/ioexpander/Make.defs b/drivers/ioexpander/Make.defs
index 2777af4415..cdf0921ccd 100644
--- a/drivers/ioexpander/Make.defs
+++ b/drivers/ioexpander/Make.defs
@@ -32,6 +32,10 @@ ifeq ($(CONFIG_IOEXPANDER_DUMMY),y)
CSRCS += ioe_dummy.c
endif
+ifeq ($(CONFIG_IOEXPANDER_ICJX),y)
+ CSRCS += icjx.c
+endif
+
ifeq ($(CONFIG_IOEXPANDER_ISO1H812G),y)
CSRCS += iso1h812g.c
endif
diff --git a/drivers/ioexpander/icjx.c b/drivers/ioexpander/icjx.c
new file mode 100644
index 0000000000..bff1975d18
--- /dev/null
+++ b/drivers/ioexpander/icjx.c
@@ -0,0 +1,750 @@
+/****************************************************************************
+ * drivers/ioexpander/icjx.c
+ *
+ * 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 <debug.h>
+#include <sys/param.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/ioexpander/ioexpander.h>
+#include <nuttx/ioexpander/icjx.h>
+
+#include "icjx.h"
+
+#ifdef CONFIG_IOEXPANDER_ICJX
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ICJX_NOP 0x00
+#define ICJX_RNW 0x01
+#define ICJX_NOB 0x0f
+#define ICJX_CONTROL 0x59
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct icjx_dev_s
+{
+ struct ioexpander_dev_s dev; /* Nested structure to allow casting
+ * as public gpio expander. */
+ FAR struct icjx_config_s *config; /* Board configuration data */
+ FAR struct spi_dev_s *spi; /* Saved SPI driver instance */
+ uint16_t outpins;
+ uint16_t outstate;
+ mutex_t lock;
+};
+
+/****************************************************************************
+ * Private Function Protototypes
+ ****************************************************************************/
+
+/* SPI helpers */
+
+static void icjx_select(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config, int bits);
+static void icjx_deselect(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config);
+
+/* Read/Write helpers */
+
+static int icjx_read(FAR struct icjx_dev_s *priv, uint8_t reg,
+ uint8_t *data);
+static int icjx_write(FAR struct icjx_dev_s *priv, uint8_t reg,
+ uint8_t data);
+
+/* I/O Expander Methods */
+
+static int icjx_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ int dir);
+static int icjx_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ int opt, void *regval);
+static int icjx_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ bool value);
+static int icjx_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ FAR bool *value);
+#ifdef CONFIG_IOEXPANDER_MULTIPIN
+static int icjx_multiwritepin(FAR struct ioexpander_dev_s *dev,
+ FAR const uint8_t *pins, FAR bool *values,
+ int count);
+static int icjx_multireadpin(FAR struct ioexpander_dev_s *dev,
+ FAR const uint8_t *pins, FAR bool *values,
+ int count);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifndef CONFIG_ICJX_MULTIPLE
+/* If only a single device is supported, then the driver state structure may
+ * as well be pre-allocated.
+ */
+
+static struct icjx_dev_s g_icjx;
+#endif
+
+/* I/O expander vtable */
+
+static const struct ioexpander_ops_s g_icjx_ops =
+{
+ icjx_direction,
+ icjx_option,
+ icjx_writepin,
+ icjx_readpin,
+ icjx_readpin
+#ifdef CONFIG_IOEXPANDER_MULTIPIN
+ , icjx_multiwritepin
+ , icjx_multireadpin
+ , icjx_multireadpin
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: icjx_select
+ *
+ * Description:
+ * Select the SPI, locking and re-configuring if necessary
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ * config - Reference to iC-JX configuration structure
+ * bits - Number of SPI bits
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void icjx_select(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config, int bits)
+{
+ /* Select iC-JX chip (locking the SPI bus in case there are multiple
+ * devices competing for the SPI bus
+ */
+
+ SPI_LOCK(spi, true);
+ SPI_SELECT(spi, SPIDEV_EXPANDER(config->id), true);
+
+ /* Now make sure that the SPI bus is configured for the iC-JX (it
+ * might have gotten configured for a different device while unlocked)
+ */
+
+ SPI_SETMODE(spi, config->mode);
+ SPI_SETBITS(spi, bits);
+ SPI_SETFREQUENCY(spi, config->frequency);
+}
+
+/****************************************************************************
+ * Name: icjx_deselect
+ *
+ * Description:
+ * De-select the SPI
+ *
+ * Input Parameters:
+ * spi - Reference to the SPI driver structure
+ * config - Reference to iC-JX configuration structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void icjx_deselect(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config)
+{
+ /* De-select iC-JX chip and relinquish the SPI bus. */
+
+ SPI_SELECT(spi, SPIDEV_EXPANDER(config->id), false);
+ SPI_LOCK(spi, false);
+}
+
+/****************************************************************************
+ * Name: icjx_read
+ *
+ * Description:
+ * Helper function for register read operation.
+ *
+ * Input Parameters:
+ * priv - Pointer to icjx_dev_s structure
+ * reg - Register offset (see icjx.h)
+ * data - Pointer to read data
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_read(FAR struct icjx_dev_s *priv, uint8_t reg, uint8_t *data)
+{
+ uint8_t startaddr;
+ uint8_t tx_buffer[5];
+ uint8_t rx_buffer[5];
+
+ startaddr = (priv->config->addr << 6) | (reg << 1) | ICJX_RNW;
+ tx_buffer[0] = startaddr;
+ tx_buffer[1] = ICJX_NOP;
+ tx_buffer[2] = ICJX_NOB;
+
+ icjx_select(priv->spi, priv->config, 8);
+ SPI_EXCHANGE(priv->spi, tx_buffer, rx_buffer, 3);
+
+ *data = rx_buffer[2];
+
+ if (priv->config->verification)
+ {
+ tx_buffer[0] = rx_buffer[2];
+ tx_buffer[1] = ICJX_CONTROL;
+ SPI_EXCHANGE(priv->spi, tx_buffer, rx_buffer, 2);
+ }
+
+ icjx_deselect(priv->spi, priv->config);
+
+ if (priv->config->verification)
+ {
+ if (rx_buffer[0] != startaddr)
+ {
+ gpioerr("ERROR: Data verification error for register 0x%x!\n",
+ reg);
+ return -EIO;
+ }
+
+ if (rx_buffer[1] != ICJX_CONTROL)
+ {
+ gpioerr("ERROR: Control byte verification error for register"
+ "0x%x!\n", reg);
+ return -EIO;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: icjx_write
+ *
+ * Description:
+ * Helper function for register write operation.
+ *
+ * Input Parameters:
+ * priv - Pointer to icjx_dev_s structure
+ * reg - Register offset (see icjx.h)
+ * data - Data to be written
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_write(FAR struct icjx_dev_s *priv, uint8_t reg, uint8_t data)
+{
+ uint8_t startaddr;
+ uint8_t bytes_to_exchange;
+ uint8_t tx_buffer[5];
+ uint8_t rx_buffer[5];
+
+ bytes_to_exchange = 3;
+ startaddr = (priv->config->addr << 6) | (reg << 1);
+ tx_buffer[0] = startaddr;
+ tx_buffer[1] = ICJX_NOB;
+ tx_buffer[2] = data;
+
+ if (priv->config->verification)
+ {
+ tx_buffer[3] = startaddr;
+ tx_buffer[4] = ICJX_CONTROL;
+ bytes_to_exchange = 5;
+ }
+
+ icjx_select(priv->spi, priv->config, 8);
+ SPI_EXCHANGE(priv->spi, tx_buffer, rx_buffer, bytes_to_exchange);
+ icjx_deselect(priv->spi, priv->config);
+
+ if (priv->config->verification)
+ {
+ if (rx_buffer[2] != ICJX_NOB)
+ {
+ gpioerr("ERROR: Start address verification error for register"
+ "0x%x!\n", reg);
+ return -EIO;
+ }
+
+ if (rx_buffer[3] != data)
+ {
+ gpioerr("ERROR: Data verification error for register 0x%x!\n",
+ reg);
+ return -EIO;
+ }
+
+ if (rx_buffer[4] != ICJX_CONTROL)
+ {
+ gpioerr("ERROR: Data verification error for register 0x%x!\n",
+ reg);
+ return -EIO;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: icjx_direction
+ *
+ * Description:
+ * iC-JX is only input pin. However interface is provided in order
+ * to avoid system falls if called.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pin - The index of the pin to alter in this call
+ * dir - One of the IOEXPANDER_DIRECTION_ macros
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ int dir)
+{
+ FAR struct icjx_dev_s *priv = (FAR struct icjx_dev_s *)dev;
+ uint8_t outpins;
+ uint8_t out_set;
+ uint8_t regaddr;
+ int ret;
+
+ if (dir != IOEXPANDER_DIRECTION_IN &&
+ dir != IOEXPANDER_DIRECTION_OUT)
+ {
+ return -EINVAL;
+ }
+
+ DEBUGASSERT(priv != NULL && priv->config != NULL && pin < 16);
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (dir == IOEXPANDER_DIRECTION_OUT)
+ {
+ priv->outpins |= (1 << pin);
+ }
+ else
+ {
+ priv->outpins &= ~(1 << pin);
+ }
+
+ if (pin < 8)
+ {
+ regaddr = ICJX_CTRL_WORD_2_A;
+ outpins = priv->outpins & 0xff;
+ }
+ else
+ {
+ regaddr = ICJX_CTRL_WORD_2_B;
+ outpins = (priv->outpins >> 8) & 0xff;
+ }
+
+ ret = icjx_read(priv, regaddr, &out_set);
+ if (ret < 0)
+ {
+ nxmutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ /* Enable output generation */
+
+ if ((outpins & 0xf) != 0)
+ {
+ out_set |= ICJX_CTRL_WORD_2_NIOL;
+ }
+ else
+ {
+ out_set &= ~ICJX_CTRL_WORD_2_NIOL;
+ }
+
+ if ((outpins & 0xf0) != 0)
+ {
+ out_set |= ICJX_CTRL_WORD_2_NIOH;
+ }
+ else
+ {
+ out_set &= ~ICJX_CTRL_WORD_2_NIOH;
+ }
+
+ ret = icjx_write(priv, regaddr, out_set);
+ nxmutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: icjx_option
+ *
+ * Description:
+ * Set pin options. Required.
+ * Since all IO expanders have various pin options, this API allows setting
+ * pin options in a flexible way.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pin - The index of the pin to alter in this call
+ * opt - One of the IOEXPANDER_OPTION_ macros
+ * value - The option's value
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_option(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ int opt, FAR void *value)
+{
+ /* TODO: Implementation of iC-JX options should be here. This includes
+ * setup of filters, ADC, interrupts etc. The right way to implement
+ * this would probably be to introduce config structure to
+ * include/nuttx/ioexpanders/icjx.h that the user could use for the
+ * nibbles configuration.
+ */
+
+ gpiowarn("ERROR: iC-JX options are not yet implemted!\n");
+
+ return -ENOTSUP;
+}
+
+/****************************************************************************
+ * Name: icjx_writepin
+ *
+ * Description:
+ * Set the pin level. Required.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pin - The index of the pin to alter in this call
+ * value - The pin level. Usually TRUE will set the pin high,
+ * except if OPTION_INVERT has been set on this pin.
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ bool value)
+{
+ FAR struct icjx_dev_s *priv = (FAR struct icjx_dev_s *)dev;
+ uint8_t outstate;
+ uint8_t regaddr;
+ int ret;
+
+ if (pin > 16)
+ {
+ return -ENXIO;
+ }
+
+ DEBUGASSERT(priv != NULL && priv->config != NULL);
+
+ gpioinfo("Expander id=%02x, pin=%u\n", priv->config->id, pin);
+
+ /* Get exclusive access to the I/O Expander */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if ((priv->outpins & (1 << pin)) == 0)
+ {
+ gpioerr("ERROR: pin%u is an input\n", pin);
+ nxmutex_unlock(&priv->lock);
+ return -EINVAL;
+ }
+
+ /* Set/clear a bit in outstate. */
+
+ if (value)
+ {
+ priv->outstate |= (1 << pin);
+ }
+ else
+ {
+ priv->outstate &= ~(1 << pin);
+ }
+
+ if (pin < 8)
+ {
+ regaddr = ICJX_OUTPUT_A;
+ outstate = priv->outstate & 0xff;
+ }
+ else
+ {
+ regaddr = ICJX_OUTPUT_B;
+ outstate = (priv->outstate >> 8) & 0xff;
+ }
+
+ ret = icjx_write(priv, regaddr, outstate);
+ nxmutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: icjx_readpin
+ *
+ * Description:
+ * Read the actual PIN level. This can be different from the last value
+ * written to this pin. Required.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pin - The index of the pin
+ * value - Pointer to a buffer where the pin level is stored. Usually
+ * TRUE if the pin is high, except if OPTION_INVERT has been
+ * set this pin.
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin,
+ FAR bool *value)
+{
+ FAR struct icjx_dev_s *priv = (FAR struct icjx_dev_s *)dev;
+ uint8_t regaddr;
+ uint8_t data;
+ int ret;
+
+ if (pin > 16)
+ {
+ return -ENXIO;
+ }
+
+ DEBUGASSERT(priv != NULL && priv->config != NULL && value != NULL);
+
+ gpioinfo("Expander addr=%02x, pin=%u\n", priv->config->addr, pin);
+
+ /* Get exclusive access to the I/O Expander */
+
+ ret = nxmutex_lock(&priv->lock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Make sure that this is an output pin */
+
+ if ((priv->outpins & (1 << pin)) != 0)
+ {
+ *value = ((priv->outstate & (1 << pin)) != 0);
+ nxmutex_unlock(&priv->lock);
+ return OK;
+ }
+
+ regaddr = pin < 8 ? ICJX_INPUT_A : ICJX_INPUT_B;
+
+ ret = icjx_read(priv, regaddr, &data);
+ nxmutex_unlock(&priv->lock);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ *value = (bool)((data >> (pin & 0xf)) & 1);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: icjx_multiwritepin
+ *
+ * Description:
+ * Set the pin level for multiple pins. This routine may be faster than
+ * individual pin accesses. Optional.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pins - The list of pin indexes to alter in this call
+ * values - The list of pin levels.
+ * count - Number of pins to be written
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IOEXPANDER_MULTIPIN
+static int icjx_multiwritepin(FAR struct ioexpander_dev_s *dev,
+ FAR const uint8_t *pins,
+ FAR bool *values, int count)
+{
+ gpiowarn("WARNING: iC-JX does not have implemented support for"
+ "multiple pin write operation!\n");
+
+ return -ENOTSUP;
+}
+
+/****************************************************************************
+ * Name: icjx_multireadpin
+ *
+ * Description:
+ * Read the actual level for multiple pins. This routine may be faster than
+ * individual pin accesses. Optional.
+ *
+ * Input Parameters:
+ * dev - Device-specific state data
+ * pin - The list of pin indexes to read
+ * values - Pointer to a buffer where the pin levels are stored.
+ * count - Number of pins to be read
+ *
+ * Returned Value:
+ * 0 on success, else a negative error code
+ *
+ ****************************************************************************/
+
+static int icjx_multireadpin(FAR struct ioexpander_dev_s *dev,
+ FAR const uint8_t *pins, FAR bool *values,
+ int count)
+{
+ gpiowarn("WARNING: iC-JX does not have implemented support for"
+ "multiple pin read operation!\n");
+
+ return -ENOTSUP;
+}
+#endif /* CONFIG_IOEXPANDER_MULTIPIN */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: icjx_initialize
+ *
+ * Description:
+ * Instantiate and configure the ICJXxx device driver to use the
+ * provided SPI device instance.
+ *
+ * Input Parameters:
+ * spi - A SPI driver instance
+ * config - Persistent board configuration data
+ *
+ * Returned Value:
+ * an ioexpander_dev_s instance on success, NULL on failure.
+ *
+ ****************************************************************************/
+
+FAR struct ioexpander_dev_s *icjx_initialize(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config)
+{
+ FAR struct icjx_dev_s *priv;
+ uint8_t regval;
+ int ret;
+
+#ifdef CONFIG_ICJX_MULTIPLE
+ /* Allocate the device state structure */
+
+ priv = kmm_zalloc(sizeof(struct icjx_dev_s));
+ if (!priv)
+ {
+ gpioerr("ERROR: Failed to allocate driver instance\n");
+ return NULL;
+ }
+#else
+ /* Use the one-and-only I/O Expander driver instance */
+
+ priv = &g_icjx;
+#endif
+
+ /* Initialize the device state structure */
+
+ priv->dev.ops = &g_icjx_ops;
+ priv->spi = spi;
+ priv->config = config;
+ priv->outpins = 0;
+ priv->outstate = 0;
+
+ nxmutex_init(&priv->lock);
+
+ /* Set pull up/down based on expander configuration. This is the first
+ * time we are accessing the expander therefore we do not have to worry
+ * about reading the register content first.
+ */
+
+ regval = (config->current_src << 4) | config->current_src;
+
+ ret = icjx_write(priv, ICJX_CTRL_WORD_2_A, regval);
+ if (ret < 0)
+ {
+ gpioerr("ERROR: Could write to ICJX_CTRL_WORD_2_A: %d!\n", ret);
+ goto err;
+ }
+
+ ret = icjx_write(priv, ICJX_CTRL_WORD_2_B, regval);
+ if (ret < 0)
+ {
+ gpioerr("ERROR: Could write to ICJX_CTRL_WORD_2_B: %d!\n", ret);
+ goto err;
+ }
+
+ /* Bypass filters as those are not yet supported. */
+
+ regval = ICJX_CTRL_WORD_1_BYP0 | ICJX_CTRL_WORD_1_BYP1;
+ ret = icjx_write(priv, ICJX_CTRL_WORD_1_A, regval);
+ if (ret < 0)
+ {
+ gpioerr("ERROR: Could write to ICJX_CTRL_WORD_1_A: %d!\n", ret);
+ goto err;
+ }
+
+ ret = icjx_write(priv, ICJX_CTRL_WORD_1_B, regval);
+ if (ret < 0)
+ {
+ gpioerr("ERROR: Could write to ICJX_CTRL_WORD_1_B: %d!\n", ret);
+ goto err;
+ }
+
+ return &priv->dev;
+
+err:
+ nxmutex_destroy(&priv->lock);
+#ifdef CONFIG_ICJX_MULTIPLE
+ kmm_free(priv);
+#endif
+ return NULL;
+}
+
+#endif /* CONFIG_IOEXPANDER_ICJX */
diff --git a/drivers/ioexpander/icjx.h b/drivers/ioexpander/icjx.h
new file mode 100644
index 0000000000..f0a736ce0e
--- /dev/null
+++ b/drivers/ioexpander/icjx.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * drivers/ioexpander/icjx.h
+ *
+ * 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 __DRIVERS_IOEXPANDER_ICJX_H
+#define __DRIVERS_IOEXPANDER_ICJX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/mutex.h>
+
+#if defined(CONFIG_IOEXPANDER) && defined(CONFIG_IOEXPANDER_ICJX)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Prerequisites:
+ * CONFIG_SPI
+ * SPI support is required
+ * CONFIG_IOEXPANDER
+ * Enables I/O expander support
+ *
+ * CONFIG_IOEXPANDER_ICJX
+ * Enables support for the ICJX driver (Needs CONFIG_INPUT)
+ * CONFIG_ICJX_MULTIPLE
+ * Can be defined to support multiple ICJX devices on board.
+ */
+
+#ifndef CONFIG_SPI
+# error "CONFIG_SPI is required by ICJX"
+#endif
+
+/* iC-JX Registers **********************************************************/
+
+#define ICJX_INPUT_A 0x00
+#define ICJX_INPUT_B 0x01
+#define ICJX_CHNG_MSG_A 0x02
+#define ICJX_CNNG_MSG_B 0x03
+#define ICJX_INT_STATUS_A 0x04
+#define ICJX_INT_STATUS_B 0x05
+#define ICJX_OVERCURR_MSG_A 0x06
+#define ICJX_OVERCURR_MSG_B 0x07
+#define ICJX_OVERCURR_STATUS_A 0x08
+#define ICJX_OVERCURR_STATUS_B 0x09
+#define ICJX_ADC_DATA_1 0x0A
+#define ICJX_ADC_DATA_2 0x0B
+#define ICJX_OUTPUT_A 0x0C
+#define ICJX_OUTPUT_B 0x0D
+#define ICJX_FLASH_PULSE_A 0x0E
+#define ICJX_FLASH_PULSE_B 0x0F
+#define ICJX_CHNG_INT_EN_A 0x10
+#define ICJX_CHNG_INT_EN_B 0x11
+#define ICJX_OVERCURR_INT_EN_A 0x12
+#define ICJX_OVERCURR_INT_EN_B 0x13
+#define ICJX_CTRL_WORD_1_A 0x14
+#define ICJX_CTRL_WORD_1_B 0x15
+#define ICJX_CTRL_WORD_2_A 0x16
+#define ICJX_CTRL_WORD_2_B 0x17
+#define ICJX_CTRL_WORD_3_A 0x18
+#define ICJX_CTRL_WORD_3_B 0x19
+#define ICJX_CTRL_WORD_4 0x1A
+#define ICJX_CTRL_WORD_5 0x1B
+#define ICJX_CTRL_WORD_6 0x1C
+#define ICJX_DEV_ID 0x1D
+#define ICJX_TEST_1 0x1E
+#define ICJX_TEST_2 0x1F
+
+/* Control Word 1 */
+
+#define ICJX_CTRL_WORD_1_BYP0 (1 << 3)
+#define ICJX_CTRL_WORD_1_BYP1 (1 << 7)
+
+/* Control Word 2 */
+
+#define ICJX_CTRL_WORD_2_NIOL (1 << 3)
+#define ICJX_CTRL_WORD_2_NIOH (1 << 7)
+
+#endif /* CONFIG_IOEXPANDER && CONFIG_IOEXPANDER_ICJX */
+#endif /* __DRIVERS_IOEXPANDER_ICJX_H */
diff --git a/include/nuttx/ioexpander/icjx.h b/include/nuttx/ioexpander/icjx.h
new file mode 100644
index 0000000000..629a048019
--- /dev/null
+++ b/include/nuttx/ioexpander/icjx.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+ * include/nuttx/ioexpander/icjx.h
+ *
+ * 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_ICJX_H
+#define __INCLUDE_NUTTX_IOEXPANDER_ICJX_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ICJX_CTRL_WORD_CURR_SRC_DIS 0 /* Pin current source disabled */
+#define ICJX_CTRL_WORD_PULLDOWN_200U 1 /* 200 uA pull down */
+#define ICJX_CTRL_WORD_PULLDOWN_600U 2 /* 600 uA pull down */
+#define ICJX_CTRL_WORD_PULLDOWN_2M 3 /* 2 mA pull down */
+#define ICJX_CTRL_WORD_PULLUP_200U 5 /* 200 uA pull up */
+#define ICJX_CTRL_WORD_PULLUP_600U 6 /* 600 uA pull up */
+#define ICJX_CTRL_WORD_PULLUP_2M 7 /* 2 mA pull up*/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the iC-JX
+ * driver when the driver is instantiated. This structure provides
+ * information about the configuration of the iC-JX and provides some
+ * board-specific hooks.
+ *
+ * Memory for this structure is provided by the caller. It is not copied by
+ * the driver and is presumed to persist while the driver is active. The
+ * memory must be writeable because, under certain circumstances, the driver
+ * may modify the frequency.
+ */
+
+struct icjx_config_s
+{
+ /* Device characterization */
+
+ uint8_t id; /* Device ID (if more expanders are used) */
+ uint8_t verification; /* True if data verification on MISO line is used */
+ uint8_t current_src; /* Current sources for pin nibbles (pull up,
+ * pull down) - see Control Word 2 register
+ */
+ uint8_t addr; /* Device address (set by A(1:0) pins) */
+ uint8_t mode; /* SPI mode */
+ uint32_t frequency; /* SPI frequency */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: icjx_initialize
+ *
+ * Description:
+ * Instantiate and configure the iC-JX device driver to use the
+ * provided SPI device instance.
+ *
+ * Input Parameters:
+ * spi - A SPI driver instance
+ * config - Persistent board configuration data
+ *
+ * Returned Value:
+ * an ioexpander_dev_s instance on success, NULL on failure.
+ *
+ ****************************************************************************/
+
+struct spi_dev_s;
+FAR struct ioexpander_dev_s *icjx_initialize(FAR struct spi_dev_s *spi,
+ FAR struct icjx_config_s *config);
+
+#endif /* __INCLUDE_NUTTX_IOEXPANDER_ICJX_H */