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.
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.
33 #include <comm/sercomm.h>
35 #include <calypso/irq.h>
38 #define BASE_ADDR_UART_MODEM 0xffff5000
39 #define OFFSET_IRDA 0x800
41 #define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m))
46 #define REG_OFFS(m) ((m) & ~(LCR7BIT|LCRBFBIT|MCR6BIT))
47 /* read access LCR[7] = 0 */
68 /* read access LCR[7] = 1 */
71 DIV1_6 = ACREG | LCR7BIT,
72 /* read/write access LCR[7:0] = 0xbf */
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 */
82 /* write access LCR[7] = 0 */
84 #define FCR IIR /* only if EFR[4] = 1 */
92 RX_FIFO_CLEAR = (1 << 1),
93 TX_FIFO_CLEAR = (1 << 2),
96 #define TX_FIFO_TRIG_SHIFT 4
97 #define RX_FIFO_TRIG_SHIFT 6
100 IIR_INT_PENDING = 0x01,
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,
112 #define UART_REG_UIR 0xffff6000
114 /* enable or disable the divisor latch for access to DLL, DLH */
115 static void uart_set_lcr7bit(int uart, int on)
119 reg = readb(UART_REG(uart, LCR));
124 writeb(reg, UART_REG(uart, LCR));
127 static uint8_t old_lcr;
128 static void uart_set_lcr_bf(int uart, int on)
131 old_lcr = readb(UART_REG(uart, LCR));
132 writeb(0xBF, UART_REG(uart, LCR));
134 writeb(old_lcr, UART_REG(uart, LCR));
138 /* Enable or disable the TCR_TLR latch bit in MCR[6] */
139 static void uart_set_mcr6bit(int uart, int on)
142 /* we assume EFR[4] is always set to 1 */
143 mcr = readb(UART_REG(uart, MCR));
148 writeb(mcr, UART_REG(uart, MCR));
151 static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val)
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);
160 writeb(val, UART_REG(uart, REG_OFFS(reg)));
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);
170 /* read from a UART register, applying any required latch bits */
171 static uint8_t uart_reg_read(int uart, enum uart_reg reg)
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);
182 ret = readb(UART_REG(uart, REG_OFFS(reg)));
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);
194 static void uart_irq_handler_cons(__unused enum irq_nr irqnr)
196 const uint8_t uart = CONS_UART_NR;
199 //uart_putchar_nb(uart, 'U');
201 iir = uart_reg_read(uart, IIR);
202 if (iir & IIR_INT_PENDING)
205 switch (iir & IIR_INT_TYPE) {
206 case IIR_INT_TYPE_RHR:
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);
213 uart_reg_write(uart, IER, ier);
216 case IIR_INT_TYPE_MSR:
218 case IIR_INT_TYPE_RX_STATUS_ERROR:
220 case IIR_INT_TYPE_RX_TIMEOUT:
222 case IIR_INT_TYPE_XOFF:
227 static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr)
229 const uint8_t uart = SERCOMM_UART_NR;
232 //uart_putchar_nb(uart, 'U');
234 iir = uart_reg_read(uart, IIR);
235 if (iir & IIR_INT_PENDING)
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);
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);
258 /* write the byte into the TX FIFO */
259 uart_putchar_nb(uart, ch);
262 case IIR_INT_TYPE_MSR:
263 printf("UART IRQ MSR\n");
265 case IIR_INT_TYPE_RX_STATUS_ERROR:
266 printf("UART IRQ RX_SE\n");
268 case IIR_INT_TYPE_XOFF:
269 printf("UART IRQXOFF\n");
274 static const uint8_t uart2irq[] = {
276 [1] = IRQ_UART_MODEM,
279 void uart_init(uint8_t uart, uint8_t interrupts)
281 uint8_t irq = uart2irq[uart];
283 uart_reg_write(uart, IER, 0x00);
284 if (uart == CONS_UART_NR) {
287 irq_register_handler(irq, &uart_irq_handler_cons);
288 irq_config(irq, 0, 0, 0xff);
294 irq_register_handler(irq, &uart_irq_handler_sercomm);
295 irq_config(irq, 0, 0, 0xff);
298 uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1);
302 /* assign UART to MCU and unmask interrupts*/
303 writeb(UART_REG_UIR, 0x00);
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 */
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));
324 /* THR interrupt only when TX FIFO and TX shift register are empty */
325 uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3));
327 /* 8 bit, 1 stop bit, no parity, no break */
328 uart_reg_write(uart, LCR, 0x03);
330 uart_set_lcr7bit(uart, 0);
333 void uart_poll(uint8_t uart) {
334 if(uart == CONS_UART_NR) {
335 uart_irq_handler_cons(0);
337 uart_irq_handler_sercomm(0);
341 void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on)
343 uint8_t ier = uart_reg_read(uart, IER);
347 case UART_IRQ_TX_EMPTY:
350 case UART_IRQ_RX_CHAR:
360 uart_reg_write(uart, IER, ier);
364 void uart_putchar_wait(uint8_t uart, int c)
366 /* wait while TX FIFO indicates full */
367 while (readb(UART_REG(uart, SSR)) & 0x01) { }
369 /* put character in TX FIFO */
370 writeb(c, UART_REG(uart, THR));
373 int uart_putchar_nb(uint8_t uart, int c)
375 /* if TX FIFO indicates full, abort */
376 if (readb(UART_REG(uart, SSR)) & 0x01)
379 writeb(c, UART_REG(uart, THR));
383 int uart_getchar_nb(uint8_t uart, uint8_t *ch)
387 lsr = readb(UART_REG(uart, LSR));
389 /* something strange happened */
391 printf("LSR RX_OE\n");
393 printf("LSR RX_PE\n");
395 printf("LSR RX_FE\n");
397 printf("LSR RX_BI\n");
399 printf("LSR RX_FIFO_STS\n");
401 /* is the Rx FIFO empty? */
405 *ch = readb(UART_REG(uart, RHR));
406 //printf("getchar_nb(%u) = %02x\n", uart, *ch);
410 int uart_tx_busy(uint8_t uart)
412 if (readb(UART_REG(uart, SSR)) & 0x01)
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) */
426 int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt)
430 if (bdrt > ARRAY_SIZE(divider))
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);