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

📄 ip22zilog.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Driver for Zilog serial chips found on SGI workstations and * servers.  This driver could actually be made more generic. * * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the * old drivers/sgi/char/sgiserial.c code which itself is based of the original * drivers/sbus/char/zs.c code.  A lot of code has been simply moved over * directly from there but much has been rewritten.  Credits therefore go out * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell * for their work there. * *  Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org) *  Copyright (C) 2002 David S. Miller (davem@redhat.com) */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/circ_buf.h>#include <linux/serial.h>#include <linux/sysrq.h>#include <linux/console.h>#include <linux/spinlock.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/sgialib.h>#include <asm/sgi/ioc.h>#include <asm/sgi/hpc3.h>#include <asm/sgi/ip22.h>#if defined(CONFIG_SERIAL_IP22_ZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include "ip22zilog.h"/* * On IP22 we need to delay after register accesses but we do not need to * flush writes. */#define ZSDELAY()		udelay(5)#define ZSDELAY_LONG()		udelay(20)#define ZS_WSYNC(channel)	do { } while (0)#define NUM_IP22ZILOG		1#define NUM_CHANNELS		(NUM_IP22ZILOG * 2)#define ZS_CLOCK		3672000	/* Zilog input clock rate. */#define ZS_CLOCK_DIVISOR	16      /* Divisor this driver uses. *//* * We wrap our port structure around the generic uart_port. */struct uart_ip22zilog_port {	struct uart_port		port;	/* IRQ servicing chain.  */	struct uart_ip22zilog_port	*next;	/* Current values of Zilog write registers.  */	unsigned char			curregs[NUM_ZSREGS];	unsigned int			flags;#define IP22ZILOG_FLAG_IS_CONS		0x00000004#define IP22ZILOG_FLAG_IS_KGDB		0x00000008#define IP22ZILOG_FLAG_MODEM_STATUS	0x00000010#define IP22ZILOG_FLAG_IS_CHANNEL_A	0x00000020#define IP22ZILOG_FLAG_REGS_HELD	0x00000040#define IP22ZILOG_FLAG_TX_STOPPED	0x00000080#define IP22ZILOG_FLAG_TX_ACTIVE	0x00000100#define IP22ZILOG_FLAG_RESET_DONE	0x00000200	unsigned int			tty_break;	unsigned char			parity_mask;	unsigned char			prev_status;};#define ZILOG_CHANNEL_FROM_PORT(PORT)	((struct zilog_channel *)((PORT)->membase))#define UART_ZILOG(PORT)		((struct uart_ip22zilog_port *)(PORT))#define IP22ZILOG_GET_CURR_REG(PORT, REGNUM)		\	(UART_ZILOG(PORT)->curregs[REGNUM])#define IP22ZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL)	\	((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))#define ZS_IS_CONS(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CONS)#define ZS_IS_KGDB(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_KGDB)#define ZS_WANTS_MODEM_STATUS(UP)	((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS)#define ZS_IS_CHANNEL_A(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A)#define ZS_REGS_HELD(UP)	((UP)->flags & IP22ZILOG_FLAG_REGS_HELD)#define ZS_TX_STOPPED(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED)#define ZS_TX_ACTIVE(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE)/* Reading and writing Zilog8530 registers.  The delays are to make this * driver work on the IP22 which needs a settling delay after each chip * register access, other machines handle this in hardware via auxiliary * flip-flops which implement the settle time we do in software. * * The port lock must be held and local IRQs must be disabled * when {read,write}_zsreg is invoked. */static unsigned char read_zsreg(struct zilog_channel *channel,				unsigned char reg){	unsigned char retval;	writeb(reg, &channel->control);	ZSDELAY();	retval = readb(&channel->control);	ZSDELAY();	return retval;}static void write_zsreg(struct zilog_channel *channel,			unsigned char reg, unsigned char value){	writeb(reg, &channel->control);	ZSDELAY();	writeb(value, &channel->control);	ZSDELAY();}static void ip22zilog_clear_fifo(struct zilog_channel *channel){	int i;	for (i = 0; i < 32; i++) {		unsigned char regval;		regval = readb(&channel->control);		ZSDELAY();		if (regval & Rx_CH_AV)			break;		regval = read_zsreg(channel, R1);		readb(&channel->data);		ZSDELAY();		if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) {			writeb(ERR_RES, &channel->control);			ZSDELAY();			ZS_WSYNC(channel);		}	}}/* This function must only be called when the TX is not busy.  The UART * port lock must be held and local interrupts disabled. */static void __load_zsregs(struct zilog_channel *channel, unsigned char *regs){	int i;	/* Let pending transmits finish.  */	for (i = 0; i < 1000; i++) {		unsigned char stat = read_zsreg(channel, R1);		if (stat & ALL_SNT)			break;		udelay(100);	}	writeb(ERR_RES, &channel->control);	ZSDELAY();	ZS_WSYNC(channel);	ip22zilog_clear_fifo(channel);	/* Disable all interrupts.  */	write_zsreg(channel, R1,		    regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB));	/* Set parity, sync config, stop bits, and clock divisor.  */	write_zsreg(channel, R4, regs[R4]);	/* Set misc. TX/RX control bits.  */	write_zsreg(channel, R10, regs[R10]);	/* Set TX/RX controls sans the enable bits.  */	write_zsreg(channel, R3, regs[R3] & ~RxENAB);	write_zsreg(channel, R5, regs[R5] & ~TxENAB);	/* Synchronous mode config.  */	write_zsreg(channel, R6, regs[R6]);	write_zsreg(channel, R7, regs[R7]);	/* Don't mess with the interrupt vector (R2, unused by us) and	 * master interrupt control (R9).  We make sure this is setup	 * properly at probe time then never touch it again.	 */	/* Disable baud generator.  */	write_zsreg(channel, R14, regs[R14] & ~BRENAB);	/* Clock mode control.  */	write_zsreg(channel, R11, regs[R11]);	/* Lower and upper byte of baud rate generator divisor.  */	write_zsreg(channel, R12, regs[R12]);	write_zsreg(channel, R13, regs[R13]);	/* Now rewrite R14, with BRENAB (if set).  */	write_zsreg(channel, R14, regs[R14]);	/* External status interrupt control.  */	write_zsreg(channel, R15, regs[R15]);	/* Reset external status interrupts.  */	write_zsreg(channel, R0, RES_EXT_INT);	write_zsreg(channel, R0, RES_EXT_INT);	/* Rewrite R3/R5, this time without enables masked.  */	write_zsreg(channel, R3, regs[R3]);	write_zsreg(channel, R5, regs[R5]);	/* Rewrite R1, this time without IRQ enabled masked.  */	write_zsreg(channel, R1, regs[R1]);}/* Reprogram the Zilog channel HW registers with the copies found in the * software state struct.  If the transmitter is busy, we defer this update * until the next TX complete interrupt.  Else, we do it right now. * * The UART port lock must be held and local interrupts disabled. */static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,				       struct zilog_channel *channel){	if (!ZS_REGS_HELD(up)) {		if (ZS_TX_ACTIVE(up)) {			up->flags |= IP22ZILOG_FLAG_REGS_HELD;		} else {			__load_zsregs(channel, up->curregs);		}	}}#define Rx_BRK 0x0100                   /* BREAK event software flag.  */#define Rx_SYS 0x0200                   /* SysRq event software flag.  */static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up,						  struct zilog_channel *channel){	struct tty_struct *tty;	unsigned char ch, flag;	unsigned int r1;	tty = NULL;	if (up->port.info != NULL &&	    up->port.info->tty != NULL)		tty = up->port.info->tty;	for (;;) {		ch = readb(&channel->control);		ZSDELAY();		if (!(ch & Rx_CH_AV))			break;		r1 = read_zsreg(channel, R1);		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {			writeb(ERR_RES, &channel->control);			ZSDELAY();			ZS_WSYNC(channel);		}		ch = readb(&channel->data);		ZSDELAY();		ch &= up->parity_mask;		/* Handle the null char got when BREAK is removed.  */		if (!ch)			r1 |= up->tty_break;		/* A real serial line, record the character and status.  */		flag = TTY_NORMAL;		up->port.icount.rx++;		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {			up->tty_break = 0;			if (r1 & (Rx_SYS | Rx_BRK)) {				up->port.icount.brk++;				if (r1 & Rx_SYS)					continue;				r1 &= ~(PAR_ERR | CRC_ERR);			}			else if (r1 & PAR_ERR)				up->port.icount.parity++;			else if (r1 & CRC_ERR)				up->port.icount.frame++;			if (r1 & Rx_OVR)				up->port.icount.overrun++;			r1 &= up->port.read_status_mask;			if (r1 & Rx_BRK)				flag = TTY_BREAK;			else if (r1 & PAR_ERR)				flag = TTY_PARITY;			else if (r1 & CRC_ERR)				flag = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch))			continue;		if (tty)			uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);	}	return tty;}static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,				   struct zilog_channel *channel){	unsigned char status;	status = readb(&channel->control);	ZSDELAY();	writeb(RES_EXT_INT, &channel->control);	ZSDELAY();	ZS_WSYNC(channel);	if (up->curregs[R15] & BRKIE) {		if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) {			if (uart_handle_break(&up->port))				up->tty_break = Rx_SYS;			else				up->tty_break = Rx_BRK;		}	}	if (ZS_WANTS_MODEM_STATUS(up)) {		if (status & SYNC)			up->port.icount.dsr++;		/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.		 * But it does not tell us which bit has changed, we have to keep		 * track of this ourselves.		 */		if ((status ^ up->prev_status) ^ DCD)			uart_handle_dcd_change(&up->port,					       (status & DCD));		if ((status ^ up->prev_status) ^ CTS)			uart_handle_cts_change(&up->port,					       (status & CTS));		wake_up_interruptible(&up->port.info->delta_msr_wait);	}	up->prev_status = status;}static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,				    struct zilog_channel *channel){	struct circ_buf *xmit;	if (ZS_IS_CONS(up)) {		unsigned char status = readb(&channel->control);		ZSDELAY();		/* TX still busy?  Just wait for the next TX done interrupt.		 *		 * It can occur because of how we do serial console writes.  It would		 * be nice to transmit console writes just like we normally would for		 * a TTY line. (ie. buffered and TX interrupt driven).  That is not		 * easy because console writes cannot sleep.  One solution might be		 * to poll on enough port->xmit space becomming free.  -DaveM		 */		if (!(status & Tx_BUF_EMP))			return;	}	up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE;	if (ZS_REGS_HELD(up)) {		__load_zsregs(channel, up->curregs);		up->flags &= ~IP22ZILOG_FLAG_REGS_HELD;	}	if (ZS_TX_STOPPED(up)) {		up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED;		goto ack_tx_int;	}	if (up->port.x_char) {		up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;		writeb(up->port.x_char, &channel->data);		ZSDELAY();		ZS_WSYNC(channel);		up->port.icount.tx++;		up->port.x_char = 0;		return;	}	if (up->port.info == NULL)		goto ack_tx_int;

⌨️ 快捷键说明

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