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