From dc608de1442605de8fc0b414d3288c288ae7c889 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sat, 24 Jun 2023 15:08:27 +0200 Subject: [PATCH] drivers/ioexpander: add support for SX1509 SX1509 from Semtech is GPIO expander with LED driver and keypad engine. For now, the keypad engine is not supported. --- drivers/ioexpander/Kconfig | 52 + drivers/ioexpander/Make.defs | 4 + drivers/ioexpander/sx1509.c | 1356 +++++++++++++++++++++++++ drivers/ioexpander/sx1509.h | 293 ++++++ include/nuttx/ioexpander/ioexpander.h | 3 + include/nuttx/ioexpander/sx1509.h | 114 +++ 6 files changed, 1822 insertions(+) create mode 100644 drivers/ioexpander/sx1509.c create mode 100644 drivers/ioexpander/sx1509.h create mode 100644 include/nuttx/ioexpander/sx1509.h diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig index e5294dcc42..0fc26afd4c 100644 --- a/drivers/ioexpander/Kconfig +++ b/drivers/ioexpander/Kconfig @@ -297,12 +297,64 @@ config PCF8574_INT_POLLDELAY endif # IOEXPANDER_PCF8574 +config IOEXPANDER_SX1509 + bool "SX1509 I2C IO expander" + default n + depends on I2C + ---help--- + Enable support for the SX1509 IO Expander + +if IOEXPANDER_SX1509 + +config SX1509_MULTIPLE + bool "Multiple SX1509 Devices" + default n + ---help--- + Can be defined to support multiple SX1509 devices on board. + +config SX1509_INT_ENABLE + bool "Enable SX1509 Interrupt Support" + default n + select IOEXPANDER_INT_ENABLE + ---help--- + Enable driver interrupt functionality + +config SX1509_LED_ENABLE + bool "Enable SX1509 LED Support" + default n + select IOEXPANDER_LED_ENABLE + ---help--- + Enable driver LED functionality + +config SX1509_INT_NCALLBACKS + int "Max number of interrupt callbacks" + default 4 + depends on SX1509_INT_ENABLE + ---help--- + This is the maximum number of interrupt callbacks supported + +config SX1509_RETRY + bool "Retry to send commands and data at I2C communication errors" + default n + ---help--- + Retry to send commands and data if a I2C-communication + error occurs (eg. caused by EMC). + +endif # IOEXPANDER_SX1509 + config IOEXPANDER_INT_ENABLE bool default n ---help--- This is the global INT supported flag for io expanders +config IOEXPANDER_LED_ENABLE + bool + default n + select USERLED + ---help--- + This is the global LED supported flag for io expanders + config IOEXPANDER_NPINS int "Number of pins" default 16 diff --git a/drivers/ioexpander/Make.defs b/drivers/ioexpander/Make.defs index 9c5d2daa28..c208800a73 100644 --- a/drivers/ioexpander/Make.defs +++ b/drivers/ioexpander/Make.defs @@ -52,6 +52,10 @@ ifeq ($(CONFIG_IOEXPANDER_MCP23X17),y) CSRCS += mcp23x17.c endif +ifeq ($(CONFIG_IOEXPANDER_SX1509),y) + CSRCS += sx1509.c +endif + endif # CONFIG_IOEXPANDER # GPIO test device driver (independent of IOEXPANDERS) diff --git a/drivers/ioexpander/sx1509.c b/drivers/ioexpander/sx1509.c new file mode 100644 index 0000000000..88fdbafed9 --- /dev/null +++ b/drivers/ioexpander/sx1509.c @@ -0,0 +1,1356 @@ +/**************************************************************************** + * drivers/ioexpander/sx1509.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 "sx1509.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#if (!defined CONFIG_SCHED_HPWORK) && (defined CONFIG_SX1509_INT_ENABLE) +# error High-Priority Work support is required (CONFIG_SCHED_HPWORK) +#endif + +#ifdef CONFIG_IOEXPANDER_MULTIPIN +# error Multipin operation not supported for SX1509 for now +#endif + +#ifdef CONFIG_SX1509_INT_ENABLE +# error Interrupt support not testen on HW +#endif + +/* LED driver register index */ + +#define SX1509_LEDREGS_TON (0) +#define SX1509_LEDREGS_ION (1) +#define SX1509_LEDREGS_OFF (2) +#define SX1509_LEDREGS_RISE (3) +#define SX1509_LEDREGS_FALL (4) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline int sx1509_write(FAR struct sx1509_dev_s *priv, + FAR const uint8_t *wbuffer, int wbuflen); +static inline int sx1509_writeread(FAR struct sx1509_dev_s *priv, + FAR const uint8_t *wbuffer, int wbuflen, FAR uint8_t *rbuffer, + int rbuflen); +static int sx1509_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int dir); +static int sx1509_option(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int opt, void *val); +static int sx1509_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + bool value); +static int sx1509_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value); +static int sx1509_readbuf(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value); +#ifdef CONFIG_IOEXPANDER_INT_ENABLE +static FAR void *sx1509_attach(FAR struct ioexpander_dev_s *dev, + ioe_pinset_t pinset, ioe_callback_t callback, FAR void *arg); +static int sx1509_detach(FAR struct ioexpander_dev_s *dev, + FAR void *handle); +#endif +#ifdef CONFIG_SX1509_LED_ENABLE +static int sx1509_osc_config(FAR struct sx1509_dev_s *priv); +static int sx1509_led2pin(FAR struct sx1509_dev_s *priv, uint8_t led); +static userled_set_t +sx1509_ll_supported(FAR const struct userled_lowerhalf_s *lower); +static void sx1509_ll_setled(FAR const struct userled_lowerhalf_s *lower, + int led, bool ledon); +static void sx1509_ll_setall(FAR const struct userled_lowerhalf_s *lower, + userled_set_t ledset); +# ifdef CONFIG_USERLED_LOWER_READSTATE +static void sx1509_ll_getall(FAR const struct userled_lowerhalf_s *lower, + userled_set_t *ledset); +# endif +# ifdef CONFIG_USERLED_EFFECTS +static void sx1509_ll_effect_sup(FAR const struct userled_lowerhalf_s *lower, + FAR struct userled_effect_sup_s *sup); +static int sx1509_ll_effect_set(FAR const struct userled_lowerhalf_s *lower, + FAR struct userled_effect_set_s *effect); +# endif +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifndef CONFIG_SX1509_MULTIPLE +/* If only a single SX1509 device is supported, then the driver state + * structure may as well be pre-allocated. + */ + +static struct sx1509_dev_s g_sx1509; + +/* Otherwise, we will need to maintain allocated driver instances in a list */ + +#else +static struct sx1509_dev_s *g_sx1509list; +#endif + +/* I/O expander vtable */ + +static const struct ioexpander_ops_s g_sx1509_ops = +{ + sx1509_direction, + sx1509_option, + sx1509_writepin, + sx1509_readpin, + sx1509_readbuf +#ifdef CONFIG_IOEXPANDER_MULTIPIN + , NULL + , NULL + , NULL +#endif +#ifdef CONFIG_IOEXPANDER_INT_ENABLE + , sx1509_attach + , sx1509_detach +#endif +}; + +#ifdef CONFIG_USERLED_EFFECTS +/* LED driver registers: + * TON, ION, OFF, TRISE, TFALL + */ + +static FAR uint8_t g_sx1509_led_regs[SX1509_NR_GPIO_MAX][5] = +{ + { + SX1509_REGTON_0, SX1509_REGION_0, SX1509_REGOFF_0, + 0, 0 + }, + + { + SX1509_REGTON_1, SX1509_REGION_1, SX1509_REGOFF_1, + 0, 0 + }, + { + SX1509_REGTON_2, SX1509_REGION_2, SX1509_REGOFF_2, + 0, 0 + }, + { + SX1509_REGTON_3, SX1509_REGION_3, SX1509_REGOFF_3, + 0, 0 + }, + { + SX1509_REGTON_4, SX1509_REGION_4, SX1509_REGOFF_4, + SX1509_TRISE_4, SX1509_TFALL_4 + }, + { + SX1509_REGTON_5, SX1509_REGION_5, SX1509_REGOFF_5, + SX1509_TRISE_5, SX1509_TFALL_5 + }, + { + SX1509_REGTON_6, SX1509_REGION_6, SX1509_REGOFF_6, + SX1509_TRISE_6, SX1509_TFALL_6 + }, + { + SX1509_REGTON_7, SX1509_REGION_7, SX1509_REGOFF_7, + SX1509_TRISE_7, SX1509_TFALL_7 + }, + { + SX1509_REGTON_8, SX1509_REGION_8, SX1509_REGOFF_8, + 0, 0 + }, + { + SX1509_REGTON_9, SX1509_REGION_9, SX1509_REGOFF_9, + 0, 0 + }, + { + SX1509_REGTON_10, SX1509_REGION_10, SX1509_REGOFF_10, + 0, 0 + }, + { + SX1509_REGTON_11, SX1509_REGION_11, SX1509_REGOFF_11, + 0, 0 + }, + { + SX1509_REGTON_12, SX1509_REGION_12, SX1509_REGOFF_12, + SX1509_TRISE_12, SX1509_TFALL_12 + }, + { + SX1509_REGTON_13, SX1509_REGION_13, SX1509_REGOFF_13, + SX1509_TRISE_13, SX1509_TFALL_13 + }, + { + SX1509_REGTON_14, SX1509_REGION_14, SX1509_REGOFF_14, + SX1509_TRISE_14, SX1509_TFALL_14 + }, + { + SX1509_REGTON_15, SX1509_REGION_15, SX1509_REGOFF_15, + SX1509_TRISE_15, SX1509_TFALL_15 + } +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sx1509_write + * + * Description: + * Write to the I2C device. + * + ****************************************************************************/ + +static inline int sx1509_write(FAR struct sx1509_dev_s *priv, + FAR const uint8_t *wbuffer, int wbuflen) +{ + struct i2c_msg_s msg; + int ret; + + /* Setup for the transfer */ + + msg.frequency = priv->config->frequency; + msg.addr = priv->config->address; + msg.flags = 0; + msg.buffer = (FAR uint8_t *)wbuffer; /* Override const */ + msg.length = wbuflen; + + /* Then perform the transfer. */ + + ret = I2C_TRANSFER(priv->i2c, &msg, 1); + return (ret >= 0) ? OK : ret; +} + +/**************************************************************************** + * Name: sx1509_writeread + * + * Description: + * Write to then read from the I2C device. + * + ****************************************************************************/ + +static inline int sx1509_writeread(FAR struct sx1509_dev_s *priv, + FAR const uint8_t *wbuffer, int wbuflen, + FAR uint8_t *rbuffer, int rbuflen) +{ + struct i2c_config_s config; + + /* Set up the configuration and perform the write-read operation */ + + config.frequency = priv->config->frequency; + config.address = priv->config->address; + config.addrlen = 7; + + return i2c_writeread(priv->i2c, &config, wbuffer, wbuflen, + rbuffer, rbuflen); +} + +/**************************************************************************** + * Name: sx1509_setbit + * + * Description: + * Write a bit in a register pair + * + ****************************************************************************/ + +static int sx1509_setbit(FAR struct sx1509_dev_s *priv, uint8_t addr, + uint8_t pin, bool bitval) +{ + uint8_t buf[2]; + int ret; + + if (pin > 15) + { + return -ENXIO; + } + else if (pin > 7) + { + addr -= 1; + pin -= 8; + } + + buf[0] = addr; + + /* Get the register value from the IO-Expander */ + + ret = sx1509_writeread(priv, &buf[0], 1, &buf[1], 1); + if (ret < 0) + { + return ret; + } + + if (bitval) + { + buf[1] |= (1 << pin); + } + else + { + buf[1] &= ~(1 << pin); + } + + ret = sx1509_write(priv, buf, 2); +#ifdef CONFIG_SX1509_RETRY + if (ret != OK) + { + /* Try again (only once) */ + + ret = sx1509_write(priv, buf, 2); + } +#endif + + return ret; +} + +/**************************************************************************** + * Name: sx1509_getbit + * + * Description: + * Get a bit from a register pair + * + ****************************************************************************/ + +static int sx1509_getbit(FAR struct sx1509_dev_s *priv, uint8_t addr, + uint8_t pin, FAR bool *val) +{ + uint8_t buf; + int ret; + + if (pin > 15) + { + return -ENXIO; + } + else if (pin > 7) + { + addr -= 1; + pin -= 8; + } + + ret = sx1509_writeread(priv, &addr, 1, &buf, 1); + if (ret < 0) + { + return ret; + } + + *val = (buf >> pin) & 1; + return OK; +} + +/**************************************************************************** + * Name: sx1509_direction + * + * Description: + * Set the direction of an ioexpander pin. Required. + * + * 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 sx1509_direction(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int direction) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + int ret; + + if (direction != IOEXPANDER_DIRECTION_IN && + direction != IOEXPANDER_DIRECTION_IN_PULLUP && + direction != IOEXPANDER_DIRECTION_IN_PULLDOWN && + direction != IOEXPANDER_DIRECTION_OUT && + direction != IOEXPANDER_DIRECTION_OUT_OPENDRAIN && + direction != IOEXPANDER_DIRECTION_OUT_LED) + { + return -EINVAL; + } + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = sx1509_setbit(priv, SX1509_REGLEDDRIVERENABLE_A, pin, + (direction == IOEXPANDER_DIRECTION_OUT_LED)); + if (ret < 0) + { + goto errout_unlock; + } + + ret = sx1509_setbit(priv, SX1509_REGDIR_A, pin, + (direction == IOEXPANDER_DIRECTION_IN) || + (direction == IOEXPANDER_DIRECTION_IN_PULLUP) || + (direction == IOEXPANDER_DIRECTION_IN_PULLDOWN)); + if (ret < 0) + { + goto errout_unlock; + } + + ret = sx1509_setbit(priv, SX1509_REGPULLUP_A, pin, + (direction == IOEXPANDER_DIRECTION_IN_PULLUP)); + if (ret < 0) + { + goto errout_unlock; + } + + ret = sx1509_setbit(priv, SX1509_REGPULLUP_A, pin, + (direction == IOEXPANDER_DIRECTION_IN_PULLDOWN)); + if (ret < 0) + { + goto errout_unlock; + } + + ret = sx1509_setbit(priv, SX1509_REGOPENDRAIN_A, pin, + (direction == IOEXPANDER_DIRECTION_OUT_OPENDRAIN)); + if (ret < 0) + { + goto errout_unlock; + } + +errout_unlock: + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: sx1509_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 sx1509_option(FAR struct ioexpander_dev_s *dev, uint8_t pin, + int opt, FAR void *value) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + int ret = -EINVAL; + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + if (opt == IOEXPANDER_OPTION_INVERT) + { + ret = sx1509_setbit(priv, SX1509_REGPOLARITY_A, pin, + ((uintptr_t)value == IOEXPANDER_VAL_INVERT)); + } +#ifdef CONFIG_SX1509_INT_ENABLE + else if (opt == IOEXPANDER_OPTION_INTCFG) + { + uint8_t addr = SX1509_REGSENSELOW_A - (pin / 4); + uint8_t shift = (pin % 4) * 2; + uint8_t buf[2]; + + buf[0] = addr; + ret = sx1509_writeread(priv, &buf[0], 1, &buf[1], 1); + if (ret < 0) + { + goto errout; + } + + buf[1] &= ~(SX1509_SENSE_MASK << shift); + + if ((uintptr_t)value & IOEXPANDER_VAL_DISABLE) + { + buf[1] |= SX1509_SENSE_NONE; + } + else if ((uintptr_t)value & IOEXPANDER_VAL_RISING) + { + buf[1] |= SX1509_SENSE_RISING; + } + else if ((uintptr_t)value & IOEXPANDER_VAL_FALLING) + { + buf[1] |= SX1509_SENSE_FALLING; + } + else if ((uintptr_t)value & IOEXPANDER_VAL_BOTH) + { + buf[1] |= SX1509_SENSE_BOTH; + } + + ret = sx1509_write(priv, buf, 2); +# ifdef CONFIG_SX1509_RETRY + if (ret != OK) + { + /* Try again (only once) */ + + ret = sx1509_write(priv, buf, 2); + } +# endif + } +#endif +#ifdef CONFIG_SX1509_LED_ENABLE + else if (opt == IOEXPANDER_OPTION_LEDCFG) + { + if ((intptr_t)value < (sizeof(userled_set_t) * 8)) + { + priv->pin2led[pin] = (intptr_t)value; + } + else + { + ret = -EINVAL; + } + } +#endif + +#ifdef CONFIG_SX1509_INT_ENABLE +errout: +#endif + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: sx1509_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 sx1509_writepin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + bool value) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + int ret; + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = sx1509_setbit(priv, SX1509_REGDATA_A, pin, value); + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: sx1509_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 sx1509_readpin(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + int ret; + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = sx1509_getbit(priv, SX1509_REGDATA_A, pin, value); + nxmutex_unlock(&priv->lock); + return ret; +} + +/**************************************************************************** + * Name: sx1509_readbuf + * + * Description: + * Read the buffered pin level. + * This can be different from the actual pin state. Required. + * + * Input Parameters: + * dev - Device-specific state data + * pin - The index of the pin + * valptr - Pointer to a buffer where the level is stored. + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +static int sx1509_readbuf(FAR struct ioexpander_dev_s *dev, uint8_t pin, + FAR bool *value) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + int ret; + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + ret = sx1509_getbit(priv, SX1509_REGDATA_A, pin, value); + nxmutex_unlock(&priv->lock); + return ret; +} + +#ifdef CONFIG_SX1509_INT_ENABLE +/**************************************************************************** + * Name: sx1509_attach + * + * Description: + * Attach and enable a pin interrupt callback function. + * + * Input Parameters: + * dev - Device-specific state data + * pinset - The set of pin events that will generate the callback + * callback - The pointer to callback function. NULL will detach the + * callback. + * arg - User-provided callback argument + * + * Returned Value: + * A non-NULL handle value is returned on success. This handle may be + * used later to detach and disable the pin interrupt. + * + ****************************************************************************/ + +static FAR void *sx1509_attach(FAR struct ioexpander_dev_s *dev, + ioe_pinset_t pinset, + ioe_callback_t callback, + FAR void *arg) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + FAR void *handle = NULL; + uint8_t addr = SX1509_REGINTERRUPTMASK_B; + uint8_t buf[3]; + int i; + int ret; + + /* Get exclusive access to the SX1509 */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return NULL; + } + + ret = sx1509_writeread(priv, &addr, 1, &buf[1], 2); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return NULL; + } + + buf[0] = addr; + buf[1] &= (uint8_t)(~(pinset & 0xff00)); + buf[2] &= (uint8_t)(~((pinset & 0x00ff) >> 8)); + + ret = sx1509_write(priv, buf, 3); + if (ret < 0) + { + nxmutex_unlock(&priv->lock); + return NULL; + } + + /* Find and available in entry in the callback table */ + + for (i = 0; i < CONFIG_SX1509_INT_NCALLBACKS; i++) + { + /* Is this entry available (i.e., no callback attached) */ + + if (priv->cb[i].cbfunc == NULL) + { + /* Yes.. use this entry */ + + priv->cb[i].pinset = pinset; + priv->cb[i].cbfunc = callback; + priv->cb[i].cbarg = arg; + handle = &priv->cb[i]; + break; + } + } + + /* Add this callback to the table */ + + nxmutex_unlock(&priv->lock); + return handle; +} + +/**************************************************************************** + * Name: sx1509_detach + * + * Description: + * Detach and disable a pin interrupt callback function. + * + * Input Parameters: + * dev - Device-specific state data + * handle - The non-NULL opaque value return by sx1509_attch() + * + * Returned Value: + * 0 on success, else a negative error code + * + ****************************************************************************/ + +static int sx1509_detach(FAR struct ioexpander_dev_s *dev, FAR void *handle) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)dev; + FAR struct sx1509_callback_s *cb = + (FAR struct sx1509_callback_s *)handle; + + DEBUGASSERT(priv != NULL && cb != NULL); + DEBUGASSERT((uintptr_t)cb >= (uintptr_t)&priv->cb[0] && + (uintptr_t)cb <= + (uintptr_t)&priv->cb[CONFIG_SX1509_INT_NCALLBACKS - 1]); + UNUSED(priv); + + cb->pinset = 0; + cb->cbfunc = NULL; + cb->cbarg = NULL; + return OK; +} + +/**************************************************************************** + * Name: sx1509_irqworker + * + * Description: + * Handle GPIO interrupt events (this function actually executes in the + * context of the worker thread). + * + ****************************************************************************/ + +static void sx1509_irqworker(void *arg) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)arg; + uint8_t addr = SX1509_REGINTERRUPTSOURCE_B; + uint8_t buf[2]; + ioe_pinset_t pinset; + int ret; + int i; + + /* Read interrupt flags */ + + ret = sx1509_writeread(priv, &addr, 1, buf, 2); + if (ret == OK) + { + /* Create a 16-bit pinset */ + + pinset = ((unsigned int)buf[0] << 8) | buf[1]; + + /* Perform pin interrupt callbacks */ + + for (i = 0; i < CONFIG_SX1509_INT_NCALLBACKS; i++) + { + /* Is this entry valid (i.e., callback attached)? If so, did + * any of the requested pin interrupts occur? + */ + + if (priv->cb[i].cbfunc != NULL) + { + /* Did any of the requested pin interrupts occur? */ + + ioe_pinset_t match = pinset & priv->cb[i].pinset; + if (match != 0) + { + /* Yes.. perform the callback */ + + priv->cb[i].cbfunc(&priv->dev, match, + priv->cb[i].cbarg); + } + } + } + + /* Read GPIOs to clear interrupt condition */ + + addr = SX1509_REGDATA_B; + sx1509_writeread(priv, &addr, 1, buf, 2); + } +} + +/**************************************************************************** + * Name: sx1509_interrupt + * + * Description: + * Handle GPIO interrupt events (this function executes in the + * context of the interrupt). + * + ****************************************************************************/ + +static int sx1509_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)arg; + + /* In complex environments, we cannot do I2C transfers from the interrupt + * handler because semaphores are probably used to lock the I2C bus. In + * this case, we will defer processing to the worker thread. This is also + * much kinder in the use of system resources and is, therefore, probably + * a good thing to do in any event. + */ + + /* Notice that further GPIO interrupts are disabled until the work is + * actually performed. This is to prevent overrun of the worker thread. + * Interrupts are re-enabled in sx1509_irqworker() when the work is + * completed. + */ + + if (work_available(&priv->work)) + { + work_queue(HPWORK, &priv->work, sx1509_irqworker, + (FAR void *)priv, 0); + } + + return OK; +} +#endif + +#ifdef CONFIG_SX1509_LED_ENABLE +/**************************************************************************** + * Name: sx1509_osc_config + * + * Description: + * Configure oscilator required for LED driver and keypad engine. + * + ****************************************************************************/ + +static int sx1509_osc_config(FAR struct sx1509_dev_s *priv) +{ + int ret = OK; + uint8_t buf[2]; + + if (priv->config->led_pre <= 0 || priv->config->led_pre > 7) + { + gpioerr("Invalid oscillator prescaler value %d\n", + priv->config->led_pre); + return ret; + } + + /* Configure oscilator source */ + + buf[0] = SX1509_REGCLOCK; + buf[1] = SX1509_OSC_INT; + + ret = sx1509_write(priv, buf, 2); + if (ret < 0) + { + return ret; + } + + /* Configure frequency of the LED driver clock */ + + buf[0] = SX1509_REGMISC; + buf[1] = priv->config->led_pre << SX1509_LEDCLK_SHIFT; + + ret = sx1509_write(priv, buf, 2); + if (ret == OK) + { + priv->led_freq = (SX1509_INTOSC_FREQ / + (2 << (priv->config->led_pre - 1))); + + priv->t_on_1_ms = 64 * 15 * 255 * 1000 / priv->led_freq; + priv->t_on_2_ms = 512 * 15 * 255 * 1000 / priv->led_freq; + + priv->t_off_1_ms = 64 * 15 * 255 * 1000 / priv->led_freq; + priv->t_off_2_ms = 512 * 15 * 255 * 1000 / priv->led_freq; + } + + return ret; +} + +/**************************************************************************** + * Name: sx1509_led2pin + * + * Description: + * Return the pin number assigned to a given LED + * + ****************************************************************************/ + +static int sx1509_led2pin(FAR struct sx1509_dev_s *priv, uint8_t led) +{ + int pin = -1; + int i = 0; + + for (i = 0; i < SX1509_NR_GPIO_MAX; i++) + { + if (priv->pin2led[i] == led) + { + pin = i; + break; + } + } + + return pin; +} + +/**************************************************************************** + * Name: sx1509_ll_supported + * + * Description: + * Return the set of LEDs supported by the device + * + ****************************************************************************/ + +static userled_set_t +sx1509_ll_supported(FAR const struct userled_lowerhalf_s *lower) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + userled_set_t ledset = 0; + int i = 0; + + for (i = 0; i < SX1509_NR_GPIO_MAX; i++) + { + if (priv->pin2led[i] >= 0) + { + ledset |= 1 << priv->pin2led[i]; + } + } + + return ledset; +} + +/**************************************************************************** + * Name: sx1509_ll_setled + * + * Description: + * Set the current state of one LED + * + ****************************************************************************/ + +static void sx1509_ll_setled(FAR const struct userled_lowerhalf_s *lower, + int led, bool ledon) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + int pin; + + /* Find corresponding pin */ + + pin = sx1509_led2pin(priv, led); + + if (pin != -1) + { + sx1509_writepin((FAR struct ioexpander_dev_s *)priv, pin, ledon); + } +} + +/**************************************************************************** + * Name: sx1509_ll_setall + * + * Description: + * Set the state of all LEDs + * + ****************************************************************************/ + +static void sx1509_ll_setall(FAR const struct userled_lowerhalf_s *lower, + userled_set_t ledset) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + int pin = 0; + + for (pin = 0; pin < SX1509_NR_GPIO_MAX; pin++) + { + if ((priv->pin2led[pin] >= 0)) + { + sx1509_ll_setled(lower, priv->pin2led[pin], + ledset & (1 << priv->pin2led[pin])); + } + } +} + +# ifdef CONFIG_USERLED_LOWER_READSTATE +/**************************************************************************** + * Name: sx1509_ll_getall + * + * Description: + * Get the state of all LEDs + * + ****************************************************************************/ + +static void sx1509_ll_getall(FAR const struct userled_lowerhalf_s *lower, + userled_set_t *ledset) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + int val = 0; + + for (pin = 0; pin < SX1509_NR_GPIO_MAX; pin++) + { + if ((priv->pin2led[pin] >= 0)) + { + ret = sx1509_readpin((FAR struct ioexpander_dev_s *)priv, + pin, &val); + if (ret < 0) + { + break; + } + + *ledset |= (val << pin); + } + } +} +# endif + +# ifdef CONFIG_USERLED_EFFECTS +/**************************************************************************** + * Name: sx1509_ll_effect_sup + * + * Description: + * Get supported effects for a given LED pin. + * + ****************************************************************************/ + +static void sx1509_ll_effect_sup(FAR const struct userled_lowerhalf_s *lower, + FAR struct userled_effect_sup_s *sup) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + int pin; + + /* Find corresponding pin */ + + pin = sx1509_led2pin(priv, sup->led); + + if (pin == -1) + { + /* Pin not found */ + + sup->int_on = false; + sup->int_off = false; + sup->t_on = false; + sup->t_off = false; + sup->t_fade = false; + sup->t_fall = false; + } + else + { + sup->int_on = true; + sup->int_off = true; + sup->t_on = true; + sup->t_off = true; + + if (g_sx1509_led_regs[pin][SX1509_LEDREGS_RISE] != 0) + { + sup->t_fade = true; + sup->t_fall = true; + } + } +} + +/**************************************************************************** + * Name: sx1509_ll_effect_set + * + * Description: + * Set effects for a given LED pin. + * + ****************************************************************************/ + +static int sx1509_ll_effect_set(FAR const struct userled_lowerhalf_s *lower, + FAR struct userled_effect_set_s *set) +{ + FAR struct sx1509_dev_s *priv = + container_of(lower, struct sx1509_dev_s, userleds); + uint8_t buf[2]; + uint8_t tmp; + int pin; + + pin = sx1509_led2pin(priv, set->led); + + /* ON intensity [0% - 100%] */ + + if (set->int_on > 100) + { + gpioerr("int_on value out of supported range %" PRId8 "\n", + set->int_on); + return -EINVAL; + } + else + { + buf[0] = g_sx1509_led_regs[pin][SX1509_LEDREGS_ION]; + buf[1] = 255 * set->int_on / 100; + + sx1509_write(priv, buf, 2); + } + + /* OFF intensity - not supported for now */ + + if (set->int_off > 0) + { + gpioerr("int_off value out of supported range %" PRId8 "\n", + set->int_off); + return -EINVAL; + } + + /* ON time [ms] */ + + if (set->t_on <= priv->t_on_1_ms) + { + tmp = (SX1509_TON_1_MAX * set->t_on / priv->t_on_1_ms); + if (tmp == 0) + { + tmp += 1; + } + } + + else if (set->t_on <= priv->t_on_2_ms) + { + tmp = ((SX1509_TON_2_MAX * set->t_on / priv->t_on_2_ms) + + SX1509_TON_1_MAX); + if (tmp == SX1509_TON_1_MAX) + { + tmp += 1; + } + } + + else + { + gpioerr("t_on value out of supported range %" PRId32 "\n", + set->t_on); + return -EINVAL; + } + + buf[0] = g_sx1509_led_regs[pin][SX1509_LEDREGS_TON]; + buf[1] = (tmp << SX1509_TON_SHIFT); + sx1509_write(priv, buf, 2); + + /* OFF time [ms] */ + + if (set->t_off <= priv->t_off_1_ms) + { + tmp = (SX1509_TOFF_1_MAX * set->t_off / priv->t_off_1_ms); + if (tmp == 0) + { + tmp += 1; + } + } + + else if (set->t_off <= priv->t_off_2_ms) + { + tmp = ((SX1509_TOFF_2_MAX * set->t_off / priv->t_off_2_ms) + + SX1509_TOFF_1_MAX); + if (tmp == SX1509_TOFF_1_MAX) + { + tmp += 1; + } + } + + else + { + gpioerr("t_off value out of supported range %" PRId32 "\n", + set->t_off); + return -EINVAL; + } + + buf[0] = g_sx1509_led_regs[pin][SX1509_LEDREGS_OFF]; + buf[1] = (tmp << SX1509_TOFF_SHIFT); + sx1509_write(priv, buf, 2); + + /* Fade in */ + + if (g_sx1509_led_regs[pin][SX1509_LEDREGS_RISE] > 0) + { + buf[0] = g_sx1509_led_regs[pin][SX1509_LEDREGS_RISE]; + buf[1] = set->t_fade; + sx1509_write(priv, buf, 2); + } + + /* Fade out */ + + if (g_sx1509_led_regs[pin][SX1509_LEDREGS_FALL] > 0) + { + buf[0] = g_sx1509_led_regs[pin][SX1509_LEDREGS_FALL]; + buf[1] = set->t_fall; + sx1509_write(priv, buf, 2); + } + + /* Force ON */ + + sx1509_writepin((FAR struct ioexpander_dev_s *)priv, pin, true); + + return OK; +} +# endif +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sx1509_initialize + * + * Description: + * Initialize a SX1509 I2C device. + * + * Input Parameters: + * i2c - An I2C driver instance + * config - Persistent board configuration data + * + ****************************************************************************/ + +FAR struct ioexpander_dev_s * +sx1509_initialize(FAR struct i2c_master_s *i2cdev, + FAR struct sx1509_config_s *config) +{ + FAR struct sx1509_dev_s *priv = NULL; + + DEBUGASSERT(i2cdev != NULL); + DEBUGASSERT(config != NULL); + DEBUGASSERT(config->set_nreset != NULL); + + /* Reset device */ + + config->set_nreset(false); + up_mdelay(20); + config->set_nreset(true); + up_mdelay(20); + +#ifdef CONFIG_SX1509_MULTIPLE + /* Allocate the device state structure */ + + priv = (FAR struct sx1509_dev_s *) + kmm_zalloc(sizeof(struct sx1509_dev_s)); + if (!priv) + { + return NULL; + } + + /* And save the device structure in the list of SX1509 so that we can + * find it later. + */ + + priv->flink = g_sx1509list; + g_sx1509list = priv; +#else + /* Use the global SX1509 driver instance */ + + priv = &g_sx1509; +#endif + + /* Initialize the device state structure */ + + priv->i2c = i2cdev; + priv->dev.ops = &g_sx1509_ops; + priv->config = config; + +#ifdef CONFIG_SX1509_INT_ENABLE + priv->config->attach(priv->config, sx1509_interrupt, priv); +#endif + +#ifdef CONFIG_SX1509_LED_ENABLE + /* Reset pin to LED mapping */ + + memset(priv->pin2led, -1, SX1509_NR_GPIO_MAX); +#endif + + nxmutex_init(&priv->lock); + + return &priv->dev; +} + +#ifdef CONFIG_SX1509_LED_ENABLE +/**************************************************************************** + * Name: sx1509_leds_initialize + * + * Description: + * Initialize the LED driver for a given SX1509 device. + * + ****************************************************************************/ + +int sx1509_leds_initialize(FAR struct ioexpander_dev_s *ioe, + FAR const char *devname) +{ + FAR struct sx1509_dev_s *priv = (FAR struct sx1509_dev_s *)ioe; + int ret; + + /* Configure oscilator */ + + ret = sx1509_osc_config(priv); + if (ret < 0) + { + return ret; + } + + /* Connect LED lower-half */ + + priv->userleds.ll_supported = sx1509_ll_supported; + priv->userleds.ll_setled = sx1509_ll_setled; + priv->userleds.ll_setall = sx1509_ll_setall; +#ifdef CONFIG_USERLED_LOWER_READSTATE + priv->userleds.ll_getall = sx1509_ll_getall; +#endif +#ifdef CONFIG_USERLED_EFFECTS + priv->userleds.ll_effect_sup = sx1509_ll_effect_sup; + priv->userleds.ll_effect_set = sx1509_ll_effect_set; +#endif + + return userled_register(devname, &priv->userleds); +} +#endif diff --git a/drivers/ioexpander/sx1509.h b/drivers/ioexpander/sx1509.h new file mode 100644 index 0000000000..8cc7ad39f5 --- /dev/null +++ b/drivers/ioexpander/sx1509.h @@ -0,0 +1,293 @@ +/**************************************************************************** + * drivers/ioexpander/sx1509.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_SX1509_H +#define __DRIVERS_IOEXPANDER_SX1509_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SX1509_LED_ENABLE +# include +#endif + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SX1509 Definitions *******************************************************/ + +/* I2C frequency */ + +#define SX1509_I2C_MAXFREQUENCY 400000 /* 400KHz */ + +/* SX1509 *******************************************************************/ + +/* Device and IO Banks */ + +#define SX1509_REGINPUTDISABLE_B 0x00 +#define SX1509_REGINPUTDISABLE_A 0x01 +#define SX1509_REGLONGSLEW_B 0x02 +#define SX1509_REGLONGSLEW_A 0x03 +#define SX1509_REGLOWDRIVE_B 0x04 +#define SX1509_REGLOWDRIVE_A 0x05 +#define SX1509_REGPULLUP_B 0x06 +#define SX1509_REGPULLUP_A 0x07 +#define SX1509_REGPULLDOWN_B 0x08 +#define SX1509_REGPULLDOWN_A 0x09 +#define SX1509_REGOPENDRAIN_B 0x0a +#define SX1509_REGOPENDRAIN_A 0x0b +#define SX1509_REGPOLARITY_B 0x0c +#define SX1509_REGPOLARITY_A 0x0d +#define SX1509_REGDIR_B 0x0e +#define SX1509_REGDIR_A 0x0f +#define SX1509_REGDATA_B 0x10 +#define SX1509_REGDATA_A 0x11 +#define SX1509_REGINTERRUPTMASK_B 0x12 +#define SX1509_REGINTERRUPTMASK_A 0x13 +#define SX1509_REGSENSEHIGH_B 0x14 +#define SX1509_REGSENSELOW_B 0x15 +#define SX1509_REGSENSEHIGH_A 0x16 +#define SX1509_REGSENSELOW_A 0x17 +#define SX1509_REGINTERRUPTSOURCE_B 0x18 +#define SX1509_REGINTERRUPTSOURCE_A 0x19 +#define SX1509_REGEVENTSTATUS_B 0x1a +#define SX1509_REGEVENTSTATUS_A 0x1b +#define SX1509_REGLEVELSHIFTER_1 0x1c +#define SX1509_REGLEVELSHIFTER_2 0x1d +#define SX1509_REGCLOCK 0x1e +#define SX1509_REGMISC 0x1f +#define SX1509_REGLEDDRIVERENABLE_B 0x20 +#define SX1509_REGLEDDRIVERENABLE_A 0x21 + +/* Debounce and Keypad Engine */ + +#define SX1509_REGDEBOUNCE_CONFIG 0x22 +#define SX1509_REGDEBOUNCEENABLE_B 0x23 +#define SX1509_REGDEBOUNCEENABLE_A 0x24 +#define SX1509_REGKEYCONFIG_1 0x25 +#define SX1509_REGKEYCONFIG_2 0x26 +#define SX1509_REGKEYDATA_1 0x27 +#define SX1509_REGKEYDATA_2 0x28 + +/* LED Driver (PWM, blinking, breathing) */ + +#define SX1509_REGTON_0 0x29 +#define SX1509_REGION_0 0x2a +#define SX1509_REGOFF_0 0x2b +#define SX1509_REGTON_1 0x2c +#define SX1509_REGION_1 0x2d +#define SX1509_REGOFF_1 0x2e +#define SX1509_REGTON_2 0x2f +#define SX1509_REGION_2 0x30 +#define SX1509_REGOFF_2 0x31 +#define SX1509_REGTON_3 0x32 +#define SX1509_REGION_3 0x33 +#define SX1509_REGOFF_3 0x34 +#define SX1509_REGTON_4 0x35 +#define SX1509_REGION_4 0x36 +#define SX1509_REGOFF_4 0x37 +#define SX1509_TRISE_4 0x38 +#define SX1509_TFALL_4 0x39 +#define SX1509_REGTON_5 0x3a +#define SX1509_REGION_5 0x3b +#define SX1509_REGOFF_5 0x3c +#define SX1509_TRISE_5 0x3d +#define SX1509_TFALL_5 0x3e +#define SX1509_REGTON_6 0x3f +#define SX1509_REGION_6 0x40 +#define SX1509_REGOFF_6 0x41 +#define SX1509_TRISE_6 0x42 +#define SX1509_TFALL_6 0x43 +#define SX1509_REGTON_7 0x44 +#define SX1509_REGION_7 0x45 +#define SX1509_REGOFF_7 0x46 +#define SX1509_TRISE_7 0x47 +#define SX1509_TFALL_7 0x48 +#define SX1509_REGTON_8 0x49 +#define SX1509_REGION_8 0x4a +#define SX1509_REGOFF_8 0x4b +#define SX1509_REGTON_9 0x4c +#define SX1509_REGION_9 0x4d +#define SX1509_REGOFF_9 0x4e +#define SX1509_REGTON_10 0x4f +#define SX1509_REGION_10 0x50 +#define SX1509_REGOFF_10 0x51 +#define SX1509_REGTON_11 0x52 +#define SX1509_REGION_11 0x53 +#define SX1509_REGOFF_11 0x54 +#define SX1509_REGTON_12 0x55 +#define SX1509_REGION_12 0x56 +#define SX1509_REGOFF_12 0x57 +#define SX1509_TRISE_12 0x58 +#define SX1509_TFALL_12 0x59 +#define SX1509_REGTON_13 0x5a +#define SX1509_REGION_13 0x5b +#define SX1509_REGOFF_13 0x5c +#define SX1509_TRISE_13 0x5d +#define SX1509_TFALL_13 0x5e +#define SX1509_REGTON_14 0x5f +#define SX1509_REGION_14 0x60 +#define SX1509_REGOFF_14 0x61 +#define SX1509_TRISE_14 0x62 +#define SX1509_TFALL_14 0x63 +#define SX1509_REGTON_15 0x64 +#define SX1509_REGION_15 0x65 +#define SX1509_REGOFF_15 0x66 +#define SX1509_TRISE_15 0x67 +#define SX1509_TFALL_15 0x68 + +/* Miscellaneous */ + +#define SX1509_REGHIGHINPUT_B 0x69 +#define SX1509_REGHIGHINPUT_A 0x6a + +/* Software Reset */ + +#define SX1509_REGRESET 0x7d + +/* Test */ + +#define SX1509_REGTEST1 0x7e +#define SX1509_REGTEST2 0x7f + +/* RegSense */ + +#define SX1509_SENSE_MASK (0x3) +#define SX1509_SENSE_NONE (0) +#define SX1509_SENSE_RISING (1) +#define SX1509_SENSE_FALLING (2) +#define SX1509_SENSE_BOTH (3) + +/* RegClock */ + +#define SX1509_OSC_OFF (0 << 5) +#define SX1509_OSC_EXT (1 << 5) +#define SX1509_OSC_INT (2 << 5) + +/* RegMisc */ + +#define SX1509_LEDCLK_SHIFT (4) + +/* RegTOnX */ + +#define SX1509_TON_SHIFT (0) +#define SX1509_TON_1_MAX (15) +#define SX1509_TON_2_MAX (15) + +/* RegOffX */ + +#define SX1509_IOFF_SHIFT (0) +#define SX1509_TOFF_SHIFT (3) +#define SX1509_TOFF_1_MAX (15) +#define SX1509_TOFF_2_MAX (15) + +/* Driver definitions */ + +#define SX1509_NR_GPIO_MAX 16 +#define SX1509_POLLDELAY (CONFIG_SX1509_INT_POLLDELAY / USEC_PER_TICK) +#define SX1509_INTOSC_FREQ (2000000) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifdef CONFIG_IOEXPANDER_INT_ENABLE +/* This type represents on registered pin interrupt callback */ + +struct sx1509_callback_s +{ + ioe_pinset_t pinset; /* Set of pin interrupts that will generate + * the callback. */ + ioe_callback_t cbfunc; /* The saved callback function pointer */ + FAR void *cbarg; /* Callback argument */ +}; +#endif + +/* This structure represents the state of the SX1509 driver */ + +struct sx1509_dev_s +{ + struct ioexpander_dev_s dev; /* Nested structure to allow casting + * as public gpio expander. + */ +#ifdef CONFIG_SX1509_MULTIPLE + /* Supports a singly linked list of drivers */ + + FAR struct sx1509_dev_s *flink; +#endif + + FAR struct sx1509_config_s *config; /* Board configuration data */ + FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */ + mutex_t lock; /* Mutual exclusion */ + +#ifdef CONFIG_IOEXPANDER_INT_ENABLE +# ifdef CONFIG_SX1509_INT_POLL + + /* Timer used to poll for missed interrupts */ + + struct wdog_s wdog; +# endif + + /* Supports the interrupt handling "bottom half" */ + + struct work_s work; + + /* Saved callback information for each I/O expander client */ + + struct sx1509_callback_s cb[CONFIG_SX1509_INT_NCALLBACKS]; +#endif + +#ifdef CONFIG_SX1509_LED_ENABLE + /* Lower half LED driver */ + + struct userled_lowerhalf_s userleds; + + /* Pin number to LED number map. If set to -1 then no LED assigned. */ + + int8_t pin2led[SX1509_NR_GPIO_MAX]; + + /* LED driver clock frequency */ + + uint32_t led_freq; + + /* LED driver timings */ + + uint32_t t_on_1_ms; + uint32_t t_on_2_ms; + + uint32_t t_off_1_ms; + uint32_t t_off_2_ms; +#endif +}; + +#endif /* __DRIVERS_IOEXPANDER_SX1509_H */ diff --git a/include/nuttx/ioexpander/ioexpander.h b/include/nuttx/ioexpander/ioexpander.h index e823eb625b..f0c44e96be 100644 --- a/include/nuttx/ioexpander/ioexpander.h +++ b/include/nuttx/ioexpander/ioexpander.h @@ -48,6 +48,7 @@ #define IOEXPANDER_DIRECTION_IN_PULLDOWN 2 #define IOEXPANDER_DIRECTION_OUT 3 /* push-pull */ #define IOEXPANDER_DIRECTION_OUT_OPENDRAIN 4 +#define IOEXPANDER_DIRECTION_OUT_LED 5 /* LED output */ #define IOEXPANDER_PINMASK (((ioe_pinset_t)1 << CONFIG_IOEXPANDER_NPINS) - 1) #define PINSET_ALL (~((ioe_pinset_t)0)) @@ -68,6 +69,8 @@ # define IOEXPANDER_VAL_FALLING 10 /* 1010 Interrupt on falling edge */ # define IOEXPANDER_VAL_BOTH 14 /* 1110 Interrupt on both edges */ +#define IOEXPANDER_OPTION_LEDCFG 3 /* Assign LED number to a pin */ + /* Access macros ************************************************************/ /**************************************************************************** diff --git a/include/nuttx/ioexpander/sx1509.h b/include/nuttx/ioexpander/sx1509.h new file mode 100644 index 0000000000..a61ce97ede --- /dev/null +++ b/include/nuttx/ioexpander/sx1509.h @@ -0,0 +1,114 @@ +/**************************************************************************** + * include/nuttx/ioexpander/sx1509.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_SX1509_H +#define __INCLUDE_NUTTX_IOEXPANDER_SX1509_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the sx1509 + * driver when the driver is instantiated. This structure provides + * information about the configuration of the sx1509 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 sx1509_config_s +{ + /* Device characterization */ + + uint8_t address; /* 7-bit I2C address (only bits 0-6 used) */ + uint32_t frequency; /* I2C or SPI frequency */ + +#ifdef CONFIG_SX1509_LED_ENABLE + uint8_t led_pre; /* LED driver frequency prescaler */ +#endif + + /* Sets the state of nReset pin */ + + CODE void (*set_nreset)(bool state); + +#ifdef CONFIG_IOEXPANDER_INT_ENABLE + CODE int (*attach)(FAR struct sx1509_config_s *state, xcpt_t isr, + FAR void *arg); +#endif +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: sx1509_initialize + * + * Description: + * Initialize a SX1509 I2C device. + * + * Input Parameters: + * i2c - An I2C driver instance + * config - Persistent board configuration data + * + ****************************************************************************/ + +FAR struct ioexpander_dev_s *sx1509_initialize( + FAR struct i2c_master_s *i2cdev, + FAR struct sx1509_config_s *config); + +#ifdef CONFIG_SX1509_LED_ENABLE +/**************************************************************************** + * Name: sx1509_leds_initialize + * + * Description: + * Initialize the LED driver for a given SX1509 device. + * + ****************************************************************************/ + +int sx1509_leds_initialize(FAR struct ioexpander_dev_s *ioe, + FAR const char *devname); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_IOEXPANDER_SX1509_H */