Revert bad commit.
[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  * \version $Id$
36  *
37  * \author Francesco Sacchi <batt@develer.com>
38  */
39
40 #include "twi_at91.h"
41
42 #include "cfg/cfg_twi.h"
43 #include <cfg/compiler.h>
44 #include <cfg/debug.h>
45 #include <cfg/macros.h>
46 #include <cfg/module.h>
47
48 #include <drv/timer.h>
49
50 #include <io/arm.h>
51
52 /**
53  * Timeout for ACK slave waiting.
54  */
55 #define TWI_TIMEOUT ms_to_ticks(50)
56
57 /**
58  * Send \a size bytes over the twi line to slave \a id.
59  * If the device requires internal addressing before writing, \a byte1 \a byte2 and \a byte3 can
60  * be specified. Internal addressign bytes not used *must* be set to TWI_NO_IADDR. If 1 or 2 bytes
61  * are required for internal addressing you *must* first use \a byte1 and than \a byte2.
62  * \note Atmel TWI implementation is broken so it was not possible to supply a better
63  *       interface. Additionally NACK handling is also broken, so if the i2c device reply nack
64  *       this function will return after TWI_TIMEOUT.
65  * \return true if ok, false on slave timeout.
66  */
67 bool twi_write(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, const void *_buf, size_t size)
68 {
69         uint8_t addr_size = 0;
70         const uint8_t *buf = (const uint8_t *)_buf;
71         ticks_t start;
72
73         /* At least 1 byte *must* be transmitted, thanks to crappy hw implementation */
74         ASSERT(size >= 1);
75
76         /* Check internal byte address presence */
77         if (byte1 != TWI_NO_IADDR)
78                 addr_size++;
79
80         if (byte2 != TWI_NO_IADDR)
81         {
82                 ASSERT(addr_size == 1);
83                 addr_size++;
84         }
85
86         if (byte3 != TWI_NO_IADDR)
87         {
88                 ASSERT(addr_size == 2);
89                 addr_size++;
90         }
91
92         start = timer_clock();
93         /* Wait tx buffer empty */
94         while (!(TWI_SR & BV(TWI_TXRDY)))
95         {
96                 if (timer_clock() - start > TWI_TIMEOUT)
97                         return false;
98         }
99
100         /* Set slave address and (optional) internal slave addresses */
101         TWI_MMR = (uint32_t)id << TWI_DADR_SHIFT | (uint32_t)addr_size << TWI_IADRSZ_SHIFT;
102
103         TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
104
105         while (size--)
106         {
107                 /* Send data */
108                 TWI_THR = *buf++;
109
110                 start = timer_clock();
111                 /* Wait tx buffer empty */
112                 while (!(TWI_SR & BV(TWI_TXRDY)))
113                 {
114                         if (timer_clock() - start > TWI_TIMEOUT)
115                                 return false;
116                 }
117         }
118
119         /* Wait transmit complete bit */
120         start = timer_clock();
121         while (!(TWI_SR & BV(TWI_TXCOMP)))
122         {
123                 if (timer_clock() - start > TWI_TIMEOUT)
124                         return false;
125         }
126
127         return true;
128 }
129
130
131 /**
132  * Read \a size bytes from the twi line from slave \a id.
133  * If the device requires internal addressing before reading, \a byte1 \a byte2 and \a byte3 must
134  * be specified. Internal addressign bytes not used *must* be set to TWI_NO_IADDR. If 1 or 2 bytes
135  * are required for internal addressing you *must* first use \a byte1 and than \a byte2.
136  * \note Atmel TWI implementation is broken so it was not possible to supply a better
137  *       interface. Additionally NACK handling is also broken, so if the i2c device reply nack
138  *       this function will return after TWI_TIMEOUT.
139  * \return true if ok, false on slave timeout.
140  */
141 bool twi_read(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, void *_buf, size_t size)
142 {
143         uint8_t addr_size = 0;
144         uint8_t *buf = (uint8_t *)_buf;
145         bool stopped = false;
146         ticks_t start;
147
148         /* At least 1 byte *must* be transmitted, thanks to crappy twi implementation */
149         ASSERT(size >= 1);
150
151         /* Check internal byte address presence */
152         if (byte1 != TWI_NO_IADDR)
153                 addr_size++;
154
155         if (byte2 != TWI_NO_IADDR)
156         {
157                 ASSERT(addr_size == 1);
158                 addr_size++;
159         }
160
161         if (byte3 != TWI_NO_IADDR)
162         {
163                 ASSERT(addr_size == 2);
164                 addr_size++;
165         }
166
167         /* Wait tx buffer empty */
168         start = timer_clock();
169         while (!(TWI_SR & BV(TWI_TXRDY)))
170         {
171                 if (timer_clock() - start > TWI_TIMEOUT)
172                         return false;
173         }
174
175
176         /* Set slave address and (optional) internal slave addresses */
177         TWI_MMR = ((uint32_t)id << TWI_DADR_SHIFT) | BV(TWI_MREAD) | ((uint32_t)addr_size << TWI_IADRSZ_SHIFT);
178
179         TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
180
181         /*
182          * Start reception.
183          * Kludge: if we want to receive only 1 byte, the stop but *must* be set here
184          * (thanks to crappy twi implementation again).
185          */
186         if (size == 1)
187         {
188                 TWI_CR = BV(TWI_START) | BV(TWI_STOP);
189                 stopped = true;
190         }
191         else
192                 TWI_CR = BV(TWI_START);
193
194         while (size--)
195         {
196                 /* If we are at the last byte, inform the crappy hw that we
197                    want to stop the reception. */
198                 if (!size && !stopped)
199                         TWI_CR = BV(TWI_STOP);
200
201                 /* Wait until a byte is received */
202                 start = timer_clock();
203                 while (!(TWI_SR & BV(TWI_RXRDY)))
204                 {
205                         if (timer_clock() - start > TWI_TIMEOUT)
206                         {
207                                 TWI_CR = BV(TWI_STOP);
208                                 return false;
209                         }
210                 }
211
212
213                 *buf++ = TWI_RHR;
214         }
215
216         /* Wait transmit complete bit */
217         start = timer_clock();
218         while (!(TWI_SR & BV(TWI_TXCOMP)))
219         {
220                 if (timer_clock() - start > TWI_TIMEOUT)
221                         return false;
222         }
223
224         return true;
225 }
226
227 MOD_DEFINE(twi);
228
229 /**
230  * Init the (broken) sam7 twi driver.
231  */
232 void twi_init(void)
233 {
234         /* Disable PIO on TWI pins */
235         PIOA_PDR = BV(TWD) | BV(TWCK);
236
237         /* Enable oper drain on TWI pins */
238         PIOA_MDER = BV(TWD);
239
240         /* Disable all irqs */
241         TWI_IDR = 0xFFFFFFFF;
242
243         TWI_CR = BV(TWI_SWRST);
244
245         /* Enable master mode */
246         TWI_CR = BV(TWI_MSEN);
247
248         PMC_PCER = BV(TWI_ID);
249
250         /*
251          * Compute twi clock.
252          * CLDIV = ((Tlow * 2^CKDIV) -3) * Tmck
253          * CHDIV = ((THigh * 2^CKDIV) -3) * Tmck
254          * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
255          */
256         uint16_t cldiv, ckdiv = 0;
257         while ((cldiv = ((CLOCK_FREQ / (2 * CONFIG_TWI_FREQ)) - 3) / (1 << ckdiv)) > 255)
258                 ckdiv++;
259
260         /* Atmel errata states that ckdiv *must* be less than 5 for unknown reason */
261         ASSERT(ckdiv < 5);
262
263         TWI_CWGR = ((uint32_t)ckdiv << TWI_CKDIV_SHIFT) | (cldiv << TWI_CLDIV_SHIFT) | (cldiv << TWI_CHDIV_SHIFT);
264         TRACEMSG("TWI_CWGR [%08lx]", TWI_CWGR);
265
266         MOD_INIT(twi);
267 }
268