?? 8250.c
字號:
/* * linux/drivers/char/serial_8250.c * * Driver for 8250/16550-type serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * Copyright (C) 2001 Russell King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * $Id: 8250.c,v 1.14.2.8 2002/10/24 14:31:31 rmk Exp $ * * A note about mapbase / membase * * mapbase is the physical address of the IO port. Currently, we don't * support this very well, and it may well be dropped from this driver * in future. As such, mapbase should be NULL. * * membase is an 'ioremapped' cookie. This is compatible with the old * serial.c driver, and is currently the preferred form. */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/serial_reg.h>#include <linux/serialP.h>#include <linux/delay.h>#include <linux/serial_core.h>#include <linux/kmod.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include "8250.h"/* * This converts from our new CONFIG_ symbols to the symbols * that asm/serial.h expects. You _NEED_ to comment out the * linux/config.h include contained inside asm/serial.h for * this to work. */#undef CONFIG_SERIAL_MANY_PORTS#undef CONFIG_SERIAL_DETECT_IRQ#undef CONFIG_SERIAL_MULTIPORT#undef CONFIG_HUB6#ifdef CONFIG_SERIAL_8250_MANY_PORTS#define CONFIG_SERIAL_MANY_PORTS 1#endif#ifdef CONFIG_SERIAL_8250_DETECT_IRQ#define CONFIG_SERIAL_DETECT_IRQ 1#endif#ifdef CONFIG_SERIAL_8250_MULTIPORT#define CONFIG_SERIAL_MULTIPORT 1#endif#ifdef CONFIG_SERIAL_8250_HUB6#define CONFIG_HUB6 1#endif#include <asm/serial.h>static struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */};#define UART_NR ARRAY_SIZE(old_serial_port)static struct tty_driver normal, callout;static struct tty_struct *serial8250_table[UART_NR];static struct termios *serial8250_termios[UART_NR], *serial8250_termios_locked[UART_NR];#ifdef CONFIG_SERIAL_8250_CONSOLEstatic struct console serial8250_console;static unsigned int lsr_break_flag;#endifstatic struct uart_info *IRQ_ports[NR_IRQS];#if defined(CONFIG_SERIAL_RSA) && defined(MODULE)#define PORT_RSA_MAX 4static int probe_rsa[PORT_RSA_MAX];static int force_rsa[PORT_RSA_MAX];MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");#endif /* CONFIG_SERIAL_RSA */#define port_acr unused[0] /* 8bit */#define port_ier unused[1] /* 8bit */#define port_rev unused[2] /* 8bit */#define port_lcr unused[3] /* 8bit *//* * 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 }};static _INLINE_ unsigned int serial_in(struct uart_port *port, int offset){ offset <<= port->regshift; switch (port->iotype) {#ifdef CONFIG_SERIAL_8250_HUB6 case SERIAL_IO_HUB6: outb(port->hub6 - 1 + offset, port->iobase); return inb(port->iobase + 1);#endif case SERIAL_IO_MEM: return readb((unsigned long)port->membase + offset); default: return inb(port->iobase + offset); }}static _INLINE_ voidserial_out(struct uart_port *port, int offset, int value){ offset <<= port->regshift; switch (port->iotype) {#ifdef CONFIG_SERIAL_8250_HUB6 case SERIAL_IO_HUB6: outb(port->hub6 - 1 + offset, port->iobase); outb(value, port->iobase + 1); break;#endif case SERIAL_IO_MEM: writeb(value, (unsigned long)port->membase + offset); break; default: outb(value, 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(port, offset) serial_in(port, offset)#define serial_outp(port, offset, value) serial_out(port, offset, value)/* * For the 16C950 */static void serial_icr_write(struct uart_port *port, int offset, int value){ serial_out(port, UART_SCR, offset); serial_out(port, UART_ICR, value);}static unsigned int serial_icr_read(struct uart_port *port, int offset){ unsigned int value; serial_icr_write(port, UART_ACR, port->port_acr | UART_ACR_ICRRD); serial_out(port, UART_SCR, offset); value = serial_in(port, UART_ICR); serial_icr_write(port, UART_ACR, port->port_acr); return value;}#ifdef CONFIG_SERIAL_RSA/* Attempts to turn on the RSA FIFO. Returns zero on failure */static int enable_rsa(struct uart_port *port){ unsigned char mode; int result; unsigned long flags; save_flags(flags); cli(); mode = serial_inp(port, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; if (!result) { serial_outp(port, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); mode = serial_inp(port, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; } restore_flags(flags); return result;}/* Attempts to turn off the RSA FIFO. Returns zero on failure */static int disable_rsa(struct uart_port *port){ unsigned char mode; int result; unsigned long flags; save_flags(flags); cli(); mode = serial_inp(port, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); if (!result) { serial_outp(port, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); mode = serial_inp(port, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); } restore_flags(flags); return result;}#endif /* CONFIG_SERIAL_RSA *//* * This is a quickie test to see how big the FIFO is. * It doesn't work at all the time, more's the pity. */static int size_fifo(struct uart_port *port){ unsigned char old_fcr, old_mcr, old_dll, old_dlm; int count; old_fcr = serial_inp(port, UART_FCR); old_mcr = serial_inp(port, UART_MCR); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(port, UART_MCR, UART_MCR_LOOP); serial_outp(port, UART_LCR, UART_LCR_DLAB); old_dll = serial_inp(port, UART_DLL); old_dlm = serial_inp(port, UART_DLM); serial_outp(port, UART_DLL, 0x01); serial_outp(port, UART_DLM, 0x00); serial_outp(port, UART_LCR, 0x03); for (count = 0; count < 256; count++) serial_outp(port, UART_TX, count); mdelay(20); for (count = 0; (serial_inp(port, UART_LSR) & UART_LSR_DR) && (count < 256); count++) serial_inp(port, UART_RX); serial_outp(port, UART_FCR, old_fcr); serial_outp(port, UART_MCR, old_mcr); serial_outp(port, UART_LCR, UART_LCR_DLAB); serial_outp(port, UART_DLL, old_dll); serial_outp(port, UART_DLM, old_dlm); return count;}/* * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. * When this function is called we know it is at least a StarTech * 16650 V2, but it might be one of several StarTech UARTs, or one of * its clones. (We treat the broken original StarTech 16650 V1 as a * 16550, and why not? Startech doesn't seem to even acknowledge its * existence.) * * What evil have men's minds wrought... */static voidautoconfig_startech_uarts(struct uart_port *port){ unsigned char scratch, scratch2, scratch3, scratch4; /* * First we check to see if it's an Oxford Semiconductor UART. * * If we have to do this here because some non-National * Semiconductor clone chips lock up if you try writing to the * LSR register (which serial_icr_read does) */ if (port->type == PORT_16550A) { /* * EFR [4] must be set else this test fails * * This shouldn't be necessary, but Mike Hudson * (Exoray@isys.ca) claims that it's needed for 952 * dual UART's (which are not recommended for new designs). */ port->port_acr = 0; serial_out(port, UART_LCR, 0xBF); serial_out(port, UART_EFR, 0x10); serial_out(port, UART_LCR, 0x00); /* Check for Oxford Semiconductor 16C950 */ scratch = serial_icr_read(port, UART_ID1); scratch2 = serial_icr_read(port, UART_ID2); scratch3 = serial_icr_read(port, UART_ID3); if (scratch == 0x16 && scratch2 == 0xC9 && (scratch3 == 0x50 || scratch3 == 0x52 || scratch3 == 0x54)) { port->type = PORT_16C950; port->port_rev = serial_icr_read(port, UART_REV) | (scratch3 << 8); return; } } /* * We check for a XR16C850 by setting DLL and DLM to 0, and then * reading back DLL and DLM. The chip type depends on the DLM * value read back: * 0x10 - XR16C850 and the DLL contains the chip revision. * 0x12 - XR16C2850. * 0x14 - XR16C854. */ /* Save the DLL and DLM */ serial_outp(port, UART_LCR, UART_LCR_DLAB); scratch3 = serial_inp(port, UART_DLL); scratch4 = serial_inp(port, UART_DLM); serial_outp(port, UART_DLL, 0); serial_outp(port, UART_DLM, 0); scratch2 = serial_inp(port, UART_DLL); scratch = serial_inp(port, UART_DLM); serial_outp(port, UART_LCR, 0); if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) { if (scratch == 0x10) port->port_rev = scratch2; port->type = PORT_16850; return; } /* Restore the DLL and DLM */ serial_outp(port, UART_LCR, UART_LCR_DLAB); serial_outp(port, UART_DLL, scratch3); serial_outp(port, UART_DLM, scratch4); serial_outp(port, UART_LCR, 0); /* * We distinguish between the '654 and the '650 by counting * how many bytes are in the FIFO. I'm using this for now, * since that's the technique that was sent to me in the * serial driver update, but I'm not convinced this works. * I've had problems doing this in the past. -TYT */ if (size_fifo(port) == 64) port->type = PORT_16654; else port->type = PORT_16650V2;}/* * This routine is called by rs_init() to initialize a specific serial * port. It determines what type of UART chip this serial port is * using: 8250, 16450, 16550, 16550A. The important question is * whether or not this UART is a 16550A or not, since this will * determine whether or not we can use its FIFO features or not. */static void autoconfig(struct uart_port *port, unsigned int probeflags){ unsigned char status1, status2, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags;#ifdef SERIAL_DEBUG_AUTOCONF printk("Testing ttyS%d (0x%04x, 0x%08lx)...\n", port->line, port->iobase, port->membase);#endif if (!port->iobase && !port->membase) return; save_flags(flags); cli(); if (!(port->flags & ASYNC_BUGGY_UART)) { /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. * * 0x80 is used as a nonsense port to prevent against * false positives due to ISA bus float. The * assumption is that 0x80 is a non-existent port; * which should be safe since include/asm/io.h also * makes this assumption. */ scratch = serial_inp(port, UART_IER); serial_outp(port, UART_IER, 0);#ifdef __i386__ outb(0xff, 0x080);#endif scratch2 = serial_inp(port, UART_IER); serial_outp(port, UART_IER, 0x0F);#ifdef __i386__ outb(0, 0x080);#endif scratch3 = serial_inp(port, UART_IER); serial_outp(port, UART_IER, scratch); if (scratch2 || scratch3 != 0x0F) {#ifdef SERIAL_DEBUG_AUTOCONF printk("serial: ttyS%d: simple autoconfig failed " "(%02x, %02x)\n", port->line, scratch2, scratch3);#endif restore_flags(flags); return; /* We failed; there's nothing here */ } } save_mcr = serial_in(port, UART_MCR); save_lcr = serial_in(port, UART_LCR); /* * Check to see if a UART is really there. Certain broken * internal modems based on the Rockwell chipset fail this * test, because they apparently don't implement the loopback * test mode. So this test is skipped on the COM 1 through * COM 4 ports. This *should* be safe, since no board * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope! */ if (!(port->flags & ASYNC_SKIP_TEST)) { serial_outp(port, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(port, UART_MSR) & 0xF0; serial_outp(port, UART_MCR, save_mcr); if (status1 != 0x90) {#ifdef SERIAL_DEBUG_AUTOCONF printk("serial: ttyS%d: no UART loopback failed\n", port->line);#endif restore_flags(flags); return; } } serial_outp(port, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(port, UART_EFR, 0); /* EFR is the same as FCR */ serial_outp(port, UART_LCR, 0); serial_outp(port, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(port, UART_IIR) >> 6; switch (scratch) { case 0: port->type = PORT_16450; break; case 1: port->type = PORT_UNKNOWN; break; case 2: port->type = PORT_16550; break; case 3: port->type = PORT_16550A; break; } if (port->type == PORT_16550A) { /* Check for Startech UART's */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -