📄 serial_s3c44b0.c
字号:
/* * linux/drivers/serial/serial_s3c440b.c * * Driver for S3C44B0 serial ports * * Copyright (c) 2005 JieZheng (caolingzi@netease.com) * * * Based on drivers/char/serial_s3c4510b.c * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/config.h> /*---><linux/autoconfig.h> (inlcude .config)*/#include <linux/kernel.h> /* printk() */#include <linux/sched.h> /* struct task_struct *current */#include <linux/major.h> /* TTY_MAJOR is 4 */#include <linux/init.h> /* module_init(), module_exit(), __init, __exit */#include <linux/slab.h> /* kmalloc(); */#include <linux/module.h> /* it must be included by a module source */#include <linux/types.h> /* MAJOR(), MINOR() */#include <linux/spinlock.h> #include <linux/string.h> /* string operations */#include <linux/fcntl.h> /* Macro file operation define */#include <linux/ioport.h>#include <linux/prtace.h> /* structs and defines to help the user use the ptrace system call. */#include <linux/mm.h> /* memory operations */#include <linux/errno.h>#include <linux/interrupt.h> /* struct irqaction{}; */#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/circ_buf.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/serial_core.h>#include <asm/system.h>#include <asm/io.h>#include <asm/mach/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/arch/system.h> /* S3C44B0 head file */#include <asm/arch/irq.h> /* S3C44B0 irq head file */#include <asm/irq.h>#define SERIAL_S3C44B0_NAME "Samsung S3C44B0 Internal UART"#define UART_NR 2static void led_set(int){}static void led_clr(int){}static void __xmit_char(struct uart_port *port, const char ch) { while(!(SYSREG_GET(S3C44B0X_UTRSTAT0) & 0x2)); SYSREG_SETB(S3C44B0X_UTXH0, ch); if (ch == '\n'){ while(!(SYSREG_GET(S3C44B0X_UTRSTAT0) & 0x2)); SYSREG_SETB(S3C44B0X_UTXH0, '\r'); } }static void __xmit_string(struct uart_port *port, const char *p, int len){ while( len-- > 0) { __xmit_char( port, *p++); }}static void __s3c44b0_stop_tx(struct uart_port *port, unsigned int tty_stop){ /*SYSREG_AND_SET(port->iobase+4, 0xfffffff3);*/}static void __s3c44b0_tx_chars(struct uart_port *port){ struct circ_buf *xmit = &port->info->xmit; int count; _DPRINTK("called with info = 0x%08x", (unsigned int) port); if ( port->x_char) { __xmit_char( port, port->x_char); port->icount.tx++; port->x_char = 0; return; } if (uart_circ_empty( xmit) || uart_tx_stopped( port)) { __s3c44b0_stop_tx( port, 0); return; } count = port->fifosize >> 1; do { __xmit_char( port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } while (--count > 0); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup( port); if (uart_circ_empty(xmit)) __s3c44b0_stop_tx( port, 0);}static void __s3c44b0_rx_char(struct uart_port *port){ struct tty_struct *tty = port->info->tty; unsigned int ch; UART_STAT status; if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk(KERN_WARNING "TTY_DONT_FLIP set\n"); return; } } ch = SYSREG_GET(port->iobase+0x24) & 0xFF; *tty->flip.char_buf_ptr = ch; *tty->flip.flag_buf_ptr = TTY_NORMAL; port->icount.rx++; /* * Note that the error handling code is * out of the main execution path */ tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; tty->flip.count++; tty_flip_buffer_push(tty);}static void __s3c44b0_start_tx(struct uart_port *port, unsigned int tty_start){ __s3c44b0_tx_chars(port);}static void __s3c44b0_send_xchar(struct uart_port *port, char ch){ _DPRINTK("called with port = 0x%08x", (unsigned int) port);}static void __s3c44b0_stop_rx(struct uart_port *port){ /* disable_irq(S3C44B0X_INTERRUPT_UTX0); */}static irqreturn_t __s3c44b0_rx_int(int irq, void *dev_id, struct pt_regs *regs){ _DPRINTK("called with irq = 0x%08x", irq); struct uart_port *port = dev_id; led_set(2); __s3c44b0_rx_char( port); led_clr(2); return IRQ_HANDLED;}static irqreturn_t __s3c44b0_tx_int(int irq, void *dev_id, struct pt_regs *regs){ _DPRINTK("called with irq = 0x%08x", irq); struct uart_port *port = dev_id; led_set(1); __s3c44b0_start_tx( port, 0); led_clr(1); return IRQ_HANDLED;}static void __s3c44b0_enable_ms(struct uart_port *port){ _DPRINTK("called with port = 0x%08x", (unsigned int) port);}static unsigned int __s3c44b0_tx_empty(struct uart_port *port){ return 0;}static unsigned int __s3c44b0_get_mctrl(struct uart_port *port){ return 0;}static void __s3c44b0_set_mctrl(struct uart_port *port, unsigned int mctrl){}static void __s3c44b0_break_ctl(struct uart_port *port, int break_state){}static void __s3c44b0_init(struct uart_port *port){ SYSREG_SET(port->iobase, 0x03); SYSREG_SET(port->iobase+4, 0x0309); SYSREG_SET(port->iobase+8, 0x01); SYSREG_SET(port->iobase+28, 0x22);}static struct irqaction __rx_irqaction[UART_NR] = { { .name = "serial0_rx", .flags = SA_INTERRUPT, .handler = __s3c44b0_rx_int, }, { .name = "serial1_rx", .flags = SA_INTERRUPT, .handler = __s3c44b0_rx_int, },};static struct irqaction __tx_irqaction[UART_NR] = { { .name = "serial0_tx", .flags = SA_INTERRUPT, .handler = __s3c44b0_tx_int, }, { .name = "serial1_tx", .flags = SA_INTERRUPT, .handler = __s3c44b0_tx_int, },};static int __s3c44b0_startup(struct uart_port *port){ /* this is the first time this port is opened */ /* do any hardware initialization needed here */ int status; __s3c44b0_init(port); /* * Allocate the IRQs for TX and RX */ __tx_irqaction[port->line].dev_id = (void *)port; __rx_irqaction[port->line].dev_id = (void *)port; status = setup_irq( port->irq, &__tx_irqaction[port->line]); if ( status) { printk( KERN_ERR "Unabled to hook interrupt for serial %d TX\n", port->line); return status; } status = setup_irq( port->irq+1, &__rx_irqaction[port->line]); if ( status) { printk( KERN_ERR "Unabled to hook interrupt for serial %d RX\n", port->line); return status; } /* * Finally, enable interrupts */ spin_lock_irq( &port->lock); if(port->line == 0) SYSREG_AND_SET(S3C44B0X_INTMSK, 0xfffffff7); else if(port->line == 1) SYSREG_AND_SET(S3C44B0X_INTMSK, 0xfffffffb); else printk(KERN_ERR "Unmask interrupt for none serial port\n"); spin_unlock_irq( &port->lock); return 0;}static void __s3c44b0_shutdown(struct uart_port *port){ /* The port is being closed by the last user. */ /* Do any hardware specific stuff here */ spin_lock_irq( &port->lock); if(port->line == 0) SYSREG_AND_SET(S3C44B0X_INTMSK, 0xfffffff7); else if(port->line == 1) SYSREG_AND_SET(S3C44B0X_INTMSK, 0xfffffffb); else printk(KERN_ERR "mask interrupt for none serial port\n"); spin_unlock_irq( &port->lock);}static void __s3c44b0_set_termios(struct uart_port *port, struct termios *termios, struct termios *old){ uart_update_timeout(port, termios->c_cflag, 115200);}static void __s3c44b0_pm(struct uart_port *port, unsigned int state, unsigned int oldstate){ }static int __s3c44b0_set_wake(struct uart_port *port, unsigned int state){ return 0;}static const char *__s3c44b0_type(struct uart_port *port){ return SERIAL_S3C44B0_NAME;}static void __s3c44b0_release_port(struct uart_port *port){}static int __s3c44b0_request_port(struct uart_port *port){ return 0;}static void __s3c44b0_config_port(struct uart_port *port, int flags){}static int __s3c44b0_verify_port(struct uart_port *port, struct serial_struct *ser){ return 0;}static struct uart_ops __s3c44b0_ops = { .tx_empty = __s3c44b0_tx_empty, .set_mctrl = __s3c44b0_set_mctrl, .get_mctrl = __s3c44b0_get_mctrl, .stop_tx = __s3c44b0_stop_tx, .start_tx = __s3c44b0_start_tx, .send_xchar: = __s3c44b0_send_xchar, .stop_rx = __s3c44b0_stop_rx, .enable_ms = __s3c44b0_enable_ms, .break_ctl = __s3c44b0_break_ctl, .startup = __s3c44b0_startup, .shutdown = __s3c44b0_shutdown, .set_termios = __s3c44b0_set_termios, .pm = __s3c44b0_pm, .set_wake = __s3c44b0_set_wake, .type = __s3c44b0_type, .release_port = __s3c44b0_release_port, .request_port = __s3c44b0_request_port, .config_port = __s3c44b0_config_port, .verify_port = __s3c44b0_verify_port,};static struct uart_port __s3c44b0_ports[UART_NR] = { { .iobase = S3C44B0X_ULCON0, .line = 0, /* port index */ .irq = S3C44B0X_INTERRUPT_UTX0, /* irq number */ .fifosize = 1, /* tx fifo size */ .ops = &__s3c44b0_ops, .ignore_status_mask = 0x0000000F, /* ? */ .type = PORT_S3C44B0, }, { .iobase = S3C44B0X_ULCON1, .line = 1, .irq = S3C44B0X_INTERRUPT_UTX1, .fifosize = 1, .ops = &__s3c44b0_ops, .ignore_status_mask = 0x0000000F, .type = PORT_S3C44B0, }};#ifdef CONFIG_SERIAL_S3C44B0_CONSOLE/************** console driver *****************/static void __s3c44b0_console_write(struct console *co, const char *s, u_int count){ struct uart_port *port = &__s3c44b0_ports[co->index]; __xmit_string( port, s, count);}static int __init __s3c44b0_console_setup(struct console *co, char *options){ struct uart_port *port; int baud = 115200; int bits = 8; int parity = 'n'; int flow = 0; /* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support. */ port = uart_get_console(__s3c44b0_ports, UART_NR, co);// _DPRINTK("using port = 0x%08x", (unsigned int) port); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); __s3c44b0_init(port, baud); return uart_set_options(port, co, baud, parity, bits, flow);}extern struct uart_driver __s3c44b0_driver;static struct uart_console __s3c44b0_console = { .name = "ttyS", .write = __s3c44b0_console_write, .device = uart_console_device, .setup = __s3c44b0_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &__s3c44b0_driver,};static int __init __s3c44b0_console_init(void){ register_console(&__s3c44b0_console); return 0;}console_initcall(__s3c44b0_console_init);#endif /* CONFIG_SERIAL_S3C44B0_CONSOLE */static struct uart_driver __s3c44b0_driver = { .owner = THIS_MODULE, .driver_name = SERIAL_S3C44B0_NAME, .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 68, .nr = UART_NR,#ifdef CONFIG_SERIAL_S3C44B0_CONSOLE .cons: &__s3c44b0_console,#endif };static int __init __s3c44b0_serial_init(void){ int status, i;/* _DPRINTK("initializing driver with drv = 0x%08x", (unsigned int) &__s3c44b0_driver); */ status = uart_register_driver( &__s3c44b0_driver); if ( status) { _DPRINTK("uart_register_driver() returned %d", status); } for ( i = 0; i < UART_NR; i++) { status = uart_add_one_port( &__s3c44b0_driver, &__s3c44b0_ports[i]); if ( status) { _DPRINTK("uart_add_one_port(%d) returned %d", i, status); } } return 0;}module_init(__s3c44b0_serial_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -