diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/msi.c | 46 |
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 | } |