sam3 port: add serial module.
[bertos.git] / bertos / cpu / cortex-m3 / drv / ser_sam3.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 Bernie Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief ARM UART and SPI I/O driver
35  *
36  *
37  * \author Daniele Basile <asterix@develer.com>
38  */
39
40 #include "hw/hw_ser.h"  /* Required for bus macros overrides */
41 #include <hw/hw_cpufreq.h>  /* CPU_FREQ */
42
43 #include "cfg/cfg_ser.h"
44 #include <cfg/debug.h>
45
46
47 //#include <io/arm.h>
48 #include <io/cm3.h>
49 #include <drv/irq_cm3.h>
50
51 #include <cpu/attr.h>
52
53 #include <drv/ser.h>
54 #include <drv/ser_p.h>
55
56 #include <struct/fifobuf.h>
57
58
59 #define SERIRQ_PRIORITY 4 ///< default priority for serial irqs.
60
61 /**
62  * \name Overridable serial bus hooks
63  *
64  * These can be redefined in hw.h to implement
65  * special bus policies such as half-duplex, 485, etc.
66  *
67  *
68  * \code
69  *  TXBEGIN      TXCHAR      TXEND  TXOFF
70  *    |   __________|__________ |     |
71  *    |   |   |   |   |   |   | |     |
72  *    v   v   v   v   v   v   v v     v
73  * ______  __  __  __  __  __  __  ________________
74  *       \/  \/  \/  \/  \/  \/  \/
75  * ______/\__/\__/\__/\__/\__/\__/
76  *
77  * \endcode
78  *
79  * \{
80  */
81
82 #ifndef SER_UART0_BUS_TXINIT
83         /**
84          * Default TXINIT macro - invoked in uart0_init()
85          *
86          * - Disable GPIO on USART0 tx/rx pins
87          */
88         #if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
89                 #warning Check USART0 pins!
90         #endif
91         #define SER_UART0_BUS_TXINIT do { \
92                 PIOA_PDR = BV(RXD0) | BV(TXD0); \
93         } while (0)
94 #endif
95
96 #ifndef SER_UART0_BUS_TXBEGIN
97         /**
98          * Invoked before starting a transmission
99          */
100         #define SER_UART0_BUS_TXBEGIN
101 #endif
102
103 #ifndef SER_UART0_BUS_TXCHAR
104         /**
105          * Invoked to send one character.
106          */
107         #define SER_UART0_BUS_TXCHAR(c) do { \
108                 US0_THR = (c); \
109         } while (0)
110 #endif
111
112 #ifndef SER_UART0_BUS_TXEND
113         /**
114          * Invoked as soon as the txfifo becomes empty
115          */
116         #define SER_UART0_BUS_TXEND
117 #endif
118
119 /* End USART0 macros */
120
121 #if !CPU_CM3_AT91SAM3U
122
123         #ifndef SER_UART1_BUS_TXINIT
124                 /**
125                  * Default TXINIT macro - invoked in uart1_init()
126                  *
127                  * - Disable GPIO on USART1 tx/rx pins
128                  */
129                 #if CPU_ARM_AT91
130                         #if !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
131                                 #warning Check USART1 pins!
132                         #endif
133                         #define SER_UART1_BUS_TXINIT do { \
134                                 PIOA_PDR = BV(RXD1) | BV(TXD1); \
135                         } while (0)
136                 #elif CPU_CM3_AT91SAM3
137                         #define SER_UART1_BUS_TXINIT do { \
138                                 PIOB_PDR = BV(RXD1) | BV(TXD1); \
139                         } while (0)
140                 #else
141                         #error Unknown CPU
142                 #endif
143         #endif
144
145         #ifndef SER_UART1_BUS_TXBEGIN
146                 /**
147                  * Invoked before starting a transmission
148                  */
149                 #define SER_UART1_BUS_TXBEGIN
150         #endif
151
152         #ifndef SER_UART1_BUS_TXCHAR
153                 /**
154                  * Invoked to send one character.
155                  */
156                 #define SER_UART1_BUS_TXCHAR(c) do { \
157                         US1_THR = (c); \
158                 } while (0)
159         #endif
160
161         #ifndef SER_UART1_BUS_TXEND
162                 /**
163                  * Invoked as soon as the txfifo becomes empty
164                  */
165                 #define SER_UART1_BUS_TXEND
166         #endif
167
168 #endif
169
170 /**
171 * \name Overridable SPI hooks
172 *
173 * These can be redefined in hw.h to implement
174 * special bus policies such as slave select pin handling, etc.
175 *
176 * \{
177 */
178
179 #ifndef SER_SPI0_BUS_TXINIT
180         /**
181         * Default TXINIT macro - invoked in spi_init()
182         * The default is no action.
183         */
184         #define SER_SPI0_BUS_TXINIT do { \
185                 /* Disable PIO on SPI pins */ \
186                 PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
187         } while (0)
188 #endif
189
190 #ifndef SER_SPI0_BUS_TXCLOSE
191         /**
192         * Invoked after the last character has been transmitted.
193         * The default is no action.
194         */
195         #define SER_SPI0_BUS_TXCLOSE do { \
196                 /* Enable PIO on SPI pins */ \
197                 PIOA_PER = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
198         } while (0)
199 #endif
200
201 #if CPU_ARM_SAM7X
202
203         #ifndef SER_SPI1_BUS_TXINIT
204                 /**
205                 * Default TXINIT macro - invoked in spi_init()
206                 * The default is no action.
207                 */
208                 #define SER_SPI1_BUS_TXINIT do { \
209                         /* Disable PIO on SPI pins */ \
210                         PIOA_PDR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
211                         /* SPI1 pins are on B peripheral function! */ \
212                         PIOA_BSR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
213                 } while (0)
214         #endif
215
216         #ifndef SER_SPI1_BUS_TXCLOSE
217                 /**
218                 * Invoked after the last character has been transmitted.
219                 * The default is no action.
220                 */
221                 #define SER_SPI1_BUS_TXCLOSE do { \
222                         /* Enable PIO on SPI pins */ \
223                         PIOA_PER = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
224                 } while (0)
225         #endif
226 #endif
227 /*\}*/
228
229
230 /**
231  * \name Core dependent interrupt handling macros
232  *
233  * Atmel serial hardware is used on different CPU cores,
234  * i.e. SAM3 and SAM7.  The user interface of the serial
235  * subsystem is identical but core interrupt controllers
236  * are different.
237  *
238  * \{
239  */
240 #if CPU_ARM_AT91
241
242 INLINE void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler)
243 {
244         /* Set the vector. */
245         AIC_SVR(irq) = uart0_irq_dispatcher;
246
247         /* Initialize to level/edge sensitive with defined priority. */
248 #if CPU_ARM_SAM7X
249         if (irq == SPI0_ID || irq == SPI1_ID)
250 #else
251         if (irq == SPI0_ID)
252 #endif
253                 AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_EDGE_TRIGGERED;
254         else // USART/UART
255                 AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
256
257         /* Enable IRQ */
258         AIC_IECR = BV(irq);
259 }
260
261 INLINE void sysirq_setPriority(sysirq_t irq, int prio)
262 {
263         AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_PRIOR_MASK) | SERIRQ_PRIORITY;
264 }
265
266 /** Inform hw that we have served the IRQ */
267 #define SER_INT_ACK do { \
268         AIC_EOICR = 0; \
269 } while (0)
270
271 #elif CPU_CM3_AT91SAM3
272
273 /** Inform hw that we have served the IRQ */
274 #define SER_INT_ACK do { /* nop */ } while (0)
275
276 #else
277         #error No interrupt handling macros defined for current architecture
278 #endif
279
280 /*\}*/
281
282 /* From the high-level serial driver */
283 extern struct Serial *ser_handles[SER_CNT];
284
285 /* TX and RX buffers */
286 static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE];
287 static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE];
288 #if !CPU_CM3_AT91SAM3U
289 static unsigned char uart1_txbuffer[CONFIG_UART1_TXBUFSIZE];
290 static unsigned char uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE];
291 #endif
292 static unsigned char spi0_txbuffer[CONFIG_SPI0_TXBUFSIZE];
293 static unsigned char spi0_rxbuffer[CONFIG_SPI0_RXBUFSIZE];
294 #if CPU_ARM_SAM7X
295 static unsigned char spi1_txbuffer[CONFIG_SPI1_TXBUFSIZE];
296 static unsigned char spi1_rxbuffer[CONFIG_SPI1_RXBUFSIZE];
297 #endif
298
299 /**
300  * Internal hardware state structure
301  *
302  * The \a sending variable is true while the transmission
303  * interrupt is retriggering itself.
304  *
305  * For the USARTs the \a sending flag is useful for taking specific
306  * actions before sending a burst of data, at the start of a trasmission
307  * but not before every char sent.
308  *
309  * For the SPI, this flag is necessary because the SPI sends and receives
310  * bytes at the same time and the SPI IRQ is unique for send/receive.
311  * The only way to start transmission is to write data in SPDR (this
312  * is done by spi_starttx()). We do this *only* if a transfer is
313  * not already started.
314  */
315 struct ArmSerial
316 {
317         struct SerialHardware hw;
318         volatile bool sending;
319 };
320
321 static ISR_PROTO(uart0_irq_dispatcher);
322 #if !CPU_CM3_AT91SAM3U
323 static ISR_PROTO(uart1_irq_dispatcher);
324 #endif
325 static ISR_PROTO(spi0_irq_handler);
326 #if CPU_ARM_SAM7X
327 static ISR_PROTO(spi1_irq_handler);
328 #endif
329 /*
330  * Callbacks for USART0
331  */
332 static void uart0_init(
333         UNUSED_ARG(struct SerialHardware *, _hw),
334         UNUSED_ARG(struct Serial *, ser))
335 {
336         US0_IDR = 0xFFFFFFFF;
337         PMC_PCER = BV(US0_ID);
338
339         /*
340          * - Reset USART0
341          * - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
342          * - Enable both the receiver and the transmitter
343          * - Enable only the RX complete interrupt
344          */
345         US0_CR = BV(US_RSTRX) | BV(US_RSTTX);
346         US0_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
347         US0_CR = BV(US_RXEN) | BV(US_TXEN);
348         US0_IER = BV(US_RXRDY);
349
350         SER_UART0_BUS_TXINIT;
351
352         sysirq_setPriority(US0_ID, SERIRQ_PRIORITY);
353         sysirq_setHandler(US0_ID, uart0_irq_dispatcher);
354
355         SER_STROBE_INIT;
356 }
357
358 static void uart0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
359 {
360         US0_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
361 }
362
363 static void uart0_enabletxirq(struct SerialHardware *_hw)
364 {
365         struct ArmSerial *hw = (struct ArmSerial *)_hw;
366
367         /*
368          * WARNING: racy code here!  The tx interrupt sets hw->sending to false
369          * when it runs with an empty fifo.  The order of statements in the
370          * if-block matters.
371          */
372         if (!hw->sending)
373         {
374                 hw->sending = true;
375                 /*
376                  * - Enable the transmitter
377                  * - Enable TX empty interrupt
378                  */
379                 SER_UART0_BUS_TXBEGIN;
380                 US0_IER = BV(US_TXEMPTY);
381         }
382 }
383
384 static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
385 {
386         /* Compute baud-rate period */
387         US0_BRGR = CPU_FREQ / (16 * rate);
388         //DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
389 }
390
391 static void uart0_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
392 {
393         US0_MR &= ~US_PAR_MASK;
394         /* Set UART parity */
395         switch(parity)
396         {
397                 case SER_PARITY_NONE:
398                 {
399                         /* Parity none. */
400                         US0_MR |= US_PAR_NO;
401                         break;
402                 }
403                 case SER_PARITY_EVEN:
404                 {
405                         /* Even parity. */
406                         US0_MR |= US_PAR_EVEN;
407                         break;
408                 }
409                 case SER_PARITY_ODD:
410                 {
411                         /* Odd parity. */
412                         US0_MR |= US_PAR_ODD;
413                         break;
414                 }
415                 default:
416                         ASSERT(0);
417         }
418 }
419 /*
420  * Callbacks for USART1
421  */
422 static void uart1_init(
423         UNUSED_ARG(struct SerialHardware *, _hw),
424         UNUSED_ARG(struct Serial *, ser))
425 {
426         US1_IDR = 0xFFFFFFFF;
427         PMC_PCER = BV(US1_ID);
428
429         /*
430          * - Reset USART1
431          * - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
432          * - Enable both the receiver and the transmitter
433          * - Enable only the RX complete interrupt
434          */
435         US1_CR = BV(US_RSTRX) | BV(US_RSTTX);
436         US1_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
437         US1_CR = BV(US_RXEN) | BV(US_TXEN);
438         US1_IER = BV(US_RXRDY);
439
440         SER_UART1_BUS_TXINIT;
441
442         sysirq_setPriority(US1_ID, SERIRQ_PRIORITY);
443         sysirq_setHandler(US1_ID, uart1_irq_dispatcher);
444
445         SER_STROBE_INIT;
446 }
447
448 static void uart1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
449 {
450         US1_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
451 }
452
453 static void uart1_enabletxirq(struct SerialHardware *_hw)
454 {
455         struct ArmSerial *hw = (struct ArmSerial *)_hw;
456
457         /*
458          * WARNING: racy code here!  The tx interrupt sets hw->sending to false
459          * when it runs with an empty fifo.  The order of statements in the
460          * if-block matters.
461          */
462         if (!hw->sending)
463         {
464                 hw->sending = true;
465                 /*
466                  * - Enable the transmitter
467                  * - Enable TX empty interrupt
468                  */
469                 SER_UART1_BUS_TXBEGIN;
470                 US1_IER = BV(US_TXEMPTY);
471         }
472 }
473
474 static void uart1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
475 {
476         /* Compute baud-rate period */
477         US1_BRGR = CPU_FREQ / (16 * rate);
478         //DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
479 }
480
481 static void uart1_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
482 {
483         US1_MR &= ~US_PAR_MASK;
484         /* Set UART parity */
485         switch(parity)
486         {
487                 case SER_PARITY_NONE:
488                 {
489                         /* Parity none. */
490                         US1_MR |= US_PAR_NO;
491                         break;
492                 }
493                 case SER_PARITY_EVEN:
494                 {
495                         /* Even parity. */
496                         US1_MR |= US_PAR_EVEN;
497                         break;
498                 }
499                 case SER_PARITY_ODD:
500                 {
501                         /* Odd parity. */
502                         US1_MR |= US_PAR_ODD;
503                         break;
504                 }
505                 default:
506                         ASSERT(0);
507         }
508 }
509
510 /* SPI driver */
511 static void spi0_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
512 {
513         SER_SPI0_BUS_TXINIT;
514
515         /* Reset device */
516         SPI0_CR = BV(SPI_SWRST);
517
518         /*
519          * Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
520          * SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
521          */
522         SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
523
524         /*
525          * Set SPI mode.
526          * At reset clock division factor is set to 0, that is
527          * *forbidden*. Set SPI clock to minimum to keep it valid.
528          */
529         SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
530
531         /* Disable all irqs */
532         SPI0_IDR = 0xFFFFFFFF;
533
534         sysirq_setPriority(SPI0_ID, SERIRQ_PRIORITY);
535         sysirq_setHandler(SPI0_ID, spi0_irq_handler);
536         PMC_PCER = BV(SPI0_ID);
537
538         /* Enable interrupt on tx buffer empty */
539         SPI0_IER = BV(SPI_TXEMPTY);
540
541         /* Enable SPI */
542         SPI0_CR = BV(SPI_SPIEN);
543
544         SER_STROBE_INIT;
545 }
546
547 static void spi0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
548 {
549         /* Disable SPI */
550         SPI0_CR = BV(SPI_SPIDIS);
551
552         /* Disable all irqs */
553         SPI0_IDR = 0xFFFFFFFF;
554
555         SER_SPI0_BUS_TXCLOSE;
556 }
557
558 static void spi0_starttx(struct SerialHardware *_hw)
559 {
560         struct ArmSerial *hw = (struct ArmSerial *)_hw;
561
562         cpu_flags_t flags;
563         IRQ_SAVE_DISABLE(flags);
564
565         /* Send data only if the SPI is not already transmitting */
566         if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
567         {
568                 hw->sending = true;
569                 SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
570         }
571
572         IRQ_RESTORE(flags);
573 }
574
575 static void spi0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
576 {
577         SPI0_CSR0 &= ~SPI_SCBR;
578
579         ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
580         SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
581 }
582
583 #if CPU_ARM_SAM7X
584 /* SPI driver */
585 static void spi1_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
586 {
587         SER_SPI1_BUS_TXINIT;
588
589         /* Reset device */
590         SPI1_CR = BV(SPI_SWRST);
591
592         /*
593          * Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
594          * SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
595          */
596         SPI1_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
597
598         /*
599          * Set SPI mode.
600          * At reset clock division factor is set to 0, that is
601          * *forbidden*. Set SPI clock to minimum to keep it valid.
602          */
603         SPI1_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
604
605         /* Disable all SPI irqs */
606         SPI1_IDR = 0xFFFFFFFF;
607
608         sysirq_setPriority(SPI1_ID, SERIRQ_PRIORITY);
609         sysirq_setHandler(SPI1_ID, spi1_irq_dispatcher);
610         PMC_PCER = BV(SPI1_ID);
611
612         /* Enable interrupt on tx buffer empty */
613         SPI1_IER = BV(SPI_TXEMPTY);
614
615         /* Enable SPI */
616         SPI1_CR = BV(SPI_SPIEN);
617
618         SER_STROBE_INIT;
619 }
620
621 static void spi1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
622 {
623         /* Disable SPI */
624         SPI1_CR = BV(SPI_SPIDIS);
625
626         /* Disable all irqs */
627         SPI1_IDR = 0xFFFFFFFF;
628
629         SER_SPI1_BUS_TXCLOSE;
630 }
631
632 static void spi1_starttx(struct SerialHardware *_hw)
633 {
634         struct ArmSerial *hw = (struct ArmSerial *)_hw;
635
636         cpu_flags_t flags;
637         IRQ_SAVE_DISABLE(flags);
638
639         /* Send data only if the SPI is not already transmitting */
640         if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
641         {
642                 hw->sending = true;
643                 SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
644         }
645
646         IRQ_RESTORE(flags);
647 }
648
649 static void spi1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
650 {
651         SPI1_CSR0 &= ~SPI_SCBR;
652
653         ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
654         SPI1_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
655 }
656 #endif
657
658 static void spi_setparity(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(int, parity))
659 {
660         // nop
661 }
662
663
664 static bool tx_sending(struct SerialHardware* _hw)
665 {
666         struct ArmSerial *hw = (struct ArmSerial *)_hw;
667         return hw->sending;
668 }
669
670 // FIXME: move into compiler.h?  Ditch?
671 #if COMPILER_C99
672         #define C99INIT(name,val) .name = val
673 #elif defined(__GNUC__)
674         #define C99INIT(name,val) name: val
675 #else
676         #warning No designated initializers, double check your code
677         #define C99INIT(name,val) (val)
678 #endif
679
680 /*
681  * High-level interface data structures
682  */
683 static const struct SerialHardwareVT UART0_VT =
684 {
685         C99INIT(init, uart0_init),
686         C99INIT(cleanup, uart0_cleanup),
687         C99INIT(setBaudrate, uart0_setbaudrate),
688         C99INIT(setParity, uart0_setparity),
689         C99INIT(txStart, uart0_enabletxirq),
690         C99INIT(txSending, tx_sending),
691 };
692
693 static const struct SerialHardwareVT UART1_VT =
694 {
695         C99INIT(init, uart1_init),
696         C99INIT(cleanup, uart1_cleanup),
697         C99INIT(setBaudrate, uart1_setbaudrate),
698         C99INIT(setParity, uart1_setparity),
699         C99INIT(txStart, uart1_enabletxirq),
700         C99INIT(txSending, tx_sending),
701 };
702
703 static const struct SerialHardwareVT SPI0_VT =
704 {
705         C99INIT(init, spi0_init),
706         C99INIT(cleanup, spi0_cleanup),
707         C99INIT(setBaudrate, spi0_setbaudrate),
708         C99INIT(setParity, spi_setparity),
709         C99INIT(txStart, spi0_starttx),
710         C99INIT(txSending, tx_sending),
711 };
712 #if CPU_ARM_SAM7X
713 static const struct SerialHardwareVT SPI1_VT =
714 {
715         C99INIT(init, spi1_init),
716         C99INIT(cleanup, spi1_cleanup),
717         C99INIT(setBaudrate, spi1_setbaudrate),
718         C99INIT(setParity, spi_setparity),
719         C99INIT(txStart, spi1_starttx),
720         C99INIT(txSending, tx_sending),
721 };
722 #endif
723
724 static struct ArmSerial UARTDescs[SER_CNT] =
725 {
726         {
727                 C99INIT(hw, /**/) {
728                         C99INIT(table, &UART0_VT),
729                         C99INIT(txbuffer, uart0_txbuffer),
730                         C99INIT(rxbuffer, uart0_rxbuffer),
731                         C99INIT(txbuffer_size, sizeof(uart0_txbuffer)),
732                         C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)),
733                 },
734                 C99INIT(sending, false),
735         },
736         {
737                 C99INIT(hw, /**/) {
738                         C99INIT(table, &UART1_VT),
739                         C99INIT(txbuffer, uart1_txbuffer),
740                         C99INIT(rxbuffer, uart1_rxbuffer),
741                         C99INIT(txbuffer_size, sizeof(uart1_txbuffer)),
742                         C99INIT(rxbuffer_size, sizeof(uart1_rxbuffer)),
743                 },
744                 C99INIT(sending, false),
745         },
746
747         {
748                 C99INIT(hw, /**/) {
749                         C99INIT(table, &SPI0_VT),
750                         C99INIT(txbuffer, spi0_txbuffer),
751                         C99INIT(rxbuffer, spi0_rxbuffer),
752                         C99INIT(txbuffer_size, sizeof(spi0_txbuffer)),
753                         C99INIT(rxbuffer_size, sizeof(spi0_rxbuffer)),
754                 },
755                 C99INIT(sending, false),
756         },
757         #if CPU_ARM_SAM7X
758         {
759                 C99INIT(hw, /**/) {
760                         C99INIT(table, &SPI1_VT),
761                         C99INIT(txbuffer, spi1_txbuffer),
762                         C99INIT(rxbuffer, spi1_rxbuffer),
763                         C99INIT(txbuffer_size, sizeof(spi1_txbuffer)),
764                         C99INIT(rxbuffer_size, sizeof(spi1_rxbuffer)),
765                 },
766                 C99INIT(sending, false),
767         }
768
769         #endif
770 };
771
772 struct SerialHardware *ser_hw_getdesc(int unit)
773 {
774         ASSERT(unit < SER_CNT);
775         return &UARTDescs[unit].hw;
776 }
777
778 /**
779  * Serial 0 TX interrupt handler
780  */
781 INLINE void uart0_irq_tx(void)
782 {
783         SER_STROBE_ON;
784
785         struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo;
786
787         if (fifo_isempty(txfifo))
788         {
789                 /*
790                  * - Disable the TX empty interrupts
791                  */
792                 US0_IDR = BV(US_TXEMPTY);
793                 SER_UART0_BUS_TXEND;
794                 UARTDescs[SER_UART0].sending = false;
795         }
796         else
797         {
798                 char c = fifo_pop(txfifo);
799                 SER_UART0_BUS_TXCHAR(c);
800         }
801
802         SER_STROBE_OFF;
803 }
804
805 /**
806  * Serial 0 RX complete interrupt handler.
807  */
808 INLINE void uart0_irq_rx(void)
809 {
810         SER_STROBE_ON;
811
812         /* Should be read before US_CRS */
813         ser_handles[SER_UART0]->status |= US0_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
814         US0_CR = BV(US_RSTSTA);
815
816         char c = US0_RHR;
817         struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART0]->rxfifo;
818
819         if (fifo_isfull(rxfifo))
820                 ser_handles[SER_UART0]->status |= SERRF_RXFIFOOVERRUN;
821         else
822                 fifo_push(rxfifo, c);
823
824         SER_STROBE_OFF;
825 }
826
827 /**
828  * Serial IRQ dispatcher for USART0.
829  */
830 static DECLARE_ISR(uart0_irq_dispatcher)
831 {
832         if (US0_CSR & BV(US_RXRDY))
833                 uart0_irq_rx();
834
835         if (US0_CSR & BV(US_TXEMPTY))
836                 uart0_irq_tx();
837
838         SER_INT_ACK;
839 }
840
841 /**
842  * Serial 1 TX interrupt handler
843  */
844 INLINE void uart1_irq_tx(void)
845 {
846         SER_STROBE_ON;
847
848         struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo;
849
850         if (fifo_isempty(txfifo))
851         {
852                 /*
853                  * - Disable the TX empty interrupts
854                  */
855                 US1_IDR = BV(US_TXEMPTY);
856                 SER_UART1_BUS_TXEND;
857                 UARTDescs[SER_UART1].sending = false;
858         }
859         else
860         {
861                 char c = fifo_pop(txfifo);
862                 SER_UART1_BUS_TXCHAR(c);
863         }
864
865         SER_STROBE_OFF;
866 }
867
868 /**
869  * Serial 1 RX complete interrupt handler.
870  */
871 INLINE void uart1_irq_rx(void)
872 {
873         SER_STROBE_ON;
874
875         /* Should be read before US_CRS */
876         ser_handles[SER_UART1]->status |= US1_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
877         US1_CR = BV(US_RSTSTA);
878
879         char c = US1_RHR;
880         struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART1]->rxfifo;
881
882         if (fifo_isfull(rxfifo))
883                 ser_handles[SER_UART1]->status |= SERRF_RXFIFOOVERRUN;
884         else
885                 fifo_push(rxfifo, c);
886
887         SER_STROBE_OFF;
888 }
889
890 /**
891  * Serial IRQ dispatcher for USART1.
892  */
893 static DECLARE_ISR(uart1_irq_dispatcher)
894 {
895         if (US1_CSR & BV(US_RXRDY))
896                 uart1_irq_rx();
897
898         if (US1_CSR & BV(US_TXEMPTY))
899                 uart1_irq_tx();
900
901         SER_INT_ACK;
902 }
903
904 /**
905  * SPI0 interrupt handler
906  */
907 static DECLARE_ISR(spi0_irq_handler)
908 {
909         SER_STROBE_ON;
910
911         char c = SPI0_RDR;
912         /* Read incoming byte. */
913         if (!fifo_isfull(&ser_handles[SER_SPI0]->rxfifo))
914                 fifo_push(&ser_handles[SER_SPI0]->rxfifo, c);
915         /*
916          * FIXME
917         else
918                 ser_handles[SER_SPI0]->status |= SERRF_RXFIFOOVERRUN;
919         */
920
921         /* Send */
922         if (!fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
923                 SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
924         else
925                 UARTDescs[SER_SPI0].sending = false;
926
927         SER_INT_ACK;
928
929         SER_STROBE_OFF;
930 }
931
932
933 #if CPU_ARM_SAM7X
934 /**
935  * SPI1 interrupt handler
936  */
937 static DECLARE_ISR(spi1_irq_handler)
938 {
939         SER_STROBE_ON;
940
941         char c = SPI1_RDR;
942         /* Read incoming byte. */
943         if (!fifo_isfull(&ser_handles[SER_SPI1]->rxfifo))
944                 fifo_push(&ser_handles[SER_SPI1]->rxfifo, c);
945         /*
946          * FIXME
947         else
948                 ser_handles[SER_SPI1]->status |= SERRF_RXFIFOOVERRUN;
949         */
950
951         /* Send */
952         if (!fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
953                 SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
954         else
955                 UARTDescs[SER_SPI1].sending = false;
956
957         SER_INT_ACK;
958
959         SER_STROBE_OFF;
960 }
961 #endif