forked from Imagelibrary/rtems
Add generic EEPROM I2C device driver
This commit is contained in:
@@ -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
260
cpukit/dev/i2c/eeprom.c
Normal 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);
|
||||
}
|
||||
58
cpukit/dev/include/dev/i2c/eeprom.h
Normal file
58
cpukit/dev/include/dev/i2c/eeprom.h
Normal 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 */
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user