sunsab.c

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

C
1,166
字号
/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be) * Copyright (C) 2002  David S. Miller (davem@redhat.com) * * Rewrote buffer handling to use CIRC(Circular Buffer) macros. *   Maxim Krasnyanskiy <maxk@qualcomm.com> * * Fixed to use tty_get_baud_rate, and to allow for arbitrary baud * rates to be programmed into the UART.  Also eliminated a lot of * duplicated code in the console setup. *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * * Ported to new 2.5.x UART layer. *   David S. Miller <davem@redhat.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.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/circ_buf.h>#include <linux/serial.h>#include <linux/sysrq.h>#include <linux/console.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/ebus.h>#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include "suncore.h"#include "sunsab.h"struct uart_sunsab_port {	struct uart_port		port;		/* Generic UART port	*/	union sab82532_async_regs	*regs;		/* Chip registers	*/	unsigned long			irqflags;	/* IRQ state flags	*/	int				dsr;		/* Current DSR state	*/	unsigned int			cec_timeout;	/* Chip poll timeout... */	unsigned int			tec_timeout;	/* likewise		*/	unsigned char			interrupt_mask0;/* ISR0 masking		*/	unsigned char			interrupt_mask1;/* ISR1 masking		*/	unsigned char			pvr_dtr_bit;	/* Which PVR bit is DTR */	unsigned char			pvr_dsr_bit;	/* Which PVR bit is DSR */	int				type;		/* SAB82532 version	*/};/* * This assumes you have a 29.4912 MHz clock for your UART. */#define SAB_BASE_BAUD ( 29491200 / 16 )static char *sab82532_version[16] = {	"V1.0", "V2.0", "V3.2", "V(0x03)",	"V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)",	"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",	"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"};#define SAB82532_MAX_TEC_TIMEOUT 200000	/* 1 character time (at 50 baud) */#define SAB82532_MAX_CEC_TIMEOUT  50000	/* 2.5 TX CLKs (at 50 baud) */#define SAB82532_RECV_FIFO_SIZE	32      /* Standard async fifo sizes */#define SAB82532_XMIT_FIFO_SIZE	32static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up){	int timeout = up->tec_timeout;	while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout)		udelay(1);}static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up){	int timeout = up->cec_timeout;	while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout)		udelay(1);}static struct tty_struct *receive_chars(struct uart_sunsab_port *up,	      union sab82532_irq_status *stat,	      struct pt_regs *regs){	struct tty_struct *tty = NULL;	unsigned char buf[32];	int saw_console_brk = 0;	int free_fifo = 0;	int count = 0;	int i;	if (up->port.info != NULL)		/* Unopened serial console */		tty = up->port.info->tty;	/* Read number of BYTES (Character + Status) available. */	if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {		count = SAB82532_RECV_FIFO_SIZE;		free_fifo++;	}	if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {		count = readb(&up->regs->r.rbcl) & (SAB82532_RECV_FIFO_SIZE - 1);		free_fifo++;	}	/* Issue a FIFO read command in case we where idle. */	if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {		sunsab_cec_wait(up);		writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);		return tty;	}	if (stat->sreg.isr0 & SAB82532_ISR0_RFO)		free_fifo++;	/* Read the FIFO. */	for (i = 0; i < count; i++)		buf[i] = readb(&up->regs->r.rfifo[i]);	/* Issue Receive Message Complete command. */	if (free_fifo) {		sunsab_cec_wait(up);		writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);	}	for (i = 0; i < count; i++) {		unsigned char ch = buf[i];		if (tty == NULL) {			uart_handle_sysrq_char(&up->port, ch, regs);			continue;		}		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {			tty->flip.work.func((void *)tty);			if (tty->flip.count >= TTY_FLIPBUF_SIZE)				return tty; // if TTY_DONT_FLIP is set		}		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = TTY_NORMAL;		up->port.icount.rx++;		if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |						SAB82532_ISR0_FERR |						SAB82532_ISR0_RFO)) ||		    unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) {			/*			 * For statistics only			 */			if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {				stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |						     SAB82532_ISR0_FERR);				up->port.icount.brk++;				if (up->port.line == up->port.cons->index)					saw_console_brk = 1;				/*				 * We do the SysRQ and SAK checking				 * here because otherwise the break				 * may get masked by ignore_status_mask				 * or read_status_mask.				 */				if (uart_handle_break(&up->port))					continue;			} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)				up->port.icount.parity++;			else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)				up->port.icount.frame++;			if (stat->sreg.isr0 & SAB82532_ISR0_RFO)				up->port.icount.overrun++;			/*			 * Mask off conditions which should be ingored.			 */			stat->sreg.isr0 &= (up->port.read_status_mask & 0xff);			stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);			if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {				*tty->flip.flag_buf_ptr = TTY_BREAK;			} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)				*tty->flip.flag_buf_ptr = TTY_PARITY;			else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)				*tty->flip.flag_buf_ptr = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch, regs))			continue;		if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&		    (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0){			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}		if ((stat->sreg.isr0 & SAB82532_ISR0_RFO) &&		    tty->flip.count < TTY_FLIPBUF_SIZE) {			/*			 * Overrun is special, since it's reported			 * immediately, and doesn't affect the current			 * character.			 */			*tty->flip.flag_buf_ptr = TTY_OVERRUN;			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}	}	if (saw_console_brk)		sun_do_break();	return tty;}static void sunsab_stop_tx(struct uart_port *, unsigned int);static void transmit_chars(struct uart_sunsab_port *up,			   union sab82532_irq_status *stat){	struct circ_buf *xmit = &up->port.info->xmit;	int i;	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {		up->interrupt_mask1 |= SAB82532_IMR1_ALLS;		writeb(up->interrupt_mask1, &up->regs->w.imr1);		set_bit(SAB82532_ALLS, &up->irqflags);	}#if 0 /* bde@nwlink.com says this check causes problems */	if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))		return;#endif	if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW))		return;	set_bit(SAB82532_XPR, &up->irqflags);	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {		up->interrupt_mask1 |= SAB82532_IMR1_XPR;		writeb(up->interrupt_mask1, &up->regs->w.imr1);		uart_write_wakeup(&up->port);		return;	}	up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);	writeb(up->interrupt_mask1, &up->regs->w.imr1);	clear_bit(SAB82532_ALLS, &up->irqflags);	/* Stuff 32 bytes into Transmit FIFO. */	clear_bit(SAB82532_XPR, &up->irqflags);	for (i = 0; i < up->port.fifosize; i++) {		writeb(xmit->buf[xmit->tail],		       &up->regs->w.xfifo[i]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		up->port.icount.tx++;		if (uart_circ_empty(xmit))			break;	}	/* Issue a Transmit Frame command. */	sunsab_cec_wait(up);	writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(&up->port);	if (uart_circ_empty(xmit))		sunsab_stop_tx(&up->port, 0);}static void check_status(struct uart_sunsab_port *up,			 union sab82532_irq_status *stat){	if (stat->sreg.isr0 & SAB82532_ISR0_CDSC)		uart_handle_dcd_change(&up->port,				       !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD));	if (stat->sreg.isr1 & SAB82532_ISR1_CSC)		uart_handle_cts_change(&up->port,				       (readb(&up->regs->r.star) & SAB82532_STAR_CTS));	if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) {		up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1;		up->port.icount.dsr++;	}	wake_up_interruptible(&up->port.info->delta_msr_wait);}static irqreturn_t sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct uart_sunsab_port *up = dev_id;	struct tty_struct *tty;	union sab82532_irq_status status;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	status.stat = 0;	if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)		status.sreg.isr0 = readb(&up->regs->r.isr0);	if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)		status.sreg.isr1 = readb(&up->regs->r.isr1);	tty = NULL;	if (status.stat) {		if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |					SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))			tty = receive_chars(up, &status, regs);		if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||		    (status.sreg.isr1 & SAB82532_ISR1_CSC))			check_status(up, &status);		if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))			transmit_chars(up, &status);	}	spin_unlock(&up->port.lock);	if (tty)		tty_flip_buffer_push(tty);	up++;	spin_lock(&up->port.lock);	status.stat = 0;	if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0)		status.sreg.isr0 = readb(&up->regs->r.isr0);	if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1)		status.sreg.isr1 = readb(&up->regs->r.isr1);	tty = NULL;	if (status.stat) {		if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |					SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))			tty = receive_chars(up, &status, regs);		if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||		    (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))			check_status(up, &status);		if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))			transmit_chars(up, &status);	}	spin_unlock_irqrestore(&up->port.lock, flags);	if (tty)		tty_flip_buffer_push(tty);	return IRQ_HANDLED;}/* port->lock is not held.  */static unsigned int sunsab_tx_empty(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	int ret;	/* Do not need a lock for a state test like this.  */	if (test_bit(SAB82532_ALLS, &up->irqflags))		ret = TIOCSER_TEMT;	else		ret = 0;	return ret;}/* port->lock held by caller.  */static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	if (mctrl & TIOCM_RTS) {		writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,		       &up->regs->rw.mode);		writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,		       &up->regs->rw.mode);	} else {		writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,		       &up->regs->rw.mode);		writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,		       &up->regs->rw.mode);	}	if (mctrl & TIOCM_DTR) {		writeb(readb(&up->regs->rw.pvr) & ~(up->pvr_dtr_bit), &up->regs->rw.pvr);	} else {		writeb(readb(&up->regs->rw.pvr) | up->pvr_dtr_bit, &up->regs->rw.pvr);	}}/* port->lock is not held.  */static unsigned int sunsab_get_mctrl(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	unsigned char val;	unsigned int result;	result = 0;	spin_lock_irqsave(&up->port.lock, flags);	val = readb(&up->regs->r.pvr);	result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR;	val = readb(&up->regs->r.vstr);	result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR;	val = readb(&up->regs->r.star);	result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0;	spin_unlock_irqrestore(&up->port.lock, flags);	return result;}/* port->lock held by caller.  */static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	up->interrupt_mask1 |= SAB82532_IMR1_XPR;	writeb(up->interrupt_mask1, &up->regs->w.imr1);}/* port->lock held by caller.  */static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	struct circ_buf *xmit = &up->port.info->xmit;	int i;	up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);	writeb(up->interrupt_mask1, &up->regs->w.imr1);		if (!test_bit(SAB82532_XPR, &up->irqflags))		return;	clear_bit(SAB82532_ALLS, &up->irqflags);	clear_bit(SAB82532_XPR, &up->irqflags);	for (i = 0; i < up->port.fifosize; i++) {		writeb(xmit->buf[xmit->tail],		       &up->regs->w.xfifo[i]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		up->port.icount.tx++;		if (uart_circ_empty(xmit))			break;	}	/* Issue a Transmit Frame command.  */	sunsab_cec_wait(up);	writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);}/* port->lock is not held.  */static void sunsab_send_xchar(struct uart_port *port, char ch){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	sunsab_tec_wait(up);	writeb(ch, &up->regs->w.tic);	spin_unlock_irqrestore(&up->port.lock, flags);}/* port->lock held by caller.  */static void sunsab_stop_rx(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	up->interrupt_mask0 |= SAB82532_ISR0_TCD;	writeb(up->interrupt_mask1, &up->regs->w.imr0);}/* port->lock held by caller.  */static void sunsab_enable_ms(struct uart_port *port){	/* For now we always receive these interrupts.  */}/* port->lock is not held.  */static void sunsab_break_ctl(struct uart_port *port, int break_state){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	unsigned char val;	spin_lock_irqsave(&up->port.lock, flags);	val = readb(&up->regs->rw.dafo);	if (break_state)		val |= SAB82532_DAFO_XBRK;	else		val &= ~SAB82532_DAFO_XBRK;	writeb(val, &up->regs->rw.dafo);	spin_unlock_irqrestore(&up->port.lock, flags);}/* port->lock is not held.  */static int sunsab_startup(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned long flags;	unsigned char tmp;	spin_lock_irqsave(&up->port.lock, flags);	/*	 * Wait for any commands or immediate characters	 */	sunsab_cec_wait(up);	sunsab_tec_wait(up);	/*	 * Clear the FIFO buffers.	 */	writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr);	sunsab_cec_wait(up);	writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr);	/*	 * Clear the interrupt registers.	 */	(void) readb(&up->regs->r.isr0);	(void) readb(&up->regs->r.isr1);	/*	 * Now, initialize the UART 	 */	writeb(0, &up->regs->w.ccr0);				/* power-down */	writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |	       SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0);	writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1);	writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |	       SAB82532_CCR2_TOE, &up->regs->w.ccr2);	writeb(0, &up->regs->w.ccr3);	writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);	writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |	       SAB82532_MODE_RAC, &up->regs->w.mode);	writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc);		tmp = readb(&up->regs->rw.ccr0);	tmp |= SAB82532_CCR0_PU;	/* power-up */	writeb(tmp, &up->regs->rw.ccr0);	/*	 * Finally, enable interrupts	 */	up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |			       SAB82532_IMR0_PLLA);	writeb(up->interrupt_mask0, &up->regs->w.imr0);	up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |			       SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |			       SAB82532_IMR1_CSC | SAB82532_IMR1_XON |			       SAB82532_IMR1_XPR);	writeb(up->interrupt_mask1, &up->regs->w.imr1);	set_bit(SAB82532_ALLS, &up->irqflags);	set_bit(SAB82532_XPR, &up->irqflags);

⌨️ 快捷键说明

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