sunsu.c

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

C
1,741
字号
/* $Id: su.c,v 1.55 2002/01/08 16:00:16 davem Exp $ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be) * Copyright (C) 1998-1999  Pete Zaitcev   (zaitcev@yahoo.com) * * This is mainly a variation of 8250.c, credits go to authors mentioned * therein.  In fact this driver should be merged into the generic 8250.c * infrastructure perhaps using a 8250_sparc.c module. * * Fixed to use tty_get_baud_rate(). *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * * Converted to new 2.5.x UART layer. *   David S. Miller (davem@redhat.com), 2002-Jul-29 */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/spinlock.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>#ifdef CONFIG_SERIO#include <linux/serio.h>#endif#include <linux/serial_reg.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/oplib.h>#include <asm/ebus.h>#ifdef CONFIG_SPARC64#include <asm/isa.h>#endif#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include "suncore.h"/* We are on a NS PC87303 clocked with 24.0 MHz, which results * in a UART clock of 1.8462 MHz. */#define SU_BASE_BAUD	(1846200 / 16)enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" };/* * Here we define the default xmit fifo size used for each type of UART. */static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {	{ "unknown",	1,	0 },	{ "8250",	1,	0 },	{ "16450",	1,	0 },	{ "16550",	1,	0 },	{ "16550A",	16,	UART_CLEAR_FIFO | UART_USE_FIFO },	{ "Cirrus",	1, 	0 },	{ "ST16650",	1,	UART_CLEAR_FIFO | UART_STARTECH },	{ "ST16650V2",	32,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },	{ "TI16750",	64,	UART_CLEAR_FIFO | UART_USE_FIFO },	{ "Startech",	1,	0 },	{ "16C950/954",	128,	UART_CLEAR_FIFO | UART_USE_FIFO },	{ "ST16654",	64,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },	{ "XR16850",	128,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },	{ "RSA",	2048,	UART_CLEAR_FIFO | UART_USE_FIFO }};struct uart_sunsu_port {	struct uart_port	port;	unsigned char		acr;	unsigned char		ier;	unsigned short		rev;	unsigned char		lcr;	unsigned int		lsr_break_flag;	unsigned int		cflag;	/* Probing information.  */	enum su_type		su_type;	unsigned int		type_probed;	/* XXX Stupid */	int			port_node;	unsigned int		irq;#ifdef CONFIG_SERIO	struct serio		*serio;	int			serio_open;#endif};#define _INLINE_static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset){	offset <<= up->port.regshift;	switch (up->port.iotype) {	case SERIAL_IO_HUB6:		outb(up->port.hub6 - 1 + offset, up->port.iobase);		return inb(up->port.iobase + 1);	case SERIAL_IO_MEM:		return readb(up->port.membase + offset);	default:		return inb(up->port.iobase + offset);	}}static _INLINE_ voidserial_out(struct uart_sunsu_port *up, int offset, int value){#ifndef CONFIG_SPARC64	/*	 * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are	 * connected with a gate then go to SlavIO. When IRQ4 goes tristated	 * gate outputs a logical one. Since we use level triggered interrupts	 * we have lockup and watchdog reset. We cannot mask IRQ because	 * keyboard shares IRQ with us (Word has it as Bob Smelik's design).	 * This problem is similar to what Alpha people suffer, see serial.c.	 */	if (offset == UART_MCR)		value |= UART_MCR_OUT2;#endif	offset <<= up->port.regshift;	switch (up->port.iotype) {	case SERIAL_IO_HUB6:		outb(up->port.hub6 - 1 + offset, up->port.iobase);		outb(value, up->port.iobase + 1);		break;	case SERIAL_IO_MEM:		writeb(value, up->port.membase + offset);		break;	default:		outb(value, up->port.iobase + offset);	}}/* * We used to support using pause I/O for certain machines.  We * haven't supported this for a while, but just in case it's badly * needed for certain old 386 machines, I've left these #define's * in.... */#define serial_inp(up, offset)		serial_in(up, offset)#define serial_outp(up, offset, value)	serial_out(up, offset, value)/* * For the 16C950 */static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value){	serial_out(up, UART_SCR, offset);	serial_out(up, UART_ICR, value);}#if 0 /* Unused currently */static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset){	unsigned int value;	serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);	serial_out(up, UART_SCR, offset);	value = serial_in(up, UART_ICR);	serial_icr_write(up, UART_ACR, up->acr);	return value;}#endif#ifdef CONFIG_SERIAL_8250_RSA/* * Attempts to turn on the RSA FIFO.  Returns zero on failure. * We set the port uart clock rate if we succeed. */static int __enable_rsa(struct uart_sunsu_port *up){	unsigned char mode;	int result;	mode = serial_inp(up, UART_RSA_MSR);	result = mode & UART_RSA_MSR_FIFO;	if (!result) {		serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);		mode = serial_inp(up, UART_RSA_MSR);		result = mode & UART_RSA_MSR_FIFO;	}	if (result)		up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;	return result;}static void enable_rsa(struct uart_sunsu_port *up){	if (up->port.type == PORT_RSA) {		if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {			spin_lock_irq(&up->port.lock);			__enable_rsa(up);			spin_unlock_irq(&up->port.lock);		}		if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)			serial_outp(up, UART_RSA_FRR, 0);	}}/* * Attempts to turn off the RSA FIFO.  Returns zero on failure. * It is unknown why interrupts were disabled in here.  However, * the caller is expected to preserve this behaviour by grabbing * the spinlock before calling this function. */static void disable_rsa(struct uart_sunsu_port *up){	unsigned char mode;	int result;	if (up->port.type == PORT_RSA &&	    up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {		spin_lock_irq(&up->port.lock);		mode = serial_inp(up, UART_RSA_MSR);		result = !(mode & UART_RSA_MSR_FIFO);		if (!result) {			serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);			mode = serial_inp(up, UART_RSA_MSR);			result = !(mode & UART_RSA_MSR_FIFO);		}		if (result)			up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;		spin_unlock_irq(&up->port.lock);	}}#endif /* CONFIG_SERIAL_8250_RSA */static void sunsu_stop_tx(struct uart_port *port, unsigned int tty_stop){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	if (up->ier & UART_IER_THRI) {		up->ier &= ~UART_IER_THRI;		serial_out(up, UART_IER, up->ier);	}	if (up->port.type == PORT_16C950 && tty_stop) {		up->acr |= UART_ACR_TXDIS;		serial_icr_write(up, UART_ACR, up->acr);	}}static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	if (!(up->ier & UART_IER_THRI)) {		up->ier |= UART_IER_THRI;		serial_out(up, UART_IER, up->ier);	}	/*	 * We only do this from uart_start	 */	if (tty_start && up->port.type == PORT_16C950) {		up->acr &= ~UART_ACR_TXDIS;		serial_icr_write(up, UART_ACR, up->acr);	}}static void sunsu_stop_rx(struct uart_port *port){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	up->ier &= ~UART_IER_RLSI;	up->port.read_status_mask &= ~UART_LSR_DR;	serial_out(up, UART_IER, up->ier);	spin_unlock_irqrestore(&up->port.lock, flags);}static void sunsu_enable_ms(struct uart_port *port){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	up->ier |= UART_IER_MSI;	serial_out(up, UART_IER, up->ier);	spin_unlock_irqrestore(&up->port.lock, flags);}static _INLINE_ struct tty_struct *receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs *regs){	struct tty_struct *tty = up->port.info->tty;	unsigned char ch;	int max_count = 256;	int saw_console_brk = 0;	do {		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		}		ch = serial_inp(up, UART_RX);		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = TTY_NORMAL;		up->port.icount.rx++;		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |				       UART_LSR_FE | UART_LSR_OE))) {			/*			 * For statistics only			 */			if (*status & UART_LSR_BI) {				*status &= ~(UART_LSR_FE | UART_LSR_PE);				up->port.icount.brk++;				if (up->port.cons != NULL &&				    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))					goto ignore_char;			} else if (*status & UART_LSR_PE)				up->port.icount.parity++;			else if (*status & UART_LSR_FE)				up->port.icount.frame++;			if (*status & UART_LSR_OE)				up->port.icount.overrun++;			/*			 * Mask off conditions which should be ingored.			 */			*status &= up->port.read_status_mask;			if (up->port.cons != NULL &&			    up->port.line == up->port.cons->index) {				/* Recover the break flag from console xmit */				*status |= up->lsr_break_flag;				up->lsr_break_flag = 0;			}			if (*status & UART_LSR_BI) {				*tty->flip.flag_buf_ptr = TTY_BREAK;			} else if (*status & UART_LSR_PE)				*tty->flip.flag_buf_ptr = TTY_PARITY;			else if (*status & UART_LSR_FE)				*tty->flip.flag_buf_ptr = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch, regs))			goto ignore_char;		if ((*status & up->port.ignore_status_mask) == 0) {			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}		if ((*status & UART_LSR_OE) &&		    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++;		}	ignore_char:		*status = serial_inp(up, UART_LSR);	} while ((*status & UART_LSR_DR) && (max_count-- > 0));	if (saw_console_brk)		sun_do_break();	return tty;}static _INLINE_ void transmit_chars(struct uart_sunsu_port *up){	struct circ_buf *xmit = &up->port.info->xmit;	int count;	if (up->port.x_char) {		serial_outp(up, UART_TX, up->port.x_char);		up->port.icount.tx++;		up->port.x_char = 0;		return;	}	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {		sunsu_stop_tx(&up->port, 0);		return;	}	count = up->port.fifosize;	do {		serial_out(up, UART_TX, xmit->buf[xmit->tail]);		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		up->port.icount.tx++;		if (uart_circ_empty(xmit))			break;	} while (--count > 0);	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(&up->port);	if (uart_circ_empty(xmit))		sunsu_stop_tx(&up->port, 0);}static _INLINE_ void check_modem_status(struct uart_sunsu_port *up){	int status;	status = serial_in(up, UART_MSR);	if ((status & UART_MSR_ANY_DELTA) == 0)		return;	if (status & UART_MSR_TERI)		up->port.icount.rng++;	if (status & UART_MSR_DDSR)		up->port.icount.dsr++;	if (status & UART_MSR_DDCD)		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);	if (status & UART_MSR_DCTS)		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);	wake_up_interruptible(&up->port.info->delta_msr_wait);}static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct uart_sunsu_port *up = dev_id;	unsigned long flags;	unsigned char status;	spin_lock_irqsave(&up->port.lock, flags);	do {		struct tty_struct *tty;		status = serial_inp(up, UART_LSR);		tty = NULL;		if (status & UART_LSR_DR)			tty = receive_chars(up, &status, regs);		check_modem_status(up);		if (status & UART_LSR_THRE)			transmit_chars(up);		spin_unlock_irqrestore(&up->port.lock, flags);		if (tty)			tty_flip_buffer_push(tty);		spin_lock_irqsave(&up->port.lock, flags);	} while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT));	spin_unlock_irqrestore(&up->port.lock, flags);	return IRQ_HANDLED;}/* Separate interrupt handling path for keyboard/mouse ports.  */static voidsunsu_change_speed(struct uart_port *port, unsigned int cflag,		   unsigned int iflag, unsigned int quot);static void sunsu_change_mouse_baud(struct uart_sunsu_port *up){	unsigned int cur_cflag = up->cflag;	int quot, new_baud;	up->cflag &= ~CBAUD;	up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud);	quot = up->port.uartclk / (16 * new_baud);	spin_unlock(&up->port.lock);	sunsu_change_speed(&up->port, up->cflag, 0, quot);	spin_lock(&up->port.lock);}static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *regs, int is_break){	do {		unsigned char ch = serial_inp(up, UART_RX);		/* Stop-A is handled by drivers/char/keyboard.c now. */		if (up->su_type == SU_PORT_KBD) {#ifdef CONFIG_SERIO			serio_interrupt(up->serio, ch, 0, regs);#endif		} else if (up->su_type == SU_PORT_MS) {			int ret = suncore_mouse_baud_detection(ch, is_break);			switch (ret) {			case 2:				sunsu_change_mouse_baud(up);				/* fallthru */			case 1:				break;			case 0:#ifdef CONFIG_SERIO				serio_interrupt(up->serio, ch, 0, regs);#endif				break;			};		}	} while (serial_in(up, UART_LSR) & UART_LSR_DR);}static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct uart_sunsu_port *up = dev_id;	if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) {		unsigned char status = serial_inp(up, UART_LSR);		if ((status & UART_LSR_DR) || (status & UART_LSR_BI))			receive_kbd_ms_chars(up, regs,					     (status & UART_LSR_BI) != 0);	}	return IRQ_HANDLED;}static unsigned int sunsu_tx_empty(struct uart_port *port){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	unsigned long flags;	unsigned int ret;	spin_lock_irqsave(&up->port.lock, flags);	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;	spin_unlock_irqrestore(&up->port.lock, flags);	return ret;}static unsigned int sunsu_get_mctrl(struct uart_port *port){	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;	unsigned long flags;	unsigned char status;	unsigned int ret;	spin_lock_irqsave(&up->port.lock, flags);	status = serial_in(up, UART_MSR);

⌨️ 快捷键说明

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