vserver 1.9.3
[linux-2.6.git] / drivers / serial / s3c2410.c
1 /*
2  * linux/drivers/char/s3c2410.c
3  *
4  * Driver for onboard UARTs on the Samsung S3C2410
5  *
6  * Based on drivers/char/serial.c and drivers/char/21285.c
7  *
8  * Ben Dooks, (c) 2003 Simtec Electronics
9  *
10  * Changelog:
11  *
12  */
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>
22
23 #include <asm/io.h>
24 #include <asm/irq.h>
25
26 #include <asm/hardware.h>
27 #include <asm/arch/regs-serial.h>
28
29 #if 0
30 #include <asm/debug-ll.h>
31 #define dbg(x...) llprintk(x)
32 #else
33 #define dbg(x...)
34 #endif
35
36 #define SERIAL_S3C2410_NAME     "ttySAC"
37 #define SERIAL_S3C2410_MAJOR    204
38 #define SERIAL_S3C2410_MINOR    64
39
40 /* we can support 3 uarts, but not always use them */
41
42 #define NR_PORTS (3)
43
44 static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
45
46 /* port irq numbers */
47
48 #define TX_IRQ(port) ((port)->irq + 1)
49 #define RX_IRQ(port) ((port)->irq)
50
51 #define tx_enabled(port) ((port)->unused[0])
52 #define rx_enabled(port) ((port)->unused[1])
53
54 /* flag to ignore all characters comming in */
55 #define RXSTAT_DUMMY_READ (0x10000000)
56
57 /* access functions */
58
59 #define portaddr(port, reg) ((void *)((port)->membase + (reg)))
60
61 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
62 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
63
64 #define wr_regb(port, reg, val) \
65   do { __raw_writeb(val, portaddr(port, reg)); } while(0)
66
67 #define wr_regl(port, reg, val) \
68   do { __raw_writel(val, portaddr(port, reg)); } while(0)
69
70
71
72
73 /* code */
74
75 static void
76 serial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop)
77 {
78         if (tx_enabled(port)) {
79                 disable_irq(TX_IRQ(port));
80                 tx_enabled(port) = 0;
81         }
82 }
83
84 static void
85 serial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start)
86 {
87         if (!tx_enabled(port)) {
88                 enable_irq(TX_IRQ(port));
89                 tx_enabled(port) = 1;
90         }
91 }
92
93 static void serial_s3c2410_stop_rx(struct uart_port *port)
94 {
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;
99         }
100 }
101
102 static void serial_s3c2410_enable_ms(struct uart_port *port)
103 {
104 }
105
106 /* ? - where has parity gone?? */
107 #define S3C2410_UERSTAT_PARITY (0x1000)
108
109 static irqreturn_t
110 serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
111 {
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;
116
117         while (max_count-- > 0) {
118                 ufcon = rd_regl(port, S3C2410_UFCON);
119                 ufstat = rd_regl(port, S3C2410_UFSTAT);
120
121                 if (S3C2410_UFCON_RXC(ufstat) == 0)
122                         break;
123
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;
129                         }
130                 }
131
132                 ch = rd_regb(port, S3C2410_URXH);
133
134                 *tty->flip.char_buf_ptr = ch;
135                 *tty->flip.flag_buf_ptr = TTY_NORMAL;
136                 port->icount.rx++;
137
138                 rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;
139
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++;
145
146                         rxs &= port->read_status_mask;
147
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;
152                 }
153
154                 if ((rxs & port->ignore_status_mask) == 0) {
155                         tty->flip.flag_buf_ptr++;
156                         tty->flip.char_buf_ptr++;
157                         tty->flip.count++;
158                 }
159
160                 if ((rxs & S3C2410_UERSTAT_OVERRUN) &&
161                     tty->flip.count < TTY_FLIPBUF_SIZE) {
162                         /*
163                          * Overrun is special, since it's reported
164                          * immediately, and doesn't affect the current
165                          * character.
166                          */
167                         *tty->flip.char_buf_ptr++ = 0;
168                         *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
169                         tty->flip.count++;
170                 }
171         }
172         tty_flip_buffer_push(tty);
173
174  out:
175         return IRQ_HANDLED;
176 }
177
178 static irqreturn_t
179 serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
180 {
181         struct uart_port *port = (struct uart_port *)dev_id;
182         struct circ_buf *xmit = &port->info->xmit;
183         int count = 256;
184
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;
190         }
191
192         /* if there isnt anything more to transmit, or the uart is now
193          * stopped, disable the uart and exit
194         */
195
196         if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
197                 serial_s3c2410_stop_tx(port, 0);
198                 goto out;
199         }
200
201         /* try and drain the buffer... */
202
203         while (!uart_circ_empty(xmit) && count-- > 0) {
204                 if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)
205                         break;
206
207                 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
208                 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
209                 port->icount.tx++;
210         }
211
212         if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
213                 uart_write_wakeup(port);
214
215         if (uart_circ_empty(xmit))
216                 serial_s3c2410_stop_tx(port, 0);
217
218  out:
219         return IRQ_HANDLED;
220 }
221
222 static unsigned int
223 serial_s3c2410_tx_empty(struct uart_port *port)
224 {
225         unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
226         return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;
227 }
228
229 /* no modem control lines */
230 static unsigned int
231 serial_s3c2410_get_mctrl(struct uart_port *port)
232 {
233         unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
234
235         if (umstat & S3C2410_UMSTAT_CTS)
236                 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
237         else
238                 return TIOCM_CAR | TIOCM_DSR;
239 }
240
241 static void
242 serial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl)
243 {
244         /* todo - possibly remove AFC and do manual CTS */
245 }
246
247 static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
248 {
249         unsigned long flags;
250         unsigned int ucon;
251
252         spin_lock_irqsave(&port->lock, flags);
253
254         ucon = rd_regl(port, S3C2410_UCON);
255
256         if (break_state)
257                 ucon |= S3C2410_UCON_SBREAK;
258         else
259                 ucon &= ~S3C2410_UCON_SBREAK;
260
261         wr_regl(port, S3C2410_UCON, ucon);
262
263         spin_unlock_irqrestore(&port->lock, flags);
264 }
265
266 static int serial_s3c2410_startup(struct uart_port *port)
267 {
268         int ret;
269
270         tx_enabled(port) = 1;
271         rx_enabled(port) = 1;
272
273         dbg("serial_s3c2410_startup: port=%p (%p)\n",
274             port, port->mapbase);
275
276         ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,
277                           serial_s3c2410_name, port);
278
279         if (ret != 0)
280                 return ret;
281
282         ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,
283                           serial_s3c2410_name, port);
284
285         if (ret) {
286                 free_irq(RX_IRQ(port), port);
287                 return ret;
288         }
289
290         /* the port reset code should have done the correct
291          * register setup for the port controls */
292
293         return ret;
294 }
295
296 static void serial_s3c2410_shutdown(struct uart_port *port)
297 {
298         free_irq(TX_IRQ(port), port);
299         free_irq(RX_IRQ(port), port);
300 }
301
302 static void
303 serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
304                            struct termios *old)
305 {
306         unsigned long flags;
307         unsigned int baud, quot;
308         unsigned int ulcon;
309
310         /*
311          * We don't support modem control lines.
312          */
313         termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
314         termios->c_cflag |= CLOCAL;
315
316         /*
317          * We don't support BREAK character recognition.
318          */
319         termios->c_iflag &= ~(IGNBRK | BRKINT);
320
321         /*
322          * Ask the core to calculate the divisor for us.
323          */
324         baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
325         quot = uart_get_divisor(port, baud);
326
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;
345         }
346
347         if (termios->c_cflag & CSTOPB)
348                 ulcon |= S3C2410_LCON_STOPB;
349
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;
357         }
358
359         /*
360         if (port->fifosize)
361         enable_fifo()
362         */
363
364         spin_lock_irqsave(&port->lock, flags);
365
366         dbg("setting ulcon to %08x\n", ulcon);
367         //dbg("<flushing output from serial>\n");
368
369         /* set the ulcon register */
370         wr_regl(port, S3C2410_ULCON, ulcon);
371
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));
376
377         /*
378          * Update the per-port timeout.
379          */
380         uart_update_timeout(port, termios->c_cflag, baud);
381
382         /*
383          * Which character status flags are we interested in?
384          */
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;
388
389         /*
390          * Which character status flags should we ignore?
391          */
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;
397
398         /*
399          * Ignore all characters if CREAD is not set.
400          */
401         if ((termios->c_cflag & CREAD) == 0)
402                 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
403
404         spin_unlock_irqrestore(&port->lock, flags);
405 }
406
407 static const char *serial_s3c2410_type(struct uart_port *port)
408 {
409         return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
410 }
411
412 #define MAP_SIZE (0x100)
413
414 static void
415 serial_s3c2410_release_port(struct uart_port *port)
416 {
417         release_mem_region(port->mapbase, MAP_SIZE);
418 }
419
420 static int
421 serial_s3c2410_request_port(struct uart_port *port)
422 {
423         return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)
424                 != NULL ? 0 : -EBUSY;
425 }
426
427 static void
428 serial_s3c2410_config_port(struct uart_port *port, int flags)
429 {
430         if (flags & UART_CONFIG_TYPE &&
431             serial_s3c2410_request_port(port) == 0)
432                 port->type = PORT_S3C2410;
433 }
434
435 /*
436  * verify the new serial_struct (for TIOCSSERIAL).
437  */
438 static int
439 serial_s3c2410_verify_port(struct uart_port *port, struct serial_struct *ser)
440 {
441         int ret = 0;
442
443         if (ser->type != PORT_UNKNOWN && ser->type != PORT_S3C2410)
444                 ret = -EINVAL;
445
446         return ret;
447 }
448
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,
466 };
467
468 static struct uart_port serial_s3c2410_ports[NR_PORTS] = {
469         {
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,
479         },
480         {
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,
490         }
491 #if NR_PORTS > 2
492         ,
493         {
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,
503         }
504 #endif
505 };
506
507 static int
508 serial_s3c2410_resetport(struct uart_port *port,
509                          struct s3c2410_uartcfg *cfg)
510 {
511         /* ensure registers are setup */
512
513         dbg("serial_s3c2410_resetport: port=%p (%08x), cfg=%p\n",
514             port, port->mapbase, cfg);
515
516         wr_regl(port, S3C2410_UCON,  cfg->ucon);
517         wr_regl(port, S3C2410_ULCON, cfg->ulcon);
518
519         /* reset both fifos */
520
521         wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
522         wr_regl(port, S3C2410_UFCON, cfg->ufcon);
523
524         return 0;
525 }
526
527 /* serial_s3c2410_init_ports
528  *
529  * initialise the serial ports from the machine provided initialisation
530  * data.
531 */
532
533 static int serial_s3c2410_init_ports(void)
534 {
535         struct uart_port *ptr = serial_s3c2410_ports;
536         struct s3c2410_uartcfg *cfg = s3c2410_uartcfgs;
537         static int inited = 0;
538         int i;
539
540         if (inited)
541                 return 0;
542         inited = 1;
543
544         dbg("serial_s3c2410_init_ports: initialising ports...\n");
545
546         for (i = 0; i < NR_PORTS; i++, ptr++, cfg++) {
547
548                 if (cfg->hwport > 3)
549                         continue;
550
551                 dbg("serial_s3c2410_init_ports: port %d (hw %d)...\n",
552                     i, cfg->hwport);
553
554                 if (cfg->clock != NULL)
555                         ptr->uartclk = *cfg->clock;
556
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;
563
564                 case 1:
565                         ptr->mapbase = S3C2410_PA_UART1;
566                         ptr->membase = (char *)S3C2410_VA_UART1;
567                         ptr->irq     = IRQ_S3CUART_RX1;
568                         break;
569
570                 case 2:
571                         ptr->mapbase = S3C2410_PA_UART2;
572                         ptr->membase = (char *)S3C2410_VA_UART2;
573                         ptr->irq     = IRQ_S3CUART_RX2;
574                         break;
575                 }
576
577                 if (ptr->mapbase == 0)
578                         continue;
579
580                 /* reset the fifos (and setup the uart */
581                 serial_s3c2410_resetport(ptr, cfg);
582         }
583
584         return 0;
585 }
586
587 #ifdef CONFIG_SERIAL_S3C2410_CONSOLE
588
589 static struct uart_port *cons_uart;
590
591 static int
592 serial_s3c2410_console_txrdy(struct uart_port *port, unsigned int ufcon)
593 {
594         unsigned long ufstat, utrstat;
595
596         if (ufcon & S3C2410_UFCON_FIFOMODE) {
597                 /* fifo mode - check ammount of data in fifo registers... */
598
599                 ufstat = rd_regl(port, S3C2410_UFSTAT);
600
601                 return S3C2410_UFCON_TXC(ufstat) < 12;
602         }
603
604         /* in non-fifo mode, we go and use the tx buffer empty */
605
606         utrstat = rd_regl(port, S3C2410_UTRSTAT);
607
608         return (utrstat & S3C2410_UTRSTAT_TXFE) ? 1 : 0;
609 }
610
611 static void
612 serial_s3c2410_console_write(struct console *co, const char *s,
613                              unsigned int count)
614 {
615         int i;
616         unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
617
618         for (i = 0; i < count; i++) {
619                 while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
620                         barrier();
621
622                 wr_regb(cons_uart, S3C2410_UTXH, s[i]);
623
624                 if (s[i] == '\n') {
625                         while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
626                                 barrier();
627
628                         wr_regb(cons_uart, S3C2410_UTXH, '\r');
629                 }
630         }
631 }
632
633 static void __init
634 serial_s3c2410_get_options(struct uart_port *port, int *baud,
635                            int *parity, int *bits)
636 {
637
638         unsigned int ulcon, ucon, ubrdiv;
639
640         ulcon  = rd_regl(port, S3C2410_ULCON);
641         ucon   = rd_regl(port, S3C2410_UCON);
642         ubrdiv = rd_regl(port, S3C2410_UBRDIV);
643
644         dbg("serial_s3c2410_get_options: port=%p\n"
645             "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
646             port, ulcon, ucon, ubrdiv);
647
648         if ((ucon & 0xf) != 0) {
649                 /* consider the serial port configured if the tx/rx mode set */
650
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;
665                 }
666
667                 switch (ulcon & S3C2410_LCON_PMASK) {
668                 case S3C2410_LCON_PEVEN:
669                         *parity = 'e';
670                         break;
671
672                 case S3C2410_LCON_PODD:
673                         *parity = 'o';
674                         break;
675
676                 default:
677                 case S3C2410_LCON_PNONE:
678                         *parity = 'n';
679                 }
680
681                 /* now calculate the baud rate */
682
683                 *baud = port->uartclk / ( 16 * (ubrdiv + 1));
684                 dbg("calculated baud %d\n", *baud);
685         }
686
687 }
688
689 static int __init
690 serial_s3c2410_console_setup(struct console *co, char *options)
691 {
692         struct uart_port *port;
693         int baud = 9600;
694         int bits = 8;
695         int parity = 'n';
696         int flow = 'n';
697
698         /* is this a valid port */
699
700         if (co->index == -1 || co->index >= NR_PORTS)
701                 co->index = 0;
702
703         port = &serial_s3c2410_ports[co->index];
704
705         /* is the port configured? */
706
707         if (port->mapbase == 0x0) {
708                 co->index = 0;
709                 port = &serial_s3c2410_ports[co->index];
710         }
711
712         cons_uart = port;
713
714         dbg("serial_s3c2410_console_setup: port=%p (%d)\n", port, co->index);
715
716         /*
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.
720          */
721         if (options)
722                 uart_parse_options(options, &baud, &parity, &bits, &flow);
723         else
724                 serial_s3c2410_get_options(port, &baud, &parity, &bits);
725
726         return uart_set_options(port, co, baud, parity, bits, flow);
727 }
728
729 static struct uart_driver s3c2410_uart_drv;
730
731 static struct console serial_s3c2410_console =
732 {
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,
740 };
741
742 static int __init s3c2410_console_init(void)
743 {
744         dbg("s3c2410_console_init:\n");
745
746         serial_s3c2410_init_ports();
747         register_console(&serial_s3c2410_console);
748         return 0;
749 }
750 console_initcall(s3c2410_console_init);
751
752 #define SERIAL_S3C2410_CONSOLE  &serial_s3c2410_console
753 #else
754 #define SERIAL_S3C2410_CONSOLE  NULL
755 #endif
756
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,
765 };
766
767 /* device driver */
768
769 static int s3c2410_serial_probe(struct device *_dev);
770 static int s3c2410_serial_remove(struct device *_dev);
771
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,
779 };
780
781 #define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
782
783 static int s3c2410_serial_probe(struct device *_dev)
784 {
785         struct platform_device *dev = to_platform_device(_dev);
786         struct resource *res = dev->resource;
787         int i;
788
789         dbg("s3c2410_serial_probe: dev=%p, _dev=%p, res=%p\n", _dev, dev, res);
790
791         for (i = 0; i < dev->num_resources; i++, res++)
792                 if (res->flags & IORESOURCE_MEM)
793                         break;
794
795         if (i < dev->num_resources) {
796                 struct uart_port *ptr = serial_s3c2410_ports;
797
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);
801
802                         if (ptr->mapbase != res->start)
803                                 continue;
804
805                         dbg("s3c2410_serial_probe: got device %p: port=%p\n",
806                             _dev, ptr);
807
808                         uart_add_one_port(&s3c2410_uart_drv, ptr);
809                         dev_set_drvdata(_dev, ptr);
810                         break;
811                 }
812         }
813
814         return 0;
815 }
816
817 static int s3c2410_serial_remove(struct device *_dev)
818 {
819         struct uart_port *port = s3c2410_dev_to_port(_dev);
820
821         if (port)
822                 uart_remove_one_port(&s3c2410_uart_drv, port);
823
824         return 0;
825 }
826
827
828
829 static int __init serial_s3c2410_init(void)
830 {
831         int ret;
832
833         printk(KERN_INFO "S3C2410X Serial, (c) 2003 Simtec Electronics\n");
834
835         ret = uart_register_driver(&s3c2410_uart_drv);
836         if (ret != 0)
837                 return ret;
838
839         ret = driver_register(&s3c2410_serial_drv);
840         if (ret) {
841                 uart_unregister_driver(&s3c2410_uart_drv);
842         }
843
844         return ret;
845 }
846
847 static void __exit serial_s3c2410_exit(void)
848 {
849         driver_unregister(&s3c2410_serial_drv);
850         uart_unregister_driver(&s3c2410_uart_drv);
851 }
852
853 module_init(serial_s3c2410_init);
854 module_exit(serial_s3c2410_exit);
855
856 MODULE_LICENSE("GPL");
857 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
858 MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver");