summaryrefslogtreecommitdiffstats
path: root/cpukit/dev
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-11-07 13:49:29 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-11-20 10:30:11 +0100
commitcce4678047449f80e6018710be45b55838d789a9 (patch)
treebb3140dc2e13c7abac28c5ea7e50781640da631c /cpukit/dev
parentAdd I2C driver framework (diff)
downloadrtems-cce4678047449f80e6018710be45b55838d789a9.tar.bz2
Add generic EEPROM I2C device driver
Diffstat (limited to 'cpukit/dev')
-rw-r--r--cpukit/dev/Makefile.am2
-rw-r--r--cpukit/dev/i2c/eeprom.c260
-rw-r--r--cpukit/dev/include/dev/i2c/eeprom.h58
-rw-r--r--cpukit/dev/preinstall.am4
4 files changed, 324 insertions, 0 deletions
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
+ * <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/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) {
+ 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
+ * <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_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 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