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>
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>
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"
61 # define _DPRINTK(format, args...) \
62 printk (KERN_INFO "%s():%05d "format".\n" , __FUNCTION__ , __LINE__ , ## args);
64 # define _DPRINTK(format, args...)
69 ** Internal(private) helper functions
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
);
82 while( !uart
->m_stat
.bf
.txBufEmpty
);
88 static void __xmit_string(struct uart_port
*port
, const char *p
, int len
)
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
;
99 UART_LINE_CTRL ulctrl
;
103 /* control register */
105 uctrl
.bf
.rxMode
= 0x1;
106 uctrl
.bf
.rxIrq
= 0x1;
107 uctrl
.bf
.txMode
= 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 */
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
;
124 /* see table on page 10-15 in SAMSUNG S3C4510B manual */
125 /* get correct divisor */
126 switch( baud
? baud
: 19200) {
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
;
184 // _DPRINTK("called with info = 0x%08x", (unsigned int) port);
187 __xmit_char( port
, port
->x_char
);
193 if (uart_circ_empty( xmit
) || uart_tx_stopped( port
)) {
194 __s3c4510b_stop_tx( port
, 0);
198 count
= port
->fifosize
>> 1;
200 __xmit_char( port
, xmit
->buf
[xmit
->tail
]);
201 xmit
->tail
= (xmit
->tail
+ 1) & (UART_XMIT_SIZE
- 1);
203 if (uart_circ_empty(xmit
))
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
;
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
;
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");
257 ch
= uart
->m_rx
& 0xFF;
259 *tty
->flip
.char_buf_ptr
= ch
;
260 *tty
->flip
.flag_buf_ptr
= TTY_NORMAL
;
264 * Note that the error handling code is
265 * out of the main execution path
267 if ( status
.bf
.breakIrq
) {
269 if (uart_handle_break(port
))
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
289 *tty
->flip
.char_buf_ptr
++ = 0;
290 *tty
->flip
.flag_buf_ptr
++ = TTY_OVERRUN
;
296 tty
->flip
.flag_buf_ptr
++;
297 tty
->flip
.char_buf_ptr
++;
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
;
315 __s3c4510b_rx_char( port
);
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
;
328 __s3c4510b_start_tx( port
, 0);
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);
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
] = {
364 handler
: __s3c4510b_rx_int
,
369 handler
: __s3c4510b_rx_int
,
373 static struct irqaction __tx_irqaction
[UART_NR
] = {
377 handler
: __s3c4510b_tx_int
,
382 handler
: __s3c4510b_tx_int
,
386 static int __s3c4510b_startup(struct uart_port
*port
)
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
]);
402 printk( KERN_ERR
"Unabled to hook interrupt for serial %d TX\n", port
->line
);
406 status
= setup_irq( port
->irq
+1, &__rx_irqaction
[port
->line
]);
408 printk( KERN_ERR
"Unabled to hook interrupt for serial %d RX\n", port
->line
);
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
);
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);
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);
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);
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);
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);
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
,
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
] = {
530 ignore_status_mask
: 0x0000000F,
539 ignore_status_mask
: 0x0000000F,
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
;
564 * Check whether an invalid uart number has been specified, and
565 * if so, search for the first available port that does have
568 port
= uart_get_console(__s3c4510b_ports
, UART_NR
, co
);
570 // _DPRINTK("using port = 0x%08x", (unsigned int) port);
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
= {
583 write
: __s3c4510b_console_write
,
584 device
: uart_console_device
,
585 setup
: __s3c4510b_console_setup
,
586 flags
: CON_PRINTBUFFER
,
588 data
: &__s3c4510b_driver
,
591 static int __init
__s3c4510b_console_init(void)
593 register_console(&__s3c4510b_console
);
597 console_initcall(__s3c4510b_console_init
);
599 #endif /* CONFIG_SERIAL_S3C4510B_CONSOLE */
602 static struct uart_driver __s3c4510b_driver
= {
604 driver_name
: __DRIVER_NAME
,
609 #ifdef CONFIG_SERIAL_S3C4510B_CONSOLE
610 cons
: &__s3c4510b_console
,
614 static int __init
__s3c4510b_serial_init(void)
619 // _DPRINTK("initializing driver with drv = 0x%08x", (unsigned int) &__s3c4510b_driver);
621 status
= uart_register_driver( &__s3c4510b_driver
);
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
]);
630 _DPRINTK("uart_add_one_port(%d) returned %d", i
, status
);
637 module_init(__s3c4510b_serial_init
);