diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2009-01-13 08:42:01 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@hobbes.lan> | 2009-03-19 22:29:22 -0400 |
commit | f118c0c3cff4fed39bde1863f9d59850719645cc (patch) | |
tree | aef3aaa33d5c20c21d222e43ce295693d1f0d718 /drivers/pci/pcie | |
parent | 90e9cd50f7feeddc911325c8a8c1b7e1fccc6599 (diff) |
PCI: PCIe portdrv: Do not enable port device before setting up interrupts
The PCI Express port driver calls pci_enable_device() before setting
up interrupts, which is wrong, because if there is an interrupt pin
configured for the port, pci_enable_device() will likely set up an
interrupt link for it. However, this shouldn't be done if either
MSI or MSI-X interrupt mode is chosen for the port.
The solution is to call pci_enable_device() after setting up
interrupts, because in that case the interrupt link won't be set up
if MSI or MSI-X are enabled.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 38 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 11 |
2 files changed, 31 insertions, 18 deletions
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 265eba033a4a..91ecbc43155f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c | |||
@@ -208,7 +208,7 @@ int pcie_port_device_probe(struct pci_dev *dev) | |||
208 | int pcie_port_device_register(struct pci_dev *dev) | 208 | int pcie_port_device_register(struct pci_dev *dev) |
209 | { | 209 | { |
210 | struct pcie_port_data *port_data; | 210 | struct pcie_port_data *port_data; |
211 | int status, capabilities, irq_mode, i; | 211 | int status, capabilities, irq_mode, i, nr_serv; |
212 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; | 212 | int vectors[PCIE_PORT_DEVICE_MAXSERVICES]; |
213 | u16 reg16; | 213 | u16 reg16; |
214 | 214 | ||
@@ -229,24 +229,32 @@ int pcie_port_device_register(struct pci_dev *dev) | |||
229 | capabilities |= PCIE_PORT_SERVICE_PME; | 229 | capabilities |= PCIE_PORT_SERVICE_PME; |
230 | 230 | ||
231 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); | 231 | irq_mode = assign_interrupt_mode(dev, vectors, capabilities); |
232 | if (irq_mode == PCIE_PORT_NO_IRQ) { | ||
233 | /* | ||
234 | * Don't use service devices that require interrupts if there is | ||
235 | * no way to generate them. | ||
236 | */ | ||
237 | if (!(capabilities & PCIE_PORT_SERVICE_VC)) { | ||
238 | status = -ENODEV; | ||
239 | goto Error; | ||
240 | } | ||
241 | capabilities = PCIE_PORT_SERVICE_VC; | ||
242 | } | ||
232 | port_data->port_irq_mode = irq_mode; | 243 | port_data->port_irq_mode = irq_mode; |
233 | 244 | ||
245 | status = pci_enable_device(dev); | ||
246 | if (status) | ||
247 | goto Error; | ||
248 | pci_set_master(dev); | ||
249 | |||
234 | /* Allocate child services if any */ | 250 | /* Allocate child services if any */ |
235 | for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { | 251 | for (i = 0, nr_serv = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { |
236 | struct pcie_device *child; | 252 | struct pcie_device *child; |
237 | int service = 1 << i; | 253 | int service = 1 << i; |
238 | 254 | ||
239 | if (!(capabilities & service)) | 255 | if (!(capabilities & service)) |
240 | continue; | 256 | continue; |
241 | 257 | ||
242 | /* | ||
243 | * Don't use service devices that require interrupts if there is | ||
244 | * no way to generate them. | ||
245 | */ | ||
246 | if (irq_mode == PCIE_PORT_NO_IRQ | ||
247 | && service != PCIE_PORT_SERVICE_VC) | ||
248 | continue; | ||
249 | |||
250 | child = alloc_pcie_device(dev, service, vectors[i]); | 258 | child = alloc_pcie_device(dev, service, vectors[i]); |
251 | if (!child) | 259 | if (!child) |
252 | continue; | 260 | continue; |
@@ -258,9 +266,19 @@ int pcie_port_device_register(struct pci_dev *dev) | |||
258 | } | 266 | } |
259 | 267 | ||
260 | get_device(&child->device); | 268 | get_device(&child->device); |
269 | nr_serv++; | ||
270 | } | ||
271 | if (!nr_serv) { | ||
272 | pci_disable_device(dev); | ||
273 | status = -ENODEV; | ||
274 | goto Error; | ||
261 | } | 275 | } |
262 | 276 | ||
263 | return 0; | 277 | return 0; |
278 | |||
279 | Error: | ||
280 | kfree(port_data); | ||
281 | return status; | ||
264 | } | 282 | } |
265 | 283 | ||
266 | #ifdef CONFIG_PM | 284 | #ifdef CONFIG_PM |
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 5ea566e20b37..d17611f390bd 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c | |||
@@ -82,18 +82,13 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, | |||
82 | if (status) | 82 | if (status) |
83 | return status; | 83 | return status; |
84 | 84 | ||
85 | if (pci_enable_device(dev) < 0) | ||
86 | return -ENODEV; | ||
87 | |||
88 | pci_set_master(dev); | ||
89 | if (!dev->irq && dev->pin) { | 85 | if (!dev->irq && dev->pin) { |
90 | dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; " | 86 | dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; " |
91 | "check vendor BIOS\n", dev->vendor, dev->device); | 87 | "check vendor BIOS\n", dev->vendor, dev->device); |
92 | } | 88 | } |
93 | if (pcie_port_device_register(dev)) { | 89 | status = pcie_port_device_register(dev); |
94 | pci_disable_device(dev); | 90 | if (status) |
95 | return -ENOMEM; | 91 | return status; |
96 | } | ||
97 | 92 | ||
98 | pcie_portdrv_save_config(dev); | 93 | pcie_portdrv_save_config(dev); |
99 | 94 | ||