From fa63ef69c9e01c1edbfc43810a3a38452dfce1a4 Mon Sep 17 00:00:00 2001 From: Michal Lenc Date: Tue, 22 Aug 2023 13:12:19 +0200 Subject: [PATCH] ioexpander: add support for ISO1H812G expander This commit adds support for galvanic isolated ISO1H812G SPI expander. Both single pin and multiple pin writes are supported. Signed-off-by: Michal Lenc --- drivers/ioexpander/Kconfig | 17 + drivers/ioexpander/Make.defs | 4 + drivers/ioexpander/iso1h812g.c | 465 +++++++++++++++++++++++++++ drivers/ioexpander/iso1h812g.h | 57 ++++ include/nuttx/ioexpander/iso1h812g.h | 79 +++++ 5 files changed, 622 insertions(+) create mode 100644 drivers/ioexpander/iso1h812g.c create mode 100644 drivers/ioexpander/iso1h812g.h create mode 100644 include/nuttx/ioexpander/iso1h812g.h diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index 2beb01e3c7..fd459809e2 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -56,6 +56,23 @@ config IOEXPANDER_DUMMY_INT_POLLDELAY endif # IOEXPANDER_DUMMY +config IOEXPANDER_ISO1H812G + bool "ISO1H812G SPI IO expander" + default n + depends on SPI + ---help--- + Enable support for the ISO1H812G SPI IO expander. + +if IOEXPANDER_ISO1H812G + +config ISO1H812G_MULTIPLE + bool "Multiple ISO1H812G Devices" + default n + ---help--- + Can be defined to support multiple ISO1H812G devices on board. + +endif # IOEXPANDER_ISO1H812G + config IOEXPANDER_MCP23X17 bool "MCP23017/MCP23S17 I2C/SPI IO expander" default n diff --git a/drivers/ioexpander/Make.defs b/drivers/ioexpander/Make.defs index 0a75167fd0..ccf12f7d4a 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_ISO1H812G),y) + CSRCS += iso1h812g.c +endif + ifeq ($(CONFIG_IOEXPANDER_PCA9555),y) CSRCS += pca9555.c endif diff --git a/drivers/ioexpander/iso1h812g.c b/drivers/ioexpander/iso1h812g.c new file mode 100644 index 0000000000..93a0e3f031 --- /dev/null +++ b/drivers/ioexpander/iso1h812g.c @@ -0,0 +1,465 @@ +/**************************************************************************** + * drivers/ioexpander/iso1h812g.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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "iso1h812g.h" + +#ifdef CONFIG_IOEXPANDER_ISO1H812G + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct iso1h812g_dev_s +{ + struct ioexpander_dev_s dev; /* Nested structure to allow casting as public gpio + * expander. */ + FAR struct iso1h812g_config_s *config; /* Board configuration data */ + FAR struct spi_dev_s *spi; /* Saved SPI driver instance */ + uint8_t outstate; + mutex_t lock; +}; + +/**************************************************************************** + * Private Function Protototypes + ****************************************************************************/ + +/* SPI helpers */ + +static void iso1h812g_select(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config, + int bits); +static void iso1h812g_deselect(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config); + +/* I/O Expander Methods */ + +static int iso1h812g_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int dir); +static int iso1h812g_option(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int opt, void *regval); +static int iso1h812g_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + bool value); +static int iso1h812g_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value); +#ifdef CONFIG_IOEXPANDER_MULTIPIN +static int iso1h812g_multiwritepin(FAR struct ioexpander_dev_s *dev, + FAR const uint8_t *pins, FAR bool *values, int count); +static int iso1h812g_multireadpin(FAR struct ioexpander_dev_s *dev, + FAR const uint8_t *pins, FAR bool *values, int count); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifndef CONFIG_ISO1H812G_MULTIPLE +/* If only a single device is supported, then the driver state structure may + * as well be pre-allocated. + */ + +static struct iso1h812g_dev_s g_iso1h812g; +#endif + +/* I/O expander vtable */ + +static const struct ioexpander_ops_s g_iso1h812g_ops = +{ + iso1h812g_direction, + iso1h812g_option, + iso1h812g_writepin, + iso1h812g_readpin, + iso1h812g_readpin +#ifdef CONFIG_IOEXPANDER_MULTIPIN + , iso1h812g_multiwritepin + , iso1h812g_multireadpin + , iso1h812g_multireadpin +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iso1h812g_select + * + * Description: + * Select the SPI, locking and re-configuring if necessary + * + * Input Parameters: + * spi - Reference to the SPI driver structure + * bits - Number of SPI bits + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void iso1h812g_select(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config, int bits) +{ + /* Select ISO1H812G 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 ISO1H812G (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: iso1h812g_deselect + * + * Description: + * De-select the SPI + * + * Input Parameters: + * spi - Reference to the SPI driver structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void iso1h812g_deselect(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config) +{ + /* De-select ISO1H812G chip and relinquish the SPI bus. */ + + SPI_SELECT(spi, SPIDEV_EXPANDER(config->id), false); + SPI_LOCK(spi, false); +} + +/**************************************************************************** + * Name: iso1h812g_direction + * + * Description: + * ISO1H812G 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 iso1h812g_direction(FAR struct ioexpander_dev_s *dev, + uint8_t pin, int direction) +{ + FAR struct iso1h812g_dev_s *priv = (FAR struct iso1h812g_dev_s *)dev; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + + gpiowarn("WARNING: ISO1H812G is only input expander!\n"); + + return OK; +} + +/**************************************************************************** + * Name: iso1h812g_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 + * val - The option's value + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +static int iso1h812g_option(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int opt, FAR void *value) +{ + gpiowarn("WARNING: ISO1H812G does not have options!\n"); + + return OK; +} + +/**************************************************************************** + * Name: iso1h812g_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 + * val - 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 iso1h812g_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + bool value) +{ + FAR struct iso1h812g_dev_s *priv = (FAR struct iso1h812g_dev_s *)dev; + int ret; + + if (pin > 7) + { + 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; + } + + /* Set/clear a bit in outstate. */ + + if (value) + { + priv->outstate |= (1 << pin); + } + else + { + priv->outstate &= ~(1 << pin); + } + + iso1h812g_select(priv->spi, priv->config, 8); + SPI_SEND(priv->spi, priv->outstate); + iso1h812g_deselect(priv->spi, priv->config); + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: iso1h812g_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 + * valptr - Pointer to a buffer where the pin level is stored. Usually TRUE + * if the pin is high, except if OPTION_INVERT has been set on + * this pin. + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +static int iso1h812g_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value) +{ + gpiowarn("WARNING: ISO1H812G is only output expander!\n"); + + return OK; +} + +/**************************************************************************** + * Name: iso1h812g_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 + * val - The list of pin levels. + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +#ifdef CONFIG_IOEXPANDER_MULTIPIN +static int iso1h812g_multiwritepin(FAR struct ioexpander_dev_s *dev, + FAR const uint8_t *pins, FAR bool *values, + int count) +{ + FAR struct iso1h812g_dev_s *priv = (FAR struct iso1h812g_dev_s *)dev; + int ret; + uint8_t pin; + int i; + + DEBUGASSERT(priv != NULL && priv->config != NULL && values != NULL); + + /* Get exclusive access to the I/O Expander */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + /* Set/clear a bit in outstate. */ + + for (i = 0; i < count; i++) + { + pin = pins[i]; + gpioinfo("Expander id=%02x, pin=%u\n", priv->config->id, pin); + + if (pin > 7) + { + return -ENXIO; + } + + if (values[i]) + { + priv->outstate |= (1 << pin); + } + else + { + priv->outstate &= ~(1 << pin); + } + } + + iso1h812g_select(priv->spi, priv->config, 8); + SPI_SEND(priv->spi, priv->outstate); + iso1h812g_deselect(priv->spi, priv->config); + + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: iso1h812g_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 + * valptr - Pointer to a buffer where the pin levels are stored. + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +static int iso1h812g_multireadpin(FAR struct ioexpander_dev_s *dev, + FAR const uint8_t *pins, FAR bool *values, + int count) +{ + gpiowarn("WARNING: ISO1H812G is only output expander!\n"); + + return OK; +} +#endif /* CONFIG_IOEXPANDER_MULTIPIN */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iso1h812g_initialize + * + * Description: + * Instantiate and configure the ISO1H812Gxx 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 *iso1h812g_initialize(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config) +{ + FAR struct iso1h812g_dev_s *priv; + +#ifdef CONFIG_ISO1H812G_MULTIPLE + /* Allocate the device state structure */ + + priv = kmm_zalloc(sizeof(struct iso1h812g_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_iso1h812g; +#endif + + /* Initialize the device state structure */ + + priv->dev.ops = &g_iso1h812g_ops; + priv->spi = spi; + priv->config = config; + priv->outstate = 0; + + nxmutex_init(&priv->lock); + return &priv->dev; +} + +#endif /* CONFIG_IOEXPANDER_ISO1H812G */ diff --git a/drivers/ioexpander/iso1h812g.h b/drivers/ioexpander/iso1h812g.h new file mode 100644 index 0000000000..83ff8b4c34 --- /dev/null +++ b/drivers/ioexpander/iso1h812g.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * drivers/ioexpander/iso1h812g.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_ISO1H812G_H +#define __DRIVERS_IOEXPANDER_ISO1H812G_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#if defined(CONFIG_IOEXPANDER) && defined(CONFIG_IOEXPANDER_ISO1H812G) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Prerequisites: + * CONFIG_SPI + * SPI support is required + * CONFIG_IOEXPANDER + * Enables I/O expander support + * + * CONFIG_IOEXPANDER_ISO1H812G + * Enables support for the ISO1H812G driver (Needs CONFIG_INPUT) + * CONFIG_ISO1H812G_MULTIPLE + * Can be defined to support multiple ISO1H812G devices on board. + */ + +#ifndef CONFIG_SPI +# error "CONFIG_SPI is required by ISO1H812G" +#endif + +#endif /* CONFIG_IOEXPANDER && CONFIG_IOEXPANDER_ISO1H812G */ +#endif /* __DRIVERS_IOEXPANDER_ISO1H812G_H */ diff --git a/include/nuttx/ioexpander/iso1h812g.h b/include/nuttx/ioexpander/iso1h812g.h new file mode 100644 index 0000000000..3c6c6e2da9 --- /dev/null +++ b/include/nuttx/ioexpander/iso1h812g.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * include/nuttx/ioexpander/iso1h812g.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_ISO1H812G_H +#define __INCLUDE_NUTTX_IOEXPANDER_ISO1H812G_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the ISO1H812Gxx + * driver when the driver is instantiated. This structure provides + * information about the configuration of the ISO1H812Gxx 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 iso1h812g_config_s +{ + /* Device characterization */ + + uint8_t id; /* ID if multiple devices used to select correct CS */ + uint8_t mode; /* SPI mode */ + uint32_t frequency; /* SPI frequency */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: iso1h812g_initialize + * + * Description: + * Instantiate and configure the ISO1H812Gxx 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 *iso1h812g_initialize(FAR struct spi_dev_s *spi, + FAR struct iso1h812g_config_s *config); + +#endif /* __INCLUDE_NUTTX_IOEXPANDER_ISO1H812G_H */