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