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