Add NXP PCA9535 16-bit GPIO I2C driver

This commit is contained in:
Sebastian Huber
2014-11-07 13:50:01 +01:00
parent cce4678047
commit 67ac69fc3f
5 changed files with 428 additions and 1 deletions

View File

@@ -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

View File

@@ -0,0 +1,149 @@
/**
* @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@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;
static int gpio_nxp_pca9535_get_reg(
i2c_dev *dev,
gpio_nxp_pca9535_port port,
uint16_t *val
)
{
uint8_t buf[1] = { port };
i2c_msg msgs[2] = {
{
.addr = dev->address,
.flags = 0,
.len = (uint16_t) sizeof(buf),
.buf = &buf[0]
}, {
.addr = dev->address,
.flags = I2C_M_RD,
.len = (uint16_t) sizeof(*val),
.buf = (uint8_t *) val
}
};
return i2c_bus_transfer(dev->bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
}
static int gpio_nxp_pca9535_set_reg(
i2c_dev *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->address,
.flags = 0,
.len = (uint16_t) sizeof(buf),
.buf = &buf[0]
}
};
return i2c_bus_transfer(dev->bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
}
static int gpio_nxp_pca9535_ioctl(
i2c_dev *dev,
ioctl_command_t command,
void *arg
)
{
uint16_t v16 = (uint16_t)(uintptr_t) arg;
uint32_t v32 = (uint32_t)(uintptr_t) arg;
int err;
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);
break;
case GPIO_NXP_PCA9535_CLEAR_AND_SET_OUTPUT:
i2c_bus_obtain(dev->bus);
err = gpio_nxp_pca9535_get_reg(dev, GPIO_NXP_PCA9535_OUTPUT_PORT_0, &v16);
if (err == 0) {
v16 &= ~((uint16_t) v32);
v16 |= (uint16_t) (v32 >> 16);
err = gpio_nxp_pca9535_set_reg(dev, GPIO_NXP_PCA9535_OUTPUT_PORT_0, v16);
}
i2c_bus_release(dev->bus);
break;
case GPIO_NXP_PCA9535_SET_OUTPUT:
err = gpio_nxp_pca9535_set_reg(dev, GPIO_NXP_PCA9535_OUTPUT_PORT_0, 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;
}
return err;
}
int i2c_dev_register_gpio_nxp_pca9535(
const char *bus_path,
const char *dev_path,
uint16_t address
)
{
i2c_dev *dev;
dev = i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
if (dev == NULL) {
return -1;
}
dev->ioctl = gpio_nxp_pca9535_ioctl;
return i2c_dev_register(dev, dev_path);
}

View File

@@ -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@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_DEV_IO_CONTROL + 0)
#define GPIO_NXP_PCA9535_GET_OUTPUT (I2C_DEV_IO_CONTROL + 1)
#define GPIO_NXP_PCA9535_SET_OUTPUT (I2C_DEV_IO_CONTROL + 2)
#define GPIO_NXP_PCA9535_CLEAR_AND_SET_OUTPUT (I2C_DEV_IO_CONTROL + 3)
#define GPIO_NXP_PCA9535_GET_POL_INV (I2C_DEV_IO_CONTROL + 4)
#define GPIO_NXP_PCA9535_SET_POL_INV (I2C_DEV_IO_CONTROL + 5)
#define GPIO_NXP_PCA9535_GET_CONFIG (I2C_DEV_IO_CONTROL + 6)
#define GPIO_NXP_PCA9535_SET_CONFIG (I2C_DEV_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 */

View File

@@ -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

View File

@@ -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;
@@ -57,6 +60,12 @@ typedef struct {
char buf[3];
} test_device_simple_read_write;
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;
@@ -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);