Merge branch 'for-linus' of git://neil.brown.name/md
[safe/jmp/linux-2.6] / drivers / pci / intr_remapping.c
index f7ecd85..f5e0ea7 100644 (file)
@@ -9,6 +9,7 @@
 #include <asm/cpu.h>
 #include <linux/intel-iommu.h>
 #include "intr_remapping.h"
+#include <acpi/acpi.h>
 
 static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
 static int ir_ioapic_num;
@@ -21,7 +22,7 @@ struct irq_2_iommu {
        u8  irte_mask;
 };
 
-#ifdef CONFIG_SPARSE_IRQ
+#ifdef CONFIG_GENERIC_HARDIRQS
 static struct irq_2_iommu *get_one_free_irq_2_iommu(int cpu)
 {
        struct irq_2_iommu *iommu;
@@ -415,12 +416,27 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
 
        /* Set interrupt-remapping table pointer */
        cmd = iommu->gcmd | DMA_GCMD_SIRTP;
+       iommu->gcmd |= DMA_GCMD_SIRTP;
        writel(cmd, iommu->reg + DMAR_GCMD_REG);
 
        IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
                      readl, (sts & DMA_GSTS_IRTPS), sts);
        spin_unlock_irqrestore(&iommu->register_lock, flags);
 
+       if (mode == 0) {
+               spin_lock_irqsave(&iommu->register_lock, flags);
+
+               /* enable comaptiblity format interrupt pass through */
+               cmd = iommu->gcmd | DMA_GCMD_CFI;
+               iommu->gcmd |= DMA_GCMD_CFI;
+               writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+               IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
+                             readl, (sts & DMA_GSTS_CFIS), sts);
+
+               spin_unlock_irqrestore(&iommu->register_lock, flags);
+       }
+
        /*
         * global invalidation of interrupt entry cache before enabling
         * interrupt-remapping.
@@ -447,12 +463,12 @@ static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
        struct page *pages;
 
        ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
-                                            GFP_KERNEL);
+                                            GFP_ATOMIC);
 
        if (!iommu->ir_table)
                return -ENOMEM;
 
-       pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
+       pages = alloc_pages(GFP_ATOMIC | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
 
        if (!pages) {
                printk(KERN_ERR "failed to allocate pages of order %d\n",
@@ -470,7 +486,7 @@ static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
 /*
  * Disable Interrupt Remapping.
  */
-static void disable_intr_remapping(struct intel_iommu *iommu)
+static void iommu_disable_intr_remapping(struct intel_iommu *iommu)
 {
        unsigned long flags;
        u32 sts;
@@ -478,6 +494,12 @@ static void disable_intr_remapping(struct intel_iommu *iommu)
        if (!ecap_ir_support(iommu->ecap))
                return;
 
+       /*
+        * global invalidation of interrupt entry cache before disabling
+        * interrupt-remapping.
+        */
+       qi_global_iec(iommu);
+
        spin_lock_irqsave(&iommu->register_lock, flags);
 
        sts = dmar_readq(iommu->reg + DMAR_GSTS_REG);
@@ -503,6 +525,13 @@ int __init enable_intr_remapping(int eim)
                struct intel_iommu *iommu = drhd->iommu;
 
                /*
+                * If the queued invalidation is already initialized,
+                * shouldn't disable it.
+                */
+               if (iommu->qi)
+                       continue;
+
+               /*
                 * Clear previous faults.
                 */
                dmar_fault(-1, iommu);
@@ -511,7 +540,7 @@ int __init enable_intr_remapping(int eim)
                 * Disable intr remapping and queued invalidation, if already
                 * enabled prior to OS handover.
                 */
-               disable_intr_remapping(iommu);
+               iommu_disable_intr_remapping(iommu);
 
                dmar_disable_qi(iommu);
        }
@@ -639,3 +668,54 @@ int __init parse_ioapics_under_ir(void)
 
        return ir_supported;
 }
+
+void disable_intr_remapping(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu = NULL;
+
+       /*
+        * Disable Interrupt-remapping for all the DRHD's now.
+        */
+       for_each_iommu(iommu, drhd) {
+               if (!ecap_ir_support(iommu->ecap))
+                       continue;
+
+               iommu_disable_intr_remapping(iommu);
+       }
+}
+
+int reenable_intr_remapping(int eim)
+{
+       struct dmar_drhd_unit *drhd;
+       int setup = 0;
+       struct intel_iommu *iommu = NULL;
+
+       for_each_iommu(iommu, drhd)
+               if (iommu->qi)
+                       dmar_reenable_qi(iommu);
+
+       /*
+        * Setup Interrupt-remapping for all the DRHD's now.
+        */
+       for_each_iommu(iommu, drhd) {
+               if (!ecap_ir_support(iommu->ecap))
+                       continue;
+
+               /* Set up interrupt remapping for iommu.*/
+               iommu_set_intr_remapping(iommu, eim);
+               setup = 1;
+       }
+
+       if (!setup)
+               goto error;
+
+       return 0;
+
+error:
+       /*
+        * handle error condition gracefully here!
+        */
+       return -1;
+}
+