Add blacklisting capability to serial_pci to avoid misdetection of serial ports
[pandora-kernel.git] / drivers / serial / 8250_pci.c
index 5e48587..e3140e5 100644 (file)
@@ -580,6 +580,138 @@ static int pci_netmos_init(struct pci_dev *dev)
        return num_serial;
 }
 
+/*
+ * ITE support by Niels de Vos <niels.devos@wincor-nixdorf.com>
+ *
+ * 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 __devinit 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);
+}
+
 static int
 pci_default_setup(struct serial_private *priv, struct pciserial_board *board,
                  struct uart_port *port, int idx)
@@ -652,6 +784,18 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
                .init           = pci_inteli960ni_init,
                .setup          = pci_default_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),
+       },
        /*
         * Panacom
         */
@@ -933,6 +1077,7 @@ enum pci_board_num_t {
 
        pbn_b1_2_1250000,
 
+       pbn_b1_bt_1_115200,
        pbn_b1_bt_2_921600,
 
        pbn_b1_1_1382400,
@@ -1211,6 +1356,13 @@ static struct pciserial_board pci_boards[] __devinitdata = {
                .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_921600] = {
                .flags          = FL_BASE1|FL_BASE_BARS,
                .num_ports      = 2,
@@ -1500,6 +1652,10 @@ static struct pciserial_board pci_boards[] __devinitdata = {
        },
 };
 
+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
@@ -1508,6 +1664,7 @@ static struct pciserial_board pci_boards[] __devinitdata = {
 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;
        
        /*
@@ -1522,6 +1679,18 @@ serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
            (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) {
@@ -2364,6 +2533,13 @@ static struct pci_device_id serial_pci_tbl[] = {
        {       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