📄 8250.c
字号:
/* * linux/drivers/char/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.90 2002/07/28 10:03:27 rmk Exp $ * * A note about mapbase / membase * * mapbase is the physical address of the IO port. * membase is an 'ioremapped' cookie. */#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/tty.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/serial_reg.h>#include <linux/serial.h>#include <linux/serialP.h>#include <linux/delay.h>#include <linux/device.h>#include <asm/io.h>#include <asm/irq.h>#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>#include "8250.h"/* * Configuration: * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option * is unsafe when used on edge-triggered interrupts. */unsigned int share_irqs = SERIAL8250_SHARE_IRQS;/* * Debugging. */#if 0#define DEBUG_AUTOCONF(fmt...) printk(fmt)#else#define DEBUG_AUTOCONF(fmt...) do { } while (0)#endif#if 0#define DEBUG_INTR(fmt...) printk(fmt)#else#define DEBUG_INTR(fmt...) do { } while (0)#endif#define PASS_LIMIT 256/* * We default to IRQ0 for the "no irq" hack. Some * machine types want others as well - they're free * to redefine this in their header file. */#define is_real_interrupt(irq) ((irq) != 0)/* * 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_DETECT_IRQ#define CONFIG_SERIAL_DETECT_IRQ 1#endif#ifdef CONFIG_SERIAL_8250_MULTIPORT#define CONFIG_SERIAL_MULTIPORT 1#endif#ifdef CONFIG_SERIAL_8250_MANY_PORTS#define CONFIG_SERIAL_MANY_PORTS 1#endif/* * HUB6 is always on. This will be removed once the header * files have been cleaned. */#define CONFIG_HUB6 1#include <asm/serial.h>/* * SERIAL_PORT_DFNS tells us about built-in ports that have no * standard enumeration mechanism. Platforms that can find all * serial ports via mechanisms like ACPI or PCI need not supply it. */#ifndef SERIAL_PORT_DFNS#define SERIAL_PORT_DFNS#endifstatic struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */};#define UART_NR (ARRAY_SIZE(old_serial_port) + CONFIG_SERIAL_8250_NR_UARTS)#ifdef CONFIG_SERIAL_8250_RSA#define PORT_RSA_MAX 4static unsigned long probe_rsa[PORT_RSA_MAX];static unsigned int probe_rsa_count;#endif /* CONFIG_SERIAL_8250_RSA */struct uart_8250_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ unsigned int capabilities; /* port capabilities */ unsigned int tx_loadsz; /* transmit fifo load size */ unsigned short rev; unsigned char acr; unsigned char ier; unsigned char lcr; unsigned char mcr; unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ unsigned char lsr_break_flag; /* * We provide a per-port pm hook. */ void (*pm)(struct uart_port *port, unsigned int state, unsigned int old);};struct irq_info { spinlock_t lock; struct list_head *head;};static struct irq_info irq_lists[NR_IRQS];/* * Here we define the default xmit fifo size used for each type of UART. */static const struct serial8250_config uart_config[PORT_MAX_8250+1] = { { "unknown", 1, 1, 0 }, { "8250", 1, 1, 0 }, { "16450", 1, 1, 0 }, { "16550", 1, 1, 0 }, { "16550A", 16, 16, UART_CAP_FIFO }, { "Cirrus", 1, 1, 0 }, { "ST16650", 1, 1, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, { "ST16650V2", 32, 16, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, { "TI16750", 64, 64, UART_CAP_FIFO | UART_CAP_SLEEP }, { "Startech", 1, 1, 0 }, { "16C950/954", 128, 128, UART_CAP_FIFO }, { "ST16654", 64, 32, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, { "XR16850", 128, 128, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, { "RSA", 2048, 2048, UART_CAP_FIFO }, { "NS16550A", 16, 16, UART_CAP_FIFO | UART_NATSEMI }, { "XScale", 32, 32, UART_CAP_FIFO }, { "OMAP UART", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }};static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset){ offset <<= up->port.regshift; switch (up->port.iotype) { case UPIO_HUB6: outb(up->port.hub6 - 1 + offset, up->port.iobase); return inb(up->port.iobase + 1); case UPIO_MEM: return readb(up->port.membase + offset); case UPIO_MEM32: return readl(up->port.membase + offset); default: return inb(up->port.iobase + offset); }}static _INLINE_ voidserial_out(struct uart_8250_port *up, int offset, int value){ offset <<= up->port.regshift; switch (up->port.iotype) { case UPIO_HUB6: outb(up->port.hub6 - 1 + offset, up->port.iobase); outb(value, up->port.iobase + 1); break; case UPIO_MEM: writeb(value, up->port.membase + offset); break; case UPIO_MEM32: writel(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_8250_port *up, int offset, int value){ serial_out(up, UART_SCR, offset); serial_out(up, UART_ICR, value);}static unsigned int serial_icr_read(struct uart_8250_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;}/* * FIFO support. */static inline void serial8250_clear_fifos(struct uart_8250_port *p){ if (p->capabilities & UART_CAP_FIFO) { serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(p, UART_FCR, 0); }}/* * IER sleep support. UARTs which have EFRs need the "extended * capability" bit enabled. Note that on XR16C850s, we need to * reset LCR to write to IER. */static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep){ if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { serial_outp(p, UART_LCR, 0xBF); serial_outp(p, UART_EFR, UART_EFR_ECB); serial_outp(p, UART_LCR, 0); } serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); if (p->capabilities & UART_CAP_EFR) { serial_outp(p, UART_LCR, 0xBF); serial_outp(p, UART_EFR, 0); serial_outp(p, UART_LCR, 0); } }}#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_8250_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_8250_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_8250_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 *//* * 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_8250_port *up){ unsigned char old_fcr, old_mcr, old_dll, old_dlm; int count; old_fcr = serial_inp(up, UART_FCR); old_mcr = serial_inp(up, UART_MCR); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(up, UART_MCR, UART_MCR_LOOP); serial_outp(up, UART_LCR, UART_LCR_DLAB); old_dll = serial_inp(up, UART_DLL); old_dlm = serial_inp(up, UART_DLM); serial_outp(up, UART_DLL, 0x01); serial_outp(up, UART_DLM, 0x00); serial_outp(up, UART_LCR, 0x03); for (count = 0; count < 256; count++) serial_outp(up, UART_TX, count); mdelay(20);/* FIXME - schedule_timeout */ for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && (count < 256); count++) serial_inp(up, UART_RX); serial_outp(up, UART_FCR, old_fcr); serial_outp(up, UART_MCR, old_mcr); serial_outp(up, UART_LCR, UART_LCR_DLAB); serial_outp(up, UART_DLL, old_dll); serial_outp(up, 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 void autoconfig_has_efr(struct uart_8250_port *up){ unsigned char id1, id2, id3, rev, saved_dll, saved_dlm; /* * 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) */ /* * Check for Oxford Semiconductor 16C950. * * 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). */ up->acr = 0; serial_out(up, UART_LCR, 0xBF); serial_out(up, UART_EFR, UART_EFR_ECB); serial_out(up, UART_LCR, 0x00); id1 = serial_icr_read(up, UART_ID1); id2 = serial_icr_read(up, UART_ID2); id3 = serial_icr_read(up, UART_ID3); rev = serial_icr_read(up, UART_REV); DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); if (id1 == 0x16 && id2 == 0xC9 && (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { up->port.type = PORT_16C950; up->rev = rev | (id3 << 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. */ serial_outp(up, UART_LCR, UART_LCR_DLAB); saved_dll = serial_inp(up, UART_DLL); saved_dlm = serial_inp(up, UART_DLM); serial_outp(up, UART_DLL, 0); serial_outp(up, UART_DLM, 0); id2 = serial_inp(up, UART_DLL); id1 = serial_inp(up, UART_DLM); serial_outp(up, UART_DLL, saved_dll); serial_outp(up, UART_DLM, saved_dlm); DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2); if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) { if (id1 == 0x10) up->rev = id2; up->port.type = PORT_16850; return; } /* * It wasn't an XR16C850. * * 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(up) == 64) up->port.type = PORT_16654; else up->port.type = PORT_16650V2;}/* * We detected a chip without a FIFO. Only two fall into * this category - the original 8250 and the 16450. The * 16450 has a scratch register (accessible with LCR=0) */static void autoconfig_8250(struct uart_8250_port *up){ unsigned char scratch, status1, status2; up->port.type = PORT_8250; scratch = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0xa5); status1 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0x5a); status2 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, scratch); if (status1 == 0xa5 && status2 == 0x5a) up->port.type = PORT_16450;}/* * We know that the chip has FIFOs. Does it have an EFR? The * EFR is located in the same register position as the IIR and * we know the top two bits of the IIR are currently set. The * EFR should contain zero. Try to read the EFR. */static void autoconfig_16550a(struct uart_8250_port *up){ unsigned char status1, status2; up->port.type = PORT_16550A; /* * Check for presence of the EFR when DLAB is set. * Only ST16C650V1 UARTs pass this test. */ serial_outp(up, UART_LCR, UART_LCR_DLAB); if (serial_in(up, UART_EFR) == 0) { serial_outp(up, UART_EFR, 0xA8); if (serial_in(up, UART_EFR) != 0) { DEBUG_AUTOCONF("EFRv1 "); up->port.type = PORT_16650; } else { DEBUG_AUTOCONF("Motorola 8xxx DUART "); } serial_outp(up, UART_EFR, 0); return; } /* * Maybe it requires 0xbf to be written to the LCR. * (other ST16C650V2 UARTs, TI16C752A, etc) */ serial_outp(up, UART_LCR, 0xBF); if (serial_in(up, UART_EFR) == 0) { DEBUG_AUTOCONF("EFRv2 "); autoconfig_has_efr(up); return; } /* * Check for a National Semiconductor SuperIO chip. * Attempt to switch to bank 2, read the value of the LOOP bit * from EXCR1. Switch back to bank 0, change it in MCR. Then * switch back to bank 2, read it from EXCR1 again and check * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 * On PowerPC we don't want to change baud_base, as we have * a number of different divisors. -- Tom Rini */ serial_outp(up, UART_LCR, 0); status1 = serial_in(up, UART_MCR); serial_outp(up, UART_LCR, 0xE0); status2 = serial_in(up, 0x02); /* EXCR1 */ if (!((status2 ^ status1) & UART_MCR_LOOP)) { serial_outp(up, UART_LCR, 0); serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -