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