CONFIG_EEPROM_VERIFY: New config option.
[bertos.git] / drv / eeprom.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See devlib/README 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.12  2004/11/02 17:50:01  bernie
20  *#* CONFIG_EEPROM_VERIFY: New config option.
21  *#*
22  *#* Revision 1.11  2004/10/26 08:35:31  bernie
23  *#* Reset watchdog for long operations.
24  *#*
25  *#* Revision 1.10  2004/09/20 03:31:22  bernie
26  *#* Sanitize for C++.
27  *#*
28  *#* Revision 1.9  2004/09/14 21:03:46  bernie
29  *#* Use debug.h instead of kdebug.h.
30  *#*
31  *#* Revision 1.8  2004/08/25 14:12:08  rasky
32  *#* Aggiornato il comment block dei log RCS
33  *#*
34  *#* Revision 1.7  2004/08/24 16:48:40  bernie
35  *#* Note reason for including <macros.h>
36  *#*
37  *#* Revision 1.6  2004/08/24 14:27:20  bernie
38  *#* Doxygen fix.
39  *#*
40  *#* Revision 1.5  2004/08/24 13:46:48  bernie
41  *#* Include <macros.h>.
42  *#*
43  *#* Revision 1.4  2004/08/10 06:57:22  bernie
44  *#* eeprom_erase(): New function.
45  *#*
46  *#* Revision 1.3  2004/07/29 22:57:09  bernie
47  *#* Add 24LC16 support.
48  *#*
49  *#* Revision 1.2  2004/07/22 01:24:43  bernie
50  *#* Document AVR dependency.
51  *#*
52  *#* Revision 1.1  2004/07/20 17:11:18  bernie
53  *#* Import into DevLib.
54  *#*
55  *#*/
56
57 #include "eeprom.h"
58
59 #include <drv/wdt.h>
60 #include <mware/byteorder.h> /* cpu_to_be16() */
61 #include <debug.h>
62 #include <hw.h>
63 #include <config.h>  // CONFIG_EEPROM_VERIFY
64 #include <macros.h>  // MIN()
65
66 #include <string.h>  // memset()
67
68 #include <avr/twi.h>
69
70 // Configuration sanity checks
71 #if !defined(CONFIG_EEPROM_VERIFY) || (CONFIG_EEPROM_VERIFY != 0 && CONFIG_EEPROM_VERIFY != 1)
72         #error CONFIG_EEPROM_VERIFY must be defined to either 0 or 1
73 #endif
74
75
76 /* Wait for TWINT flag set: bus is ready */
77 #define WAIT_TWI_READY  do {} while (!(TWCR & BV(TWINT)))
78
79 /*! \name EEPROM control codes */
80 /*@{*/
81 #define SLA_W  0xA0
82 #define SLA_R  0xA1
83 /*@}*/
84
85
86 /*!
87  * Send START condition on the bus.
88  *
89  * \return true on success, false otherwise.
90  */
91 static bool twi_start(void)
92 {
93         TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN);
94         WAIT_TWI_READY;
95
96         if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START)
97                 return true;
98
99         DB(kprintf("!TW_(REP)START: %x\n", TWSR);)
100         return false;
101 }
102
103
104 /*!
105  * Send START condition and select slave for write.
106  *
107  * \return true on success, false otherwise.
108  */
109 static bool twi_start_w(uint8_t slave_addr)
110 {
111         ASSERT(slave_addr < 8);
112
113         /*
114          * Loop on the select write sequence: when the eeprom is busy
115          * writing previously sent data it will reply to the SLA_W
116          * control byte with a NACK.  In this case, we must
117          * keep trying until the eeprom responds with an ACK.
118          */
119         while (twi_start())
120         {
121                 TWDR = SLA_W | (slave_addr << 1);
122                 TWCR = BV(TWINT) | BV(TWEN);
123                 WAIT_TWI_READY;
124
125                 if (TW_STATUS == TW_MT_SLA_ACK)
126                         return true;
127                 else if (TW_STATUS != TW_MT_SLA_NACK)
128                 {
129                         DB(kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR);)
130                         break;
131                 }
132         }
133
134         return false;
135 }
136
137
138 /*!
139  * Send START condition and select slave for read.
140  *
141  * \return true on success, false otherwise.
142  */
143 static bool twi_start_r(uint8_t slave_addr)
144 {
145         ASSERT(slave_addr < 8);
146
147         if (twi_start())
148         {
149                 TWDR = SLA_R | (slave_addr << 1);
150                 TWCR = BV(TWINT) | BV(TWEN);
151                 WAIT_TWI_READY;
152
153                 if (TW_STATUS == TW_MR_SLA_ACK)
154                         return true;
155
156                 DB(kprintf("!TW_MR_SLA_ACK: %x\n", TWSR);)
157         }
158
159         return false;
160 }
161
162
163 /*!
164  * Send STOP condition.
165  */
166 static void twi_stop(void)
167 {
168         TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
169 }
170
171
172 /*!
173  * Send a sequence of bytes in master transmitter mode
174  * to the selected slave device through the TWI bus.
175  *
176  * \return true on success, false on error.
177  */
178 static bool twi_send(const void *_buf, size_t count)
179 {
180         const uint8_t *buf = (const uint8_t *)_buf;
181
182         while (count--)
183         {
184                 TWDR = *buf++;
185                 TWCR = BV(TWINT) | BV(TWEN);
186                 WAIT_TWI_READY;
187                 if (TW_STATUS != TW_MT_DATA_ACK)
188                 {
189                         DB(kprintf("!TW_MT_DATA_ACK: %x\n", TWSR);)
190                         return false;
191                 }
192         }
193
194         return true;
195 }
196
197
198 /*!
199  * Receive a sequence of one or more bytes from the
200  * selected slave device in master receive mode through
201  * the TWI bus.
202  *
203  * Received data is placed in \c buf.
204  *
205  * \return true on success, false on error
206  */
207 static bool twi_recv(void *_buf, size_t count)
208 {
209         uint8_t *buf = (uint8_t *)_buf;
210
211         /*
212          * When reading the last byte the TWEA bit is not
213          * set, and the eeprom should answer with NACK
214          */
215         while (count--)
216         {
217                 TWCR = BV(TWINT) | BV(TWEN) | (count ? BV(TWEA) : 0);
218                 WAIT_TWI_READY;
219
220                 if (count)
221                 {
222                         if (TW_STATUS != TW_MR_DATA_ACK)
223                         {
224                                 DB(kprintf("!TW_MR_DATA_ACK: %x\n", TWSR);)
225                                 return false;
226                         }
227                 }
228                 else
229                 {
230                         if (TW_STATUS != TW_MR_DATA_NACK)
231                         {
232                                 DB(kprintf("!TW_MR_DATA_NACK: %x\n", TWSR);)
233                                 return false;
234                         }
235                 }
236                 *buf++ = TWDR;
237         }
238
239         return true;
240 }
241
242 /*!
243  * Copy \c count bytes from buffer \c buf to
244  * eeprom at address \c addr.
245  */
246 static bool eeprom_writeRaw(e2addr_t addr, const void *buf, size_t count)
247 {
248         bool result = true;
249         ASSERT(addr + count <= EEPROM_SIZE);
250
251         while (count && result)
252         {
253                 /*
254                  * Split write in multiple sequential mode operations that
255                  * don't cross page boundaries.
256                  */
257                 size_t size =
258                         MIN(count, (size_t)(EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1))));
259
260         #if CONFIG_EEPROM_TYPE == EEPROM_24XX16
261                 /*
262                  * The 24LC16 uses the slave address as a 3-bit
263                  * block address.
264                  */
265                 uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07);
266                 uint8_t blk_offs = (uint8_t)addr;
267
268                 result =
269                         twi_start_w(blk_addr)
270                         && twi_send(&blk_offs, sizeof blk_offs)
271                         && twi_send(buf, size);
272
273         #elif CONFIG_EEPROM_TYPE == EEPROM_24XX256
274
275                 // 24LC256 wants big-endian addresses
276                 uint16_t addr_be = cpu_to_be16(addr);
277
278                 result =
279                         twi_start_w(0)
280                         && twi_send((uint8_t *)&addr_be, sizeof addr_be)
281                         && twi_send(buf, size);
282
283         #else
284                 #error Unknown device type
285         #endif
286
287                 twi_stop();
288
289                 // DEBUG
290                 //kprintf("addr=%d, count=%d, size=%d, *#?=%d\n",
291                 //      addr, count, size,
292                 //      (EEPROM_BLKSIZE - (addr & (EEPROM_BLKSIZE - 1)))
293                 //);
294
295                 /* Update count and addr for next operation */
296                 count -= size;
297                 addr += size;
298                 buf = ((const char *)buf) + size;
299         }
300
301         return result;
302 }
303
304
305 #if CONFIG_EEPROM_VERIFY
306 /*!
307  * Check that the contents of an EEPROM range
308  * match with a provided data buffer.
309  */
310 static bool eeprom_verify(e2addr_t addr, const void *buf, size_t count)
311 {
312         uint8_t verify_buf[16];
313         bool result = true;
314
315         while (count && result)
316         {
317                 /* Split read in smaller pieces */
318                 size_t size = MIN(count, sizeof verify_buf);
319
320                 /* Read back buffer */
321                 if (eeprom_read(addr, verify_buf, size))
322                 {
323                         if (memcmp(buf, verify_buf, size) != 0)
324                         {
325                                 TRACEMSG("Data mismatch!\n");
326                                 result = false;
327                         }
328                 }
329                 else
330                 {
331                         TRACEMSG("Read error!\n");
332                         result = false;
333                 }
334
335                 /* Update count and addr for next operation */
336                 count -= size;
337                 addr += size;
338                 buf = ((const char *)buf) + size;
339         }
340
341         return result;
342 }
343 #endif /* CONFIG_EEPROM_VERIFY */
344
345
346 bool eeprom_write(e2addr_t addr, const void *buf, size_t count)
347 {
348 #if CONFIG_EEPROM_VERIFY
349         int retries = 5;
350
351         while (retries--)
352                 if (eeprom_writeRaw(addr, buf, count)
353                                 && eeprom_verify(addr, buf, count))
354                         return true;
355
356         return false;
357
358 #else /* !CONFIG_EEPROM_VERIFY */
359         return eeprom_writeRaw(addr, buf, count);
360 #endif /* !CONFIG_EEPROM_VERIFY */
361 }
362
363
364 /*!
365  * Copy \c count bytes at address \c addr
366  * from eeprom to RAM to buffer \c buf.
367  *
368  * \return true on success.
369  */
370 bool eeprom_read(e2addr_t addr, void *buf, size_t count)
371 {
372         ASSERT(addr + count <= EEPROM_SIZE);
373
374 #if CONFIG_EEPROM_TYPE == EEPROM_24XX16
375         /*
376          * The 24LC16 uses the slave address as a 3-bit
377          * block address.
378          */
379         uint8_t blk_addr = (uint8_t)((addr >> 8) & 0x07);
380         uint8_t blk_offs = (uint8_t)addr;
381
382         bool res =
383                 twi_start_w(blk_addr)
384                 && twi_send(&blk_offs, sizeof blk_offs)
385                 && twi_start_r(blk_addr)
386                 && twi_recv(buf, count);
387
388 #elif CONFIG_EEPROM_TYPE == EEPROM_24XX256
389
390         // 24LC256 wants big-endian addresses
391         addr = cpu_to_be16(addr);
392
393         bool res =
394                 twi_start_w(0)
395                 && twi_send((uint8_t *)&addr, sizeof(addr))
396                 && twi_start_r(0)
397                 && twi_recv(buf, count);
398 #else
399         #error Unknown device type
400 #endif
401
402         twi_stop();
403
404         return res;
405 }
406
407
408 /*!
409  * Write a single character \a c at address \a addr.
410  */
411 bool eeprom_write_char(e2addr_t addr, char c)
412 {
413         return eeprom_write(addr, &c, 1);
414 }
415
416
417 /*!
418  * Read a single character at address \a addr.
419  *
420  * \return the requested character or -1 in case of failure.
421  */
422 int eeprom_read_char(e2addr_t addr)
423 {
424         char c;
425
426         if (eeprom_read(addr, &c, 1))
427                 return c;
428         else
429                 return -1;
430 }
431
432
433 /*!
434  * Erase specified part of eeprom, writing 0xFF.
435  *
436  * \param addr   starting address
437  * \param count  length of block to erase
438  */
439 void eeprom_erase(e2addr_t addr, size_t count)
440 {
441         uint8_t buf[EEPROM_BLKSIZE];
442         memset(buf, 0xFF, sizeof buf);
443
444         // Clear all but struct hw_info at start of eeprom
445         while (count)
446         {
447                 // Long operation, reset watchdog
448                 wdt_reset();
449
450                 size_t size = MIN(count, sizeof buf);
451                 eeprom_write(addr, buf, size);
452                 addr += size;
453                 count -= size;
454         }
455 }
456
457
458 /*!
459  * Initialize TWI module.
460  */
461 void eeprom_init(void)
462 {
463         cpuflags_t flags;
464         DISABLE_IRQSAVE(flags);
465
466 #if defined(__AVR_ATmega64__)
467         PORTD |= BV(PD0) | BV(PD1);
468         DDRD |= BV(PD0) | BV(PD1);
469 #elif defined(__AVR_ATmega8__)
470         PORTC |= BV(PC4) | BV(PC5);
471         DDRC |= BV(PC4) | BV(PC5);
472 #else
473         #error Unsupported architecture
474 #endif
475
476         /*
477          * Set speed:
478          * F = CLOCK_FREQ / (16 + 2*TWBR * 4^TWPS)
479          */
480         #define TWI_FREQ  300000  /* 300 kHz */
481         #define TWI_PRESC 1       /* 4 ^ TWPS */
482
483         TWBR = (CLOCK_FREQ / (2 * TWI_FREQ * TWI_PRESC)) - (8 / TWI_PRESC);
484         TWSR = 0;
485         TWCR = BV(TWEN);
486
487         ENABLE_IRQRESTORE(flags);
488 }
489
490
491 #ifdef _DEBUG
492
493 #include <string.h>
494
495 void eeprom_test(void)
496 {
497         static const char magic[14] = "Humpty Dumpty";
498         char buf[sizeof magic];
499         size_t i;
500
501         // Write something to EEPROM using unaligned sequential writes
502         for (i = 0; i < 42; ++i)
503                 eeprom_write(i * sizeof magic, magic, sizeof magic);
504
505         // Read back with single-byte reads
506         for (i = 0; i < 42 * sizeof magic; ++i)
507         {
508                 eeprom_read(i, buf, 1);
509                 kprintf("EEPROM byte read: %c (%d)\n", buf[0], buf[0]);
510                 ASSERT(buf[0] == magic[i % sizeof magic]);
511         }
512
513         // Read back again using sequential reads
514         for (i = 0; i < 42; ++i)
515         {
516                 memset(buf, 0, sizeof buf);
517                 eeprom_read(i * sizeof magic, buf, sizeof magic);
518                 kprintf("EEPROM seq read @ 0x%x: '%s'\n", i * sizeof magic, buf);
519                 ASSERT(memcmp(buf, magic, sizeof magic) == 0);
520         }
521 }
522
523 #endif // _DEBUG