--- cpukit/dev/Makefile.am | 2 + cpukit/dev/i2c/eeprom.c | 264 ++++++++++++++++++++++++++++++++++++ cpukit/dev/include/dev/i2c/eeprom.h | 58 ++++++++ cpukit/dev/preinstall.am | 4 + testsuites/libtests/i2c01/init.c | 142 ++++++++++++++++++- 5 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 cpukit/dev/i2c/eeprom.c create mode 100644 cpukit/dev/include/dev/i2c/eeprom.h
diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am index c2913a6..f782da2 100644 --- a/cpukit/dev/Makefile.am +++ b/cpukit/dev/Makefile.am @@ -6,6 +6,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/i2c.h include_linuxdir = $(includedir)/linux @@ -16,6 +17,7 @@ include_linux_HEADERS += include/linux/i2c-dev.h noinst_LIBRARIES = libdev.a libdev_a_SOURCES = +libdev_a_SOURCES += i2c/eeprom.c libdev_a_SOURCES += i2c/i2c-bus.c libdev_a_SOURCES += i2c/i2c-dev.c diff --git a/cpukit/dev/i2c/eeprom.c b/cpukit/dev/i2c/eeprom.c new file mode 100644 index 0000000..fe9b0d1 --- /dev/null +++ b/cpukit/dev/i2c/eeprom.c @@ -0,0 +1,264 @@ +/** + * @file + * + * @brief EEPROM Driver Implementation + * + * @ingroup I2CEEPROM + */ + +/* + * 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/eeprom.h> + +#include <string.h> + +#define EEPROM_MAX_ADDRESS_BYTES 4 + +#define EEPROM_MAX_PAGE_SIZE 128 + +typedef struct { + i2c_dev base; + uint16_t address_bytes; + uint16_t page_size; + uint32_t size; + uint16_t i2c_address_mask; + uint16_t i2c_address_shift; + rtems_interval program_timeout; +} eeprom; + +static uint16_t eeprom_i2c_addr(eeprom *dev, uint32_t off) +{ + return dev->base.address + | ((off >> dev->i2c_address_shift) & dev->i2c_address_mask); +} + +static ssize_t eeprom_read( + i2c_dev *base, + void *buf, + size_t n, + off_t offset +) +{ + eeprom *dev = (eeprom *) base; + off_t avail = dev->size - offset; + uint32_t off = (uint32_t) offset; + uint8_t *in = buf; + size_t todo; + + if (avail <= 0) { + return 0; + } + + if (n > avail) { + n = (size_t) avail; + } + + todo = n; + + while (todo > 0) { + uint16_t i2c_addr = eeprom_i2c_addr(dev, off); + + /* + * Limit the transfer size so that it can be stored in 8-bits. This may + * help some bus controllers. + */ + uint16_t cur = (uint16_t) (todo < 255 ? todo : 255); + + uint8_t addr[EEPROM_MAX_ADDRESS_BYTES] = { + (uint8_t) off, + (uint8_t) (off >> 8), + (uint8_t) (off >> 16), + (uint8_t) (off >> 24) + }; + i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .len = dev->address_bytes, + .buf = &addr[0] + }, { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = in, + .len = cur + } + }; + int err; + + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + if (err != 0) { + return err; + } + + todo -= cur; + off += cur; + in += cur; + } + + return (ssize_t) n; +} + +static ssize_t eeprom_write( + i2c_dev *base, + const void *buf, + size_t n, + off_t offset +) +{ + eeprom *dev = (eeprom *) base; + off_t avail = dev->size - offset; + uint32_t off = (uint32_t) offset; + const uint8_t *out = buf; + size_t todo; + + if (avail <= 0) { + return 0; + } + + if (n > avail) { + n = (size_t) avail; + } + + todo = n; + + while (todo > 0) { + uint16_t i2c_addr = eeprom_i2c_addr(dev, off); + uint16_t rem = dev->page_size - (off & (dev->page_size - 1)); + uint16_t cur = (uint16_t) (todo < rem ? todo : rem); + uint8_t addr[EEPROM_MAX_ADDRESS_BYTES] = { + (uint8_t) off, + (uint8_t) (off >> 8), + (uint8_t) (off >> 16), + (uint8_t) (off >> 24) + }; + i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .len = dev->address_bytes, + .buf = &addr[0] + }, { + .addr = i2c_addr, + .flags = I2C_M_NOSTART, + .buf = RTEMS_DECONST(uint8_t *, out), + .len = cur + } + }; + uint8_t in[EEPROM_MAX_PAGE_SIZE]; + int err; + ssize_t m; + rtems_interval timeout; + + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + if (err != 0) { + return err; + } + + timeout = rtems_clock_tick_later(dev->program_timeout); + + do { + m = eeprom_read(&dev->base, &in[0], cur, off); + } while (m != cur && rtems_clock_tick_before(timeout)); + + if (m != cur) { + return -ETIMEDOUT; + } + + if (memcmp(&in[0], &out[0], cur) != 0) { + return -EIO; + } + + todo -= cur; + off += cur; + out += cur; + } + + return (ssize_t) n; +} + +static off_t eeprom_get_size(i2c_dev *base) +{ + eeprom *dev = (eeprom *) base; + + return dev->size; +} + +static blksize_t eeprom_get_block_size(i2c_dev *base) +{ + eeprom *dev = (eeprom *) base; + + return dev->page_size; +} + +int i2c_dev_register_eeprom( + const char *bus_path, + const char *dev_path, + uint16_t i2c_address, + uint16_t address_bytes, + uint16_t page_size_in_bytes, + uint32_t size_in_bytes, + uint32_t program_timeout_in_ms +) +{ + uint32_t extra_address; + eeprom *dev; + + if (address_bytes > EEPROM_MAX_ADDRESS_BYTES) { + errno = ERANGE; + + return -1; + } + + if (page_size_in_bytes > EEPROM_MAX_PAGE_SIZE) { + page_size_in_bytes = EEPROM_MAX_PAGE_SIZE; + } + + extra_address = size_in_bytes >> (8 * address_bytes); + if (extra_address != 0 && (extra_address & (extra_address - 1)) != 0) { + errno = EINVAL; + + return -1; + } + + if (program_timeout_in_ms == 0) { + program_timeout_in_ms = 1000; + } + + dev = (eeprom *) + i2c_dev_alloc_and_init(sizeof(*dev), bus_path, i2c_address); + if (dev == NULL) { + return -1; + } + + dev->base.read = eeprom_read; + dev->base.write = eeprom_write; + dev->base.get_size = eeprom_get_size; + dev->base.get_block_size = eeprom_get_block_size; + dev->address_bytes = address_bytes; + dev->page_size = page_size_in_bytes; + dev->size = size_in_bytes; + dev->program_timeout = RTEMS_MILLISECONDS_TO_TICKS(program_timeout_in_ms); + + if (extra_address != 0) { + dev->i2c_address_mask = extra_address - 1; + dev->i2c_address_shift = (uint16_t) (8 * address_bytes); + } + + return i2c_dev_register(&dev->base, dev_path); +} diff --git a/cpukit/dev/include/dev/i2c/eeprom.h b/cpukit/dev/include/dev/i2c/eeprom.h new file mode 100644 index 0000000..73df5ad --- /dev/null +++ b/cpukit/dev/include/dev/i2c/eeprom.h @@ -0,0 +1,58 @@ +/** + * @file + * + * @brief EEPROM Driver API + * + * @ingroup I2CEEPROM + */ + +/* + * 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_EEPROM_H +#define _DEV_I2C_EEPROM_H + +#include <dev/i2c/i2c.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @defgroup I2CEEPROM EEPROM Driver + * + * @ingroup I2CDevice + * + * @brief Driver for EEPROM device. + * + * @{ + */ + +int i2c_dev_register_eeprom( + const char *bus_path, + const char *dev_path, + uint16_t i2c_address, + uint16_t address_bytes, + uint16_t page_size_in_bytes, + uint32_t size_in_bytes, + uint32_t program_timeout_in_ms +); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _DEV_I2C_EEPROM_H */ diff --git a/cpukit/dev/preinstall.am b/cpukit/dev/preinstall.am index 8239327..4472b81 100644 --- a/cpukit/dev/preinstall.am +++ b/cpukit/dev/preinstall.am @@ -23,6 +23,10 @@ $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp): @: > $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp) PREINSTALL_DIRS += $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp) +$(PROJECT_INCLUDE)/dev/i2c/eeprom.h: include/dev/i2c/eeprom.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/eeprom.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/eeprom.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 7f38d34..b0024e4 100644 --- a/testsuites/libtests/i2c01/init.c +++ b/testsuites/libtests/i2c01/init.c @@ -17,6 +17,7 @@ #endif #include <dev/i2c/i2c.h> +#include <dev/i2c/eeprom.h> #include <sys/ioctl.h> #include <sys/stat.h> @@ -36,6 +37,10 @@ const char rtems_test_name[] = "I2C 1"; #define DEVICE_SIMPLE_READ_WRITE (0UL << SPARE_ADDRESS_BITS) +#define DEVICE_EEPROM (1UL << SPARE_ADDRESS_BITS) + +#define EEPROM_SIZE 512 + typedef struct test_device test_device; struct test_device { @@ -53,14 +58,31 @@ typedef struct { } test_device_simple_read_write; typedef struct { + test_device base; + unsigned current_address; + uint8_t data[EEPROM_SIZE]; +} test_device_eeprom; + +typedef struct { i2c_bus base; unsigned long clock; - test_device *devices[1]; + test_device *devices[2]; test_device_simple_read_write simple_read_write; + test_device_eeprom eeprom; } test_bus; static const char bus_path[] = "/dev/i2c-0"; +static const char eeprom_path[] = "/dev/i2c-0.eeprom-0"; + +static void cyclic_inc(unsigned *val, unsigned cycle) +{ + unsigned v = *val; + unsigned m = cycle - 1U; + + *val = (v & ~m) + ((v + 1U) & m); +} + static int test_simple_read_write_transfer( i2c_bus *bus, i2c_msg *msgs, @@ -83,6 +105,48 @@ static int test_simple_read_write_transfer( } } +static int test_eeprom_transfer( + i2c_bus *bus, + i2c_msg *msgs, + uint32_t msg_count, + test_device *base +) +{ + test_device_eeprom *dev = (test_device_eeprom *) base; + i2c_msg *msg = &msgs[0]; + uint32_t i; + + if (msg_count > 0 && (msg->flags & I2C_M_RD) == 0) { + if (msg->len < 1) { + return -EIO; + } + + dev->current_address = msg->buf[0] | ((msg->addr & 0x1) << 8); + --msg->len; + ++msg->buf; + } + + for (i = 0; i < msg_count; ++i) { + int j; + + msg = &msgs[i]; + + if ((msg->flags & I2C_M_RD) != 0) { + for (j = 0; j < msg->len; ++j) { + msg->buf[j] = dev->data[dev->current_address]; + cyclic_inc(&dev->current_address, sizeof(dev->data)); + } + } else { + for (j = 0; j < msg->len; ++j) { + dev->data[dev->current_address] = msg->buf[j]; + cyclic_inc(&dev->current_address, 8); + } + } + } + + return 0; +} + static int test_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count) { test_bus *bus = (test_bus *) base; @@ -157,6 +221,78 @@ 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_eeprom(void) +{ + int rv; + int fd_in; + int fd_out; + struct stat st; + uint8_t in[EEPROM_SIZE]; + uint8_t out[EEPROM_SIZE]; + ssize_t n; + off_t off; + size_t i; + + rv = i2c_dev_register_eeprom( + &bus_path[0], + &eeprom_path[0], + DEVICE_EEPROM, + 1, + 8, + sizeof(out), + 0 + ); + rtems_test_assert(rv == 0); + + fd_in = open(&eeprom_path[0], O_RDWR); + rtems_test_assert(fd_in >= 0); + + fd_out = open(&eeprom_path[0], O_RDWR); + rtems_test_assert(fd_out >= 0); + + rv = fstat(fd_in, &st); + rtems_test_assert(rv == 0); + rtems_test_assert(st.st_blksize == 8); + rtems_test_assert(st.st_size == sizeof(out)); + + memset(&out[0], 0, sizeof(out)); + + n = read(fd_in, &in[0], sizeof(in) + 1); + rtems_test_assert(n == (ssize_t) sizeof(in)); + + rtems_test_assert(memcmp(&in[0], &out[0], sizeof(in)) == 0); + + off = lseek(fd_in, 0, SEEK_CUR); + rtems_test_assert(off == sizeof(out)); + + for (i = 0; i < sizeof(out); ++i) { + off = lseek(fd_out, 0, SEEK_CUR); + rtems_test_assert(off == i); + + out[i] = (uint8_t) i; + + n = write(fd_out, &out[i], sizeof(out[i])); + rtems_test_assert(n == (ssize_t) sizeof(out[i])); + + off = lseek(fd_in, 0, SEEK_SET); + rtems_test_assert(off == 0); + + n = read(fd_in, &in[0], sizeof(in)); + rtems_test_assert(n == (ssize_t) sizeof(in)); + + rtems_test_assert(memcmp(&in[0], &out[0], sizeof(in)) == 0); + } + + rv = close(fd_in); + rtems_test_assert(rv == 0); + + rv = close(fd_out); + rtems_test_assert(rv == 0); + + rv = unlink(&eeprom_path[0]); + rtems_test_assert(rv == 0); +} + static void test(void) { rtems_resource_snapshot snapshot; @@ -178,6 +314,9 @@ static void test(void) bus->simple_read_write.base.transfer = test_simple_read_write_transfer; bus->devices[0] = &bus->simple_read_write.base; + bus->eeprom.base.transfer = test_eeprom_transfer; + bus->devices[1] = &bus->eeprom.base; + rv = i2c_bus_register(&bus->base, &bus_path[0]); rtems_test_assert(rv == 0); @@ -244,6 +383,7 @@ static void test(void) rtems_test_assert(bus->base.timeout == 0); test_simple_read_write(bus, fd); + test_eeprom(); 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