eeprom.c

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