Include top-level headers from cfg/ subdir.
[bertos.git] / drv / eeprom.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See README.devlib for information.
6  * -->
7  *
8  * \brief Driver for the 24xx16 and 24xx256 I2C EEPROMS (implementation)
9  *
10  * \note This implementation is AVR specific.
11  *
12  * \version $Id$
13  * \author Stefano Fedrigo <aleph@develer.com>
14  * \author Bernardo Innocenti <bernie@develer.com>
15  */
16
17 /*#*
18  *#* $Log$
19  *#* Revision 1.17  2005/04/11 19:10:27  bernie
20  *#* Include top-level headers from cfg/ subdir.
21  *#*
22  *#* Revision 1.16  2005/03/01 23:25:09  bernie
23  *#* Prune CVS log.
24  *#*
25  *#* Revision 1.11  2004/10/26 08:35:31  bernie
26  *#* Reset watchdog for long operations.
27  *#*
28  *#* Revision 1.10  2004/09/20 03:31:22  bernie
29  *#* Sanitize for C++.
30  *#*
31  *#* Revision 1.9  2004/09/14 21:03:46  bernie
32  *#* Use debug.h instead of kdebug.h.
33  *#*/
34
35 #include "eeprom.h"
36
37 #include <cfg/debug.h>
38 #include <cfg/config.h>  // CONFIG_EEPROM_VERIFY
39 #include <cfg/macros.h>  // MIN()
40 #include <drv/twi.h>
41 #include <drv/wdt.h>
42 #include <mware/byteorder.h> // cpu_to_be16()
43
44 #include <string.h>  // memset()
45
46
47 // Configuration sanity checks
48 #if !defined(CONFIG_EEPROM_VERIFY) || (CONFIG_EEPROM_VERIFY != 0 && CONFIG_EEPROM_VERIFY != 1)
49         #error CONFIG_EEPROM_VERIFY must be defined to either 0 or 1
50 #endif
51
52
53 /*!
54  * Copy \c count bytes from buffer \c buf to
55  * eeprom at address \c addr.
56  */
57 static bool eeprom_writeRaw(e2addr_t addr, const void *buf, size_t count)
58 {
59         bool result = true;
60         ASSERT(addr + count <= EEPROM_SIZE);
61
62         while (count && result)
63         {
64                 /*
65                  * Split write in multiple sequential mode operations that
66                  * don't cross page boundaries.
67                  */
68                 size_t size =
69                         MIN(count, (size_t)(EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1))));
70
71         #if CONFIG_EEPROM_TYPE == EEPROM_24XX16
72                 /*
73                  * The 24LC16 uses the slave address as a 3-bit
74                  * block address.
75                  */
76                 uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07);
77                 uint8_t blk_offs = (uint8_t)addr;
78
79                 result =
80                         twi_start_w(blk_addr)
81                         && twi_send(&blk_offs, sizeof blk_offs)
82                         && twi_send(buf, size);
83
84         #elif CONFIG_EEPROM_TYPE == EEPROM_24XX256
85
86                 // 24LC256 wants big-endian addresses
87                 uint16_t addr_be = cpu_to_be16(addr);
88
89                 result =
90                         twi_start_w(0)
91                         && twi_send((uint8_t *)&addr_be, sizeof addr_be)
92                         && twi_send(buf, size);
93
94         #else
95                 #error Unknown device type
96         #endif
97
98                 twi_stop();
99
100                 // DEBUG
101                 //kprintf("addr=%d, count=%d, size=%d, *#?=%d\n",
102                 //      addr, count, size,
103                 //      (EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1)))
104                 //);
105
106                 /* Update count and addr for next operation */
107                 count -= size;
108                 addr += size;
109                 buf = ((const char *)buf) + size;
110         }
111
112         if (!result)
113                 TRACEMSG("Write error!");
114         return result;
115 }
116
117
118 #if CONFIG_EEPROM_VERIFY
119 /*!
120  * Check that the contents of an EEPROM range
121  * match with a provided data buffer.
122  *
123  * \return true on success.
124  */
125 static bool eeprom_verify(e2addr_t addr, const void *buf, size_t count)
126 {
127         uint8_t verify_buf[16];
128         bool result = true;
129
130         while (count && result)
131         {
132                 /* Split read in smaller pieces */
133                 size_t size = MIN(count, sizeof verify_buf);
134
135                 /* Read back buffer */
136                 if (eeprom_read(addr, verify_buf, size))
137                 {
138                         if (memcmp(buf, verify_buf, size) != 0)
139                         {
140                                 TRACEMSG("Data mismatch!");
141                                 result = false;
142                         }
143                 }
144                 else
145                 {
146                         TRACEMSG("Read error!");
147                         result = false;
148                 }
149
150                 /* Update count and addr for next operation */
151                 count -= size;
152                 addr += size;
153                 buf = ((const char *)buf) + size;
154         }
155
156         return result;
157 }
158 #endif /* CONFIG_EEPROM_VERIFY */
159
160
161 bool eeprom_write(e2addr_t addr, const void *buf, size_t count)
162 {
163 #if CONFIG_EEPROM_VERIFY
164         int retries = 5;
165
166         while (retries--)
167                 if (eeprom_writeRaw(addr, buf, count)
168                                 && eeprom_verify(addr, buf, count))
169                         return true;
170
171         return false;
172
173 #else /* !CONFIG_EEPROM_VERIFY */
174         return eeprom_writeRaw(addr, buf, count);
175 #endif /* !CONFIG_EEPROM_VERIFY */
176 }
177
178
179 /*!
180  * Copy \c count bytes at address \c addr
181  * from eeprom to RAM to buffer \c buf.
182  *
183  * \return true on success.
184  */
185 bool eeprom_read(e2addr_t addr, void *buf, size_t count)
186 {
187         ASSERT(addr + count <= EEPROM_SIZE);
188
189 #if CONFIG_EEPROM_TYPE == EEPROM_24XX16
190         /*
191          * The 24LC16 uses the slave address as a 3-bit
192          * block address.
193          */
194         uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07);
195         uint8_t blk_offs = (uint8_t)addr;
196
197         bool res =
198                 twi_start_w(blk_addr)
199                 && twi_send(&blk_offs, sizeof blk_offs)
200                 && twi_start_r(blk_addr)
201                 && twi_recv(buf, count);
202
203 #elif CONFIG_EEPROM_TYPE == EEPROM_24XX256
204
205         // 24LC256 wants big-endian addresses
206         addr = cpu_to_be16(addr);
207
208         bool res =
209                 twi_start_w(0)
210                 && twi_send((uint8_t *)&addr, sizeof(addr))
211                 && twi_start_r(0)
212                 && twi_recv(buf, count);
213 #else
214         #error Unknown device type
215 #endif
216
217         twi_stop();
218
219         if (!res)
220                 TRACEMSG("Read error!");
221         return res;
222 }
223
224
225 /*!
226  * Write a single character \a c at address \a addr.
227  */
228 bool eeprom_write_char(e2addr_t addr, char c)
229 {
230         return eeprom_write(addr, &c, 1);
231 }
232
233
234 /*!
235  * Read a single character at address \a addr.
236  *
237  * \return the requested character or -1 in case of failure.
238  */
239 int eeprom_read_char(e2addr_t addr)
240 {
241         char c;
242
243         if (eeprom_read(addr, &c, 1))
244                 return c;
245         else
246                 return -1;
247 }
248
249
250 /*!
251  * Erase specified part of eeprom, writing 0xFF.
252  *
253  * \param addr   starting address
254  * \param count  length of block to erase
255  */
256 void eeprom_erase(e2addr_t addr, size_t count)
257 {
258         uint8_t buf[EEPROM_BLKSIZE];
259         memset(buf, 0xFF, sizeof buf);
260
261         // Clear all but struct hw_info at start of eeprom
262         while (count)
263         {
264                 // Long operation, reset watchdog
265                 wdt_reset();
266
267                 size_t size = MIN(count, sizeof buf);
268                 eeprom_write(addr, buf, size);
269                 addr += size;
270                 count -= size;
271         }
272 }
273
274
275 /*!
276  * Initialize TWI module.
277  */
278 void eeprom_init(void)
279 {
280         twi_init();
281 }
282
283
284 #ifdef _DEBUG
285
286 #include <string.h>
287
288 void eeprom_test(void)
289 {
290         static const char magic[14] = "Humpty Dumpty";
291         char buf[sizeof magic];
292         size_t i;
293
294         // Write something to EEPROM using unaligned sequential writes
295         for (i = 0; i < 42; ++i)
296         {
297                 wdt_reset();
298                 eeprom_write(i * sizeof magic, magic, sizeof magic);
299         }
300
301         // Read back with single-byte reads
302         for (i = 0; i < 42 * sizeof magic; ++i)
303         {
304                 wdt_reset();
305                 eeprom_read(i, buf, 1);
306                 kprintf("EEPROM byte read: %c (%d)\n", buf[0], buf[0]);
307                 ASSERT(buf[0] == magic[i % sizeof magic]);
308         }
309
310         // Read back again using sequential reads
311         for (i = 0; i < 42; ++i)
312         {
313                 wdt_reset();
314                 memset(buf, 0, sizeof buf);
315                 eeprom_read(i * sizeof magic, buf, sizeof magic);
316                 kprintf("EEPROM seq read @ 0x%x: '%s'\n", i * sizeof magic, buf);
317                 ASSERT(memcmp(buf, magic, sizeof magic) == 0);
318         }
319 }
320
321 #endif // _DEBUG