aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-01-13 08:39:39 -0500
committerJesse Barnes <jbarnes@hobbes.lan>2009-03-19 22:29:21 -0400
commit90e9cd50f7feeddc911325c8a8c1b7e1fccc6599 (patch)
tree4a9536daa4ea8e8b96be4cb20f93ea6237663144
parent1bf83e558cb29d163f4bc6decbc3800ecf4db195 (diff)
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>
-rw-r--r--drivers/pci/pcie/portdrv_core.c41
-rw-r--r--include/linux/pcieport_if.h1
2 files changed, 29 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)
43{ 43{
44 struct pcie_port_data *port_data = pci_get_drvdata(dev); 44 struct pcie_port_data *port_data = pci_get_drvdata(dev);
45 int i, pos, nvec, status = -EINVAL; 45 int i, pos, nvec, status = -EINVAL;
46 int interrupt_mode = PCIE_PORT_INTx_MODE; 46 int interrupt_mode = PCIE_PORT_NO_IRQ;
47 47
48 /* Set INTx as default */ 48 /* Set INTx as default */
49 for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { 49 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)
51 nvec++; 51 nvec++;
52 vectors[i] = dev->irq; 52 vectors[i] = dev->irq;
53 } 53 }
54 54 if (dev->pin)
55 interrupt_mode = PCIE_PORT_INTx_MODE;
56
55 /* Check MSI quirk */ 57 /* Check MSI quirk */
56 if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) 58 if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk)
57 return interrupt_mode; 59 return interrupt_mode;
@@ -141,7 +143,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
141 dev->id.vendor = parent->vendor; 143 dev->id.vendor = parent->vendor;
142 dev->id.device = parent->device; 144 dev->id.device = parent->device;
143 dev->id.port_type = port_type; 145 dev->id.port_type = port_type;
144 dev->id.service_type = (1 << service_type); 146 dev->id.service_type = service_type;
145 147
146 /* Initialize generic device interface */ 148 /* Initialize generic device interface */
147 device = &dev->device; 149 device = &dev->device;
@@ -232,19 +234,32 @@ int pcie_port_device_register(struct pci_dev *dev)
232 /* Allocate child services if any */ 234 /* Allocate child services if any */
233 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { 235 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
234 struct pcie_device *child; 236 struct pcie_device *child;
237 int service = 1 << i;
235 238
236 if (capabilities & (1 << i)) { 239 if (!(capabilities & service))
237 child = alloc_pcie_device(dev, i, vectors[i]); 240 continue;
238 if (child) { 241
239 status = device_register(&child->device); 242 /*
240 if (status) { 243 * Don't use service devices that require interrupts if there is
241 kfree(child); 244 * no way to generate them.
242 continue; 245 */
243 } 246 if (irq_mode == PCIE_PORT_NO_IRQ
244 get_device(&child->device); 247 && service != PCIE_PORT_SERVICE_VC)
245 } 248 continue;
249
250 child = alloc_pcie_device(dev, service, vectors[i]);
251 if (!child)
252 continue;
253
254 status = device_register(&child->device);
255 if (status) {
256 kfree(child);
257 continue;
246 } 258 }
259
260 get_device(&child->device);
247 } 261 }
262
248 return 0; 263 return 0;
249} 264}
250 265
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h
index 194409af1037..8e1ae1fd92f6 100644
--- a/include/linux/pcieport_if.h
+++ b/include/linux/pcieport_if.h
@@ -22,6 +22,7 @@
22#define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */ 22#define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */
23 23
24/* Root/Upstream/Downstream Port's Interrupt Mode */ 24/* Root/Upstream/Downstream Port's Interrupt Mode */
25#define PCIE_PORT_NO_IRQ (-1)
25#define PCIE_PORT_INTx_MODE 0 26#define PCIE_PORT_INTx_MODE 0
26#define PCIE_PORT_MSI_MODE 1 27#define PCIE_PORT_MSI_MODE 1
27#define PCIE_PORT_MSIX_MODE 2 28#define PCIE_PORT_MSIX_MODE 2