[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