netx-serial.c

来自「linux 内核源代码」· C语言 代码 · 共 748 行 · 第 1/2 页

C
748
字号
/* * drivers/serial/netx-serial.c * * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * 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 */#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/device.h>#include <linux/module.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/tty_flip.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/netx-regs.h>/* We've been assigned a range on the "Low-density serial ports" major */#define SERIAL_NX_MAJOR	204#define MINOR_START	170#ifdef CONFIG_SERIAL_NETX_CONSOLEenum uart_regs {	UART_DR              = 0x00,	UART_SR              = 0x04,	UART_LINE_CR         = 0x08,	UART_BAUDDIV_MSB     = 0x0c,	UART_BAUDDIV_LSB     = 0x10,	UART_CR              = 0x14,	UART_FR              = 0x18,	UART_IIR             = 0x1c,	UART_ILPR            = 0x20,	UART_RTS_CR          = 0x24,	UART_RTS_LEAD        = 0x28,	UART_RTS_TRAIL       = 0x2c,	UART_DRV_ENABLE      = 0x30,	UART_BRM_CR          = 0x34,	UART_RXFIFO_IRQLEVEL = 0x38,	UART_TXFIFO_IRQLEVEL = 0x3c,};#define SR_FE (1<<0)#define SR_PE (1<<1)#define SR_BE (1<<2)#define SR_OE (1<<3)#define LINE_CR_BRK       (1<<0)#define LINE_CR_PEN       (1<<1)#define LINE_CR_EPS       (1<<2)#define LINE_CR_STP2      (1<<3)#define LINE_CR_FEN       (1<<4)#define LINE_CR_5BIT      (0<<5)#define LINE_CR_6BIT      (1<<5)#define LINE_CR_7BIT      (2<<5)#define LINE_CR_8BIT      (3<<5)#define LINE_CR_BITS_MASK (3<<5)#define CR_UART_EN (1<<0)#define CR_SIREN   (1<<1)#define CR_SIRLP   (1<<2)#define CR_MSIE    (1<<3)#define CR_RIE     (1<<4)#define CR_TIE     (1<<5)#define CR_RTIE    (1<<6)#define CR_LBE     (1<<7)#define FR_CTS  (1<<0)#define FR_DSR  (1<<1)#define FR_DCD  (1<<2)#define FR_BUSY (1<<3)#define FR_RXFE (1<<4)#define FR_TXFF (1<<5)#define FR_RXFF (1<<6)#define FR_TXFE (1<<7)#define IIR_MIS (1<<0)#define IIR_RIS (1<<1)#define IIR_TIS (1<<2)#define IIR_RTIS (1<<3)#define IIR_MASK 0xf#define RTS_CR_AUTO (1<<0)#define RTS_CR_RTS  (1<<1)#define RTS_CR_COUNT (1<<2)#define RTS_CR_MOD2  (1<<3)#define RTS_CR_RTS_POL (1<<4)#define RTS_CR_CTS_CTR (1<<5)#define RTS_CR_CTS_POL (1<<6)#define RTS_CR_STICK   (1<<7)#define UART_PORT_SIZE 0x40#define DRIVER_NAME "netx-uart"struct netx_port {	struct uart_port	port;};static void netx_stop_tx(struct uart_port *port){	unsigned int val;	val = readl(port->membase + UART_CR);	writel(val & ~CR_TIE,  port->membase + UART_CR);}static void netx_stop_rx(struct uart_port *port){	unsigned int val;	val = readl(port->membase + UART_CR);	writel(val & ~CR_RIE,  port->membase + UART_CR);}static void netx_enable_ms(struct uart_port *port){	unsigned int val;	val = readl(port->membase + UART_CR);	writel(val | CR_MSIE, port->membase + UART_CR);}static inline void netx_transmit_buffer(struct uart_port *port){	struct circ_buf *xmit = &port->info->xmit;	if (port->x_char) {		writel(port->x_char, port->membase + UART_DR);		port->icount.tx++;		port->x_char = 0;		return;	}	if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {		netx_stop_tx(port);		return;	}	do {		/* send xmit->buf[xmit->tail]		 * out the port here */		writel(xmit->buf[xmit->tail], port->membase + UART_DR);		xmit->tail = (xmit->tail + 1) &		         (UART_XMIT_SIZE - 1);		port->icount.tx++;		if (uart_circ_empty(xmit))			break;	} while (!(readl(port->membase + UART_FR) & FR_TXFF));	if (uart_circ_empty(xmit))		netx_stop_tx(port);}static void netx_start_tx(struct uart_port *port){	writel(	    readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR);	if (!(readl(port->membase + UART_FR) & FR_TXFF))		netx_transmit_buffer(port);}static unsigned int netx_tx_empty(struct uart_port *port){	return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT;}static void netx_txint(struct uart_port *port){	struct circ_buf *xmit = &port->info->xmit;	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {		netx_stop_tx(port);		return;	}	netx_transmit_buffer(port);	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(port);}static void netx_rxint(struct uart_port *port){	unsigned char rx, flg, status;	struct tty_struct *tty = port->info->tty;	while (!(readl(port->membase + UART_FR) & FR_RXFE)) {		rx = readl(port->membase + UART_DR);		flg = TTY_NORMAL;		port->icount.rx++;		status = readl(port->membase + UART_SR);		if (status & SR_BE) {			writel(0, port->membase + UART_SR);			if (uart_handle_break(port))				continue;		}		if (unlikely(status & (SR_FE | SR_PE | SR_OE))) {			if (status & SR_PE)				port->icount.parity++;			else if (status & SR_FE)				port->icount.frame++;			if (status & SR_OE)				port->icount.overrun++;			status &= port->read_status_mask;			if (status & SR_BE)				flg = TTY_BREAK;			else if (status & SR_PE)				flg = TTY_PARITY;			else if (status & SR_FE)				flg = TTY_FRAME;		}		if (uart_handle_sysrq_char(port, rx))			continue;		uart_insert_char(port, status, SR_OE, rx, flg);	}	tty_flip_buffer_push(tty);	return;}static irqreturn_t netx_int(int irq, void *dev_id){	struct uart_port *port = dev_id;	unsigned long flags;	unsigned char status;	spin_lock_irqsave(&port->lock,flags);	status = readl(port->membase + UART_IIR) & IIR_MASK;	while (status) {		if (status & IIR_RIS)			netx_rxint(port);		if (status & IIR_TIS)			netx_txint(port);		if (status & IIR_MIS) {			if (readl(port->membase + UART_FR) & FR_CTS)				uart_handle_cts_change(port, 1);			else				uart_handle_cts_change(port, 0);		}		writel(0, port->membase + UART_IIR);		status = readl(port->membase + UART_IIR) & IIR_MASK;	}	spin_unlock_irqrestore(&port->lock,flags);	return IRQ_HANDLED;}static unsigned int netx_get_mctrl(struct uart_port *port){	unsigned int ret = TIOCM_DSR | TIOCM_CAR;	if (readl(port->membase + UART_FR) & FR_CTS)		ret |= TIOCM_CTS;	return ret;}static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl){	unsigned int val;	if (mctrl & TIOCM_RTS) {		val = readl(port->membase + UART_RTS_CR);		writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR);	}}static void netx_break_ctl(struct uart_port *port, int break_state){	unsigned int line_cr;	spin_lock_irq(&port->lock);	line_cr = readl(port->membase + UART_LINE_CR);	if (break_state != 0)		line_cr |= LINE_CR_BRK;	else		line_cr &= ~LINE_CR_BRK;	writel(line_cr, port->membase + UART_LINE_CR);	spin_unlock_irq(&port->lock);}static int netx_startup(struct uart_port *port){	int ret;	ret = request_irq(port->irq, netx_int, 0,			     DRIVER_NAME, port);	if (ret) {		dev_err(port->dev, "unable to grab irq%d\n",port->irq);		goto exit;	}	writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN,		port->membase + UART_LINE_CR);	writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN,		port->membase + UART_CR);exit:	return ret;}static void netx_shutdown(struct uart_port *port){	writel(0, port->membase + UART_CR) ;	free_irq(port->irq, port);}static voidnetx_set_termios(struct uart_port *port, struct ktermios *termios,		   struct ktermios *old){	unsigned int baud, quot;	unsigned char old_cr;	unsigned char line_cr = LINE_CR_FEN;	unsigned char rts_cr = 0;	switch (termios->c_cflag & CSIZE) {	case CS5:		line_cr |= LINE_CR_5BIT;		break;	case CS6:		line_cr |= LINE_CR_6BIT;		break;	case CS7:		line_cr |= LINE_CR_7BIT;		break;	case CS8:		line_cr |= LINE_CR_8BIT;		break;	}	if (termios->c_cflag & CSTOPB)		line_cr |= LINE_CR_STP2;	if (termios->c_cflag & PARENB) {		line_cr |= LINE_CR_PEN;		if (!(termios->c_cflag & PARODD))			line_cr |= LINE_CR_EPS;	}	if (termios->c_cflag & CRTSCTS)		rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL;

⌨️ 快捷键说明

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