[PATCH 4/9] Add NXP PCA9535 16-bit GPIO I2C driver

Sebastian Huber sebastian.huber at embedded-brains.de
Thu Nov 13 09:30:22 UTC 2014


---
 cpukit/dev/Makefile.am                        |   2 +
 cpukit/dev/i2c/gpio-nxp-pca9535.c             | 173 ++++++++++++++++++++++++++
 cpukit/dev/include/dev/i2c/gpio-nxp-pca9535.h | 123 ++++++++++++++++++
 cpukit/dev/preinstall.am                      |   4 +
 testsuites/libtests/i2c01/init.c              | 151 +++++++++++++++++++++-
 5 files changed, 452 insertions(+), 1 deletion(-)
 create mode 100644 cpukit/dev/i2c/gpio-nxp-pca9535.c
 create mode 100644 cpukit/dev/include/dev/i2c/gpio-nxp-pca9535.h

diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am
index f782da2..93737f9 100644
--- a/cpukit/dev/Makefile.am
+++ b/cpukit/dev/Makefile.am
@@ -7,6 +7,7 @@ include_dev_HEADERS =
 include_dev_i2cdir = $(includedir)/dev/i2c
 include_dev_i2c_HEADERS =
 include_dev_i2c_HEADERS += include/dev/i2c/eeprom.h
+include_dev_i2c_HEADERS += include/dev/i2c/gpio-nxp-pca9535.h
 include_dev_i2c_HEADERS += include/dev/i2c/i2c.h
 
 include_linuxdir = $(includedir)/linux
@@ -18,6 +19,7 @@ noinst_LIBRARIES = libdev.a
 
 libdev_a_SOURCES =
 libdev_a_SOURCES += i2c/eeprom.c
+libdev_a_SOURCES += i2c/gpio-nxp-pca9535.c
 libdev_a_SOURCES += i2c/i2c-bus.c
 libdev_a_SOURCES += i2c/i2c-dev.c
 
