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 + -
显示快捷键?