Add generic EEPROM I2C device driver

This commit is contained in:
Sebastian Huber
2014-11-07 13:49:29 +01:00
parent 41c5f1b779
commit cce4678047
5 changed files with 465 additions and 1 deletions

View File

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

260
cpukit/dev/i2c/eeprom.c Normal file
View File

@@ -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);
}

View File

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

View File

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