📄 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 "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)
#endif
struct 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 int
get_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 __devinit
pci_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 __devinit
pci_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 __devinit
pci_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 __devinit
pci_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 __devinit
pci_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 __devinit
pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
__set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -