Edge sensitive IRQ on UART
[mw/milkymist.git] / software / libbase / uart-async.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <uart.h>
19 #include <irq.h>
20 #include <hw/uart.h>
21 #include <hw/interrupts.h>
22
23 /*
24  * Buffer sizes must be a power of 2 so that modulos can be computed
25  * with logical AND.
26  * RX functions are written in such a way that they do not require locking.
27  * TX functions already implement locking.
28  */
29
30 #define UART_RINGBUFFER_SIZE_RX 4096
31 #define UART_RINGBUFFER_MASK_RX (UART_RINGBUFFER_SIZE_RX-1)
32
33 static char rx_buf[UART_RINGBUFFER_SIZE_RX];
34 static volatile unsigned int rx_produce;
35 static volatile unsigned int rx_consume;
36
37 void uart_async_isr_rx()
38 {
39         rx_buf[rx_produce] = CSR_UART_RXTX;
40         rx_produce = (rx_produce + 1) & UART_RINGBUFFER_MASK_RX;
41 }
42
43 char readchar()
44 {
45         char c;
46         
47         while(rx_consume == rx_produce);
48         c = rx_buf[rx_consume];
49         rx_consume = (rx_consume + 1) & UART_RINGBUFFER_MASK_RX;
50         return c;
51 }
52
53 int readchar_nonblock()
54 {
55         return (rx_consume != rx_produce);
56 }
57
58 #define UART_RINGBUFFER_SIZE_TX 131072
59 #define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX-1)
60
61 static char tx_buf[UART_RINGBUFFER_SIZE_TX];
62 static unsigned int tx_produce;
63 static unsigned int tx_consume;
64 static volatile int tx_cts;
65
66 static int force_sync;
67
68 void uart_async_isr_tx()
69 {
70         if(tx_produce != tx_consume) {
71                 CSR_UART_RXTX = tx_buf[tx_consume];
72                 tx_consume = (tx_consume + 1) & UART_RINGBUFFER_MASK_TX;
73         } else
74                 tx_cts = 1;
75 }
76
77 void writechar(char c)
78 {
79         unsigned int oldmask = 0;
80         
81         /* Synchronization required because of CTS */
82         oldmask = irq_getmask();
83         irq_setmask(oldmask & (~IRQ_UARTTX));
84         if(force_sync) {
85                 CSR_UART_RXTX = c;
86                 while(!(irq_pending() & IRQ_UARTTX));
87                 irq_ack(IRQ_UARTTX);
88         } else {
89                 if(tx_cts) {
90                         tx_cts = 0;
91                         CSR_UART_RXTX = c;
92                 } else {
93                         tx_buf[tx_produce] = c;
94                         tx_produce = (tx_produce + 1) & UART_RINGBUFFER_MASK_TX;
95                 }
96         }
97         irq_setmask(oldmask);
98 }
99
100 void uart_async_init()
101 {
102         unsigned int mask;
103         
104         rx_produce = 0;
105         rx_consume = 0;
106         tx_produce = 0;
107         tx_consume = 0;
108         tx_cts = 1;
109
110         irq_ack(IRQ_UARTRX|IRQ_UARTTX);
111
112         mask = irq_getmask();
113         mask |= IRQ_UARTRX|IRQ_UARTTX;
114         irq_setmask(mask);
115 }
116
117 void uart_force_sync(int f)
118 {
119         if(f) while(!tx_cts);
120         force_sync = f;
121 }