aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorMatthew Wilcox <willy@linux.intel.com>2009-06-18 22:15:59 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-06-19 18:11:39 -0400
commitf598282f5145036312d90875d0ed5c14b49fd8a7 (patch)
tree3cb76a5a2cbd161ce03007370f5e38c133efe21d /drivers/pci
parent5c92ffb1ecc7f13267cdef5dda8a838937912c93 (diff)
PCI: Fix the NIU MSI-X problem in a better way
The previous MSI-X fix (8d181018532dd709ec1f789e374cda92d7b01ce1) had three bugs. First, it didn't move the write that disabled the vector. This led to writing garbage to the MSI-X vector (spotted by Michael Ellerman). It didn't fix the PCI resume case, and it had a race window where the device could generate an interrupt before the MSI-X registers were programmed (leading to a DMA to random addresses). Fortunately, the MSI-X capability has a bit to mask all the vectors. By setting this bit instead of clearing the enable bit, we can ensure the device will not generate spurious interrupts. Since the capability is now enabled, the NIU device will not have a problem with the reads and writes to the MSI-X registers being in the original order in the code. Signed-off-by: Matthew Wilcox <willy@linux.intel.com> Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/msi.c46
1 files changed, 28 insertions, 18 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 79e56906c7f1..944e45e4a84f 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -313,22 +313,22 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
313 313
314 if (!dev->msix_enabled) 314 if (!dev->msix_enabled)
315 return; 315 return;
316 BUG_ON(list_empty(&dev->msi_list));
317 entry = list_entry(dev->msi_list.next, struct msi_desc, list);
318 pos = entry->msi_attrib.pos;
319 pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
316 320
317 /* route the table */ 321 /* route the table */
318 pci_intx_for_msi(dev, 0); 322 pci_intx_for_msi(dev, 0);
319 msix_set_enable(dev, 0); 323 control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL;
324 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
320 325
321 list_for_each_entry(entry, &dev->msi_list, list) { 326 list_for_each_entry(entry, &dev->msi_list, list) {
322 write_msi_msg(entry->irq, &entry->msg); 327 write_msi_msg(entry->irq, &entry->msg);
323 msix_mask_irq(entry, entry->masked); 328 msix_mask_irq(entry, entry->masked);
324 } 329 }
325 330
326 BUG_ON(list_empty(&dev->msi_list));
327 entry = list_entry(dev->msi_list.next, struct msi_desc, list);
328 pos = entry->msi_attrib.pos;
329 pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
330 control &= ~PCI_MSIX_FLAGS_MASKALL; 331 control &= ~PCI_MSIX_FLAGS_MASKALL;
331 control |= PCI_MSIX_FLAGS_ENABLE;
332 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); 332 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
333} 333}
334 334
@@ -419,11 +419,14 @@ static int msix_capability_init(struct pci_dev *dev,
419 u8 bir; 419 u8 bir;
420 void __iomem *base; 420 void __iomem *base;
421 421
422 msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */
423
424 pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); 422 pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
423 pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
424
425 /* Ensure MSI-X is disabled while it is set up */
426 control &= ~PCI_MSIX_FLAGS_ENABLE;
427 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
428
425 /* Request & Map MSI-X table region */ 429 /* Request & Map MSI-X table region */
426 pci_read_config_word(dev, msi_control_reg(pos), &control);
427 nr_entries = multi_msix_capable(control); 430 nr_entries = multi_msix_capable(control);
428 431
429 pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); 432 pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset);
@@ -434,7 +437,6 @@ static int msix_capability_init(struct pci_dev *dev,
434 if (base == NULL) 437 if (base == NULL)
435 return -ENOMEM; 438 return -ENOMEM;
436 439
437 /* MSI-X Table Initialization */
438 for (i = 0; i < nvec; i++) { 440 for (i = 0; i < nvec; i++) {
439 entry = alloc_msi_entry(dev); 441 entry = alloc_msi_entry(dev);
440 if (!entry) 442 if (!entry)
@@ -447,7 +449,6 @@ static int msix_capability_init(struct pci_dev *dev,
447 entry->msi_attrib.default_irq = dev->irq; 449 entry->msi_attrib.default_irq = dev->irq;
448 entry->msi_attrib.pos = pos; 450 entry->msi_attrib.pos = pos;
449 entry->mask_base = base; 451 entry->mask_base = base;
450 msix_mask_irq(entry, 1);
451 452
452 list_add_tail(&entry->list, &dev->msi_list); 453 list_add_tail(&entry->list, &dev->msi_list);
453 } 454 }
@@ -472,22 +473,31 @@ static int msix_capability_init(struct pci_dev *dev,
472 return ret; 473 return ret;
473 } 474 }
474 475
476 /*
477 * Some devices require MSI-X to be enabled before we can touch the
478 * MSI-X registers. We need to mask all the vectors to prevent
479 * interrupts coming in before they're fully set up.
480 */
481 control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE;
482 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
483
475 i = 0; 484 i = 0;
476 list_for_each_entry(entry, &dev->msi_list, list) { 485 list_for_each_entry(entry, &dev->msi_list, list) {
477 entries[i].vector = entry->irq; 486 entries[i].vector = entry->irq;
478 set_irq_msi(entry->irq, entry); 487 set_irq_msi(entry->irq, entry);
488 j = entries[i].entry;
489 entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE +
490 PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
491 msix_mask_irq(entry, 1);
479 i++; 492 i++;
480 } 493 }
481 /* Set MSI-X enabled bits */ 494
495 /* Set MSI-X enabled bits and unmask the function */
482 pci_intx_for_msi(dev, 0); 496 pci_intx_for_msi(dev, 0);
483 msix_set_enable(dev, 1);
484 dev->msix_enabled = 1; 497 dev->msix_enabled = 1;
485 498
486 list_for_each_entry(entry, &dev->msi_list, list) { 499 control &= ~PCI_MSIX_FLAGS_MASKALL;
487 int vector = entry->msi_attrib.entry_nr; 500 pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
488 entry->masked = readl(base + vector * PCI_MSIX_ENTRY_SIZE +
489 PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
490 }
491 501
492 return 0; 502 return 0;
493} 503}