Import into DevLib.
[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  *
12  * \brief I2C eeprom driver
13  */
14
15 /*
16  * $Log$
17  * Revision 1.1  2004/07/20 17:11:18  bernie
18  * Import into DevLib.
19  *
20  */
21
22 #include "eeprom.h"
23 #include <mware/byteorder.h> /* cpu_to_be16() */
24 #include <drv/kdebug.h>
25 #include <hw.h>
26
27 #include <avr/twi.h>
28
29
30 /* Wait for TWINT flag set: bus is ready */
31 #define WAIT_TWI_READY  do {} while (!(TWCR & BV(TWINT)))
32
33 /*! \name EEPROM control codes */
34 /*@{*/
35 #define SLA_W  0xA0
36 #define SLA_R  0xA1
37 /*@}*/
38
39
40 /*!
41  * Send START condition on the bus.
42  *
43  * \return true on success, false otherwise.
44  */
45 static bool twi_start(void)
46 {
47         TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN);
48         WAIT_TWI_READY;
49
50         if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START)
51                 return true;
52
53         DB(kprintf("!TW_(REP)START: %x\n", TWSR);)
54         return false;
55 }
56
57
58 /*!
59  * Send START condition and select slave for write.
60  *
61  * \return true on success, false otherwise.
62  */
63 static bool twi_start_w(uint8_t slave_addr)
64 {
65         //TRACE;
66
67         /* Do a loop on the select write sequence because if the
68          * eeprom is busy writing precedently sent data it will respond
69          * with NACK to the SLA_W control byte. In this case we have
70          * to try until the eeprom reply with an ACK.
71          */
72         while (twi_start())
73         {
74                 TWDR = SLA_W | ((slave_addr & 0x5) << 1);
75                 TWCR = BV(TWINT) | BV(TWEN);
76                 WAIT_TWI_READY;
77
78                 if (TW_STATUS == TW_MT_SLA_ACK)
79                         return true;
80                 else if (TW_STATUS != TW_MT_SLA_NACK)
81                 {
82                         DB(kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR);)
83                         break;
84                 }
85         }
86
87         return false;
88 }
89
90
91 /*!
92  * Send START condition and select slave for read.
93  *
94  * \return true on success, false otherwise.
95  */
96 static bool twi_start_r(uint8_t slave_addr)
97 {
98         //TRACE;
99
100         if (twi_start())
101         {
102                 TWDR = SLA_R | ((slave_addr & 0x5) << 1);
103                 TWCR = BV(TWINT) | BV(TWEN);
104                 WAIT_TWI_READY;
105
106                 if (TW_STATUS == TW_MR_SLA_ACK)
107                         return true;
108
109                 DB(kprintf("!TW_MR_SLA_ACK: %x\n", TWSR);)
110         }
111
112         return false;
113 }
114
115
116 /*!
117  * Send STOP condition.
118  */
119 static void twi_stop(void)
120 {
121         //TRACE;
122
123         TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
124 }
125
126
127 /*!
128  * Send a sequence of bytes in master transmitter mode
129  * to the selected slave device through the TWI bus.
130  *
131  * \return true on success, false on error.
132  */
133 static bool twi_send(const uint8_t *buf, size_t count)
134 {
135         //TRACE;
136
137         while (count--)
138         {
139                 TWDR = *buf++;
140                 TWCR = BV(TWINT) | BV(TWEN);
141                 WAIT_TWI_READY;
142                 if (TW_STATUS != TW_MT_DATA_ACK)
143                 {
144                         DB(kprintf("!TW_MT_DATA_ACK: %x\n", TWSR);)
145                         return false;
146                 }
147         }
148
149         return true;
150 }
151
152
153 /*!
154  * Receive a sequence of one or more bytes from the
155  * selected slave device in master receive mode through
156  * the TWI bus.
157  *
158  * Received data is placed in \c buf.
159  *
160  * \return true on success, false on error
161  */
162 static bool twi_recv(uint8_t *buf, size_t count)
163 {
164         //TRACE;
165
166         /*
167          * When reading the last byte the TWEA bit is not
168          * set, and the eeprom should answer with NACK
169          */
170         while (count--)
171         {
172                 TWCR = BV(TWINT) | BV(TWEN) | (count ? BV(TWEA) : 0);
173                 WAIT_TWI_READY;
174
175                 if (count)
176                 {
177                         if (TW_STATUS != TW_MR_DATA_ACK)
178                         {
179                                 DB(kprintf("!TW_MR_DATA_ACK: %x\n", TWSR);)
180                                 return false;
181                         }
182                 }
183                 else
184                 {
185                         if (TW_STATUS != TW_MR_DATA_NACK)
186                         {
187                                 DB(kprintf("!TW_MR_DATA_NACK: %x\n", TWSR);)
188                                 return false;
189                         }
190                 }
191                 *buf++ = TWDR;
192         }
193
194         return true;
195 }
196
197
198 /*!
199  * Copy \c count bytes from buffer \c buf to
200  * eeprom at address \c addr.
201  *
202  * \note No check is done for data crossing page
203  *       boundaries.
204  */
205 bool eeprom_write(e2addr_t addr, const void *buf, size_t count)
206 {
207         // eeprom accepts address as big endian
208         addr = cpu_to_be16(addr);
209
210         bool res =
211                 twi_start_w(0)
212                 && twi_send((uint8_t *)&addr, sizeof(addr))
213                 && twi_send(buf, count);
214
215         twi_stop();
216
217         return res;
218 }
219
220
221 /*!
222  * Copy \c count bytes at address \c addr
223  * from eeprom to RAM to buffer \c buf.
224  */
225 bool eeprom_read(e2addr_t addr, void *buf, size_t count)
226 {
227         // eeprom accepts address as big endian
228         addr = cpu_to_be16(addr);
229
230         bool res =
231                 twi_start_w(0)
232                 && twi_send((uint8_t *)&addr, sizeof(addr))
233                 && twi_start_r(0)
234                 && twi_recv(buf, count);
235
236         twi_stop();
237
238         return res;
239 }
240
241
242 /*!
243  * Write a single character \a c at address \a addr.
244  */
245 bool eeprom_write_char(e2addr_t addr, char c)
246 {
247         return eeprom_write(addr, &c, 1);
248 }
249
250
251 /*!
252  * Read a single character at address \a addr.
253  *
254  * \return the requested character or -1 in case of failure.
255  */
256 int eeprom_read_char(e2addr_t addr)
257 {
258         char c;
259
260         if (eeprom_read(addr, &c, 1))
261                 return c;
262         else
263                 return -1;
264 }
265
266
267 /*!
268  * Initialize TWI module.
269  */
270 void eeprom_init(void)
271 {
272         cpuflags_t flags;
273         DISABLE_IRQSAVE(flags);
274
275         DDRD |= BV(PORTD0) | BV(PORTD1);
276         PORTD |= BV(PORTD0) | BV(PORTD1);
277
278         /*
279          * Set speed:
280          * F = CLOCK_FREQ / (16 + 2*TWBR * 4^TWPS)
281          */
282 #       define TWI_FREQ  300000  /* 300 kHz */
283 #       define TWI_PRESC 1       /* 4 ^ TWPS */
284
285         TWBR = (CLOCK_FREQ / (2 * TWI_FREQ * TWI_PRESC)) - (8 / TWI_PRESC);
286         TWSR = 0;
287
288         ENABLE_IRQRESTORE(flags);
289 }
290
291
292 #ifdef _DEBUG
293
294 void eeprom_test(void)
295 {
296         static const char magic[] = "Humpty Dumpty";
297         char buf[sizeof magic];
298
299         // Write something to EEPROM and read it back
300         eeprom_write(0, magic, sizeof magic);
301         eeprom_read(0, buf, sizeof buf);
302         kprintf("EEPROM read: %s\n", buf);
303 }
304
305 #endif // _DEBUG
306