Clean up. Rename variable.
[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 /**
109  * Check that the contents of an EEPROM range
110  * match with a provided data buffer.
111  *
112  * \return true on success.
113  * \note Seek position of \a fd will not change.
114  */
115 bool eeprom_verify(Eeprom *fd, const void *buf, size_t count)
116 {
117         uint8_t verify_buf[16];
118         bool result = true;
119
120         /* Save seek position */
121         kfile_off_t prev_seek = fd->fd.seek_pos;
122
123         while (count && result)
124         {
125                 /* Split read in smaller pieces */
126                 size_t size = MIN(count, sizeof verify_buf);
127
128                 /* Read back buffer */
129                 if (eeprom_read(&fd->fd, verify_buf, size))
130                 {
131                         if (memcmp(buf, verify_buf, size) != 0)
132                         {
133                                 TRACEMSG("Data mismatch!");
134                                 result = false;
135                         }
136                 }
137                 else
138                 {
139                         TRACEMSG("Read error!");
140                         result = false;
141                 }
142
143                 /* Update count and addr for next operation */
144                 count -= size;
145                 buf = ((const char *)buf) + size;
146         }
147
148         /* Restore previous seek position */
149         fd->fd.seek_pos = prev_seek;
150         return result;
151 }
152
153 /**
154  * Erase specified part of eeprom, writing 0xFF.
155  *
156  * \a addr   starting address
157  * \a count  length of block to erase
158  * \note Seek position is unchanged.
159  * \return true if ok, false otherwise.
160  */
161 bool eeprom_erase(Eeprom *fd, e2addr_t addr, e2_size_t count)
162 {
163         e2blk_size_t blk_size = mem_info[fd->type].blk_size;
164         uint8_t buf[blk_size];
165         kfile_off_t prev_off = fd->fd.seek_pos;
166         bool res = true;
167         size_t size;
168
169         memset(buf, 0xFF, blk_size);
170
171
172         kfile_seek(&fd->fd, addr, KSM_SEEK_SET);
173
174         /*
175          * Optimization: this first write id used to realign
176          * current address to block boundaries.
177          */
178
179         wdt_reset();
180         size = MIN(count, (e2_size_t)(blk_size - (addr & (blk_size - 1))));
181         if (kfile_write(&fd->fd, buf, size) != size)
182         {
183                 fd->fd.seek_pos = prev_off;
184                 return false;
185         }
186         count -= size;
187
188         /* Clear all */
189         while (count)
190         {
191                 /* Long operation, reset watchdog */
192                 wdt_reset();
193
194                 size = MIN(count, (e2_size_t)sizeof buf);
195                 if (kfile_write(&fd->fd, buf, size) != size)
196                 {
197                         res = false;
198                         break;
199                 }
200
201                 count -= size;
202         }
203         fd->fd.seek_pos = prev_off;
204         return res;
205 }
206
207
208 /**
209  * Initialize EEPROM module.
210  * \a fd is the Kfile context.
211  * \a type is the eeprom device we want to initialize (\see EepromType)
212  * \a addr is the i2c devide address (usually pins A0, A1, A2).
213  * \a verify is true if you want that every write operation will be verified.
214  */
215 void eeprom_init(Eeprom *fd, EepromType type, e2dev_addr_t addr, bool verify)
216 {
217         MOD_CHECK(i2c);
218         ASSERT(type < EEPROM_CNT);
219
220         memset(fd, 0, sizeof(*fd));
221         DB(fd->fd._type = KFT_EEPROM);
222
223         fd->type = type;
224         fd->addr = addr;
225         fd->fd.size = mem_info[fd->type].e2_size;
226
227         // Setup eeprom programming functions.
228         fd->fd.read = eeprom_read;
229         if (verify)
230                 fd->fd.write = eeprom_writeVerify;
231         else
232                 fd->fd.write = eeprom_writeRaw;
233         fd->fd.close = kfile_genericClose;
234
235         fd->fd.seek = kfile_genericSeek;
236 }
237
238
239 static size_t eeprom_writeDirect(KBlock *blk, block_idx_t idx, const void *buf, size_t offset, size_t size)
240 {
241         Eeprom *fd = EEPROM_CAST_KBLOCK(blk);
242         e2dev_addr_t dev_addr;
243         uint8_t addr_buf[2];
244         uint8_t addr_len;
245         uint32_t abs_addr = blk->blk_size * idx + offset;
246
247         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
248
249
250         /* clamp size to memory limit (otherwise may roll back) */
251         ASSERT(idx <= blk->blk_cnt);
252         size = MIN(size, blk->blk_size - offset);
253
254         if (mem_info[fd->type].has_dev_addr)
255         {
256                 dev_addr = fd->addr;
257                 addr_len = 2;
258         }
259         else
260         {
261                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
262                 addr_len = 1;
263         }
264
265         if (mem_info[fd->type].has_dev_addr)
266         {
267                 addr_buf[0] = (abs_addr >> 8) & 0xFF;
268                 addr_buf[1] = (abs_addr & 0xFF);
269         }
270         else
271         {
272                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
273                 addr_buf[0] = (abs_addr & 0xFF);
274         }
275
276         i2c_start_w(fd->i2c, EEPROM_ADDR(dev_addr),  addr_len + size, I2C_STOP);
277         i2c_write(fd->i2c, addr_buf, addr_len);
278         i2c_write(fd->i2c, buf, size);
279
280         if (i2c_error(fd->i2c))
281                 return 0;
282
283         return size;
284 }
285
286
287 static size_t eeprom_readDirect(struct KBlock *_blk, block_idx_t idx, void *_buf, size_t offset, size_t size)
288 {
289         Eeprom *blk = EEPROM_CAST_KBLOCK(_blk);
290         uint8_t addr_buf[2];
291         uint8_t addr_len;
292         size_t rd_len = 0;
293         uint8_t *buf = (uint8_t *)_buf;
294         uint32_t abs_addr = mem_info[blk->type].blk_size * idx + offset;
295
296         STATIC_ASSERT(countof(addr_buf) <= sizeof(e2addr_t));
297
298         /* clamp size to memory limit (otherwise may roll back) */
299         ASSERT(idx <= blk->blk.blk_cnt);
300         size = MIN(size, blk->blk.blk_size - offset);
301
302         e2dev_addr_t dev_addr;
303         if (mem_info[blk->type].has_dev_addr)
304         {
305                 dev_addr = blk->addr;
306                 addr_len = 2;
307                 addr_buf[0] = (abs_addr >> 8) & 0xFF;
308                 addr_buf[1] = (abs_addr & 0xFF);
309         }
310         else
311         {
312                 dev_addr = (e2dev_addr_t)((abs_addr >> 8) & 0x07);
313                 addr_len = 1;
314                 addr_buf[0] = (abs_addr & 0xFF);
315         }
316
317
318         i2c_start_w(blk->i2c, EEPROM_ADDR(dev_addr),  addr_len, I2C_NOSTOP);
319         i2c_write(blk->i2c, addr_buf, addr_len);
320
321         i2c_start_r(blk->i2c, EEPROM_ADDR(dev_addr), size, I2C_STOP);
322         i2c_read(blk->i2c, buf, size);
323
324         if (i2c_error(blk->i2c))
325                    return rd_len;
326
327         rd_len += size;
328
329         return rd_len;
330 }
331
332 static int kblockEeprom_dummy(UNUSED_ARG(struct KBlock *,b))
333 {
334         return 0;
335 }
336
337
338 static const KBlockVTable eeprom_unbuffered_vt =
339 {
340         .readDirect = eeprom_readDirect,
341         .writeDirect = eeprom_writeDirect,
342
343         .error = kblockEeprom_dummy,
344         .clearerr = (kblock_clearerr_t)kblockEeprom_dummy,
345 };
346
347 /**
348  * Initialize EEPROM module.
349  * \param b is the Kblock context.
350  * \param type is the eeprom device we want to initialize (\see EepromType)
351  * \param i2c context for i2c channel
352  * \param addr is the i2c devide address (usually pins A0, A1, A2).
353  */
354 void eeprom_init_5(Eeprom *blk, I2c *i2c, EepromType type, e2dev_addr_t addr, bool verify)
355 {
356         ASSERT(type < EEPROM_CNT);
357
358         memset(blk, 0, sizeof(*blk));
359         DB(blk->blk.priv.type = KBT_EEPROM);
360
361         blk->type = type;
362         blk->addr = addr;
363         blk->i2c = i2c;
364
365         blk->blk.blk_size = mem_info[type].blk_size;
366         blk->blk.blk_cnt = mem_info[type].e2_size / mem_info[type].blk_size;
367         blk->blk.priv.flags |= KB_PARTIAL_WRITE;
368         blk->blk.priv.vt = &eeprom_unbuffered_vt;
369 }
370
371