initial commit with v2.6.9
[linux-2.6.9-moxart.git] / drivers / serial / s3c2410.c
blob70d2f833318e1240d83311fd796115961443716a
1 /*
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
10 * Changelog:
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>
23 #include <asm/io.h>
24 #include <asm/irq.h>
26 #include <asm/hardware.h>
27 #include <asm/arch/regs-serial.h>
29 #if 0
30 #include <asm/debug-ll.h>
31 #define dbg(x...) llprintk(x)
32 #else
33 #define dbg(x...)
34 #endif
36 #define SERIAL_S3C2410_NAME "ttySAC"
37 #define SERIAL_S3C2410_MAJOR 204
38 #define SERIAL_S3C2410_MINOR 64
40 /* we can support 3 uarts, but not always use them */
42 #define NR_PORTS (3)
44 static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
46 /* port irq numbers */
48 #define TX_IRQ(port) ((port)->irq + 1)
49 #define RX_IRQ(port) ((port)->irq)
51 #define tx_enabled(port) ((port)->unused[0])
52 #define rx_enabled(port) ((port)->unused[1])
54 /* flag to ignore all characters comming in */
55 #define RXSTAT_DUMMY_READ (0x10000000)
57 /* access functions */
59 #define portaddr(port, reg) ((void *)((port)->membase + (reg)))
61 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
62 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
64 #define wr_regb(port, reg, val) \
65 do { __raw_writeb(val, portaddr(port, reg)); } while(0)
67 #define wr_regl(port, reg, val) \
68 do { __raw_writel(val, portaddr(port, reg)); } while(0)
73 /* code */
75 static void
76 serial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop)
78 if (tx_enabled(port)) {
79 disable_irq(TX_IRQ(port));
80 tx_enabled(port) = 0;
84 static void
85 serial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start)
87 if (!tx_enabled(port)) {
88 enable_irq(TX_IRQ(port));
89 tx_enabled(port) = 1;
93 static void serial_s3c2410_stop_rx(struct uart_port *port)
95 if (rx_enabled(port)) {
96 dbg("serial_s3c2410_stop_rx: port=%p\n", port);
97 disable_irq(RX_IRQ(port));
98 rx_enabled(port) = 0;
102 static void serial_s3c2410_enable_ms(struct uart_port *port)
106 /* ? - where has parity gone?? */
107 #define S3C2410_UERSTAT_PARITY (0x1000)
109 static irqreturn_t
110 serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
112 struct uart_port *port = dev_id;
113 struct tty_struct *tty = port->info->tty;
114 unsigned int ufcon, ch, rxs, ufstat;
115 int max_count = 256;
117 while (max_count-- > 0) {
118 ufcon = rd_regl(port, S3C2410_UFCON);
119 ufstat = rd_regl(port, S3C2410_UFSTAT);
121 if (S3C2410_UFCON_RXC(ufstat) == 0)
122 break;
124 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
125 tty->flip.work.func((void *)tty);
126 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
127 printk(KERN_WARNING "TTY_DONT_FLIP set\n");
128 goto out;
132 ch = rd_regb(port, S3C2410_URXH);
134 *tty->flip.char_buf_ptr = ch;
135 *tty->flip.flag_buf_ptr = TTY_NORMAL;
136 port->icount.rx++;
138 rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;
140 if (rxs & S3C2410_UERSTAT_ANY) {
141 if (rxs & S3C2410_UERSTAT_FRAME)
142 port->icount.frame++;
143 if (rxs & S3C2410_UERSTAT_OVERRUN)
144 port->icount.overrun++;
146 rxs &= port->read_status_mask;
148 if (rxs & S3C2410_UERSTAT_PARITY)
149 *tty->flip.flag_buf_ptr = TTY_PARITY;
150 else if (rxs & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
151 *tty->flip.flag_buf_ptr = TTY_FRAME;
154 if ((rxs & port->ignore_status_mask) == 0) {
155 tty->flip.flag_buf_ptr++;
156 tty->flip.char_buf_ptr++;
157 tty->flip.count++;
160 if ((rxs & S3C2410_UERSTAT_OVERRUN) &&
161 tty->flip.count < TTY_FLIPBUF_SIZE) {
163 * Overrun is special, since it's reported
164 * immediately, and doesn't affect the current
165 * character.
167 *tty->flip.char_buf_ptr++ = 0;
168 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
169 tty->flip.count++;
172 tty_flip_buffer_push(tty);
174 out:
175 return IRQ_HANDLED;
178 static irqreturn_t
179 serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
181 struct uart_port *port = (struct uart_port *)dev_id;
182 struct circ_buf *xmit = &port->info->xmit;
183 int count = 256;
185 if (port->x_char) {
186 wr_regb(port, S3C2410_UTXH, port->x_char);
187 port->icount.tx++;
188 port->x_char = 0;
189 goto out;
192 /* if there isnt anything more to transmit, or the uart is now
193 * stopped, disable the uart and exit
196 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
197 serial_s3c2410_stop_tx(port, 0);
198 goto out;
201 /* try and drain the buffer... */
203 while (!uart_circ_empty(xmit) && count-- > 0) {
204 if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)
205 break;
207 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
208 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
209 port->icount.tx++;
212 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
213 uart_write_wakeup(port);
215 if (uart_circ_empty(xmit))
216 serial_s3c2410_stop_tx(port, 0);
218 out:
219 return IRQ_HANDLED;
222 static unsigned int
223 serial_s3c2410_tx_empty(struct uart_port *port)
225 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
226 return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;
229 /* no modem control lines */
230 static unsigned int
231 serial_s3c2410_get_mctrl(struct uart_port *port)
233 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
235 if (umstat & S3C2410_UMSTAT_CTS)
236 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
237 else
238 return TIOCM_CAR | TIOCM_DSR;
241 static void
242 serial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl)
244 /* todo - possibly remove AFC and do manual CTS */
247 static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
249 unsigned long flags;
250 unsigned int ucon;
252 spin_lock_irqsave(&port->lock, flags);
254 ucon = rd_regl(port, S3C2410_UCON);
256 if (break_state)
257 ucon |= S3C2410_UCON_SBREAK;
258 else
259 ucon &= ~S3C2410_UCON_SBREAK;
261 wr_regl(port, S3C2410_UCON, ucon);
263 spin_unlock_irqrestore(&port->lock, flags);
266 static int serial_s3c2410_startup(struct uart_port *port)
268 int ret;
270 tx_enabled(port) = 1;
271 rx_enabled(port) = 1;
273 dbg("serial_s3c2410_startup: port=%p (%p)\n",
274 port, port->mapbase);
276 ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,
277 serial_s3c2410_name, port);
279 if (ret != 0)
280 return ret;
282 ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,
283 serial_s3c2410_name, port);
285 if (ret) {
286 free_irq(RX_IRQ(port), port);
287 return ret;
290 /* the port reset code should have done the correct
291 * register setup for the port controls */
293 return ret;
296 static void serial_s3c2410_shutdown(struct uart_port *port)
298 free_irq(TX_IRQ(port), port);
299 free_irq(RX_IRQ(port), port);
302 static void
303 serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
304 struct termios *old)
306 unsigned long flags;
307 unsigned int baud, quot;
308 unsigned int ulcon;
311 * We don't support modem control lines.
313 termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
314 termios->c_cflag |= CLOCAL;
317 * We don't support BREAK character recognition.
319 termios->c_iflag &= ~(IGNBRK | BRKINT);
322 * Ask the core to calculate the divisor for us.
324 baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
325 quot = uart_get_divisor(port, baud);
327 switch (termios->c_cflag & CSIZE) {
328 case CS5:
329 dbg("config: 5bits/char\n");
330 ulcon = S3C2410_LCON_CS5;
331 break;
332 case CS6:
333 dbg("config: 6bits/char\n");
334 ulcon = S3C2410_LCON_CS6;
335 break;
336 case CS7:
337 dbg("config: 7bits/char\n");
338 ulcon = S3C2410_LCON_CS7;
339 break;
340 case CS8:
341 default:
342 dbg("config: 8bits/char\n");
343 ulcon = S3C2410_LCON_CS8;
344 break;
347 if (termios->c_cflag & CSTOPB)
348 ulcon |= S3C2410_LCON_STOPB;
350 if (termios->c_cflag & PARENB) {
351 if (!(termios->c_cflag & PARODD))
352 ulcon |= S3C2410_LCON_PODD;
353 else
354 ulcon |= S3C2410_LCON_PEVEN;
355 } else {
356 ulcon |= S3C2410_LCON_PNONE;
360 if (port->fifosize)
361 enable_fifo()
364 spin_lock_irqsave(&port->lock, flags);
366 dbg("setting ulcon to %08x\n", ulcon);
367 //dbg("<flushing output from serial>\n");
369 /* set the ulcon register */
370 wr_regl(port, S3C2410_ULCON, ulcon);
372 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
373 rd_regl(port, S3C2410_ULCON),
374 rd_regl(port, S3C2410_UCON),
375 rd_regl(port, S3C2410_UFCON));
378 * Update the per-port timeout.
380 uart_update_timeout(port, termios->c_cflag, baud);
383 * Which character status flags are we interested in?
385 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
386 if (termios->c_iflag & INPCK)
387 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
390 * Which character status flags should we ignore?
392 port->ignore_status_mask = 0;
393 if (termios->c_iflag & IGNPAR)
394 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
395 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
396 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
399 * Ignore all characters if CREAD is not set.
401 if ((termios->c_cflag & CREAD) == 0)
402 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
404 spin_unlock_irqrestore(&port->lock, flags);
407 static const char *serial_s3c2410_type(struct uart_port *port)
409 return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
412 #define MAP_SIZE (0x100)
414 static void
415 serial_s3c2410_release_port(struct uart_port *port)
417 release_mem_region(port->mapbase, MAP_SIZE);
420 static int
421 serial_s3c2410_request_port(struct uart_port *port)
423 return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)
424 != NULL ? 0 : -EBUSY;
427 static void
428 serial_s3c2410_config_port(struct uart_port *port, int flags)
430 if (flags & UART_CONFIG_TYPE &&
431 serial_s3c2410_request_port(port) == 0)
432 port->type = PORT_S3C2410;
436 * verify the new serial_struct (for TIOCSSERIAL).
438 static int
439 serial_s3c2410_verify_port(struct uart_port *port, struct serial_struct *ser)
441 int ret = 0;
443 if (ser->type != PORT_UNKNOWN && ser->type != PORT_S3C2410)
444 ret = -EINVAL;
446 return ret;
449 static struct uart_ops serial_s3c2410_ops = {
450 .tx_empty = serial_s3c2410_tx_empty,
451 .get_mctrl = serial_s3c2410_get_mctrl,
452 .set_mctrl = serial_s3c2410_set_mctrl,
453 .stop_tx = serial_s3c2410_stop_tx,
454 .start_tx = serial_s3c2410_start_tx,
455 .stop_rx = serial_s3c2410_stop_rx,
456 .enable_ms = serial_s3c2410_enable_ms,
457 .break_ctl = serial_s3c2410_break_ctl,
458 .startup = serial_s3c2410_startup,
459 .shutdown = serial_s3c2410_shutdown,
460 .set_termios = serial_s3c2410_set_termios,
461 .type = serial_s3c2410_type,
462 .release_port = serial_s3c2410_release_port,
463 .request_port = serial_s3c2410_request_port,
464 .config_port = serial_s3c2410_config_port,
465 .verify_port = serial_s3c2410_verify_port,
468 static struct uart_port serial_s3c2410_ports[NR_PORTS] = {
470 .membase = 0,
471 .mapbase = 0,
472 .iotype = UPIO_MEM,
473 .irq = IRQ_S3CUART_RX0,
474 .uartclk = 0,
475 .fifosize = 16,
476 .ops = &serial_s3c2410_ops,
477 .flags = UPF_BOOT_AUTOCONF,
478 .line = 0,
481 .membase = 0,
482 .mapbase = 0,
483 .iotype = UPIO_MEM,
484 .irq = IRQ_S3CUART_RX1,
485 .uartclk = 0,
486 .fifosize = 16,
487 .ops = &serial_s3c2410_ops,
488 .flags = UPF_BOOT_AUTOCONF,
489 .line = 1,
491 #if NR_PORTS > 2
494 .membase = 0,
495 .mapbase = 0,
496 .iotype = UPIO_MEM,
497 .irq = IRQ_S3CUART_RX2,
498 .uartclk = 0,
499 .fifosize = 16,
500 .ops = &serial_s3c2410_ops,
501 .flags = UPF_BOOT_AUTOCONF,
502 .line = 2,
504 #endif
507 static int
508 serial_s3c2410_resetport(struct uart_port *port,
509 struct s3c2410_uartcfg *cfg)
511 /* ensure registers are setup */
513 dbg("serial_s3c2410_resetport: port=%p (%08x), cfg=%p\n",
514 port, port->mapbase, cfg);
516 wr_regl(port, S3C2410_UCON, cfg->ucon);
517 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
519 /* reset both fifos */
521 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
522 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
524 return 0;
527 /* serial_s3c2410_init_ports
529 * initialise the serial ports from the machine provided initialisation
530 * data.
533 static int serial_s3c2410_init_ports(void)
535 struct uart_port *ptr = serial_s3c2410_ports;
536 struct s3c2410_uartcfg *cfg = s3c2410_uartcfgs;
537 static int inited = 0;
538 int i;
540 if (inited)
541 return 0;
542 inited = 1;
544 dbg("serial_s3c2410_init_ports: initialising ports...\n");
546 for (i = 0; i < NR_PORTS; i++, ptr++, cfg++) {
548 if (cfg->hwport > 3)
549 continue;
551 dbg("serial_s3c2410_init_ports: port %d (hw %d)...\n",
552 i, cfg->hwport);
554 if (cfg->clock != NULL)
555 ptr->uartclk = *cfg->clock;
557 switch (cfg->hwport) {
558 case 0:
559 ptr->mapbase = S3C2410_PA_UART0;
560 ptr->membase = (char *)S3C2410_VA_UART0;
561 ptr->irq = IRQ_S3CUART_RX0;
562 break;
564 case 1:
565 ptr->mapbase = S3C2410_PA_UART1;
566 ptr->membase = (char *)S3C2410_VA_UART1;
567 ptr->irq = IRQ_S3CUART_RX1;
568 break;
570 case 2:
571 ptr->mapbase = S3C2410_PA_UART2;
572 ptr->membase = (char *)S3C2410_VA_UART2;
573 ptr->irq = IRQ_S3CUART_RX2;
574 break;
577 if (ptr->mapbase == 0)
578 continue;
580 /* reset the fifos (and setup the uart */
581 serial_s3c2410_resetport(ptr, cfg);
584 return 0;
587 #ifdef CONFIG_SERIAL_S3C2410_CONSOLE
589 static struct uart_port *cons_uart;
591 static int
592 serial_s3c2410_console_txrdy(struct uart_port *port, unsigned int ufcon)
594 unsigned long ufstat, utrstat;
596 if (ufcon & S3C2410_UFCON_FIFOMODE) {
597 /* fifo mode - check ammount of data in fifo registers... */
599 ufstat = rd_regl(port, S3C2410_UFSTAT);
601 return S3C2410_UFCON_TXC(ufstat) < 12;
604 /* in non-fifo mode, we go and use the tx buffer empty */
606 utrstat = rd_regl(port, S3C2410_UTRSTAT);
608 return (utrstat & S3C2410_UTRSTAT_TXFE) ? 1 : 0;
611 static void
612 serial_s3c2410_console_write(struct console *co, const char *s,
613 unsigned int count)
615 int i;
616 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
618 for (i = 0; i < count; i++) {
619 while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
620 barrier();
622 wr_regb(cons_uart, S3C2410_UTXH, s[i]);
624 if (s[i] == '\n') {
625 while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
626 barrier();
628 wr_regb(cons_uart, S3C2410_UTXH, '\r');
633 static void __init
634 serial_s3c2410_get_options(struct uart_port *port, int *baud,
635 int *parity, int *bits)
638 unsigned int ulcon, ucon, ubrdiv;
640 ulcon = rd_regl(port, S3C2410_ULCON);
641 ucon = rd_regl(port, S3C2410_UCON);
642 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
644 dbg("serial_s3c2410_get_options: port=%p\n"
645 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
646 port, ulcon, ucon, ubrdiv);
648 if ((ucon & 0xf) != 0) {
649 /* consider the serial port configured if the tx/rx mode set */
651 switch (ulcon & S3C2410_LCON_CSMASK) {
652 case S3C2410_LCON_CS5:
653 *bits = 5;
654 break;
655 case S3C2410_LCON_CS6:
656 *bits = 6;
657 break;
658 case S3C2410_LCON_CS7:
659 *bits = 7;
660 break;
661 default:
662 case S3C2410_LCON_CS8:
663 *bits = 8;
664 break;
667 switch (ulcon & S3C2410_LCON_PMASK) {
668 case S3C2410_LCON_PEVEN:
669 *parity = 'e';
670 break;
672 case S3C2410_LCON_PODD:
673 *parity = 'o';
674 break;
676 default:
677 case S3C2410_LCON_PNONE:
678 *parity = 'n';
681 /* now calculate the baud rate */
683 *baud = port->uartclk / ( 16 * (ubrdiv + 1));
684 dbg("calculated baud %d\n", *baud);
689 static int __init
690 serial_s3c2410_console_setup(struct console *co, char *options)
692 struct uart_port *port;
693 int baud = 9600;
694 int bits = 8;
695 int parity = 'n';
696 int flow = 'n';
698 /* is this a valid port */
700 if (co->index == -1 || co->index >= NR_PORTS)
701 co->index = 0;
703 port = &serial_s3c2410_ports[co->index];
705 /* is the port configured? */
707 if (port->mapbase == 0x0) {
708 co->index = 0;
709 port = &serial_s3c2410_ports[co->index];
712 cons_uart = port;
714 dbg("serial_s3c2410_console_setup: port=%p (%d)\n", port, co->index);
717 * Check whether an invalid uart number has been specified, and
718 * if so, search for the first available port that does have
719 * console support.
721 if (options)
722 uart_parse_options(options, &baud, &parity, &bits, &flow);
723 else
724 serial_s3c2410_get_options(port, &baud, &parity, &bits);
726 return uart_set_options(port, co, baud, parity, bits, flow);
729 static struct uart_driver s3c2410_uart_drv;
731 static struct console serial_s3c2410_console =
733 .name = SERIAL_S3C2410_NAME,
734 .write = serial_s3c2410_console_write,
735 .device = uart_console_device,
736 .setup = serial_s3c2410_console_setup,
737 .flags = CON_PRINTBUFFER,
738 .index = -1,
739 .data = &s3c2410_uart_drv,
742 static int __init s3c2410_console_init(void)
744 dbg("s3c2410_console_init:\n");
746 serial_s3c2410_init_ports();
747 register_console(&serial_s3c2410_console);
748 return 0;
750 console_initcall(s3c2410_console_init);
752 #define SERIAL_S3C2410_CONSOLE &serial_s3c2410_console
753 #else
754 #define SERIAL_S3C2410_CONSOLE NULL
755 #endif
757 static struct uart_driver s3c2410_uart_drv = {
758 .owner = THIS_MODULE,
759 .driver_name = SERIAL_S3C2410_NAME,
760 .dev_name = SERIAL_S3C2410_NAME,
761 .major = SERIAL_S3C2410_MAJOR,
762 .minor = SERIAL_S3C2410_MINOR,
763 .nr = 3,
764 .cons = SERIAL_S3C2410_CONSOLE,
767 /* device driver */
769 static int s3c2410_serial_probe(struct device *_dev);
770 static int s3c2410_serial_remove(struct device *_dev);
772 static struct device_driver s3c2410_serial_drv = {
773 .name = "s3c2410-uart",
774 .bus = &platform_bus_type,
775 .probe = s3c2410_serial_probe,
776 .remove = s3c2410_serial_remove,
777 .suspend = NULL,
778 .resume = NULL,
781 #define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
783 static int s3c2410_serial_probe(struct device *_dev)
785 struct platform_device *dev = to_platform_device(_dev);
786 struct resource *res = dev->resource;
787 int i;
789 dbg("s3c2410_serial_probe: dev=%p, _dev=%p, res=%p\n", _dev, dev, res);
791 for (i = 0; i < dev->num_resources; i++, res++)
792 if (res->flags & IORESOURCE_MEM)
793 break;
795 if (i < dev->num_resources) {
796 struct uart_port *ptr = serial_s3c2410_ports;
798 for (i = 0; i < NR_PORTS; i++, ptr++) {
799 dbg("s3c2410_serial_probe: ptr=%p (%08x, %08x)\n",
800 ptr, ptr->mapbase, ptr->membase);
802 if (ptr->mapbase != res->start)
803 continue;
805 dbg("s3c2410_serial_probe: got device %p: port=%p\n",
806 _dev, ptr);
808 uart_add_one_port(&s3c2410_uart_drv, ptr);
809 dev_set_drvdata(_dev, ptr);
810 break;
814 return 0;
817 static int s3c2410_serial_remove(struct device *_dev)
819 struct uart_port *port = s3c2410_dev_to_port(_dev);
821 if (port)
822 uart_remove_one_port(&s3c2410_uart_drv, port);
824 return 0;
829 static int __init serial_s3c2410_init(void)
831 int ret;
833 printk(KERN_INFO "S3C2410X Serial, (c) 2003 Simtec Electronics\n");
835 ret = uart_register_driver(&s3c2410_uart_drv);
836 if (ret != 0)
837 return ret;
839 ret = driver_register(&s3c2410_serial_drv);
840 if (ret) {
841 uart_unregister_driver(&s3c2410_uart_drv);
844 return ret;
847 static void __exit serial_s3c2410_exit(void)
849 driver_unregister(&s3c2410_serial_drv);
850 uart_unregister_driver(&s3c2410_uart_drv);
853 module_init(serial_s3c2410_init);
854 module_exit(serial_s3c2410_exit);
856 MODULE_LICENSE("GPL");
857 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
858 MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver");