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