Add support for 24XX08 eeproms, signed-off by Giovanni Casano.
[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 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 #warning TODO:Test and complete this module for arm platform.
42 #if !CPU_ARM
43
44 #include <cfg/macros.h>  // MIN()
45 #include <cfg/debug.h>
46 #include <cfg/module.h>  // MOD_CHECK()
47
48 #include <cpu/attr.h>
49 #include <drv/i2c.h>
50
51 #include <drv/wdt.h>
52
53 #include <cpu/byteorder.h> // cpu_to_be16()
54
55 #include <string.h>  // memset()
56
57 /**
58  * EEPROM ID code
59  */
60 #define EEPROM_ID  0xA0
61
62 /**
63  * This macros form the correct slave address for EEPROMs
64  */
65 #define EEPROM_ADDR(x) (EEPROM_ID | (((uint8_t)((x) & 0x07)) << 1))
66
67
68 /**
69  * Array used to describe EEPROM memory devices currently supported.
70  */
71 static const EepromInfo mem_info[] =
72 {
73         {
74                 /* 24XX08 */
75                 .has_dev_addr = false,
76                 .blk_size = 0x10,
77                 .e2_size = 0x400,
78         },
79         {
80                 /* 24XX16 */
81                 .has_dev_addr = false,
82                 .blk_size = 0x10,
83                 .e2_size = 0x800,
84         },
85         {
86                 /* 24XX256 */
87                 .has_dev_addr = true,
88                 .blk_size = 0x40,
89                 .e2_size = 0x8000,
90         },
91         {
92                 /* 24XX512 */
93                 .has_dev_addr = true,
94                 .blk_size = 0x80,
95                 .e2_size = 0x10000,
96         },
97         /* Add other memories here */
98 };
99
100 STATIC_ASSERT(countof(mem_info) == EEPROM_CNT);
101
102
103 /**
104  * Copy \a size bytes from buffer \a buf to
105  * eeprom.
106  */
107 static size_t eeprom_writeRaw(struct KFile *_fd, const void *buf, size_t size)
108 {
109         Eeprom *fd = EEPROM_CAST(_fd);
110         e2dev_addr_t dev_addr;
111         uint8_t addr_buf[2];
112         uint8_t addr_len;
113         size_t wr_len = 0;
114
115         e2blk_size_t blk_size = mem_info[fd->type].blk_size;
116
117         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
118
119         /* clamp size to memory limit (otherwise may roll back) */
120         ASSERT(_fd->seek_pos + size <= (kfile_off_t)_fd->size);
121         size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
122
123         if (mem_info[fd->type].has_dev_addr)
124         {
125                 dev_addr = fd->addr;
126                 addr_len = 2;
127         }
128         else
129         {
130                 dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
131                 addr_len = 1;
132         }
133
134         while (size)
135         {
136                 /*
137                  * Split write in multiple sequential mode operations that
138                  * don't cross page boundaries.
139                  */
140                 size_t count = MIN(size, (size_t)(blk_size - (fd->fd.seek_pos & (blk_size - 1))));
141
142                 if (mem_info[fd->type].has_dev_addr)
143                 {
144                         addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
145                         addr_buf[1] = (fd->fd.seek_pos & 0xFF);
146                 }
147                 else
148                 {
149                         dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
150                         addr_buf[0] = (fd->fd.seek_pos & 0xFF);
151                 }
152
153
154                 if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
155                         && i2c_send(addr_buf, addr_len)
156                         && i2c_send(buf, count)))
157                 {
158                         i2c_stop();
159                         return wr_len;
160                 }
161
162                 i2c_stop();
163
164                 /* Update count and addr for next operation */
165                 size -= count;
166                 fd->fd.seek_pos += count;
167                 buf = ((const char *)buf) + count;
168                 wr_len += count;
169         }
170
171         return wr_len;
172 }
173
174 /**
175  * Copy \a size bytes from buffer \a _buf to
176  * eeprom.
177  * \note Writes are verified and if buffer content
178  *       is not matching we retry 5 times max.
179  */
180 static size_t eeprom_writeVerify(struct KFile *_fd, const void *_buf, size_t size)
181 {
182         Eeprom *fd = EEPROM_CAST(_fd);
183         int retries = 5;
184         size_t wr_len = 0;
185
186         while (retries--)
187         {
188                 wr_len = eeprom_writeRaw(_fd, _buf, size);
189                 /* rewind to verify what we have just written */
190                 kfile_seek(_fd, -(kfile_off_t)wr_len, KSM_SEEK_CUR);
191                 if (wr_len == size
192                  && eeprom_verify(fd, _buf, wr_len))
193                 {
194                         /* Forward to go after what we have written*/
195                         kfile_seek(_fd, wr_len, KSM_SEEK_CUR);
196                         return wr_len;
197                 }
198         }
199         return wr_len;
200 }
201
202
203 /**
204  * Copy \a size bytes
205  * from eeprom to RAM to buffer \a _buf.
206  *
207  * \return the number of bytes read.
208  */
209 static size_t eeprom_read(struct KFile *_fd, void *_buf, size_t size)
210 {
211         Eeprom *fd = EEPROM_CAST(_fd);
212         uint8_t addr_buf[2];
213         uint8_t addr_len;
214         size_t rd_len = 0;
215         uint8_t *buf = (uint8_t *)_buf;
216
217         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
218
219         /* clamp size to memory limit (otherwise may roll back) */
220         ASSERT(_fd->seek_pos + size <= (kfile_off_t)_fd->size);
221         size = MIN((kfile_off_t)size, _fd->size - _fd->seek_pos);
222
223         e2dev_addr_t dev_addr;
224         if (mem_info[fd->type].has_dev_addr)
225         {
226                 dev_addr = fd->addr;
227                 addr_len = 2;
228                 addr_buf[0] = (fd->fd.seek_pos >> 8) & 0xFF;
229                 addr_buf[1] = (fd->fd.seek_pos & 0xFF);
230         }
231         else
232         {
233                 dev_addr = (e2dev_addr_t)((fd->fd.seek_pos >> 8) & 0x07);
234                 addr_len = 1;
235                 addr_buf[0] = (fd->fd.seek_pos & 0xFF);
236         }
237
238
239         if (!(i2c_start_w(EEPROM_ADDR(dev_addr))
240            && i2c_send(addr_buf, addr_len)
241            && i2c_start_r(EEPROM_ADDR(dev_addr))))
242         {
243                 i2c_stop();
244                 return 0;
245         }
246
247         while (size--)
248         {
249                 /*
250                  * The last byte read does not have an ACK
251                  * to stop communication.
252                  */
253                 int c = i2c_get(size);
254
255                 if (c == EOF)
256                         break;
257
258                 *buf++ = c;
259                 fd->fd.seek_pos++;
260                 rd_len++;
261         }
262
263         i2c_stop();
264         return rd_len;
265 }
266
267 /**
268  * Check that the contents of an EEPROM range
269  * match with a provided data buffer.
270  *
271  * \return true on success.
272  * \note Seek position of \a fd will not change.
273  */
274 bool eeprom_verify(Eeprom *fd, const void *buf, size_t count)
275 {
276         uint8_t verify_buf[16];
277         bool result = true;
278
279         /* Save seek position */
280         kfile_off_t prev_seek = fd->fd.seek_pos;
281
282         while (count && result)
283         {
284                 /* Split read in smaller pieces */
285                 size_t size = MIN(count, sizeof verify_buf);
286
287                 /* Read back buffer */
288                 if (eeprom_read(&fd->fd, verify_buf, size))
289                 {
290                         if (memcmp(buf, verify_buf, size) != 0)
291                         {
292                                 TRACEMSG("Data mismatch!");
293                                 result = false;
294                         }
295                 }
296                 else
297                 {
298                         TRACEMSG("Read error!");
299                         result = false;
300                 }
301
302                 /* Update count and addr for next operation */
303                 count -= size;
304                 buf = ((const char *)buf) + size;
305         }
306
307         /* Restore previous seek position */
308         fd->fd.seek_pos = prev_seek;
309         return result;
310 }
311
312 /**
313  * Erase specified part of eeprom, writing 0xFF.
314  *
315  * \a addr   starting address
316  * \a count  length of block to erase
317  * \note Seek position is unchanged.
318  * \return true if ok, false otherwise.
319  */
320 bool eeprom_erase(Eeprom *fd, e2addr_t addr, e2_size_t count)
321 {
322         e2blk_size_t blk_size = mem_info[fd->type].blk_size;
323         uint8_t buf[blk_size];
324         kfile_off_t prev_off = fd->fd.seek_pos;
325         bool res = true;
326         size_t size;
327
328         memset(buf, 0xFF, blk_size);
329
330
331         kfile_seek(&fd->fd, addr, KSM_SEEK_SET);
332
333         /*
334          * Optimization: this first write id used to realign
335          * current address to block boundaries.
336          */
337
338         wdt_reset();
339         size = MIN(count, (e2_size_t)(blk_size - (addr & (blk_size - 1))));
340         if (kfile_write(&fd->fd, buf, size) != size)
341         {
342                 fd->fd.seek_pos = prev_off;
343                 return false;
344         }
345         count -= size;
346
347         /* Clear all */
348         while (count)
349         {
350                 /* Long operation, reset watchdog */
351                 wdt_reset();
352
353                 size = MIN(count, (e2_size_t)sizeof buf);
354                 if (kfile_write(&fd->fd, buf, size) != size)
355                 {
356                         res = false;
357                         break;
358                 }
359
360                 count -= size;
361         }
362         fd->fd.seek_pos = prev_off;
363         return res;
364 }
365
366
367 /**
368  * Initialize EEPROM module.
369  * \a fd is the Kfile context.
370  * \a type is the eeprom device we want to initialize (\see EepromType)
371  * \a addr is the i2c devide address (usually pins A0, A1, A2).
372  * \a verify is true if you want that every write operation will be verified.
373  */
374 void eeprom_init(Eeprom *fd, EepromType type, e2dev_addr_t addr, bool verify)
375 {
376         MOD_CHECK(i2c);
377         ASSERT(type < EEPROM_CNT);
378
379         memset(fd, 0, sizeof(*fd));
380         DB(fd->fd._type = KFT_EEPROM);
381
382         fd->type = type;
383         fd->addr = addr;
384         fd->fd.size = mem_info[fd->type].e2_size;
385
386         // Setup eeprom programming functions.
387         fd->fd.read = eeprom_read;
388         if (verify)
389                 fd->fd.write = eeprom_writeVerify;
390         else
391                 fd->fd.write = eeprom_writeRaw;
392         fd->fd.close = kfile_genericClose;
393
394         fd->fd.seek = kfile_genericSeek;
395 }
396
397 #endif