diff options
author | Marek Szyprowski <m.szyprowski@samsung.com> | 2016-02-18 15:13:00 +0100 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2016-02-25 15:32:11 +0100 |
commit | 5fa61cbff16224ef8595102552ca49b2c6775ab4 (patch) | |
tree | 73839db342ce9b0ba356a8367c05cd5eb89cd4fb /drivers/iommu/exynos-iommu.c | |
parent | 9b93a409fd317be25dca7118163efe74154bc9e7 (diff) | |
download | linux-5fa61cbff16224ef8595102552ca49b2c6775ab4.tar.gz linux-5fa61cbff16224ef8595102552ca49b2c6775ab4.tar.xz |
iommu/exynos: Support multiple attach_device calls
IOMMU core calls attach_device callback without detaching device from
the previous domain. This patch adds support for such unballanced calls.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu/exynos-iommu.c')
-rw-r--r-- | drivers/iommu/exynos-iommu.c | 72 |
1 files changed, 40 insertions, 32 deletions
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9c8ce951158d..b0665042bf29 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -201,6 +201,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = { */ struct exynos_iommu_owner { struct list_head controllers; /* list of sysmmu_drvdata.owner_node */ + struct iommu_domain *domain; /* domain this device is attached */ }; /* @@ -825,6 +826,41 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain) kfree(domain); } +static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, + struct device *dev) +{ + struct exynos_iommu_owner *owner = dev->archdata.iommu; + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); + phys_addr_t pagetable = virt_to_phys(domain->pgtable); + struct sysmmu_drvdata *data, *next; + unsigned long flags; + bool found = false; + + if (!has_sysmmu(dev) || owner->domain != iommu_domain) + return; + + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry_safe(data, next, &domain->clients, domain_node) { + if (data->master == dev) { + if (__sysmmu_disable(data)) { + data->master = NULL; + list_del_init(&data->domain_node); + } + pm_runtime_put(data->sysmmu); + found = true; + } + } + spin_unlock_irqrestore(&domain->lock, flags); + + owner->domain = NULL; + + if (found) + dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", + __func__, &pagetable); + else + dev_err(dev, "%s: No IOMMU is attached\n", __func__); +} + static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, struct device *dev) { @@ -838,6 +874,9 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, if (!has_sysmmu(dev)) return -ENODEV; + if (owner->domain) + exynos_iommu_detach_device(owner->domain, dev); + list_for_each_entry(data, &owner->controllers, owner_node) { pm_runtime_get_sync(data->sysmmu); ret = __sysmmu_enable(data, pagetable, domain); @@ -856,44 +895,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, return ret; } + owner->domain = iommu_domain; dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n", __func__, &pagetable, (ret == 0) ? "" : ", again"); return ret; } -static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, - struct device *dev) -{ - struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); - phys_addr_t pagetable = virt_to_phys(domain->pgtable); - struct sysmmu_drvdata *data, *next; - unsigned long flags; - bool found = false; - - if (!has_sysmmu(dev)) - return; - - spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry_safe(data, next, &domain->clients, domain_node) { - if (data->master == dev) { - if (__sysmmu_disable(data)) { - data->master = NULL; - list_del_init(&data->domain_node); - } - pm_runtime_put(data->sysmmu); - found = true; - } - } - spin_unlock_irqrestore(&domain->lock, flags); - - if (found) - dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", - __func__, &pagetable); - else - dev_err(dev, "%s: No IOMMU is attached\n", __func__); -} - static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter) { |