Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

8250_pci.c

/*
 *  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.
 */
#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 "8250.h"

#undef SERIAL_DEBUG_PCI

/*
 * init function returns:
 *  > 0 - number of ports
 *  = 0 - use board->num_ports
 *  < 0 - error
 */
00039 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

00053 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;
}

/*
 * ADDI-DATA GmbH communication cards <info@addi-data.com>
 */
static int addidata_apci7800_setup(struct serial_private *priv,
                        const struct pciserial_board *board,
                        struct uart_port *port, int idx)
{
      unsigned int bar = 0, offset = board->first_offset;
      bar = FL_GET_BASE(board->flags);

      if (idx < 2) {
            offset += idx * board->uart_offset;
      } else if ((idx >= 2) && (idx < 4)) {
            bar += 1;
            offset += ((idx - 2) * board->uart_offset);
      } else if ((idx >= 4) && (idx < 6)) {
            bar += 2;
            offset += ((idx - 4) * board->uart_offset);
      } else if (idx >= 6) {
            bar += 3;
            offset += ((idx - 6) * board->uart_offset);
      }

      return setup_port(priv, port, bar, offset, board->reg_shift);
}

/*
 * AFAVLAB uses a different mixture of BARs and offsets
 * Not that ugly ;) -- HW
 */
static int
afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
            struct uart_port *port, 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(priv, port, 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 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:
      case PCI_DEVICE_ID_HP_DIVA_HURRICANE:
            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 int
pci_hp_diva_setup(struct serial_private *priv,
            const struct pciserial_board *board,
            struct uart_port *port, int idx)
{
      unsigned int offset = board->first_offset;
      unsigned int bar = FL_GET_BASE(board->flags);

      switch (priv->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(priv, port, bar, offset, board->reg_shift);
}

/*
 * Added for EKF Intel i960 serial boards
 */
static int 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 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 ||
          dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS)
            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_nocache(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 __iomem *p;

      if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0)
            return;

      /*
       * disable interrupts
       */
      p = ioremap_nocache(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);
      }
}

#define NI8420_INT_ENABLE_REG 0x38
#define NI8420_INT_ENABLE_BIT 0x2000

static void __devexit pci_ni8420_exit(struct pci_dev *dev)
{
      void __iomem *p;
      unsigned long base, len;
      unsigned int bar = 0;

      if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
            moan_device("no memory in bar", dev);
            return;
      }

      base = pci_resource_start(dev, bar);
      len =  pci_resource_len(dev, bar);
      p = ioremap_nocache(base, len);
      if (p == NULL)
            return;

      /* Disable the CPU Interrupt */
      writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT),
             p + NI8420_INT_ENABLE_REG);
      iounmap(p);
}


/* MITE registers */
#define MITE_IOWBSR1    0xc4
#define MITE_IOWCR1     0xf4
#define MITE_LCIMR1     0x08
#define MITE_LCIMR2     0x10

#define MITE_LCIMR2_CLR_CPU_IE      (1 << 30)

static void __devexit pci_ni8430_exit(struct pci_dev *dev)
{
      void __iomem *p;
      unsigned long base, len;
      unsigned int bar = 0;

      if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
            moan_device("no memory in bar", dev);
            return;
      }

      base = pci_resource_start(dev, bar);
      len =  pci_resource_len(dev, bar);
      p = ioremap_nocache(base, len);
      if (p == NULL)
            return;

      /* Disable the CPU Interrupt */
      writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2);
      iounmap(p);
}

/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
static int
sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
            struct uart_port *port, 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(priv, port, 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        0x500

static int sbs_init(struct pci_dev *dev)
{
      u8 __iomem *p;

      p = pci_ioremap_bar(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 __iomem *p;

      p = pci_ioremap_bar(dev, 0);
      /* FIXME: What if resource_len < OCT_REG_CR_OFF */
      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: all 10x cards have PCI device ids 0x10..
 *       all 20x cards have PCI device ids 0x20..
 *
 * There are also Quartet Serial cards which use Oxford Semiconductor
 * 16954 quad UART PCI chip clocked by 18.432 MHz quartz.
 *
 * 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_nocache(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;
}

static int pci_siig_init(struct pci_dev *dev)
{
      unsigned int type = dev->device & 0xff00;

      if (type == 0x1000)
            return pci_siig10x_init(dev);
      else if (type == 0x2000)
            return pci_siig20x_init(dev);

      moan_device("Unknown SIIG card", dev);
      return -ENODEV;
}

static int pci_siig_setup(struct serial_private *priv,
                    const struct pciserial_board *board,
                    struct uart_port *port, int idx)
{
      unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0;

      if (idx > 3) {
            bar = 4;
            offset = (idx - 4) * 8;
      }

      return setup_port(priv, port, bar, offset, 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
};

00566 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 }
};

static int pci_timedia_init(struct pci_dev *dev)
{
      const unsigned short *ids;
      int i, j;

      for (i = 0; i < ARRAY_SIZE(timedia_data); 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 int
pci_timedia_setup(struct serial_private *priv,
              const struct pciserial_board *board,
              struct uart_port *port, 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;
            /* FALLTHROUGH */
      case 4: /* BAR 2 */
      case 5: /* BAR 3 */
      case 6: /* BAR 4 */
      case 7: /* BAR 5 */
            bar = idx - 2;
      }

      return setup_port(priv, port, bar, offset, board->reg_shift);
}

/*
 * Some Titan cards are also a little weird
 */
static int
titan_400l_800l_setup(struct serial_private *priv,
                  const struct pciserial_board *board,
                  struct uart_port *port, int idx)
{
      unsigned int bar, offset = board->first_offset;

      switch (idx) {
      case 0:
            bar = 1;
            break;
      case 1:
            bar = 2;
            break;
      default:
            bar = 4;
            offset = (idx - 2) * board->uart_offset;
      }

      return setup_port(priv, port, bar, offset, board->reg_shift);
}

static int pci_xircom_init(struct pci_dev *dev)
{
      msleep(100);
      return 0;
}

static int pci_ni8420_init(struct pci_dev *dev)
{
      void __iomem *p;
      unsigned long base, len;
      unsigned int bar = 0;

      if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
            moan_device("no memory in bar", dev);
            return 0;
      }

      base = pci_resource_start(dev, bar);
      len =  pci_resource_len(dev, bar);
      p = ioremap_nocache(base, len);
      if (p == NULL)
            return -ENOMEM;

      /* Enable CPU Interrupt */
      writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT,
             p + NI8420_INT_ENABLE_REG);

      iounmap(p);
      return 0;
}

#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

static int pci_ni8430_init(struct pci_dev *dev)
{
      void __iomem *p;
      unsigned long base, len;
      u32 device_window;
      unsigned int bar = 0;

      if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
            moan_device("no memory in bar", dev);
            return 0;
      }

      base = pci_resource_start(dev, bar);
      len =  pci_resource_len(dev, bar);
      p = ioremap_nocache(base, len);
      if (p == NULL)
            return -ENOMEM;

      /* Set device window address and size in BAR0 */
      device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00)
                      | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE;
      writel(device_window, p + MITE_IOWBSR1);

      /* Set window access to go to RAMSEL IO address space */
      writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK),
             p + MITE_IOWCR1);

      /* Enable IO Bus Interrupt 0 */
      writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1);

      /* Enable CPU Interrupt */
      writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2);

      iounmap(p);
      return 0;
}

/* UART Port Control Register */
#define NI8430_PORTCON  0x0f
#define NI8430_PORTCON_TXVR_ENABLE  (1 << 3)

static int
pci_ni8430_setup(struct serial_private *priv,
             const struct pciserial_board *board,
             struct uart_port *port, int idx)
{
      void __iomem *p;
      unsigned long base, len;
      unsigned int bar, offset = board->first_offset;

      if (idx >= board->num_ports)
            return 1;

      bar = FL_GET_BASE(board->flags);
      offset += idx * board->uart_offset;

      base = pci_resource_start(priv->dev, bar);
      len =  pci_resource_len(priv->dev, bar);
      p = ioremap_nocache(base, len);

      /* enable the transciever */
      writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,
             p + offset + NI8430_PORTCON);

      iounmap(p);

      return setup_port(priv, port, bar, offset, board->reg_shift);
}


static int pci_netmos_init(struct pci_dev *dev)
{
      /* subdevice 0x00PS means <P> parallel, <S> serial */
      unsigned int num_serial = dev->subsystem_device & 0xf;

      if (dev->device == PCI_DEVICE_ID_NETMOS_9901)
            return 0;
      if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
                  dev->subsystem_device == 0x0299)
            return 0;

      if (num_serial == 0)
            return -ENODEV;
      return num_serial;
}

/*
 * 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_ite887x_init(struct pci_dev *dev)
{
      /* inta_addr are the configuration addresses of the ITE */
      static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0,
                                          0x200, 0x280, 0 };
      int ret, i, type;
      struct resource *iobase = NULL;
      u32 miscr, uartbar, ioport;

      /* search for the base-ioport */
      i = 0;
      while (inta_addr[i] && iobase == NULL) {
            iobase = request_region(inta_addr[i], ITE_887x_IOSIZE,
                                                "ite887x");
            if (iobase != NULL) {
                  /* write POSIO0R - speed | size | ioport */
                  pci_write_config_dword(dev, ITE_887x_POSIO0,
                        ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED |
                        ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]);
                  /* write INTCBAR - ioport */
                  pci_write_config_dword(dev, ITE_887x_INTCBAR,
                                                inta_addr[i]);
                  ret = inb(inta_addr[i]);
                  if (ret != 0xff) {
                        /* ioport connected */
                        break;
                  }
                  release_region(iobase->start, ITE_887x_IOSIZE);
                  iobase = NULL;
            }
            i++;
      }

      if (!inta_addr[i]) {
            printk(KERN_ERR "ite887x: could not find iobase\n");
            return -ENODEV;
      }

      /* start of undocumented type checking (see parport_pc.c) */
      type = inb(iobase->start + 0x18) & 0x0f;

      switch (type) {
      case 0x2:   /* ITE8871 (1P) */
      case 0xa:   /* ITE8875 (1P) */
            ret = 0;
            break;
      case 0xe:   /* ITE8872 (2S1P) */
            ret = 2;
            break;
      case 0x6:   /* ITE8873 (1S) */
            ret = 1;
            break;
      case 0x8:   /* ITE8874 (2S) */
            ret = 2;
            break;
      default:
            moan_device("Unknown ITE887x", dev);
            ret = -ENODEV;
      }

      /* configure all serial ports */
      for (i = 0; i < ret; i++) {
            /* read the I/O port from the device */
            pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)),
                                                &ioport);
            ioport &= 0x0000FF00;   /* the actual base address */
            pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)),
                  ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED |
                  ITE_887x_POSIO_IOSIZE_8 | ioport);

            /* write the ioport to the UARTBAR */
            pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar);
            uartbar &= ~(0xffff << (16 * i));   /* clear half the reg */
            uartbar |= (ioport << (16 * i));    /* set the ioport */
            pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar);

            /* get current config */
            pci_read_config_dword(dev, ITE_887x_MISCR, &miscr);
            /* disable interrupts (UARTx_Routing[3:0]) */
            miscr &= ~(0xf << (12 - 4 * i));
            /* activate the UART (UARTx_En) */
            miscr |= 1 << (23 - i);
            /* write new config with activated UART */
            pci_write_config_dword(dev, ITE_887x_MISCR, miscr);
      }

      if (ret <= 0) {
            /* the device has no UARTs if we get here */
            release_region(iobase->start, ITE_887x_IOSIZE);
      }

      return ret;
}

static void __devexit pci_ite887x_exit(struct pci_dev *dev)
{
      u32 ioport;
      /* the ioport is bit 0-15 in POSIO0R */
      pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport);
      ioport &= 0xffff;
      release_region(ioport, ITE_887x_IOSIZE);
}

/*
 * Oxford Semiconductor Inc.
 * Check that device is part of the Tornado range of devices, then determine
 * the number of ports available on the device.
 */
static int pci_oxsemi_tornado_init(struct pci_dev *dev)
{
      u8 __iomem *p;
      unsigned long deviceID;
      unsigned int  number_uarts = 0;

      /* OxSemi Tornado devices are all 0xCxxx */
      if (dev->vendor == PCI_VENDOR_ID_OXSEMI &&
          (dev->device & 0xF000) != 0xC000)
            return 0;

      p = pci_iomap(dev, 0, 5);
      if (p == NULL)
            return -ENOMEM;

      deviceID = ioread32(p);
      /* Tornado device */
      if (deviceID == 0x07000200) {
            number_uarts = ioread8(p + 4);
            printk(KERN_DEBUG
                  "%d ports detected on Oxford PCI Express device\n",
                                                number_uarts);
      }
      pci_iounmap(dev, p);
      return number_uarts;
}

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);
}

static int skip_tx_en_setup(struct serial_private *priv,
                  const struct pciserial_board *board,
                  struct uart_port *port, int idx)
{
      port->flags |= UPF_NO_TXEN_TEST;
      printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
                    "[%04x:%04x] subsystem [%04x:%04x]\n",
                    priv->dev->vendor,
                    priv->dev->device,
                    priv->dev->subsystem_vendor,
                    priv->dev->subsystem_device);

      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 = {
      /*
      * ADDI-DATA GmbH communication cards <info@addi-data.com>
      */
      {
            .vendor         = PCI_VENDOR_ID_ADDIDATA_OLD,
            .device         = PCI_DEVICE_ID_ADDIDATA_APCI7800,
            .subvendor      = PCI_ANY_ID,
            .subdevice      = PCI_ANY_ID,
            .setup          = addidata_apci7800_setup,
      },
      /*
       * AFAVLAB cards - these may be called via parport_serial
       *  It is not clear whether this applies to all products.
       */
      {
            .vendor           = PCI_VENDOR_ID_AFAVLAB,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = afavlab_setup,
      },
      /*
       * HP Diva
       */
      {
            .vendor           = PCI_VENDOR_ID_HP,
            .device           = PCI_DEVICE_ID_HP_DIVA,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_hp_diva_init,
            .setup            = pci_hp_diva_setup,
      },
      /*
       * Intel
       */
      {
            .vendor           = PCI_VENDOR_ID_INTEL,
            .device           = PCI_DEVICE_ID_INTEL_80960_RP,
            .subvendor  = 0xe4bf,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_inteli960ni_init,
            .setup            = pci_default_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_INTEL,
            .device           = PCI_DEVICE_ID_INTEL_8257X_SOL,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = skip_tx_en_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_INTEL,
            .device           = PCI_DEVICE_ID_INTEL_82573L_SOL,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = skip_tx_en_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_INTEL,
            .device           = PCI_DEVICE_ID_INTEL_82573E_SOL,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = skip_tx_en_setup,
      },
      /*
       * ITE
       */
      {
            .vendor           = PCI_VENDOR_ID_ITE,
            .device           = PCI_DEVICE_ID_ITE_8872,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ite887x_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ite887x_exit),
      },
      /*
       * National Instruments
       */
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI23216,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI2328,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI2324,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI2322,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI2324I,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PCI2322I,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8420_23216,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8420_2328,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8420_2324,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8420_2322,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8422_2324,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_DEVICE_ID_NI_PXI8422_2322,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8420_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_ni8420_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_NI,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_ni8430_init,
            .setup            = pci_ni8430_setup,
            .exit       = __devexit_p(pci_ni8430_exit),
      },
      /*
       * Panacom
       */
      {
            .vendor           = PCI_VENDOR_ID_PANACOM,
            .device           = PCI_DEVICE_ID_PANACOM_QUADMODEM,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_PANACOM,
            .device           = PCI_DEVICE_ID_PANACOM_DUALMODEM,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      /*
       * PLX
       */
      {
            .vendor           = PCI_VENDOR_ID_PLX,
            .device           = PCI_DEVICE_ID_PLX_9030,
            .subvendor  = PCI_SUBVENDOR_ID_PERLE,
            .subdevice  = PCI_ANY_ID,
            .setup            = pci_default_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_PLX,
            .device           = PCI_DEVICE_ID_PLX_9050,
            .subvendor  = PCI_SUBVENDOR_ID_EXSYS,
            .subdevice  = PCI_SUBDEVICE_ID_EXSYS_4055,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_PLX,
            .device           = PCI_DEVICE_ID_PLX_9050,
            .subvendor  = PCI_SUBVENDOR_ID_KEYSPAN,
            .subdevice  = PCI_SUBDEVICE_ID_KEYSPAN_SX2,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_PLX,
            .device           = PCI_DEVICE_ID_PLX_9050,
            .subvendor  = PCI_VENDOR_ID_PLX,
            .subdevice  = PCI_SUBDEVICE_ID_UNKNOWN_0x1584,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      {
            .vendor           = PCI_VENDOR_ID_PLX,
            .device           = PCI_DEVICE_ID_PLX_ROMULUS,
            .subvendor  = PCI_VENDOR_ID_PLX,
            .subdevice  = PCI_DEVICE_ID_PLX_ROMULUS,
            .init       = pci_plx9050_init,
            .setup            = pci_default_setup,
            .exit       = __devexit_p(pci_plx9050_exit),
      },
      /*
       * SBS Technologies, Inc., PMC-OCTALPRO 232
       */
      {
            .vendor           = PCI_VENDOR_ID_SBSMODULARIO,
            .device           = PCI_DEVICE_ID_OCTPRO,
            .subvendor  = PCI_SUBVENDOR_ID_SBSMODULARIO,
            .subdevice  = PCI_SUBDEVICE_ID_OCTPRO232,
            .init       = sbs_init,
            .setup            = sbs_setup,
            .exit       = __devexit_p(sbs_exit),
      },
      /*
       * SBS Technologies, Inc., PMC-OCTALPRO 422
       */
      {
            .vendor           = PCI_VENDOR_ID_SBSMODULARIO,
            .device           = PCI_DEVICE_ID_OCTPRO,
            .subvendor  = PCI_SUBVENDOR_ID_SBSMODULARIO,
            .subdevice  = PCI_SUBDEVICE_ID_OCTPRO422,
            .init       = sbs_init,
            .setup            = sbs_setup,
            .exit       = __devexit_p(sbs_exit),
      },
      /*
       * SBS Technologies, Inc., P-Octal 232
       */
      {
            .vendor           = PCI_VENDOR_ID_SBSMODULARIO,
            .device           = PCI_DEVICE_ID_OCTPRO,
            .subvendor  = PCI_SUBVENDOR_ID_SBSMODULARIO,
            .subdevice  = PCI_SUBDEVICE_ID_POCTAL232,
            .init       = sbs_init,
            .setup            = sbs_setup,
            .exit       = __devexit_p(sbs_exit),
      },
      /*
       * SBS Technologies, Inc., P-Octal 422
       */
      {
            .vendor           = PCI_VENDOR_ID_SBSMODULARIO,
            .device           = PCI_DEVICE_ID_OCTPRO,
            .subvendor  = PCI_SUBVENDOR_ID_SBSMODULARIO,
            .subdevice  = PCI_SUBDEVICE_ID_POCTAL422,
            .init       = sbs_init,
            .setup            = sbs_setup,
            .exit       = __devexit_p(sbs_exit),
      },
      /*
       * SIIG cards - these may be called via parport_serial
       */
      {
            .vendor           = PCI_VENDOR_ID_SIIG,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_siig_init,
            .setup            = pci_siig_setup,
      },
      /*
       * Titan cards
       */
      {
            .vendor           = PCI_VENDOR_ID_TITAN,
            .device           = PCI_DEVICE_ID_TITAN_400L,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = titan_400l_800l_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_TITAN,
            .device           = PCI_DEVICE_ID_TITAN_800L,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = titan_400l_800l_setup,
      },
      /*
       * Timedia cards
       */
      {
            .vendor           = PCI_VENDOR_ID_TIMEDIA,
            .device           = PCI_DEVICE_ID_TIMEDIA_1889,
            .subvendor  = PCI_VENDOR_ID_TIMEDIA,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_timedia_init,
            .setup            = pci_timedia_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_TIMEDIA,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = pci_timedia_setup,
      },
      /*
       * Xircom cards
       */
      {
            .vendor           = PCI_VENDOR_ID_XIRCOM,
            .device           = PCI_DEVICE_ID_XIRCOM_X3201_MDM,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_xircom_init,
            .setup            = pci_default_setup,
      },
      /*
       * Netmos cards - these may be called via parport_serial
       */
      {
            .vendor           = PCI_VENDOR_ID_NETMOS,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_netmos_init,
            .setup            = pci_default_setup,
      },
      /*
       * For Oxford Semiconductor and Mainpine
       */
      {
            .vendor           = PCI_VENDOR_ID_OXSEMI,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_oxsemi_tornado_init,
            .setup            = pci_default_setup,
      },
      {
            .vendor           = PCI_VENDOR_ID_MAINPINE,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .init       = pci_oxsemi_tornado_init,
            .setup            = pci_default_setup,
      },
      /*
       * Default "match everything" terminator entry
       */
      {
            .vendor           = PCI_ANY_ID,
            .device           = PCI_ANY_ID,
            .subvendor  = PCI_ANY_ID,
            .subdevice  = PCI_ANY_ID,
            .setup            = pci_default_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_8_115200,

      pbn_b0_1_921600,
      pbn_b0_2_921600,
      pbn_b0_4_921600,

      pbn_b0_2_1130000,

      pbn_b0_4_1152000,

      pbn_b0_2_1843200,
      pbn_b0_4_1843200,

      pbn_b0_2_1843200_200,
      pbn_b0_4_1843200_200,
      pbn_b0_8_1843200_200,

      pbn_b0_1_4000000,

      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_16_115200,

      pbn_b1_1_921600,
      pbn_b1_2_921600,
      pbn_b1_4_921600,
      pbn_b1_8_921600,

      pbn_b1_2_1250000,

      pbn_b1_bt_1_115200,
      pbn_b1_bt_2_115200,
      pbn_b1_bt_4_115200,

      pbn_b1_bt_2_921600,

      pbn_b1_1_1382400,
      pbn_b1_2_1382400,
      pbn_b1_4_1382400,
      pbn_b1_8_1382400,

      pbn_b2_1_115200,
      pbn_b2_2_115200,
      pbn_b2_4_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_2_115200,
      pbn_b3_4_115200,
      pbn_b3_8_115200,

      /*
       * Board-specific versions.
       */
      pbn_panacom,
      pbn_panacom2,
      pbn_panacom4,
      pbn_exsys_4055,
      pbn_plx_romulus,
      pbn_oxsemi,
      pbn_oxsemi_1_4000000,
      pbn_oxsemi_2_4000000,
      pbn_oxsemi_4_4000000,
      pbn_oxsemi_8_4000000,
      pbn_intel_i960,
      pbn_sgi_ioc3,
      pbn_computone_4,
      pbn_computone_6,
      pbn_computone_8,
      pbn_sbsxrsio,
      pbn_exar_XR17C152,
      pbn_exar_XR17C154,
      pbn_exar_XR17C158,
      pbn_pasemi_1682M,
      pbn_ni8430_2,
      pbn_ni8430_4,
      pbn_ni8430_8,
      pbn_ni8430_16,
};

/*
 * 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[] __devinitdata = {
      [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_8_115200] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .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_1130000] = {
            .flags          = FL_BASE0,
            .num_ports      = 2,
            .base_baud      = 1130000,
            .uart_offset    = 8,
      },

      [pbn_b0_4_1152000] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 1152000,
            .uart_offset      = 8,
      },

      [pbn_b0_2_1843200] = {
            .flags            = FL_BASE0,
            .num_ports  = 2,
            .base_baud  = 1843200,
            .uart_offset      = 8,
      },
      [pbn_b0_4_1843200] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 1843200,
            .uart_offset      = 8,
      },

      [pbn_b0_2_1843200_200] = {
            .flags            = FL_BASE0,
            .num_ports  = 2,
            .base_baud  = 1843200,
            .uart_offset      = 0x200,
      },
      [pbn_b0_4_1843200_200] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 1843200,
            .uart_offset      = 0x200,
      },
      [pbn_b0_8_1843200_200] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 1843200,
            .uart_offset      = 0x200,
      },
      [pbn_b0_1_4000000] = {
            .flags            = FL_BASE0,
            .num_ports  = 1,
            .base_baud  = 4000000,
            .uart_offset      = 8,
      },

      [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_16_115200] = {
            .flags            = FL_BASE1,
            .num_ports  = 16,
            .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_2_1250000] = {
            .flags            = FL_BASE1,
            .num_ports  = 2,
            .base_baud  = 1250000,
            .uart_offset      = 8,
      },

      [pbn_b1_bt_1_115200] = {
            .flags            = FL_BASE1|FL_BASE_BARS,
            .num_ports  = 1,
            .base_baud  = 115200,
            .uart_offset      = 8,
      },
      [pbn_b1_bt_2_115200] = {
            .flags            = FL_BASE1|FL_BASE_BARS,
            .num_ports  = 2,
            .base_baud  = 115200,
            .uart_offset      = 8,
      },
      [pbn_b1_bt_4_115200] = {
            .flags            = FL_BASE1|FL_BASE_BARS,
            .num_ports  = 4,
            .base_baud  = 115200,
            .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_1_1382400] = {
            .flags            = FL_BASE1,
            .num_ports  = 1,
            .base_baud  = 1382400,
            .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_2_115200] = {
            .flags            = FL_BASE2,
            .num_ports  = 2,
            .base_baud  = 115200,
            .uart_offset      = 8,
      },
      [pbn_b2_4_115200] = {
            .flags          = FL_BASE2,
            .num_ports      = 4,
            .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_2_115200] = {
            .flags            = FL_BASE3,
            .num_ports  = 2,
            .base_baud  = 115200,
            .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,
      },

      /*
       * Entries following this are board-specific.
       */

      /*
       * Panacom - IOMEM
       */
      [pbn_panacom] = {
            .flags            = FL_BASE2,
            .num_ports  = 2,
            .base_baud  = 921600,
            .uart_offset      = 0x400,
            .reg_shift  = 7,
      },
      [pbn_panacom2] = {
            .flags            = FL_BASE2|FL_BASE_BARS,
            .num_ports  = 2,
            .base_baud  = 921600,
            .uart_offset      = 0x400,
            .reg_shift  = 7,
      },
      [pbn_panacom4] = {
            .flags            = FL_BASE2|FL_BASE_BARS,
            .num_ports  = 4,
            .base_baud  = 921600,
            .uart_offset      = 0x400,
            .reg_shift  = 7,
      },

      [pbn_exsys_4055] = {
            .flags            = FL_BASE2,
            .num_ports  = 4,
            .base_baud  = 115200,
            .uart_offset      = 8,
      },

      /* I think this entry is broken - the first_offset looks wrong --rmk */
      [pbn_plx_romulus] = {
            .flags            = FL_BASE2,
            .num_ports  = 4,
            .base_baud  = 921600,
            .uart_offset      = 8 << 2,
            .reg_shift  = 2,
            .first_offset     = 0x03,
      },

      /*
       * This board uses the size of PCI Base region 0 to
       * signal now many ports are available
       */
      [pbn_oxsemi] = {
            .flags            = FL_BASE0|FL_REGION_SZ_CAP,
            .num_ports  = 32,
            .base_baud  = 115200,
            .uart_offset      = 8,
      },
      [pbn_oxsemi_1_4000000] = {
            .flags            = FL_BASE0,
            .num_ports  = 1,
            .base_baud  = 4000000,
            .uart_offset      = 0x200,
            .first_offset     = 0x1000,
      },
      [pbn_oxsemi_2_4000000] = {
            .flags            = FL_BASE0,
            .num_ports  = 2,
            .base_baud  = 4000000,
            .uart_offset      = 0x200,
            .first_offset     = 0x1000,
      },
      [pbn_oxsemi_4_4000000] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 4000000,
            .uart_offset      = 0x200,
            .first_offset     = 0x1000,
      },
      [pbn_oxsemi_8_4000000] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 4000000,
            .uart_offset      = 0x200,
            .first_offset     = 0x1000,
      },


      /*
       * EKF addition for i960 Boards form EKF with serial port.
       * Max 256 ports.
       */
      [pbn_intel_i960] = {
            .flags            = FL_BASE0,
            .num_ports  = 32,
            .base_baud  = 921600,
            .uart_offset      = 8 << 2,
            .reg_shift  = 2,
            .first_offset     = 0x10000,
      },
      [pbn_sgi_ioc3] = {
            .flags            = FL_BASE0|FL_NOIRQ,
            .num_ports  = 1,
            .base_baud  = 458333,
            .uart_offset      = 8,
            .reg_shift  = 0,
            .first_offset     = 0x20178,
      },

      /*
       * Computone - uses IOMEM.
       */
      [pbn_computone_4] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 921600,
            .uart_offset      = 0x40,
            .reg_shift  = 2,
            .first_offset     = 0x200,
      },
      [pbn_computone_6] = {
            .flags            = FL_BASE0,
            .num_ports  = 6,
            .base_baud  = 921600,
            .uart_offset      = 0x40,
            .reg_shift  = 2,
            .first_offset     = 0x200,
      },
      [pbn_computone_8] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 921600,
            .uart_offset      = 0x40,
            .reg_shift  = 2,
            .first_offset     = 0x200,
      },
      [pbn_sbsxrsio] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 460800,
            .uart_offset      = 256,
            .reg_shift  = 4,
      },
      /*
       * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
       *  Only basic 16550A support.
       *  XR17C15[24] are not tested, but they should work.
       */
      [pbn_exar_XR17C152] = {
            .flags            = FL_BASE0,
            .num_ports  = 2,
            .base_baud  = 921600,
            .uart_offset      = 0x200,
      },
      [pbn_exar_XR17C154] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 921600,
            .uart_offset      = 0x200,
      },
      [pbn_exar_XR17C158] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 921600,
            .uart_offset      = 0x200,
      },
      /*
       * PA Semi PWRficient PA6T-1682M on-chip UART
       */
      [pbn_pasemi_1682M] = {
            .flags            = FL_BASE0,
            .num_ports  = 1,
            .base_baud  = 8333333,
      },
      /*
       * National Instruments 843x
       */
      [pbn_ni8430_16] = {
            .flags            = FL_BASE0,
            .num_ports  = 16,
            .base_baud  = 3686400,
            .uart_offset      = 0x10,
            .first_offset     = 0x800,
      },
      [pbn_ni8430_8] = {
            .flags            = FL_BASE0,
            .num_ports  = 8,
            .base_baud  = 3686400,
            .uart_offset      = 0x10,
            .first_offset     = 0x800,
      },
      [pbn_ni8430_4] = {
            .flags            = FL_BASE0,
            .num_ports  = 4,
            .base_baud  = 3686400,
            .uart_offset      = 0x10,
            .first_offset     = 0x800,
      },
      [pbn_ni8430_2] = {
            .flags            = FL_BASE0,
            .num_ports  = 2,
            .base_baud  = 3686400,
            .uart_offset      = 0x10,
            .first_offset     = 0x800,
      },
};

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 __devinit
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 *
pciserial_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 %x, irq %d, type %d\n",
                   serial_port.iobase, serial_port.irq, serial_port.iotype);
