Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6
[deliverable/linux.git] / drivers / tty / serial / 8250_pci.c
index cf35e0dc50854bbf536ab004243fdfcdb76f42c1..6b887d90a20554683975d921c7ca492bba3ccf68 100644 (file)
@@ -39,6 +39,7 @@ struct pci_serial_quirk {
        u32     device;
        u32     subvendor;
        u32     subdevice;
+       int     (*probe)(struct pci_dev *dev);
        int     (*init)(struct pci_dev *dev);
        int     (*setup)(struct serial_private *,
                         const struct pciserial_board *,
@@ -56,6 +57,9 @@ struct serial_private {
        int                     line[0];
 };
 
+static int pci_default_setup(struct serial_private*,
+         const struct pciserial_board*, struct uart_port*, int);
+
 static void moan_device(const char *str, struct pci_dev *dev)
 {
        printk(KERN_WARNING
@@ -571,6 +575,28 @@ static const struct timedia_struct {
        { 8, timedia_eight_port }
 };
 
+/*
+ * There are nearly 70 different Timedia/SUNIX PCI serial devices.  Instead of
+ * listing them individually, this driver merely grabs them all with
+ * PCI_ANY_ID.  Some of these devices, however, also feature a parallel port,
+ * and should be left free to be claimed by parport_serial instead.
+ */
+static int pci_timedia_probe(struct pci_dev *dev)
+{
+       /*
+        * Check the third digit of the subdevice ID
+        * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel)
+        */
+       if ((dev->subsystem_device & 0x00f0) >= 0x70) {
+               dev_info(&dev->dev,
+                       "ignoring Timedia subdevice %04x for parport_serial\n",
+                       dev->subsystem_device);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
 static int pci_timedia_init(struct pci_dev *dev)
 {
        const unsigned short *ids;
@@ -752,6 +778,62 @@ pci_ni8430_setup(struct serial_private *priv,
        return setup_port(priv, port, bar, offset, board->reg_shift);
 }
 
+static int pci_netmos_9900_setup(struct serial_private *priv,
+                               const struct pciserial_board *board,
+                               struct uart_port *port, int idx)
+{
+       unsigned int bar;
+
+       if ((priv->dev->subsystem_device & 0xff00) == 0x3000) {
+               /* netmos apparently orders BARs by datasheet layout, so serial
+                * ports get BARs 0 and 3 (or 1 and 4 for memmapped)
+                */
+               bar = 3 * idx;
+
+               return setup_port(priv, port, bar, 0, board->reg_shift);
+       } else {
+               return pci_default_setup(priv, board, port, idx);
+       }
+}
+
+/* the 99xx series comes with a range of device IDs and a variety
+ * of capabilities:
+ *
+ * 9900 has varying capabilities and can cascade to sub-controllers
+ *   (cascading should be purely internal)
+ * 9904 is hardwired with 4 serial ports
+ * 9912 and 9922 are hardwired with 2 serial ports
+ */
+static int pci_netmos_9900_numports(struct pci_dev *dev)
+{
+       unsigned int c = dev->class;
+       unsigned int pi;
+       unsigned short sub_serports;
+
+       pi = (c & 0xff);
+
+       if (pi == 2) {
+               return 1;
+       } else if ((pi == 0) &&
+                          (dev->device == PCI_DEVICE_ID_NETMOS_9900)) {
+               /* two possibilities: 0x30ps encodes number of parallel and
+                * serial ports, or 0x1000 indicates *something*. This is not
+                * immediately obvious, since the 2s1p+4s configuration seems
+                * to offer all functionality on functions 0..2, while still
+                * advertising the same function 3 as the 4s+2s1p config.
+                */
+               sub_serports = dev->subsystem_device & 0xf;
+               if (sub_serports > 0) {
+                       return sub_serports;
+               } else {
+                       printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
+                       return 0;
+               }
+       }
+
+       moan_device("unknown NetMos/Mostech program interface", dev);
+       return 0;
+}
 
 static int pci_netmos_init(struct pci_dev *dev)
 {
@@ -761,12 +843,28 @@ static int pci_netmos_init(struct pci_dev *dev)
        if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) ||
                (dev->device == PCI_DEVICE_ID_NETMOS_9865))
                return 0;
+
        if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
                        dev->subsystem_device == 0x0299)
                return 0;
 
+       switch (dev->device) { /* FALLTHROUGH on all */
+               case PCI_DEVICE_ID_NETMOS_9904:
+               case PCI_DEVICE_ID_NETMOS_9912:
+               case PCI_DEVICE_ID_NETMOS_9922:
+               case PCI_DEVICE_ID_NETMOS_9900:
+                       num_serial = pci_netmos_9900_numports(dev);
+                       break;
+
+               default:
+                       if (num_serial == 0 ) {
+                               moan_device("unknown NetMos/Mostech device", dev);
+                       }
+       }
+
        if (num_serial == 0)
                return -ENODEV;
+
        return num_serial;
 }
 
@@ -1396,6 +1494,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .device         = PCI_DEVICE_ID_TIMEDIA_1889,
                .subvendor      = PCI_VENDOR_ID_TIMEDIA,
                .subdevice      = PCI_ANY_ID,
+               .probe          = pci_timedia_probe,
                .init           = pci_timedia_init,
                .setup          = pci_timedia_setup,
        },
@@ -1426,7 +1525,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
                .init           = pci_netmos_init,
-               .setup          = pci_default_setup,
+               .setup          = pci_netmos_9900_setup,
        },
        /*
         * For Oxford Semiconductor Tornado based devices
@@ -1703,6 +1802,7 @@ enum pci_board_num_t {
        pbn_ADDIDATA_PCIe_8_3906250,
        pbn_ce4100_1_115200,
        pbn_omegapci,
+       pbn_NETMOS9900_2s_115200,
 };
 
 /*
@@ -2404,6 +2504,11 @@ static struct pciserial_board pci_boards[] __devinitdata = {
                .base_baud      = 115200,
                .uart_offset    = 0x200,
        },
+       [pbn_NETMOS9900_2s_115200] = {
+               .flags          = FL_BASE0,
+               .num_ports      = 2,
+               .base_baud      = 115200,
+       },
 };
 
 static const struct pci_device_id softmodem_blacklist[] = {
@@ -2640,11 +2745,19 @@ EXPORT_SYMBOL_GPL(pciserial_resume_ports);
 static int __devinit
 pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
 {
+       struct pci_serial_quirk *quirk;
        struct serial_private *priv;
        const struct pciserial_board *board;
        struct pciserial_board tmp;
        int rc;
 
+       quirk = find_quirk(dev);
+       if (quirk->probe) {
+               rc = quirk->probe(dev);
+               if (rc)
+                       return rc;
+       }
+
        if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
                printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
                        ent->driver_data);
@@ -2654,6 +2767,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
        board = &pci_boards[ent->driver_data];
 
        rc = pci_enable_device(dev);
+       pci_save_state(dev);
        if (rc)
                return rc;
 
@@ -3885,6 +3999,27 @@ static struct pci_device_id serial_pci_tbl[] = {
                0xA000, 0x1000,
                0, 0, pbn_b0_1_115200 },
 
+       /* the 9901 is a rebranded 9912 */
+       {       PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
+               0xA000, 0x1000,
+               0, 0, pbn_b0_1_115200 },
+
+       {       PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922,
+               0xA000, 0x1000,
+               0, 0, pbn_b0_1_115200 },
+
+       {       PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904,
+               0xA000, 0x1000,
+               0, 0, pbn_b0_1_115200 },
+
+       {       PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+               0xA000, 0x1000,
+               0, 0, pbn_b0_1_115200 },
+
+       {       PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+               0xA000, 0x3002,
+               0, 0, pbn_NETMOS9900_2s_115200 },
+
        /*
         * Best Connectivity PCI Multi I/O cards
         */
@@ -3927,6 +4062,51 @@ static struct pci_device_id serial_pci_tbl[] = {
        { 0, }
 };
 
+static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev,
+                                               pci_channel_state_t state)
+{
+       struct serial_private *priv = pci_get_drvdata(dev);
+
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       if (priv)
+               pciserial_suspend_ports(priv);
+
+       pci_disable_device(dev);
+
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev)
+{
+       int rc;
+
+       rc = pci_enable_device(dev);
+
+       if (rc)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       pci_restore_state(dev);
+       pci_save_state(dev);
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void serial8250_io_resume(struct pci_dev *dev)
+{
+       struct serial_private *priv = pci_get_drvdata(dev);
+
+       if (priv)
+               pciserial_resume_ports(priv);
+}
+
+static struct pci_error_handlers serial8250_err_handler = {
+       .error_detected = serial8250_io_error_detected,
+       .slot_reset = serial8250_io_slot_reset,
+       .resume = serial8250_io_resume,
+};
+
 static struct pci_driver serial_pci_driver = {
        .name           = "serial",
        .probe          = pciserial_init_one,
@@ -3936,6 +4116,7 @@ static struct pci_driver serial_pci_driver = {
        .resume         = pciserial_resume_one,
 #endif
        .id_table       = serial_pci_tbl,
+       .err_handler    = &serial8250_err_handler,
 };
 
 static int __init serial8250_pci_init(void)
This page took 0.039658 seconds and 5 git commands to generate.