📄 8250_pci.c
字号:
/* * linux/drivers/char/serial_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.8.2.1 2002/10/24 09:53:24 rmk Exp $ */#include <linux/config.h>#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/serial.h>/* 2.4.6 compatibility cruft ;( */#define pci_board __pci_board#include <linux/serialP.h>#undef pci_board#include <asm/bitops.h>#include <asm/byteorder.h>#include <asm/serial.h>#include "serial_8250.h"#ifndef IS_PCI_REGION_IOPORT#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_IO)#endif#ifndef IS_PCI_REGION_IOMEM#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_MEM)#endif#ifndef PCI_IRQ_RESOURCE#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)#endif#ifndef pci_get_subvendor#define pci_get_subvendor(dev) ((dev)->subsystem_vendor)#define pci_get_subdevice(dev) ((dev)->subsystem_device)#endifstruct serial_private { unsigned int nr; struct pci_board *board; int line[0];};struct pci_board { int flags; int num_ports; int base_baud; int uart_offset; int reg_shift; int (*init_fn)(struct pci_dev *dev, struct pci_board *board, int enable); int first_uart_offset;};static intget_pci_port(struct pci_dev *dev, struct pci_board *board, struct serial_struct *req, int idx){ unsigned long port; int base_idx; int max_port; int offset; base_idx = SPCI_FL_GET_BASE(board->flags); if (board->flags & SPCI_FL_BASE_TABLE) base_idx += idx; if (board->flags & SPCI_FL_REGION_SZ_CAP) { max_port = pci_resource_len(dev, base_idx) / 8; if (idx >= max_port) return 1; } offset = board->first_uart_offset; /* Timedia/SUNIX uses a mixture of BARs and offsets */ /* Ugh, this is ugly as all hell --- TYT */ if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ switch(idx) { case 0: base_idx=0; break; case 1: base_idx=0; offset=8; break; case 2: base_idx=1; break; case 3: base_idx=1; offset=8; break; case 4: /* BAR 2*/ case 5: /* BAR 3 */ case 6: /* BAR 4*/ case 7: base_idx=idx-2; /* BAR 5*/ } /* Some Titan cards are also a little weird */ if (dev->vendor == PCI_VENDOR_ID_TITAN && (dev->device == PCI_DEVICE_ID_TITAN_400L || dev->device == PCI_DEVICE_ID_TITAN_800L)) { switch (idx) { case 0: base_idx = 1; break; case 1: base_idx = 2; break; default: base_idx = 4; offset = 8 * (idx - 2); } } port = pci_resource_start(dev, base_idx) + offset; if ((board->flags & SPCI_FL_BASE_TABLE) == 0) port += idx * (board->uart_offset ? board->uart_offset : 8); if (IS_PCI_REGION_IOPORT(dev, base_idx)) { req->port = port; if (HIGH_BITS_OFFSET) req->port_high = port >> HIGH_BITS_OFFSET; else req->port_high = 0; return 0; } req->io_type = SERIAL_IO_MEM; req->iomem_base = ioremap(port, board->uart_offset); req->iomem_reg_shift = board->reg_shift; req->port = 0; return 0;}static _INLINE_ int get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx){ int base_idx; if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) return dev->irq; base_idx = SPCI_FL_GET_IRQBASE(board->flags); if (board->flags & SPCI_FL_IRQ_TABLE) base_idx += idx; return PCI_IRQ_RESOURCE(dev, base_idx);}/* * 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 __devinitpci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable){ u8 data, *p, irq_config; int pci_config; irq_config = 0x41; pci_config = PCI_COMMAND_MEMORY; 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; pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; } pci_read_config_byte(dev, PCI_COMMAND, &data); if (enable) pci_write_config_byte(dev, PCI_COMMAND, data | pci_config); /* enable/disable interrupts */ p = ioremap(pci_resource_start(dev, 0), 0x80); writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); iounmap(p); if (!enable) pci_write_config_byte(dev, PCI_COMMAND, data & ~pci_config); return 0;}/* * 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@mail.tp.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. */#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 __devinitpci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable){ u16 data, *p; if (!enable) return 0; p = ioremap(pci_resource_start(dev, 0), 0x80); 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; } writew(readw((unsigned long) p + 0x28) & data, (unsigned long) 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 __devinitpci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable){ u8 data; if (!enable) return 0; /* 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;}/* Added for EKF Intel i960 serial boards */static int __devinitpci_inteli960ni_fn(struct pci_dev *dev, struct pci_board *board, int enable){ unsigned long oldval; if (!(pci_get_subdevice(dev) & 0x1000)) return(-1); if (!enable) /* is there something to deinit? */ return(0); /* is firmware started? */ pci_read_config_dword(dev, 0x44, (void*) &oldval); if (oldval == 0x00001000L) { /* RESET value */ printk(KERN_DEBUG "Local i960 firmware missing"); return(-1); } return(0);}/* * 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, 0 }};static int __devinitpci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable){ int i, j; unsigned short *ids; if (!enable) return 0; for (i=0; timedia_data[i].num; i++) { ids = timedia_data[i].ids; for (j=0; ids[j]; j++) { if (pci_get_subdevice(dev) == ids[j]) { board->num_ports = timedia_data[i].num; return 0; } } } return 0;}static int __devinitpci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable){ __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -