2 * linux/drivers/char/s3c2410.c
4 * Driver for onboard UARTs on the Samsung S3C2410
6 * Based on drivers/char/serial.c and drivers/char/21285.c
8 * Ben Dooks, (c) 2003 Simtec Electronics
13 #include <linux/config.h>
14 #include <linux/module.h>
15 #include <linux/tty.h>
16 #include <linux/ioport.h>
17 #include <linux/device.h>
18 #include <linux/init.h>
19 #include <linux/console.h>
20 #include <linux/serial_core.h>
21 #include <linux/serial.h>
26 #include <asm/hardware.h>
27 #include <asm/arch/regs-serial.h>
29 #include <asm/mach-types.h>
32 #include <asm/debug-ll.h>
33 #define dbg(x...) llprintk(x)
38 #define SERIAL_S3C2410_NAME "ttySAC"
39 #define SERIAL_S3C2410_MAJOR 204
40 #define SERIAL_S3C2410_MINOR 4
42 /* we can support 3 uarts, but not always use them */
46 static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
48 /* port irq numbers */
50 #define TX_IRQ(port) ((port)->irq + 1)
51 #define RX_IRQ(port) ((port)->irq)
53 #define tx_enabled(port) ((port)->unused[0])
54 #define rx_enabled(port) ((port)->unused[1])
56 /* flag to ignore all characters comming in */
57 #define RXSTAT_DUMMY_READ (0x10000000)
59 /* access functions */
61 #define portaddr(port, reg) ((void *)((port)->membase + (reg)))
63 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
64 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
66 #define wr_regb(port, reg, val) \
67 do { __raw_writeb(val, portaddr(port, reg)); } while(0)
69 #define wr_regl(port, reg, val) \
70 do { __raw_writel(val, portaddr(port, reg)); } while(0)
78 serial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop)
80 if (tx_enabled(port)) {
81 disable_irq(TX_IRQ(port));
87 serial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start)
89 if (!tx_enabled(port)) {
90 enable_irq(TX_IRQ(port));
95 static void serial_s3c2410_stop_rx(struct uart_port *port)
97 if (rx_enabled(port)) {
98 dbg("serial_s3c2410_stop_rx: port=%p\n", port);
99 disable_irq(RX_IRQ(port));
100 rx_enabled(port) = 0;
104 static void serial_s3c2410_enable_ms(struct uart_port *port)
108 /* ? - where has parity gone?? */
109 #define S3C2410_UERSTAT_PARITY (0x1000)
112 serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
114 struct uart_port *port = dev_id;
115 struct tty_struct *tty = port->info->tty;
116 unsigned int ufcon, ch, rxs, ufstat;
119 while (max_count-- > 0) {
120 ufcon = rd_regl(port, S3C2410_UFCON);
121 ufstat = rd_regl(port, S3C2410_UFSTAT);
123 if (S3C2410_UFCON_RXC(ufstat) == 0)
126 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
127 tty->flip.work.func((void *)tty);
128 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
129 printk(KERN_WARNING "TTY_DONT_FLIP set\n");
134 ch = rd_regb(port, S3C2410_URXH);
136 *tty->flip.char_buf_ptr = ch;
137 *tty->flip.flag_buf_ptr = TTY_NORMAL;
140 rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;
142 if (rxs & S3C2410_UERSTAT_ANY) {
143 if (rxs & S3C2410_UERSTAT_FRAME)
144 port->icount.frame++;
145 if (rxs & S3C2410_UERSTAT_OVERRUN)
146 port->icount.overrun++;
148 rxs &= port->read_status_mask;
150 if (rxs & S3C2410_UERSTAT_PARITY)
151 *tty->flip.flag_buf_ptr = TTY_PARITY;
152 else if (rxs & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
153 *tty->flip.flag_buf_ptr = TTY_FRAME;
156 if ((rxs & port->ignore_status_mask) == 0) {
157 tty->flip.flag_buf_ptr++;
158 tty->flip.char_buf_ptr++;
162 if ((rxs & S3C2410_UERSTAT_OVERRUN) &&
163 tty->flip.count < TTY_FLIPBUF_SIZE) {
165 * Overrun is special, since it's reported
166 * immediately, and doesn't affect the current
169 *tty->flip.char_buf_ptr++ = 0;
170 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
174 tty_flip_buffer_push(tty);
181 serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
183 struct uart_port *port = (struct uart_port *)dev_id;
184 struct circ_buf *xmit = &port->info->xmit;
188 wr_regb(port, S3C2410_UTXH, port->x_char);
194 /* if there isnt anything more to transmit, or the uart is now
195 * stopped, disable the uart and exit
198 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
199 serial_s3c2410_stop_tx(port, 0);
203 /* try and drain the buffer... */
205 while (!uart_circ_empty(xmit) && count-- > 0) {
206 if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)
209 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
210 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
214 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
215 uart_write_wakeup(port);
217 if (uart_circ_empty(xmit))
218 serial_s3c2410_stop_tx(port, 0);
225 serial_s3c2410_tx_empty(struct uart_port *port)
227 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
228 return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;
231 /* no modem control lines */
233 serial_s3c2410_get_mctrl(struct uart_port *port)
235 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
237 if (umstat & S3C2410_UMSTAT_CTS)
238 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
240 return TIOCM_CAR | TIOCM_DSR;
244 serial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl)
246 /* todo - possibly remove AFC and do manual CTS */
249 static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
254 spin_lock_irqsave(&port->lock, flags);
256 ucon = rd_regl(port, S3C2410_UCON);
259 ucon |= S3C2410_UCON_SBREAK;
261 ucon &= ~S3C2410_UCON_SBREAK;
263 wr_regl(port, S3C2410_UCON, ucon);
265 spin_unlock_irqrestore(&port->lock, flags);
268 static int serial_s3c2410_startup(struct uart_port *port)
272 tx_enabled(port) = 1;
273 rx_enabled(port) = 1;
275 dbg("serial_s3c2410_startup: port=%p (%p)\n",
276 port, port->mapbase);
278 ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,
279 serial_s3c2410_name, port);
284 ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,
285 serial_s3c2410_name, port);
288 free_irq(RX_IRQ(port), port);
292 /* the port reset code should have done the correct
293 * register setup for the port controls */
298 static void serial_s3c2410_shutdown(struct uart_port *port)
300 free_irq(TX_IRQ(port), port);
301 free_irq(RX_IRQ(port), port);
305 serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
309 unsigned int baud, quot;
313 * We don't support modem control lines.
315 termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
316 termios->c_cflag |= CLOCAL;
319 * We don't support BREAK character recognition.
321 termios->c_iflag &= ~(IGNBRK | BRKINT);
324 * Ask the core to calculate the divisor for us.
326 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
327 quot = uart_get_divisor(port, baud);
329 switch (termios->c_cflag & CSIZE) {
331 dbg("config: 5bits/char\n");
332 ulcon = S3C2410_LCON_CS5;
335 dbg("config: 6bits/char\n");
336 ulcon = S3C2410_LCON_CS6;
339 dbg("config: 7bits/char\n");
340 ulcon = S3C2410_LCON_CS7;
344 dbg("config: 8bits/char\n");
345 ulcon = S3C2410_LCON_CS8;
349 if (termios->c_cflag & CSTOPB)
350 ulcon |= S3C2410_LCON_STOPB;
352 if (termios->c_cflag & PARENB) {
353 if (!(termios->c_cflag & PARODD))
354 ulcon |= S3C2410_LCON_PODD;
356 ulcon |= S3C2410_LCON_PEVEN;
358 ulcon |= S3C2410_LCON_PNONE;
366 spin_lock_irqsave(&port->lock, flags);
368 dbg("setting ulcon to %08x\n", ulcon);
369 //dbg("<flushing output from serial>\n");
371 /* set the ulcon register */
372 wr_regl(port, S3C2410_ULCON, ulcon);
374 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
375 rd_regl(port, S3C2410_ULCON),
376 rd_regl(port, S3C2410_UCON),
377 rd_regl(port, S3C2410_UFCON));
380 * Update the per-port timeout.
382 uart_update_timeout(port, termios->c_cflag, baud);
385 * Which character status flags are we interested in?
387 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
388 if (termios->c_iflag & INPCK)
389 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
392 * Which character status flags should we ignore?
394 port->ignore_status_mask = 0;
395 if (termios->c_iflag & IGNPAR)
396 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
397 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
398 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
401 * Ignore all characters if CREAD is not set.
403 if ((termios->c_cflag & CREAD) == 0)
404 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
406 spin_unlock_irqrestore(&port->lock, flags);
409 static const char *serial_s3c2410_type(struct uart_port *port)
411 return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
414 #define MAP_SIZE (0x100)
417 serial_s3c2410_release_port(struct uart_port *port)
419 release_mem_region(port->mapbase, MAP_SIZE);
423 serial_s3c2410_request_port(struct uart_port *port)
425 return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)
426 != NULL ? 0 : -EBUSY;
430 serial_s3c2410_config_port(struct uart_port *port, int flags)
432 if (flags & UART_CONFIG_TYPE &&
433 serial_s3c2410_request_port(port) == 0)
434 port->type = PORT_S3C2410;
438 * verify the new serial_struct (for TIOCSSERIAL).
441 serial_s3c2410_verify_port(struct uart_port *port, struct serial_struct *ser)
445 if (ser->type != PORT_UNKNOWN && ser->type != PORT_S3C2410)
451 static struct uart_ops serial_s3c2410_ops = {
452 .tx_empty = serial_s3c2410_tx_empty,
453 .get_mctrl = serial_s3c2410_get_mctrl,
454 .set_mctrl = serial_s3c2410_set_mctrl,
455 .stop_tx = serial_s3c2410_stop_tx,
456 .start_tx = serial_s3c2410_start_tx,
457 .stop_rx = serial_s3c2410_stop_rx,
458 .enable_ms = serial_s3c2410_enable_ms,
459 .break_ctl = serial_s3c2410_break_ctl,
460 .startup = serial_s3c2410_startup,
461 .shutdown = serial_s3c2410_shutdown,
462 .set_termios = serial_s3c2410_set_termios,
463 .type = serial_s3c2410_type,
464 .release_port = serial_s3c2410_release_port,
465 .request_port = serial_s3c2410_request_port,
466 .config_port = serial_s3c2410_config_port,
467 .verify_port = serial_s3c2410_verify_port,
470 static struct uart_port serial_s3c2410_ports[NR_PORTS] = {
475 .irq = IRQ_S3CUART_RX0,
478 .ops = &serial_s3c2410_ops,
479 .flags = UPF_BOOT_AUTOCONF,
486 .irq = IRQ_S3CUART_RX1,
489 .ops = &serial_s3c2410_ops,
490 .flags = UPF_BOOT_AUTOCONF,
499 .irq = IRQ_S3CUART_RX2,
502 .ops = &serial_s3c2410_ops,
503 .flags = UPF_BOOT_AUTOCONF,
510 serial_s3c2410_resetport(struct uart_port *port,
511 struct s3c2410_uartcfg *cfg)
513 /* ensure registers are setup */
515 dbg("serial_s3c2410_resetport: port=%p (%08x), cfg=%p\n",
516 port, port->mapbase, cfg);
518 wr_regl(port, S3C2410_UCON, cfg->ucon);
519 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
521 /* reset both fifos */
523 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
524 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
529 /* serial_s3c2410_init_ports
531 * initialise the serial ports from the machine provided initialisation
535 static int serial_s3c2410_init_ports(void)
537 struct uart_port *ptr = serial_s3c2410_ports;
538 struct s3c2410_uartcfg *cfg = s3c2410_uartcfgs;
539 static int inited = 0;
546 dbg("serial_s3c2410_init_ports: initialising ports...\n");
548 for (i = 0; i < NR_PORTS; i++, ptr++, cfg++) {
553 dbg("serial_s3c2410_init_ports: port %d (hw %d)...\n",
556 if (cfg->clock != NULL)
557 ptr->uartclk = *cfg->clock;
559 switch (cfg->hwport) {
561 ptr->mapbase = S3C2410_PA_UART0;
562 ptr->membase = (char *)S3C2410_VA_UART0;
563 ptr->irq = IRQ_S3CUART_RX0;
567 ptr->mapbase = S3C2410_PA_UART1;
568 ptr->membase = (char *)S3C2410_VA_UART1;
569 ptr->irq = IRQ_S3CUART_RX1;
573 ptr->mapbase = S3C2410_PA_UART2;
574 ptr->membase = (char *)S3C2410_VA_UART2;
575 ptr->irq = IRQ_S3CUART_RX2;
579 if (ptr->mapbase == 0)
582 /* reset the fifos (and setup the uart */
583 serial_s3c2410_resetport(ptr, cfg);
589 #ifdef CONFIG_SERIAL_S3C2410_CONSOLE
591 static struct uart_port *cons_uart;
594 serial_s3c2410_console_txrdy(struct uart_port *port, unsigned int ufcon)
596 unsigned long ufstat, utrstat;
598 if (ufcon & S3C2410_UFCON_FIFOMODE) {
599 /* fifo mode - check ammount of data in fifo registers... */
601 ufstat = rd_regl(port, S3C2410_UFSTAT);
603 return S3C2410_UFCON_TXC(ufstat) < 12;
606 /* in non-fifo mode, we go and use the tx buffer empty */
608 utrstat = rd_regl(port, S3C2410_UTRSTAT);
610 return (utrstat & S3C2410_UTRSTAT_TXFE) ? 1 : 0;
614 serial_s3c2410_console_write(struct console *co, const char *s,
618 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
620 for (i = 0; i < count; i++) {
621 while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
624 wr_regb(cons_uart, S3C2410_UTXH, s[i]);
627 while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
630 wr_regb(cons_uart, S3C2410_UTXH, '\r');
636 serial_s3c2410_get_options(struct uart_port *port, int *baud,
637 int *parity, int *bits)
640 unsigned int ulcon, ucon, ubrdiv;
642 ulcon = rd_regl(port, S3C2410_ULCON);
643 ucon = rd_regl(port, S3C2410_UCON);
644 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
646 dbg("serial_s3c2410_get_options: port=%p\n"
647 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
648 port, ulcon, ucon, ubrdiv);
650 if ((ucon & 0xf) != 0) {
651 /* consider the serial port configured if the tx/rx mode set */
653 switch (ulcon & S3C2410_LCON_CSMASK) {
654 case S3C2410_LCON_CS5:
657 case S3C2410_LCON_CS6:
660 case S3C2410_LCON_CS7:
664 case S3C2410_LCON_CS8:
669 switch (ulcon & S3C2410_LCON_PMASK) {
670 case S3C2410_LCON_PEVEN:
674 case S3C2410_LCON_PODD:
679 case S3C2410_LCON_PNONE:
683 /* now calculate the baud rate */
685 *baud = port->uartclk / ( 16 * (ubrdiv + 1));
686 dbg("calculated baud %d\n", *baud);
692 serial_s3c2410_console_setup(struct console *co, char *options)
694 struct uart_port *port;
700 /* is this a valid port */
702 if (co->index == -1 || co->index >= NR_PORTS)
705 port = &serial_s3c2410_ports[co->index];
707 /* is the port configured? */
709 if (port->mapbase == 0x0) {
711 port = &serial_s3c2410_ports[co->index];
716 dbg("serial_s3c2410_console_setup: port=%p (%d)\n", port, co->index);
719 * Check whether an invalid uart number has been specified, and
720 * if so, search for the first available port that does have
724 uart_parse_options(options, &baud, &parity, &bits, &flow);
726 serial_s3c2410_get_options(port, &baud, &parity, &bits);
728 return uart_set_options(port, co, baud, parity, bits, flow);
731 static struct uart_driver s3c2410_uart_drv;
733 static struct console serial_s3c2410_console =
735 .name = SERIAL_S3C2410_NAME,
736 .write = serial_s3c2410_console_write,
737 .device = uart_console_device,
738 .setup = serial_s3c2410_console_setup,
739 .flags = CON_PRINTBUFFER,
741 .data = &s3c2410_uart_drv,
744 static int __init s3c2410_console_init(void)
746 dbg("s3c2410_console_init:\n");
748 serial_s3c2410_init_ports();
749 register_console(&serial_s3c2410_console);
752 console_initcall(s3c2410_console_init);
754 #define SERIAL_S3C2410_CONSOLE &serial_s3c2410_console
756 #define SERIAL_S3C2410_CONSOLE NULL
759 static struct uart_driver s3c2410_uart_drv = {
760 .owner = THIS_MODULE,
761 .driver_name = SERIAL_S3C2410_NAME,
762 .dev_name = SERIAL_S3C2410_NAME,
763 .major = SERIAL_S3C2410_MAJOR,
764 .minor = SERIAL_S3C2410_MINOR,
766 .cons = SERIAL_S3C2410_CONSOLE,
771 static int s3c2410_serial_probe(struct device *_dev);
772 static int s3c2410_serial_remove(struct device *_dev);
774 static struct device_driver s3c2410_serial_drv = {
775 .name = "s3c2410-uart",
776 .bus = &platform_bus_type,
777 .probe = s3c2410_serial_probe,
778 .remove = s3c2410_serial_remove,
783 #define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
785 static int s3c2410_serial_probe(struct device *_dev)
787 struct platform_device *dev = to_platform_device(_dev);
788 struct resource *res = dev->resource;
791 dbg("s3c2410_serial_probe: dev=%p, _dev=%p, res=%p\n", _dev, dev, res);
793 for (i = 0; i < dev->num_resources; i++, res++)
794 if (res->flags & IORESOURCE_MEM)
797 if (i < dev->num_resources) {
798 struct uart_port *ptr = serial_s3c2410_ports;
800 for (i = 0; i < NR_PORTS; i++, ptr++) {
801 dbg("s3c2410_serial_probe: ptr=%p (%08x, %08x)\n",
802 ptr, ptr->mapbase, ptr->membase);
804 if (ptr->mapbase != res->start)
807 dbg("s3c2410_serial_probe: got device %p: port=%p\n",
810 uart_add_one_port(&s3c2410_uart_drv, ptr);
811 dev_set_drvdata(_dev, ptr);
819 static int s3c2410_serial_remove(struct device *_dev)
821 struct uart_port *port = s3c2410_dev_to_port(_dev);
824 uart_remove_one_port(&s3c2410_uart_drv, port);
831 static int __init serial_s3c2410_init(void)
835 printk(KERN_INFO "S3C2410X Serial, (c) 2003 Simtec Electronics\n");
837 ret = uart_register_driver(&s3c2410_uart_drv);
841 ret = driver_register(&s3c2410_serial_drv);
843 uart_unregister_driver(&s3c2410_uart_drv);
849 static void __exit serial_s3c2410_exit(void)
851 driver_unregister(&s3c2410_serial_drv);
852 uart_unregister_driver(&s3c2410_uart_drv);
855 module_init(serial_s3c2410_init);
856 module_exit(serial_s3c2410_exit);
858 MODULE_LICENSE("GPL");
859 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
860 MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver");