dz.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 828 行 · 第 1/2 页

C
828
字号
/* * dz.c: Serial port driver for DECStations equiped  *       with the DZ chipset. * * Copyright (C) 1998 Olivier A. D. Lebaillif  *              * Email: olivier.lebaillif@ifrsys.com * * [31-AUG-98] triemer * Changed IRQ to use Harald's dec internals interrupts.h * removed base_addr code - moving address assignment to setup.c * Changed name of dz_init to rs_init to be consistent with tc code * [13-NOV-98] triemer fixed code to receive characters *    after patches by harald to irq code.   * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout *            field from "current" - somewhere between 2.1.121 and 2.1.131 Qua Jun 27 15:02:26 BRT 2001 * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups *   * Parts (C) 1999 David Airlie, airlied@linux.ie  * [07-SEP-99] Bugfixes  * * [06-Jan-2002] Russell King <rmk@arm.linux.org.uk> * Converted to new serial core */#undef DEBUG_DZ#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/console.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <asm/bootinfo.h>#include <asm/dec/interrupts.h>#include <asm/dec/kn01.h>#include <asm/dec/kn02.h>#include <asm/dec/machtype.h>#include <asm/dec/prom.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/uaccess.h>#define CONSOLE_LINE (3)	/* for definition of struct console */#include "dz.h"#define DZ_INTR_DEBUG 1static char *dz_name = "DECstation DZ serial driver version ";static char *dz_version = "1.02";struct dz_port {	struct uart_port	port;	unsigned int		cflag;};static struct dz_port dz_ports[DZ_NB_PORT];#ifdef DEBUG_DZ/* * debugging code to send out chars via prom  */static void debug_console(const char *s, int count){	unsigned i;	for (i = 0; i < count; i++) {		if (*s == 10)			prom_printf("%c", 13);		prom_printf("%c", *s++);	}}#endif/* * ------------------------------------------------------------ * dz_in () and dz_out () * * These routines are used to access the registers of the DZ  * chip, hiding relocation differences between implementation. * ------------------------------------------------------------ */static inline unsigned short dz_in(struct dz_port *dport, unsigned offset){	volatile unsigned short *addr =		(volatile unsigned short *) (dport->port.membase + offset);	return *addr;}static inline void dz_out(struct dz_port *dport, unsigned offset,                          unsigned short value){	volatile unsigned short *addr =		(volatile unsigned short *) (dport->port.membase + offset);	*addr = value;}/* * ------------------------------------------------------------ * rs_stop () and rs_start () * * These routines are called before setting or resetting  * tty->stopped. They enable or disable transmitter interrupts,  * as necessary. * ------------------------------------------------------------ */static void dz_stop_tx(struct uart_port *uport, unsigned int tty_stop){	struct dz_port *dport = (struct dz_port *)uport;	unsigned short tmp, mask = 1 << dport->port.line;	unsigned long flags;	spin_lock_irqsave(&dport->port.lock, flags);	tmp = dz_in(dport, DZ_TCR);	/* read the TX flag */	tmp &= ~mask;			/* clear the TX flag */	dz_out(dport, DZ_TCR, tmp);	spin_unlock_irqrestore(&dport->port.lock, flags);}static void dz_start_tx(struct uart_port *uport, unsigned int tty_start){	struct dz_port *dport = (struct dz_port *)uport;	unsigned short tmp, mask = 1 << dport->port.line;	unsigned long flags;	spin_lock_irqsave(&dport->port.lock, flags);	tmp = dz_in(dport, DZ_TCR);	/* read the TX flag */	tmp |= mask;			/* set the TX flag */	dz_out(dport, DZ_TCR, tmp);	spin_unlock_irqrestore(&dport->port.lock, flags);}static void dz_stop_rx(struct uart_port *uport){	struct dz_port *dport = (struct dz_port *)uport;	unsigned long flags;	spin_lock_irqsave(&dport->port.lock, flags);	dport->cflag &= ~DZ_CREAD;	dz_out(dport, DZ_LPR, dport->cflag);	spin_unlock_irqrestore(&dport->port.lock, flags);}static void dz_enable_ms(struct uart_port *port){	/* nothing to do */}/* * ------------------------------------------------------------ * Here starts the interrupt handling routines.  All of the  * following subroutines are declared as inline and are folded  * into dz_interrupt.  They were separated out for readability's  * sake.  * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible.  After you are done making modifications, it is not a bad * idea to do: *  *	make drivers/serial/dz.s * * and look at the resulting assemble code in dz.s. * * ------------------------------------------------------------ *//* * ------------------------------------------------------------ * receive_char () * * This routine deals with inputs from any lines. * ------------------------------------------------------------ */static inline void dz_receive_chars(struct dz_port *dport){	struct tty_struct *tty = NULL;	struct uart_icount *icount;	int ignore = 0;	unsigned short status, tmp;	unsigned char ch;	/* this code is going to be a problem...	   the call to tty_flip_buffer is going to need	   to be rethought...	 */	do {		status = dz_in(dport, DZ_RBUF);		/* punt so we don't get duplicate characters */		if (!(status & DZ_DVAL))			goto ignore_char;		ch = UCHAR(status);	/* grab the char */#if 0		if (info->is_console) {			if (ch == 0)				return;		/* it's a break ... */		}#endif		tty = dport->port.info->tty;/* now tty points to the proper dev */		icount = &dport->port.icount;		if (!tty)			break;		if (tty->flip.count >= TTY_FLIPBUF_SIZE)			break;		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = 0;		icount->rx++;		/* keep track of the statistics */		if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) {			if (status & DZ_PERR)	/* parity error */				icount->parity++;			else if (status & DZ_FERR)	/* frame error */				icount->frame++;			if (status & DZ_OERR)	/* overrun error */				icount->overrun++;			/*  check to see if we should ignore the character			   and mask off conditions that should be ignored			 */			if (status & dport->port.ignore_status_mask) {				if (++ignore > 100)					break;				goto ignore_char;			}			/* mask off the error conditions we want to ignore */			tmp = status & dport->port.read_status_mask;			if (tmp & DZ_PERR) {				*tty->flip.flag_buf_ptr = TTY_PARITY;#ifdef DEBUG_DZ				debug_console("PERR\n", 5);#endif			} else if (tmp & DZ_FERR) {				*tty->flip.flag_buf_ptr = TTY_FRAME;#ifdef DEBUG_DZ				debug_console("FERR\n", 5);#endif			}			if (tmp & DZ_OERR) {#ifdef DEBUG_DZ				debug_console("OERR\n", 5);#endif				if (tty->flip.count < TTY_FLIPBUF_SIZE) {					tty->flip.count++;					tty->flip.flag_buf_ptr++;					tty->flip.char_buf_ptr++;					*tty->flip.flag_buf_ptr = TTY_OVERRUN;				}			}		}		tty->flip.flag_buf_ptr++;		tty->flip.char_buf_ptr++;		tty->flip.count++;	      ignore_char:	} while (status & DZ_DVAL);	if (tty)		tty_flip_buffer_push(tty);}/* * ------------------------------------------------------------ * transmit_char () * * This routine deals with outputs to any lines. * ------------------------------------------------------------ */static inline void dz_transmit_chars(struct dz_port *dport){	struct circ_buf *xmit = &dport->port.info->xmit;	unsigned char tmp;	if (dport->port.x_char) {	/* XON/XOFF chars */		dz_out(dport, DZ_TDR, dport->port.x_char);		dport->port.icount.tx++;		dport->port.x_char = 0;		return;	}	/* if nothing to do or stopped or hardware stopped */	if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) {		dz_stop_tx(&dport->port, 0);		return;	}	/*	 * if something to do ... (rember the dz has no output fifo so we go	 * one char at a time :-<	 */	tmp = xmit->buf[xmit->tail];	xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1);	dz_out(dport, DZ_TDR, tmp);	dport->port.icount.tx++;	if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)		uart_write_wakeup(&dport->port);	/* Are we done */	if (uart_circ_empty(xmit))		dz_stop_tx(&dport->port, 0);}/* * ------------------------------------------------------------ * check_modem_status () * * Only valid for the MODEM line duh ! * ------------------------------------------------------------ */static inline void check_modem_status(struct dz_port *dport){	unsigned short status;	/* if not ne modem line just return */	if (dport->port.line != DZ_MODEM)		return;	status = dz_in(dport, DZ_MSR);	/* it's easy, since DSR2 is the only bit in the register */	if (status)		dport->port.icount.dsr++;}/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */static irqreturn_t dz_interrupt(int irq, void *dev, struct pt_regs *regs){	struct dz_port *dport;	unsigned short status;	/* get the reason why we just got an irq */	status = dz_in((struct dz_port *)dev, DZ_CSR);	dport = &dz_ports[LINE(status)];	if (status & DZ_RDONE)		dz_receive_chars(dport);	if (status & DZ_TRDY)		dz_transmit_chars(dport);	/* FIXME: what about check modem status??? --rmk */	return IRQ_HANDLED;}/* * ------------------------------------------------------------------- * Here ends the DZ interrupt routines. * ------------------------------------------------------------------- */static unsigned int dz_get_mctrl(struct uart_port *uport){	struct dz_port *dport = (struct dz_port *)uport;	unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;	if (dport->port.line == DZ_MODEM) {		/*		 * CHECKME: This is a guess from the other code... --rmk		 */		if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR)			mctrl &= ~TIOCM_DSR;	}	return mctrl;}static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl){	struct dz_port *dport = (struct dz_port *)uport;	unsigned short tmp;	if (dport->port.line == DZ_MODEM) {		tmp = dz_in(dport, DZ_TCR);		if (mctrl & TIOCM_DTR)			tmp &= ~DZ_MODEM_DTR;		else			tmp |= DZ_MODEM_DTR;		dz_out(dport, DZ_TCR, tmp);	}}/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * -------------------------------------------------------------------  */static int dz_startup(struct uart_port *uport){

⌨️ 快捷键说明

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