cregit-Linux how code gets into the kernel

Release 4.11 drivers/tty/serial/zs.c

/*
 * 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/module.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/tty_flip.h>
#include <linux/types.h>

#include <linux/atomic.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); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki13100.00%1100.00%
Total13100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki66100.00%1100.00%
Total66100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki67100.00%1100.00%
Total67100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki42100.00%1100.00%
Total42100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki43100.00%1100.00%
Total43100.00%1100.00%

#ifdef ZS_DEBUG_REGS
void 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"); } }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki123100.00%1100.00%
Total123100.00%1100.00%

#endif
static void zs_spin_lock_cond_irq(spinlock_t *lock, int irq) { if (irq) spin_lock_irq(lock); else spin_lock(lock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki28100.00%1100.00%
Total28100.00%1100.00%


static void zs_spin_unlock_cond_irq(spinlock_t *lock, int irq) { if (irq) spin_unlock_irq(lock); else spin_unlock(lock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki28100.00%1100.00%
Total28100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki3997.50%150.00%
Roel Kluin12.50%150.00%
Total40100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki7498.67%150.00%
Roel Kluin11.33%150.00%
Total75100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki7498.67%150.00%
Roel Kluin11.33%150.00%
Total75100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki235100.00%1100.00%
Total235100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki76100.00%1100.00%
Total76100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki97100.00%1100.00%
Total97100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki41100.00%1100.00%
Total41100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki166100.00%1100.00%
Total166100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki61100.00%1100.00%
Total61100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki203100.00%1100.00%
Total203100.00%1100.00%


static void zs_raw_stop_tx(struct zs_port *zport) { write_zsreg(zport, R0, RES_Tx_P); zport->tx_stopped = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki26100.00%1100.00%
Total26100.00%1100.00%


static void zs_stop_tx(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; spin_lock(&scc->zlock); zs_raw_stop_tx(zport); spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki51100.00%1100.00%
Total51100.00%1100.00%

static void zs_raw_transmit_chars(struct zs_port *);
static void zs_start_tx(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; spin_lock(&scc->zlock); if (zport->tx_stopped) { zs_transmit_drain(zport, 0); zport->tx_stopped = 0; zs_raw_transmit_chars(zport); } spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki72100.00%1100.00%
Total72100.00%1100.00%


static void zs_stop_rx(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; spin_lock(&scc->zlock); zport->regs[15] &= ~BRKIE; zport->regs[1] &= ~(RxINT_MASK | TxINT_ENAB); zport->regs[1] |= RxINT_DISAB; if (zport != zport_a) { /* A-side DCD tracks RI and SYNC tracks DSR. */ zport_a->regs[15] &= ~(DCDIE | SYNCIE); write_zsreg(zport_a, R15, zport_a->regs[15]); if (!(zport_a->regs[15] & BRKIE)) { zport_a->regs[1] &= ~EXT_INT_ENAB; write_zsreg(zport_a, R1, zport_a->regs[1]); } /* This-side DCD tracks DCD and CTS tracks CTS. */ zport->regs[15] &= ~(DCDIE | CTSIE); zport->regs[1] &= ~EXT_INT_ENAB; } else { /* DCD tracks RI and SYNC tracks DSR for the B side. */ if (!(zport->regs[15] & (DCDIE | SYNCIE))) zport->regs[1] &= ~EXT_INT_ENAB; } write_zsreg(zport, R15, zport->regs[15]); write_zsreg(zport, R1, zport->regs[1]); spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki254100.00%1100.00%
Total254100.00%1100.00%


static void zs_enable_ms(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; if (zport == zport_a) return; spin_lock(&scc->zlock); /* Clear Ext interrupts if not being handled already. */ if (!(zport_a->regs[1] & EXT_INT_ENAB)) write_zsreg(zport_a, R0, RES_EXT_INT); /* A-side DCD tracks RI and SYNC tracks DSR. */ zport_a->regs[1] |= EXT_INT_ENAB; zport_a->regs[15] |= DCDIE | SYNCIE; /* This-side DCD tracks DCD and CTS tracks CTS. */ zport->regs[15] |= DCDIE | CTSIE; zs_raw_xor_mctrl(zport); write_zsreg(zport_a, R1, zport_a->regs[1]); write_zsreg(zport_a, R15, zport_a->regs[15]); write_zsreg(zport, R15, zport->regs[15]); spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki170100.00%1100.00%
Total170100.00%1100.00%


static void zs_break_ctl(struct uart_port *uport, int break_state) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); if (break_state == -1) zport->regs[5] |= SND_BRK; else zport->regs[5] &= ~SND_BRK; write_zsreg(zport, R5, zport->regs[5]); spin_unlock_irqrestore(&scc->zlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki98100.00%1100.00%
Total98100.00%1100.00%

/* * Interrupt handling routines. */ #define Rx_BRK 0x0100 /* BREAK event software flag. */ #define Rx_SYS 0x0200 /* SysRq event software flag. */
static void zs_receive_chars(struct zs_port *zport) { struct uart_port *uport = &zport->port; struct zs_scc *scc = zport->scc; struct uart_icount *icount; unsigned int avail, status, ch, flag; int count; for (count = 16; count; count--) { spin_lock(&scc->zlock); avail = read_zsreg(zport, R0) & Rx_CH_AV; spin_unlock(&scc->zlock); if (!avail) break; spin_lock(&scc->zlock); status = read_zsreg(zport, R1) & (Rx_OVR | FRM_ERR | PAR_ERR); ch = read_zsdata(zport); spin_unlock(&scc->zlock); flag = TTY_NORMAL; icount = &uport->icount; icount->rx++; /* Handle the null char got when BREAK is removed. */ if (!ch) status |= zport->tty_break; if (unlikely(status & (Rx_OVR | FRM_ERR | PAR_ERR | Rx_SYS | Rx_BRK))) { zport->tty_break = 0; /* Reset the error indication. */ if (status & (Rx_OVR | FRM_ERR | PAR_ERR)) { spin_lock(&scc->zlock); write_zsreg(zport, R0, ERR_RES); spin_unlock(&scc->zlock); } if (status & (Rx_SYS | Rx_BRK)) { icount->brk++; /* SysRq discards the null char. */ if (status & Rx_SYS) continue; } else if (status & FRM_ERR) icount->frame++; else if (status & PAR_ERR) icount->parity++; if (status & Rx_OVR) icount->overrun++; status &= uport->read_status_mask; if (status & Rx_BRK) flag = TTY_BREAK; else if (status & FRM_ERR) flag = TTY_FRAME; else if (status & PAR_ERR) flag = TTY_PARITY; } if (uart_handle_sysrq_char(uport, ch)) continue; uart_insert_char(uport, status, Rx_OVR, ch, flag); } tty_flip_buffer_push(&uport->state->port); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki35799.17%125.00%
Jiri Slaby10.28%125.00%
Alan Cox10.28%125.00%
Takashi Iwai10.28%125.00%
Total360100.00%4100.00%


static void zs_raw_transmit_chars(struct zs_port *zport) { struct circ_buf *xmit = &zport->port.state->xmit; /* XON/XOFF chars. */ if (zport->port.x_char) { write_zsdata(zport, zport->port.x_char); zport->port.icount.tx++; zport->port.x_char = 0; return; } /* If nothing to do or stopped or hardware stopped. */ if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) { zs_raw_stop_tx(zport); return; } /* Send char. */ write_zsdata(zport, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); zport->port.icount.tx++; if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&zport->port); /* Are we are done? */ if (uart_circ_empty(xmit)) zs_raw_stop_tx(zport); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki16099.38%150.00%
Alan Cox10.62%150.00%
Total161100.00%2100.00%


static void zs_transmit_chars(struct zs_port *zport) { struct zs_scc *scc = zport->scc; spin_lock(&scc->zlock); zs_raw_transmit_chars(zport); spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki41100.00%1100.00%
Total41100.00%1100.00%


static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) { struct uart_port *uport = &zport->port; struct zs_scc *scc = zport->scc; unsigned int delta; u8 status, brk; spin_lock(&scc->zlock); /* Get status from Read Register 0. */ status = read_zsreg(zport, R0); if (zport->regs[15] & BRKIE) { brk = status & BRK_ABRT; if (brk && !zport->brk) { spin_unlock(&scc->zlock); if (uart_handle_break(uport)) zport->tty_break = Rx_SYS; else zport->tty_break = Rx_BRK; spin_lock(&scc->zlock); } zport->brk = brk; } if (zport != zport_a) { delta = zs_raw_xor_mctrl(zport); spin_unlock(&scc->zlock); if (delta & TIOCM_CTS) uart_handle_cts_change(uport, zport->mctrl & TIOCM_CTS); if (delta & TIOCM_CAR) uart_handle_dcd_change(uport, zport->mctrl & TIOCM_CAR); if (delta & TIOCM_RNG) uport->icount.dsr++; if (delta & TIOCM_DSR) uport->icount.rng++; if (delta) wake_up_interruptible(&uport->state->port.delta_msr_wait); spin_lock(&scc->zlock); } /* Clear the status condition... */ write_zsreg(zport, R0, RES_EXT_INT); spin_unlock(&scc->zlock); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki25698.84%133.33%
Alan Cox31.16%266.67%
Total259100.00%3100.00%

/* * This is the Z85C30 driver's generic interrupt routine. */
static irqreturn_t zs_interrupt(int irq, void *dev_id) { struct zs_scc *scc = dev_id; struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; struct zs_port *zport_b = &scc->zport[ZS_CHAN_B]; irqreturn_t status = IRQ_NONE; u8 zs_intreg; int count; /* * NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A * channel and is only valid when read from channel A. * Yes... broken hardware... */ for (count = 16; count; count--) { spin_lock(&scc->zlock); zs_intreg = read_zsreg(zport_a, R3); spin_unlock(&scc->zlock); if (!zs_intreg) break; /* * We do not like losing characters, so we prioritise * interrupt sources a little bit differently than * the SCC would, was it allowed to. */ if (zs_intreg & CHBRxIP) zs_receive_chars(zport_b); if (zs_intreg & CHARxIP) zs_receive_chars(zport_a); if (zs_intreg & CHBEXT) zs_status_handle(zport_b, zport_a); if (zs_intreg & CHAEXT) zs_status_handle(zport_a, zport_a); if (zs_intreg & CHBTxIP) zs_transmit_chars(zport_b); if (zs_intreg & CHATxIP) zs_transmit_chars(zport_a); status = IRQ_HANDLED; } return status; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki180100.00%1100.00%
Total180100.00%1100.00%

/* * Finally, routines used to initialize the serial port. */
static int zs_startup(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; unsigned long flags; int irq_guard; int ret; irq_guard = atomic_add_return(1, &scc->irq_guard); if (irq_guard == 1) { ret = request_irq(zport->port.irq, zs_interrupt, IRQF_SHARED, "scc", scc); if (ret) { atomic_add(-1, &scc->irq_guard); printk(KERN_ERR "zs: can't get irq %d\n", zport->port.irq); return ret; } } spin_lock_irqsave(&scc->zlock, flags); /* Clear the receive FIFO. */ zs_receive_drain(zport); /* Clear the interrupt registers. */ write_zsreg(zport, R0, ERR_RES); write_zsreg(zport, R0, RES_Tx_P); /* But Ext only if not being handled already. */ if (!(zport->regs[1] & EXT_INT_ENAB)) write_zsreg(zport, R0, RES_EXT_INT); /* Finally, enable sequencing and interrupts. */ zport->regs[1] &= ~RxINT_MASK; zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB; zport->regs[3] |= RxENABLE; zport->regs[15] |= BRKIE; write_zsreg(zport, R1, zport->regs[1]); write_zsreg(zport, R3, zport->regs[3]); write_zsreg(zport, R5, zport->regs[5]); write_zsreg(zport, R15, zport->regs[15]); /* Record the current state of RR0. */ zport->mctrl = zs_raw_get_mctrl(zport); zport->brk = read_zsreg(zport, R0) & BRK_ABRT; zport->tx_stopped = 1; spin_unlock_irqrestore(&scc->zlock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki310100.00%1100.00%
Total310100.00%1100.00%


static void zs_shutdown(struct uart_port *uport) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; unsigned long flags; int irq_guard; spin_lock_irqsave(&scc->zlock, flags); zport->regs[3] &= ~RxENABLE; write_zsreg(zport, R5, zport->regs[5]); write_zsreg(zport, R3, zport->regs[3]); spin_unlock_irqrestore(&scc->zlock, flags); irq_guard = atomic_add_return(-1, &scc->irq_guard); if (!irq_guard) free_irq(zport->port.irq, scc); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki124100.00%1100.00%
Total124100.00%1100.00%


static void zs_reset(struct zs_port *zport) { struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); if (!scc->initialised) { /* Reset the pointer first, just in case... */ read_zsreg(zport, R0); /* And let the current transmission finish. */ zs_line_drain(zport, irq); write_zsreg(zport, R9, FHWRES); udelay(10); write_zsreg(zport, R9, 0); scc->initialised = 1; } load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki120100.00%1100.00%
Total120100.00%1100.00%


static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, struct ktermios *old_termios) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; int irq; unsigned int baud, brg; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); /* Byte size. */ zport->regs[3] &= ~RxNBITS_MASK; zport->regs[5] &= ~TxNBITS_MASK; switch (termios->c_cflag & CSIZE) { case CS5: zport->regs[3] |= Rx5; zport->regs[5] |= Tx5; break; case CS6: zport->regs[3] |= Rx6; zport->regs[5] |= Tx6; break; case CS7: zport->regs[3] |= Rx7; zport->regs[5] |= Tx7; break; case CS8: default: zport->regs[3] |= Rx8; zport->regs[5] |= Tx8; break; } /* Parity and stop bits. */ zport->regs[4] &= ~(XCLK_MASK | SB_MASK | PAR_ENA | PAR_EVEN); if (termios->c_cflag & CSTOPB) zport->regs[4] |= SB2; else zport->regs[4] |= SB1; if (termios->c_cflag & PARENB) zport->regs[4] |= PAR_ENA; if (!(termios->c_cflag & PARODD)) zport->regs[4] |= PAR_EVEN; switch (zport->clk_mode) { case 64: zport->regs[4] |= X64CLK; break; case 32: zport->regs[4] |= X32CLK; break; case 16: zport->regs[4] |= X16CLK; break; case 1: zport->regs[4] |= X1CLK; break; default: BUG(); } baud = uart_get_baud_rate(uport, termios, old_termios, 0, uport->uartclk / zport->clk_mode / 4); brg = ZS_BPS_TO_BRG(baud, uport->uartclk / zport->clk_mode); zport->regs[12] = brg & 0xff; zport->regs[13] = (brg >> 8) & 0xff; uart_update_timeout(uport, termios->c_cflag, baud); uport->read_status_mask = Rx_OVR; if (termios->c_iflag & INPCK) uport->read_status_mask |= FRM_ERR | PAR_ERR; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) uport->read_status_mask |= Rx_BRK; uport->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) uport->ignore_status_mask |= FRM_ERR | PAR_ERR; if (termios->c_iflag & IGNBRK) { uport->ignore_status_mask |= Rx_BRK; if (termios->c_iflag & IGNPAR) uport->ignore_status_mask |= Rx_OVR; } if (termios->c_cflag & CREAD) zport->regs[3] |= RxENABLE; else zport->regs[3] &= ~RxENABLE; if (zport != zport_a) { if (!(termios->c_cflag & CLOCAL)) { zport->regs[15] |= DCDIE; } else zport->regs[15] &= ~DCDIE; if (termios->c_cflag & CRTSCTS) { zport->regs[15] |= CTSIE; } else zport->regs[15] &= ~CTSIE; zs_raw_xor_mctrl(zport); } /* Load up the new values. */ load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki64499.69%150.00%
Peter Hurley20.31%150.00%
Total646100.00%2100.00%

/* * Hack alert! * Required solely so that the initial PROM-based console * works undisturbed in parallel with this one. */
static void zs_pm(struct uart_port *uport, unsigned int state, unsigned int oldstate) { struct zs_port *zport = to_zport(uport); if (state < 3) zport->regs[5] |= TxENAB; else zport->regs[5] &= ~TxENAB; write_zsreg(zport, R5, zport->regs[5]); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki69100.00%1100.00%
Total69100.00%1100.00%


static const char *zs_type(struct uart_port *uport) { return "Z85C30 SCC"; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki16100.00%1100.00%
Total16100.00%1100.00%


static void zs_release_port(struct uart_port *uport) { iounmap(uport->membase); uport->membase = 0; release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki33100.00%1100.00%
Total33100.00%1100.00%


static int zs_map_port(struct uart_port *uport) { if (!uport->membase) uport->membase = ioremap_nocache(uport->mapbase, ZS_CHAN_IO_SIZE); if (!uport->membase) { printk(KERN_ERR "zs: Cannot map MMIO\n"); return -ENOMEM; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki53100.00%1100.00%
Total53100.00%1100.00%


static int zs_request_port(struct uart_port *uport) { int ret; if (!request_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE, "scc")) { printk(KERN_ERR "zs: Unable to reserve MMIO resource\n"); return -EBUSY; } ret = zs_map_port(uport); if (ret) { release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki68100.00%1100.00%
Total68100.00%1100.00%


static void zs_config_port(struct uart_port *uport, int flags) { struct zs_port *zport = to_zport(uport); if (flags & UART_CONFIG_TYPE) { if (zs_request_port(uport)) return; uport->type = PORT_ZS; zs_reset(zport); } }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki51100.00%1100.00%
Total51100.00%1100.00%


static int zs_verify_port(struct uart_port *uport, struct serial_struct *ser) { struct zs_port *zport = to_zport(uport); int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_ZS) ret = -EINVAL; if (ser->irq != uport->irq) ret = -EINVAL; if (ser->baud_base != uport->uartclk / zport->clk_mode / 4) ret = -EINVAL; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki89100.00%1100.00%
Total89100.00%1100.00%

static const struct uart_ops zs_ops = { .tx_empty = zs_tx_empty, .set_mctrl = zs_set_mctrl, .get_mctrl = zs_get_mctrl, .stop_tx = zs_stop_tx, .start_tx = zs_start_tx, .stop_rx = zs_stop_rx, .enable_ms = zs_enable_ms, .break_ctl = zs_break_ctl, .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, .config_port = zs_config_port, .verify_port = zs_verify_port, }; /* * Initialize Z85C30 port structures. */
static int __init zs_probe_sccs(void) { static int probed; struct zs_parms zs_parms; int chip, side, irq; int n_chips = 0; int i; if (probed) return 0; irq = dec_interrupt[DEC_IRQ_SCC0]; if (irq >= 0) { zs_parms.scc[n_chips] = IOASIC_SCC0; zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; n_chips++; } irq = dec_interrupt[DEC_IRQ_SCC1]; if (irq >= 0) { zs_parms.scc[n_chips] = IOASIC_SCC1; zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; n_chips++; } if (!n_chips) return -ENXIO; probed = 1; for (chip = 0; chip < n_chips; chip++) { spin_lock_init(&zs_sccs[chip].zlock); for (side = 0; side < ZS_NUM_CHAN; side++) { struct zs_port *zport = &zs_sccs[chip].zport[side]; struct uart_port *uport = &zport->port; zport->scc = &zs_sccs[chip]; zport->clk_mode = 16; uport->irq = zs_parms.irq[chip]; uport->uartclk = ZS_CLOCK; uport->fifosize = 1; uport->iotype = UPIO_MEM; uport->flags = UPF_BOOT_AUTOCONF; uport->ops = &zs_ops; uport->line = chip * ZS_NUM_CHAN + side; uport->mapbase = dec_kn_slot_base + zs_parms.scc[chip] + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; for (i = 0; i < ZS_NUM_REGS; i++) zport->regs[i] = zs_init_regs[i]; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki314100.00%1100.00%
Total314100.00%1100.00%

#ifdef CONFIG_SERIAL_ZS_CONSOLE
static void zs_console_putchar(struct uart_port *uport, int ch) { struct zs_port *zport = to_zport(uport); struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); if (zs_transmit_drain(zport, irq)) write_zsdata(zport, ch); spin_unlock_irqrestore(&scc->zlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki84100.00%1100.00%
Total84100.00%1100.00%

/* * Print a string to the serial port trying not to disturb * any possible real use of the port... */
static void zs_console_write(struct console *co, const char *s, unsigned int count) { int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; struct zs_port *zport = &zs_sccs[chip].zport[side]; struct zs_scc *scc = zport->scc; unsigned long flags; u8 txint, txenb; int irq; /* Disable transmit interrupts and enable the transmitter. */ spin_lock_irqsave(&scc->zlock, flags); txint = zport->regs[1]; txenb = zport->regs[5]; if (txint & TxINT_ENAB) { zport->regs[1] = txint & ~TxINT_ENAB; write_zsreg(zport, R1, zport->regs[1]); } if (!(txenb & TxENAB)) { zport->regs[5] = txenb | TxENAB; write_zsreg(zport, R5, zport->regs[5]); } spin_unlock_irqrestore(&scc->zlock, flags); uart_console_write(&zport->port, s, count, zs_console_putchar); /* Restore transmit interrupts and the transmitter enable. */ spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); zs_line_drain(zport, irq); if (!(txenb & TxENAB)) { zport->regs[5] &= ~TxENAB; write_zsreg(zport, R5, zport->regs[5]); } if (txint & TxINT_ENAB) { zport->regs[1] |= TxINT_ENAB; write_zsreg(zport, R1, zport->regs[1]); /* Resume any transmission as the TxIP bit won't be set. */ if (!zport->tx_stopped) zs_raw_transmit_chars(zport); } spin_unlock_irqrestore(&scc->zlock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki312100.00%2100.00%
Total312100.00%2100.00%

/* * Setup serial console baud/bits/parity. We do two things here: * - construct a cflag setting for the first uart_open() * - initialise the serial port * Return non-zero if we didn't find a serial port. */
static int __init zs_console_setup(struct console *co, char *options) { int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; struct zs_port *zport = &zs_sccs[chip].zport[side]; struct uart_port *uport = &zport->port; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; int ret; ret = zs_map_port(uport); if (ret) return ret; zs_reset(zport); zs_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(uport, co, baud, parity, bits, flow); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki148100.00%2100.00%
Total148100.00%2100.00%

static struct uart_driver zs_reg; static struct console zs_console = { .name = "ttyS", .write = zs_console_write, .device = uart_console_device, .setup = zs_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &zs_reg, }; /* * Register console. */
static int __init zs_serial_console_init(void) { int ret; ret = zs_probe_sccs(); if (ret) return ret; register_console(&zs_console); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki33100.00%1100.00%
Total33100.00%1100.00%

console_initcall(zs_serial_console_init); #define SERIAL_ZS_CONSOLE &zs_console #else #define SERIAL_ZS_CONSOLE NULL #endif /* CONFIG_SERIAL_ZS_CONSOLE */ static struct uart_driver zs_reg = { .owner = THIS_MODULE, .driver_name = "serial", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, .nr = ZS_NUM_SCCS * ZS_NUM_CHAN, .cons = SERIAL_ZS_CONSOLE, }; /* zs_init inits the driver. */
static int __init zs_init(void) { int i, ret; pr_info("%s%s\n", zs_name, zs_version); /* Find out how many Z85C30 SCCs we have. */ ret = zs_probe_sccs(); if (ret) return ret; ret = uart_register_driver(&zs_reg); if (ret) return ret; for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; struct uart_port *uport = &zport->port; if (zport->scc) uart_add_one_port(&zs_reg, uport); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki123100.00%1100.00%
Total123100.00%1100.00%


static void __exit zs_exit(void) { int i; for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; struct uart_port *uport = &zport->port; if (zport->scc) uart_remove_one_port(&zs_reg, uport); } uart_unregister_driver(&zs_reg); }

Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki89100.00%1100.00%
Total89100.00%1100.00%

module_init(zs_init); module_exit(zs_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Maciej W. Rozycki632999.68%323.08%
Alan Cox50.08%215.38%
Jiri Slaby40.06%215.38%
Paul Gortmaker30.05%17.69%
Roel Kluin30.05%17.69%
Peter Hurley20.03%17.69%
Arun Sharma10.02%17.69%
Takashi Iwai10.02%17.69%
Bhumika Goyal10.02%17.69%
Total6349100.00%13100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.