1
0
mirror of https://github.com/sjlongland/adv950.git synced 2025-09-14 19:23:15 +10:00
adv950/driver/adv950_pci.c
Stuart Longland 87e8c9d69b
Rename symbols
Avoid possible clashes with the 8250 driver, now that we've effectively
forked it.
2017-03-05 16:13:21 +10:00

1873 lines
50 KiB
C

//******************************************************************************
//
// Copyright (c) 2011 Advantech Industrial Automation Group.
//
// Oxford PCI-954/952/16C950 with Advantech RS232/422/485 capacities
//
// 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.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59
// Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
//
//
//******************************************************************************
//***********************************************************************
// File: 8250_pci.c
//
// PLEASE SEE 8250.c FOR DETAIL
//***********************************************************************
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/serial_core.h>
#include <linux/8250_pci.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include "adv950_base.h"
#undef SERIAL_DEBUG_PCI
/*
* Advantech IAG PCI-954/16C950 cards
*
*/
#define ADVANTECH_16C950_VER "3.33"
#define ADVANTECH_16C950_DATE "11/07/2011"
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_ADVANTECH_PCI1600 0x1600 /* Internal */
#define PCI_DEVICE_ID_ADVANTECH_PCI1601 0x1601 /* Internal */
#define PCI_DEVICE_ID_ADVANTECH_PCI1602 0x1602 /* Internal */
#define PCI_DEVICE_ID_ADVANTECH_PCI1603 0x1603 /* Internal */
#define PCI_DEVICE_ID_ADVANTECH_PCI1604 0x1604 /* Internal */
#define PCI_DEVICE_ID_ADVANTECH_PCI16ff 0x16ff /* External */
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1601 0x1601
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1602 0x1602
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1610 0x1610
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1612 0x1612 /* Also for UNO-2059 */
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1620 0x1620
#define PCI_DEVICE_ID_ADVANTECH_PCI1600_1622 0x1622
#define PCI_DEVICE_ID_ADVANTECH_UNO2050 0x2050
#define PCI_DEVICE_ID_ADVANTECH_UNOB2201 0x2201 //2668
#define PCI_DEVICE_ID_ADVANTECH_UNOBF201 0xf201 //2668
#define PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201 0x2201 //2668
#define PCI_DEVICE_ID_ADVANTECH_MIC3620 0x3620
#define PCI_DEVICE_ID_ADVANTECH_MIC3612 0X3612
#define PCI_DEVICE_ID_ADVANTECH_MIC3611 0X3611
#define PCI_DEVICE_ID_ADVANTECH_UNO2176 0x2176
#define PCI_DEVICE_ID_ADVANTECH_UNO2X76_2176 0x2176
#define PCI_DEVICE_ID_ADVANTECH_PCIE952 0xA202
#define PCI_DEVICE_ID_ADVANTECH_PCIE954 0xA304
#define PCI_DEVICE_ID_ADVANTECH_PCIE958 0xA408
#define PCI_DEVICE_ID_ADVANTECH_PCM3614P 0x3614 //PCM-3614P
#define PCI_DEVICE_ID_ADVANTECH_PCM3641P 0x3641 //PCM-3641P
#define PCI_DEVICE_ID_ADVANTECH_PCM3618P 0x3618 //PCM-3618P
#define PCI_DEVICE_ID_ADVANTECH_PCMF618P 0xF618 //PCM-3618P
#define PCI_DEVICE_ID_ADVANTECH_PCM3681P 0x3681 //PCM-3681P
#define PCI_DEVICE_ID_ADVANTECH_PCMF681P 0xF681 //PCM-3681P
#define PCI_SUB_VENDOR_ID_ADVANTECH_PCM3614P 0x3614 //PCM-3614P
#define PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P 0x3618 //PCM-3618P
#define PCI_SUB_VENDOR_ID_ADVANTECH_PCM3641P 0x3641 //PCM-3641P
#define PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P 0x3681 //PCM-3681P
#define PCI_DEVICE_ID_ADVANTECH_UNO1150 0x3610 //UNO-1150
#define PCI_DEVICE_ID_ADVANTECH_MIC3621 0x3621 //PCM-3614P
#define PCI_SUB_VENDOR_ID_ADVANTECH_MIC3621 0x3621 //PCM-3614P
#define PCI_DEVICE_ID_ADVANTECH_A001 0xA001
#define PCI_DEVICE_ID_ADVANTECH_A002 0xA002
#define PCI_DEVICE_ID_ADVANTECH_A004 0xA004
#define PCI_DEVICE_ID_ADVANTECH_A101 0xA101
#define PCI_DEVICE_ID_ADVANTECH_A102 0xA102
#define PCI_DEVICE_ID_ADVANTECH_A104 0xA104
#define PCI_DEVICE_ID_ADVANTECH_F001 0xF001
#define PCI_DEVICE_ID_ADVANTECH_F002 0xF002
#define PCI_DEVICE_ID_ADVANTECH_F004 0xF004
#define PCI_DEVICE_ID_ADVANTECH_F101 0xF101
#define PCI_DEVICE_ID_ADVANTECH_F102 0xF102
#define PCI_DEVICE_ID_ADVANTECH_F104 0xF104
#define ACR_DTR_RS232 0x00
#define ACR_DTR_ACTIVE_LOW_RS485 0x10
#define ACR_DTR_ACTIVE_HIGH_RS485 0x18
#define UART_TYPE_AUTO 0
#define UART_TYPE_RS232 1
#define UART_TYPE_RS485 2
static char * product_line[] = {"GENERAL","PCI","PCM","ADAM","APAX","BAS","UNO","TPC","EAMB"};
/*
* 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 serial_private *,
const struct pciserial_board *,
struct uart_port *, int);
void (*exit)(struct pci_dev *dev);
};
#define PCI_NUM_BAR_RESOURCES 6
struct serial_private {
struct pci_dev *dev;
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"
"Please send the output of lspci -vv, this\n"
"message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
"manufacturer and name of serial board or\n"
"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 int
setup_port(struct serial_private *priv, struct uart_port *port,
int bar, int offset, int regshift)
{
struct pci_dev *dev = priv->dev;
unsigned long base, len;
if (bar >= PCI_NUM_BAR_RESOURCES)
return -EINVAL;
base = pci_resource_start(dev, bar);
if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
len = pci_resource_len(dev, bar);
if (!priv->remapped_bar[bar])
priv->remapped_bar[bar] = ioremap_nocache(base, len);
if (!priv->remapped_bar[bar])
return -ENOMEM;
port->iotype = UPIO_MEM;
port->iobase = 0;
port->mapbase = base + offset;
port->membase = priv->remapped_bar[bar] + offset;
port->regshift = regshift;
} else {
port->iotype = UPIO_PORT;
port->iobase = base + offset;
port->mapbase = 0;
port->membase = NULL;
port->regshift = 0;
}
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 const unsigned short timedia_single_port[] = {
0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0
};
static const 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 const 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 const unsigned short timedia_eight_port[] = {
0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166,
0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0
};
static const struct timedia_struct {
int num;
const unsigned short *ids;
} timedia_data[] = {
{ 1, timedia_single_port },
{ 2, timedia_dual_port },
{ 4, timedia_quad_port },
{ 8, timedia_eight_port }
};
#define MITE_IOWBSR1_WSIZE 0xa
#define MITE_IOWBSR1_WIN_OFFSET 0x800
#define MITE_IOWBSR1_WENAB (1 << 7)
#define MITE_LCIMR1_IO_IE_0 (1 << 24)
#define MITE_LCIMR2_SET_CPU_IE (1 << 31)
#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe
/* UART Port Control Register */
#define NI8430_PORTCON 0x0f
#define NI8430_PORTCON_TXVR_ENABLE (1 << 3)
/*
* These chips are available with optionally one parallel port and up to
* two serial ports. Unfortunately they all have the same product id.
*
* Basic configuration is done over a region of 32 I/O ports. The base
* ioport is called INTA or INTC, depending on docs/other drivers.
*
* The region of the 32 I/O ports is configured in POSIO0R...
*/
/* registers */
#define ITE_887x_MISCR 0x9c
#define ITE_887x_INTCBAR 0x78
#define ITE_887x_UARTBAR 0x7c
#define ITE_887x_PS0BAR 0x10
#define ITE_887x_POSIO0 0x60
/* I/O space size */
#define ITE_887x_IOSIZE 32
/* I/O space size (bits 26-24; 8 bytes = 011b) */
#define ITE_887x_POSIO_IOSIZE_8 (3 << 24)
/* I/O space size (bits 26-24; 32 bytes = 101b) */
#define ITE_887x_POSIO_IOSIZE_32 (5 << 24)
/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */
#define ITE_887x_POSIO_SPEED (3 << 29)
/* enable IO_Space bit */
#define ITE_887x_POSIO_ENABLE (1 << 31)
static int
pci_default_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_port *port, int idx)
{
unsigned int bar, offset = board->first_offset, maxnr;
bar = FL_GET_BASE(board->flags);
if (board->flags & FL_BASE_BARS)
bar += idx;
else
offset += idx * board->uart_offset;
maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
(board->reg_shift + 3);
if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
return 1;
return setup_port(priv, port, bar, offset, board->reg_shift);
}
/*
* Advantech IAG PCI-954/16C950 cards
*/
//static int
//pci_advantech_setup(struct pci_dev *dev, struct pci_board *board,
// struct serial_struct *req, int idx)
static int
pci_advantech_setup (struct serial_private *priv,
const struct pciserial_board *board,
struct uart_port *port, int idx)
{
u32 bar, port485, offset485;
u32 len;
void __iomem * remap;
u8 config485, activeType, configFunc, configType;
u16 config485_958;
struct pci_dev *cfgdev = NULL;
int base_idx=0;
int rc;
struct pci_dev *dev = NULL;
configFunc = 1; // Default configuration BAR is function 1
offset485 = 0x60; // Default offset to get RS232/422/485 configuration
bar = PCI_BASE_ADDRESS_0; // Default BAR is PCI_BASE_ADDRESS_0
activeType = ACR_DTR_ACTIVE_HIGH_RS485; // Default RS485 is active high
configType = UART_TYPE_AUTO; // Default UART type is auto detection
dev = priv->dev;
switch(dev->subsystem_vendor)
{
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1601:
printk("PCI-1601");
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1602:
printk("PCI-1602");
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1610:
printk("PCI-1610");
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1612: /* Also for UNO-2059 */
printk("PCI-1612 / UNO-2059");
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1620:
printk("PCI-1620");
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1600_1622:
printk("PCI-1622CU");
break;
case PCI_DEVICE_ID_ADVANTECH_UNO2050:
printk("UNO-2050");
offset485 = 0x18;
break;
case PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201:
printk("UNOB-2201CB");
activeType = ACR_DTR_ACTIVE_LOW_RS485;
break;
case PCI_DEVICE_ID_ADVANTECH_UNO2X76_2176:
printk( "UNO-2176" );
activeType = ACR_DTR_ACTIVE_LOW_RS485;
break;
case PCI_DEVICE_ID_ADVANTECH_MIC3612:
printk("MIC-3612");
configFunc = 0;
bar = PCI_BASE_ADDRESS_2;
break;
case PCI_DEVICE_ID_ADVANTECH_MIC3621:
printk("MIC-3621");
configFunc = 0;
bar = PCI_BASE_ADDRESS_2;
break;
case PCI_DEVICE_ID_ADVANTECH_MIC3620:
printk("MIC-3620");
configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_MIC3611:
printk("MIC-3611");
configFunc = 0;
bar = PCI_BASE_ADDRESS_2;
configType = UART_TYPE_RS485;
break;
case PCI_SUB_VENDOR_ID_ADVANTECH_PCM3614P:
printk( "PCM-3614P" );
activeType = ACR_DTR_ACTIVE_LOW_RS485;
break;
case PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P:
printk( "PCM-3618P" );
activeType = ACR_DTR_ACTIVE_LOW_RS485;
break;
case PCI_SUB_VENDOR_ID_ADVANTECH_PCM3641P:
printk( "PCM-3641P" );
configType = UART_TYPE_RS232;
break;
case PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P:
printk( "PCM-3681P" );
configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_UNO1150:
printk( "UNO-1150" );
configFunc = 0;
base_idx = 2;
bar = PCI_BASE_ADDRESS_2;
offset485 = 0x10;
activeType = ACR_DTR_ACTIVE_LOW_RS485;
break;
case PCI_VENDOR_ID_ADVANTECH:
switch(dev->device)
{
case PCI_DEVICE_ID_ADVANTECH_PCIE952:
printk( "PCIE952");
port->unused[1] |= 0x01;//this bit means to use new way to calculate baudrate
port->unused[1] |= 0x02;//have DMA
port->unused[1] |= 0x10;//is PCIe952/4/8
configFunc = 0;
base_idx = 13;//
bar = PCI_BASE_ADDRESS_0;//ok
offset485 = 0x100;//
activeType = ACR_DTR_ACTIVE_LOW_RS485;//
//configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_PCIE954:
printk( "PCIE954");
port->unused[1] |= 0x01;
port->unused[1] |= 0x02;
port->unused[1] |= 0x10;
configFunc = 0;
base_idx = 13;//
bar = PCI_BASE_ADDRESS_0;//ok
offset485 = 0x100;//
activeType = ACR_DTR_ACTIVE_LOW_RS485;//
//configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_PCIE958:
printk( "PCIE958");
port->unused[1] |= 0x01;
port->unused[1] |= 0x02;
port->unused[1] |= 0x10;
configFunc = 0;
base_idx = 13;//
bar = PCI_BASE_ADDRESS_0;//ok
offset485 = 0x100;//
activeType = ACR_DTR_ACTIVE_LOW_RS485;//
//configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1601:
printk("PCI-1601A/B/AU/BU");
configType = UART_TYPE_RS485;
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1602:
printk("PCI-1602A/B/AU/BU/UP");
configType = UART_TYPE_RS485;
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1603:
printk("PCI-1603");
configType = UART_TYPE_RS232;
break;
case PCI_DEVICE_ID_ADVANTECH_PCI1604:
printk("PCI-1604UP");
configType = UART_TYPE_RS232;
break;
}
break;
}
if (dev->device == PCI_DEVICE_ID_ADVANTECH_A001
|| dev->device == PCI_DEVICE_ID_ADVANTECH_A002
|| dev->device == PCI_DEVICE_ID_ADVANTECH_A004
|| dev->device == PCI_DEVICE_ID_ADVANTECH_A101
|| dev->device == PCI_DEVICE_ID_ADVANTECH_A102
|| dev->device == PCI_DEVICE_ID_ADVANTECH_A104
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F001
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F002
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F004
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F101
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F102
|| dev->device == PCI_DEVICE_ID_ADVANTECH_F104)
{
activeType = ACR_DTR_ACTIVE_LOW_RS485;
if (dev->subsystem_vendor != PCI_VENDOR_ID_ADVANTECH)
{
printk("%s-%04x",product_line[dev->subsystem_vendor],dev->subsystem_device);
}
else
{
printk("Advantech General COM Port Device");
}
}
if(configType == UART_TYPE_RS232)
{
port->unused[0] = ACR_DTR_RS232;
}
else if(configType == UART_TYPE_RS485)
{
port->unused[0] = activeType;
}
else // UART_TYPE_AUTO
{
// find RS232/422/485 configuration BAR
do {
cfgdev = pci_get_device(PCI_VENDOR_ID_ADVANTECH,
PCI_ANY_ID, cfgdev);
if ((dev->bus->number == cfgdev->bus->number) &&
(PCI_SLOT(dev->devfn) == PCI_SLOT(cfgdev->devfn)) &&
(PCI_FUNC(cfgdev->devfn) == configFunc))
{
pci_read_config_dword(cfgdev, bar, &port485);
if((port485 & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_IO)
{
rc = pci_enable_device(cfgdev);
if (rc)
return rc;
port485 &= PCI_BASE_ADDRESS_IO_MASK;
break;
}
break;
}
} while(cfgdev != NULL);
// if cannot get RS232/422/485 configuration port
if(!cfgdev)
{
printk("%x: cannot get RS232/422/485 configuration!\n",
dev->subsystem_vendor);
return -ENODEV;
}
if(port->unused[1] & 0x10){
len = pci_resource_len(cfgdev, ((bar-0x10)/0x04));
if (pci_resource_flags(cfgdev, ((bar-0x10)/0x04)) & IORESOURCE_MEM) {
remap = ioremap(port485, len);
config485_958 = readw(remap + offset485 + idx*0x10);
//printk(KERN_INFO "configure register = %x\n", config485_958);
port->unused[0] = (config485_958 & (0x01 << base_idx)) ?
activeType : ACR_DTR_RS232;
goto done;
}
else{
config485_958 = inw(port485 + offset485 + idx*0x10);
port->unused[0] = (config485_958 & (0x01 << base_idx)) ?
activeType : ACR_DTR_RS232;
goto done;
}
}
// read RS232/422/485 configuration value
config485 = inb(port485 + offset485);
if(PCI_FUNC(dev->devfn) == 1) base_idx=4;
port->unused[0] = (config485 & (0x01 << (base_idx+idx))) ?
activeType : ACR_DTR_RS232;
}
done:
printk(", function %d, port %d, %s",
PCI_FUNC(dev->devfn), idx,
(port->unused[0] != ACR_DTR_RS232) ?
"RS422/485" : "RS232");
if(port->unused[0] != ACR_DTR_RS232)
printk(", %s\n", (activeType == ACR_DTR_ACTIVE_HIGH_RS485) ?
"Active High" : "Active Low");
else
printk("\n");
return pci_default_setup(priv, board, port, idx);
}
/* This should be in linux/pci_ids.h */
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B
#define PCI_DEVICE_ID_OCTPRO 0x0001
#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108
#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
/*
* Master list of serial port init/setup/exit quirks.
* This does not describe the general nature of the port.
* (ie, baud base, number and location of ports, etc)
*
* This list is ordered alphabetically by vendor then device.
* Specific entries must come before more generic entries.
*/
static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
/*
* Advantech IAG PCI-954/16C950 cards
*/
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1601,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1602,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1610,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1612,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1620,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI16ff,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1620,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1600,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1622,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI16ff,
.subvendor = PCI_DEVICE_ID_ADVANTECH_PCI1600_1622,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_UNO2050,
.subvendor = PCI_DEVICE_ID_ADVANTECH_UNO2050,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_UNOB2201,
.subvendor = PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_UNOBF201,
.subvendor = PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_MIC3612,
.subvendor = PCI_DEVICE_ID_ADVANTECH_MIC3612,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_MIC3611,
.subvendor = PCI_DEVICE_ID_ADVANTECH_MIC3611,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_MIC3620,
.subvendor = PCI_DEVICE_ID_ADVANTECH_MIC3620,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1601,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1602,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1603,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCI1604,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_UNO2176,
.subvendor = PCI_DEVICE_ID_ADVANTECH_UNO2X76_2176,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCIE952,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_DEVICE_ID_ADVANTECH_PCIE952,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCIE954,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_DEVICE_ID_ADVANTECH_PCIE954,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCIE958,
.subvendor = PCI_VENDOR_ID_ADVANTECH,
.subdevice = PCI_DEVICE_ID_ADVANTECH_PCIE958,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCM3614P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3614P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCM3618P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCMF618P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCM3641P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3641P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCM3681P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_PCMF681P,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_UNO1150,
.subvendor = PCI_DEVICE_ID_ADVANTECH_UNO1150,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_MIC3621,
.subvendor = PCI_SUB_VENDOR_ID_ADVANTECH_MIC3621,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A001,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A002,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A004,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A102,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_A104,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F001,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F002,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F004,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F102,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
{
.vendor = PCI_VENDOR_ID_ADVANTECH,
.device = PCI_DEVICE_ID_ADVANTECH_F104,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_advantech_setup,
},
};
static inline int quirk_id_matches(u32 quirk_id, u32 dev_id)
{
return quirk_id == PCI_ANY_ID || quirk_id == dev_id;
}
static struct pci_serial_quirk *find_quirk(struct pci_dev *dev)
{
struct pci_serial_quirk *quirk;
for (quirk = pci_serial_quirks; ; quirk++)
if (quirk_id_matches(quirk->vendor, dev->vendor) &&
quirk_id_matches(quirk->device, dev->device) &&
quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) &&
quirk_id_matches(quirk->subdevice, dev->subsystem_device))
break;
return quirk;
}
static inline int get_pci_irq(struct pci_dev *dev,
const struct pciserial_board *board)
{
if (board->flags & FL_NOIRQ)
return 0;
else
return dev->irq;
}
/*
* This is the configuration table for all of the PCI serial boards
* which we support. It is directly indexed by the pci_board_num_t enum
* value, which is encoded in the pci_device_id PCI probe table's
* driver_data member.
*
* The makeup of these names are:
* pbn_bn{_bt}_n_baud{_offsetinhex}
*
* bn = PCI BAR number
* bt = Index using PCI BARs
* n = number of serial ports
* baud = baud rate
* offsetinhex = offset for each sequential port (in hex)
*
* This table is sorted by (in order): bn, bt, baud, offsetindex, n.
*
* Please note: in theory if n = 1, _bt infix should make no difference.
* ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200
*/
enum pci_board_num_t {
pbn_default = 0,
pbn_b0_1_115200,
pbn_b0_2_115200,
pbn_b0_4_115200,
pbn_b0_5_115200,
pbn_b0_1_921600,
pbn_b0_2_921600,
pbn_b0_4_921600,
pbn_b0_2_d_921600,
pbn_b0_4_d_921600,
pbn_b0_8_d_921600,
pbn_b0_bt_1_115200,
pbn_b0_bt_2_115200,
pbn_b0_bt_8_115200,
pbn_b0_bt_1_460800,
pbn_b0_bt_2_460800,
pbn_b0_bt_4_460800,
pbn_b0_bt_1_921600,
pbn_b0_bt_2_921600,
pbn_b0_bt_4_921600,
pbn_b0_bt_8_921600,
pbn_b1_1_115200,
pbn_b1_2_115200,
pbn_b1_4_115200,
pbn_b1_8_115200,
pbn_b1_1_921600,
pbn_b1_2_921600,
pbn_b1_4_921600,
pbn_b1_8_921600,
pbn_b1_bt_2_921600,
pbn_b1_2_1382400,
pbn_b1_4_1382400,
pbn_b1_8_1382400,
pbn_b2_1_115200,
pbn_b2_8_115200,
pbn_b2_1_460800,
pbn_b2_4_460800,
pbn_b2_8_460800,
pbn_b2_16_460800,
pbn_b2_1_921600,
pbn_b2_4_921600,
pbn_b2_8_921600,
pbn_b2_bt_1_115200,
pbn_b2_bt_2_115200,
pbn_b2_bt_4_115200,
pbn_b2_bt_2_921600,
pbn_b2_bt_4_921600,
pbn_b3_4_115200,
pbn_b3_8_115200,
};
/*
* uart_offset - the space between channels
* reg_shift - describes how the UART registers are mapped
* to PCI memory by the card.
* For example IER register on SBS, Inc. PMC-OctPro is located at
* offset 0x10 from the UART base, while UART_IER is defined as 1
* in include/linux/serial_reg.h,
* see first lines of serial_in() and serial_out() in 8250.c
*/
static struct pciserial_board pci_boards[] = {
[pbn_default] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_1_115200] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_2_115200] = {
.flags = FL_BASE0,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_4_115200] = {
.flags = FL_BASE0,
.num_ports = 4,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_5_115200] = {
.flags = FL_BASE0,
.num_ports = 5,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_1_921600] = {
.flags = FL_BASE0,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_2_921600] = {
.flags = FL_BASE0,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_4_921600] = {
.flags = FL_BASE0,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_2_d_921600] = {
.flags = FL_BASE0,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
[pbn_b0_4_d_921600] = {
.flags = FL_BASE0,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
[pbn_b0_8_d_921600] = {
.flags = FL_BASE0,
.num_ports = 8,
.base_baud = 921600,
.uart_offset = 0x200,
.first_offset = 0x1000,
},
[pbn_b0_bt_1_115200] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_bt_2_115200] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_bt_8_115200] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 8,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b0_bt_1_460800] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b0_bt_2_460800] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b0_bt_4_460800] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 4,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b0_bt_1_921600] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_bt_2_921600] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_bt_4_921600] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b0_bt_8_921600] = {
.flags = FL_BASE0|FL_BASE_BARS,
.num_ports = 8,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_1_115200] = {
.flags = FL_BASE1,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b1_2_115200] = {
.flags = FL_BASE1,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b1_4_115200] = {
.flags = FL_BASE1,
.num_ports = 4,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b1_8_115200] = {
.flags = FL_BASE1,
.num_ports = 8,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b1_1_921600] = {
.flags = FL_BASE1,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_2_921600] = {
.flags = FL_BASE1,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_4_921600] = {
.flags = FL_BASE1,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_8_921600] = {
.flags = FL_BASE1,
.num_ports = 8,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_bt_2_921600] = {
.flags = FL_BASE1|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b1_2_1382400] = {
.flags = FL_BASE1,
.num_ports = 2,
.base_baud = 1382400,
.uart_offset = 8,
},
[pbn_b1_4_1382400] = {
.flags = FL_BASE1,
.num_ports = 4,
.base_baud = 1382400,
.uart_offset = 8,
},
[pbn_b1_8_1382400] = {
.flags = FL_BASE1,
.num_ports = 8,
.base_baud = 1382400,
.uart_offset = 8,
},
[pbn_b2_1_115200] = {
.flags = FL_BASE2,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b2_8_115200] = {
.flags = FL_BASE2,
.num_ports = 8,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b2_1_460800] = {
.flags = FL_BASE2,
.num_ports = 1,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b2_4_460800] = {
.flags = FL_BASE2,
.num_ports = 4,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b2_8_460800] = {
.flags = FL_BASE2,
.num_ports = 8,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b2_16_460800] = {
.flags = FL_BASE2,
.num_ports = 16,
.base_baud = 460800,
.uart_offset = 8,
},
[pbn_b2_1_921600] = {
.flags = FL_BASE2,
.num_ports = 1,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b2_4_921600] = {
.flags = FL_BASE2,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b2_8_921600] = {
.flags = FL_BASE2,
.num_ports = 8,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b2_bt_1_115200] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 1,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b2_bt_2_115200] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b2_bt_4_115200] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 4,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b2_bt_2_921600] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 2,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b2_bt_4_921600] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 4,
.base_baud = 921600,
.uart_offset = 8,
},
[pbn_b3_4_115200] = {
.flags = FL_BASE3,
.num_ports = 4,
.base_baud = 115200,
.uart_offset = 8,
},
[pbn_b3_8_115200] = {
.flags = FL_BASE3,
.num_ports = 8,
.base_baud = 115200,
.uart_offset = 8,
},
};
static const struct pci_device_id softmodem_blacklist[] = {
{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
};
/*
* Given a complete unknown PCI device, try to use some heuristics to
* guess what the configuration might be, based on the pitiful PCI
* serial specs. Returns 0 on success, 1 on failure.
*/
static int
serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
{
const struct pci_device_id *blacklist;
int num_iomem, num_port, first_port = -1, i;
/*
* If it is not a communications device or the programming
* interface is greater than 6, give up.
*
* (Should we try to make guesses for multiport serial devices
* later?)
*/
if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
(dev->class & 0xff) > 6)
return -ENODEV;
/*
* Do not access blacklisted devices that are known not to
* feature serial ports.
*/
for (blacklist = softmodem_blacklist;
blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist);
blacklist++) {
if (dev->vendor == blacklist->vendor &&
dev->device == blacklist->device)
return -ENODEV;
}
num_iomem = num_port = 0;
for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
num_port++;
if (first_port == -1)
first_port = i;
}
if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
num_iomem++;
}
/*
* If there is 1 or 0 iomem regions, and exactly one port,
* use it. We guess the number of ports based on the IO
* region size.
*/
if (num_iomem <= 1 && num_port == 1) {
board->flags = first_port;
board->num_ports = pci_resource_len(dev, first_port) / 8;
return 0;
}
/*
* Now guess if we've got a board which indexes by BARs.
* Each IO BAR should be 8 bytes, and they should follow
* consecutively.
*/
first_port = -1;
num_port = 0;
for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
if (pci_resource_flags(dev, i) & IORESOURCE_IO &&
pci_resource_len(dev, i) == 8 &&
(first_port == -1 || (first_port + num_port) == i)) {
num_port++;
if (first_port == -1)
first_port = i;
}
}
if (num_port > 1) {
board->flags = first_port | FL_BASE_BARS;
board->num_ports = num_port;
return 0;
}
return -ENODEV;
}
static inline int
serial_pci_matches(const struct pciserial_board *board,
const struct pciserial_board *guessed)
{
return
board->num_ports == guessed->num_ports &&
board->base_baud == guessed->base_baud &&
board->uart_offset == guessed->uart_offset &&
board->reg_shift == guessed->reg_shift &&
board->first_offset == guessed->first_offset;
}
struct serial_private *
adv950_uart_pci_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
{
struct uart_port serial_port;
struct serial_private *priv;
struct pci_serial_quirk *quirk;
int rc, nr_ports, i;
nr_ports = board->num_ports;
/*
* Find an init and setup quirks.
*/
quirk = find_quirk(dev);
/*
* Run the new-style initialization function.
* The initialization function returns:
* <0 - error
* 0 - use board->num_ports
* >0 - number of ports
*/
if (quirk->init) {
rc = quirk->init(dev);
if (rc < 0) {
priv = ERR_PTR(rc);
goto err_out;
}
if (rc)
nr_ports = rc;
}
priv = kzalloc(sizeof(struct serial_private) +
sizeof(unsigned int) * nr_ports,
GFP_KERNEL);
if (!priv) {
priv = ERR_PTR(-ENOMEM);
goto err_deinit;
}
priv->dev = dev;
priv->quirk = quirk;
memset(&serial_port, 0, sizeof(struct uart_port));
serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
serial_port.uartclk = board->base_baud * 16;
serial_port.irq = get_pci_irq(dev, board);
serial_port.dev = &dev->dev;
for (i = 0; i < nr_ports; i++) {
if (quirk->setup(priv, board, &serial_port, i))
break;
#ifdef SERIAL_DEBUG_PCI
printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
serial_port.iobase, serial_port.irq, serial_port.iotype);
#endif
priv->line[i] = adv950_uart_register_port(&serial_port);
//printk("port->unused[0] = %d\n",serial_port.unused[0]);
if (priv->line[i] < 0) {
printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
break;
}
}
priv->nr = i;
return priv;
err_deinit:
if (quirk->exit)
quirk->exit(dev);
err_out:
return priv;
}
void adv950_uart_pci_remove_ports(struct serial_private *priv)
{
struct pci_serial_quirk *quirk;
int i;
for (i = 0; i < priv->nr; i++)
adv950_uart_unregister_port(priv->line[i]);
for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
if (priv->remapped_bar[i])
iounmap(priv->remapped_bar[i]);
priv->remapped_bar[i] = NULL;
}
/*
* Find the exit quirks.
*/
quirk = find_quirk(priv->dev);
if (quirk->exit)
quirk->exit(priv->dev);
kfree(priv);
}
void adv950_uart_pci_suspend_ports(struct serial_private *priv)
{
int i;
for (i = 0; i < priv->nr; i++)
if (priv->line[i] >= 0)
adv950_suspend_port(priv->line[i]);
}
void adv950_uart_pci_resume_ports(struct serial_private *priv)
{
int i;
/*
* Ensure that the board is correctly configured.
*/
if (priv->quirk->init)
priv->quirk->init(priv->dev);
for (i = 0; i < priv->nr; i++)
if (priv->line[i] >= 0)
adv950_resume_port(priv->line[i]);
}
/*
* Probe one serial board. Unfortunately, there is no rhyme nor reason
* to the arrangement of serial ports on a PCI card.
*/
static int
pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
struct serial_private *priv;
const struct pciserial_board *board;
struct pciserial_board tmp;
int rc;
if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
ent->driver_data);
return -EINVAL;
}
board = &pci_boards[ent->driver_data];
rc = pci_enable_device(dev);
if (rc)
return rc;
if (ent->driver_data == pbn_default) {
/*
* Use a copy of the pci_board entry for this;
* avoid changing entries in the table.
*/
memcpy(&tmp, board, sizeof(struct pciserial_board));
board = &tmp;
/*
* We matched one of our class entries. Try to
* determine the parameters of this board.
*/
rc = serial_pci_guess_board(dev, &tmp);
if (rc)
goto disable;
} else {
/*
* We matched an explicit entry. If we are able to
* detect this boards settings with our heuristic,
* then we no longer need this entry.
*/
memcpy(&tmp, &pci_boards[pbn_default],
sizeof(struct pciserial_board));
rc = serial_pci_guess_board(dev, &tmp);
if (rc == 0 && serial_pci_matches(board, &tmp))
moan_device("Redundant entry in serial pci_table.",
dev);
}
priv = adv950_uart_pci_init_ports(dev, board);
if (!IS_ERR(priv)) {
pci_set_drvdata(dev, priv);
return 0;
}
rc = PTR_ERR(priv);
disable:
pci_disable_device(dev);
return rc;
}
static void pciserial_remove_one(struct pci_dev *dev)
{
struct serial_private *priv = pci_get_drvdata(dev);
pci_set_drvdata(dev, NULL);
adv950_uart_pci_remove_ports(priv);
pci_disable_device(dev);
}
#ifdef CONFIG_PM
static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
{
struct serial_private *priv = pci_get_drvdata(dev);
if (priv)
pciserial_suspend_ports(priv);
pci_save_state(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
static int pciserial_resume_one(struct pci_dev *dev)
{
int err;
struct serial_private *priv = pci_get_drvdata(dev);
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
if (priv) {
/*
* The device may have been disabled. Re-enable it.
*/
err = pci_enable_device(dev);
/* FIXME: We cannot simply error out here */
if (err)
printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n");
adv950_uart_pci_resume_ports(priv);
}
return 0;
}
#endif
static struct pci_device_id serial_pci_tbl[] = {
/*
* Advantech IAG PCI-954/16C950 cards
*/
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1601, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1602, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1610, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1612, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1620, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI16ff,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1620, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1600,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1622, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI16ff,
PCI_DEVICE_ID_ADVANTECH_PCI1600_1622, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_UNO2050,
PCI_DEVICE_ID_ADVANTECH_UNO2050, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_UNOB2201,
PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_UNOBF201,
PCI_DEVICE_ID_ADVANTECH_UNOBX201_2201, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_MIC3612,
PCI_DEVICE_ID_ADVANTECH_MIC3612, PCI_ANY_ID, 0, 0,
pbn_b2_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_MIC3620,
PCI_DEVICE_ID_ADVANTECH_MIC3620, PCI_ANY_ID, 0, 0,
pbn_b2_8_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_MIC3611,
PCI_DEVICE_ID_ADVANTECH_MIC3611, PCI_ANY_ID, 0, 0,
pbn_b2_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1601,
PCI_VENDOR_ID_ADVANTECH, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1602,
PCI_VENDOR_ID_ADVANTECH, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1603,
PCI_VENDOR_ID_ADVANTECH, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI1604,
PCI_VENDOR_ID_ADVANTECH, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_UNO2176,
PCI_DEVICE_ID_ADVANTECH_UNO2X76_2176, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIE952,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_d_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIE954,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_d_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIE958,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_8_d_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCM3614P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3614P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCM3618P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCMF618P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3618P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCM3641P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3641P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCM3681P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600},
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCMF681P,
PCI_SUB_VENDOR_ID_ADVANTECH_PCM3681P, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_UNO1150,
PCI_DEVICE_ID_ADVANTECH_UNO1150, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_MIC3621,
PCI_SUB_VENDOR_ID_ADVANTECH_MIC3621, PCI_ANY_ID, 0, 0,
pbn_b2_8_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A001,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_1_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A002,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A004,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A101,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_1_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A102,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_A104,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F001,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_1_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F002,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F004,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F101,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_1_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F102,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
{ PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_F104,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_4_921600 },
{ 0, }
};
static struct pci_driver serial_pci_driver = {
.name = "advserial",
.probe = pciserial_init_one,
.remove = pciserial_remove_one,
#ifdef CONFIG_PM
.suspend = pciserial_suspend_one,
.resume = pciserial_resume_one,
#endif
.id_table = serial_pci_tbl,
};
static int __init adv950_uart_pci_init(void)
{
printk("\n");
printk("==========================================================="
"====\n");
printk("Advantech PCI-954/952/16C950 Device Drivers. V%s [%s]\n",
ADVANTECH_16C950_VER, ADVANTECH_16C950_DATE);
printk("Supports: RS232/422/485 auto detection and setting\n");
printk("Devices: UNO: UNO2050 [COM3/COM4]\n");
printk(" UNO2059 [COM1~COM4]\n");
printk(" UNOB-2201CB [COM1~COM8]\n");
printk(" UNOB-2176 [COM1~COM4]\n");
printk(" UNO-1150 [COM2/COM3]\n");
printk(" UNO-2679 [COM3~COM6]\n");
printk(" UNO-4672 [COM3~COM10]\n");
printk(" ICOM: PCI-1601, PCI-1602\n"
" PCI-1603, PCI-1604\n"
" PCI-1610, PCI-1612\n"
" PCI-1620, PCI-1622\n");
printk(" MIC: MIC-3611, MIC-3612\n");
printk(" MIC-3620, MIC-3621\n");
printk(" PCM: PCM-3614P/I, PCM-3641P/I\n");
printk(" PCM-3618P/I, PCM-3681P/I\n");
printk(" General: A001, A002, A004\n");
printk(" A101, A102, A104\n");
printk(" F001, F002, F004\n");
printk(" F101, F102, F104\n");
printk(" A202, A304, A408\n");
printk("Advantech Industrial Automation Group.\n");
printk("==========================================================="
"====\n");
if(adv950_uart_init() >= 0)
return pci_register_driver(&serial_pci_driver);
return -ENODEV;
}
static void __exit adv950_uart_pci_exit(void)
{
pci_unregister_driver(&serial_pci_driver);
adv950_uart_exit();
}
EXPORT_SYMBOL_GPL(adv950_uart_pci_remove_ports);
EXPORT_SYMBOL_GPL(adv950_uart_pci_suspend_ports);
EXPORT_SYMBOL_GPL(adv950_uart_pci_resume_ports);
EXPORT_SYMBOL_GPL(adv950_uart_pci_init_ports);
module_init(adv950_uart_pci_init);
module_exit(adv950_uart_pci_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
MODULE_DEVICE_TABLE(pci, serial_pci_tbl);