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