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