MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / drivers / serial / serial_s3c4510b.c
blob7d0dcd476b982debd721c375655dab9126db9381
1 /*
2 * linux/drivers/serial/serial_s3c4510b.c
4 * Driver for S3C4510B serial ports
6 * Copyright (c) 2004 Cucy Systems (http://www.cucy.com)
7 * Curt Brune <curt@cucy.com>
9 * Based on drivers/char/serial_amba.c
10 * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/module.h>
28 #include <linux/errno.h>
29 #include <linux/signal.h>
30 #include <linux/sched.h>
31 #include <linux/interrupt.h>
32 #include <linux/tty.h>
33 #include <linux/tty_flip.h>
34 #include <linux/major.h>
35 #include <linux/string.h>
36 #include <linux/fcntl.h>
37 #include <linux/ptrace.h>
38 #include <linux/ioport.h>
39 #include <linux/mm.h>
40 #include <linux/slab.h>
41 #include <linux/init.h>
42 #include <linux/circ_buf.h>
43 #include <linux/serial.h>
44 #include <linux/console.h>
45 #include <linux/sysrq.h>
46 #include <linux/serial_core.h>
48 #include <asm/system.h>
49 #include <asm/io.h>
50 #include <asm/mach/irq.h>
51 #include <asm/uaccess.h>
52 #include <asm/bitops.h>
53 #include <asm/arch/hardware.h>
54 #include <asm/arch/uart.h>
56 #define __DRIVER_NAME "Samsung S3C4510B Internal UART"
58 #define _SDEBUG
59 #ifdef _SDEBUG
60 # define _DPRINTK(format, args...) \
61 printk (KERN_INFO "%s():%05d "format".\n" , __FUNCTION__ , __LINE__ , ## args);
62 #else
63 # define _DPRINTK(format, args...)
64 #endif
66 /**
68 ** Internal(private) helper functions
70 **/
72 static void __xmit_char(struct uart_port *port, const char ch) {
74 struct uart_regs *uart = (struct uart_regs *)port->iobase;
76 while( !uart->m_stat.bf.txBufEmpty);
78 uart->m_tx = ch;
80 if ( ch == '\n') {
81 while( !uart->m_stat.bf.txBufEmpty);
82 uart->m_tx = '\r';
87 static void __xmit_string(struct uart_port *port, const char *p, int len)
89 while( len-- > 0) {
90 __xmit_char( port, *p++);
94 static void __s3c4510b_init(const struct uart_port *port, int baud)
96 struct uart_regs *uart = (struct uart_regs *)port->iobase;
97 UART_CTRL uctrl;
98 UART_LINE_CTRL ulctrl;
99 UART_BAUD_DIV ubd;
101 /* Reset the UART */
102 /* control register */
103 uctrl.ui = 0x0;
104 uctrl.bf.rxMode = 0x1;
105 uctrl.bf.rxIrq = 0x1;
106 uctrl.bf.txMode = 0x1;
107 uctrl.bf.DSR = 0x1;
108 uctrl.bf.sendBreak = 0x0;
109 uctrl.bf.loopBack = 0x0;
110 uart->m_ctrl.ui = uctrl.ui;
112 /* Set the line control register into a safe sane state */
113 ulctrl.ui = 0x0;
114 ulctrl.bf.wordLen = 0x3; /* 8 bit data */
115 ulctrl.bf.nStop = 0x0; /* 1 stop bit */
116 ulctrl.bf.parity = 0x0; /* no parity */
117 ulctrl.bf.clk = 0x0; /* internal clock */
118 ulctrl.bf.infra_red = 0x0; /* no infra_red */
119 uart->m_lineCtrl.ui = ulctrl.ui;
121 ubd.ui = 0x0;
123 /* see table on page 10-15 in SAMSUNG S3C4510B manual */
124 /* get correct divisor */
125 switch( baud ? baud : 19200) {
127 case 1200:
128 ubd.bf.cnt0 = 1301;
129 break;
131 case 2400:
132 ubd.bf.cnt0 = 650;
133 break;
135 case 4800:
136 ubd.bf.cnt0 = 324;
137 break;
139 case 9600:
140 ubd.bf.cnt0 = 162;
141 break;
143 case 19200:
144 ubd.bf.cnt0 = 80;
145 break;
147 case 38400:
148 ubd.bf.cnt0 = 40;
149 break;
151 case 57600:
152 ubd.bf.cnt0 = 26;
153 break;
155 case 115200:
156 ubd.bf.cnt0 = 13;
157 break;
160 uart->m_baudDiv.ui = ubd.ui;
161 uart->m_baudCnt = 0x0;
162 uart->m_baudClk = 0x0;
168 ** struct uart_ops functions below
172 static void __s3c4510b_stop_tx(struct uart_port *port)
178 static void __s3c4510b_tx_chars(struct uart_port *port)
180 struct circ_buf *xmit = &port->info->xmit;
181 int count;
183 // _DPRINTK("called with info = 0x%08x", (unsigned int) port);
185 if ( port->x_char) {
186 __xmit_char( port, port->x_char);
187 port->icount.tx++;
188 port->x_char = 0;
189 return;
192 if (uart_circ_empty( xmit) || uart_tx_stopped( port)) {
193 __s3c4510b_stop_tx( port);
194 return;
197 count = port->fifosize >> 1;
198 do {
199 __xmit_char( port, xmit->buf[xmit->tail]);
200 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
201 port->icount.tx++;
202 if (uart_circ_empty(xmit))
203 break;
204 } while (--count > 0);
206 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
207 uart_write_wakeup( port);
209 if (uart_circ_empty(xmit))
210 __s3c4510b_stop_tx( port);
213 static void __s3c4510b_start_tx(struct uart_port *port)
215 __s3c4510b_tx_chars( port);
218 static void __s3c4510b_send_xchar(struct uart_port *port, char ch)
220 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
223 static void __s3c4510b_stop_rx(struct uart_port *port)
225 struct uart_regs *uart = (struct uart_regs *)port->iobase;
226 UART_CTRL uctrl;
228 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
230 uctrl.ui = uart->m_ctrl.ui;
231 uctrl.bf.rxMode = 0x0;
232 uart->m_ctrl.ui = uctrl.ui;
235 static void __s3c4510b_enable_ms(struct uart_port *port)
237 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
240 static void __s3c4510b_rx_char(struct uart_port *port)
242 struct uart_regs *uart = (struct uart_regs *)port->iobase;
243 struct tty_struct *tty = port->info->tty;
244 unsigned int ch;
245 UART_STAT status;
247 status.ui = uart->m_stat.ui;
248 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
249 tty->flip.work.func((void *)tty);
250 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
251 printk(KERN_WARNING "TTY_DONT_FLIP set\n");
252 return;
256 ch = uart->m_rx & 0xFF;
258 *tty->flip.char_buf_ptr = ch;
259 *tty->flip.flag_buf_ptr = TTY_NORMAL;
260 port->icount.rx++;
263 * Note that the error handling code is
264 * out of the main execution path
266 if ( status.bf.breakIrq) {
267 port->icount.brk++;
268 if (uart_handle_break(port))
269 goto ignore_char;
270 *tty->flip.flag_buf_ptr = TTY_BREAK;
272 else if ( status.bf.parity) {
273 port->icount.parity++;
274 *tty->flip.flag_buf_ptr = TTY_PARITY;
276 else if ( status.bf.frame) {
277 port->icount.frame++;
278 *tty->flip.flag_buf_ptr = TTY_FRAME;
280 else if ( status.bf.overrun) {
281 port->icount.overrun++;
282 if ( tty->flip.count < TTY_FLIPBUF_SIZE) {
284 * Overrun is special, since it's reported
285 * immediately, and doesn't affect the current
286 * character
288 *tty->flip.char_buf_ptr++ = 0;
289 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
290 tty->flip.count++;
293 else {
294 /* no errors */
295 tty->flip.flag_buf_ptr++;
296 tty->flip.char_buf_ptr++;
297 tty->flip.count++;
300 ignore_char:
302 tty_flip_buffer_push(tty);
307 static irqreturn_t __s3c4510b_rx_int(int irq, void *dev_id, struct pt_regs *regs)
309 // _DPRINTK("called with irq = 0x%08x", irq);
311 struct uart_port *port = dev_id;
313 LED_SET(2);
314 __s3c4510b_rx_char( port);
315 LED_CLR(2);
317 return IRQ_HANDLED;
320 static irqreturn_t __s3c4510b_tx_int(int irq, void *dev_id, struct pt_regs *regs)
322 // _DPRINTK("called with irq = 0x%08x", irq);
324 struct uart_port *port = dev_id;
326 LED_SET(1);
327 __s3c4510b_start_tx( port);
328 LED_CLR(1);
330 return IRQ_HANDLED;
333 static unsigned int __s3c4510b_tx_empty(struct uart_port *port)
335 struct uart_regs *uart = (struct uart_regs *)port->iobase;
337 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
339 return uart->m_stat.bf.txBufEmpty ? 1 : 0;
342 static unsigned int __s3c4510b_get_mctrl(struct uart_port *port)
344 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
346 return 0;
349 static void __s3c4510b_set_mctrl(struct uart_port *port, u_int mctrl)
351 // _DPRINTK("called with port = 0x%08x, mctrl = 0x%08x", (unsigned int) port, mctrl);
354 static void __s3c4510b_break_ctl(struct uart_port *port, int break_state)
356 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
359 static struct irqaction __rx_irqaction[UART_NR] = {
361 name: "serial0_rx",
362 flags: SA_INTERRUPT,
363 handler: __s3c4510b_rx_int,
366 name: "serial1_rx",
367 flags: SA_INTERRUPT,
368 handler: __s3c4510b_rx_int,
372 static struct irqaction __tx_irqaction[UART_NR] = {
374 name: "serial0_tx",
375 flags: SA_INTERRUPT,
376 handler: __s3c4510b_tx_int,
379 name: "serial1_tx",
380 flags: SA_INTERRUPT,
381 handler: __s3c4510b_tx_int,
385 static int __s3c4510b_startup(struct uart_port *port)
387 int status;
389 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
391 __s3c4510b_init(port, 19200);
394 * Allocate the IRQs for TX and RX
396 __tx_irqaction[port->line].dev_id = (void *)port;
397 __rx_irqaction[port->line].dev_id = (void *)port;
399 status = setup_irq( port->irq, &__tx_irqaction[port->line]);
400 if ( status) {
401 printk( KERN_ERR "Unabled to hook interrupt for serial %d TX\n", port->line);
402 return status;
405 status = setup_irq( port->irq+1, &__rx_irqaction[port->line]);
406 if ( status) {
407 printk( KERN_ERR "Unabled to hook interrupt for serial %d RX\n", port->line);
408 return status;
412 * Finally, enable interrupts
414 spin_lock_irq( &port->lock);
415 INT_ENABLE( port->irq);
416 INT_ENABLE( port->irq+1);
417 spin_unlock_irq( &port->lock);
419 return 0;
422 static void __s3c4510b_shutdown(struct uart_port *port)
424 struct uart_regs *uart = (struct uart_regs *)port->iobase;
426 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
428 INT_DISABLE( port->irq);
429 INT_DISABLE( port->irq+1);
431 /* turn off TX/RX */
432 uart->m_ctrl.ui = 0x0;
436 static void __s3c4510b_set_termios(struct uart_port *port, struct termios *termios, struct termios *old)
438 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
441 ** Ignore -- only 19200 baud supported
445 * Update the per-port timeout.
447 uart_update_timeout(port, termios->c_cflag, 19200);
451 static void __s3c4510b_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
453 // _DPRINTK("called with port = 0x%08x, state = %u", (unsigned int) port, state);
456 static int __s3c4510b_set_wake(struct uart_port *port, unsigned int state)
458 // _DPRINTK("called with port = 0x%08x, state = %u", (unsigned int) port, state);
459 return 0;
462 static const char *__s3c4510b_type(struct uart_port *port)
464 return __DRIVER_NAME;
468 static void __s3c4510b_release_port(struct uart_port *port)
470 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
473 static int __s3c4510b_request_port(struct uart_port *port)
475 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
476 return 0;
479 static void __s3c4510b_config_port(struct uart_port *port, int config)
481 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
484 static int __s3c4510b_verify_port(struct uart_port *port, struct serial_struct *serial)
486 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
487 return 0;
490 #if 0
491 static int __s3c4510b_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
493 // _DPRINTK("called with port = 0x%08x, cmd %u, arg 0x%08lx", (unsigned int) port, cmd, arg);
494 return 0;
496 #endif
498 static struct uart_ops s3c4510b_pops = {
499 tx_empty: __s3c4510b_tx_empty,
500 set_mctrl: __s3c4510b_set_mctrl,
501 get_mctrl: __s3c4510b_get_mctrl,
502 stop_tx: __s3c4510b_stop_tx,
503 start_tx: __s3c4510b_start_tx,
504 send_xchar: __s3c4510b_send_xchar,
505 stop_rx: __s3c4510b_stop_rx,
506 enable_ms: __s3c4510b_enable_ms,
507 break_ctl: __s3c4510b_break_ctl,
508 startup: __s3c4510b_startup,
509 shutdown: __s3c4510b_shutdown,
510 set_termios: __s3c4510b_set_termios,
511 pm: __s3c4510b_pm,
512 set_wake: __s3c4510b_set_wake,
513 type: __s3c4510b_type,
514 release_port: __s3c4510b_release_port,
515 request_port: __s3c4510b_request_port,
516 config_port: __s3c4510b_config_port,
517 verify_port: __s3c4510b_verify_port,
518 // ioctl: __s3c4510b_ioctl,
522 static struct uart_port __s3c4510b_ports[UART_NR] = {
524 iobase: UART0_BASE,
525 line: 0,
526 irq: INT_UARTTX0,
527 fifosize: 1,
528 ops: &s3c4510b_pops,
529 ignore_status_mask: 0x0000000F,
530 type: PORT_S3C4510B,
533 iobase: UART1_BASE,
534 line: 1,
535 irq: INT_UARTTX1,
536 fifosize: 1,
537 ops: &s3c4510b_pops,
538 ignore_status_mask: 0x0000000F,
539 type: PORT_S3C4510B,
543 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
544 /************** console driver *****************/
546 static void __s3c4510b_console_write(struct console *co, const char *s, u_int count)
548 struct uart_port *port = &__s3c4510b_ports[co->index];
550 __xmit_string( port, s, count);
554 static int __init __s3c4510b_console_setup(struct console *co, char *options)
556 struct uart_port *port;
557 int baud = 19200;
558 int bits = 8;
559 int parity = 'n';
560 int flow = 0;
563 * Check whether an invalid uart number has been specified, and
564 * if so, search for the first available port that does have
565 * console support.
567 port = uart_get_console(__s3c4510b_ports, UART_NR, co);
569 // _DPRINTK("using port = 0x%08x", (unsigned int) port);
571 if (options)
572 uart_parse_options(options, &baud, &parity, &bits, &flow);
574 __s3c4510b_init(port, baud);
576 return uart_set_options(port, co, baud, parity, bits, flow);
579 extern struct uart_driver __s3c4510b_driver;
580 static struct console __s3c4510b_console = {
581 name: "ttyS",
582 write: __s3c4510b_console_write,
583 device: uart_console_device,
584 setup: __s3c4510b_console_setup,
585 flags: CON_PRINTBUFFER,
586 index: -1,
587 data: &__s3c4510b_driver,
590 static int __init __s3c4510b_console_init(void)
592 register_console(&__s3c4510b_console);
593 return 0;
596 console_initcall(__s3c4510b_console_init);
598 #endif /* CONFIG_SERIAL_S3C4510B_CONSOLE */
601 static struct uart_driver __s3c4510b_driver = {
602 owner: THIS_MODULE,
603 driver_name: __DRIVER_NAME,
604 dev_name: "ttyS",
605 major: TTY_MAJOR,
606 minor: 64,
607 nr: UART_NR,
608 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
609 cons: &__s3c4510b_console,
610 #endif
613 static int __init __s3c4510b_serial_init(void)
616 int status, i;
618 // _DPRINTK("initializing driver with drv = 0x%08x", (unsigned int) &__s3c4510b_driver);
620 status = uart_register_driver( &__s3c4510b_driver);
622 if ( status) {
623 _DPRINTK("uart_register_driver() returned %d", status);
626 for ( i = 0; i < UART_NR; i++) {
627 status = uart_add_one_port( &__s3c4510b_driver, &__s3c4510b_ports[i]);
628 if ( status) {
629 _DPRINTK("uart_add_one_port(%d) returned %d", i, status);
633 return 0;
636 module_init(__s3c4510b_serial_init);