d3ede4d80175d97c2f4b51547ad853614c45979e
[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 <defines.h>
32 #include <console.h>
33 #include <comm/sercomm.h>
34
35 #include <calypso/irq.h>
36 #include <uart.h>
37
38 #define BASE_ADDR_UART_MODEM    0xffff5000
39 #define OFFSET_IRDA             0x800
40
41 #define UART_REG(n,m)   (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m))
42
43 #define LCR7BIT         0x80
44 #define LCRBFBIT        0x40
45 #define MCR6BIT         0x20
46 #define REG_OFFS(m)     ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT))
47 /* read access LCR[7] = 0 */
48 enum uart_reg {
49         RHR     = 0,
50         IER     = 1,
51         IIR     = 2,
52         LCR     = 3,
53         MCR     = 4,
54         LSR     = 5,
55         MSR     = 6,
56         SPR     = 7,
57         MDR1    = 8,
58         DMR2    = 9,
59         SFLSR   = 0x0a,
60         RESUME  = 0x0b,
61         SFREGL  = 0x0c,
62         SFREGH  = 0x0d,
63         BLR     = 0x0e,
64         ACREG   = 0x0f,
65         SCR     = 0x10,
66         SSR     = 0x11,
67         EBLR    = 0x12,
68 /* read access LCR[7] = 1 */
69         DLL     = RHR | LCR7BIT,
70         DLH     = IER | LCR7BIT,
71         DIV1_6  = ACREG | LCR7BIT,
72 /* read/write access LCR[7:0] = 0xbf */
73         EFR     = IIR | LCRBFBIT,
74         XON1    = MCR | LCRBFBIT,
75         XON2    = LSR | LCRBFBIT,
76         XOFF1   = MSR | LCRBFBIT,
77         XOFF2   = SPR | LCRBFBIT,
78 /* read/write access if EFR[4] = 1 and MCR[6] = 1 */
79         TCR     = MSR | MCR6BIT,
80         TLR     = SPR | MCR6BIT,
81 };
82 /* write access LCR[7] = 0 */
83 #define THR     RHR
84 #define FCR     IIR             /* only if EFR[4] = 1 */
85 #define TXFLL   SFLSR
86 #define TXFLH   RESUME
87 #define RXFLL   SFREGL
88 #define RXFLH   SFREGH
89
90 enum fcr_bits {
91         FIFO_EN         = (1 << 0),
92         RX_FIFO_CLEAR   = (1 << 1),
93         TX_FIFO_CLEAR   = (1 << 2),
94         DMA_MODE        = (1 << 3),
95 };
96 #define TX_FIFO_TRIG_SHIFT      4
97 #define RX_FIFO_TRIG_SHIFT      6
98
99 enum iir_bits {
100         IIR_INT_PENDING                 = 0x01,
101         IIR_INT_TYPE                    = 0x3E,
102         IIR_INT_TYPE_RX_STATUS_ERROR    = 0x06,
103         IIR_INT_TYPE_RX_TIMEOUT         = 0x0C,
104         IIR_INT_TYPE_RHR                = 0x04,
105         IIR_INT_TYPE_THR                = 0x02,
106         IIR_INT_TYPE_MSR                = 0x00,
107         IIR_INT_TYPE_XOFF               = 0x10,
108         IIR_INT_TYPE_FLOW               = 0x20,
109         IIR_FCR0_MIRROR                 = 0xC0,
110 };
111
112 #define UART_REG_UIR    0xffff6000
113
114 /* enable or disable the divisor latch for access to DLL, DLH */
115 static void uart_set_lcr7bit(int uart, int on)
116 {
117         uint8_t reg;
118
119         reg = readb(UART_REG(uart, LCR));
120         if (on)
121                 reg |= (1 << 7);
122         else
123                 reg &= ~(1 << 7);
124         writeb(reg, UART_REG(uart, LCR));
125 }
126
127 static uint8_t old_lcr;
128 static void uart_set_lcr_bf(int uart, int on)
129 {
130         if (on) {
131                 old_lcr = readb(UART_REG(uart, LCR));
132                 writeb(0xBF, UART_REG(uart, LCR));
133         } else {
134                 writeb(old_lcr, UART_REG(uart, LCR));
135         }
136 }
137
138 /* Enable or disable the TCR_TLR latch bit in MCR[6] */
139 static void uart_set_mcr6bit(int uart, int on)
140 {
141         uint8_t mcr;
142         /* we assume EFR[4] is always set to 1 */
143         mcr = readb(UART_REG(uart, MCR));
144         if (on)
145                 mcr |= (1 << 6);
146         else
147                 mcr &= ~(1 << 6);
148         writeb(mcr, UART_REG(uart, MCR));
149 }
150
151 static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val)
152 {
153         if (reg & LCRBFBIT)
154                 uart_set_lcr_bf(uart, 1);
155         else if (reg & LCR7BIT)
156                 uart_set_lcr7bit(uart, 1);
157         else if (reg & MCR6BIT)
158                 uart_set_mcr6bit(uart, 1);
159
160         writeb(val, UART_REG(uart, REG_OFFS(reg)));
161
162         if (reg & LCRBFBIT)
163                 uart_set_lcr_bf(uart, 0);
164         else if (reg & LCR7BIT)
165                 uart_set_lcr7bit(uart, 0);
166         else if (reg & MCR6BIT)
167                 uart_set_mcr6bit(uart, 0);
168 }
169
170 /* read from a UART register, applying any required latch bits */
171 static uint8_t uart_reg_read(int uart, enum uart_reg reg)
172 {
173         uint8_t ret;
174
175         if (reg & LCRBFBIT)
176                 uart_set_lcr_bf(uart, 1);
177         else if (reg & LCR7BIT)
178                 uart_set_lcr7bit(uart, 1);
179         else if (reg & MCR6BIT)
180                 uart_set_mcr6bit(uart, 1);
181
182         ret = readb(UART_REG(uart, REG_OFFS(reg)));
183
184         if (reg & LCRBFBIT)
185                 uart_set_lcr_bf(uart, 0);
186         else if (reg & LCR7BIT)
187                 uart_set_lcr7bit(uart, 0);
188         else if (reg & MCR6BIT)
189                 uart_set_mcr6bit(uart, 0);
190
191         return ret;
192 }
193
194 static void uart_irq_handler_cons(__unused enum irq_nr irqnr)
195 {
196         const uint8_t uart = CONS_UART_NR;
197         uint8_t iir;
198
199         //uart_putchar_nb(uart, 'U');
200
201         iir = uart_reg_read(uart, IIR);
202         if (iir & IIR_INT_PENDING)
203                 return;
204
205         switch (iir & IIR_INT_TYPE) {
206         case IIR_INT_TYPE_RHR:
207                 break;
208         case IIR_INT_TYPE_THR:
209                 if (cons_rb_flush() == 1) {
210                         /* everything was flushed, disable THR IRQ */
211                         uint8_t ier = uart_reg_read(uart, IER);
212                         ier &= ~(1 << 1);
213                         uart_reg_write(uart, IER, ier);
214                 }
215                 break;
216         case IIR_INT_TYPE_MSR:
217                 break;
218         case IIR_INT_TYPE_RX_STATUS_ERROR:
219                 break;
220         case IIR_INT_TYPE_RX_TIMEOUT:
221                 break;
222         case IIR_INT_TYPE_XOFF:
223                 break;
224         }
225 }
226
227 static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr)
228 {
229         const uint8_t uart = SERCOMM_UART_NR;
230         uint8_t iir, ch;
231
232         //uart_putchar_nb(uart, 'U');
233
234         iir = uart_reg_read(uart, IIR);
235         if (iir & IIR_INT_PENDING)
236                 return;
237
238         switch (iir & IIR_INT_TYPE) {
239         case IIR_INT_TYPE_RX_TIMEOUT:
240         case IIR_INT_TYPE_RHR:
241                 /* as long as we have rx data available */
242                 while (uart_getchar_nb(uart, &ch)) {
243                         if (sercomm_drv_rx_char(ch) < 0) {
244                                 /* sercomm cannot receive more data right now */
245                                 uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0);
246                         }
247                 }
248                 break;
249         case IIR_INT_TYPE_THR:
250                 /* as long as we have space in the FIFO */
251                 while (!uart_tx_busy(uart)) {
252                         /* get a byte from sercomm */
253                         if (!sercomm_drv_pull(&ch)) {
254                                 /* no more bytes in sercomm, stop TX interrupts */
255                                 uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0);
256                                 break;
257                         }
258                         /* write the byte into the TX FIFO */
259                         uart_putchar_nb(uart, ch);
260                 }
261                 break;
262         case IIR_INT_TYPE_MSR:
263                 printf("UART IRQ MSR\n");
264                 break;
265         case IIR_INT_TYPE_RX_STATUS_ERROR:
266                 printf("UART IRQ RX_SE\n");
267                 break;
268         case IIR_INT_TYPE_XOFF:
269                 printf("UART IRQXOFF\n");
270                 break;
271         }
272 }
273
274 static const uint8_t uart2irq[] = {
275         [0]     = IRQ_UART_IRDA,
276         [1]     = IRQ_UART_MODEM,
277 };
278
279 void uart_init(uint8_t uart, uint8_t interrupts)
280 {
281         uint8_t irq = uart2irq[uart];
282
283         uart_reg_write(uart, IER, 0x00);
284         if (uart == CONS_UART_NR) {
285                 cons_init();
286                 if(interrupts) {
287                         irq_register_handler(irq, &uart_irq_handler_cons);
288                         irq_config(irq, 0, 0, 0xff);
289                         irq_enable(irq);
290                 }
291         } else {
292                 sercomm_init();
293                 if(interrupts) {
294                         irq_register_handler(irq, &uart_irq_handler_sercomm);
295                         irq_config(irq, 0, 0, 0xff);
296                         irq_enable(irq);
297                 }
298                 uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
299         }
300 #if 0
301         if (uart == 1) {
302                 /* assign UART to MCU and unmask interrupts*/
303                 writeb(UART_REG_UIR, 0x00);
304         }
305 #endif
306
307         /* if we don't initialize these, we get strange corruptions in the
308            received data... :-( */
309         uart_reg_write(uart,  MDR1, 0x07); /* turn off UART */
310         uart_reg_write(uart,  XON1, 0x00); /* Xon1/Addr Register */
311         uart_reg_write(uart,  XON2, 0x00); /* Xon2/Addr Register */
312         uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */
313         uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */
314         uart_reg_write(uart,   EFR, 0x00); /* Enhanced Features Register */
315
316         /* select  UART mode */
317         uart_reg_write(uart, MDR1, 0);
318         /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */
319         uart_reg_write(uart, EFR, (1 << 4));
320         /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */
321         uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR |
322                         (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT));
323
324         /* THR interrupt only when TX FIFO and TX shift register are empty */
325         uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3));
326
327         /* 8 bit, 1 stop bit, no parity, no break */
328         uart_reg_write(uart, LCR, 0x03);
329
330         uart_set_lcr7bit(uart, 0);
331 }
332
333 void uart_poll(uint8_t uart) {
334         if(uart == CONS_UART_NR) {
335                 uart_irq_handler_cons(0);
336         } else {
337                 uart_irq_handler_sercomm(0);
338         }
339 }
340
341 void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on)
342 {
343         uint8_t ier = uart_reg_read(uart, IER);
344         uint8_t mask = 0;
345
346         switch (irq) {
347         case UART_IRQ_TX_EMPTY:
348                 mask = (1 << 1);
349                 break;
350         case UART_IRQ_RX_CHAR:
351                 mask = (1 << 0);
352                 break;
353         }
354
355         if (on)
356                 ier |= mask;
357         else
358                 ier &= ~mask;
359
360         uart_reg_write(uart, IER, ier);
361 }
362
363
364 void uart_putchar_wait(uint8_t uart, int c)
365 {
366         /* wait while TX FIFO indicates full */
367         while (readb(UART_REG(uart, SSR)) & 0x01) { }
368
369         /* put character in TX FIFO */
370         writeb(c, UART_REG(uart, THR));
371 }
372
373 int uart_putchar_nb(uint8_t uart, int c)
374 {
375         /* if TX FIFO indicates full, abort */
376         if (readb(UART_REG(uart, SSR)) & 0x01)
377                 return 0;
378
379         writeb(c, UART_REG(uart, THR));
380         return 1;
381 }
382
383 int uart_getchar_nb(uint8_t uart, uint8_t *ch)
384 {
385         uint8_t lsr;
386
387         lsr = readb(UART_REG(uart, LSR));
388
389         /* something strange happened */
390         if (lsr & 0x02)
391                 printf("LSR RX_OE\n");
392         if (lsr & 0x04)
393                 printf("LSR RX_PE\n");
394         if (lsr & 0x08)
395                 printf("LSR RX_FE\n");
396         if (lsr & 0x10)
397                 printf("LSR RX_BI\n");
398         if (lsr & 0x80)
399                 printf("LSR RX_FIFO_STS\n");
400
401         /* is the Rx FIFO empty? */
402         if (!(lsr & 0x01))
403                 return 0;
404
405         *ch = readb(UART_REG(uart, RHR));
406         //printf("getchar_nb(%u) = %02x\n", uart, *ch);
407         return 1;
408 }
409
410 int uart_tx_busy(uint8_t uart)
411 {
412         if (readb(UART_REG(uart, SSR)) & 0x01)
413                 return 1;
414         return 0;
415 }
416
417 static const uint16_t divider[] = {
418         [UART_38400]    = 21,   /*   38,690 */
419         [UART_57600]    = 14,   /*   58,035 */
420         [UART_115200]   = 7,    /*  116,071 */
421         [UART_230400]   = 4,    /*  203,125! (-3% would be 223,488) */
422         [UART_460800]   = 2,    /*  406,250! (-3% would be 446,976) */
423         [UART_921600]   = 1,    /*  812,500! (-3% would be 893,952) */
424 };
425
426 int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt)
427 {
428         uint16_t div;
429
430         if (bdrt > ARRAY_SIZE(divider))
431                 return -1;
432
433         div = divider[bdrt];
434         uart_set_lcr7bit(uart, 1);
435         writeb(div & 0xff, UART_REG(uart, DLL));
436         writeb(div >> 8, UART_REG(uart, DLH));
437         uart_set_lcr7bit(uart, 0);
438
439         return 0;
440 }