diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2008-07-10 14:16:43 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-12 02:44:53 -0400 |
commit | 2ae21010694e56461a63bfc80e960090ce0a5ed9 (patch) | |
tree | d4ecdb710c4361df473b063eda9e1426fcf5c309 /drivers/pci/intr_remapping.c | |
parent | fe962e90cb17a8426e144dee970e77ed789d98ee (diff) |
x64, x2apic/intr-remap: Interrupt remapping infrastructure
Interrupt remapping (part of Intel Virtualization Tech for directed I/O)
infrastructure.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: akpm@linux-foundation.org
Cc: arjan@linux.intel.com
Cc: andi@firstfloor.org
Cc: ebiederm@xmission.com
Cc: jbarnes@virtuousgeek.org
Cc: steiner@sgi.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/pci/intr_remapping.c')
-rw-r--r-- | drivers/pci/intr_remapping.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index a80b87921c68..3d10cdc90314 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c | |||
@@ -1,10 +1,147 @@ | |||
1 | #include <linux/dmar.h> | 1 | #include <linux/dmar.h> |
2 | #include <linux/spinlock.h> | ||
3 | #include <linux/jiffies.h> | ||
4 | #include <linux/pci.h> | ||
2 | #include <asm/io_apic.h> | 5 | #include <asm/io_apic.h> |
3 | #include "intel-iommu.h" | 6 | #include "intel-iommu.h" |
4 | #include "intr_remapping.h" | 7 | #include "intr_remapping.h" |
5 | 8 | ||
6 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; | 9 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; |
7 | static int ir_ioapic_num; | 10 | static int ir_ioapic_num; |
11 | int intr_remapping_enabled; | ||
12 | |||
13 | static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) | ||
14 | { | ||
15 | u64 addr; | ||
16 | u32 cmd, sts; | ||
17 | unsigned long flags; | ||
18 | |||
19 | addr = virt_to_phys((void *)iommu->ir_table->base); | ||
20 | |||
21 | spin_lock_irqsave(&iommu->register_lock, flags); | ||
22 | |||
23 | dmar_writeq(iommu->reg + DMAR_IRTA_REG, | ||
24 | (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE); | ||
25 | |||
26 | /* Set interrupt-remapping table pointer */ | ||
27 | cmd = iommu->gcmd | DMA_GCMD_SIRTP; | ||
28 | writel(cmd, iommu->reg + DMAR_GCMD_REG); | ||
29 | |||
30 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, | ||
31 | readl, (sts & DMA_GSTS_IRTPS), sts); | ||
32 | spin_unlock_irqrestore(&iommu->register_lock, flags); | ||
33 | |||
34 | /* | ||
35 | * global invalidation of interrupt entry cache before enabling | ||
36 | * interrupt-remapping. | ||
37 | */ | ||
38 | qi_global_iec(iommu); | ||
39 | |||
40 | spin_lock_irqsave(&iommu->register_lock, flags); | ||
41 | |||
42 | /* Enable interrupt-remapping */ | ||
43 | cmd = iommu->gcmd | DMA_GCMD_IRE; | ||
44 | iommu->gcmd |= DMA_GCMD_IRE; | ||
45 | writel(cmd, iommu->reg + DMAR_GCMD_REG); | ||
46 | |||
47 | IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, | ||
48 | readl, (sts & DMA_GSTS_IRES), sts); | ||
49 | |||
50 | spin_unlock_irqrestore(&iommu->register_lock, flags); | ||
51 | } | ||
52 | |||
53 | |||
54 | static int setup_intr_remapping(struct intel_iommu *iommu, int mode) | ||
55 | { | ||
56 | struct ir_table *ir_table; | ||
57 | struct page *pages; | ||
58 | |||
59 | ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), | ||
60 | GFP_KERNEL); | ||
61 | |||
62 | if (!iommu->ir_table) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); | ||
66 | |||
67 | if (!pages) { | ||
68 | printk(KERN_ERR "failed to allocate pages of order %d\n", | ||
69 | INTR_REMAP_PAGE_ORDER); | ||
70 | kfree(iommu->ir_table); | ||
71 | return -ENOMEM; | ||
72 | } | ||
73 | |||
74 | ir_table->base = page_address(pages); | ||
75 | |||
76 | iommu_set_intr_remapping(iommu, mode); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | int __init enable_intr_remapping(int eim) | ||
81 | { | ||
82 | struct dmar_drhd_unit *drhd; | ||
83 | int setup = 0; | ||
84 | |||
85 | /* | ||
86 | * check for the Interrupt-remapping support | ||
87 | */ | ||
88 | for_each_drhd_unit(drhd) { | ||
89 | struct intel_iommu *iommu = drhd->iommu; | ||
90 | |||
91 | if (!ecap_ir_support(iommu->ecap)) | ||
92 | continue; | ||
93 | |||
94 | if (eim && !ecap_eim_support(iommu->ecap)) { | ||
95 | printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, " | ||
96 | " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap); | ||
97 | return -1; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Enable queued invalidation for all the DRHD's. | ||
103 | */ | ||
104 | for_each_drhd_unit(drhd) { | ||
105 | int ret; | ||
106 | struct intel_iommu *iommu = drhd->iommu; | ||
107 | ret = dmar_enable_qi(iommu); | ||
108 | |||
109 | if (ret) { | ||
110 | printk(KERN_ERR "DRHD %Lx: failed to enable queued, " | ||
111 | " invalidation, ecap %Lx, ret %d\n", | ||
112 | drhd->reg_base_addr, iommu->ecap, ret); | ||
113 | return -1; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Setup Interrupt-remapping for all the DRHD's now. | ||
119 | */ | ||
120 | for_each_drhd_unit(drhd) { | ||
121 | struct intel_iommu *iommu = drhd->iommu; | ||
122 | |||
123 | if (!ecap_ir_support(iommu->ecap)) | ||
124 | continue; | ||
125 | |||
126 | if (setup_intr_remapping(iommu, eim)) | ||
127 | goto error; | ||
128 | |||
129 | setup = 1; | ||
130 | } | ||
131 | |||
132 | if (!setup) | ||
133 | goto error; | ||
134 | |||
135 | intr_remapping_enabled = 1; | ||
136 | |||
137 | return 0; | ||
138 | |||
139 | error: | ||
140 | /* | ||
141 | * handle error condition gracefully here! | ||
142 | */ | ||
143 | return -1; | ||
144 | } | ||
8 | 145 | ||
9 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | 146 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, |
10 | struct intel_iommu *iommu) | 147 | struct intel_iommu *iommu) |