summaryrefslogtreecommitdiff
path: root/drivers/iommu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/amd_iommu.c5
-rw-r--r--drivers/iommu/amd_iommu_init.c189
-rw-r--r--drivers/iommu/amd_iommu_v2.c14
-rw-r--r--drivers/iommu/msm_iommu.c7
-rw-r--r--drivers/iommu/omap-iommu-debug.c59
-rw-r--r--drivers/iommu/omap-iommu.c3
6 files changed, 189 insertions, 88 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index cce1f03b889..ae2ec929e52 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2804,7 +2804,7 @@ static int amd_iommu_dma_supported(struct device *dev, u64 mask)
* we don't need to preallocate the protection domains anymore.
* For now we have to.
*/
-static void prealloc_protection_domains(void)
+static void __init prealloc_protection_domains(void)
{
struct iommu_dev_data *dev_data;
struct dma_ops_domain *dma_dom;
@@ -2863,6 +2863,9 @@ static unsigned device_dma_ops_init(void)
for_each_pci_dev(pdev) {
if (!check_device(&pdev->dev)) {
+
+ iommu_ignore_device(&pdev->dev);
+
unhandled += 1;
continue;
}
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index bdea288dc18..c56790375e0 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -196,6 +196,8 @@ static u32 rlookup_table_size; /* size if the rlookup table */
*/
extern void iommu_flush_all_caches(struct amd_iommu *iommu);
+static int amd_iommu_enable_interrupts(void);
+
static inline void update_last_devid(u16 devid)
{
if (devid > amd_iommu_last_bdf)
@@ -275,7 +277,7 @@ static void iommu_set_exclusion_range(struct amd_iommu *iommu)
}
/* Programs the physical address of the device table into the IOMMU hardware */
-static void __init iommu_set_device_table(struct amd_iommu *iommu)
+static void iommu_set_device_table(struct amd_iommu *iommu)
{
u64 entry;
@@ -358,8 +360,6 @@ static void iommu_disable(struct amd_iommu *iommu)
*/
static u8 * __init iommu_map_mmio_space(u64 address)
{
- u8 *ret;
-
if (!request_mem_region(address, MMIO_REGION_LENGTH, "amd_iommu")) {
pr_err("AMD-Vi: Can not reserve memory region %llx for mmio\n",
address);
@@ -367,13 +367,7 @@ static u8 * __init iommu_map_mmio_space(u64 address)
return NULL;
}
- ret = ioremap_nocache(address, MMIO_REGION_LENGTH);
- if (ret != NULL)
- return ret;
-
- release_mem_region(address, MMIO_REGION_LENGTH);
-
- return NULL;
+ return ioremap_nocache(address, MMIO_REGION_LENGTH);
}
static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)
@@ -1131,8 +1125,9 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
{
int r;
- if (pci_enable_msi(iommu->dev))
- return 1;
+ r = pci_enable_msi(iommu->dev);
+ if (r)
+ return r;
r = request_threaded_irq(iommu->dev->irq,
amd_iommu_int_handler,
@@ -1142,27 +1137,36 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
if (r) {
pci_disable_msi(iommu->dev);
- return 1;
+ return r;
}
iommu->int_enabled = true;
- iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
-
- if (iommu->ppr_log != NULL)
- iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
return 0;
}
static int iommu_init_msi(struct amd_iommu *iommu)
{
+ int ret;
+
if (iommu->int_enabled)
- return 0;
+ goto enable_faults;
if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI))
- return iommu_setup_msi(iommu);
+ ret = iommu_setup_msi(iommu);
+ else
+ ret = -ENODEV;
- return 1;
+ if (ret)
+ return ret;
+
+enable_faults:
+ iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
+
+ if (iommu->ppr_log != NULL)
+ iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
+
+ return 0;
}
/****************************************************************************
@@ -1381,7 +1385,6 @@ static void enable_iommus(void)
iommu_enable_ppr_log(iommu);
iommu_enable_gt(iommu);
iommu_set_exclusion_range(iommu);
- iommu_init_msi(iommu);
iommu_enable(iommu);
iommu_flush_all_caches(iommu);
}
@@ -1409,6 +1412,8 @@ static void amd_iommu_resume(void)
/* re-load the hardware */
enable_iommus();
+
+ amd_iommu_enable_interrupts();
}
static int amd_iommu_suspend(void)
@@ -1424,10 +1429,40 @@ static struct syscore_ops amd_iommu_syscore_ops = {
.resume = amd_iommu_resume,
};
+static void __init free_on_init_error(void)
+{
+ amd_iommu_uninit_devices();
+
+ free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
+ get_order(MAX_DOMAIN_ID/8));
+
+ free_pages((unsigned long)amd_iommu_rlookup_table,
+ get_order(rlookup_table_size));
+
+ free_pages((unsigned long)amd_iommu_alias_table,
+ get_order(alias_table_size));
+
+ free_pages((unsigned long)amd_iommu_dev_table,
+ get_order(dev_table_size));
+
+ free_iommu_all();
+
+ free_unity_maps();
+
+#ifdef CONFIG_GART_IOMMU
+ /*
+ * We failed to initialize the AMD IOMMU - try fallback to GART
+ * if possible.
+ */
+ gart_iommu_init();
+
+#endif
+}
+
/*
- * This is the core init function for AMD IOMMU hardware in the system.
- * This function is called from the generic x86 DMA layer initialization
- * code.
+ * This is the hardware init function for AMD IOMMU in the system.
+ * This function is called either from amd_iommu_init or from the interrupt
+ * remapping setup code.
*
* This function basically parses the ACPI table for AMD IOMMU (IVRS)
* three times:
@@ -1446,16 +1481,21 @@ static struct syscore_ops amd_iommu_syscore_ops = {
* remapping requirements parsed out of the ACPI table in
* this last pass.
*
- * After that the hardware is initialized and ready to go. In the last
- * step we do some Linux specific things like registering the driver in
- * the dma_ops interface and initializing the suspend/resume support
- * functions. Finally it prints some information about AMD IOMMUs and
- * the driver state and enables the hardware.
+ * After everything is set up the IOMMUs are enabled and the necessary
+ * hotplug and suspend notifiers are registered.
*/
-static int __init amd_iommu_init(void)
+int __init amd_iommu_init_hardware(void)
{
int i, ret = 0;
+ if (!amd_iommu_detected)
+ return -ENODEV;
+
+ if (amd_iommu_dev_table != NULL) {
+ /* Hardware already initialized */
+ return 0;
+ }
+
/*
* First parse ACPI tables to find the largest Bus/Dev/Func
* we need to handle. Upon this information the shared data
@@ -1472,9 +1512,8 @@ static int __init amd_iommu_init(void)
alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE);
rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE);
- ret = -ENOMEM;
-
/* Device table - directly used by all IOMMUs */
+ ret = -ENOMEM;
amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(dev_table_size));
if (amd_iommu_dev_table == NULL)
@@ -1546,20 +1585,65 @@ static int __init amd_iommu_init(void)
enable_iommus();
+ amd_iommu_init_notifier();
+
+ register_syscore_ops(&amd_iommu_syscore_ops);
+
+out:
+ return ret;
+
+free:
+ free_on_init_error();
+
+ return ret;
+}
+
+static int amd_iommu_enable_interrupts(void)
+{
+ struct amd_iommu *iommu;
+ int ret = 0;
+
+ for_each_iommu(iommu) {
+ ret = iommu_init_msi(iommu);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * This is the core init function for AMD IOMMU hardware in the system.
+ * This function is called from the generic x86 DMA layer initialization
+ * code.
+ *
+ * The function calls amd_iommu_init_hardware() to setup and enable the
+ * IOMMU hardware if this has not happened yet. After that the driver
+ * registers for the DMA-API and for the IOMMU-API as necessary.
+ */
+static int __init amd_iommu_init(void)
+{
+ int ret = 0;
+
+ ret = amd_iommu_init_hardware();
+ if (ret)
+ goto out;
+
+ ret = amd_iommu_enable_interrupts();
+ if (ret)
+ goto free;
+
if (iommu_pass_through)
ret = amd_iommu_init_passthrough();
else
ret = amd_iommu_init_dma_ops();
if (ret)
- goto free_disable;
+ goto free;
amd_iommu_init_api();
- amd_iommu_init_notifier();
-
- register_syscore_ops(&amd_iommu_syscore_ops);
-
if (iommu_pass_through)
goto out;
@@ -1569,39 +1653,14 @@ static int __init amd_iommu_init(void)
printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n");
x86_platform.iommu_shutdown = disable_iommus;
+
out:
return ret;
-free_disable:
- disable_iommus();
-
free:
- amd_iommu_uninit_devices();
-
- free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
- get_order(MAX_DOMAIN_ID/8));
-
- free_pages((unsigned long)amd_iommu_rlookup_table,
- get_order(rlookup_table_size));
-
- free_pages((unsigned long)amd_iommu_alias_table,
- get_order(alias_table_size));
-
- free_pages((unsigned long)amd_iommu_dev_table,
- get_order(dev_table_size));
-
- free_iommu_all();
-
- free_unity_maps();
-
-#ifdef CONFIG_GART_IOMMU
- /*
- * We failed to initialize the AMD IOMMU - try fallback to GART
- * if possible.
- */
- gart_iommu_init();
+ disable_iommus();
-#endif
+ free_on_init_error();
goto out;
}
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 8add9f125d3..036fe9bf157 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -921,7 +921,16 @@ static int __init amd_iommu_v2_init(void)
size_t state_table_size;
int ret;
- pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>");
+ pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>\n");
+
+ if (!amd_iommu_v2_supported()) {
+ pr_info("AMD IOMMUv2 functionality not available on this sytem\n");
+ /*
+ * Load anyway to provide the symbols to other modules
+ * which may use AMD IOMMUv2 optionally.
+ */
+ return 0;
+ }
spin_lock_init(&state_lock);
@@ -961,6 +970,9 @@ static void __exit amd_iommu_v2_exit(void)
size_t state_table_size;
int i;
+ if (!amd_iommu_v2_supported())
+ return;
+
profile_event_unregister(PROFILE_TASK_EXIT, &profile_nb);
amd_iommu_unregister_ppr_notifier(&ppr_nb);
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 08a90b88e40..cee307e8660 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -482,23 +482,19 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
priv = domain->priv;
- if (!priv) {
- ret = -ENODEV;
+ if (!priv)
goto fail;
- }
fl_table = priv->pgtable;
if (len != SZ_16M && len != SZ_1M &&
len != SZ_64K && len != SZ_4K) {
pr_debug("Bad length: %d\n", len);
- ret = -EINVAL;
goto fail;
}
if (!fl_table) {
pr_debug("Null page table\n");
- ret = -EINVAL;
goto fail;
}
@@ -507,7 +503,6 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
if (*fl_pte == 0) {
pr_debug("First level PTE is 0\n");
- ret = -ENODEV;
goto fail;
}
diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c
index 288da5c1499..103dbd92e25 100644
--- a/drivers/iommu/omap-iommu-debug.c
+++ b/drivers/iommu/omap-iommu-debug.c
@@ -44,7 +44,8 @@ static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
+ struct omap_iommu *obj = dev_to_omap_iommu(dev);
char *p, *buf;
ssize_t bytes;
@@ -67,7 +68,8 @@ static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
+ struct omap_iommu *obj = dev_to_omap_iommu(dev);
char *p, *buf;
ssize_t bytes, rest;
@@ -97,7 +99,8 @@ static ssize_t debug_write_pagetable(struct file *file,
struct iotlb_entry e;
struct cr_regs cr;
int err;
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
+ struct omap_iommu *obj = dev_to_omap_iommu(dev);
char buf[MAXCOLUMN], *p = buf;
count = min(count, sizeof(buf));
@@ -184,7 +187,8 @@ out:
static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
+ struct omap_iommu *obj = dev_to_omap_iommu(dev);
char *p, *buf;
size_t bytes;
@@ -212,7 +216,8 @@ static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
+ struct omap_iommu *obj = dev_to_omap_iommu(dev);
char *p, *buf;
struct iovm_struct *tmp;
int uninitialized_var(i);
@@ -254,7 +259,7 @@ static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
char *p, *buf;
struct iovm_struct *area;
ssize_t bytes;
@@ -268,8 +273,8 @@ static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
mutex_lock(&iommu_debug_lock);
- area = omap_find_iovm_area(obj, (u32)ppos);
- if (IS_ERR(area)) {
+ area = omap_find_iovm_area(dev, (u32)ppos);
+ if (!area) {
bytes = -EINVAL;
goto err_out;
}
@@ -287,7 +292,7 @@ err_out:
static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
- struct omap_iommu *obj = file->private_data;
+ struct device *dev = file->private_data;
struct iovm_struct *area;
char *p, *buf;
@@ -305,8 +310,8 @@ static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
goto err_out;
}
- area = omap_find_iovm_area(obj, (u32)ppos);
- if (IS_ERR(area)) {
+ area = omap_find_iovm_area(dev, (u32)ppos);
+ if (!area) {
count = -EINVAL;
goto err_out;
}
@@ -350,7 +355,7 @@ DEBUG_FOPS(mem);
{ \
struct dentry *dent; \
dent = debugfs_create_file(#attr, mode, parent, \
- obj, &debug_##attr##_fops); \
+ dev, &debug_##attr##_fops); \
if (!dent) \
return -ENOMEM; \
}
@@ -362,20 +367,29 @@ static int iommu_debug_register(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct omap_iommu *obj = platform_get_drvdata(pdev);
+ struct omap_iommu_arch_data *arch_data;
struct dentry *d, *parent;
if (!obj || !obj->dev)
return -EINVAL;
+ arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL);
+ if (!arch_data)
+ return -ENOMEM;
+
+ arch_data->iommu_dev = obj;
+
+ dev->archdata.iommu = arch_data;
+
d = debugfs_create_dir(obj->name, iommu_debug_root);
if (!d)
- return -ENOMEM;
+ goto nomem;
parent = d;
d = debugfs_create_u8("nr_tlb_entries", 400, parent,
(u8 *)&obj->nr_tlb_entries);
if (!d)
- return -ENOMEM;
+ goto nomem;
DEBUG_ADD_FILE_RO(ver);
DEBUG_ADD_FILE_RO(regs);
@@ -385,6 +399,22 @@ static int iommu_debug_register(struct device *dev, void *data)
DEBUG_ADD_FILE(mem);
return 0;
+
+nomem:
+ kfree(arch_data);
+ return -ENOMEM;
+}
+
+static int iommu_debug_unregister(struct device *dev, void *data)
+{
+ if (!dev->archdata.iommu)
+ return 0;
+
+ kfree(dev->archdata.iommu);
+
+ dev->archdata.iommu = NULL;
+
+ return 0;
}
static int __init iommu_debug_init(void)
@@ -411,6 +441,7 @@ module_init(iommu_debug_init)
static void __exit iommu_debugfs_exit(void)
{
debugfs_remove_recursive(iommu_debug_root);
+ omap_foreach_iommu_device(NULL, iommu_debug_unregister);
}
module_exit(iommu_debugfs_exit)
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index d8edd979d01..6899dcd02df 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1223,7 +1223,8 @@ static int __init omap_iommu_init(void)
return platform_driver_register(&omap_iommu_driver);
}
-module_init(omap_iommu_init);
+/* must be ready before omap3isp is probed */
+subsys_initcall(omap_iommu_init);
static void __exit omap_iommu_exit(void)
{