#endif

            priv->line[i] = serial8250_register_port(&serial_port);
            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;
}
EXPORT_SYMBOL_GPL(pciserial_init_ports);

void pciserial_remove_ports(struct serial_private *priv)
{
      struct pci_serial_quirk *quirk;
      int i;

      for (i = 0; i < priv->nr; i++)
            serial8250_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);
}
EXPORT_SYMBOL_GPL(pciserial_remove_ports);

void pciserial_suspend_ports(struct serial_private *priv)
{
      int i;

      for (i = 0; i < priv->nr; i++)
            if (priv->line[i] >= 0)
                  serial8250_suspend_port(priv->line[i]);
}
EXPORT_SYMBOL_GPL(pciserial_suspend_ports);

void pciserial_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)
                  serial8250_resume_port(priv->line[i]);
}
EXPORT_SYMBOL_GPL(pciserial_resume_ports);

/*
 * Probe one serial board.  Unfortunately, there is no rhyme nor reason
 * to the arrangement of serial ports on a PCI card.
 */
static int __devinit
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 = pciserial_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 __devexit pciserial_remove_one(struct pci_dev *dev)
{
      struct serial_private *priv = pci_get_drvdata(dev);

      pci_set_drvdata(dev, NULL);

      pciserial_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");
            pciserial_resume_ports(priv);
      }
      return 0;
}
#endif

static struct pci_device_id serial_pci_tbl[] = {
      /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */
      {     PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
            PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0,
            pbn_b2_8_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
            pbn_b1_8_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
            pbn_b1_4_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
            pbn_b1_2_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
            pbn_b1_8_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
            pbn_b1_4_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
            pbn_b1_2_1382400 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
            pbn_b1_8_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
            pbn_b1_8_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
            pbn_b1_4_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
            pbn_b1_4_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
            pbn_b1_2_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
            pbn_b1_8_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
            pbn_b1_8_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
            pbn_b1_4_921600 },
      {     PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0,
            pbn_b1_2_1250000 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0,
            pbn_b0_2_1843200 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0,
            pbn_b0_4_1843200 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_VENDOR_ID_AFAVLAB,
            PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0,
            pbn_b0_4_1152000 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0,
            pbn_b0_2_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0,
            pbn_b0_4_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0,
            pbn_b0_8_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0,
            pbn_b0_2_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0,
            pbn_b0_4_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0,
            pbn_b0_8_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0,
            pbn_b0_2_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0,
            pbn_b0_4_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0,
            pbn_b0_8_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0,
            pbn_b0_2_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0,
            pbn_b0_4_1843200_200 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
            PCI_SUBVENDOR_ID_CONNECT_TECH,
            PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0,
            pbn_b0_8_1843200_200 },

      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_1_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_4_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_4_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_8_115200 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_8_460800 },
      {     PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_8_115200 },

      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_115200 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_921600 },
      /*
       * VScom SPCOM800, from sl@s.pl
       */
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_8_921600 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_4_921600 },
      /* Unknown card - subdevice 0x1584 */
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_VENDOR_ID_PLX,
            PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0,
            pbn_b0_4_115200 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_KEYSPAN,
            PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
            pbn_panacom },
      {     PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_panacom4 },
      {     PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_panacom2 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
            PCI_VENDOR_ID_ESDGMBH,
            PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0,
            pbn_b2_4_115200 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIFAST,
            PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0,
            pbn_b2_4_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIFAST,
            PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0,
            pbn_b2_8_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIFAST,
            PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0,
            pbn_b2_16_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIFAST,
            PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0,
            pbn_b2_16_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIRAS,
            PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0,
            pbn_b2_4_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_CHASE_PCIRAS,
            PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0,
            pbn_b2_8_460800 },
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
            PCI_SUBVENDOR_ID_EXSYS,
            PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
            pbn_exsys_4055 },
      /*
       * Megawolf Romulus PCI Serial Card, from Mike Hudson
       * (Exoray@isys.ca)
       */
      {     PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
            0x10b5, 0x106a, 0, 0,
            pbn_plx_romulus },
      {     PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_4_115200 },
      {     PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_2_115200 },
      {     PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_8_115200 },
      {     PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_8_115200 },
      {     PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
            0, 0,
            pbn_b0_4_921600 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL,
            0, 0,
            pbn_b0_4_1152000 },

            /*
             * The below card is a little controversial since it is the
             * subject of a PCI vendor/device ID clash.  (See
             * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html).
             * For now just used the hex ID 0x950a.
             */
      {     PCI_VENDOR_ID_OXSEMI, 0x950a,
            PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0,
            pbn_b0_2_115200 },
      {     PCI_VENDOR_ID_OXSEMI, 0x950a,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_2_1130000 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950,
            PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0,
            pbn_b0_1_921600 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_4_115200 },
      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_921600 },

      /*
       * Oxford Semiconductor Inc. Tornado PCI express device range.
       */
      {     PCI_VENDOR_ID_OXSEMI, 0xc101,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc105,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc11b,    /* OXPCIe952 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc11f,    /* OXPCIe952 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc120,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc124,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc138,    /* OXPCIe952 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc13d,    /* OXPCIe952 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc140,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc141,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc144,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc145,    /* OXPCIe952 1 Legacy UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc158,    /* OXPCIe952 2 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_2_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc15d,    /* OXPCIe952 2 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_2_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc208,    /* OXPCIe954 4 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_4_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc20d,    /* OXPCIe954 4 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_4_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc308,    /* OXPCIe958 8 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_8_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc30d,    /* OXPCIe958 8 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_8_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc40b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc40f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc41b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc41f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc42b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc42f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc43b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc43f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc44b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc44f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc45b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc45f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc46b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc46f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc47b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc47f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc48b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc48f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc49b,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc49f,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4ab,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4af,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4bb,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4bf,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4cb,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_OXSEMI, 0xc4cf,    /* OXPCIe200 1 Native UART */
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi_1_4000000 },
      /*
       * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado
       */
      {     PCI_VENDOR_ID_MAINPINE, 0x4000,     /* IQ Express 1 Port V.34 Super-G3 Fax */
            PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0,
            pbn_oxsemi_1_4000000 },
      {     PCI_VENDOR_ID_MAINPINE, 0x4000,     /* IQ Express 2 Port V.34 Super-G3 Fax */
            PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0,
            pbn_oxsemi_2_4000000 },
      {     PCI_VENDOR_ID_MAINPINE, 0x4000,     /* IQ Express 4 Port V.34 Super-G3 Fax */
            PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0,
            pbn_oxsemi_4_4000000 },
      {     PCI_VENDOR_ID_MAINPINE, 0x4000,     /* IQ Express 8 Port V.34 Super-G3 Fax */
            PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0,
            pbn_oxsemi_8_4000000 },
      /*
       * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards,
       * from skokodyn@yahoo.com
       */
      {     PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
            PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0,
            pbn_sbsxrsio },
      {     PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
            PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0,
            pbn_sbsxrsio },
      {     PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
            PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0,
            pbn_sbsxrsio },
      {     PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
            PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0,
            pbn_sbsxrsio },

      /*
       * Digitan DS560-558, from jimd@esoft.com
       */
      {     PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_1_115200 },

      /*
       * Titan Electronic cards
       *  The 400L and 800L have a custom setup quirk.
       */
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_2_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_4_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_4_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_1_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_2_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_921600 },
      {     PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_921600 },

      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_1_460800 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_1_460800 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_1_460800 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_921600 },
      {     PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_921600 },

      /*
       * Computone devices submitted by Doug McNash dmcnash@computone.com
       */
      {     PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
            PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
            0, 0, pbn_computone_4 },
      {     PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
            PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
            0, 0, pbn_computone_8 },
      {     PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
            PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
            0, 0, pbn_computone_6 },

      {     PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_oxsemi },
      {     PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
            PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_1_921600 },

      /*
       * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
       */
      {     PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_115200 },
      {     PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_8_115200 },

      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_115200 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_115200 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_115200 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_460800 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_4_460800 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_460800 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_460800 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_2_460800 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_1_115200 },
      {     PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_bt_1_460800 },

      /*
       * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408).
       * Cards are identified by their subsystem vendor IDs, which
       * (in hex) match the model number.
       *
       * Note that JC140x are RS422/485 cards which require ox950
       * ACR = 0x10, and as such are not currently fully supported.
       */
      {     PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
            0x1204, 0x0004, 0, 0,
            pbn_b0_4_921600 },
      {     PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
            0x1208, 0x0004, 0, 0,
            pbn_b0_4_921600 },
