8250.c
来自「linux 内核源代码」· C语言 代码 · 共 2,571 行 · 第 1/5 页
C
2,571 行
/* * 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. */#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial_reg.h>#include <linux/serial_core.h>#include <linux/serial.h>#include <linux/serial_8250.h>#include <linux/nmi.h>#include <linux/mutex.h>#include <asm/io.h>#include <asm/irq.h>#include "8250.h"/* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option * is unsafe when used on edge-triggered interrupts. */static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;/* * 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)#ifdef CONFIG_SERIAL_8250_DETECT_IRQ#define CONFIG_SERIAL_DETECT_IRQ 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 const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */};#define UART_NR 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 short capabilities; /* port capabilities */ unsigned short bugs; /* port bugs */ unsigned int tx_loadsz; /* transmit fifo load size */ 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 */ /* * Some bits in registers are cleared on a read, so they must * be saved whenever the register is read but the bits will not * be immediately processed. */#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS unsigned char lsr_saved_flags;#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; /* * 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_UNKNOWN] = { .name = "unknown", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_8250] = { .name = "8250", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_16450] = { .name = "16450", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_16550] = { .name = "16550", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_16550A] = { .name = "16550A", .fifo_size = 16, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, [PORT_CIRRUS] = { .name = "Cirrus", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_16650] = { .name = "ST16650", .fifo_size = 1, .tx_loadsz = 1, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16650V2] = { .name = "ST16650V2", .fifo_size = 32, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_00, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16750] = { .name = "TI16750", .fifo_size = 64, .tx_loadsz = 64, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | UART_FCR7_64BYTE, .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, }, [PORT_STARTECH] = { .name = "Startech", .fifo_size = 1, .tx_loadsz = 1, }, [PORT_16C950] = { .name = "16C950/954", .fifo_size = 128, .tx_loadsz = 128, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, [PORT_16654] = { .name = "ST16654", .fifo_size = 64, .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16850] = { .name = "XR16850", .fifo_size = 128, .tx_loadsz = 128, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_RSA] = { .name = "RSA", .fifo_size = 2048, .tx_loadsz = 2048, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, .flags = UART_CAP_FIFO, }, [PORT_NS16550A] = { .name = "NS16550A", .fifo_size = 16, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_NATSEMI, }, [PORT_XSCALE] = { .name = "XScale", .fifo_size = 32, .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_UUE, }, [PORT_RM9000] = { .name = "RM9000", .fifo_size = 16, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, },};#if defined (CONFIG_SERIAL_8250_AU1X00)/* Au1x00 UART hardware has a weird register layout */static const u8 au_io_in_map[] = { [UART_RX] = 0, [UART_IER] = 2, [UART_IIR] = 3, [UART_LCR] = 5, [UART_MCR] = 6, [UART_LSR] = 7, [UART_MSR] = 8,};static const u8 au_io_out_map[] = { [UART_TX] = 1, [UART_IER] = 2, [UART_FCR] = 4, [UART_LCR] = 5, [UART_MCR] = 6,};/* sane hardware needs no mapping */static inline int map_8250_in_reg(struct uart_8250_port *up, int offset){ if (up->port.iotype != UPIO_AU) return offset; return au_io_in_map[offset];}static inline int map_8250_out_reg(struct uart_8250_port *up, int offset){ if (up->port.iotype != UPIO_AU) return offset; return au_io_out_map[offset];}#elif defined (CONFIG_SERIAL_8250_RM9K)static const u8 regmap_in[8] = { [UART_RX] = 0x00, [UART_IER] = 0x0c, [UART_IIR] = 0x14, [UART_LCR] = 0x1c, [UART_MCR] = 0x20, [UART_LSR] = 0x24, [UART_MSR] = 0x28, [UART_SCR] = 0x2c }, regmap_out[8] = { [UART_TX] = 0x04, [UART_IER] = 0x0c, [UART_FCR] = 0x18, [UART_LCR] = 0x1c, [UART_MCR] = 0x20, [UART_LSR] = 0x24, [UART_MSR] = 0x28, [UART_SCR] = 0x2c };static inline int map_8250_in_reg(struct uart_8250_port *up, int offset){ if (up->port.iotype != UPIO_RM9000) return offset; return regmap_in[offset];}static inline int map_8250_out_reg(struct uart_8250_port *up, int offset){ if (up->port.iotype != UPIO_RM9000) return offset; return regmap_out[offset];}#else/* sane hardware needs no mapping */#define map_8250_in_reg(up, offset) (offset)#define map_8250_out_reg(up, offset) (offset)#endifstatic unsigned int serial_in(struct uart_8250_port *up, int offset){ unsigned int tmp; offset = map_8250_in_reg(up, 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: case UPIO_DWAPB: return readb(up->port.membase + offset); case UPIO_RM9000: case UPIO_MEM32: return readl(up->port.membase + offset);#ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: return __raw_readl(up->port.membase + offset);#endif case UPIO_TSI: if (offset == UART_IIR) { tmp = readl(up->port.membase + (UART_IIR & ~3)); return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ } else return readb(up->port.membase + offset); default: return inb(up->port.iobase + offset); }}static voidserial_out(struct uart_8250_port *up, int offset, int value){ /* Save the offset before it's remapped */ int save_offset = offset; offset = map_8250_out_reg(up, 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_RM9000: case UPIO_MEM32: writel(value, up->port.membase + offset); break;#ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: __raw_writel(value, up->port.membase + offset); break;#endif case UPIO_TSI: if (!((offset == UART_IER) && (value & UART_IER_UUE))) writeb(value, up->port.membase + offset); break; case UPIO_DWAPB: /* Save the LCR value so it can be re-written when a * Busy Detect interrupt occurs. */ if (save_offset == UART_LCR) up->lcr = value; writeb(value, up->port.membase + offset); /* Read the IER to ensure any interrupt is cleared before * returning from ISR. */ if (save_offset == UART_TX || save_offset == UART_IER) value = serial_in(up, UART_IER); break; default: outb(value, up->port.iobase + offset); }}static voidserial_out_sync(struct uart_8250_port *up, int offset, int value){ switch (up->port.iotype) { case UPIO_MEM: case UPIO_MEM32:#ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU:#endif case UPIO_DWAPB: serial_out(up, offset, value); serial_in(up, UART_LCR); /* safe, no side-effects */ break; default: serial_out(up, offset, value); }}/* * 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)/* Uart divisor latch read */static inline int _serial_dl_read(struct uart_8250_port *up){ return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;}/* Uart divisor latch write */static inline void _serial_dl_write(struct uart_8250_port *up, int value){ serial_outp(up, UART_DLL, value & 0xff); serial_outp(up, UART_DLM, value >> 8 & 0xff);}#if defined (CONFIG_SERIAL_8250_AU1X00)/* Au1x00 haven't got a standard divisor latch */static int serial_dl_read(struct uart_8250_port *up){ if (up->port.iotype == UPIO_AU) return __raw_readl(up->port.membase + 0x28); else return _serial_dl_read(up);}static void serial_dl_write(struct uart_8250_port *up, int value){ if (up->port.iotype == UPIO_AU) __raw_writel(value, up->port.membase + 0x28); else _serial_dl_write(up, value);}#elif defined (CONFIG_SERIAL_8250_RM9K)static int serial_dl_read(struct uart_8250_port *up){ return (up->port.iotype == UPIO_RM9000) ? (((__raw_readl(up->port.membase + 0x10) << 8) | (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : _serial_dl_read(up);}static void serial_dl_write(struct uart_8250_port *up, int value){ if (up->port.iotype == UPIO_RM9000) { __raw_writel(value, up->port.membase + 0x08); __raw_writel(value >> 8, up->port.membase + 0x10); } else { _serial_dl_write(up, value); }}#else#define serial_dl_read(up) _serial_dl_read(up)#define serial_dl_write(up, value) _serial_dl_write(up, value)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?