summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-01-13 14:39:39 +0100
committerJesse Barnes <jbarnes@hobbes.lan>2009-03-19 19:29:21 -0700
commit90e9cd50f7feeddc911325c8a8c1b7e1fccc6599 (patch)
tree4a9536daa4ea8e8b96be4cb20f93ea6237663144 /drivers/pci
parent1bf83e558cb29d163f4bc6decbc3800ecf4db195 (diff)
downloadlinux-90e9cd50f7feeddc911325c8a8c1b7e1fccc6599.tar.bz2
PCI: PCIe portdrv: Aviod using service devices with wrong interrupts
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pcie/portdrv_core.c41
1 files changed, 28 insertions, 13 deletions
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 273e97619bce..265eba033a4a 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -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;
}