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>
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.
29 #include <comm/sercomm.h>
31 #include <calypso/irq.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))
42 #define REG_OFFS(m) ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT))
43 /* read access LCR[7] = 0 */
64 /* read access LCR[7] = 1 */
67 DIV1_6
= ACREG
| LCR7BIT
,
68 /* read/write access LCR[7:0] = 0xbf */
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 */
78 /* write access LCR[7] = 0 */
80 #define FCR IIR /* only if EFR[4] = 1 */
88 RX_FIFO_CLEAR
= (1 << 1),
89 TX_FIFO_CLEAR
= (1 << 2),
92 #define TX_FIFO_TRIG_SHIFT 4
93 #define RX_FIFO_TRIG_SHIFT 6
96 IIR_INT_PENDING
= 0x01,
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
)
115 reg
= readb(UART_REG(uart
, LCR
));
120 writeb(reg
, UART_REG(uart
, LCR
));
123 static uint8_t old_lcr
;
124 static void uart_set_lcr_bf(int uart
, int on
)
127 old_lcr
= readb(UART_REG(uart
, LCR
));
128 writeb(0xBF, UART_REG(uart
, LCR
));
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
)
138 /* we assume EFR[4] is always set to 1 */
139 mcr
= readb(UART_REG(uart
, MCR
));
144 writeb(mcr
, UART_REG(uart
, MCR
));
147 static void uart_reg_write(int uart
, enum uart_reg reg
, uint8_t val
)
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
)));
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
)
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
)));
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);
190 static void uart_irq_handler_cons(__unused
enum irq_nr irqnr
)
192 const uint8_t uart
= cons_get_uart();
195 //uart_putchar_nb(uart, 'U');
197 iir
= uart_reg_read(uart
, IIR
);
198 if (iir
& IIR_INT_PENDING
)
201 switch (iir
& IIR_INT_TYPE
) {
202 case IIR_INT_TYPE_RHR
:
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
);
209 uart_reg_write(uart
, IER
, ier
);
212 case IIR_INT_TYPE_MSR
:
214 case IIR_INT_TYPE_RX_STATUS_ERROR
:
216 case IIR_INT_TYPE_RX_TIMEOUT
:
218 case IIR_INT_TYPE_XOFF
:
223 static void uart_irq_handler_sercomm(__unused
enum irq_nr irqnr
)
225 const uint8_t uart
= sercomm_get_uart();
228 //uart_putchar_nb(uart, 'U');
230 iir
= uart_reg_read(uart
, IIR
);
231 if (iir
& IIR_INT_PENDING
)
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);
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);
254 /* write the byte into the TX FIFO */
255 uart_putchar_nb(uart
, ch
);
258 case IIR_INT_TYPE_MSR
:
259 printf("UART IRQ MSR\n");
261 case IIR_INT_TYPE_RX_STATUS_ERROR
:
262 printf("UART IRQ RX_SE\n");
264 case IIR_INT_TYPE_XOFF
:
265 printf("UART IRQXOFF\n");
270 static const uint8_t uart2irq
[] = {
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()) {
283 irq_register_handler(irq
, &uart_irq_handler_cons
);
284 irq_config(irq
, 0, 0, 0xff);
287 } else if (uart
== sercomm_get_uart()) {
290 irq_register_handler(irq
, &uart_irq_handler_sercomm
);
291 irq_config(irq
, 0, 0, 0xff);
294 uart_irq_enable(uart
, UART_IRQ_RX_CHAR
, 1);
300 /* assign UART to MCU and unmask interrupts*/
301 writeb(UART_REG_UIR
, 0x00);
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);
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
);
345 case UART_IRQ_TX_EMPTY
:
348 case UART_IRQ_RX_CHAR
:
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)
377 writeb(c
, UART_REG(uart
, THR
));
381 int uart_getchar_nb(uint8_t uart
, uint8_t *ch
)
385 lsr
= readb(UART_REG(uart
, LSR
));
387 /* something strange happened */
389 printf("LSR RX_OE\n");
391 printf("LSR RX_PE\n");
393 printf("LSR RX_FE\n");
395 printf("LSR RX_BI\n");
397 printf("LSR RX_FIFO_STS\n");
399 /* is the Rx FIFO empty? */
403 *ch
= readb(UART_REG(uart
, RHR
));
404 //printf("getchar_nb(%u) = %02x\n", uart, *ch);
408 int uart_tx_busy(uint8_t uart
)
410 if (readb(UART_REG(uart
, SSR
)) & 0x01)
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
)
428 if (bdrt
>= ARRAY_SIZE(divider
))
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);