diff --git a/cpukit/dev/i2c/gpio-nxp-pca9535.c b/cpukit/dev/i2c/gpio-nxp-pca9535.c
new file mode 100644
index 0000000..46473bb
--- /dev/null
+++ b/cpukit/dev/i2c/gpio-nxp-pca9535.c
@@ -0,0 +1,173 @@
+/**
+ * @file
+ *
+ * @brief GPIO NXP PCA9535 Driver Implementation
+ *
+ * @ingroup I2CGPIONXPPCA9535
+ */
+
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <dev/i2c/gpio-nxp-pca9535.h>
+
+typedef enum {
+  GPIO_NXP_PCA9535_INPUT_PORT_0,
+  GPIO_NXP_PCA9535_INPUT_PORT_1,
+  GPIO_NXP_PCA9535_OUTPUT_PORT_0,
+  GPIO_NXP_PCA9535_OUTPUT_PORT_1,
+  GPIO_NXP_PCA9535_POL_INV_PORT_0,
+  GPIO_NXP_PCA9535_POL_INV_PORT_1,
+  GPIO_NXP_PCA9535_CONF_PORT_0,
+  GPIO_NXP_PCA9535_CONF_PORT_1
+} gpio_nxp_pca9535_port;
+
+typedef struct {
+  i2c_dev base;
+  uint16_t output;
+} gpio_nxp_pca9535;
+
+static int gpio_nxp_pca9535_get_reg(
+  gpio_nxp_pca9535 *dev,
+  gpio_nxp_pca9535_port port,
+  uint16_t *val
+)
+{
+  uint8_t buf[1] = { port };
+  i2c_msg msgs[2] = {
+    {
+      .addr = dev->base.address,
+      .flags = 0,
+      .len = (uint16_t) sizeof(buf),
+      .buf = &buf[0]
+    }, {
+      .addr = dev->base.address,
+      .flags = I2C_M_RD,
+      .len = (uint16_t) sizeof(*val),
+      .buf = (uint8_t *) val
+    }
+  };
+
+  return i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+}
+
+static int gpio_nxp_pca9535_set_reg(
+  gpio_nxp_pca9535 *dev,
+  gpio_nxp_pca9535_port port,
+  uint16_t val
+)
+{
+  uint8_t buf[3] = { port, (uint8_t) val, (uint8_t) (val >> 8) };
+  i2c_msg msgs[1] = {
+    {
+      .addr = dev->base.address,
+      .flags = 0,
+      .len = (uint16_t) sizeof(buf),
+      .buf = &buf[0]
+    }
+  };
+
+  return i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+}
+
+static int gpio_nxp_pca9535_ioctl(
+  i2c_dev *base,
+  ioctl_command_t command,
+  void *arg
+)
+{
+  gpio_nxp_pca9535 *dev = (gpio_nxp_pca9535 *) base;
+  uint16_t v16 = (uint16_t)(uintptr_t) arg;
+  uint32_t v32 = (uint32_t)(uintptr_t) arg;
+  int err;
+
+  i2c_bus_obtain(dev->base.bus);
+
+  switch (command) {
+    case GPIO_NXP_PCA9535_GET_INPUT:
+      err = gpio_nxp_pca9535_get_reg(dev, GPIO_NXP_PCA9535_INPUT_PORT_0, arg);
+      break;
+    case GPIO_NXP_PCA9535_GET_OUTPUT:
+      err = gpio_nxp_pca9535_get_reg(dev, GPIO_NXP_PCA9535_OUTPUT_PORT_0, arg);
+      if (err == 0) {
+        dev->output = *(uint16_t *) arg;
+      }
+      break;
+    case GPIO_NXP_PCA9535_CLEAR_AND_SET_OUTPUT:
+      v16 = dev->output;
+      v16 &= ~((uint16_t) v32);
+      v16 |= (uint16_t) (v32 >> 16);
+      /* Fall through */
+    case GPIO_NXP_PCA9535_SET_OUTPUT:
+      err = gpio_nxp_pca9535_set_reg(dev, GPIO_NXP_PCA9535_OUTPUT_PORT_0, v16);
+      if (err == 0) {
+        dev->output = v16;
+      }
+      break;
+    case GPIO_NXP_PCA9535_GET_POL_INV:
+      err = gpio_nxp_pca9535_get_reg(dev, GPIO_NXP_PCA9535_POL_INV_PORT_0, arg);
+      break;
+    case GPIO_NXP_PCA9535_SET_POL_INV:
+      err = gpio_nxp_pca9535_set_reg(dev, GPIO_NXP_PCA9535_POL_INV_PORT_0, v16);
+      break;
+    case GPIO_NXP_PCA9535_GET_CONFIG:
+      err = gpio_nxp_pca9535_get_reg(dev, GPIO_NXP_PCA9535_CONF_PORT_0, arg);
+      break;
+    case GPIO_NXP_PCA9535_SET_CONFIG:
+      err = gpio_nxp_pca9535_set_reg(dev, GPIO_NXP_PCA9535_CONF_PORT_0, v16);
+      break;
+    default:
+      err = -ENOTTY;
+      break;
+  }
+
+  i2c_bus_release(dev->base.bus);
+
+  return err;
+}
+
+int i2c_dev_register_gpio_nxp_pca9535(
+  const char *bus_path,
+  const char *dev_path,
+  uint16_t address
+)
+{
+  gpio_nxp_pca9535 *dev;
+  int err;
+
+  dev = (gpio_nxp_pca9535 *)
+    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
+  if (dev == NULL) {
+    return -1;
+  }
+
+  dev->base.ioctl = gpio_nxp_pca9535_ioctl;
+
+  err = gpio_nxp_pca9535_get_reg(
+    dev,
+    GPIO_NXP_PCA9535_OUTPUT_PORT_0,
+    &dev->output
+  );
+  if (err != 0) {
+    i2c_dev_destroy_and_free(&dev->base);
+
+    rtems_set_errno_and_return_minus_one(-err);
+  }
+
+  return i2c_dev_register(&dev->base, dev_path);
+}
diff --git a/cpukit/dev/include/dev/i2c/gpio-nxp-pca9535.h b/cpukit/dev/include/dev/i2c/gpio-nxp-pca9535.h
new file mode 100644
index 0000000..c101aca
--- /dev/null
+++ b/cpukit/dev/include/dev/i2c/gpio-nxp-pca9535.h
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * @brief GPIO NXP PCA9535 Driver API
+ *
+ * @ingroup I2CGPIONXPPCA9535
+ */
+
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef _DEV_I2C_GPIO_NXP_PCA9539_H
+#define _DEV_I2C_GPIO_NXP_PCA9539_H
+
+#include <dev/i2c/i2c.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup I2CGPIONXPPCA9535 GPIO NXP PCA9535 Driver
+ *
+ * @ingroup I2CDevice
+ *
+ * @brief Driver for NXP PCA9535 16-bit GPIO device.
+ *
+ * @{
+ */
+
+int i2c_dev_register_gpio_nxp_pca9535(
+  const char *bus_path,
+  const char *dev_path,
+  uint16_t address
+);
+
+#define GPIO_NXP_PCA9535_GET_INPUT (I2C_DEVICE_IO_CONTROL + 0)
+
+#define GPIO_NXP_PCA9535_GET_OUTPUT (I2C_DEVICE_IO_CONTROL + 1)
+
+#define GPIO_NXP_PCA9535_SET_OUTPUT (I2C_DEVICE_IO_CONTROL + 2)
+
+#define GPIO_NXP_PCA9535_CLEAR_AND_SET_OUTPUT (I2C_DEVICE_IO_CONTROL + 3)
+
+#define GPIO_NXP_PCA9535_GET_POL_INV (I2C_DEVICE_IO_CONTROL + 4)
+
+#define GPIO_NXP_PCA9535_SET_POL_INV (I2C_DEVICE_IO_CONTROL + 5)
+
+#define GPIO_NXP_PCA9535_GET_CONFIG (I2C_DEVICE_IO_CONTROL + 6)
+
+#define GPIO_NXP_PCA9535_SET_CONFIG (I2C_DEVICE_IO_CONTROL + 7)
+
+static inline int gpio_nxp_pca9535_get_input(int fd, uint16_t *val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_GET_INPUT, val);
+}
+
+static inline int gpio_nxp_pca9535_get_output(int fd, uint16_t *val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_GET_OUTPUT, val);
+}
+
+static inline int gpio_nxp_pca9535_set_output(int fd, uint16_t val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_SET_OUTPUT, (void *)(uintptr_t) val);
+}
+
+static inline int gpio_nxp_pca9535_clear_and_set_output(
+  int fd,
+  uint16_t clear,
+  uint16_t set
+)
+{
+  uint32_t clear_and_set = ((uint32_t) set << 16) | (uint32_t) clear;
+
+  return ioctl(
+    fd,
+    GPIO_NXP_PCA9535_CLEAR_AND_SET_OUTPUT,
+    (void *)(uintptr_t) clear_and_set
+  );
+}
+
+static inline int gpio_nxp_pca9535_get_polarity_inversion(
+  int fd,
+  uint16_t *val
+)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_GET_POL_INV, val);
+}
+
+static inline int gpio_nxp_pca9535_set_polarity_inversion(int fd, uint16_t val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_SET_POL_INV, (void *)(uintptr_t) val);
+}
+
+static inline int gpio_nxp_pca9535_get_config(int fd, uint16_t *val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_GET_CONFIG, val);
+}
+
+static inline int gpio_nxp_pca9535_set_config(int fd, uint16_t val)
+{
+  return ioctl(fd, GPIO_NXP_PCA9535_SET_CONFIG, (void *)(uintptr_t) val);
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _DEV_I2C_GPIO_NXP_PCA9539_H */
diff --git a/cpukit/dev/preinstall.am b/cpukit/dev/preinstall.am
index 4472b81..28e361b 100644
--- a/cpukit/dev/preinstall.am
+++ b/cpukit/dev/preinstall.am
@@ -27,6 +27,10 @@ $(PROJECT_INCLUDE)/dev/i2c/eeprom.h: include/dev/i2c/eeprom.h $(PROJECT_INCLUDE)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/eeprom.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/eeprom.h
 
+$(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h: include/dev/i2c/gpio-nxp-pca9535.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h
+
 $(PROJECT_INCLUDE)/dev/i2c/i2c.h: include/dev/i2c/i2c.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/i2c.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/i2c.h
diff --git a/testsuites/libtests/i2c01/init.c b/testsuites/libtests/i2c01/init.c
index b0024e4..e6f2eb3 100644
--- a/testsuites/libtests/i2c01/init.c
+++ b/testsuites/libtests/i2c01/init.c
@@ -18,6 +18,7 @@
 
 #include <dev/i2c/i2c.h>
 #include <dev/i2c/eeprom.h>
+#include <dev/i2c/gpio-nxp-pca9535.h>
 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -39,6 +40,8 @@ const char rtems_test_name[] = "I2C 1";
 
 #define DEVICE_EEPROM (1UL << SPARE_ADDRESS_BITS)
 
+#define DEVICE_GPIO_NXP_PCA9535 (2UL << SPARE_ADDRESS_BITS)
+
 #define EEPROM_SIZE 512
 
 typedef struct test_device test_device;
@@ -59,6 +62,12 @@ typedef struct {
 
 typedef struct {
   test_device base;
+  unsigned current_reg;
+  uint8_t regs[8];
+} test_device_gpio_nxp_pca9535;
+
+typedef struct {
+  test_device base;
   unsigned current_address;
   uint8_t data[EEPROM_SIZE];
 } test_device_eeprom;
@@ -66,13 +75,16 @@ typedef struct {
 typedef struct {
   i2c_bus base;
   unsigned long clock;
-  test_device *devices[2];
+  test_device *devices[3];
   test_device_simple_read_write simple_read_write;
+  test_device_gpio_nxp_pca9535 gpio_nxp_pca9535;
   test_device_eeprom eeprom;
 } test_bus;
 
 static const char bus_path[] = "/dev/i2c-0";
 
+static const char gpio_nxp_pca9535_path[] = "/dev/i2c-0.gpio-nxp-pc9535-0";
+
 static const char eeprom_path[] = "/dev/i2c-0.eeprom-0";
 
 static void cyclic_inc(unsigned *val, unsigned cycle)
@@ -105,6 +117,64 @@ static int test_simple_read_write_transfer(
   }
 }
 
+static int test_gpio_nxp_pca9535_transfer(
+  i2c_bus *bus,
+  i2c_msg *msgs,
+  uint32_t msg_count,
+  test_device *base
+)
+{
+  test_device_gpio_nxp_pca9535 *dev = (test_device_gpio_nxp_pca9535 *) base;
+  i2c_msg *first = &msgs[0];
+  i2c_msg *second = &msgs[1];
+  int i;
+
+  /* Get command byte */
+  if (
+    msg_count < 1
+      || (first->flags & I2C_M_RD) != 0
+      || first->len < 1
+  ) {
+    return -EIO;
+  }
+
+  dev->current_reg = first->buf[0];
+
+  if (first->len > 1) {
+    /* Write */
+
+    if (msg_count != 1) {
+      return -EIO;
+    }
+
+    for (i = 1; i < first->len; ++i) {
+      dev->regs[dev->current_reg] = first->buf[i];
+
+      /* Output is input */
+      if (dev->current_reg == 2) {
+        dev->regs[0] = first->buf[i];
+      } else if (dev->current_reg == 3) {
+        dev->regs[1] = first->buf[i];
+      }
+
+      cyclic_inc(&dev->current_reg, 2);
+    }
+  } else {
+    /* Read */
+
+    if (msg_count != 2) {
+      return -EIO;
+    }
+
+    for (i = 0; i < second->len; ++i) {
+      second->buf[i] = dev->regs[dev->current_reg];
+      cyclic_inc(&dev->current_reg, 2);
+    }
+  }
+
+  return 0;
+}
+
 static int test_eeprom_transfer(
   i2c_bus *bus,
   i2c_msg *msgs,
@@ -221,6 +291,81 @@ static void test_simple_read_write(test_bus *bus, int fd)
   rtems_test_assert(memcmp(&buf[0], &abc[0], sizeof(buf)) == 0);
 }
 
+static void test_gpio_nxp_pca9535(void)
+{
+  int rv;
+  int fd;
+  uint16_t val;
+
+  rv = i2c_dev_register_gpio_nxp_pca9535(
+    &bus_path[0],
+    &gpio_nxp_pca9535_path[0],
+    DEVICE_GPIO_NXP_PCA9535
+  );
+  rtems_test_assert(rv == 0);
+
+  fd = open(&gpio_nxp_pca9535_path[0], O_RDWR);
+  rtems_test_assert(fd >= 0);
+
+  rv = gpio_nxp_pca9535_get_input(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0);
+
+  rv = gpio_nxp_pca9535_get_output(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0);
+
+  rv = gpio_nxp_pca9535_set_output(fd, 0xa5ef);
+  rtems_test_assert(rv == 0);
+
+  rv = gpio_nxp_pca9535_get_input(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0xa5ef);
+
+  rv = gpio_nxp_pca9535_get_output(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0xa5ef);
+
+  rv = gpio_nxp_pca9535_clear_and_set_output(fd, 0x0ff0, 0x0170);
+  rtems_test_assert(rv == 0);
+
+  rv = gpio_nxp_pca9535_get_polarity_inversion(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0);
+
+  rv = gpio_nxp_pca9535_set_polarity_inversion(fd, 0x5afe);
+  rtems_test_assert(rv == 0);
+
+  rv = gpio_nxp_pca9535_get_config(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0);
+
+  rv = gpio_nxp_pca9535_set_config(fd, 0x2bcd);
+  rtems_test_assert(rv == 0);
+
+  rv = gpio_nxp_pca9535_get_input(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0xa17f);
+
+  rv = gpio_nxp_pca9535_get_output(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0xa17f);
+
+  rv = gpio_nxp_pca9535_get_polarity_inversion(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0x5afe);
+
+  rv = gpio_nxp_pca9535_get_config(fd, &val);
+  rtems_test_assert(rv == 0);
+  rtems_test_assert(val == 0x2bcd);
+
+  rv = close(fd);
+  rtems_test_assert(rv == 0);
+
+  rv = unlink(&gpio_nxp_pca9535_path[0]);
+  rtems_test_assert(rv == 0);
+}
+
 static void test_eeprom(void)
 {
   int rv;
@@ -317,6 +462,9 @@ static void test(void)
   bus->eeprom.base.transfer = test_eeprom_transfer;
   bus->devices[1] = &bus->eeprom.base;
 
+  bus->gpio_nxp_pca9535.base.transfer = test_gpio_nxp_pca9535_transfer;
+  bus->devices[2] = &bus->gpio_nxp_pca9535.base;
+
   rv = i2c_bus_register(&bus->base, &bus_path[0]);
   rtems_test_assert(rv == 0);
 
@@ -384,6 +532,7 @@ static void test(void)
 
   test_simple_read_write(bus, fd);
   test_eeprom();
+  test_gpio_nxp_pca9535();
 
   rv = close(fd);
   rtems_test_assert(rv == 0);
-- 
1.8.4.5




More information about the devel mailing list