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