Add deprecared i2c api switch for eeprom. Silent warning.
[bertos.git] / bertos / drv / eeprom.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2003, 2004, 2005, 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Driver for the 24xx16 and 24xx256 I2C EEPROMS (implementation)
34  *
35  * \author Stefano Fedrigo <aleph@develer.com>
36  * \author Bernie Innocenti <bernie@codewiz.org>
37  */
38
39 #include "eeprom.h"
40
41 #include "cfg/cfg_i2c.h"
42
43 #include <cfg/macros.h>  // MIN()
44 #include <cfg/debug.h>
45 #include <cfg/module.h>  // MOD_CHECK()
46
47 #include <cpu/attr.h>
48 #include <drv/i2c.h>
49
50 #include <drv/wdt.h>
51
52 #include <cpu/byteorder.h> // cpu_to_be16()
53
54 #include <string.h>  // memset()
55
56 /**
57  * EEPROM ID code
58  */
59 #define EEPROM_ID  0xA0
60
61 /**
62  * This macros form the correct slave address for EEPROMs
63  */
64 #define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)((x) & 0x07)) << 1))
65
66
67 /**
68  * Array used to describe EEPROM memory devices currently supported.
69  */
70 static const EepromInfo mem_info[] =
71 {
72         {
73                 /* 24XX08 */
74                 .has_dev_addr = false,
75                 .blk_size = 0x10,
76                 .e2_size = 0x400,
77         },
78         {
79                 /* 24XX16 */
80                 .has_dev_addr = false,
81                 .blk_size = 0x10,
82                 .e2_size = 0x800,
83         },
84         {
85                 /* 24XX256 */
86                 .has_dev_addr = true,
87                 .blk_size = 0x40,
88                 .e2_size = 0x8000,
89         },
90         {
91                 /* 24XX512 */
92                 .has_dev_addr = true,
93                 .blk_size = 0x80,
94                 .e2_size = 0x10000,
95         },
96         {
97                 /* 24XX1024 */
98                 .has_dev_addr = true,
99                 .blk_size = 0x100,
100                 .e2_size = 0x20000,
101         },
102
103         /* Add other memories here */
104 };
105
106 STATIC_ASSERT(countof(mem_info) == EEPROM_CNT);
107
108 #if !CONFIG_I2C_DISABLE_OLD_API
109
110 /**
111  * Copy \a size bytes from buffer \a buf to
112  * eeprom.
113  */
114 static size_t eeprom_writeRaw(struct KFile *_fd, const void *buf, size_t size)
115 {
116         Eeprom *fd = EEPROM_CAST(_fd);
117         e2dev_addr_t dev_addr;
118         uint8_t addr_buf[2];
119         uint8_t addr_len;
120         size_t wr_len = 0;
121
122         e2blk_size_t blk_size = mem_info[fd->type].blk_size;
123
124         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
125
126         /* clamp size to memory limit (otherwise may roll back) */
127         ASSERT(_fd->seek_pos + (kfile_off_t)size <= (kfile_off_t)_fd->size);
128         size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
129
130         if (mem_info[fd->type].has_dev_addr)
131         {
132                 dev_addr = fd->addr;
133                 addr_len = 2;
134         }
135         else
136         {
137                 dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
138                 addr_len = 1;
139         }
140
141         while (size)
142         {
143                 /*
144                  * Split write in multiple sequential mode operations that
145                  * don't cross page boundaries.
146                  */
147                 size_t count = MIN(size, (size_t)(blk_size - (fd->fd.seek_pos & (blk_size - 1))));
148
149                 if (mem_info[fd->type].has_dev_addr)
150                 {
151                         addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
152                         addr_buf[1] = (fd->fd.seek_pos & 0xFF);
153                 }
154                 else
155                 {
156                         dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
157                         addr_buf[0] = (fd->fd.seek_pos & 0xFF);
158                 }
159
160
161                 if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
162                         && i2c_send(addr_buf, addr_len)
163                         && i2c_send(buf, count)))
164                 {
165                         i2c_stop();
166                         return wr_len;
167                 }
168
169                 i2c_stop();
170
171                 /* Update count and addr for next operation */
172                 size -= count;
173                 fd->fd.seek_pos += count;
174                 buf = ((const char *)buf) + count;
175                 wr_len += count;
176         }
177
178         return wr_len;
179 }
180
181 /**
182  * Copy \a size bytes from buffer \a _buf to
183  * eeprom.
184  * \note Writes are verified and if buffer content
185  *       is not matching we retry 5 times max.
186  */
187 static size_t eeprom_writeVerify(struct KFile *_fd, const void *_buf, size_t size)
188 {
189         Eeprom *fd = EEPROM_CAST(_fd);
190         int retries = 5;
191         size_t wr_len = 0;
192
193         while (retries--)
194         {
195                 wr_len = eeprom_writeRaw(_fd, _buf, size);
196                 /* rewind to verify what we have just written */
197                 kfile_seek(_fd, -(kfile_off_t)wr_len, KSM_SEEK_CUR);
198                 if (wr_len == size
199                  && eeprom_verify(fd, _buf, wr_len))
200                 {
201                         /* Forward to go after what we have written*/
202                         kfile_seek(_fd, wr_len, KSM_SEEK_CUR);
203                         return wr_len;
204                 }
205         }
206         return wr_len;
207 }
208
209
210 /**
211  * Copy \a size bytes
212  * from eeprom to RAM to buffer \a _buf.
213  *
214  * \return the number of bytes read.
215  */
216 static size_t eeprom_read(struct KFile *_fd, void *_buf, size_t size)
217 {
218         Eeprom *fd = EEPROM_CAST(_fd);
219         uint8_t addr_buf[2];
220         uint8_t addr_len;
221         size_t rd_len = 0;
222         uint8_t *buf = (uint8_t *)_buf;
223
224         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
225
226         /* clamp size to memory limit (otherwise may roll back) */
227         ASSERT(_fd->seek_pos + (kfile_off_t)size <= (kfile_off_t)_fd->size);
228         size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
229
230         e2dev_addr_t dev_addr;
231         if (mem_info[fd->type].has_dev_addr)
232         {
233                 dev_addr = fd->addr;
234                 addr_len = 2;
235                 addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
236                 addr_buf[1] = (fd->fd.seek_pos & 0xFF);
237         }
238         else
239         {
240                 dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
241                 addr_len = 1;
242                 addr_buf[0] = (fd->fd.seek_pos & 0xFF);
243         }
244
245
246         if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
247            && i2c_send(addr_buf, addr_len)
248            && i2c_start_r(EEPROM_ADDR(dev_addr))))
249         {
250                 i2c_stop();
251                 return 0;
252         }
253
254
255         if (i2c_recv(buf, size))
256         {
257                 fd->fd.seek_pos += size;
258                 rd_len += size;
259         }
260
261         i2c_stop();
262         return rd_len;
263 }
264
265 /**
266  * Check that the contents of an EEPROM range
267  * match with a provided data buffer.
268  *
269  * \return true on success.
270  * \note Seek position of \a fd will not change.
271  */
272 bool eeprom_verify(Eeprom *fd, const void *buf, size_t count)
273 {
274         uint8_t verify_buf[16];
275         bool result = true;
276
277         /* Save seek position */
278         kfile_off_t prev_seek = fd->fd.seek_pos;
279
280         while (count && result)
281         {
282                 /* Split read in smaller pieces */
283                 size_t size = MIN(count, sizeof verify_buf);
284
285                 /* Read back buffer */
286                 if (eeprom_read(&fd->fd, verify_buf, size))
287                 {
288                         if (memcmp(buf, verify_buf, size) != 0)
289                         {
290                                 TRACEMSG("Data mismatch!");
291                                 result = false;
292                         }
293                 }
294                 else
295                 {
296                         TRACEMSG("Read error!");
297                         result = false;
298                 }
299
300                 /* Update count and addr for next operation */
301                 count -= size;
302                 buf = ((const char *)buf) + size;
303         }
304
305         /* Restore previous seek position */
306         fd->fd.seek_pos = prev_seek;
307         return result;
308 }
309
310 /**
311  * Erase specified part of eeprom, writing 0xFF.
312  *
313  * \a addr   starting address
314  * \a count  length of block to erase
315  * \note Seek position is unchanged.
316  * \return true if ok, false otherwise.
317  */
318 bool eeprom_erase(Eeprom *fd, e2addr_t addr, e2_size_t count)
319 {
320         e2blk_size_t blk_size = mem_info[fd->type].blk_size;
321         uint8_t buf[blk_size];
322         kfile_off_t prev_off = fd->fd.seek_pos;
323         bool res = true;
324         size_t size;
325
326         memset(buf, 0xFF, blk_size);
327
328
329         kfile_seek(&fd->fd, addr, KSM_SEEK_SET);
330
331         /*
332          * Optimization: this first write id used to realign
333          * current address to block boundaries.
334          */
335
336         wdt_reset();
337         size = MIN(count, (e2_size_t)(blk_size - (addr & (blk_size - 1))));
338         if (kfile_write(&fd->fd, buf, size) != size)
339         {
340                 fd->fd.seek_pos = prev_off;
341                 return false;
342         }
343         count -= size;
344
345         /* Clear all */
346         while (count)
347         {
348                 /* Long operation, reset watchdog */
349                 wdt_reset();
350
351                 size = MIN(count, (e2_size_t)sizeof buf);
352                 if (kfile_write(&fd->fd, buf, size) != size)
353                 {
354                         res = false;
355                         break;
356                 }
357
358                 count -= size;
359         }
360         fd->fd.seek_pos = prev_off;
361         return res;
362 }
363
364
365 /**
366  * Initialize EEPROM module.
367  * \a fd is the Kfile context.
368  * \a type is the eeprom device we want to initialize (\see EepromType)
369  * \a addr is the i2c devide address (usually pins A0, A1, A2).
370  * \a verify is true if you want that every write operation will be verified.
371  */
372 void eeprom_init(Eeprom *fd, EepromType type, e2dev_addr_t addr, bool verify)
373 {
374         MOD_CHECK(i2c);
375         ASSERT(type < EEPROM_CNT);
376
377         memset(fd, 0, sizeof(*fd));
378         DB(fd->fd._type = KFT_EEPROM);
379
380         fd->type = type;
381         fd->addr = addr;
382         fd->fd.size = mem_info[fd->type].e2_size;
383
384         // Setup eeprom programming functions.
385         fd->fd.read = eeprom_read;
386         if (verify)
387                 fd->fd.write = eeprom_writeVerify;
388         else
389                 fd->fd.write = eeprom_writeRaw;
390         fd->fd.close = kfile_genericClose;
391
392         fd->fd.seek = kfile_genericSeek;
393 }
394
395 #endif /* !CONFIG_I2C_DISABLE_OLD_API */