Add definitions for 24C32, 24C64 and 24C128 eeproms. Use the right eeprom type.
[bertos.git] / bertos / drv / eeprom.c
index 835c1cc5ec6d4c5b48a864ccf1b3785e99db6eee..82fb9590f6126524ce8d63a76290df2ab1f706b9 100644 (file)
  * 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 <aleph@develer.com>
- * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Bernie Innocenti <bernie@codewiz.org>
  */
 
 #include "eeprom.h"
 
-#include <cfg/cfg_eeprom.h>  // CONFIG_EEPROM_VERIFY
-#include <cfg/macros.h>  // 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 <cfg/log.h>
 #include <cfg/debug.h>
+#include <cfg/macros.h>  // MIN()
 
 #include <cpu/attr.h>
-#include CPU_HEADER(twi)
-
-#include <drv/wdt.h>
 
-#include <mware/byteorder.h> // cpu_to_be16()
+#include <drv/i2c.h>
 
 #include <string.h>  // 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
  */
 /**
  * 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 <string.h>
+       .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
+