⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 8250.c

📁 linux 2.6内核下8250串口设备驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  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 */	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_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,	},};#ifdef 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];}#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:		return readb(up->port.membase + offset);	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){	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_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;	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)/* 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);}#ifdef 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);}#else#define serial_dl_read(up) _serial_dl_read(up)#define serial_dl_write(up, value) _serial_dl_write(up, value)#endif/* * 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) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -