eeprom.c

Go to the documentation of this file.
00001 
00039 #include "eeprom.h"
00040 
00041 #include <cfg/macros.h>  // MIN()
00042 #include <cfg/debug.h>
00043 #include <cfg/module.h>  // MOD_CHECK()
00044 
00045 #include <cpu/attr.h>
00046 #include <drv/i2c.h>
00047 
00048 #include <drv/wdt.h>
00049 
00050 #include <cpu/byteorder.h> // cpu_to_be16()
00051 
00052 #include <string.h>  // memset()
00053 
00057 #define EEPROM_ID  0xA0
00058 
00062 #define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)((x) & 0x07)) << 1))
00063 
00064 
00068 static const EepromInfo mem_info[] =
00069 {
00070     {
00071         /* 24XX08 */
00072         .has_dev_addr = false,
00073         .blk_size = 0x10,
00074         .e2_size = 0x400,
00075     },
00076     {
00077         /* 24XX16 */
00078         .has_dev_addr = false,
00079         .blk_size = 0x10,
00080         .e2_size = 0x800,
00081     },
00082     {
00083         /* 24XX256 */
00084         .has_dev_addr = true,
00085         .blk_size = 0x40,
00086         .e2_size = 0x8000,
00087     },
00088     {
00089         /* 24XX512 */
00090         .has_dev_addr = true,
00091         .blk_size = 0x80,
00092         .e2_size = 0x10000,
00093     },
00094     {
00095         /* 24XX1024 */
00096         .has_dev_addr = true,
00097         .blk_size = 0x100,
00098         .e2_size = 0x20000,
00099     },
00100 
00101     /* Add other memories here */
00102 };
00103 
00104 STATIC_ASSERT(countof(mem_info) == EEPROM_CNT);
00105 
00106 
00111 static size_t eeprom_writeRaw(struct KFile *_fd, const void *buf, size_t size)
00112 {
00113     Eeprom *fd = EEPROM_CAST(_fd);
00114     e2dev_addr_t dev_addr;
00115     uint8_t addr_buf[2];
00116     uint8_t addr_len;
00117     size_t wr_len = 0;
00118 
00119     e2blk_size_t blk_size = mem_info[fd->type].blk_size;
00120 
00121     STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
00122 
00123     /* clamp size to memory limit (otherwise may roll back) */
00124     ASSERT(_fd->seek_pos + (kfile_off_t)size <= (kfile_off_t)_fd->size);
00125     size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
00126 
00127     if (mem_info[fd->type].has_dev_addr)
00128     {
00129         dev_addr = fd->addr;
00130         addr_len = 2;
00131     }
00132     else
00133     {
00134         dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
00135         addr_len = 1;
00136     }
00137 
00138     while (size)
00139     {
00140         /*
00141          * Split write in multiple sequential mode operations that
00142          * don't cross page boundaries.
00143          */
00144         size_t count = MIN(size, (size_t)(blk_size - (fd->fd.seek_pos & (blk_size - 1))));
00145 
00146         if (mem_info[fd->type].has_dev_addr)
00147         {
00148             addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
00149             addr_buf[1] = (fd->fd.seek_pos & 0xFF);
00150         }
00151         else
00152         {
00153             dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
00154             addr_buf[0] = (fd->fd.seek_pos & 0xFF);
00155         }
00156 
00157 
00158         if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
00159             && i2c_send(addr_buf, addr_len)
00160             && i2c_send(buf, count)))
00161         {
00162             i2c_stop();
00163             return wr_len;
00164         }
00165 
00166         i2c_stop();
00167 
00168         /* Update count and addr for next operation */
00169         size -= count;
00170         fd->fd.seek_pos += count;
00171         buf = ((const char *)buf) + count;
00172         wr_len += count;
00173     }
00174 
00175     return wr_len;
00176 }
00177 
00184 static size_t eeprom_writeVerify(struct KFile *_fd, const void *_buf, size_t size)
00185 {
00186     Eeprom *fd = EEPROM_CAST(_fd);
00187     int retries = 5;
00188     size_t wr_len = 0;
00189 
00190     while (retries--)
00191     {
00192         wr_len = eeprom_writeRaw(_fd, _buf, size);
00193         /* rewind to verify what we have just written */
00194         kfile_seek(_fd, -(kfile_off_t)wr_len, KSM_SEEK_CUR);
00195         if (wr_len == size
00196          && eeprom_verify(fd, _buf, wr_len))
00197         {
00198             /* Forward to go after what we have written*/
00199             kfile_seek(_fd, wr_len, KSM_SEEK_CUR);
00200             return wr_len;
00201         }
00202     }
00203     return wr_len;
00204 }
00205 
00206 
00213 static size_t eeprom_read(struct KFile *_fd, void *_buf, size_t size)
00214 {
00215     Eeprom *fd = EEPROM_CAST(_fd);
00216     uint8_t addr_buf[2];
00217     uint8_t addr_len;
00218     size_t rd_len = 0;
00219     uint8_t *buf = (uint8_t *)_buf;
00220 
00221     STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
00222 
00223     /* clamp size to memory limit (otherwise may roll back) */
00224     ASSERT(_fd->seek_pos + (kfile_off_t)size <= (kfile_off_t)_fd->size);
00225     size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
00226 
00227     e2dev_addr_t dev_addr;
00228     if (mem_info[fd->type].has_dev_addr)
00229     {
00230         dev_addr = fd->addr;
00231         addr_len = 2;
00232         addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
00233         addr_buf[1] = (fd->fd.seek_pos & 0xFF);
00234     }
00235     else
00236     {
00237         dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
00238         addr_len = 1;
00239         addr_buf[0] = (fd->fd.seek_pos & 0xFF);
00240     }
00241 
00242 
00243     if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
00244        && i2c_send(addr_buf, addr_len)
00245        && i2c_start_r(EEPROM_ADDR(dev_addr))))
00246     {
00247         i2c_stop();
00248         return 0;
00249     }
00250 
00251     while (size--)
00252     {
00253         /*
00254          * The last byte read does not have an ACK
00255          * to stop communication.
00256          */
00257         int c = i2c_get(size);
00258 
00259         if (c == EOF)
00260             break;
00261 
00262         *buf++ = c;
00263         fd->fd.seek_pos++;
00264         rd_len++;
00265     }
00266 
00267     i2c_stop();
00268     return rd_len;
00269 }
00270 
00278 bool eeprom_verify(Eeprom *fd, const void *buf, size_t count)
00279 {
00280     uint8_t verify_buf[16];
00281     bool result = true;
00282 
00283     /* Save seek position */
00284     kfile_off_t prev_seek = fd->fd.seek_pos;
00285 
00286     while (count && result)
00287     {
00288         /* Split read in smaller pieces */
00289         size_t size = MIN(count, sizeof verify_buf);
00290 
00291         /* Read back buffer */
00292         if (eeprom_read(&fd->fd, verify_buf, size))
00293         {
00294             if (memcmp(buf, verify_buf, size) != 0)
00295             {
00296                 TRACEMSG("Data mismatch!");
00297                 result = false;
00298             }
00299         }
00300         else
00301         {
00302             TRACEMSG("Read error!");
00303             result = false;
00304         }
00305 
00306         /* Update count and addr for next operation */
00307         count -= size;
00308         buf = ((const char *)buf) + size;
00309     }
00310 
00311     /* Restore previous seek position */
00312     fd->fd.seek_pos = prev_seek;
00313     return result;
00314 }
00315 
00324 bool eeprom_erase(Eeprom *fd, e2addr_t addr, e2_size_t count)
00325 {
00326     e2blk_size_t blk_size = mem_info[fd->type].blk_size;
00327     uint8_t buf[blk_size];
00328     kfile_off_t prev_off = fd->fd.seek_pos;
00329     bool res = true;
00330     size_t size;
00331 
00332     memset(buf, 0xFF, blk_size);
00333 
00334 
00335     kfile_seek(&fd->fd, addr, KSM_SEEK_SET);
00336 
00337     /*
00338      * Optimization: this first write id used to realign
00339      * current address to block boundaries.
00340      */
00341 
00342     wdt_reset();
00343     size = MIN(count, (e2_size_t)(blk_size - (addr & (blk_size - 1))));
00344     if (kfile_write(&fd->fd, buf, size) != size)
00345     {
00346         fd->fd.seek_pos = prev_off;
00347         return false;
00348     }
00349     count -= size;
00350 
00351     /* Clear all */
00352     while (count)
00353     {
00354         /* Long operation, reset watchdog */
00355         wdt_reset();
00356 
00357         size = MIN(count, (e2_size_t)sizeof buf);
00358         if (kfile_write(&fd->fd, buf, size) != size)
00359         {
00360             res = false;
00361             break;
00362         }
00363 
00364         count -= size;
00365     }
00366     fd->fd.seek_pos = prev_off;
00367     return res;
00368 }
00369 
00370 
00378 void eeprom_init(Eeprom *fd, EepromType type, e2dev_addr_t addr, bool verify)
00379 {
00380     MOD_CHECK(i2c);
00381     ASSERT(type < EEPROM_CNT);
00382 
00383     memset(fd, 0, sizeof(*fd));
00384     DB(fd->fd._type = KFT_EEPROM);
00385 
00386     fd->type = type;
00387     fd->addr = addr;
00388     fd->fd.size = mem_info[fd->type].e2_size;
00389 
00390     // Setup eeprom programming functions.
00391     fd->fd.read = eeprom_read;
00392     if (verify)
00393         fd->fd.write = eeprom_writeVerify;
00394     else
00395         fd->fd.write = eeprom_writeRaw;
00396     fd->fd.close = kfile_genericClose;
00397 
00398     fd->fd.seek = kfile_genericSeek;
00399 }