diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2007-04-18 05:39:22 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-05-02 22:02:38 -0400 |
commit | 032de8e2fe3c0eec5fb0ffe4d38aa602dad397dc (patch) | |
tree | b9ad28ab3642c2dfba8e059fc72bd8e86c667449 /drivers/pci/msi.c | |
parent | 9c8313343c83c0ca731ceb8d2a4ab1e022ed9c94 (diff) |
MSI: Give archs the option to free all MSI/Xs at once.
This patch introduces an optional function, arch_teardown_msi_irqs(),
which gives an arch the opportunity to do per-device teardown for
MSI/X. If that's not required, the default version simply calls
arch_teardown_msi_irq() for each msi irq required.
arch_teardown_msi_irqs() is simply passed a pdev, attached to the pdev
is a list of msi_descs, it is up to the arch to free the irq associated
with each of these as appropriate.
For archs that _don't_ implement arch_teardown_msi_irqs(), all msi_descs
with irq == 0 are considered unallocated, and the arch teardown routine
is not called on them.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/msi.c')
-rw-r--r-- | drivers/pci/msi.c | 75 |
1 files changed, 39 insertions, 36 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index c71e8e4c7168..9e1321d0d5e6 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -207,7 +207,7 @@ void unmask_msi_irq(unsigned int irq) | |||
207 | msix_flush_writes(irq); | 207 | msix_flush_writes(irq); |
208 | } | 208 | } |
209 | 209 | ||
210 | static int msi_free_irq(struct pci_dev* dev, int irq); | 210 | static int msi_free_irqs(struct pci_dev* dev); |
211 | 211 | ||
212 | 212 | ||
213 | static struct msi_desc* alloc_msi_entry(void) | 213 | static struct msi_desc* alloc_msi_entry(void) |
@@ -339,8 +339,7 @@ static int msi_capability_init(struct pci_dev *dev) | |||
339 | /* Configure MSI capability structure */ | 339 | /* Configure MSI capability structure */ |
340 | ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); | 340 | ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI); |
341 | if (ret) { | 341 | if (ret) { |
342 | list_del(&entry->list); | 342 | msi_free_irqs(dev); |
343 | kfree(entry); | ||
344 | return ret; | 343 | return ret; |
345 | } | 344 | } |
346 | 345 | ||
@@ -415,10 +414,11 @@ static int msix_capability_init(struct pci_dev *dev, | |||
415 | list_for_each_entry(entry, &dev->msi_list, list) { | 414 | list_for_each_entry(entry, &dev->msi_list, list) { |
416 | if (entry->irq != 0) { | 415 | if (entry->irq != 0) { |
417 | avail++; | 416 | avail++; |
418 | msi_free_irq(dev, entry->irq); | ||
419 | } | 417 | } |
420 | } | 418 | } |
421 | 419 | ||
420 | msi_free_irqs(dev); | ||
421 | |||
422 | /* If we had some success report the number of irqs | 422 | /* If we had some success report the number of irqs |
423 | * we succeeded in setting up. | 423 | * we succeeded in setting up. |
424 | */ | 424 | */ |
@@ -539,39 +539,33 @@ void pci_disable_msi(struct pci_dev* dev) | |||
539 | } | 539 | } |
540 | 540 | ||
541 | default_irq = entry->msi_attrib.default_irq; | 541 | default_irq = entry->msi_attrib.default_irq; |
542 | msi_free_irq(dev, entry->irq); | 542 | msi_free_irqs(dev); |
543 | 543 | ||
544 | /* Restore dev->irq to its default pin-assertion irq */ | 544 | /* Restore dev->irq to its default pin-assertion irq */ |
545 | dev->irq = default_irq; | 545 | dev->irq = default_irq; |
546 | } | 546 | } |
547 | EXPORT_SYMBOL(pci_disable_msi); | 547 | EXPORT_SYMBOL(pci_disable_msi); |
548 | 548 | ||
549 | static int msi_free_irq(struct pci_dev* dev, int irq) | 549 | static int msi_free_irqs(struct pci_dev* dev) |
550 | { | 550 | { |
551 | struct msi_desc *entry; | 551 | struct msi_desc *entry, *tmp; |
552 | int entry_nr, type; | ||
553 | void __iomem *base; | ||
554 | |||
555 | BUG_ON(irq_has_action(irq)); | ||
556 | 552 | ||
557 | entry = get_irq_msi(irq); | 553 | list_for_each_entry(entry, &dev->msi_list, list) |
558 | if (!entry || entry->dev != dev) { | 554 | BUG_ON(irq_has_action(entry->irq)); |
559 | return -EINVAL; | ||
560 | } | ||
561 | type = entry->msi_attrib.type; | ||
562 | entry_nr = entry->msi_attrib.entry_nr; | ||
563 | base = entry->mask_base; | ||
564 | list_del(&entry->list); | ||
565 | 555 | ||
566 | arch_teardown_msi_irq(irq); | 556 | arch_teardown_msi_irqs(dev); |
567 | kfree(entry); | ||
568 | 557 | ||
569 | if (type == PCI_CAP_ID_MSIX) { | 558 | list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { |
570 | writel(1, base + entry_nr * PCI_MSIX_ENTRY_SIZE + | 559 | if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) { |
571 | PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); | 560 | if (list_is_last(&entry->list, &dev->msi_list)) |
561 | iounmap(entry->mask_base); | ||
572 | 562 | ||
573 | if (list_empty(&dev->msi_list)) | 563 | writel(1, entry->mask_base + entry->msi_attrib.entry_nr |
574 | iounmap(base); | 564 | * PCI_MSIX_ENTRY_SIZE |
565 | + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); | ||
566 | } | ||
567 | list_del(&entry->list); | ||
568 | kfree(entry); | ||
575 | } | 569 | } |
576 | 570 | ||
577 | return 0; | 571 | return 0; |
@@ -636,10 +630,7 @@ EXPORT_SYMBOL(pci_enable_msix); | |||
636 | 630 | ||
637 | static void msix_free_all_irqs(struct pci_dev *dev) | 631 | static void msix_free_all_irqs(struct pci_dev *dev) |
638 | { | 632 | { |
639 | struct msi_desc *entry; | 633 | msi_free_irqs(dev); |
640 | |||
641 | list_for_each_entry(entry, &dev->msi_list, list) | ||
642 | msi_free_irq(dev, entry->irq); | ||
643 | } | 634 | } |
644 | 635 | ||
645 | void pci_disable_msix(struct pci_dev* dev) | 636 | void pci_disable_msix(struct pci_dev* dev) |
@@ -669,12 +660,8 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) | |||
669 | if (!pci_msi_enable || !dev) | 660 | if (!pci_msi_enable || !dev) |
670 | return; | 661 | return; |
671 | 662 | ||
672 | if (dev->msi_enabled) { | 663 | if (dev->msi_enabled) |
673 | struct msi_desc *entry; | 664 | msi_free_irqs(dev); |
674 | BUG_ON(list_empty(&dev->msi_list)); | ||
675 | entry = list_entry(dev->msi_list.next, struct msi_desc, list); | ||
676 | msi_free_irq(dev, entry->irq); | ||
677 | } | ||
678 | 665 | ||
679 | if (dev->msix_enabled) | 666 | if (dev->msix_enabled) |
680 | msix_free_all_irqs(dev); | 667 | msix_free_all_irqs(dev); |
@@ -719,3 +706,19 @@ arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
719 | 706 | ||
720 | return 0; | 707 | return 0; |
721 | } | 708 | } |
709 | |||
710 | void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq) | ||
711 | { | ||
712 | return; | ||
713 | } | ||
714 | |||
715 | void __attribute__ ((weak)) | ||
716 | arch_teardown_msi_irqs(struct pci_dev *dev) | ||
717 | { | ||
718 | struct msi_desc *entry; | ||
719 | |||
720 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
721 | if (entry->irq != 0) | ||
722 | arch_teardown_msi_irq(entry->irq); | ||
723 | } | ||
724 | } | ||