Initial import of OsmocomBB into git repository
[osmocom-bb.git] / src / target / firmware / calypso / uart.c
1 /* Calypso DBB internal UART Driver */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.de>
5  *
6  * All Rights Reserved
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  */
23
24 #include <debug.h>
25
26 #include <memory.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 #include <console.h>
32 #include <comm/sercomm.h>
33
34 #include <calypso/irq.h>
35 #include <calypso/uart.h>
36
37 #define BASE_ADDR_UART_MODEM    0xffff5000
38 #define OFFSET_IRDA             0x800
39
40 #define UART_REG(n,m)   (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m))
41
42 #define LCR7BIT         0x80
43 #define LCRBFBIT        0x40
44 #define MCR6BIT         0x20
45 #define REG_OFFS(m)     ((m) &= ~(LCR7BIT|LCRBFBIT|MCR6BIT))
46 /* read access LCR[7] = 0 */
47 enum uart_reg {
48         RHR     = 0,
49         IER     = 1,
50         IIR     = 2,
51         LCR     = 3,
52         MCR     = 4,
53         LSR     = 5,
54         MSR     = 6,
55         SPR     = 7,
56         MDR1    = 8,
57         DMR2    = 9,
58         SFLSR   = 0x0a,
59         RESUME  = 0x0b,
60         SFREGL  = 0x0c,
61         SFREGH  = 0x0d,
62         BLR     = 0x0e,
63         ACREG   = 0x0f,
64         SCR     = 0x10,
65         SSR     = 0x11,
66         EBLR    = 0x12,
67 /* read access LCR[7] = 1 */
68         DLL     = RHR | LCR7BIT,
69         DLH     = IER | LCR7BIT,
70         DIV1_6  = ACREG | LCR7BIT,
71 /* read/write access LCR[7:0] = 0xbf */
72         EFR     = IIR | LCRBFBIT,
73         XON1    = MCR | LCRBFBIT,
74         XON2    = LSR | LCRBFBIT,
75         XOFF1   = MSR | LCRBFBIT,
76         XOFF2   = SPR | LCRBFBIT,
77 /* read/write access if EFR[4] = 1 and MCR[6] = 1 */
78         TCR     = MSR | MCR6BIT,
79         TLR     = SPR | MCR6BIT,
80 };
81 /* write access LCR[7] = 0 */
82 #define THR     RHR
83 #define FCR     IIR             /* only if EFR[4] = 1 */
84 #define TXFLL   SFLSR
85 #define TXFLH   RESUME
86 #define RXFLL   SFREGL
87 #define RXFLH   SFREGH
88
89 enum fcr_bits {
90         FIFO_EN         = (1 << 0),
91         RX_FIFO_CLEAR   = (1 << 1),
92         TX_FIFO_CLEAR   = (1 << 2),
93         DMA_MODE        = (1 << 3),
94 };
95 #define TX_FIFO_TRIG_SHIFT      4
96 #define RX_FIFO_TRIG_SHIFT      6
97
98 enum iir_bits {
99         IIR_INT_PENDING                 = 0x01,
100         IIR_INT_TYPE                    = 0x3E,
101         IIR_INT_TYPE_RX_STATUS_ERROR    = 0x06,
102         IIR_INT_TYPE_RX_TIMEOUT         = 0x0C,
103         IIR_INT_TYPE_RHR                = 0x04,
104         IIR_INT_TYPE_THR                = 0x02,
105         IIR_INT_TYPE_MSR                = 0x00,
106         IIR_INT_TYPE_XOFF               = 0x10,
107         IIR_INT_TYPE_FLOW               = 0x20,
108         IIR_FCR0_MIRROR                 = 0xC0,
109 };
110
111 #define UART_REG_UIR    0xffff6000
112
113 /* enable or disable the divisor latch for access to DLL, DLH */
114 static void uart_set_lcr7bit(int uart, int on)
115 {
116         uint8_t reg;
117
118         reg = readb(UART_REG(uart, LCR));
119         if (on)
120                 reg |= (1 << 7);
121         else
122                 reg &= ~(1 << 7);
123         writeb(reg, UART_REG(uart, LCR));
124 }
125
126 static uint8_t old_lcr;
127 static void uart_set_lcr_bf(int uart, int on)
128 {
129         old_lcr = readb(UART_REG(uart, LCR));
130
131         if (on)
132                 writeb(0xBF, UART_REG(uart, LCR));
133         else
134                 writeb(old_lcr, UART_REG(uart, LCR));
135 }
136
137 /* Enable or disable the TCR_TLR latch bit in MCR[6] */
138 static void uart_set_mcr6bit(int uart, int on)
139 {
140         uint8_t mcr;
141         /* we assume EFR[4] is always set to 1 */
142         mcr = readb(UART_REG(uart, MCR));
143         if (on)
144                 mcr |= (1 << 6);
145         else
146                 mcr &= ~(1 << 6);
147         writeb(mcr, UART_REG(uart, MCR));
148 }
149
150 static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val)
151 {
152         if (reg & LCRBFBIT)
153                 uart_set_lcr_bf(uart, 1);
154         else if (reg & LCR7BIT)
155                 uart_set_lcr7bit(uart, 1);
156         else if (reg & MCR6BIT)
157                 uart_set_mcr6bit(uart, 1);
158
159         writeb(val, UART_REG(uart, REG_OFFS(reg)));
160
161         if (reg & LCRBFBIT)
162                 uart_set_lcr_bf(uart, 0);
163         else if (reg & LCR7BIT)
164                 uart_set_lcr7bit(uart, 0);
165         else if (reg & MCR6BIT)
166                 uart_set_mcr6bit(uart, 0);
167 }
168
169 /* read from a UART register, applying any required latch bits */
170 static uint8_t uart_reg_read(int uart, enum uart_reg reg)
171 {
172         uint8_t ret;
173
174         if (reg & LCRBFBIT)
175                 uart_set_lcr_bf(uart, 1);
176         else if (reg & LCR7BIT)
177                 uart_set_lcr7bit(uart, 1);
178         else if (reg & MCR6BIT)
179                 uart_set_mcr6bit(uart, 1);
180
181         ret = readb(UART_REG(uart, REG_OFFS(reg)));
182
183         if (reg & LCRBFBIT)
184                 uart_set_lcr_bf(uart, 0);
185         else if (reg & LCR7BIT)
186                 uart_set_lcr7bit(uart, 0);
187         else if (reg & MCR6BIT)
188                 uart_set_mcr6bit(uart, 0);
189
190         return ret;
191 }
192
193 static void uart_irq_handler_cons(enum irq_nr irq)
194 {
195         const uint8_t uart = CONS_UART_NR;
196         uint8_t iir;
197
198         //uart_putchar_nb(uart, 'U');
199
200         iir = uart_reg_read(uart, IIR);
201         if (iir & IIR_INT_PENDING)
202                 return;
203
204         switch (iir & IIR_INT_TYPE) {
205         case IIR_INT_TYPE_RHR:
206                 break;
207         case IIR_INT_TYPE_THR:
208                 if (cons_rb_flush() == 1) {
209                         /* everything was flushed, disable THR IRQ */
210                         uint8_t ier = uart_reg_read(uart, IER);
211                         ier &= ~(1 << 1);
212                         uart_reg_write(uart, IER, ier);
213                 }
214                 break;
215         case IIR_INT_TYPE_MSR:
216                 break;
217         case IIR_INT_TYPE_RX_STATUS_ERROR:
218                 break;
219         case IIR_INT_TYPE_RX_TIMEOUT:
220                 break;
221         case IIR_INT_TYPE_XOFF:
222                 break;
223         }
224 }
225
226 static void uart_irq_handler_sercomm(enum irq_nr irq)
227 {
228         const uint8_t uart = SERCOMM_UART_NR;
229         uint8_t iir, ch;
230
231         //uart_putchar_nb(uart, 'U');
232
233         iir = uart_reg_read(uart, IIR);
234         if (iir & IIR_INT_PENDING)
235                 return;
236
237         switch (iir & IIR_INT_TYPE) {
238         case IIR_INT_TYPE_RX_TIMEOUT:
239         case IIR_INT_TYPE_RHR:
240                 /* as long as we have rx data available */
241                 while (uart_getchar_nb(uart, &ch)) {
242                         if (sercomm_drv_rx_char(ch) < 0) {
243                                 /* sercomm cannot receive more data right now */
244                                 uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0);
245                         }
246                 }
247                 break;
248         case IIR_INT_TYPE_THR:
249                 /* as long as we have space in the FIFO */
250                 while (!uart_tx_busy(uart)) {
251                         /* get a byte from sercomm */
252                         if (!sercomm_drv_pull(&ch)) {
253                                 /* no more bytes in sercomm, stop TX interrupts */
254                                 uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0);
255                                 break;
256                         }
257                         /* write the byte into the TX FIFO */
258                         uart_putchar_nb(uart, ch);
259                 }
260                 break;
261         case IIR_INT_TYPE_MSR:
262                 break;
263         case IIR_INT_TYPE_RX_STATUS_ERROR:
264                 break;
265         case IIR_INT_TYPE_XOFF:
266                 break;
267         }
268 }
269
270 static const uint8_t uart2irq[] = {
271         [0]     = IRQ_UART_IRDA,
272         [1]     = IRQ_UART_MODEM,
273 };
274
275 void uart_init(uint8_t uart)
276 {
277         uint8_t irq = uart2irq[uart];
278
279         uart_reg_write(uart, IER, 0x00);
280         if (uart == CONS_UART_NR) {
281                 cons_init();
282                 irq_register_handler(irq, &uart_irq_handler_cons);
283                 irq_config(irq, 0, 0, 0xff);
284                 irq_enable(irq);
285         } else {
286                 sercomm_init();
287                 irq_register_handler(irq, &uart_irq_handler_sercomm);
288                 irq_config(irq, 0, 0, 0xff);
289                 irq_enable(irq);
290                 uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
291         }
292 #if 0
293         if (uart == 1) {
294                 /* assign UART to MCU and unmask interrupts*/
295                 writeb(UART_REG_UIR, 0x00);
296         }
297 #endif
298         /* select  UART mode */
299         uart_reg_write(uart, MDR1, 0);
300         /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */
301         uart_reg_write(uart, EFR, (1 << 4));
302         /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */
303         uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR |
304                         (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT));
305
306         /* THR interrupt only when TX FIFO and TX shift register are empty */
307         uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3));
308
309         /* 8 bit, 1 stop bit, no parity, no break */
310         uart_reg_write(uart, LCR, 0x03);
311
312         uart_set_lcr7bit(uart, 0);
313 }
314
315 void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on)
316 {
317         uint8_t ier = uart_reg_read(uart, IER);
318         uint8_t mask = 0;
319
320         switch (irq) {
321         case UART_IRQ_TX_EMPTY:
322                 mask = (1 << 1);
323                 break;
324         case UART_IRQ_RX_CHAR:
325                 mask = (1 << 0);
326                 break;
327         }
328
329         if (on)
330                 ier |= mask;
331         else
332                 ier &= ~mask;
333
334         uart_reg_write(uart, IER, ier);
335 }
336
337
338 void uart_putchar_wait(uint8_t uart, int c)
339 {
340         /* wait while TX FIFO indicates full */
341         while (readb(UART_REG(uart, SSR)) & 0x01) { }
342
343         /* put character in TX FIFO */
344         writeb(c, UART_REG(uart, THR));
345 }
346
347 int uart_putchar_nb(uint8_t uart, int c)
348 {
349         /* if TX FIFO indicates full, abort */
350         if (readb(UART_REG(uart, SSR)) & 0x01)
351                 return 0;
352
353         writeb(c, UART_REG(uart, THR));
354         return 1;
355 }
356
357 int uart_getchar_nb(uint8_t uart, uint8_t *ch)
358 {
359         if (!(readb(UART_REG(uart, LSR)) & 0x01))
360                 return 0;
361
362         *ch = readb(UART_REG(uart, RHR));
363         return 1;
364 }
365
366 int uart_tx_busy(uint8_t uart)
367 {
368         if (readb(UART_REG(uart, SSR)) & 0x01)
369                 return 1;
370         return 0;
371 }
372
373 static const uint16_t divider[] = {
374         [UART_38400]    = 21,   /*   38,690 */
375         [UART_57600]    = 14,   /*   58,035 */
376         [UART_115200]   = 7,    /*  116,071 */
377         [UART_230400]   = 4,    /*  203,125! (-3% would be 223,488) */
378         [UART_460800]   = 2,    /*  406,250! (-3% would be 446,976) */
379         [UART_921600]   = 1,    /*  812,500! (-3% would be 893,952) */
380 };
381
382 int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt)
383 {
384         uint16_t div;
385
386         if (bdrt > ARRAY_SIZE(divider))
387                 return -1;
388
389         div = divider[bdrt];
390         uart_set_lcr7bit(uart, 1);
391         writeb(div & 0xff, UART_REG(uart, DLL));
392         writeb(div >> 8, UART_REG(uart, DLH));
393         uart_set_lcr7bit(uart, 0);
394
395         return 0;
396 }