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>
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>
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"
60 # define _DPRINTK(format, args...) \
61 printk (KERN_INFO "%s():%05d "format".\n" , __FUNCTION__ , __LINE__ , ## args);
63 # define _DPRINTK(format, args...)
68 ** Internal(private) helper functions
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
);
81 while( !uart
->m_stat
.bf
.txBufEmpty
);
87 static void __xmit_string(struct uart_port
*port
, const char *p
, int len
)
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
;
98 UART_LINE_CTRL ulctrl
;
102 /* control register */
104 uctrl
.bf
.rxMode
= 0x1;
105 uctrl
.bf
.rxIrq
= 0x1;
106 uctrl
.bf
.txMode
= 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 */
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
;
123 /* see table on page 10-15 in SAMSUNG S3C4510B manual */
124 /* get correct divisor */
125 switch( baud
? baud
: 19200) {
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
;
183 // _DPRINTK("called with info = 0x%08x", (unsigned int) port);
186 __xmit_char( port
, port
->x_char
);
192 if (uart_circ_empty( xmit
) || uart_tx_stopped( port
)) {
193 __s3c4510b_stop_tx( port
);
197 count
= port
->fifosize
>> 1;
199 __xmit_char( port
, xmit
->buf
[xmit
->tail
]);
200 xmit
->tail
= (xmit
->tail
+ 1) & (UART_XMIT_SIZE
- 1);
202 if (uart_circ_empty(xmit
))
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
;
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
;
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");
256 ch
= uart
->m_rx
& 0xFF;
258 *tty
->flip
.char_buf_ptr
= ch
;
259 *tty
->flip
.flag_buf_ptr
= TTY_NORMAL
;
263 * Note that the error handling code is
264 * out of the main execution path
266 if ( status
.bf
.breakIrq
) {
268 if (uart_handle_break(port
))
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
288 *tty
->flip
.char_buf_ptr
++ = 0;
289 *tty
->flip
.flag_buf_ptr
++ = TTY_OVERRUN
;
295 tty
->flip
.flag_buf_ptr
++;
296 tty
->flip
.char_buf_ptr
++;
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
;
314 __s3c4510b_rx_char( port
);
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
;
327 __s3c4510b_start_tx( port
);
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);
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
] = {
363 handler
: __s3c4510b_rx_int
,
368 handler
: __s3c4510b_rx_int
,
372 static struct irqaction __tx_irqaction
[UART_NR
] = {
376 handler
: __s3c4510b_tx_int
,
381 handler
: __s3c4510b_tx_int
,
385 static int __s3c4510b_startup(struct uart_port
*port
)
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
]);
401 printk( KERN_ERR
"Unabled to hook interrupt for serial %d TX\n", port
->line
);
405 status
= setup_irq( port
->irq
+1, &__rx_irqaction
[port
->line
]);
407 printk( KERN_ERR
"Unabled to hook interrupt for serial %d RX\n", port
->line
);
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
);
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);
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);
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);
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);
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);
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
,
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
] = {
529 ignore_status_mask
: 0x0000000F,
538 ignore_status_mask
: 0x0000000F,
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
;
563 * Check whether an invalid uart number has been specified, and
564 * if so, search for the first available port that does have
567 port
= uart_get_console(__s3c4510b_ports
, UART_NR
, co
);
569 // _DPRINTK("using port = 0x%08x", (unsigned int) port);
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
= {
582 write
: __s3c4510b_console_write
,
583 device
: uart_console_device
,
584 setup
: __s3c4510b_console_setup
,
585 flags
: CON_PRINTBUFFER
,
587 data
: &__s3c4510b_driver
,
590 static int __init
__s3c4510b_console_init(void)
592 register_console(&__s3c4510b_console
);
596 console_initcall(__s3c4510b_console_init
);
598 #endif /* CONFIG_SERIAL_S3C4510B_CONSOLE */
601 static struct uart_driver __s3c4510b_driver
= {
603 driver_name
: __DRIVER_NAME
,
608 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
609 cons
: &__s3c4510b_console
,
613 static int __init
__s3c4510b_serial_init(void)
618 // _DPRINTK("initializing driver with drv = 0x%08x", (unsigned int) &__s3c4510b_driver);
620 status
= uart_register_driver( &__s3c4510b_driver
);
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
]);
629 _DPRINTK("uart_add_one_port(%d) returned %d", i
, status
);
636 module_init(__s3c4510b_serial_init
);