--- 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 + * <rt...@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 + * <rt...@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 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel