1 /* drivers/serial/serial_lh7a40x.c
3 * Copyright (C) 2004 Coastal Environmental Systems
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
11 /* Driver for Sharp LH7A40X embedded serial ports
13 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
14 * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
18 * This driver supports the embedded UARTs of the Sharp LH7A40X series
19 * CPUs. While similar to the 16550 and other UART chips, there is
20 * nothing close to register compatibility. Moreover, some of the
21 * modem control lines are not available, either in the chip or they
22 * are lacking in the board-level implementation.
25 * For simplicity, we disable the IR functions of any UART whenever
30 #include <linux/config.h>
31 #include <linux/module.h>
32 #include <linux/tty.h>
33 #include <linux/ioport.h>
34 #include <linux/init.h>
35 #include <linux/serial.h>
36 #include <linux/console.h>
37 #include <linux/sysrq.h>
42 #if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
46 #include <linux/serial_core.h>
48 #include <asm/arch/serial.h>
54 #define ISR_LOOP_LIMIT 256
56 #define UR(p,o) _UR ((p)->membase, o)
57 #define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
58 #define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
59 #define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
61 #define UART_REG_SIZE 32
63 #define UARTEN (0x01) /* UART enable */
64 #define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
66 #define RxEmpty (0x10)
67 #define TxEmpty (0x80)
69 #define nRxRdy RxEmpty
73 #define RxBreak (0x0800)
74 #define RxOverrunError (0x0400)
75 #define RxParityError (0x0200)
76 #define RxFramingError (0x0100)
77 #define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
85 #define ModemInt (0x04)
86 #define RxTimeoutInt (0x08)
94 #define WLEN (0x60) /* Mask for all word-length bits */
96 #define PEN (0x02) /* Parity Enable */
97 #define EPS (0x04) /* Even Parity Set */
98 #define FEN (0x10) /* FIFO Enable */
99 #define BRK (0x01) /* Send Break */
102 struct uart_port_lh7a40x {
103 struct uart_port port;
104 unsigned int statusPrev; /* Most recently read modem status */
107 static void lh7a40xuart_stop_tx (struct uart_port* port, unsigned int tty_stop)
109 BIT_CLR (port, UART_R_INTEN, TxInt);
112 static void lh7a40xuart_start_tx (struct uart_port* port,
113 unsigned int tty_start)
115 BIT_SET (port, UART_R_INTEN, TxInt);
117 /* *** FIXME: do I need to check for startup of the
118 transmitter? The old driver did, but AMBA
122 static void lh7a40xuart_stop_rx (struct uart_port* port)
124 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
127 static void lh7a40xuart_enable_ms (struct uart_port* port)
129 BIT_SET (port, UART_R_INTEN, ModemInt);
134 lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
136 lh7a40xuart_rx_chars (struct uart_port* port)
139 struct tty_struct* tty = port->info->tty;
140 int cbRxMax = 256; /* (Gross) limit on receive */
141 unsigned int data; /* Received data and status */
143 while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
144 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
145 tty->flip.work.func((void*)tty);
146 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
147 printk(KERN_WARNING "TTY_DONT_FLIP set\n");
152 data = UR (port, UART_R_DATA);
154 *tty->flip.char_buf_ptr = (unsigned char) data;
155 *tty->flip.flag_buf_ptr = TTY_NORMAL;
158 if (data & RxError) { /* Quick check, short-circuit */
159 if (data & RxBreak) {
160 data &= ~(RxFramingError | RxParityError);
162 if (uart_handle_break (port))
165 else if (data & RxParityError)
166 ++port->icount.parity;
167 else if (data & RxFramingError)
168 ++port->icount.frame;
169 if (data & RxOverrunError)
170 ++port->icount.overrun;
172 /* Mask by termios, leave Rx'd byte */
173 data &= port->read_status_mask | 0xff;
176 *tty->flip.flag_buf_ptr = TTY_BREAK;
177 else if (data & RxParityError)
178 *tty->flip.flag_buf_ptr = TTY_PARITY;
179 else if (data & RxFramingError)
180 *tty->flip.flag_buf_ptr = TTY_FRAME;
183 if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
186 if ((data & port->ignore_status_mask) == 0) {
187 ++tty->flip.flag_buf_ptr;
188 ++tty->flip.char_buf_ptr;
191 if ((data & RxOverrunError)
192 && tty->flip.count < TTY_FLIPBUF_SIZE) {
194 * Overrun is special, since it's reported
195 * immediately, and doesn't affect the current
198 *tty->flip.char_buf_ptr++ = 0;
199 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
203 tty_flip_buffer_push (tty);
207 static void lh7a40xuart_tx_chars (struct uart_port* port)
209 struct circ_buf* xmit = &port->info->xmit;
210 int cbTxMax = port->fifosize;
213 UR (port, UART_R_DATA) = port->x_char;
218 if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
219 lh7a40xuart_stop_tx (port, 0);
223 /* Unlike the AMBA UART, the lh7a40x UART does not guarantee
224 that at least half of the FIFO is empty. Instead, we check
225 status for every character. Using the AMBA method causes
226 the transmitter to drop characters. */
229 UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
230 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
232 if (uart_circ_empty(xmit))
234 } while (!(UR (port, UART_R_STATUS) & nTxRdy)
237 if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
238 uart_write_wakeup (port);
240 if (uart_circ_empty (xmit))
241 lh7a40xuart_stop_tx (port, 0);
244 static void lh7a40xuart_modem_status (struct uart_port* port)
246 unsigned int status = UR (port, UART_R_STATUS);
248 = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
250 BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
252 if (!delta) /* Only happens if we missed 2 transitions */
255 ((struct uart_port_lh7a40x*) port)->statusPrev = status;
258 uart_handle_dcd_change (port, status & DCD);
264 uart_handle_cts_change (port, status & CTS);
266 wake_up_interruptible (&port->info->delta_msr_wait);
269 static irqreturn_t lh7a40xuart_int (int irq, void* dev_id,
270 struct pt_regs* regs)
272 struct uart_port* port = dev_id;
273 unsigned int cLoopLimit = ISR_LOOP_LIMIT;
274 unsigned int isr = UR (port, UART_R_ISR);
278 if (isr & (RxInt | RxTimeoutInt))
280 lh7a40xuart_rx_chars(port, regs);
282 lh7a40xuart_rx_chars(port);
285 lh7a40xuart_modem_status (port);
287 lh7a40xuart_tx_chars (port);
289 if (--cLoopLimit == 0)
292 isr = UR (port, UART_R_ISR);
293 } while (isr & (RxInt | TxInt | RxTimeoutInt));
298 static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
300 return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
303 static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
305 unsigned int result = 0;
306 unsigned int status = UR (port, UART_R_STATUS);
318 static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
320 /* None of the ports supports DTR. UART1 supports RTS through GPIO. */
321 /* Note, kernel appears to be setting DTR and RTS on console. */
323 /* *** FIXME: this deserves more work. There's some work in
324 tracing all of the IO pins. */
326 if( port->mapbase == UART1_PHYS) {
327 gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
329 if (mctrl & TIOCM_RTS)
330 gpio->pbdr &= ~GPIOB_UART1_RTS;
332 gpio->pbdr |= GPIOB_UART1_RTS;
337 static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
341 spin_lock_irqsave(&port->lock, flags);
342 if (break_state == -1)
343 BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
345 BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
346 spin_unlock_irqrestore(&port->lock, flags);
349 static int lh7a40xuart_startup (struct uart_port* port)
353 retval = request_irq (port->irq, lh7a40xuart_int, 0,
354 "serial_lh7a40x", port);
358 /* Initial modem control-line settings */
359 ((struct uart_port_lh7a40x*) port)->statusPrev
360 = UR (port, UART_R_STATUS);
362 /* There is presently no configuration option to enable IR.
363 Thus, we always disable it. */
365 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
366 BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
371 static void lh7a40xuart_shutdown (struct uart_port* port)
373 free_irq (port->irq, port);
374 BIT_CLR (port, UART_R_FCON, BRK | FEN);
375 BIT_CLR (port, UART_R_CON, UARTEN);
378 static void lh7a40xuart_set_termios (struct uart_port* port,
379 struct termios* termios,
389 baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
390 quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
392 switch (termios->c_cflag & CSIZE) {
407 if (termios->c_cflag & CSTOPB)
409 if (termios->c_cflag & PARENB) {
411 if (!(termios->c_cflag & PARODD))
414 if (port->fifosize > 1)
417 spin_lock_irqsave (&port->lock, flags);
419 uart_update_timeout (port, termios->c_cflag, baud);
421 port->read_status_mask = RxOverrunError;
422 if (termios->c_iflag & INPCK)
423 port->read_status_mask |= RxFramingError | RxParityError;
424 if (termios->c_iflag & (BRKINT | PARMRK))
425 port->read_status_mask |= RxBreak;
427 /* Figure mask for status we ignore */
428 port->ignore_status_mask = 0;
429 if (termios->c_iflag & IGNPAR)
430 port->ignore_status_mask |= RxFramingError | RxParityError;
431 if (termios->c_iflag & IGNBRK) {
432 port->ignore_status_mask |= RxBreak;
433 /* Ignore overrun when ignorning parity */
434 /* *** FIXME: is this in the right place? */
435 if (termios->c_iflag & IGNPAR)
436 port->ignore_status_mask |= RxOverrunError;
439 /* Ignore all receive errors when receive disabled */
440 if ((termios->c_cflag & CREAD) == 0)
441 port->ignore_status_mask |= RxError;
443 con = UR (port, UART_R_CON);
444 inten = (UR (port, UART_R_INTEN) & ~ModemInt);
446 if (UART_ENABLE_MS (port, termios->c_cflag))
449 BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
450 UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
451 UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
452 UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
453 UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
454 UR (port, UART_R_CON) = con; /* Restore UART mode */
456 spin_unlock_irqrestore(&port->lock, flags);
459 static const char* lh7a40xuart_type (struct uart_port* port)
461 return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
464 static void lh7a40xuart_release_port (struct uart_port* port)
466 release_mem_region (port->mapbase, UART_REG_SIZE);
469 static int lh7a40xuart_request_port (struct uart_port* port)
471 return request_mem_region (port->mapbase, UART_REG_SIZE,
472 "serial_lh7a40x") != NULL
476 static void lh7a40xuart_config_port (struct uart_port* port, int flags)
478 if (flags & UART_CONFIG_TYPE) {
479 port->type = PORT_LH7A40X;
480 lh7a40xuart_request_port (port);
484 static int lh7a40xuart_verify_port (struct uart_port* port,
485 struct serial_struct* ser)
489 if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
491 if (ser->irq < 0 || ser->irq >= NR_IRQS)
493 if (ser->baud_base < 9600) /* *** FIXME: is this true? */
498 static struct uart_ops lh7a40x_uart_ops = {
499 .tx_empty = lh7a40xuart_tx_empty,
500 .set_mctrl = lh7a40xuart_set_mctrl,
501 .get_mctrl = lh7a40xuart_get_mctrl,
502 .stop_tx = lh7a40xuart_stop_tx,
503 .start_tx = lh7a40xuart_start_tx,
504 .stop_rx = lh7a40xuart_stop_rx,
505 .enable_ms = lh7a40xuart_enable_ms,
506 .break_ctl = lh7a40xuart_break_ctl,
507 .startup = lh7a40xuart_startup,
508 .shutdown = lh7a40xuart_shutdown,
509 .set_termios = lh7a40xuart_set_termios,
510 .type = lh7a40xuart_type,
511 .release_port = lh7a40xuart_release_port,
512 .request_port = lh7a40xuart_request_port,
513 .config_port = lh7a40xuart_config_port,
514 .verify_port = lh7a40xuart_verify_port,
517 static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
520 .membase = (void*) io_p2v (UART1_PHYS),
521 .mapbase = UART1_PHYS,
522 .iotype = SERIAL_IO_MEM,
523 .irq = IRQ_UART1INTR,
524 .uartclk = 14745600/2,
526 .ops = &lh7a40x_uart_ops,
527 .flags = ASYNC_BOOT_AUTOCONF,
533 .membase = (void*) io_p2v (UART2_PHYS),
534 .mapbase = UART2_PHYS,
535 .iotype = SERIAL_IO_MEM,
536 .irq = IRQ_UART2INTR,
537 .uartclk = 14745600/2,
539 .ops = &lh7a40x_uart_ops,
540 .flags = ASYNC_BOOT_AUTOCONF,
546 .membase = (void*) io_p2v (UART3_PHYS),
547 .mapbase = UART3_PHYS,
548 .iotype = SERIAL_IO_MEM,
549 .irq = IRQ_UART3INTR,
550 .uartclk = 14745600/2,
552 .ops = &lh7a40x_uart_ops,
553 .flags = ASYNC_BOOT_AUTOCONF,
559 #ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
560 # define LH7A40X_CONSOLE NULL
562 # define LH7A40X_CONSOLE &lh7a40x_console
565 static void lh7a40xuart_console_write (struct console* co,
569 struct uart_port* port = &lh7a40x_ports[co->index].port;
570 unsigned int con = UR (port, UART_R_CON);
571 unsigned int inten = UR (port, UART_R_INTEN);
574 UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
575 BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
577 for (; count-- > 0; ++s) {
578 while (UR (port, UART_R_STATUS) & nTxRdy)
580 UR (port, UART_R_DATA) = *s;
582 while ((UR (port, UART_R_STATUS) & TxBusy))
584 UR (port, UART_R_DATA) = '\r';
588 /* Wait until all characters are sent */
589 while (UR (port, UART_R_STATUS) & TxBusy)
592 /* Restore control and interrupt mask */
593 UR (port, UART_R_CON) = con;
594 UR (port, UART_R_INTEN) = inten;
597 static void __init lh7a40xuart_console_get_options (struct uart_port* port,
602 if (UR (port, UART_R_CON) & UARTEN) {
603 unsigned int fcon = UR (port, UART_R_FCON);
604 unsigned int quot = UR (port, UART_R_BRCON) + 1;
606 switch (fcon & (PEN | EPS)) {
607 default: *parity = 'n'; break;
608 case PEN: *parity = 'o'; break;
609 case PEN | EPS: *parity = 'e'; break;
612 switch (fcon & WLEN) {
614 case WLEN_8: *bits = 8; break;
615 case WLEN_7: *bits = 7; break;
616 case WLEN_6: *bits = 6; break;
617 case WLEN_5: *bits = 5; break;
620 *baud = port->uartclk/(16*quot);
624 static int __init lh7a40xuart_console_setup (struct console* co, char* options)
626 struct uart_port* port;
632 if (co->index >= DEV_NR) /* Bounds check on device number */
634 port = &lh7a40x_ports[co->index].port;
637 uart_parse_options (options, &baud, &parity, &bits, &flow);
639 lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
641 return uart_set_options (port, co, baud, parity, bits, flow);
644 extern struct uart_driver lh7a40x_reg;
645 static struct console lh7a40x_console = {
647 .write = lh7a40xuart_console_write,
648 .device = uart_console_device,
649 .setup = lh7a40xuart_console_setup,
650 .flags = CON_PRINTBUFFER,
652 .data = &lh7a40x_reg,
655 static int __init lh7a40xuart_console_init(void)
657 register_console (&lh7a40x_console);
661 console_initcall (lh7a40xuart_console_init);
665 static struct uart_driver lh7a40x_reg = {
666 .owner = THIS_MODULE,
667 .driver_name = "ttyAM",
672 .cons = LH7A40X_CONSOLE,
675 static int __init lh7a40xuart_init(void)
679 printk (KERN_INFO "serial: LH7A40X serial driver\n");
681 ret = uart_register_driver (&lh7a40x_reg);
686 for (i = 0; i < DEV_NR; i++)
687 uart_add_one_port (&lh7a40x_reg,
688 &lh7a40x_ports[i].port);
693 static void __exit lh7a40xuart_exit(void)
697 for (i = 0; i < DEV_NR; i++)
698 uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
700 uart_unregister_driver (&lh7a40x_reg);
703 module_init (lh7a40xuart_init);
704 module_exit (lh7a40xuart_exit);
706 MODULE_AUTHOR ("Marc Singer");
707 MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
708 MODULE_LICENSE ("GPL");