⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c2410.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/drivers/char/s3c2410.c * * Driver for onboard UARTs on the Samsung S3C2410 * * Based on drivers/char/serial.c and drivers/char/21285.c * * Ben Dooks, (c) 2003 Simtec Electronics * * Changelog: * */#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/ioport.h>#include <linux/device.h>#include <linux/init.h>#include <linux/console.h>#include <linux/serial_core.h>#include <linux/serial.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/arch/regs-serial.h>#if 0#include <asm/debug-ll.h>#define dbg(x...) llprintk(x)#else#define dbg(x...)#endif#define SERIAL_S3C2410_NAME	"ttySAC"#define SERIAL_S3C2410_MAJOR	204#define SERIAL_S3C2410_MINOR	64/* we can support 3 uarts, but not always use them */#define NR_PORTS (3)static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";/* port irq numbers */#define TX_IRQ(port) ((port)->irq + 1)#define RX_IRQ(port) ((port)->irq)#define tx_enabled(port) ((port)->unused[0])#define rx_enabled(port) ((port)->unused[1])/* flag to ignore all characters comming in */#define RXSTAT_DUMMY_READ (0x10000000)/* access functions */#define portaddr(port, reg) ((void *)((port)->membase + (reg)))#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))#define wr_regb(port, reg, val) \  do { __raw_writeb(val, portaddr(port, reg)); } while(0)#define wr_regl(port, reg, val) \  do { __raw_writel(val, portaddr(port, reg)); } while(0)/* code */static voidserial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop){	if (tx_enabled(port)) {		disable_irq(TX_IRQ(port));		tx_enabled(port) = 0;	}}static voidserial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start){	if (!tx_enabled(port)) {		enable_irq(TX_IRQ(port));		tx_enabled(port) = 1;	}}static void serial_s3c2410_stop_rx(struct uart_port *port){	if (rx_enabled(port)) {		dbg("serial_s3c2410_stop_rx: port=%p\n", port);		disable_irq(RX_IRQ(port));		rx_enabled(port) = 0;	}}static void serial_s3c2410_enable_ms(struct uart_port *port){}/* ? - where has parity gone?? */#define S3C2410_UERSTAT_PARITY (0x1000)static irqreturn_tserial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs){	struct uart_port *port = dev_id;	struct tty_struct *tty = port->info->tty;	unsigned int ufcon, ch, rxs, ufstat;	int max_count = 256;	while (max_count-- > 0) {		ufcon = rd_regl(port, S3C2410_UFCON);		ufstat = rd_regl(port, S3C2410_UFSTAT);		if (S3C2410_UFCON_RXC(ufstat) == 0)			break;		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");				goto out;			}		}		ch = rd_regb(port, S3C2410_URXH);		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = TTY_NORMAL;		port->icount.rx++;		rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;		if (rxs & S3C2410_UERSTAT_ANY) {			if (rxs & S3C2410_UERSTAT_FRAME)				port->icount.frame++;			if (rxs & S3C2410_UERSTAT_OVERRUN)				port->icount.overrun++;			rxs &= port->read_status_mask;			if (rxs & S3C2410_UERSTAT_PARITY)				*tty->flip.flag_buf_ptr = TTY_PARITY;			else if (rxs & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))				*tty->flip.flag_buf_ptr = TTY_FRAME;		}		if ((rxs & port->ignore_status_mask) == 0) {			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}		if ((rxs & S3C2410_UERSTAT_OVERRUN) &&		    tty->flip.count < TTY_FLIPBUF_SIZE) {			/*			 * Overrun is special, since it's reported			 * immediately, and doesn't affect the current			 * character.			 */			*tty->flip.char_buf_ptr++ = 0;			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;			tty->flip.count++;		}	}	tty_flip_buffer_push(tty); out:	return IRQ_HANDLED;}static irqreturn_tserial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs){	struct uart_port *port = (struct uart_port *)dev_id;	struct circ_buf *xmit = &port->info->xmit;	int count = 256;	if (port->x_char) {		wr_regb(port, S3C2410_UTXH, port->x_char);		port->icount.tx++;		port->x_char = 0;		goto out;	}	/* if there isnt anything more to transmit, or the uart is now	 * stopped, disable the uart and exit	*/	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {		serial_s3c2410_stop_tx(port, 0);		goto out;	}	/* try and drain the buffer... */	while (!uart_circ_empty(xmit) && count-- > 0) {		if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)			break;		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		port->icount.tx++;	}	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(port);	if (uart_circ_empty(xmit))		serial_s3c2410_stop_tx(port, 0); out:	return IRQ_HANDLED;}static unsigned intserial_s3c2410_tx_empty(struct uart_port *port){	unsigned int ufcon = rd_regl(port, S3C2410_UFCON);	return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;}/* no modem control lines */static unsigned intserial_s3c2410_get_mctrl(struct uart_port *port){	unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);	if (umstat & S3C2410_UMSTAT_CTS)		return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;	else		return TIOCM_CAR | TIOCM_DSR;}static voidserial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl){	/* todo - possibly remove AFC and do manual CTS */}static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state){	unsigned long flags;	unsigned int ucon;	spin_lock_irqsave(&port->lock, flags);	ucon = rd_regl(port, S3C2410_UCON);	if (break_state)		ucon |= S3C2410_UCON_SBREAK;	else		ucon &= ~S3C2410_UCON_SBREAK;	wr_regl(port, S3C2410_UCON, ucon);	spin_unlock_irqrestore(&port->lock, flags);}static int serial_s3c2410_startup(struct uart_port *port){	int ret;	tx_enabled(port) = 1;	rx_enabled(port) = 1;	dbg("serial_s3c2410_startup: port=%p (%p)\n",	    port, port->mapbase);	ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,			  serial_s3c2410_name, port);	if (ret != 0)		return ret;	ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,			  serial_s3c2410_name, port);	if (ret) {		free_irq(RX_IRQ(port), port);		return ret;	}	/* the port reset code should have done the correct	 * register setup for the port controls */	return ret;}static void serial_s3c2410_shutdown(struct uart_port *port){	free_irq(TX_IRQ(port), port);	free_irq(RX_IRQ(port), port);}static voidserial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,			   struct termios *old){	unsigned long flags;	unsigned int baud, quot;	unsigned int ulcon;	/*	 * We don't support modem control lines.	 */	termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);	termios->c_cflag |= CLOCAL;	/*	 * We don't support BREAK character recognition.	 */	termios->c_iflag &= ~(IGNBRK | BRKINT);	/*	 * Ask the core to calculate the divisor for us.	 */	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);	quot = uart_get_divisor(port, baud);	switch (termios->c_cflag & CSIZE) {	case CS5:		dbg("config: 5bits/char\n");		ulcon = S3C2410_LCON_CS5;		break;	case CS6:		dbg("config: 6bits/char\n");		ulcon = S3C2410_LCON_CS6;		break;	case CS7:		dbg("config: 7bits/char\n");		ulcon = S3C2410_LCON_CS7;		break;	case CS8:	default:		dbg("config: 8bits/char\n");		ulcon = S3C2410_LCON_CS8;		break;	}	if (termios->c_cflag & CSTOPB)		ulcon |= S3C2410_LCON_STOPB;	if (termios->c_cflag & PARENB) {		if (!(termios->c_cflag & PARODD))			ulcon |= S3C2410_LCON_PODD;		else			ulcon |= S3C2410_LCON_PEVEN;	} else {		ulcon |= S3C2410_LCON_PNONE;	}	/*	if (port->fifosize)	enable_fifo()	*/	spin_lock_irqsave(&port->lock, flags);	dbg("setting ulcon to %08x\n", ulcon);	//dbg("<flushing output from serial>\n");	/* set the ulcon register */	wr_regl(port, S3C2410_ULCON, ulcon);	dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",	    rd_regl(port, S3C2410_ULCON),	    rd_regl(port, S3C2410_UCON),	    rd_regl(port, S3C2410_UFCON));	/*	 * Update the per-port timeout.	 */	uart_update_timeout(port, termios->c_cflag, baud);	/*	 * Which character status flags are we interested in?	 */	port->read_status_mask = S3C2410_UERSTAT_OVERRUN;	if (termios->c_iflag & INPCK)		port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;	/*	 * Which character status flags should we ignore?	 */	port->ignore_status_mask = 0;	if (termios->c_iflag & IGNPAR)		port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;	if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)		port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;	/*	 * Ignore all characters if CREAD is not set.	 */	if ((termios->c_cflag & CREAD) == 0)		port->ignore_status_mask |= RXSTAT_DUMMY_READ;	spin_unlock_irqrestore(&port->lock, flags);}static const char *serial_s3c2410_type(struct uart_port *port){	return port->type == PORT_S3C2410 ? "S3C2410" : NULL;}#define MAP_SIZE (0x100)static voidserial_s3c2410_release_port(struct uart_port *port){	release_mem_region(port->mapbase, MAP_SIZE);}static intserial_s3c2410_request_port(struct uart_port *port){	return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)		!= NULL ? 0 : -EBUSY;}static voidserial_s3c2410_config_port(struct uart_port *port, int flags){	if (flags & UART_CONFIG_TYPE &&

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -