Add DIV_ROUND macro.
[bertos.git] / cpu / arm / drv / ser_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 2003, 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief ARM UART and SPI I/O driver
35  *
36  *
37  * \version $Id: ser_amr.c 18280 2007-10-11 15:14:20Z asterix $
38  * \author Daniele Basile <asterix@develer.com>
39  */
40
41 #include <io/arm.h>
42
43 //#include "ser_at91.h"
44 #include <drv/ser.h>
45 #include <drv/ser_p.h>
46
47 #include <hw/hw_ser.h>  /* Required for bus macros overrides */
48 #include <hw/hw_cpu.h>  /* CLOCK_FREQ */
49
50 #include <mware/fifobuf.h>
51 #include <cfg/debug.h>
52
53 #include <appconfig.h>
54
55
56 /**
57  * \name Overridable serial bus hooks
58  *
59  * These can be redefined in hw.h to implement
60  * special bus policies such as half-duplex, 485, etc.
61  *
62  *
63  * \code
64  *  TXBEGIN      TXCHAR      TXEND  TXOFF
65  *    |   __________|__________ |     |
66  *    |   |   |   |   |   |   | |     |
67  *    v   v   v   v   v   v   v v     v
68  * ______  __  __  __  __  __  __  ________________
69  *       \/  \/  \/  \/  \/  \/  \/
70  * ______/\__/\__/\__/\__/\__/\__/
71  *
72  * \endcode
73  *
74  * \{
75  */
76
77 #ifndef SER_UART0_IRQ_INIT
78         /**
79          * Default IRQ INIT macro - invoked in uart0_init()
80          *
81          * - Disable all interrupt
82          * - Register USART0 interrupt
83          * - Enable USART0 clock.
84          */
85         #define SER_UART0_IRQ_INIT do { \
86                 US0_IDR = 0xFFFFFFFF; \
87                 /* Set the vector. */ \
88                 AIC_SVR(US0_ID) = uart0_irq_dispatcher; \
89                 /* Initialize to edge triggered with defined priority. */ \
90                 AIC_SMR(US0_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED; \
91                 /* Enable the USART IRQ */ \
92                 AIC_IECR = BV(US0_ID); \
93                 PMC_PCER = BV(US0_ID); \
94         } while (0)
95 #endif
96
97 #ifndef SER_UART0_BUS_TXINIT
98         /**
99          * Default TXINIT macro - invoked in uart0_init()
100          *
101          * - Disable GPIO on USART0 tx/rx pins
102          * - Reset USART0
103          * - Set serial param: mode Normal, 8bit data, 1bit stop
104          * - Enable both the receiver and the transmitter
105          * - Enable only the RX complete interrupt
106          */
107         #if CPU_ARM_AT91
108                 #define SER_UART0_BUS_TXINIT do { \
109                         PIOA_PDR = BV(5) | BV(6);\
110                         US0_CR = BV(US_RSTRX) | BV(US_RSTTX); \
111                         US0_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1; \
112                         US0_CR = BV(US_RXEN) | BV(US_TXEN); \
113                         US0_IER = BV(US_RXRDY); \
114                 } while (0)
115         /*#elif  Add other ARM families here */
116         #else
117                 #error Unknown CPU
118         #endif
119
120 #endif
121
122 #ifndef SER_UART0_BUS_TXBEGIN
123         /**
124          * Invoked before starting a transmission
125          *
126          * - Enable both the receiver and the transmitter
127          * - Enable both the RX complete and TX empty interrupts
128          */
129         #define SER_UART0_BUS_TXBEGIN do { \
130                 US0_CR = BV(US_RXEN) | BV(US_TXEN); \
131                 US0_IER = BV(US_TXRDY) | BV(US_RXRDY); \
132         } while (0)
133 #endif
134
135 #ifndef SER_UART0_BUS_TXCHAR
136         /**
137          * Invoked to send one character.
138          */
139         #define SER_UART0_BUS_TXCHAR(c) do { \
140                 US0_THR = c; \
141         } while (0)
142 #endif
143
144 #ifndef SER_UART0_BUS_TXEND
145         /**
146          * Invoked as soon as the txfifo becomes empty
147          *
148          * - Keep both the receiver and the transmitter enabled
149          * - Keep the RX complete interrupt enabled
150          * - Disable the TX empty interrupts
151          */
152         #define SER_UART0_BUS_TXEND do { \
153                 US0_CR = BV(US_RXEN) | BV(US_TXEN); \
154                 US0_IER = BV(US_RXRDY); \
155                 US0_IDR = BV(US_TXRDY); \
156         } while (0)
157 #endif
158
159 /**
160  * \def CONFIG_SER_STROBE
161  *
162  * This is a debug facility that can be used to
163  * monitor SER interrupt activity on an external pin.
164  *
165  * To use strobes, redefine the macros SER_STROBE_ON,
166  * SER_STROBE_OFF and SER_STROBE_INIT and set
167  * CONFIG_SER_STROBE to 1.
168  */
169 #if !defined(CONFIG_SER_STROBE) || !CONFIG_SER_STROBE
170         #define SER_STROBE_ON    do {/*nop*/} while(0)
171         #define SER_STROBE_OFF   do {/*nop*/} while(0)
172         #define SER_STROBE_INIT  do {/*nop*/} while(0)
173 #endif
174
175
176 /* From the high-level serial driver */
177 extern struct Serial ser_handles[SER_CNT];
178
179 /* TX and RX buffers */
180 static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE];
181 static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE];
182
183 /**
184  * Internal hardware state structure
185  *
186  * The \a sending variable is true while the transmission
187  * interrupt is retriggering itself.
188  *
189  * For the USARTs the \a sending flag is useful for taking specific
190  * actions before sending a burst of data, at the start of a trasmission
191  * but not before every char sent.
192  *
193  * For the SPI, this flag is necessary because the SPI sends and receives
194  * bytes at the same time and the SPI IRQ is unique for send/receive.
195  * The only way to start transmission is to write data in SPDR (this
196  * is done by spi_starttx()). We do this *only* if a transfer is
197  * not already started.
198  */
199 struct ArmSerial
200 {
201         struct SerialHardware hw;
202         volatile bool sending;
203 };
204
205
206 /*
207  * These are to trick GCC into *not* using absolute addressing mode
208  * when accessing ser_handles, which is very expensive.
209  *
210  * Accessing through these pointers generates much shorter
211  * (and hopefully faster) code.
212  */
213 struct Serial *ser_uart0 = &ser_handles[SER_UART0];
214
215 /**
216  * Serial 0 TX interrupt handler
217  */
218 static void serirq_tx(void)
219 {
220         SER_STROBE_ON;
221
222         struct FIFOBuffer * const txfifo = &ser_uart0->txfifo;
223
224         if (fifo_isempty(txfifo))
225         {
226                 SER_UART0_BUS_TXEND;
227         }
228         else
229         {
230                 char c = fifo_pop(txfifo);
231 //              kprintf("Tx char: %c\n", c);
232                 SER_UART0_BUS_TXCHAR(c);
233         }
234
235         SER_STROBE_OFF;
236 }
237
238 /**
239  * Serial 0 RX complete interrupt handler.
240  */
241 static void serirq_rx(void)
242 {
243         SER_STROBE_ON;
244
245         /* Should be read before US_CRS */
246         ser_uart0->status |= US0_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
247
248         char c = US0_RHR;
249         struct FIFOBuffer * const rxfifo = &ser_uart0->rxfifo;
250
251         if (fifo_isfull(rxfifo))
252                 ser_uart0->status |= SERRF_RXFIFOOVERRUN;
253         else
254         {
255 //              kprintf("Recv char: %c\n", c);
256                 fifo_push(rxfifo, c);
257         }
258
259         SER_STROBE_OFF;
260 }
261
262 /**
263  * Serial IRQ dispatcher.
264  */
265 static void uart0_irq_dispatcher(void) __attribute__ ((naked));
266 static void uart0_irq_dispatcher(void)
267 {
268         IRQ_ENTRY();
269
270         if (US0_IMR & BV(US_RXRDY))
271         {
272 //              kprintf("IRQ RX\n");
273                 serirq_rx();
274         }
275         if (US0_IMR & BV(US_TXRDY))
276         {
277 //              kprintf("IRQ TX\n");
278                 serirq_tx();
279         }
280         IRQ_EXIT();
281 }
282
283 /*
284  * Callbacks
285  */
286 static void uart0_init(
287         UNUSED_ARG(struct SerialHardware *, _hw),
288         UNUSED_ARG(struct Serial *, ser))
289 {
290         SER_UART0_IRQ_INIT;
291         SER_UART0_BUS_TXINIT;
292         SER_STROBE_INIT;
293 }
294
295 static void uart0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
296 {
297         US0_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
298 }
299
300 static void uart0_enabletxirq(struct SerialHardware *_hw)
301 {
302         struct ArmSerial *hw = (struct ArmSerial *)_hw;
303
304         /*
305          * WARNING: racy code here!  The tx interrupt sets hw->sending to false
306          * when it runs with an empty fifo.  The order of statements in the
307          * if-block matters.
308          */
309         if (!hw->sending)
310         {
311                 hw->sending = true;
312                 SER_UART0_BUS_TXBEGIN;
313         }
314 }
315
316 static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
317 {
318         /* Compute baud-rate period */
319         US0_BRGR = CLOCK_FREQ / (16 * rate);
320         //DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
321 }
322
323 static void uart0_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
324 {
325         /* Set UART parity */
326         switch(parity)
327         {
328                 case SER_PARITY_NONE:
329                 {
330             /* Parity mode. */
331                         US0_MR |= US_PAR_MASK;
332                         break;
333                 }
334                 case SER_PARITY_EVEN:
335                 {
336             /* Even parity.*/
337                         US0_MR |= US_PAR_EVEN;
338                         break;
339                 }
340                 case SER_PARITY_ODD:
341                 {
342             /* Odd parity.*/
343                         US0_MR |= US_PAR_ODD;
344                         break;
345                 }
346         }
347
348 }
349
350 static bool tx_sending(struct SerialHardware* _hw)
351 {
352         struct ArmSerial *hw = (struct ArmSerial *)_hw;
353         return hw->sending;
354 }
355
356 // FIXME: move into compiler.h?  Ditch?
357 #if COMPILER_C99
358         #define C99INIT(name,val) .name = val
359 #elif defined(__GNUC__)
360         #define C99INIT(name,val) name: val
361 #else
362         #warning No designated initializers, double check your code
363         #define C99INIT(name,val) (val)
364 #endif
365
366 /*
367  * High-level interface data structures
368  */
369 static const struct SerialHardwareVT UART0_VT =
370 {
371         C99INIT(init, uart0_init),
372         C99INIT(cleanup, uart0_cleanup),
373         C99INIT(setBaudrate, uart0_setbaudrate),
374         C99INIT(setParity, uart0_setparity),
375         C99INIT(txStart, uart0_enabletxirq),
376         C99INIT(txSending, tx_sending),
377 };
378
379 static struct ArmSerial UARTDescs[SER_CNT] =
380 {
381         {
382                 C99INIT(hw, /**/) {
383                         C99INIT(table, &UART0_VT),
384                         C99INIT(txbuffer, uart0_txbuffer),
385                         C99INIT(rxbuffer, uart0_rxbuffer),
386                         C99INIT(txbuffer_size, sizeof(uart0_txbuffer)),
387                         C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)),
388                 },
389                 C99INIT(sending, false),
390         }
391 };
392
393 struct SerialHardware *ser_hw_getdesc(int unit)
394 {
395         ASSERT(unit < SER_CNT);
396         return &UARTDescs[unit].hw;
397 }