PCI: PCIe portdrv: Aviod using service devices with wrong interrupts
authorRafael J. Wysocki <rjw@sisk.pl>
Tue, 13 Jan 2009 13:39:39 +0000 (14:39 +0100)
committerJesse Barnes <jbarnes@hobbes.lan>
Fri, 20 Mar 2009 02:29:21 +0000 (19:29 -0700)
The PCI Express port driver should not attempt to register service
devices that require the ability to generate interrupts if generating
interrupts is not possible.  Namely, if the port has no interrupt pin
configured and we cannot set up MSI or MSI-X for it, there is no way
it can generate interrupts and in such a case the port services that
rely on interrupts (PME, PCIe HP, AER) should not be enabled for it.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/pcie/portdrv_core.c
include/linux/pcieport_if.h

index 273e97619bce04be5276970c0fdac0261ea2e007..265eba033a4ac5a4e2f7f71a5536555e1712649b 100644 (file)
@@ -43,7 +43,7 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
 {
        struct pcie_port_data *port_data = pci_get_drvdata(dev);
        int i, pos, nvec, status = -EINVAL;
-       int interrupt_mode = PCIE_PORT_INTx_MODE;
+       int interrupt_mode = PCIE_PORT_NO_IRQ;
 
        /* Set INTx as default */
        for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
@@ -51,7 +51,9 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
                        nvec++;
                vectors[i] = dev->irq;
        }
-       
+       if (dev->pin)
+               interrupt_mode = PCIE_PORT_INTx_MODE;
+
        /* Check MSI quirk */
        if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk)
                return interrupt_mode;
@@ -141,7 +143,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
        dev->id.vendor = parent->vendor;
        dev->id.device = parent->device;
        dev->id.port_type = port_type;
-       dev->id.service_type = (1 << service_type);
+       dev->id.service_type = service_type;
 
        /* Initialize generic device interface */
        device = &dev->device;
@@ -232,19 +234,32 @@ int pcie_port_device_register(struct pci_dev *dev)
        /* Allocate child services if any */
        for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
                struct pcie_device *child;
+               int service = 1 << i;
 
-               if (capabilities & (1 << i)) {
-                       child = alloc_pcie_device(dev, i, vectors[i]);
-                       if (child) {
-                               status = device_register(&child->device);
-                               if (status) {
-                                       kfree(child);
-                                       continue;
-                               }
-                               get_device(&child->device);
-                       }
+               if (!(capabilities & service))
+                       continue;
+
+               /*
+                * Don't use service devices that require interrupts if there is
+                * no way to generate them.
+                */
+               if (irq_mode == PCIE_PORT_NO_IRQ
+                   && service != PCIE_PORT_SERVICE_VC)
+                       continue;
+
+               child = alloc_pcie_device(dev, service, vectors[i]);
+               if (!child)
+                       continue;
+
+               status = device_register(&child->device);
+               if (status) {
+                       kfree(child);
+                       continue;
                }
+
+               get_device(&child->device);
        }
+
        return 0;
 }
 
index 194409af1037527174c792614d8266b25ed16d75..8e1ae1fd92f6e5f799625de7b4de5c3f71d4ff07 100644 (file)
@@ -22,6 +22,7 @@
 #define PCIE_PORT_SERVICE_VC           8       /* Virtual Channel */
 
 /* Root/Upstream/Downstream Port's Interrupt Mode */
+#define PCIE_PORT_NO_IRQ               (-1)
 #define PCIE_PORT_INTx_MODE            0
 #define PCIE_PORT_MSI_MODE             1
 #define PCIE_PORT_MSIX_MODE            2