MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / serial / serial_s3c4510b.c
blob00d72cc442a504d5e910b7a80d9a09076f1c887c
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/config.h>
28 #include <linux/module.h>
29 #include <linux/errno.h>
30 #include <linux/signal.h>
31 #include <linux/sched.h>
32 #include <linux/interrupt.h>
33 #include <linux/tty.h>
34 #include <linux/tty_flip.h>
35 #include <linux/major.h>
36 #include <linux/string.h>
37 #include <linux/fcntl.h>
38 #include <linux/ptrace.h>
39 #include <linux/ioport.h>
40 #include <linux/mm.h>
41 #include <linux/slab.h>
42 #include <linux/init.h>
43 #include <linux/circ_buf.h>
44 #include <linux/serial.h>
45 #include <linux/console.h>
46 #include <linux/sysrq.h>
47 #include <linux/serial_core.h>
49 #include <asm/system.h>
50 #include <asm/io.h>
51 #include <asm/mach/irq.h>
52 #include <asm/uaccess.h>
53 #include <asm/bitops.h>
54 #include <asm/arch/hardware.h>
55 #include <asm/arch/uart.h>
57 #define __DRIVER_NAME "Samsung S3C4510B Internal UART"
59 #define _SDEBUG
60 #ifdef _SDEBUG
61 # define _DPRINTK(format, args...) \
62 printk (KERN_INFO "%s():%05d "format".\n" , __FUNCTION__ , __LINE__ , ## args);
63 #else
64 # define _DPRINTK(format, args...)
65 #endif
67 /**
69 ** Internal(private) helper functions
71 **/
73 static void __xmit_char(struct uart_port *port, const char ch) {
75 struct uart_regs *uart = (struct uart_regs *)port->iobase;
77 while( !uart->m_stat.bf.txBufEmpty);
79 uart->m_tx = ch;
81 if ( ch == '\n') {
82 while( !uart->m_stat.bf.txBufEmpty);
83 uart->m_tx = '\r';
88 static void __xmit_string(struct uart_port *port, const char *p, int len)
90 while( len-- > 0) {
91 __xmit_char( port, *p++);
95 static void __s3c4510b_init(const struct uart_port *port, int baud)
97 struct uart_regs *uart = (struct uart_regs *)port->iobase;
98 UART_CTRL uctrl;
99 UART_LINE_CTRL ulctrl;
100 UART_BAUD_DIV ubd;
102 /* Reset the UART */
103 /* control register */
104 uctrl.ui = 0x0;
105 uctrl.bf.rxMode = 0x1;
106 uctrl.bf.rxIrq = 0x1;
107 uctrl.bf.txMode = 0x1;
108 uctrl.bf.DSR = 0x1;
109 uctrl.bf.sendBreak = 0x0;
110 uctrl.bf.loopBack = 0x0;
111 uart->m_ctrl.ui = uctrl.ui;
113 /* Set the line control register into a safe sane state */
114 ulctrl.ui = 0x0;
115 ulctrl.bf.wordLen = 0x3; /* 8 bit data */
116 ulctrl.bf.nStop = 0x0; /* 1 stop bit */
117 ulctrl.bf.parity = 0x0; /* no parity */
118 ulctrl.bf.clk = 0x0; /* internal clock */
119 ulctrl.bf.infra_red = 0x0; /* no infra_red */
120 uart->m_lineCtrl.ui = ulctrl.ui;
122 ubd.ui = 0x0;
124 /* see table on page 10-15 in SAMSUNG S3C4510B manual */
125 /* get correct divisor */
126 switch( baud ? baud : 19200) {
128 case 1200:
129 ubd.bf.cnt0 = 1301;
130 break;
132 case 2400:
133 ubd.bf.cnt0 = 650;
134 break;
136 case 4800:
137 ubd.bf.cnt0 = 324;
138 break;
140 case 9600:
141 ubd.bf.cnt0 = 162;
142 break;
144 case 19200:
145 ubd.bf.cnt0 = 80;
146 break;
148 case 38400:
149 ubd.bf.cnt0 = 40;
150 break;
152 case 57600:
153 ubd.bf.cnt0 = 26;
154 break;
156 case 115200:
157 ubd.bf.cnt0 = 13;
158 break;
161 uart->m_baudDiv.ui = ubd.ui;
162 uart->m_baudCnt = 0x0;
163 uart->m_baudClk = 0x0;
169 ** struct uart_ops functions below
173 static void __s3c4510b_stop_tx(struct uart_port *port, unsigned int tty_stop)
179 static void __s3c4510b_tx_chars(struct uart_port *port)
181 struct circ_buf *xmit = &port->info->xmit;
182 int count;
184 // _DPRINTK("called with info = 0x%08x", (unsigned int) port);
186 if ( port->x_char) {
187 __xmit_char( port, port->x_char);
188 port->icount.tx++;
189 port->x_char = 0;
190 return;
193 if (uart_circ_empty( xmit) || uart_tx_stopped( port)) {
194 __s3c4510b_stop_tx( port, 0);
195 return;
198 count = port->fifosize >> 1;
199 do {
200 __xmit_char( port, xmit->buf[xmit->tail]);
201 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
202 port->icount.tx++;
203 if (uart_circ_empty(xmit))
204 break;
205 } while (--count > 0);
207 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
208 uart_write_wakeup( port);
210 if (uart_circ_empty(xmit))
211 __s3c4510b_stop_tx( port, 0);
214 static void __s3c4510b_start_tx(struct uart_port *port, unsigned int tty_start)
216 __s3c4510b_tx_chars( port);
219 static void __s3c4510b_send_xchar(struct uart_port *port, char ch)
221 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
224 static void __s3c4510b_stop_rx(struct uart_port *port)
226 struct uart_regs *uart = (struct uart_regs *)port->iobase;
227 UART_CTRL uctrl;
229 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
231 uctrl.ui = uart->m_ctrl.ui;
232 uctrl.bf.rxMode = 0x0;
233 uart->m_ctrl.ui = uctrl.ui;
236 static void __s3c4510b_enable_ms(struct uart_port *port)
238 _DPRINTK("called with port = 0x%08x", (unsigned int) port);
241 static void __s3c4510b_rx_char(struct uart_port *port)
243 struct uart_regs *uart = (struct uart_regs *)port->iobase;
244 struct tty_struct *tty = port->info->tty;
245 unsigned int ch;
246 UART_STAT status;
248 status.ui = uart->m_stat.ui;
249 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
250 tty->flip.work.func((void *)tty);
251 if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
252 printk(KERN_WARNING "TTY_DONT_FLIP set\n");
253 return;
257 ch = uart->m_rx & 0xFF;
259 *tty->flip.char_buf_ptr = ch;
260 *tty->flip.flag_buf_ptr = TTY_NORMAL;
261 port->icount.rx++;
264 * Note that the error handling code is
265 * out of the main execution path
267 if ( status.bf.breakIrq) {
268 port->icount.brk++;
269 if (uart_handle_break(port))
270 goto ignore_char;
271 *tty->flip.flag_buf_ptr = TTY_BREAK;
273 else if ( status.bf.parity) {
274 port->icount.parity++;
275 *tty->flip.flag_buf_ptr = TTY_PARITY;
277 else if ( status.bf.frame) {
278 port->icount.frame++;
279 *tty->flip.flag_buf_ptr = TTY_FRAME;
281 else if ( status.bf.overrun) {
282 port->icount.overrun++;
283 if ( tty->flip.count < TTY_FLIPBUF_SIZE) {
285 * Overrun is special, since it's reported
286 * immediately, and doesn't affect the current
287 * character
289 *tty->flip.char_buf_ptr++ = 0;
290 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
291 tty->flip.count++;
294 else {
295 /* no errors */
296 tty->flip.flag_buf_ptr++;
297 tty->flip.char_buf_ptr++;
298 tty->flip.count++;
301 ignore_char:
303 tty_flip_buffer_push(tty);
308 static irqreturn_t __s3c4510b_rx_int(int irq, void *dev_id, struct pt_regs *regs)
310 // _DPRINTK("called with irq = 0x%08x", irq);
312 struct uart_port *port = dev_id;
314 LED_SET(2);
315 __s3c4510b_rx_char( port);
316 LED_CLR(2);
318 return IRQ_HANDLED;
321 static irqreturn_t __s3c4510b_tx_int(int irq, void *dev_id, struct pt_regs *regs)
323 // _DPRINTK("called with irq = 0x%08x", irq);
325 struct uart_port *port = dev_id;
327 LED_SET(1);
328 __s3c4510b_start_tx( port, 0);
329 LED_CLR(1);
331 return IRQ_HANDLED;
334 static unsigned int __s3c4510b_tx_empty(struct uart_port *port)
336 struct uart_regs *uart = (struct uart_regs *)port->iobase;
338 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
340 return uart->m_stat.bf.txBufEmpty ? 1 : 0;
343 static unsigned int __s3c4510b_get_mctrl(struct uart_port *port)
345 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
347 return 0;
350 static void __s3c4510b_set_mctrl(struct uart_port *port, u_int mctrl)
352 // _DPRINTK("called with port = 0x%08x, mctrl = 0x%08x", (unsigned int) port, mctrl);
355 static void __s3c4510b_break_ctl(struct uart_port *port, int break_state)
357 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
360 static struct irqaction __rx_irqaction[UART_NR] = {
362 name: "serial0_rx",
363 flags: SA_INTERRUPT,
364 handler: __s3c4510b_rx_int,
367 name: "serial1_rx",
368 flags: SA_INTERRUPT,
369 handler: __s3c4510b_rx_int,
373 static struct irqaction __tx_irqaction[UART_NR] = {
375 name: "serial0_tx",
376 flags: SA_INTERRUPT,
377 handler: __s3c4510b_tx_int,
380 name: "serial1_tx",
381 flags: SA_INTERRUPT,
382 handler: __s3c4510b_tx_int,
386 static int __s3c4510b_startup(struct uart_port *port)
388 int status;
390 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
392 __s3c4510b_init(port, 19200);
395 * Allocate the IRQs for TX and RX
397 __tx_irqaction[port->line].dev_id = (void *)port;
398 __rx_irqaction[port->line].dev_id = (void *)port;
400 status = setup_irq( port->irq, &__tx_irqaction[port->line]);
401 if ( status) {
402 printk( KERN_ERR "Unabled to hook interrupt for serial %d TX\n", port->line);
403 return status;
406 status = setup_irq( port->irq+1, &__rx_irqaction[port->line]);
407 if ( status) {
408 printk( KERN_ERR "Unabled to hook interrupt for serial %d RX\n", port->line);
409 return status;
413 * Finally, enable interrupts
415 spin_lock_irq( &port->lock);
416 INT_ENABLE( port->irq);
417 INT_ENABLE( port->irq+1);
418 spin_unlock_irq( &port->lock);
420 return 0;
423 static void __s3c4510b_shutdown(struct uart_port *port)
425 struct uart_regs *uart = (struct uart_regs *)port->iobase;
427 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
429 INT_DISABLE( port->irq);
430 INT_DISABLE( port->irq+1);
432 /* turn off TX/RX */
433 uart->m_ctrl.ui = 0x0;
437 static void __s3c4510b_set_termios(struct uart_port *port, struct termios *termios, struct termios *old)
439 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
442 ** Ignore -- only 19200 baud supported
446 * Update the per-port timeout.
448 uart_update_timeout(port, termios->c_cflag, 19200);
452 static void __s3c4510b_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
454 // _DPRINTK("called with port = 0x%08x, state = %u", (unsigned int) port, state);
457 static int __s3c4510b_set_wake(struct uart_port *port, unsigned int state)
459 // _DPRINTK("called with port = 0x%08x, state = %u", (unsigned int) port, state);
460 return 0;
463 static const char *__s3c4510b_type(struct uart_port *port)
465 return __DRIVER_NAME;
469 static void __s3c4510b_release_port(struct uart_port *port)
471 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
474 static int __s3c4510b_request_port(struct uart_port *port)
476 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
477 return 0;
480 static void __s3c4510b_config_port(struct uart_port *port, int config)
482 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
485 static int __s3c4510b_verify_port(struct uart_port *port, struct serial_struct *serial)
487 // _DPRINTK("called with port = 0x%08x", (unsigned int) port);
488 return 0;
491 #if 0
492 static int __s3c4510b_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
494 // _DPRINTK("called with port = 0x%08x, cmd %u, arg 0x%08lx", (unsigned int) port, cmd, arg);
495 return 0;
497 #endif
499 static struct uart_ops s3c4510b_pops = {
500 tx_empty: __s3c4510b_tx_empty,
501 set_mctrl: __s3c4510b_set_mctrl,
502 get_mctrl: __s3c4510b_get_mctrl,
503 stop_tx: __s3c4510b_stop_tx,
504 start_tx: __s3c4510b_start_tx,
505 send_xchar: __s3c4510b_send_xchar,
506 stop_rx: __s3c4510b_stop_rx,
507 enable_ms: __s3c4510b_enable_ms,
508 break_ctl: __s3c4510b_break_ctl,
509 startup: __s3c4510b_startup,
510 shutdown: __s3c4510b_shutdown,
511 set_termios: __s3c4510b_set_termios,
512 pm: __s3c4510b_pm,
513 set_wake: __s3c4510b_set_wake,
514 type: __s3c4510b_type,
515 release_port: __s3c4510b_release_port,
516 request_port: __s3c4510b_request_port,
517 config_port: __s3c4510b_config_port,
518 verify_port: __s3c4510b_verify_port,
519 // ioctl: __s3c4510b_ioctl,
523 static struct uart_port __s3c4510b_ports[UART_NR] = {
525 iobase: UART0_BASE,
526 line: 0,
527 irq: INT_UARTTX0,
528 fifosize: 1,
529 ops: &s3c4510b_pops,
530 ignore_status_mask: 0x0000000F,
531 type: PORT_S3C4510B,
534 iobase: UART1_BASE,
535 line: 1,
536 irq: INT_UARTTX1,
537 fifosize: 1,
538 ops: &s3c4510b_pops,
539 ignore_status_mask: 0x0000000F,
540 type: PORT_S3C4510B,
544 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
545 /************** console driver *****************/
547 static void __s3c4510b_console_write(struct console *co, const char *s, u_int count)
549 struct uart_port *port = &__s3c4510b_ports[co->index];
551 __xmit_string( port, s, count);
555 static int __init __s3c4510b_console_setup(struct console *co, char *options)
557 struct uart_port *port;
558 int baud = 19200;
559 int bits = 8;
560 int parity = 'n';
561 int flow = 0;
564 * Check whether an invalid uart number has been specified, and
565 * if so, search for the first available port that does have
566 * console support.
568 port = uart_get_console(__s3c4510b_ports, UART_NR, co);
570 // _DPRINTK("using port = 0x%08x", (unsigned int) port);
572 if (options)
573 uart_parse_options(options, &baud, &parity, &bits, &flow);
575 __s3c4510b_init(port, baud);
577 return uart_set_options(port, co, baud, parity, bits, flow);
580 extern struct uart_driver __s3c4510b_driver;
581 static struct console __s3c4510b_console = {
582 name: "ttyS",
583 write: __s3c4510b_console_write,
584 device: uart_console_device,
585 setup: __s3c4510b_console_setup,
586 flags: CON_PRINTBUFFER,
587 index: -1,
588 data: &__s3c4510b_driver,
591 static int __init __s3c4510b_console_init(void)
593 register_console(&__s3c4510b_console);
594 return 0;
597 console_initcall(__s3c4510b_console_init);
599 #endif /* CONFIG_SERIAL_S3C4510B_CONSOLE */
602 static struct uart_driver __s3c4510b_driver = {
603 owner: THIS_MODULE,
604 driver_name: __DRIVER_NAME,
605 dev_name: "ttyS",
606 major: TTY_MAJOR,
607 minor: 64,
608 nr: UART_NR,
609 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
610 cons: &__s3c4510b_console,
611 #endif
614 static int __init __s3c4510b_serial_init(void)
617 int status, i;
619 // _DPRINTK("initializing driver with drv = 0x%08x", (unsigned int) &__s3c4510b_driver);
621 status = uart_register_driver( &__s3c4510b_driver);
623 if ( status) {
624 _DPRINTK("uart_register_driver() returned %d", status);
627 for ( i = 0; i < UART_NR; i++) {
628 status = uart_add_one_port( &__s3c4510b_driver, &__s3c4510b_ports[i]);
629 if ( status) {
630 _DPRINTK("uart_add_one_port(%d) returned %d", i, status);
634 return 0;
637 module_init(__s3c4510b_serial_init);