8250_pci.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,238 行 · 第 1/4 页

C
2,238
字号
/* *  linux/drivers/char/8250_pci.c * *  Probe module for 8250/16550-type PCI serial ports. * *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * *  Copyright (C) 2001 Russell King, All Rights Reserved. * * 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. * *  $Id: 8250_pci.c,v 1.28 2002/11/02 11:14:18 rmk Exp $ */#include <linux/module.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/tty.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/8250_pci.h>#include <asm/bitops.h>#include <asm/byteorder.h>#include <asm/io.h>#include "8250.h"/* * Definitions for PCI support. */#define FL_BASE_MASK		0x0007#define FL_BASE0		0x0000#define FL_BASE1		0x0001#define FL_BASE2		0x0002#define FL_BASE3		0x0003#define FL_BASE4		0x0004#define FL_GET_BASE(x)		(x & FL_BASE_MASK)/* Use successive BARs (PCI base address registers),   else use offset into some specified BAR */#define FL_BASE_BARS		0x0008/* do not assign an irq */#define FL_NOIRQ		0x0080/* Use the Base address register size to cap number of ports */#define FL_REGION_SZ_CAP	0x0100struct pci_board {	unsigned int flags;	unsigned int num_ports;	unsigned int base_baud;	unsigned int uart_offset;	unsigned int reg_shift;	unsigned int first_offset;};/* * init function returns: *  > 0 - number of ports *  = 0 - use board->num_ports *  < 0 - error */struct pci_serial_quirk {	u32	vendor;	u32	device;	u32	subvendor;	u32	subdevice;	int	(*init)(struct pci_dev *dev);	int	(*setup)(struct pci_dev *dev, struct pci_board *board,			 struct serial_struct *req, int idx);	void	(*exit)(struct pci_dev *dev);};#define PCI_NUM_BAR_RESOURCES	6struct serial_private {	unsigned int		nr;	void __iomem		*remapped_bar[PCI_NUM_BAR_RESOURCES];	struct pci_serial_quirk	*quirk;	int			line[0];};static void moan_device(const char *str, struct pci_dev *dev){	printk(KERN_WARNING "%s: %s\n"	       KERN_WARNING "Please send the output of lspci -vv, this\n"	       KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"	       KERN_WARNING "manufacturer and name of serial board or\n"	       KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n",	       pci_name(dev), str, dev->vendor, dev->device,	       dev->subsystem_vendor, dev->subsystem_device);}static intsetup_port(struct pci_dev *dev, struct serial_struct *req,	   int bar, int offset, int regshift){	struct serial_private *priv = pci_get_drvdata(dev);	unsigned long port, len;	if (bar >= PCI_NUM_BAR_RESOURCES)		return -EINVAL;	if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {		port = pci_resource_start(dev, bar);		len =  pci_resource_len(dev, bar);		if (!priv->remapped_bar[bar])			priv->remapped_bar[bar] = ioremap(port, len);		if (!priv->remapped_bar[bar])			return -ENOMEM;		req->io_type = UPIO_MEM;		req->iomap_base = port + offset;		req->iomem_base = priv->remapped_bar[bar] + offset;		req->iomem_reg_shift = regshift;	} else {		port = pci_resource_start(dev, bar) + offset;		req->io_type = UPIO_PORT;		req->port = port;		if (HIGH_BITS_OFFSET)			req->port_high = port >> HIGH_BITS_OFFSET;	}	return 0;}/* * AFAVLAB uses a different mixture of BARs and offsets * Not that ugly ;) -- HW */static intafavlab_setup(struct pci_dev *dev, struct pci_board *board,	      struct serial_struct *req, int idx){	unsigned int bar, offset = board->first_offset;		bar = FL_GET_BASE(board->flags);	if (idx < 4)		bar += idx;	else {		bar = 4;		offset += (idx - 4) * board->uart_offset;	}	return setup_port(dev, req, bar, offset, board->reg_shift);}/* * HP's Remote Management Console.  The Diva chip came in several * different versions.  N-class, L2000 and A500 have two Diva chips, each * with 3 UARTs (the third UART on the second chip is unused).  Superdome * and Keystone have one Diva chip with 3 UARTs.  Some later machines have * one Diva chip, but it has been expanded to 5 UARTs. */static int __devinit pci_hp_diva_init(struct pci_dev *dev){	int rc = 0;	switch (dev->subsystem_device) {	case PCI_DEVICE_ID_HP_DIVA_TOSCA1:	case PCI_DEVICE_ID_HP_DIVA_HALFDOME:	case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:	case PCI_DEVICE_ID_HP_DIVA_EVEREST:		rc = 3;		break;	case PCI_DEVICE_ID_HP_DIVA_TOSCA2:		rc = 2;		break;	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:		rc = 4;		break;	case PCI_DEVICE_ID_HP_DIVA_POWERBAR:		rc = 1;		break;	}	return rc;}/* * HP's Diva chip puts the 4th/5th serial port further out, and * some serial ports are supposed to be hidden on certain models. */static intpci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board,	      struct serial_struct *req, int idx){	unsigned int offset = board->first_offset;	unsigned int bar = FL_GET_BASE(board->flags);	switch (dev->subsystem_device) {	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:		if (idx == 3)			idx++;		break;	case PCI_DEVICE_ID_HP_DIVA_EVEREST:		if (idx > 0)			idx++;		if (idx > 2)			idx++;		break;	}	if (idx > 2)		offset = 0x18;	offset += idx * board->uart_offset;	return setup_port(dev, req, bar, offset, board->reg_shift);}/* * Added for EKF Intel i960 serial boards */static int __devinit pci_inteli960ni_init(struct pci_dev *dev){	unsigned long oldval;	if (!(dev->subsystem_device & 0x1000))		return -ENODEV;	/* is firmware started? */	pci_read_config_dword(dev, 0x44, (void*) &oldval); 	if (oldval == 0x00001000L) { /* RESET value */ 		printk(KERN_DEBUG "Local i960 firmware missing");		return -ENODEV;	}	return 0;}/* * Some PCI serial cards using the PLX 9050 PCI interface chip require * that the card interrupt be explicitly enabled or disabled.  This * seems to be mainly needed on card using the PLX which also use I/O * mapped memory. */static int __devinit pci_plx9050_init(struct pci_dev *dev){	u8 irq_config;	void __iomem *p;	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) {		moan_device("no memory in bar 0", dev);		return 0;	}	irq_config = 0x41;	if (dev->vendor == PCI_VENDOR_ID_PANACOM)		irq_config = 0x43;	if ((dev->vendor == PCI_VENDOR_ID_PLX) &&	    (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {		/*		 * As the megawolf cards have the int pins active		 * high, and have 2 UART chips, both ints must be		 * enabled on the 9050. Also, the UARTS are set in		 * 16450 mode by default, so we have to enable the		 * 16C950 'enhanced' mode so that we can use the		 * deep FIFOs		 */		irq_config = 0x5b;	}	/*	 * enable/disable interrupts	 */	p = ioremap(pci_resource_start(dev, 0), 0x80);	if (p == NULL)		return -ENOMEM;	writel(irq_config, p + 0x4c);	/*	 * Read the register back to ensure that it took effect.	 */	readl(p + 0x4c);	iounmap(p);	return 0;}static void __devexit pci_plx9050_exit(struct pci_dev *dev){	u8 *p;	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0)		return;	/*	 * disable interrupts	 */	p = ioremap(pci_resource_start(dev, 0), 0x80);	if (p != NULL) {		writel(0, p + 0x4c);		/*		 * Read the register back to ensure that it took effect.		 */		readl(p + 0x4c);		iounmap(p);	}}/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */static intsbs_setup(struct pci_dev *dev, struct pci_board *board,		struct serial_struct *req, int idx){	unsigned int bar, offset = board->first_offset;	bar = 0;	if (idx < 4) {		/* first four channels map to 0, 0x100, 0x200, 0x300 */		offset += idx * board->uart_offset;	} else if (idx < 8) {		/* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */		offset += idx * board->uart_offset + 0xC00;	} else /* we have only 8 ports on PMC-OCTALPRO */		return 1;	return setup_port(dev, req, bar, offset, board->reg_shift);}/** This does initialization for PMC OCTALPRO cards:* maps the device memory, resets the UARTs (needed, bc* if the module is removed and inserted again, the card* is in the sleep mode) and enables global interrupt.*//* global control register offset for SBS PMC-OctalPro */#define OCT_REG_CR_OFF		0x500static int __devinit sbs_init(struct pci_dev *dev){	u8 * p;	p = ioremap(pci_resource_start(dev, 0),pci_resource_len(dev,0));	if (p == NULL)		return -ENOMEM;	/* Set bit-4 Control Register (UART RESET) in to reset the uarts */	writeb(0x10,p + OCT_REG_CR_OFF);	udelay(50);	writeb(0x0,p + OCT_REG_CR_OFF);	/* Set bit-2 (INTENABLE) of Control Register */	writeb(0x4, p + OCT_REG_CR_OFF);	iounmap(p);	return 0;}/* * Disables the global interrupt of PMC-OctalPro */static void __devexit sbs_exit(struct pci_dev *dev){	u8 * p;	p = ioremap(pci_resource_start(dev, 0),pci_resource_len(dev,0));	if (p != NULL) {		writeb(0, p + OCT_REG_CR_OFF);	}	iounmap(p);}/* * SIIG serial cards have an PCI interface chip which also controls * the UART clocking frequency. Each UART can be clocked independently * (except cards equiped with 4 UARTs) and initial clocking settings * are stored in the EEPROM chip. It can cause problems because this * version of serial driver doesn't support differently clocked UART's * on single PCI card. To prevent this, initialization functions set * high frequency clocking for all UART's on given card. It is safe (I * hope) because it doesn't touch EEPROM settings to prevent conflicts * with other OSes (like M$ DOS). * *  SIIG support added by Andrey Panin <pazke@donpac.ru>, 10/1999 *  * There is two family of SIIG serial cards with different PCI * interface chip and different configuration methods: *     - 10x cards have control registers in IO and/or memory space; *     - 20x cards have control registers in standard PCI configuration space. * * Note: some SIIG cards are probed by the parport_serial object. */#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)static int pci_siig10x_init(struct pci_dev *dev){	u16 data;	void __iomem *p;	switch (dev->device & 0xfff8) {	case PCI_DEVICE_ID_SIIG_1S_10x:	/* 1S */		data = 0xffdf;		break;	case PCI_DEVICE_ID_SIIG_2S_10x:	/* 2S, 2S1P */		data = 0xf7ff;		break;	default:			/* 1S1P, 4S */		data = 0xfffb;		break;	}	p = ioremap(pci_resource_start(dev, 0), 0x80);	if (p == NULL)		return -ENOMEM;	writew(readw(p + 0x28) & data, p + 0x28);	readw(p + 0x28);	iounmap(p);	return 0;}#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)static int pci_siig20x_init(struct pci_dev *dev){	u8 data;	/* Change clock frequency for the first UART. */	pci_read_config_byte(dev, 0x6f, &data);	pci_write_config_byte(dev, 0x6f, data & 0xef);	/* If this card has 2 UART, we have to do the same with second UART. */	if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||	    ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {		pci_read_config_byte(dev, 0x73, &data);		pci_write_config_byte(dev, 0x73, data & 0xef);	}	return 0;}int pci_siig10x_fn(struct pci_dev *dev, int enable){	int ret = 0;	if (enable)		ret = pci_siig10x_init(dev);	return ret;}int pci_siig20x_fn(struct pci_dev *dev, int enable){	int ret = 0;	if (enable)		ret = pci_siig20x_init(dev);	return ret;}EXPORT_SYMBOL(pci_siig10x_fn);EXPORT_SYMBOL(pci_siig20x_fn);/* * Timedia has an explosion of boards, and to avoid the PCI table from * growing *huge*, we use this function to collapse some 70 entries * in the PCI table into one, for sanity's and compactness's sake. */static unsigned short timedia_single_port[] = {	0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0};static unsigned short timedia_dual_port[] = {	0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,	0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, 	0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, 	0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,	0xD079, 0};static unsigned short timedia_quad_port[] = {	0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, 	0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, 	0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,	0xB157, 0};static unsigned short timedia_eight_port[] = {	0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, 	0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0};static struct timedia_struct {	int num;	unsigned short *ids;} timedia_data[] = {	{ 1, timedia_single_port },	{ 2, timedia_dual_port },	{ 4, timedia_quad_port },	{ 8, timedia_eight_port },	{ 0, NULL }};static int __devinit pci_timedia_init(struct pci_dev *dev){	unsigned short *ids;	int i, j;	for (i = 0; timedia_data[i].num; i++) {		ids = timedia_data[i].ids;		for (j = 0; ids[j]; j++)			if (dev->subsystem_device == ids[j])				return timedia_data[i].num;	}	return 0;}/* * Timedia/SUNIX uses a mixture of BARs and offsets * Ugh, this is ugly as all hell --- TYT */static intpci_timedia_setup(struct pci_dev *dev, struct pci_board *board,		  struct serial_struct *req, int idx){	unsigned int bar = 0, offset = board->first_offset;	switch (idx) {	case 0:		bar = 0;		break;	case 1:		offset = board->uart_offset;		bar = 0;		break;	case 2:		bar = 1;		break;	case 3:		offset = board->uart_offset;		bar = 1;	case 4: /* BAR 2 */	case 5: /* BAR 3 */	case 6: /* BAR 4 */	case 7: /* BAR 5 */		bar = idx - 2;	}	return setup_port(dev, req, bar, offset, board->reg_shift);}/* * Some Titan cards are also a little weird */static inttitan_400l_800l_setup(struct pci_dev *dev, struct pci_board *board,		      struct serial_struct *req, int idx){	unsigned int bar, offset = board->first_offset;

⌨️ 快捷键说明

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