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