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