Merge with Linus' 2.6 tree
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Thu, 28 Jul 2005 08:30:20 +0000 (09:30 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 28 Jul 2005 08:30:20 +0000 (09:30 +0100)
1  2 
drivers/serial/8250_pci.c

index 52b0a0558ed4266f6f357cc478a1d2916fe6306a,07f05e9d0955eb12ae58705a09ffa4d20b05531d..0e21f583690ebdeffa57f316bd8fba2c67da8e71
  
  #undef SERIAL_DEBUG_PCI
  
 -/*
 - * Definitions for PCI support.
 - */
 -#define FL_BASE_MASK          0x0007
 -#define FL_BASE0              0x0000
 -#define FL_BASE1              0x0001
 -#define FL_BASE2              0x0002
 -#define FL_BASE3              0x0003
 -#define FL_BASE4              0x0004
 -#define FL_GET_BASE(x)                (x & FL_BASE_MASK)
 -
 -/* Use successive BARs (PCI base address registers),
 -   else use offset into some specified BAR */
 -#define FL_BASE_BARS          0x0008
 -
 -/* do not assign an irq */
 -#define FL_NOIRQ              0x0080
 -
 -/* Use the Base address register size to cap number of ports */
 -#define FL_REGION_SZ_CAP      0x0100
 -
 -struct pci_board {
 -      unsigned int flags;
 -      unsigned int num_ports;
 -      unsigned int base_baud;
 -      unsigned int uart_offset;
 -      unsigned int reg_shift;
 -      unsigned int first_offset;
 -};
 -
  /*
   * init function returns:
   *  > 0 - number of ports
@@@ -45,15 -75,14 +45,15 @@@ struct pci_serial_quirk 
        u32     subvendor;
        u32     subdevice;
        int     (*init)(struct pci_dev *dev);
 -      int     (*setup)(struct pci_dev *dev, struct pci_board *board,
 -                       struct uart_port *port, int idx);
 +      int     (*setup)(struct serial_private *, struct pciserial_board *,
 +                       struct uart_port *, int);
        void    (*exit)(struct pci_dev *dev);
  };
  
  #define PCI_NUM_BAR_RESOURCES 6
  
  struct serial_private {
 +      struct pci_dev          *dev;
        unsigned int            nr;
        void __iomem            *remapped_bar[PCI_NUM_BAR_RESOURCES];
        struct pci_serial_quirk *quirk;
@@@ -72,18 -101,17 +72,18 @@@ static void moan_device(const char *str
  }
  
  static int
 -setup_port(struct pci_dev *dev, struct uart_port *port,
 +setup_port(struct serial_private *priv, struct uart_port *port,
           int bar, int offset, int regshift)
  {
 -      struct serial_private *priv = pci_get_drvdata(dev);
 +      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) {
 -              base = pci_resource_start(dev, bar);
                len =  pci_resource_len(dev, bar);
  
                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 {
 -              base = pci_resource_start(dev, bar) + offset;
                port->iotype = UPIO_PORT;
 -              port->iobase = base;
 +              port->iobase = base + offset;
 +              port->mapbase = 0;
 +              port->membase = NULL;
 +              port->regshift = 0;
        }
        return 0;
  }
   * Not that ugly ;) -- HW
   */
  static int
 -afavlab_setup(struct pci_dev *dev, struct pci_board *board,
 +afavlab_setup(struct serial_private *priv, struct pciserial_board *board,
              struct uart_port *port, int idx)
  {
        unsigned int bar, offset = board->first_offset;
                offset += (idx - 4) * board->uart_offset;
        }
  
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      return setup_port(priv, port, bar, offset, board->reg_shift);
  }
  
  /*
@@@ -164,13 -189,13 +164,13 @@@ static int __devinit pci_hp_diva_init(s
   * some serial ports are supposed to be hidden on certain models.
   */
  static int
 -pci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board,
 +pci_hp_diva_setup(struct serial_private *priv, 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 (dev->subsystem_device) {
 +      switch (priv->dev->subsystem_device) {
        case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
                if (idx == 3)
                        idx++;
  
        offset += idx * board->uart_offset;
  
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      return setup_port(priv, port, bar, offset, board->reg_shift);
  }
  
  /*
@@@ -282,7 -307,7 +282,7 @@@ static void __devexit pci_plx9050_exit(
  
  /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
  static int
 -sbs_setup(struct pci_dev *dev, struct pci_board *board,
 +sbs_setup(struct serial_private *priv, struct pciserial_board *board,
                struct uart_port *port, int idx)
  {
        unsigned int bar, offset = board->first_offset;
        } else /* we have only 8 ports on PMC-OCTALPRO */
                return 1;
  
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      return setup_port(priv, port, bar, offset, board->reg_shift);
  }
  
  /*
@@@ -364,9 -389,6 +364,9 @@@ static void __devexit sbs_exit(struct p
   *     - 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.
   *
@@@ -423,18 -445,24 +423,18 @@@ static int pci_siig20x_init(struct pci_
        return 0;
  }
  
 -int pci_siig10x_fn(struct pci_dev *dev, int enable)
 +static int pci_siig_init(struct pci_dev *dev)
  {
 -      int ret = 0;
 -      if (enable)
 -              ret = pci_siig10x_init(dev);
 -      return ret;
 -}
 +      unsigned int type = dev->device & 0xff00;
  
 -int pci_siig20x_fn(struct pci_dev *dev, int enable)
 -{
 -      int ret = 0;
 -      if (enable)
 -              ret = pci_siig20x_init(dev);
 -      return ret;
 -}
 +      if (type == 0x1000)
 +              return pci_siig10x_init(dev);
 +      else if (type == 0x2000)
 +              return pci_siig20x_init(dev);
  
 -EXPORT_SYMBOL(pci_siig10x_fn);
 -EXPORT_SYMBOL(pci_siig20x_fn);
 +      moan_device("Unknown SIIG card", dev);
 +      return -ENODEV;
 +}
  
  /*
   * Timedia has an explosion of boards, and to avoid the PCI table from
@@@ -495,7 -523,7 +495,7 @@@ static int __devinit pci_timedia_init(s
   * Ugh, this is ugly as all hell --- TYT
   */
  static int
 -pci_timedia_setup(struct pci_dev *dev, struct pci_board *board,
 +pci_timedia_setup(struct serial_private *priv, struct pciserial_board *board,
                  struct uart_port *port, int idx)
  {
        unsigned int bar = 0, offset = board->first_offset;
                bar = idx - 2;
        }
  
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      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 pci_dev *dev, struct pci_board *board,
 +titan_400l_800l_setup(struct serial_private *priv,
 +                    struct pciserial_board *board,
                      struct uart_port *port, int idx)
  {
        unsigned int bar, offset = board->first_offset;
                offset = (idx - 2) * board->uart_offset;
        }
  
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      return setup_port(priv, port, bar, offset, board->reg_shift);
  }
  
  static int __devinit pci_xircom_init(struct pci_dev *dev)
@@@ -566,7 -593,7 +566,7 @@@ static int __devinit pci_netmos_init(st
  }
  
  static int
 -pci_default_setup(struct pci_dev *dev, struct pci_board *board,
 +pci_default_setup(struct serial_private *priv, struct pciserial_board *board,
                  struct uart_port *port, int idx)
  {
        unsigned int bar, offset = board->first_offset, maxnr;
        else
                offset += idx * board->uart_offset;
  
 -      maxnr = (pci_resource_len(dev, bar) - board->first_offset) /
 +      maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) /
                (8 << board->reg_shift);
  
        if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
                return 1;
                        
 -      return setup_port(dev, port, bar, offset, board->reg_shift);
 +      return setup_port(priv, port, bar, offset, board->reg_shift);
  }
  
  /* This should be in linux/pci_ids.h */
@@@ -727,15 -754,152 +727,15 @@@ static struct pci_serial_quirk pci_seri
                .setup          = sbs_setup,
                .exit           = __devexit_p(sbs_exit),
        },
 -
        /*
         * SIIG cards.
 -       *  It is not clear whether these could be collapsed.
         */
        {
                .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_10x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_10x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_10x_850,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_10x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_10x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_10x_850,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_10x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_10x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_10x_850,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig10x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_20x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_20x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_1S_20x_850,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_20x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {       .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_20x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_2S_20x_850,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_20x_550,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_20x_650,
 -              .subvendor      = PCI_ANY_ID,
 -              .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 -              .setup          = pci_default_setup,
 -      },
 -      {
 -              .vendor         = PCI_VENDOR_ID_SIIG,
 -              .device         = PCI_DEVICE_ID_SIIG_4S_20x_850,
 +              .device         = PCI_ANY_ID,
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
 -              .init           = pci_siig20x_init,
 +              .init           = pci_siig_init,
                .setup          = pci_default_setup,
        },
        /*
@@@ -826,7 -990,7 +826,7 @@@ static struct pci_serial_quirk *find_qu
  }
  
  static _INLINE_ int
 -get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx)
 +get_pci_irq(struct pci_dev *dev, struct pciserial_board *board)
  {
        if (board->flags & FL_NOIRQ)
                return 0;
@@@ -865,6 -1029,8 +865,8 @@@ enum pci_board_num_t 
        pbn_b0_2_921600,
        pbn_b0_4_921600,
  
+       pbn_b0_2_1130000,
        pbn_b0_4_1152000,
  
        pbn_b0_bt_1_115200,
   * see first lines of serial_in() and serial_out() in 8250.c
  */
  
 -static struct pci_board pci_boards[] __devinitdata = {
 +static struct pciserial_board pci_boards[] __devinitdata = {
        [pbn_default] = {
                .flags          = FL_BASE0,
                .num_ports      = 1,
                .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,
   * serial specs.  Returns 0 on success, 1 on failure.
   */
  static int __devinit
 -serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
 +serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
  {
        int num_iomem, num_port, first_port = -1, i;
        
  }
  
  static inline int
 -serial_pci_matches(struct pci_board *board, struct pci_board *guessed)
 +serial_pci_matches(struct pciserial_board *board,
 +                 struct pciserial_board *guessed)
  {
        return
            board->num_ports == guessed->num_ports &&
            board->first_offset == guessed->first_offset;
  }
  
 -/*
 - * 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 *
 +pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board)
  {
 +      struct uart_port serial_port;
        struct serial_private *priv;
 -      struct pci_board *board, tmp;
        struct pci_serial_quirk *quirk;
        int rc, nr_ports, i;
  
 -      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 pci_board));
 -              board = &tmp;
 -
 -              /*
 -               * We matched one of our class entries.  Try to
 -               * determine the parameters of this board.
 -               */
 -              rc = serial_pci_guess_board(dev, board);
 -              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 pci_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);
 -      }
 -
        nr_ports = board->num_ports;
  
        /*
         */
        if (quirk->init) {
                rc = quirk->init(dev);
 -              if (rc < 0)
 -                      goto disable;
 +              if (rc < 0) {
 +                      priv = ERR_PTR(rc);
 +                      goto err_out;
 +              }
                if (rc)
                        nr_ports = rc;
        }
                       sizeof(unsigned int) * nr_ports,
                       GFP_KERNEL);
        if (!priv) {
 -              rc = -ENOMEM;
 -              goto deinit;
 +              priv = ERR_PTR(-ENOMEM);
 +              goto err_deinit;
        }
  
        memset(priv, 0, sizeof(struct serial_private) +
                        sizeof(unsigned int) * nr_ports);
  
 +      priv->dev = dev;
        priv->quirk = quirk;
 -      pci_set_drvdata(dev, priv);
 +
 +      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++) {
 -              struct uart_port serial_port;
 -              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, i);
 -              serial_port.dev = &dev->dev;
 -              if (quirk->setup(dev, board, &serial_port, i))
 +              if (quirk->setup(priv, board, &serial_port, i))
                        break;
 +
  #ifdef SERIAL_DEBUG_PCI
                printk("Setup PCI port: port %x, irq %d, type %d\n",
                       serial_port.iobase, serial_port.irq, serial_port.iotype);
  
        priv->nr = i;
  
 -      return 0;
 +      return priv;
  
 - deinit:
 + err_deinit:
        if (quirk->exit)
                quirk->exit(dev);
 - disable:
 -      pci_disable_device(dev);
 -      return rc;
 + err_out:
 +      return priv;
  }
 +EXPORT_SYMBOL_GPL(pciserial_init_ports);
  
 -static void __devexit pciserial_remove_one(struct pci_dev *dev)
 +void pciserial_remove_ports(struct serial_private *priv)
  {
 -      struct serial_private *priv = pci_get_drvdata(dev);
        struct pci_serial_quirk *quirk;
        int i;
  
 -      pci_set_drvdata(dev, NULL);
 -
        for (i = 0; i < priv->nr; i++)
                serial8250_unregister_port(priv->line[i]);
  
        /*
         * Find the exit quirks.
         */
 -      quirk = find_quirk(dev);
 +      quirk = find_quirk(priv->dev);
        if (quirk->exit)
 -              quirk->exit(dev);
 +              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;
 +      struct pciserial_board *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, board);
 +              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;
 +}
  
 -      kfree(priv);
 +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);
  }
  
  static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
  {
        struct serial_private *priv = pci_get_drvdata(dev);
  
 -      if (priv) {
 -              int i;
 +      if (priv)
 +              pciserial_suspend_ports(priv);
  
 -              for (i = 0; i < priv->nr; i++)
 -                      serial8250_suspend_port(priv->line[i]);
 -      }
        pci_save_state(dev);
        pci_set_power_state(dev, pci_choose_state(dev, state));
        return 0;
@@@ -1704,12 -1825,21 +1714,12 @@@ static int pciserial_resume_one(struct 
        pci_restore_state(dev);
  
        if (priv) {
 -              int i;
 -
                /*
                 * The device may have been disabled.  Re-enable it.
                 */
                pci_enable_device(dev);
  
 -              /*
 -               * Ensure that the board is correctly configured.
 -               */
 -              if (priv->quirk->init)
 -                      priv->quirk->init(dev);
 -
 -              for (i = 0; i < priv->nr; i++)
 -                      serial8250_resume_port(priv->line[i]);
 +              pciserial_resume_ports(priv);
        }
        return 0;
  }
@@@ -1868,6 -1998,16 +1878,16 @@@ static struct pci_device_id serial_pci_
        {       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_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_2_1130000 },
        {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
                PCI_ANY_ID, PCI_ANY_ID, 0, 0,
                pbn_b0_4_115200 },