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 + -
显示快捷键?