From 4d78593a0ff3f7dfcaf0eabaf1169d7e1a048675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Giorgio=20Gro=C3=9F?= Date: Mon, 5 Mar 2018 16:11:05 +0000 Subject: [PATCH] Merged in ordsen/nuttx/feature-pca9540bdp-i2cmultiplexer (pull request #610) Feature pca9540bdp i2cmultiplexer * Implement common i2c multiplexer abstraction to be reused by i2c multiplexer devices. Provide common interface with transfer and reset calls to set up virtual i2c busses. * Remove i2cmux_lower_half.c and headers, will be implemented by each mux driver * Implement PCA9540BDP I2C multiplexer lower half which uses the common i2c mux abstraction * Remove application interface and implement common i2c functions * Remove application interface from pca driver and implement common i2c functions directly in the pca driver * Use i2cerr Approved-by: Gregory Nutt --- drivers/i2c/Kconfig | 13 ++ drivers/i2c/Make.defs | 13 +- drivers/i2c/pca9540bdp.c | 295 +++++++++++++++++++++++++++++++++ drivers/i2c/pca9540bdp.h | 89 ++++++++++ include/nuttx/i2c/pca9540bdp.h | 124 ++++++++++++++ 5 files changed, 531 insertions(+), 3 deletions(-) create mode 100644 drivers/i2c/pca9540bdp.c create mode 100644 drivers/i2c/pca9540bdp.h create mode 100644 include/nuttx/i2c/pca9540bdp.h diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b5a7399f2c0..cdfd124ce61 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -36,4 +36,17 @@ config I2C_DRIVER this driver is to support I2C testing. It is not suitable for use in any real driver application. +menu "I2C Multiplexer Support" + +config I2CMULTIPLEXER_PCA9540BDP + bool "PCA9540BDP NXP multiplexer" + default n + depends on I2C + ---help--- + Enable support for the NXP PCA9540BDP i2c multiplexer + +# put more i2c mux devices here + +endmenu # I2C Multiplexer Support + endif # I2C diff --git a/drivers/i2c/Make.defs b/drivers/i2c/Make.defs index d7d708bdf23..5bdf115c61c 100644 --- a/drivers/i2c/Make.defs +++ b/drivers/i2c/Make.defs @@ -1,8 +1,9 @@ ############################################################################ # drivers/i2c/Make.defs # -# Copyright (C) 2016 Gregory Nutt. All rights reserved. -# Author: Gregory Nutt +# Copyright (C) 2016, 2018 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt , +# Giorgio Gross # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -43,10 +44,16 @@ ifeq ($(CONFIG_I2C_DRIVER),y) CSRCS += i2c_driver.c endif +# Include the selected I2C multiplexer drivers + +ifeq ($(CONFIG_I2CMULTIPLEXER_PCA9540BDP),y) +CSRCS += pca9540bdp.c +endif + # Include I2C device driver build support DEPPATH += --dep-path i2c VPATH += :i2c CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)drivers$(DELIM)i2c} -endif +endif # CONFIG_I2C diff --git a/drivers/i2c/pca9540bdp.c b/drivers/i2c/pca9540bdp.c new file mode 100644 index 00000000000..d799c3fd2b8 --- /dev/null +++ b/drivers/i2c/pca9540bdp.c @@ -0,0 +1,295 @@ +/**************************************************************************** + * drivers/i2c/pca9540bdp.c + * Driver for the PCA9540BDP i2c multiplexer + * + * Copyright (C) 2018 Giorgio Groß. All rights reserved. + * Author: Giorgio Groß + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "pca9540bdp.h" +#include + +#ifdef CONFIG_I2CMULTIPLEXER_PCA9540BDP + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* I2C Helpers */ + +static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv, + FAR uint8_t regvalue); +static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv, + FAR uint8_t *regvalue); + +/* Other helpers */ + +static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv, uint8_t val); + +/* I2C multiplexer vtable */ + +static int pca9540bdp_transfer_on_port (FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); +#ifdef CONFIG_I2C_RESET +static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ +static const struct i2c_ops_s g_i2cmux_ops = +{ + pca9540bdp_transfer_on_port +#ifdef CONFIG_I2C_RESET + , pca9540bdp_reset_on_port +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int pca9540bdp_write_config(FAR struct pca9540bdp_dev_s *priv, + FAR uint8_t regvalue) +{ + struct i2c_config_s iconf; + iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY; + iconf.address = priv->addr; + iconf.addrlen = 7; + int ret = i2c_write(priv->i2c, &iconf, ®value, 1); + + i2cinfo("Write to address 0x%02X; register value: 0x%02x ret: %d\n", priv->addr, regvalue, ret); + return ret; +} + +static int pca9540bdp_read_config(FAR struct pca9540bdp_dev_s *priv, + FAR uint8_t *regvalue) +{ + struct i2c_config_s iconf; + iconf.frequency = CONFIG_PCA9549BDP_I2C_FREQUENCY; + iconf.address = priv->addr; + iconf.addrlen = 7; + int ret = i2c_read(priv->i2c, &iconf, regvalue, 1); + + i2cinfo("Read from address: 0x%02X; register value: 0x%02x ret: %d\n", priv->addr, *regvalue, ret); + return ret; +} + +static int pca9540bdp_select_port(FAR struct pca9540bdp_dev_s *priv, uint8_t val) +{ + if(val != PCA9540BDP_SEL_PORT0 && val != PCA9540BDP_SEL_PORT1) + { + + /* port not supported */ + + return -EINVAL; + } + + if((PCA9540BDP_ENABLE | val) == priv->state) + { + + /* port already selected */ + + return OK; + } + + /* modify state and write it to the mux */ + + priv->state = PCA9540BDP_ENABLE | val; /* selecting a port always enables the device */ + return (pca9540bdp_write_config(priv, priv->state) == 0) ? OK : -ECOMM; +} + +static int pca9540bdp_transfer_on_port (FAR struct i2c_master_s* dev, + FAR struct i2c_msg_s *msgs, int count) +{ + FAR struct i2c_port_dev_s* port_dev = (struct i2c_port_dev_s*) dev; + FAR struct pca9540bdp_dev_s* priv = port_dev->dev; + + /* select the mux port */ + + if(pca9540bdp_select_port(priv, port_dev->port) != OK) + { + i2cerr("Could not select proper mux port\n"); + return -ECOMM; /* signal error condition */ + } + + /* resume the i2c transfer to the device connected to the mux port */ + int ret = I2C_TRANSFER(priv->i2c, msgs, count); + i2cinfo("Selected port %d and resumed transfer. Result: %d\n", port_dev->port, ret); + return ret; +} + +#ifdef CONFIG_I2C_RESET +static int pca9540bdp_reset_on_port (FAR struct i2c_master_s *dev) +{ + FAR struct i2c_port_dev_s* port_dev = (struct i2c_port_dev_s*) dev; + FAR struct pca9540bdp_dev_s* priv = port_dev->dev; + int port = port_dev->port; + + /* select the mux port */ + + if(pca9540bdp_select_port(priv, port) != OK) + { + i2cerr("Could not select proper mux port\n"); + return -ECOMM; /* signal error condition */ + } + + /* resume the i2c reset for the device connected to the mux port */ + + int ret = I2C_RESET(priv->i2c); + return ret; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pca9540bdp_lower_half + * + * Description: + * Initialize the lower half of the PCA9540BDP by creating a i2c_master_s + * for the virtual i2c master and link it to the associated PCA9540BDP and + * its port. + * + * Input Parameters: + * dev - Pointer to the associated PCA9540BDP + * port - The port number as defined in pca9540bdp.h + * + * Returned Value: + * Common i2c multiplexer device instance; NULL on failure. + * + ****************************************************************************/ + +FAR struct i2c_master_s* pca9540bdp_lower_half(FAR struct pca9540bdp_dev_s *dev, + uint8_t port) +{ + FAR struct i2c_port_dev_s *port_dev; + + /* Sanity check */ + + DEBUGASSERT(dev != NULL); + + /* Initialize the i2c_port_dev_s device structure */ + + port_dev = (FAR struct i2c_port_dev_s *)kmm_malloc(sizeof(struct i2c_port_dev_s)); + if (port_dev == NULL) + { + i2cerr("ERROR: Failed to allocate i2c port lower half instance\n"); + return NULL; + } + + /* set vi2c ops to their implementations */ + + port_dev->vi2c.ops = &g_i2cmux_ops; + + /* save state variables */ + + port_dev->dev = dev; + port_dev->port = port; + + return &port_dev->vi2c; +} + +/**************************************************************************** + * Name: pca9540bdp_initialize + * + * Description: + * Initialize the PCA9540BDP device. + * + * Input Parameters: + * i2c - An instance of the I2C interface to use to communicate with PCA9540BDP + * addr - The I2C address of the PCA9540BDP. The base I2C address of the PCA9540BDP + * is 0x70. + * + * Returned Value: + * Common i2c multiplexer device instance; NULL on failure. + * + ****************************************************************************/ + +FAR struct pca9540bdp_dev_s* pca9540bdp_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr) +{ + FAR struct pca9540bdp_dev_s *priv; + + /* Sanity check */ + + DEBUGASSERT(i2c != NULL); + + /* Initialize the PCA9549BDP device structure */ + + priv = (FAR struct pca9540bdp_dev_s *)kmm_malloc(sizeof(struct pca9540bdp_dev_s)); + if (priv == NULL) + { + i2cerr("ERROR: Failed to allocate instance\n"); + return NULL; + } + + priv->i2c = i2c; + priv->addr = addr; + priv->state = 0x00; + + if(pca9540bdp_read_config(priv, &priv->state) != OK) + { + i2cerr("Could not read initial state from the device\n"); + kmm_free(priv); + return NULL; /* signal error condition */ + } + i2cinfo("Initial device state: %d\n", priv->state); + + if(pca9540bdp_select_port(priv, PCA9540BDP_SEL_PORT0) != OK) + { + i2cerr("Could not select mux port 0\n"); + kmm_free(priv); + return NULL; /* signal error condition */ + } + + i2cinfo("PCA9549BDP (addr=0x%02x) set up with port %d\n", priv->addr, PCA9540BDP_SEL_PORT0); + + return priv; +} + + +#endif /* CONFIG_I2CMULTIPLEXER_PCA9540BDP */ diff --git a/drivers/i2c/pca9540bdp.h b/drivers/i2c/pca9540bdp.h new file mode 100644 index 00000000000..7a96faa38ad --- /dev/null +++ b/drivers/i2c/pca9540bdp.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * /nuttx/drivers/i2c/pca9540bdp.h + * + * Copyright (C) 2018 Giorgio Groß. All rights reserved. + * Author: Giorgio Groß + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +#ifndef __DRIVERS_I2C_PCA9540BDP_H +#define __DRIVERS_I2C_PCA9540BDP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifdef CONFIG_I2CMULTIPLEXER_PCA9540BDP + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_PCA9549BDP_I2C_FREQUENCY +# define CONFIG_PCA9549BDP_I2C_FREQUENCY 400000 /* 400 khz */ +#endif + +/* Bit Definitions to be passed to write calls */ + +#define PCA9540BDP_ENABLE 0x4 +#define PCA9540BDP_DISABLE 0x0 + +/* Bit masks */ + +#define PCA9540BDP_CH_BITMASK 0x03 +#define PCA9540BDP_ENABLE_BITMASK 0x04 + +#define PCA9540BDP_CH_BIT 0 +#define PCA9540BDP_CH_NONE_BIT 1 +#define PCA9540BDP_ENABLE_BIT 2 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct pca9540bdp_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint16_t addr; + uint8_t state; /* control register state */ +}; + +struct i2c_port_dev_s +{ + FAR struct i2c_master_s vi2c; /* Nested structure to allow casting as + public i2c master */ + uint8_t port; /* associated port on the mux */ + FAR struct pca9540bdp_dev_s* dev; /* associated device */ +}; + +#endif /* CONFIG_I2CMULTIPLEXER_PCA9540BDP */ +#endif /* __DRIVERS_I2CMULTIPLEXER_PCA9540BDP_H */ diff --git a/include/nuttx/i2c/pca9540bdp.h b/include/nuttx/i2c/pca9540bdp.h new file mode 100644 index 00000000000..e1cb0567541 --- /dev/null +++ b/include/nuttx/i2c/pca9540bdp.h @@ -0,0 +1,124 @@ +/**************************************************************************** + * include/nuttx/i2c/pca9540bdp.h + * + * Copyright (C) 2018 Giorgio Groß. All rights reserved. + * Author: Giorgio Groß + * + * References: + * "PCA9540B 2-channel I2C-bus multiplexer product datasheet", + * 31 October 2016, NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_I2C_PCA9540BDP_H +#define __INCLUDE_I2C_PCA9540BDP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +#ifndef CONFIG_PCA9540BDP_BASEADDR +# define CONFIG_PCA9540BDP_BASEADDR 0x70 +#endif + +#define PCA9540BDP_SEL_PORT0 0x0 +#define PCA9540BDP_SEL_PORT1 0x1 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct pca9540bdp_dev_s; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + + +/**************************************************************************** + * Name: pca9540bdp_lower_half + * + * Description: + * Initialize the lower half of the PCA9540BDP by creating a i2c_master_s + * for the virtual i2c master and link it to the associated PCA9540BDP and + * its port. + * + * Input Parameters: + * dev - Pointer to the associated PCA9540BDP + * port - The port number as defined in pca9540bdp.h + * + * Returned Value: + * Common i2c multiplexer device instance; NULL on failure. + * + ****************************************************************************/ + +FAR struct i2c_master_s* pca9540bdp_lower_half(FAR struct pca9540bdp_dev_s *dev, + uint8_t port); + +/**************************************************************************** + * Name: pca9540bdp_initialize + * + * Description: + * Initialize the PCA9540BDP device. + * + * Input Parameters: + * i2c - An instance of the I2C interface to use to communicate with PCA9540BDP + * addr - The I2C address of the PCA9540BDP. The base I2C address of the PCA9540BDP + * is 0x70. + * + * Returned Value: + * Common i2c multiplexer device instance; NULL on failure. + * + ****************************************************************************/ + +FAR struct pca9540bdp_dev_s* pca9540bdp_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_I2C_PCA9540BDP_H */