aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/amd_iommu.c
diff options
context:
space:
mode:
authorJoerg Roedel <joerg.roedel@amd.com>2012-06-21 10:29:10 -0400
committerJoerg Roedel <joerg.roedel@amd.com>2012-09-28 11:43:50 -0400
commit2b324506341cb3baf65f3b6a0f950dd7648938ac (patch)
tree9c2f944ef1f4e7b9b8ea3c65820165ed2c169d5f /drivers/iommu/amd_iommu.c
parent7ef2798deb695f112f25e348b199dca79eb1ea68 (diff)
iommu/amd: Add routines to manage irq remapping tables
Add routines to: * Alloc remapping tables and single entries from these tables * Change entries in the tables * Free entries in the table Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Diffstat (limited to 'drivers/iommu/amd_iommu.c')
-rw-r--r--drivers/iommu/amd_iommu.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index e27b354c3e9b..4e429e781333 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -31,6 +31,12 @@
31#include <linux/amd-iommu.h> 31#include <linux/amd-iommu.h>
32#include <linux/notifier.h> 32#include <linux/notifier.h>
33#include <linux/export.h> 33#include <linux/export.h>
34#include <linux/irq.h>
35#include <linux/msi.h>
36#include <asm/irq_remapping.h>
37#include <asm/io_apic.h>
38#include <asm/apic.h>
39#include <asm/hw_irq.h>
34#include <asm/msidef.h> 40#include <asm/msidef.h>
35#include <asm/proto.h> 41#include <asm/proto.h>
36#include <asm/iommu.h> 42#include <asm/iommu.h>
@@ -3773,3 +3779,225 @@ int amd_iommu_device_info(struct pci_dev *pdev,
3773 return 0; 3779 return 0;
3774} 3780}
3775EXPORT_SYMBOL(amd_iommu_device_info); 3781EXPORT_SYMBOL(amd_iommu_device_info);
3782
3783#ifdef CONFIG_IRQ_REMAP
3784
3785/*****************************************************************************
3786 *
3787 * Interrupt Remapping Implementation
3788 *
3789 *****************************************************************************/
3790
3791union irte {
3792 u32 val;
3793 struct {
3794 u32 valid : 1,
3795 no_fault : 1,
3796 int_type : 3,
3797 rq_eoi : 1,
3798 dm : 1,
3799 rsvd_1 : 1,
3800 destination : 8,
3801 vector : 8,
3802 rsvd_2 : 8;
3803 } fields;
3804};
3805
3806#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
3807#define DTE_IRQ_REMAP_INTCTL (2ULL << 60)
3808#define DTE_IRQ_TABLE_LEN (8ULL << 1)
3809#define DTE_IRQ_REMAP_ENABLE 1ULL
3810
3811static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
3812{
3813 u64 dte;
3814
3815 dte = amd_iommu_dev_table[devid].data[2];
3816 dte &= ~DTE_IRQ_PHYS_ADDR_MASK;
3817 dte |= virt_to_phys(table->table);
3818 dte |= DTE_IRQ_REMAP_INTCTL;
3819 dte |= DTE_IRQ_TABLE_LEN;
3820 dte |= DTE_IRQ_REMAP_ENABLE;
3821
3822 amd_iommu_dev_table[devid].data[2] = dte;
3823}
3824
3825#define IRTE_ALLOCATED (~1U)
3826
3827static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
3828{
3829 struct irq_remap_table *table = NULL;
3830 struct amd_iommu *iommu;
3831 unsigned long flags;
3832 u16 alias;
3833
3834 write_lock_irqsave(&amd_iommu_devtable_lock, flags);
3835
3836 iommu = amd_iommu_rlookup_table[devid];
3837 if (!iommu)
3838 goto out_unlock;
3839
3840 table = irq_lookup_table[devid];
3841 if (table)
3842 goto out;
3843
3844 alias = amd_iommu_alias_table[devid];
3845 table = irq_lookup_table[alias];
3846 if (table) {
3847 irq_lookup_table[devid] = table;
3848 set_dte_irq_entry(devid, table);
3849 iommu_flush_dte(iommu, devid);
3850 goto out;
3851 }
3852
3853 /* Nothing there yet, allocate new irq remapping table */
3854 table = kzalloc(sizeof(*table), GFP_ATOMIC);
3855 if (!table)
3856 goto out;
3857
3858 if (ioapic)
3859 /* Keep the first 32 indexes free for IOAPIC interrupts */
3860 table->min_index = 32;
3861
3862 table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
3863 if (!table->table) {
3864 kfree(table);
3865 goto out;
3866 }
3867
3868 memset(table->table, 0, MAX_IRQS_PER_TABLE * sizeof(u32));
3869
3870 if (ioapic) {
3871 int i;
3872
3873 for (i = 0; i < 32; ++i)
3874 table->table[i] = IRTE_ALLOCATED;
3875 }
3876
3877 irq_lookup_table[devid] = table;
3878 set_dte_irq_entry(devid, table);
3879 iommu_flush_dte(iommu, devid);
3880 if (devid != alias) {
3881 irq_lookup_table[alias] = table;
3882 set_dte_irq_entry(devid, table);
3883 iommu_flush_dte(iommu, alias);
3884 }
3885
3886out:
3887 iommu_completion_wait(iommu);
3888
3889out_unlock:
3890 write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
3891
3892 return table;
3893}
3894
3895static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
3896{
3897 struct irq_remap_table *table;
3898 unsigned long flags;
3899 int index, c;
3900
3901 table = get_irq_table(devid, false);
3902 if (!table)
3903 return -ENODEV;
3904
3905 spin_lock_irqsave(&table->lock, flags);
3906
3907 /* Scan table for free entries */
3908 for (c = 0, index = table->min_index;
3909 index < MAX_IRQS_PER_TABLE;
3910 ++index) {
3911 if (table->table[index] == 0)
3912 c += 1;
3913 else
3914 c = 0;
3915
3916 if (c == count) {
3917 struct irq_2_iommu *irte_info;
3918
3919 for (; c != 0; --c)
3920 table->table[index - c + 1] = IRTE_ALLOCATED;
3921
3922 index -= count - 1;
3923
3924 irte_info = &cfg->irq_2_iommu;
3925 irte_info->sub_handle = devid;
3926 irte_info->irte_index = index;
3927 irte_info->iommu = (void *)cfg;
3928
3929 goto out;
3930 }
3931 }
3932
3933 index = -ENOSPC;
3934
3935out:
3936 spin_unlock_irqrestore(&table->lock, flags);
3937
3938 return index;
3939}
3940
3941static int get_irte(u16 devid, int index, union irte *irte)
3942{
3943 struct irq_remap_table *table;
3944 unsigned long flags;
3945
3946 table = get_irq_table(devid, false);
3947 if (!table)
3948 return -ENOMEM;
3949
3950 spin_lock_irqsave(&table->lock, flags);
3951 irte->val = table->table[index];
3952 spin_unlock_irqrestore(&table->lock, flags);
3953
3954 return 0;
3955}
3956
3957static int modify_irte(u16 devid, int index, union irte irte)
3958{
3959 struct irq_remap_table *table;
3960 struct amd_iommu *iommu;
3961 unsigned long flags;
3962
3963 iommu = amd_iommu_rlookup_table[devid];
3964 if (iommu == NULL)
3965 return -EINVAL;
3966
3967 table = get_irq_table(devid, false);
3968 if (!table)
3969 return -ENOMEM;
3970
3971 spin_lock_irqsave(&table->lock, flags);
3972 table->table[index] = irte.val;
3973 spin_unlock_irqrestore(&table->lock, flags);
3974
3975 iommu_flush_irt(iommu, devid);
3976 iommu_completion_wait(iommu);
3977
3978 return 0;
3979}
3980
3981static void free_irte(u16 devid, int index)
3982{
3983 struct irq_remap_table *table;
3984 struct amd_iommu *iommu;
3985 unsigned long flags;
3986
3987 iommu = amd_iommu_rlookup_table[devid];
3988 if (iommu == NULL)
3989 return;
3990
3991 table = get_irq_table(devid, false);
3992 if (!table)
3993 return;
3994
3995 spin_lock_irqsave(&table->lock, flags);
3996 table->table[index] = 0;
3997 spin_unlock_irqrestore(&table->lock, flags);
3998
3999 iommu_flush_irt(iommu, devid);
4000 iommu_completion_wait(iommu);
4001}
4002
4003#endif