diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2008-09-11 10:51:41 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-09-19 06:59:15 -0400 |
commit | a80dc3e0e0dc8393158de317d66ae0f345dc58f9 (patch) | |
tree | d7e18a5d25a6dc1101fe699966a9b59b9b88a2f2 /arch/x86/kernel/amd_iommu_init.c | |
parent | 3eaf28a1cd2686aaa185b54d5a5e18e91b41f7f2 (diff) |
AMD IOMMU: add MSI interrupt support
The AMD IOMMU can generate interrupts for various reasons. This patch
adds the basic interrupt enabling infrastructure to the driver.
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/amd_iommu_init.c')
-rw-r--r-- | arch/x86/kernel/amd_iommu_init.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c index a7eb89d8923d..14a06464a694 100644 --- a/arch/x86/kernel/amd_iommu_init.c +++ b/arch/x86/kernel/amd_iommu_init.c | |||
@@ -22,6 +22,8 @@ | |||
22 | #include <linux/gfp.h> | 22 | #include <linux/gfp.h> |
23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
24 | #include <linux/sysdev.h> | 24 | #include <linux/sysdev.h> |
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/msi.h> | ||
25 | #include <asm/pci-direct.h> | 27 | #include <asm/pci-direct.h> |
26 | #include <asm/amd_iommu_types.h> | 28 | #include <asm/amd_iommu_types.h> |
27 | #include <asm/amd_iommu.h> | 29 | #include <asm/amd_iommu.h> |
@@ -515,17 +517,20 @@ static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m) | |||
515 | static void __init init_iommu_from_pci(struct amd_iommu *iommu) | 517 | static void __init init_iommu_from_pci(struct amd_iommu *iommu) |
516 | { | 518 | { |
517 | int cap_ptr = iommu->cap_ptr; | 519 | int cap_ptr = iommu->cap_ptr; |
518 | u32 range; | 520 | u32 range, misc; |
519 | 521 | ||
520 | pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET, | 522 | pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET, |
521 | &iommu->cap); | 523 | &iommu->cap); |
522 | pci_read_config_dword(iommu->dev, cap_ptr + MMIO_RANGE_OFFSET, | 524 | pci_read_config_dword(iommu->dev, cap_ptr + MMIO_RANGE_OFFSET, |
523 | &range); | 525 | &range); |
526 | pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET, | ||
527 | &misc); | ||
524 | 528 | ||
525 | iommu->first_device = calc_devid(MMIO_GET_BUS(range), | 529 | iommu->first_device = calc_devid(MMIO_GET_BUS(range), |
526 | MMIO_GET_FD(range)); | 530 | MMIO_GET_FD(range)); |
527 | iommu->last_device = calc_devid(MMIO_GET_BUS(range), | 531 | iommu->last_device = calc_devid(MMIO_GET_BUS(range), |
528 | MMIO_GET_LD(range)); | 532 | MMIO_GET_LD(range)); |
533 | iommu->evt_msi_num = MMIO_MSI_NUM(misc); | ||
529 | } | 534 | } |
530 | 535 | ||
531 | /* | 536 | /* |
@@ -696,6 +701,8 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) | |||
696 | if (!iommu->evt_buf) | 701 | if (!iommu->evt_buf) |
697 | return -ENOMEM; | 702 | return -ENOMEM; |
698 | 703 | ||
704 | iommu->int_enabled = false; | ||
705 | |||
699 | init_iommu_from_pci(iommu); | 706 | init_iommu_from_pci(iommu); |
700 | init_iommu_from_acpi(iommu, h); | 707 | init_iommu_from_acpi(iommu, h); |
701 | init_iommu_devices(iommu); | 708 | init_iommu_devices(iommu); |
@@ -743,6 +750,95 @@ static int __init init_iommu_all(struct acpi_table_header *table) | |||
743 | 750 | ||
744 | /**************************************************************************** | 751 | /**************************************************************************** |
745 | * | 752 | * |
753 | * The following functions initialize the MSI interrupts for all IOMMUs | ||
754 | * in the system. Its a bit challenging because there could be multiple | ||
755 | * IOMMUs per PCI BDF but we can call pci_enable_msi(x) only once per | ||
756 | * pci_dev. | ||
757 | * | ||
758 | ****************************************************************************/ | ||
759 | |||
760 | static int __init iommu_setup_msix(struct amd_iommu *iommu) | ||
761 | { | ||
762 | struct amd_iommu *curr; | ||
763 | struct msix_entry entries[32]; /* only 32 supported by AMD IOMMU */ | ||
764 | int nvec = 0, i; | ||
765 | |||
766 | list_for_each_entry(curr, &amd_iommu_list, list) { | ||
767 | if (curr->dev == iommu->dev) { | ||
768 | entries[nvec].entry = curr->evt_msi_num; | ||
769 | entries[nvec].vector = 0; | ||
770 | curr->int_enabled = true; | ||
771 | nvec++; | ||
772 | } | ||
773 | } | ||
774 | |||
775 | if (pci_enable_msix(iommu->dev, entries, nvec)) { | ||
776 | pci_disable_msix(iommu->dev); | ||
777 | return 1; | ||
778 | } | ||
779 | |||
780 | for (i = 0; i < nvec; ++i) { | ||
781 | int r = request_irq(entries->vector, amd_iommu_int_handler, | ||
782 | IRQF_SAMPLE_RANDOM, | ||
783 | "AMD IOMMU", | ||
784 | NULL); | ||
785 | if (r) | ||
786 | goto out_free; | ||
787 | } | ||
788 | |||
789 | return 0; | ||
790 | |||
791 | out_free: | ||
792 | for (i -= 1; i >= 0; --i) | ||
793 | free_irq(entries->vector, NULL); | ||
794 | |||
795 | pci_disable_msix(iommu->dev); | ||
796 | |||
797 | return 1; | ||
798 | } | ||
799 | |||
800 | static int __init iommu_setup_msi(struct amd_iommu *iommu) | ||
801 | { | ||
802 | int r; | ||
803 | struct amd_iommu *curr; | ||
804 | |||
805 | list_for_each_entry(curr, &amd_iommu_list, list) { | ||
806 | if (curr->dev == iommu->dev) | ||
807 | curr->int_enabled = true; | ||
808 | } | ||
809 | |||
810 | |||
811 | if (pci_enable_msi(iommu->dev)) | ||
812 | return 1; | ||
813 | |||
814 | r = request_irq(iommu->dev->irq, amd_iommu_int_handler, | ||
815 | IRQF_SAMPLE_RANDOM, | ||
816 | "AMD IOMMU", | ||
817 | NULL); | ||
818 | |||
819 | if (r) { | ||
820 | pci_disable_msi(iommu->dev); | ||
821 | return 1; | ||
822 | } | ||
823 | |||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static int __init iommu_init_msi(struct amd_iommu *iommu) | ||
828 | { | ||
829 | if (iommu->int_enabled) | ||
830 | return 0; | ||
831 | |||
832 | if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSIX)) | ||
833 | return iommu_setup_msix(iommu); | ||
834 | else if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI)) | ||
835 | return iommu_setup_msi(iommu); | ||
836 | |||
837 | return 1; | ||
838 | } | ||
839 | |||
840 | /**************************************************************************** | ||
841 | * | ||
746 | * The next functions belong to the third pass of parsing the ACPI | 842 | * The next functions belong to the third pass of parsing the ACPI |
747 | * table. In this last pass the memory mapping requirements are | 843 | * table. In this last pass the memory mapping requirements are |
748 | * gathered (like exclusion and unity mapping reanges). | 844 | * gathered (like exclusion and unity mapping reanges). |
@@ -862,6 +958,7 @@ static void __init enable_iommus(void) | |||
862 | 958 | ||
863 | list_for_each_entry(iommu, &amd_iommu_list, list) { | 959 | list_for_each_entry(iommu, &amd_iommu_list, list) { |
864 | iommu_set_exclusion_range(iommu); | 960 | iommu_set_exclusion_range(iommu); |
961 | iommu_init_msi(iommu); | ||
865 | iommu_enable(iommu); | 962 | iommu_enable(iommu); |
866 | } | 963 | } |
867 | } | 964 | } |