diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2012-06-21 10:29:10 -0400 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2012-09-28 11:43:50 -0400 |
commit | 2b324506341cb3baf65f3b6a0f950dd7648938ac (patch) | |
tree | 9c2f944ef1f4e7b9b8ea3c65820165ed2c169d5f /drivers/iommu | |
parent | 7ef2798deb695f112f25e348b199dca79eb1ea68 (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')
-rw-r--r-- | drivers/iommu/amd_iommu.c | 228 |
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 | } |
3775 | EXPORT_SYMBOL(amd_iommu_device_info); | 3781 | EXPORT_SYMBOL(amd_iommu_device_info); |
3782 | |||
3783 | #ifdef CONFIG_IRQ_REMAP | ||
3784 | |||
3785 | /***************************************************************************** | ||
3786 | * | ||
3787 | * Interrupt Remapping Implementation | ||
3788 | * | ||
3789 | *****************************************************************************/ | ||
3790 | |||
3791 | union 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 | |||
3811 | static 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 | |||
3827 | static 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 | |||
3886 | out: | ||
3887 | iommu_completion_wait(iommu); | ||
3888 | |||
3889 | out_unlock: | ||
3890 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); | ||
3891 | |||
3892 | return table; | ||
3893 | } | ||
3894 | |||
3895 | static 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 | |||
3935 | out: | ||
3936 | spin_unlock_irqrestore(&table->lock, flags); | ||
3937 | |||
3938 | return index; | ||
3939 | } | ||
3940 | |||
3941 | static 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 | |||
3957 | static 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 | |||
3981 | static 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 | ||