X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fdrv%2Feeprom.c;h=82fb9590f6126524ce8d63a76290df2ab1f706b9;hb=ee28b0a27ccfcf160e6cde5632bb5966608bbc90;hp=835c1cc5ec6d4c5b48a864ccf1b3785e99db6eee;hpb=d62963b4a64efe8d2917f489fefaf586a9a99126;p=bertos.git diff --git a/bertos/drv/eeprom.c b/bertos/drv/eeprom.c index 835c1cc5..82fb9590 100644 --- a/bertos/drv/eeprom.c +++ b/bertos/drv/eeprom.c @@ -26,39 +26,34 @@ * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. * - * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/) + * Copyright 2003, 2004, 2005, 2010 Develer S.r.l. (http://www.develer.com/) * * --> * * \brief Driver for the 24xx16 and 24xx256 I2C EEPROMS (implementation) * - * - * \version $Id$ * \author Stefano Fedrigo - * \author Bernardo Innocenti + * \author Bernie Innocenti */ #include "eeprom.h" -#include // CONFIG_EEPROM_VERIFY -#include // MIN() +#include "cfg/cfg_i2c.h" +#include "cfg/cfg_eeprom.h" + +/* Define logging setting (for cfg/log.h module). */ +#define LOG_LEVEL EEPROM_LOG_LEVEL +#define LOG_FORMAT EEPROM_LOG_FORMAT +#include #include +#include // MIN() #include -#include CPU_HEADER(twi) - -#include -#include // cpu_to_be16() +#include #include // memset() - -// Configuration sanity checks -#if !defined(CONFIG_EEPROM_VERIFY) || (CONFIG_EEPROM_VERIFY != 0 && CONFIG_EEPROM_VERIFY != 1) - #error CONFIG_EEPROM_VERIFY must be defined to either 0 or 1 -#endif - /** * EEPROM ID code */ @@ -67,277 +62,309 @@ /** * This macros form the correct slave address for EEPROMs */ -#define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)(x)) << 1)) +#define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)((x) & 0x07)) << 1)) +/** + * Array used to describe EEPROM memory devices currently supported. + */ +static const EepromInfo mem_info[] = +{ + { + /* 24XX08 */ + .has_dev_addr = false, + .blk_size = 0x10, + .e2_size = 0x400, + }, + { + /* 24XX16 */ + .has_dev_addr = false, + .blk_size = 0x10, + .e2_size = 0x800, + }, + { + /* 24XX32 */ + .has_dev_addr = true, + .blk_size = 0x20, + .e2_size = 0x1000, + }, + { + /* 24XX64 */ + .has_dev_addr = true, + .blk_size = 0x20, + .e2_size = 0x2000, + }, + { + /* 24XX128 */ + .has_dev_addr = true, + .blk_size = 0x40, + .e2_size = 0x4000, + }, + { + /* 24XX256 */ + .has_dev_addr = true, + .blk_size = 0x40, + .e2_size = 0x8000, + }, + { + /* 24XX512 */ + .has_dev_addr = true, + .blk_size = 0x80, + .e2_size = 0x10000, + }, + { + /* 24XX1024 */ + .has_dev_addr = true, + .blk_size = 0x100, + .e2_size = 0x20000, + }, + /* Add other memories here */ +}; + +STATIC_ASSERT(countof(mem_info) == EEPROM_CNT); + +#define CHUNCK_SIZE 16 /** - * Copy \c count bytes from buffer \c buf to - * eeprom at address \c addr. + * Erase EEPROM. + * \param eep is the Kblock context. + * \param addr eeprom address where start to erase + * \param size number of byte to erase */ -static bool eeprom_writeRaw(e2addr_t addr, const void *buf, size_t count) +bool eeprom_erase(Eeprom *eep, e2addr_t addr, e2_size_t size) { - bool result = true; - ASSERT(addr + count <= EEPROM_SIZE); + uint8_t tmp[CHUNCK_SIZE] = { [0 ... (CHUNCK_SIZE - 1)] = 0xFF }; - while (count && result) + while (size) { - /* - * Split write in multiple sequential mode operations that - * don't cross page boundaries. - */ - size_t size = - MIN(count, (size_t)(EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1)))); - - #if CONFIG_EEPROM_TYPE == EEPROM_24XX16 - /* - * The 24LC16 uses the slave address as a 3-bit - * block address. - */ - uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07); - uint8_t blk_offs = (uint8_t)addr; - - result = - twi_start_w(EEPROM_ADDR(blk_addr)) - && twi_send(&blk_offs, sizeof blk_offs) - && twi_send(buf, size); - - #elif CONFIG_EEPROM_TYPE == EEPROM_24XX256 - - // 24LC256 wants big-endian addresses - uint16_t addr_be = cpu_to_be16(addr); - - result = - twi_start_w(EEPROM_ID) - && twi_send((uint8_t *)&addr_be, sizeof addr_be) - && twi_send(buf, size); - - #else - #error Unknown device type - #endif - - twi_stop(); - - // DEBUG - //kprintf("addr=%d, count=%d, size=%d, *#?=%d\n", - // addr, count, size, - // (EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1))) - //); - - /* Update count and addr for next operation */ - count -= size; - addr += size; - buf = ((const char *)buf) + size; + block_idx_t idx = addr / eep->blk.blk_size; + size_t offset = addr % eep->blk.blk_size; + size_t count = MIN(size, (e2_size_t)CHUNCK_SIZE); + size_t ret_len = eep->blk.priv.vt->writeDirect((KBlock *)eep, idx, tmp, offset, count); + size -= ret_len; + addr += ret_len; + + if (ret_len != count) + return false; } - - if (!result) - TRACEMSG("Write error!"); - return result; + return true; } - -#if CONFIG_EEPROM_VERIFY /** - * Check that the contents of an EEPROM range - * match with a provided data buffer. - * - * \return true on success. + * Verify EEPROM. + * \param eep is the Kblock context. + * \param addr eeprom address where start to verify. + * \param buf buffer of data to compare with eeprom data read. + * \param size number of byte to verify. */ -static bool eeprom_verify(e2addr_t addr, const void *buf, size_t count) +bool eeprom_verify(Eeprom *eep, e2addr_t addr, const void *buf, size_t size) { - uint8_t verify_buf[16]; - bool result = true; - - while (count && result) + uint8_t verify_buf[CHUNCK_SIZE]; + while (size) { - /* Split read in smaller pieces */ - size_t size = MIN(count, sizeof verify_buf); + block_idx_t idx = addr / eep->blk.blk_size; + size_t offset = addr % eep->blk.blk_size; + size_t count = MIN(size, (size_t)CHUNCK_SIZE); + + size_t ret_len = eep->blk.priv.vt->readDirect((KBlock *)eep, idx, verify_buf, offset, count); - /* Read back buffer */ - if (eeprom_read(addr, verify_buf, size)) + if (ret_len != count) { - if (memcmp(buf, verify_buf, size) != 0) - { - TRACEMSG("Data mismatch!"); - result = false; - } + LOG_ERR("Verify read fail.\n"); + return false; } - else + + if (memcmp(buf, verify_buf, ret_len) != 0) { - TRACEMSG("Read error!"); - result = false; + LOG_ERR("Data mismatch!\n"); + return false; } - /* Update count and addr for next operation */ - count -= size; - addr += size; - buf = ((const char *)buf) + size; + size -= ret_len; + addr += ret_len; + buf = ((const char *)buf) + ret_len; } - - return result; + return true; } -#endif /* CONFIG_EEPROM_VERIFY */ -bool eeprom_write(e2addr_t addr, const void *buf, size_t count) +static size_t eeprom_write(KBlock *blk, block_idx_t idx, const void *buf, size_t offset, size_t size) { -#if CONFIG_EEPROM_VERIFY - int retries = 5; + Eeprom *eep = EEPROM_CAST_KBLOCK(blk); + e2dev_addr_t dev_addr; + uint8_t addr_buf[2]; + uint8_t addr_len; + uint32_t abs_addr = blk->blk_size * idx + offset; - while (retries--) - if (eeprom_writeRaw(addr, buf, count) - && eeprom_verify(addr, buf, count)) - return true; + STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t)); - return false; + /* clamp size to memory limit (otherwise may roll back) */ + ASSERT(idx < blk->priv.blk_start + blk->blk_cnt); + size = MIN(size, blk->blk_size - offset); -#else /* !CONFIG_EEPROM_VERIFY */ - return eeprom_writeRaw(addr, buf, count); -#endif /* !CONFIG_EEPROM_VERIFY */ -} + if (mem_info[eep->type].has_dev_addr) + { + dev_addr = eep->addr; + addr_len = 2; + } + else + { + dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07); + addr_len = 1; + } + if (mem_info[eep->type].has_dev_addr) + { + addr_buf[0] = (abs_addr >> 8) & 0xFF; + addr_buf[1] = (abs_addr & 0xFF); + } + else + { + dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07); + addr_buf[0] = (abs_addr & 0xFF); + } -/** - * Copy \c count bytes at address \c addr - * from eeprom to RAM to buffer \c buf. - * - * \return true on success. - */ -bool eeprom_read(e2addr_t addr, void *buf, size_t count) -{ - ASSERT(addr + count <= EEPROM_SIZE); - -#if CONFIG_EEPROM_TYPE == EEPROM_24XX16 - /* - * The 24LC16 uses the slave address as a 3-bit - * block address. - */ - uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07); - uint8_t blk_offs = (uint8_t)addr; - - bool res = - twi_start_w(EEPROM_ADDR(blk_addr)) - && twi_send(&blk_offs, sizeof blk_offs) - && twi_start_r(EEPROM_ADDR(blk_addr)) - && twi_recv(buf, count); - -#elif CONFIG_EEPROM_TYPE == EEPROM_24XX256 - - // 24LC256 wants big-endian addresses - addr = cpu_to_be16(addr); - - bool res = - twi_start_w(EEPROM_ID) - && twi_send((uint8_t *)&addr, sizeof(addr)) - && twi_start_r(EEPROM_ID) - && twi_recv(buf, count); -#else - #error Unknown device type -#endif - - twi_stop(); - - if (!res) - TRACEMSG("Read error!"); - return res; -} + i2c_start_w(eep->i2c, EEPROM_ADDR(dev_addr), addr_len + size, I2C_STOP); + i2c_write(eep->i2c, addr_buf, addr_len); + i2c_write(eep->i2c, buf, size); + if (i2c_error(eep->i2c)) + return 0; -/** - * Write a single character \a c at address \a addr. - */ -bool eeprom_write_char(e2addr_t addr, char c) -{ - return eeprom_write(addr, &c, 1); + return size; } - -/** - * Read a single character at address \a addr. - * - * \return the requested character or -1 in case of failure. - */ -int eeprom_read_char(e2addr_t addr) +static size_t eeprom_readDirect(struct KBlock *_blk, block_idx_t idx, void *_buf, size_t offset, size_t size) { - char c; + Eeprom *blk = EEPROM_CAST_KBLOCK(_blk); + uint8_t addr_buf[2]; + uint8_t addr_len; + size_t rd_len = 0; + uint8_t *buf = (uint8_t *)_buf; + uint32_t abs_addr = mem_info[blk->type].blk_size * idx + offset; + + STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t)); + + /* clamp size to memory limit (otherwise may roll back) */ + ASSERT(idx < blk->blk.priv.blk_start + blk->blk.blk_cnt); + size = MIN(size, blk->blk.blk_size - offset); - if (eeprom_read(addr, &c, 1)) - return c; + e2dev_addr_t dev_addr; + if (mem_info[blk->type].has_dev_addr) + { + dev_addr = blk->addr; + addr_len = 2; + addr_buf[0] = (abs_addr >> 8) & 0xFF; + addr_buf[1] = (abs_addr & 0xFF); + } else - return -1; -} + { + dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07); + addr_len = 1; + addr_buf[0] = (abs_addr & 0xFF); + } -/** - * Erase specified part of eeprom, writing 0xFF. - * - * \param addr starting address - * \param count length of block to erase - */ -void eeprom_erase(e2addr_t addr, size_t count) -{ - uint8_t buf[EEPROM_BLKSIZE]; - memset(buf, 0xFF, sizeof buf); + i2c_start_w(blk->i2c, EEPROM_ADDR(dev_addr), addr_len, I2C_NOSTOP); + i2c_write(blk->i2c, addr_buf, addr_len); - // Clear all but struct hw_info at start of eeprom - while (count) - { - // Long operation, reset watchdog - wdt_reset(); + i2c_start_r(blk->i2c, EEPROM_ADDR(dev_addr), size, I2C_STOP); + i2c_read(blk->i2c, buf, size); - size_t size = MIN(count, sizeof buf); - eeprom_write(addr, buf, size); - addr += size; - count -= size; - } + if (i2c_error(blk->i2c)) + return rd_len; + + rd_len += size; + + return rd_len; } +static size_t eeprom_writeDirect(KBlock *blk, block_idx_t idx, const void *buf, size_t offset, size_t size) +{ + Eeprom *eep = EEPROM_CAST_KBLOCK(blk); + if (!eep->verify) + return eeprom_write(blk, idx, buf, offset, size); + else + { + int retries = 5; + while (retries--) + { + uint8_t verify_buf[CHUNCK_SIZE]; + size_t wr_len = 0; + size_t len = 0; + while (size) + { + /* Split read in smaller pieces */ + size_t count = MIN(size, (size_t)CHUNCK_SIZE); + if ((wr_len = eeprom_write(blk, idx, buf, offset, count)) != 0) + { + if (eeprom_readDirect(blk, idx, verify_buf, offset, count) != wr_len) + { + LOG_ERR("Verify read fail.\n"); + return 0; + } + else if (memcmp(buf, verify_buf, wr_len) != 0) + { + LOG_ERR("Data mismatch!\n"); + continue; + } + } + else + { + LOG_ERR("Write fail.\n"); + return 0; + } + size -= wr_len; + len += wr_len; + buf = ((const char *)buf) + wr_len; + } + return len; + } + } -/** - * Initialize TWI module. - */ -void eeprom_init(void) + return 0; +} + +static int kblockEeprom_dummy(UNUSED_ARG(struct KBlock *,b)) { - twi_init(); + return 0; } -#ifdef _DEBUG +static const KBlockVTable eeprom_unbuffered_vt = +{ + .readDirect = eeprom_readDirect, + .writeDirect = eeprom_writeDirect, -#include + .error = kblockEeprom_dummy, + .clearerr = (kblock_clearerr_t)kblockEeprom_dummy, +}; -void eeprom_test(void) +/** + * Initialize EEPROM module. + * \param eep is the Kblock context. + * \param type is the eeprom device we want to initialize (\see EepromType) + * \param i2c context for i2c channel + * \param addr is the i2c devide address (usually pins A0, A1, A2). + * \param verify enable the write check. + */ +void eeprom_init_5(Eeprom *eep, I2c *i2c, EepromType type, e2dev_addr_t addr, bool verify) { - static const char magic[14] = "Humpty Dumpty"; - char buf[sizeof magic]; - size_t i; + ASSERT(type < EEPROM_CNT); - // Write something to EEPROM using unaligned sequential writes - for (i = 0; i < 42; ++i) - { - wdt_reset(); - eeprom_write(i * sizeof magic, magic, sizeof magic); - } + memset(eep, 0, sizeof(*eep)); + DB(eep->blk.priv.type = KBT_EEPROM); - // Read back with single-byte reads - for (i = 0; i < 42 * sizeof magic; ++i) - { - wdt_reset(); - eeprom_read(i, buf, 1); - kprintf("EEPROM byte read: %c (%d)\n", buf[0], buf[0]); - ASSERT(buf[0] == magic[i % sizeof magic]); - } + eep->type = type; + eep->addr = addr; + eep->i2c = i2c; + eep->verify = verify; - // Read back again using sequential reads - for (i = 0; i < 42; ++i) - { - wdt_reset(); - memset(buf, 0, sizeof buf); - eeprom_read(i * sizeof magic, buf, sizeof magic); - kprintf("EEPROM seq read @ 0x%x: '%s'\n", i * sizeof magic, buf); - ASSERT(memcmp(buf, magic, sizeof magic) == 0); - } + eep->blk.blk_size = mem_info[type].blk_size; + eep->blk.blk_cnt = mem_info[type].e2_size / mem_info[type].blk_size; + eep->blk.priv.flags |= KB_PARTIAL_WRITE; + eep->blk.priv.vt = &eeprom_unbuffered_vt; } -#endif // _DEBUG +