clps711x.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 616 行

C
616
字号
/* *  linux/drivers/char/clps711x.c * *  Driver for CLPS711x serial ports * *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * *  Copyright 1999 ARM Limited *  Copyright (C) 2000 Deep Blue Solutions Ltd. * * 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 * *  $Id: clps711x.c,v 1.42 2002/07/28 10:03:28 rmk Exp $ * */#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/spinlock.h>#include <linux/device.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/irq.h>#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include <asm/hardware/clps7111.h>#define UART_NR		2#define SERIAL_CLPS711X_MAJOR	204#define SERIAL_CLPS711X_MINOR	40#define SERIAL_CLPS711X_NR	UART_NR/* * We use the relevant SYSCON register as a base address for these ports. */#define UBRLCR(port)		((port)->iobase + UBRLCR1 - SYSCON1)#define UARTDR(port)		((port)->iobase + UARTDR1 - SYSCON1)#define SYSFLG(port)		((port)->iobase + SYSFLG1 - SYSCON1)#define SYSCON(port)		((port)->iobase + SYSCON1 - SYSCON1)#define TX_IRQ(port)		((port)->irq)#define RX_IRQ(port)		((port)->irq + 1)#define UART_ANY_ERR		(UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)#define tx_enabled(port)	((port)->unused[0])static voidclps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop){	if (tx_enabled(port)) {		disable_irq(TX_IRQ(port));		tx_enabled(port) = 0;	}}static voidclps711xuart_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 clps711xuart_stop_rx(struct uart_port *port){	disable_irq(RX_IRQ(port));}static void clps711xuart_enable_ms(struct uart_port *port){}static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs){	struct uart_port *port = dev_id;	struct tty_struct *tty = port->info->tty;	unsigned int status, ch, flg, ignored = 0;	status = clps_readl(SYSFLG(port));	while (!(status & SYSFLG_URXFE)) {		ch = clps_readl(UARTDR(port));		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			goto ignore_char;		port->icount.rx++;		flg = TTY_NORMAL;		/*		 * Note that the error handling code is		 * out of the main execution path		 */		if (ch & UART_ANY_ERR)			goto handle_error;		if (uart_handle_sysrq_char(port, ch, regs))			goto ignore_char;	error_return:		*tty->flip.flag_buf_ptr++ = flg;		*tty->flip.char_buf_ptr++ = ch;		tty->flip.count++;	ignore_char:		status = clps_readl(SYSFLG(port));	} out:	tty_flip_buffer_push(tty);	return IRQ_HANDLED; handle_error:	if (ch & UARTDR_PARERR)		port->icount.parity++;	else if (ch & UARTDR_FRMERR)		port->icount.frame++;	if (ch & UARTDR_OVERR)		port->icount.overrun++;	if (ch & port->ignore_status_mask) {		if (++ignored > 100)			goto out;		goto ignore_char;	}	ch &= port->read_status_mask;	if (ch & UARTDR_PARERR)		flg = TTY_PARITY;	else if (ch & UARTDR_FRMERR)		flg = TTY_FRAME;	if (ch & UARTDR_OVERR) {		/*		 * CHECK: does overrun affect the current character?		 * ASSUMPTION: it does not.		 */		*tty->flip.flag_buf_ptr++ = flg;		*tty->flip.char_buf_ptr++ = ch;		tty->flip.count++;		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			goto ignore_char;		ch = 0;		flg = TTY_OVERRUN;	}#ifdef SUPPORT_SYSRQ	port->sysrq = 0;#endif	goto error_return;}static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs){	struct uart_port *port = dev_id;	struct circ_buf *xmit = &port->info->xmit;	int count;	if (port->x_char) {		clps_writel(port->x_char, UARTDR(port));		port->icount.tx++;		port->x_char = 0;		return IRQ_HANDLED;	}	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {		clps711xuart_stop_tx(port, 0);		return IRQ_HANDLED;	}	count = port->fifosize >> 1;	do {		clps_writel(xmit->buf[xmit->tail], UARTDR(port));		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))		clps711xuart_stop_tx(port, 0);	return IRQ_HANDLED;}static unsigned int clps711xuart_tx_empty(struct uart_port *port){	unsigned int status = clps_readl(SYSFLG(port));	return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;}static unsigned int clps711xuart_get_mctrl(struct uart_port *port){	unsigned int port_addr;	unsigned int result = 0;	unsigned int status;	port_addr = SYSFLG(port);	if (port_addr == SYSFLG1) {		status = clps_readl(SYSFLG1);		if (status & SYSFLG1_DCD)			result |= TIOCM_CAR;		if (status & SYSFLG1_DSR)			result |= TIOCM_DSR;		if (status & SYSFLG1_CTS)			result |= TIOCM_CTS;	}	return result;}static voidclps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl){}static void clps711xuart_break_ctl(struct uart_port *port, int break_state){	unsigned long flags;	unsigned int ubrlcr;	spin_lock_irqsave(&port->lock, flags);	ubrlcr = clps_readl(UBRLCR(port));	if (break_state == -1)		ubrlcr |= UBRLCR_BREAK;	else		ubrlcr &= ~UBRLCR_BREAK;	clps_writel(ubrlcr, UBRLCR(port));	spin_unlock_irqrestore(&port->lock, flags);}static int clps711xuart_startup(struct uart_port *port){	unsigned int syscon;	int retval;	tx_enabled(port) = 1;	/*	 * Allocate the IRQs	 */	retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,			     "clps711xuart_tx", port);	if (retval)		return retval;	retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,			     "clps711xuart_rx", port);	if (retval) {		free_irq(TX_IRQ(port), port);		return retval;	}	/*	 * enable the port	 */	syscon = clps_readl(SYSCON(port));	syscon |= SYSCON_UARTEN;	clps_writel(syscon, SYSCON(port));	return 0;}static void clps711xuart_shutdown(struct uart_port *port){	unsigned int ubrlcr, syscon;	/*	 * Free the interrupt	 */	free_irq(TX_IRQ(port), port);	/* TX interrupt */	free_irq(RX_IRQ(port), port);	/* RX interrupt */	/*	 * disable the port	 */	syscon = clps_readl(SYSCON(port));	syscon &= ~SYSCON_UARTEN;	clps_writel(syscon, SYSCON(port));	/*	 * disable break condition and fifos	 */	ubrlcr = clps_readl(UBRLCR(port));	ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);	clps_writel(ubrlcr, UBRLCR(port));}static voidclps711xuart_set_termios(struct uart_port *port, struct termios *termios,			 struct termios *old){	unsigned int ubrlcr, baud, quot;	unsigned long flags;	/*	 * We don't implement CREAD.	 */	termios->c_cflag |= CREAD;	/*	 * 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:		ubrlcr = UBRLCR_WRDLEN5;		break;	case CS6:		ubrlcr = UBRLCR_WRDLEN6;		break;	case CS7:		ubrlcr = UBRLCR_WRDLEN7;		break;	default: // CS8		ubrlcr = UBRLCR_WRDLEN8;		break;	}	if (termios->c_cflag & CSTOPB)		ubrlcr |= UBRLCR_XSTOP;	if (termios->c_cflag & PARENB) {		ubrlcr |= UBRLCR_PRTEN;		if (!(termios->c_cflag & PARODD))			ubrlcr |= UBRLCR_EVENPRT;	}	if (port->fifosize > 1)		ubrlcr |= UBRLCR_FIFOEN;	spin_lock_irqsave(&port->lock, flags);	/*	 * Update the per-port timeout.	 */	uart_update_timeout(port, termios->c_cflag, baud);	port->read_status_mask = UARTDR_OVERR;	if (termios->c_iflag & INPCK)		port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;	/*	 * Characters to ignore	 */	port->ignore_status_mask = 0;	if (termios->c_iflag & IGNPAR)		port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;	if (termios->c_iflag & IGNBRK) {		/*		 * If we're ignoring parity and break indicators,		 * ignore overruns to (for real raw support).		 */		if (termios->c_iflag & IGNPAR)			port->ignore_status_mask |= UARTDR_OVERR;	}	quot -= 1;	clps_writel(ubrlcr | quot, UBRLCR(port));	spin_unlock_irqrestore(&port->lock, flags);}static const char *clps711xuart_type(struct uart_port *port){	return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;}/* * Configure/autoconfigure the port. */static void clps711xuart_config_port(struct uart_port *port, int flags){	if (flags & UART_CONFIG_TYPE)		port->type = PORT_CLPS711X;}static void clps711xuart_release_port(struct uart_port *port){}static int clps711xuart_request_port(struct uart_port *port){	return 0;}static struct uart_ops clps711x_pops = {	.tx_empty	= clps711xuart_tx_empty,	.set_mctrl	= clps711xuart_set_mctrl_null,	.get_mctrl	= clps711xuart_get_mctrl,	.stop_tx	= clps711xuart_stop_tx,	.start_tx	= clps711xuart_start_tx,	.stop_rx	= clps711xuart_stop_rx,	.enable_ms	= clps711xuart_enable_ms,	.break_ctl	= clps711xuart_break_ctl,	.startup	= clps711xuart_startup,	.shutdown	= clps711xuart_shutdown,	.set_termios	= clps711xuart_set_termios,	.type		= clps711xuart_type,	.config_port	= clps711xuart_config_port,	.release_port	= clps711xuart_release_port,	.request_port	= clps711xuart_request_port,};static struct uart_port clps711x_ports[UART_NR] = {	{		.iobase		= SYSCON1,		.irq		= IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */		.uartclk	= 3686400,		.fifosize	= 16,		.ops		= &clps711x_pops,		.line		= 0,		.flags		= ASYNC_BOOT_AUTOCONF,	},	{		.iobase		= SYSCON2,		.irq		= IRQ_UTXINT2, /* IRQ_URXINT2 */		.uartclk	= 3686400,		.fifosize	= 16,		.ops		= &clps711x_pops,		.line		= 1,		.flags		= ASYNC_BOOT_AUTOCONF,	}};#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE/* *	Print a string to the serial port trying not to disturb *	any possible real use of the port... * *	The console_lock must be held when we get here. * *	Note that this is called with interrupts already disabled */static voidclps711xuart_console_write(struct console *co, const char *s,			   unsigned int count){	struct uart_port *port = clps711x_ports + co->index;	unsigned int status, syscon;	int i;	/*	 *	Ensure that the port is enabled.	 */	syscon = clps_readl(SYSCON(port));	clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));	/*	 *	Now, do each character	 */	for (i = 0; i < count; i++) {		do {			status = clps_readl(SYSFLG(port));		} while (status & SYSFLG_UTXFF);		clps_writel(s[i], UARTDR(port));		if (s[i] == '\n') {			do {				status = clps_readl(SYSFLG(port));			} while (status & SYSFLG_UTXFF);			clps_writel('\r', UARTDR(port));		}	}	/*	 *	Finally, wait for transmitter to become empty	 *	and restore the uart state.	 */	do {		status = clps_readl(SYSFLG(port));	} while (status & SYSFLG_UBUSY);	clps_writel(syscon, SYSCON(port));}static void __initclps711xuart_console_get_options(struct uart_port *port, int *baud,				 int *parity, int *bits){	if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {		unsigned int ubrlcr, quot;		ubrlcr = clps_readl(UBRLCR(port));		*parity = 'n';		if (ubrlcr & UBRLCR_PRTEN) {			if (ubrlcr & UBRLCR_EVENPRT)				*parity = 'e';			else				*parity = 'o';		}		if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)			*bits = 7;		else			*bits = 8;		quot = ubrlcr & UBRLCR_BAUD_MASK;		*baud = port->uartclk / (16 * (quot + 1));	}}static int __init clps711xuart_console_setup(struct console *co, char *options){	struct uart_port *port;	int baud = 38400;	int bits = 8;	int parity = 'n';	int flow = 'n';	/*	 * 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(clps711x_ports, UART_NR, co);	if (options)		uart_parse_options(options, &baud, &parity, &bits, &flow);	else		clps711xuart_console_get_options(port, &baud, &parity, &bits);	return uart_set_options(port, co, baud, parity, bits, flow);}extern struct uart_driver clps711x_reg;static struct console clps711x_console = {	.name		= "ttyCL",	.write		= clps711xuart_console_write,	.device		= uart_console_device,	.setup		= clps711xuart_console_setup,	.flags		= CON_PRINTBUFFER,	.index		= -1,	.data		= &clps711x_reg,};static int __init clps711xuart_console_init(void){	register_console(&clps711x_console);	return 0;}console_initcall(clps711xuart_console_init);#define CLPS711X_CONSOLE	&clps711x_console#else#define CLPS711X_CONSOLE	NULL#endifstatic struct uart_driver clps711x_reg = {	.driver_name		= "ttyCL",	.dev_name		= "ttyCL",	.major			= SERIAL_CLPS711X_MAJOR,	.minor			= SERIAL_CLPS711X_MINOR,	.nr			= UART_NR,	.cons			= CLPS711X_CONSOLE,};static int __init clps711xuart_init(void){	int ret, i;	printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.42 $\n");	ret = uart_register_driver(&clps711x_reg);	if (ret)		return ret;	for (i = 0; i < UART_NR; i++)		uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);	return 0;}static void __exit clps711xuart_exit(void){	int i;	for (i = 0; i < UART_NR; i++)		uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);	uart_unregister_driver(&clps711x_reg);}module_init(clps711xuart_init);module_exit(clps711xuart_exit);MODULE_AUTHOR("Deep Blue Solutions Ltd");MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.42 $");MODULE_LICENSE("GPL");MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);

⌨️ 快捷键说明

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