vr41xx_siu.c

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

C
963
字号
/* *  Driver for NEC VR4100 series Serial Interface Unit. * *  Copyright (C) 2004-2007  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> * *  Based on drivers/serial/8250.c, by Russell King. * *  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 */#if defined(CONFIG_SERIAL_VR41XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/console.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/serial_reg.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <asm/io.h>#include <asm/vr41xx/siu.h>#include <asm/vr41xx/vr41xx.h>#define SIU_BAUD_BASE	1152000#define SIU_MAJOR	204#define SIU_MINOR_BASE	82#define RX_MAX_COUNT	256#define TX_MAX_COUNT	15#define SIUIRSEL	0x08 #define TMICMODE	0x20 #define TMICTX		0x10 #define IRMSEL		0x0c #define IRMSEL_HP	0x08 #define IRMSEL_TEMIC	0x04 #define IRMSEL_SHARP	0x00 #define IRUSESEL	0x02 #define SIRSEL		0x01static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = {	[0 ... SIU_PORTS_MAX-1] = {		.lock	= __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock),		.irq	= -1,	},};#ifdef CONFIG_SERIAL_VR41XX_CONSOLEstatic uint8_t lsr_break_flag[SIU_PORTS_MAX];#endif#define siu_read(port, offset)		readb((port)->membase + (offset))#define siu_write(port, offset, value)	writeb((value), (port)->membase + (offset))void vr41xx_select_siu_interface(siu_interface_t interface){	struct uart_port *port;	unsigned long flags;	uint8_t irsel;	port = &siu_uart_ports[0];	spin_lock_irqsave(&port->lock, flags);	irsel = siu_read(port, SIUIRSEL);	if (interface == SIU_INTERFACE_IRDA)		irsel |= SIRSEL;	else		irsel &= ~SIRSEL;	siu_write(port, SIUIRSEL, irsel);	spin_unlock_irqrestore(&port->lock, flags);}EXPORT_SYMBOL_GPL(vr41xx_select_siu_interface);void vr41xx_use_irda(irda_use_t use){	struct uart_port *port;	unsigned long flags;	uint8_t irsel;	port = &siu_uart_ports[0];	spin_lock_irqsave(&port->lock, flags);	irsel = siu_read(port, SIUIRSEL);	if (use == FIR_USE_IRDA)		irsel |= IRUSESEL;	else		irsel &= ~IRUSESEL;	siu_write(port, SIUIRSEL, irsel);	spin_unlock_irqrestore(&port->lock, flags);}EXPORT_SYMBOL_GPL(vr41xx_use_irda);void vr41xx_select_irda_module(irda_module_t module, irda_speed_t speed){	struct uart_port *port;	unsigned long flags;	uint8_t irsel;	port = &siu_uart_ports[0];	spin_lock_irqsave(&port->lock, flags);	irsel = siu_read(port, SIUIRSEL);	irsel &= ~(IRMSEL | TMICTX | TMICMODE);	switch (module) {	case SHARP_IRDA:		irsel |= IRMSEL_SHARP;		break;	case TEMIC_IRDA:		irsel |= IRMSEL_TEMIC | TMICMODE;		if (speed == IRDA_TX_4MBPS)			irsel |= TMICTX;		break;	case HP_IRDA:		irsel |= IRMSEL_HP;		break;	default:		break;	}	siu_write(port, SIUIRSEL, irsel);	spin_unlock_irqrestore(&port->lock, flags);}EXPORT_SYMBOL_GPL(vr41xx_select_irda_module);static inline void siu_clear_fifo(struct uart_port *port){	siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO);	siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |	                          UART_FCR_CLEAR_XMIT);	siu_write(port, UART_FCR, 0);}static inline unsigned long siu_port_size(struct uart_port *port){	switch (port->type) {	case PORT_VR41XX_SIU:		return 11UL;	case PORT_VR41XX_DSIU:		return 8UL;	}	return 0;}static inline unsigned int siu_check_type(struct uart_port *port){	if (port->line == 0)		return PORT_VR41XX_SIU;	if (port->line == 1 && port->irq != -1)		return PORT_VR41XX_DSIU;	return PORT_UNKNOWN;}static inline const char *siu_type_name(struct uart_port *port){	switch (port->type) {	case PORT_VR41XX_SIU:		return "SIU";	case PORT_VR41XX_DSIU:		return "DSIU";	}	return NULL;}static unsigned int siu_tx_empty(struct uart_port *port){	uint8_t lsr;	lsr = siu_read(port, UART_LSR);	if (lsr & UART_LSR_TEMT)		return TIOCSER_TEMT;	return 0;}static void siu_set_mctrl(struct uart_port *port, unsigned int mctrl){	uint8_t mcr = 0;	if (mctrl & TIOCM_DTR)		mcr |= UART_MCR_DTR;	if (mctrl & TIOCM_RTS)		mcr |= UART_MCR_RTS;	if (mctrl & TIOCM_OUT1)		mcr |= UART_MCR_OUT1;	if (mctrl & TIOCM_OUT2)		mcr |= UART_MCR_OUT2;	if (mctrl & TIOCM_LOOP)		mcr |= UART_MCR_LOOP;	siu_write(port, UART_MCR, mcr);}static unsigned int siu_get_mctrl(struct uart_port *port){	uint8_t msr;	unsigned int mctrl = 0;	msr = siu_read(port, UART_MSR);	if (msr & UART_MSR_DCD)		mctrl |= TIOCM_CAR;	if (msr & UART_MSR_RI)		mctrl |= TIOCM_RNG;	if (msr & UART_MSR_DSR)		mctrl |= TIOCM_DSR;	if (msr & UART_MSR_CTS)		mctrl |= TIOCM_CTS;	return mctrl;}static void siu_stop_tx(struct uart_port *port){	unsigned long flags;	uint8_t ier;	spin_lock_irqsave(&port->lock, flags);	ier = siu_read(port, UART_IER);	ier &= ~UART_IER_THRI;	siu_write(port, UART_IER, ier);	spin_unlock_irqrestore(&port->lock, flags);}static void siu_start_tx(struct uart_port *port){	unsigned long flags;	uint8_t ier;	spin_lock_irqsave(&port->lock, flags);	ier = siu_read(port, UART_IER);	ier |= UART_IER_THRI;	siu_write(port, UART_IER, ier);	spin_unlock_irqrestore(&port->lock, flags);}static void siu_stop_rx(struct uart_port *port){	unsigned long flags;	uint8_t ier;	spin_lock_irqsave(&port->lock, flags);	ier = siu_read(port, UART_IER);	ier &= ~UART_IER_RLSI;	siu_write(port, UART_IER, ier);	port->read_status_mask &= ~UART_LSR_DR;	spin_unlock_irqrestore(&port->lock, flags);}static void siu_enable_ms(struct uart_port *port){	unsigned long flags;	uint8_t ier;	spin_lock_irqsave(&port->lock, flags);	ier = siu_read(port, UART_IER);	ier |= UART_IER_MSI;	siu_write(port, UART_IER, ier);	spin_unlock_irqrestore(&port->lock, flags);}static void siu_break_ctl(struct uart_port *port, int ctl){	unsigned long flags;	uint8_t lcr;	spin_lock_irqsave(&port->lock, flags);	lcr = siu_read(port, UART_LCR);	if (ctl == -1)		lcr |= UART_LCR_SBC;	else		lcr &= ~UART_LCR_SBC;	siu_write(port, UART_LCR, lcr);	spin_unlock_irqrestore(&port->lock, flags);}static inline void receive_chars(struct uart_port *port, uint8_t *status){	struct tty_struct *tty;	uint8_t lsr, ch;	char flag;	int max_count = RX_MAX_COUNT;	tty = port->info->tty;	lsr = *status;	do {		ch = siu_read(port, UART_RX);		port->icount.rx++;		flag = TTY_NORMAL;#ifdef CONFIG_SERIAL_VR41XX_CONSOLE		lsr |= lsr_break_flag[port->line];		lsr_break_flag[port->line] = 0;#endif		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_FE |		                    UART_LSR_PE | UART_LSR_OE))) {			if (lsr & UART_LSR_BI) {				lsr &= ~(UART_LSR_FE | UART_LSR_PE);				port->icount.brk++;				if (uart_handle_break(port))					goto ignore_char;			}			if (lsr & UART_LSR_FE)				port->icount.frame++;			if (lsr & UART_LSR_PE)				port->icount.parity++;			if (lsr & UART_LSR_OE)				port->icount.overrun++;			lsr &= port->read_status_mask;			if (lsr & UART_LSR_BI)				flag = TTY_BREAK;			if (lsr & UART_LSR_FE)				flag = TTY_FRAME;			if (lsr & UART_LSR_PE)				flag = TTY_PARITY;		}		if (uart_handle_sysrq_char(port, ch))			goto ignore_char;		uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);	ignore_char:		lsr = siu_read(port, UART_LSR);	} while ((lsr & UART_LSR_DR) && (max_count-- > 0));	tty_flip_buffer_push(tty);	*status = lsr;}static inline void check_modem_status(struct uart_port *port){	uint8_t msr;	msr = siu_read(port, UART_MSR);	if ((msr & UART_MSR_ANY_DELTA) == 0)		return;	if (msr & UART_MSR_DDCD)		uart_handle_dcd_change(port, msr & UART_MSR_DCD);	if (msr & UART_MSR_TERI)		port->icount.rng++;	if (msr & UART_MSR_DDSR)		port->icount.dsr++;	if (msr & UART_MSR_DCTS)		uart_handle_cts_change(port, msr & UART_MSR_CTS);	wake_up_interruptible(&port->info->delta_msr_wait);}static inline void transmit_chars(struct uart_port *port){	struct circ_buf *xmit;	int max_count = TX_MAX_COUNT;	xmit = &port->info->xmit;	if (port->x_char) {		siu_write(port, UART_TX, port->x_char);		port->icount.tx++;		port->x_char = 0;		return;	}	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {		siu_stop_tx(port);		return;	}	do {		siu_write(port, UART_TX, xmit->buf[xmit->tail]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		port->icount.tx++;		if (uart_circ_empty(xmit))			break;	} while (max_count-- > 0);	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(port);	if (uart_circ_empty(xmit))		siu_stop_tx(port);}static irqreturn_t siu_interrupt(int irq, void *dev_id){	struct uart_port *port;	uint8_t iir, lsr;	port = (struct uart_port *)dev_id;	iir = siu_read(port, UART_IIR);	if (iir & UART_IIR_NO_INT)		return IRQ_NONE;	lsr = siu_read(port, UART_LSR);	if (lsr & UART_LSR_DR)		receive_chars(port, &lsr);	check_modem_status(port);	if (lsr & UART_LSR_THRE)		transmit_chars(port);	return IRQ_HANDLED;}static int siu_startup(struct uart_port *port){	int retval;	if (port->membase == NULL)		return -ENODEV;	siu_clear_fifo(port);	(void)siu_read(port, UART_LSR);	(void)siu_read(port, UART_RX);	(void)siu_read(port, UART_IIR);	(void)siu_read(port, UART_MSR);	if (siu_read(port, UART_LSR) == 0xff)		return -ENODEV;	retval = request_irq(port->irq, siu_interrupt, 0, siu_type_name(port), port);	if (retval)		return retval;	if (port->type == PORT_VR41XX_DSIU)		vr41xx_enable_dsiuint(DSIUINT_ALL);	siu_write(port, UART_LCR, UART_LCR_WLEN8);	spin_lock_irq(&port->lock);	siu_set_mctrl(port, port->mctrl);	spin_unlock_irq(&port->lock);	siu_write(port, UART_IER, UART_IER_RLSI | UART_IER_RDI);	(void)siu_read(port, UART_LSR);	(void)siu_read(port, UART_RX);

⌨️ 快捷键说明

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