zs.c

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

C
1,288
字号
/* * zs.c: Serial port driver for IOASIC DECstations. * * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. * Derived from drivers/macintosh/macserial.c by Harald Koerfgen. * * DECstation changes * Copyright (C) 1998-2000 Harald Koerfgen * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007  Maciej W. Rozycki * * For the rest of the code the original Copyright applies: * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * * * Note: for IOASIC systems the wiring is as follows: * * mouse/keyboard: * DIN-7 MJ-4  signal        SCC * 2     1     TxD       <-  A.TxD * 3     4     RxD       ->  A.RxD * * EIA-232/EIA-423: * DB-25 MMJ-6 signal        SCC * 2     2     TxD       <-  B.TxD * 3     5     RxD       ->  B.RxD * 4           RTS       <- ~A.RTS * 5           CTS       -> ~B.CTS * 6     6     DSR       -> ~A.SYNC * 8           CD        -> ~B.DCD * 12          DSRS(DCE) -> ~A.CTS  (*) * 15          TxC       ->  B.TxC * 17          RxC       ->  B.RxC * 20    1     DTR       <- ~A.DTR * 22          RI        -> ~A.DCD * 23          DSRS(DTE) <- ~B.RTS * * (*) EIA-232 defines the signal at this pin to be SCD, while DSRS(DCE) *     is shared with DSRS(DTE) at pin 23. * * As you can immediately notice the wiring of the RTS, DTR and DSR signals * is a bit odd.  This makes the handling of port B unnecessarily * complicated and prevents the use of some automatic modes of operation. */#if defined(CONFIG_SERIAL_ZS_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/bug.h>#include <linux/console.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/ioport.h>#include <linux/irqflags.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/spinlock.h>#include <linux/sysrq.h>#include <linux/tty.h>#include <linux/types.h>#include <asm/atomic.h>#include <asm/system.h>#include <asm/dec/interrupts.h>#include <asm/dec/ioasic_addrs.h>#include <asm/dec/system.h>#include "zs.h"MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");MODULE_DESCRIPTION("DECstation Z85C30 serial driver");MODULE_LICENSE("GPL");static char zs_name[] __initdata = "DECstation Z85C30 serial driver version ";static char zs_version[] __initdata = "0.10";/* * It would be nice to dynamically allocate everything that * depends on ZS_NUM_SCCS, so we could support any number of * Z85C30s, but for now... */#define ZS_NUM_SCCS	2		/* Max # of ZS chips supported.  */#define ZS_NUM_CHAN	2		/* 2 channels per chip.  */#define ZS_CHAN_A	0		/* Index of the channel A.  */#define ZS_CHAN_B	1		/* Index of the channel B.  */#define ZS_CHAN_IO_SIZE 8		/* IOMEM space size.  */#define ZS_CHAN_IO_STRIDE 4		/* Register alignment.  */#define ZS_CHAN_IO_OFFSET 1		/* The SCC resides on the high byte					   of the 16-bit IOBUS.  */#define ZS_CLOCK        7372800 	/* Z85C30 PCLK input clock rate.  */#define to_zport(uport) container_of(uport, struct zs_port, port)struct zs_parms {	resource_size_t scc[ZS_NUM_SCCS];	int irq[ZS_NUM_SCCS];};static struct zs_scc zs_sccs[ZS_NUM_SCCS];static u8 zs_init_regs[ZS_NUM_REGS] __initdata = {	0,				/* write 0 */	PAR_SPEC,			/* write 1 */	0,				/* write 2 */	0,				/* write 3 */	X16CLK | SB1,			/* write 4 */	0,				/* write 5 */	0, 0, 0,			/* write 6, 7, 8 */	MIE | DLC | NV,			/* write 9 */	NRZ,				/* write 10 */	TCBR | RCBR,			/* write 11 */	0, 0,				/* BRG time constant, write 12 + 13 */	BRSRC | BRENABL,		/* write 14 */	0,				/* write 15 */};/* * Debugging. */#undef ZS_DEBUG_REGS/* * Reading and writing Z85C30 registers. */static void recovery_delay(void){	udelay(2);}static u8 read_zsreg(struct zs_port *zport, int reg){	void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET;	u8 retval;	if (reg != 0) {		writeb(reg & 0xf, control);		fast_iob();		recovery_delay();	}	retval = readb(control);	recovery_delay();	return retval;}static void write_zsreg(struct zs_port *zport, int reg, u8 value){	void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET;	if (reg != 0) {		writeb(reg & 0xf, control);		fast_iob(); recovery_delay();	}	writeb(value, control);	fast_iob();	recovery_delay();	return;}static u8 read_zsdata(struct zs_port *zport){	void __iomem *data = zport->port.membase +			     ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET;	u8 retval;	retval = readb(data);	recovery_delay();	return retval;}static void write_zsdata(struct zs_port *zport, u8 value){	void __iomem *data = zport->port.membase +			     ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET;	writeb(value, data);	fast_iob();	recovery_delay();	return;}#ifdef ZS_DEBUG_REGSvoid zs_dump(void){	struct zs_port *zport;	int i, j;	for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {		zport = &zs_sccs[i / ZS_NUM_CHAN].zport[i % ZS_NUM_CHAN];		if (!zport->scc)			continue;		for (j = 0; j < 16; j++)			printk("W%-2d = 0x%02x\t", j, zport->regs[j]);		printk("\n");		for (j = 0; j < 16; j++)			printk("R%-2d = 0x%02x\t", j, read_zsreg(zport, j));		printk("\n\n");	}}#endifstatic void zs_spin_lock_cond_irq(spinlock_t *lock, int irq){	if (irq)		spin_lock_irq(lock);	else		spin_lock(lock);}static void zs_spin_unlock_cond_irq(spinlock_t *lock, int irq){	if (irq)		spin_unlock_irq(lock);	else		spin_unlock(lock);}static int zs_receive_drain(struct zs_port *zport){	int loops = 10000;	while ((read_zsreg(zport, R0) & Rx_CH_AV) && loops--)		read_zsdata(zport);	return loops;}static int zs_transmit_drain(struct zs_port *zport, int irq){	struct zs_scc *scc = zport->scc;	int loops = 10000;	while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && loops--) {		zs_spin_unlock_cond_irq(&scc->zlock, irq);		udelay(2);		zs_spin_lock_cond_irq(&scc->zlock, irq);	}	return loops;}static int zs_line_drain(struct zs_port *zport, int irq){	struct zs_scc *scc = zport->scc;	int loops = 10000;	while (!(read_zsreg(zport, R1) & ALL_SNT) && loops--) {		zs_spin_unlock_cond_irq(&scc->zlock, irq);		udelay(2);		zs_spin_lock_cond_irq(&scc->zlock, irq);	}	return loops;}static void load_zsregs(struct zs_port *zport, u8 *regs, int irq){	/* Let the current transmission finish.  */	zs_line_drain(zport, irq);	/* Load 'em up.  */	write_zsreg(zport, R3, regs[3] & ~RxENABLE);	write_zsreg(zport, R5, regs[5] & ~TxENAB);	write_zsreg(zport, R4, regs[4]);	write_zsreg(zport, R9, regs[9]);	write_zsreg(zport, R1, regs[1]);	write_zsreg(zport, R2, regs[2]);	write_zsreg(zport, R10, regs[10]);	write_zsreg(zport, R14, regs[14] & ~BRENABL);	write_zsreg(zport, R11, regs[11]);	write_zsreg(zport, R12, regs[12]);	write_zsreg(zport, R13, regs[13]);	write_zsreg(zport, R14, regs[14]);	write_zsreg(zport, R15, regs[15]);	if (regs[3] & RxENABLE)		write_zsreg(zport, R3, regs[3]);	if (regs[5] & TxENAB)		write_zsreg(zport, R5, regs[5]);	return;}/* * Status handling routines. *//* * zs_tx_empty() -- get the transmitter empty status * * Purpose: Let user call ioctl() to get info when the UART physically * 	    is emptied.  On bus types like RS485, the transmitter must * 	    release the bus after transmitting.  This must be done when * 	    the transmit shift register is empty, not be done when the * 	    transmit holding register is empty.  This functionality * 	    allows an RS485 driver to be written in user space. */static unsigned int zs_tx_empty(struct uart_port *uport){	struct zs_port *zport = to_zport(uport);	struct zs_scc *scc = zport->scc;	unsigned long flags;	u8 status;	spin_lock_irqsave(&scc->zlock, flags);	status = read_zsreg(zport, R1);	spin_unlock_irqrestore(&scc->zlock, flags);	return status & ALL_SNT ? TIOCSER_TEMT : 0;}static unsigned int zs_raw_get_ab_mctrl(struct zs_port *zport_a,					struct zs_port *zport_b){	u8 status_a, status_b;	unsigned int mctrl;	status_a = read_zsreg(zport_a, R0);	status_b = read_zsreg(zport_b, R0);	mctrl = ((status_b & CTS) ? TIOCM_CTS : 0) |		((status_b & DCD) ? TIOCM_CAR : 0) |		((status_a & DCD) ? TIOCM_RNG : 0) |		((status_a & SYNC_HUNT) ? TIOCM_DSR : 0);	return mctrl;}static unsigned int zs_raw_get_mctrl(struct zs_port *zport){	struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];	return zport != zport_a ? zs_raw_get_ab_mctrl(zport_a, zport) : 0;}static unsigned int zs_raw_xor_mctrl(struct zs_port *zport){	struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];	unsigned int mmask, mctrl, delta;	u8 mask_a, mask_b;	if (zport == zport_a)		return 0;	mask_a = zport_a->regs[15];	mask_b = zport->regs[15];	mmask = ((mask_b & CTSIE) ? TIOCM_CTS : 0) |		((mask_b & DCDIE) ? TIOCM_CAR : 0) |		((mask_a & DCDIE) ? TIOCM_RNG : 0) |		((mask_a & SYNCIE) ? TIOCM_DSR : 0);	mctrl = zport->mctrl;	if (mmask) {		mctrl &= ~mmask;		mctrl |= zs_raw_get_ab_mctrl(zport_a, zport) & mmask;	}	delta = mctrl ^ zport->mctrl;	if (delta)		zport->mctrl = mctrl;	return delta;}static unsigned int zs_get_mctrl(struct uart_port *uport){	struct zs_port *zport = to_zport(uport);	struct zs_scc *scc = zport->scc;	unsigned int mctrl;	spin_lock(&scc->zlock);	mctrl = zs_raw_get_mctrl(zport);	spin_unlock(&scc->zlock);	return mctrl;}static void zs_set_mctrl(struct uart_port *uport, unsigned int mctrl){	struct zs_port *zport = to_zport(uport);	struct zs_scc *scc = zport->scc;	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];	u8 oldloop, newloop;	spin_lock(&scc->zlock);	if (zport != zport_a) {		if (mctrl & TIOCM_DTR)			zport_a->regs[5] |= DTR;		else			zport_a->regs[5] &= ~DTR;		if (mctrl & TIOCM_RTS)			zport_a->regs[5] |= RTS;		else			zport_a->regs[5] &= ~RTS;		write_zsreg(zport_a, R5, zport_a->regs[5]);	}	/* Rarely modified, so don't poke at hardware unless necessary. */	oldloop = zport->regs[14];	newloop = oldloop;	if (mctrl & TIOCM_LOOP)		newloop |= LOOPBAK;	else		newloop &= ~LOOPBAK;	if (newloop != oldloop) {		zport->regs[14] = newloop;		write_zsreg(zport, R14, zport->regs[14]);	}	spin_unlock(&scc->zlock);}static void zs_raw_stop_tx(struct zs_port *zport){	write_zsreg(zport, R0, RES_Tx_P);	zport->tx_stopped = 1;}static void zs_stop_tx(struct uart_port *uport){	struct zs_port *zport = to_zport(uport);	struct zs_scc *scc = zport->scc;

⌨️ 快捷键说明

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