sunsab.c

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

C
1,149
字号
/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be) * Copyright (C) 2002, 2006  David S. Miller (davem@davemloft.net) * * 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@davemloft.net> */#include <linux/module.h>#include <linux/kernel.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/prom.h>#include <asm/of_device.h>#if defined(CONFIG_SERIAL_SUNSAB_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	__iomem *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 */	unsigned int			gis_shift;	int				type;		/* SAB82532 version	*/	/* Setting configuration bits while the transmitter is active	 * can cause garbage characters to get emitted by the chip.	 * Therefore, we cache such writes here and do the real register	 * write the next time the transmitter becomes idle.	 */	unsigned int			cached_ebrg;	unsigned char			cached_mode;	unsigned char			cached_pvr;	unsigned char			cached_dafo;};/* * 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 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);	}	/* Count may be zero for BRK, so we check for it here */	if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) &&	    (up->port.line == up->port.cons->index))		saw_console_brk = 1;	for (i = 0; i < count; i++) {		unsigned char ch = buf[i], flag;		if (tty == NULL) {			uart_handle_sysrq_char(&up->port, ch);			continue;		}		flag = 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++;				/*				 * 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) {				flag = TTY_BREAK;			} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)				flag = TTY_PARITY;			else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)				flag = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch))			continue;		if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&		    (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0)			tty_insert_flip_char(tty, ch, flag);		if (stat->sreg.isr0 & SAB82532_ISR0_RFO)			tty_insert_flip_char(tty, 0, TTY_OVERRUN);	}	if (saw_console_brk)		sun_do_break();	return tty;}static void sunsab_stop_tx(struct uart_port *);static void sunsab_tx_idle(struct uart_sunsab_port *);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);	sunsab_tx_idle(up);	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {		up->interrupt_mask1 |= SAB82532_IMR1_XPR;		writeb(up->interrupt_mask1, &up->regs->w.imr1);		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);}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 uart_sunsab_port *up = dev_id;	struct tty_struct *tty;	union sab82532_irq_status status;	unsigned long flags;	unsigned char gis;	spin_lock_irqsave(&up->port.lock, flags);	status.stat = 0;	gis = readb(&up->regs->r.gis) >> up->gis_shift;	if (gis & 1)		status.sreg.isr0 = readb(&up->regs->r.isr0);	if (gis & 2)		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)) ||		    (status.sreg.isr1 & SAB82532_ISR1_BRK))			tty = receive_chars(up, &status);		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_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) {		up->cached_mode &= ~SAB82532_MODE_FRTS;		up->cached_mode |= SAB82532_MODE_RTS;	} else {		up->cached_mode |= (SAB82532_MODE_FRTS |				    SAB82532_MODE_RTS);	}	if (mctrl & TIOCM_DTR) {		up->cached_pvr &= ~(up->pvr_dtr_bit);	} else {		up->cached_pvr |= up->pvr_dtr_bit;	}	set_bit(SAB82532_REGS_PENDING, &up->irqflags);	if (test_bit(SAB82532_XPR, &up->irqflags))		sunsab_tx_idle(up);}/* port->lock is held by caller and interrupts are disabled.  */static unsigned int sunsab_get_mctrl(struct uart_port *port){	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;	unsigned char val;	unsigned int result;	result = 0;	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;	return result;}/* port->lock held by caller.  */static void sunsab_stop_tx(struct uart_port *port){	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_tx_idle(struct uart_sunsab_port *up){	if (test_bit(SAB82532_REGS_PENDING, &up->irqflags)) {		u8 tmp;		clear_bit(SAB82532_REGS_PENDING, &up->irqflags);		writeb(up->cached_mode, &up->regs->rw.mode);		writeb(up->cached_pvr, &up->regs->rw.pvr);		writeb(up->cached_dafo, &up->regs->w.dafo);		writeb(up->cached_ebrg & 0xff, &up->regs->w.bgr);		tmp = readb(&up->regs->rw.ccr2);		tmp &= ~0xc0;		tmp |= (up->cached_ebrg >> 2) & 0xc0;		writeb(tmp, &up->regs->rw.ccr2);	}}/* port->lock held by caller.  */static void sunsab_start_tx(struct uart_port *port){	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 = up->cached_dafo;	if (break_state)		val |= SAB82532_DAFO_XBRK;	else		val &= ~SAB82532_DAFO_XBRK;	up->cached_dafo = val;	set_bit(SAB82532_REGS_PENDING, &up->irqflags);	if (test_bit(SAB82532_XPR, &up->irqflags))		sunsab_tx_idle(up);	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;	int err = request_irq(up->port.irq, sunsab_interrupt,			      IRQF_SHARED, "sab", up);	if (err)		return err;	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);	up->cached_mode = (SAB82532_MODE_RTS | SAB82532_MODE_FCTS |			   SAB82532_MODE_RAC);	writeb(up->cached_mode, &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 + -
显示快捷键?