From cce4678047449f80e6018710be45b55838d789a9 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 7 Nov 2014 13:49:29 +0100 Subject: Add generic EEPROM I2C device driver --- cpukit/dev/Makefile.am | 2 + cpukit/dev/i2c/eeprom.c | 260 ++++++++++++++++++++++++++++++++++++ cpukit/dev/include/dev/i2c/eeprom.h | 58 ++++++++ cpukit/dev/preinstall.am | 4 + 4 files changed, 324 insertions(+) create mode 100644 cpukit/dev/i2c/eeprom.c create mode 100644 cpukit/dev/include/dev/i2c/eeprom.h (limited to 'cpukit/dev') diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am index c2913a6438..f782da22fb 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 0000000000..9165141346 --- /dev/null +++ b/cpukit/dev/i2c/eeprom.c @@ -0,0 +1,260 @@ +/** + * @file + * + * @brief EEPROM Driver Implementation + * + * @ingroup I2CEEPROM + */ + +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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 + +#include + +#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) { + rtems_set_errno_and_return_minus_one(ERANGE); + } + + 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) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + + 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 0000000000..73df5ad9f4 --- /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 + * + * + * 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 + +#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 82393279e2..4472b810dc 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 -- cgit v1.2.3