/*    {     PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
            0x1402, 0x0002, 0, 0,
            pbn_b0_2_921600 }, */
/*    {     PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
            0x1404, 0x0004, 0, 0,
            pbn_b0_4_921600 }, */
      {     PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1,
            0x1208, 0x0004, 0, 0,
            pbn_b0_4_921600 },

      /*
       * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com
       */
      {     PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_1_1382400 },

      /*
       * Dell Remote Access Card III - Tim_T_Murphy@Dell.com
       */
      {     PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_1_1382400 },

      /*
       * RAStel 2 port modem, gerg@moreton.com.au
       */
      {     PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_bt_2_115200 },

      /*
       * EKF addition for i960 Boards form EKF with serial port
       */
      {     PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP,
            0xE4BF, PCI_ANY_ID, 0, 0,
            pbn_intel_i960 },

      /*
       * Xircom Cardbus/Ethernet combos
       */
      {     PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_115200 },
      /*
       * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry)
       */
      {     PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_115200 },

      /*
       * Untested PCI modems, sent in from various folks...
       */

      /*
       * Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de>
       */
      {     PCI_VENDOR_ID_ROCKWELL, 0x1004,
            0x1048, 0x1500, 0, 0,
            pbn_b1_1_115200 },

      {     PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
            0xFF00, 0, 0, 0,
            pbn_sgi_ioc3 },

      /*
       * HP Diva card
       */
      {     PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
            PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0,
            pbn_b1_1_115200 },
      {     PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_5_115200 },
      {     PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b2_1_115200 },

      {     PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b3_2_115200 },
      {     PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b3_4_115200 },
      {     PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b3_8_115200 },

      /*
       * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
       */
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
            PCI_ANY_ID, PCI_ANY_ID,
            0,
            0, pbn_exar_XR17C152 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
            PCI_ANY_ID, PCI_ANY_ID,
            0,
            0, pbn_exar_XR17C154 },
      {     PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
            PCI_ANY_ID, PCI_ANY_ID,
            0,
            0, pbn_exar_XR17C158 },

      /*
       * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
       */
      {     PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b0_1_115200 },
      /*
       * ITE
       */
      {     PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
            PCI_ANY_ID, PCI_ANY_ID,
            0, 0,
            pbn_b1_bt_1_115200 },

      /*
       * IntaShield IS-200
       */
      {     PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */
            pbn_b2_2_115200 },
      /*
       * IntaShield IS-400
       */
      {     PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,    /* 135a.0dc0 */
            pbn_b2_4_115200 },
      /*
       * Perle PCI-RAS cards
       */
      {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
            PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4,
            0, 0, pbn_b2_4_921600 },
      {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
            PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8,
            0, 0, pbn_b2_8_921600 },

      /*
       * Mainpine series cards: Fairly standard layout but fools
       * parts of the autodetect in some cases and uses otherwise
       * unmatched communications subclasses in the PCI Express case
       */

      {     /* RockForceDUO */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0200,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForceQUATRO */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0300,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForceDUO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0400,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForceQUATRO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0500,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForce+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0600,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForce+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0700,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForceOCTO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0800,
            0, 0, pbn_b0_8_115200 },
      {     /* RockForceDUO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0C00,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForceQUARTRO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x0D00,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForceOCTO+ */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x1D00,
            0, 0, pbn_b0_8_115200 },
      {     /* RockForceD1 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2000,
            0, 0, pbn_b0_1_115200 },
      {     /* RockForceF1 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2100,
            0, 0, pbn_b0_1_115200 },
      {     /* RockForceD2 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2200,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForceF2 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2300,
            0, 0, pbn_b0_2_115200 },
      {     /* RockForceD4 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2400,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForceF4 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2500,
            0, 0, pbn_b0_4_115200 },
      {     /* RockForceD8 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2600,
            0, 0, pbn_b0_8_115200 },
      {     /* RockForceF8 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x2700,
            0, 0, pbn_b0_8_115200 },
      {     /* IQ Express D1 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3000,
            0, 0, pbn_b0_1_115200 },
      {     /* IQ Express F1 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3100,
            0, 0, pbn_b0_1_115200 },
      {     /* IQ Express D2 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3200,
            0, 0, pbn_b0_2_115200 },
      {     /* IQ Express F2 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3300,
            0, 0, pbn_b0_2_115200 },
      {     /* IQ Express D4 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3400,
            0, 0, pbn_b0_4_115200 },
      {     /* IQ Express F4 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3500,
            0, 0, pbn_b0_4_115200 },
      {     /* IQ Express D8 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3C00,
            0, 0, pbn_b0_8_115200 },
      {     /* IQ Express F8 */
            PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
            PCI_VENDOR_ID_MAINPINE, 0x3D00,
            0, 0, pbn_b0_8_115200 },


      /*
       * PA Semi PA6T-1682M on-chip UART
       */
      {     PCI_VENDOR_ID_PASEMI, 0xa004,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_pasemi_1682M },

      /*
       * National Instruments
       */
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_16_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_8_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_4_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_2_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_4_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_2_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_16_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_8_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_4_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_2_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_4_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_b1_bt_2_115200 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_2 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_2 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_4 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_4 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_8 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_8 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_16 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_16 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_2 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_2 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_4 },
      {     PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324,
            PCI_ANY_ID, PCI_ANY_ID, 0, 0,
            pbn_ni8430_4 },

      /*
      * ADDI-DATA GmbH communication cards <info@addi-data.com>
      */
      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7500,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_4_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7420,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_2_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7300,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_1_115200 },

      {     PCI_VENDOR_ID_ADDIDATA_OLD,
            PCI_DEVICE_ID_ADDIDATA_APCI7800,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b1_8_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7500_2,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_4_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7420_2,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_2_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7300_2,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_1_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7500_3,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_4_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7420_3,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_2_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7300_3,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_1_115200 },

      {     PCI_VENDOR_ID_ADDIDATA,
            PCI_DEVICE_ID_ADDIDATA_APCI7800_3,
            PCI_ANY_ID,
            PCI_ANY_ID,
            0,
            0,
            pbn_b0_8_115200 },

      {     PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
            PCI_VENDOR_ID_IBM, 0x0299,
            0, 0, pbn_b0_bt_2_115200 },

      {     PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
            0xA000, 0x1000,
            0, 0, pbn_b0_1_115200 },

      /*
       * These entries match devices with class COMMUNICATION_SERIAL,
       * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
       */
      {     PCI_ANY_ID, PCI_ANY_ID,
            PCI_ANY_ID, PCI_ANY_ID,
            PCI_CLASS_COMMUNICATION_SERIAL << 8,
            0xffff00, pbn_default },
      {     PCI_ANY_ID, PCI_ANY_ID,
            PCI_ANY_ID, PCI_ANY_ID,
            PCI_CLASS_COMMUNICATION_MODEM << 8,
            0xffff00, pbn_default },
      {     PCI_ANY_ID, PCI_ANY_ID,
            PCI_ANY_ID, PCI_ANY_ID,
            PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
            0xffff00, pbn_default },
      { 0, }
};

static struct pci_driver serial_pci_driver = {
      .name       = "serial",
      .probe            = pciserial_init_one,
      .remove           = __devexit_p(pciserial_remove_one),
#ifdef CONFIG_PM
      .suspend    = pciserial_suspend_one,
      .resume           = pciserial_resume_one,
#endif
      .id_table   = serial_pci_tbl,
};

static int __init serial8250_pci_init(void)
{
      return pci_register_driver(&serial_pci_driver);
}

static void __exit serial8250_pci_exit(void)
{
      pci_unregister_driver(&serial_pci_driver);
}

module_init(serial8250_pci_init);
module_exit(serial8250_pci_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
MODULE_DEVICE_TABLE(pci, serial_pci_tbl);

Generated by  Doxygen 1.6.0   Back to index