Increase ethernet irq priority.
[bertos.git] / bertos / cpu / arm / drv / twi_at91.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief Driver for the AT91 ARM TWI (implementation)
34  *
35  *
36  * \author Francesco Sacchi <batt@develer.com>
37  */
38
39 #include "twi_at91.h"
40
41 #include "cfg/cfg_i2c.h"
42 #include <cfg/compiler.h>
43 #include <cfg/debug.h>
44 #include <cfg/macros.h>
45 #include <cfg/module.h>
46
47 #include <drv/timer.h>
48
49 #include <io/arm.h>
50
51 /**
52  * Timeout for ACK slave waiting.
53  */
54 #define TWI_TIMEOUT ms_to_ticks(50)
55
56 /**
57  * Send \a size bytes over the twi line to slave \a id.
58  * If the device requires internal addressing before writing, \a byte1 \a byte2 and \a byte3 can
59  * be specified. Internal addressign bytes not used *must* be set to TWI_NO_IADDR. If 1 or 2 bytes
60  * are required for internal addressing you *must* first use \a byte1 and than \a byte2.
61  * \note Atmel TWI implementation is broken so it was not possible to supply a better
62  *       interface. Additionally NACK handling is also broken, so if the i2c device reply nack
63  *       this function will return after TWI_TIMEOUT.
64  * \return true if ok, false on slave timeout.
65  */
66 bool twi_write(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, const void *_buf, size_t size)
67 {
68         uint8_t addr_size = 0;
69         const uint8_t *buf = (const uint8_t *)_buf;
70         ticks_t start;
71
72         /* At least 1 byte *must* be transmitted, thanks to crappy hw implementation */
73         ASSERT(size >= 1);
74
75         /* Check internal byte address presence */
76         if (byte1 != TWI_NO_IADDR)
77                 addr_size++;
78
79         if (byte2 != TWI_NO_IADDR)
80         {
81                 ASSERT(addr_size == 1);
82                 addr_size++;
83         }
84
85         if (byte3 != TWI_NO_IADDR)
86         {
87                 ASSERT(addr_size == 2);
88                 addr_size++;
89         }
90
91         start = timer_clock();
92         /* Wait tx buffer empty */
93         while (!(TWI_SR & BV(TWI_TXRDY)))
94         {
95                 if (timer_clock() - start > TWI_TIMEOUT)
96                         return false;
97         }
98
99         /* Set slave address and (optional) internal slave addresses */
100         TWI_MMR = (uint32_t)id << TWI_DADR_SHIFT | (uint32_t)addr_size << TWI_IADRSZ_SHIFT;
101
102         TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
103
104         while (size--)
105         {
106                 /* Send data */
107                 TWI_THR = *buf++;
108
109                 start = timer_clock();
110                 /* Wait tx buffer empty */
111                 while (!(TWI_SR & BV(TWI_TXRDY)))
112                 {
113                         if (timer_clock() - start > TWI_TIMEOUT)
114                                 return false;
115                 }
116         }
117
118         /* Wait transmit complete bit */
119         start = timer_clock();
120         while (!(TWI_SR & BV(TWI_TXCOMP)))
121         {
122                 if (timer_clock() - start > TWI_TIMEOUT)
123                         return false;
124         }
125
126         return true;
127 }
128
129
130 /**
131  * Read \a size bytes from the twi line from slave \a id.
132  * If the device requires internal addressing before reading, \a byte1 \a byte2 and \a byte3 must
133  * be specified. Internal addressign bytes not used *must* be set to TWI_NO_IADDR. If 1 or 2 bytes
134  * are required for internal addressing you *must* first use \a byte1 and than \a byte2.
135  * \note Atmel TWI implementation is broken so it was not possible to supply a better
136  *       interface. Additionally NACK handling is also broken, so if the i2c device reply nack
137  *       this function will return after TWI_TIMEOUT.
138  * \return true if ok, false on slave timeout.
139  */
140 bool twi_read(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, void *_buf, size_t size)
141 {
142         uint8_t addr_size = 0;
143         uint8_t *buf = (uint8_t *)_buf;
144         bool stopped = false;
145         ticks_t start;
146
147         /* At least 1 byte *must* be transmitted, thanks to crappy twi implementation */
148         ASSERT(size >= 1);
149
150         /* Check internal byte address presence */
151         if (byte1 != TWI_NO_IADDR)
152                 addr_size++;
153
154         if (byte2 != TWI_NO_IADDR)
155         {
156                 ASSERT(addr_size == 1);
157                 addr_size++;
158         }
159
160         if (byte3 != TWI_NO_IADDR)
161         {
162                 ASSERT(addr_size == 2);
163                 addr_size++;
164         }
165
166         /* Wait tx buffer empty */
167         start = timer_clock();
168         while (!(TWI_SR & BV(TWI_TXRDY)))
169         {
170                 if (timer_clock() - start > TWI_TIMEOUT)
171                         return false;
172         }
173
174
175         /* Set slave address and (optional) internal slave addresses */
176         TWI_MMR = ((uint32_t)id << TWI_DADR_SHIFT) | BV(TWI_MREAD) | ((uint32_t)addr_size << TWI_IADRSZ_SHIFT);
177
178         TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
179
180         /*
181          * Start reception.
182          * Kludge: if we want to receive only 1 byte, the stop but *must* be set here
183          * (thanks to crappy twi implementation again).
184          */
185         if (size == 1)
186         {
187                 TWI_CR = BV(TWI_START) | BV(TWI_STOP);
188                 stopped = true;
189         }
190         else
191                 TWI_CR = BV(TWI_START);
192
193         while (size--)
194         {
195                 /* If we are at the last byte, inform the crappy hw that we
196                    want to stop the reception. */
197                 if (!size && !stopped)
198                         TWI_CR = BV(TWI_STOP);
199
200                 /* Wait until a byte is received */
201                 start = timer_clock();
202                 while (!(TWI_SR & BV(TWI_RXRDY)))
203                 {
204                         if (timer_clock() - start > TWI_TIMEOUT)
205                         {
206                                 TWI_CR = BV(TWI_STOP);
207                                 return false;
208                         }
209                 }
210
211
212                 *buf++ = TWI_RHR;
213         }
214
215         /* Wait transmit complete bit */
216         start = timer_clock();
217         while (!(TWI_SR & BV(TWI_TXCOMP)))
218         {
219                 if (timer_clock() - start > TWI_TIMEOUT)
220                         return false;
221         }
222
223         return true;
224 }
225
226 MOD_DEFINE(twi);
227
228 /**
229  * Init the (broken) sam7 twi driver.
230  */
231 void twi_init(void)
232 {
233         /* Disable PIO on TWI pins */
234         PIOA_PDR = BV(TWD) | BV(TWCK);
235
236         /* Enable oper drain on TWI pins */
237         PIOA_MDER = BV(TWD);
238
239         /* Disable all irqs */
240         TWI_IDR = 0xFFFFFFFF;
241
242         TWI_CR = BV(TWI_SWRST);
243
244         /* Enable master mode */
245         TWI_CR = BV(TWI_MSEN);
246
247         PMC_PCER = BV(TWI_ID);
248
249         /*
250          * Compute twi clock.
251          * CLDIV = ((Tlow * 2^CKDIV) -3) * Tmck
252          * CHDIV = ((THigh * 2^CKDIV) -3) * Tmck
253          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
254          */
255         uint16_t cldiv, ckdiv = 0;
256         while ((cldiv = ((CPU_FREQ / (2 * CONFIG_I2C_FREQ)) - 3) / (1 << ckdiv)) > 255)
257                 ckdiv++;
258
259         /* Atmel errata states that ckdiv *must* be less than 5 for unknown reason */
260         ASSERT(ckdiv < 5);
261
262         TWI_CWGR = ((uint32_t)ckdiv << TWI_CKDIV_SHIFT) | (cldiv << TWI_CLDIV_SHIFT) | (cldiv << TWI_CHDIV_SHIFT);
263         TRACEMSG("TWI_CWGR [%08lx]", TWI_CWGR);
264
265         MOD_INIT(twi);
266 }
267