Commit 5fa61cbf authored by Marek Szyprowski's avatar Marek Szyprowski Committed by Joerg Roedel

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: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 9b93a409
...@@ -201,6 +201,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = { ...@@ -201,6 +201,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = {
*/ */
struct exynos_iommu_owner { struct exynos_iommu_owner {
struct list_head controllers; /* list of sysmmu_drvdata.owner_node */ 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) ...@@ -825,6 +826,41 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
kfree(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, static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
struct device *dev) struct device *dev)
{ {
...@@ -838,6 +874,9 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, ...@@ -838,6 +874,9 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
if (!has_sysmmu(dev)) if (!has_sysmmu(dev))
return -ENODEV; return -ENODEV;
if (owner->domain)
exynos_iommu_detach_device(owner->domain, dev);
list_for_each_entry(data, &owner->controllers, owner_node) { list_for_each_entry(data, &owner->controllers, owner_node) {
pm_runtime_get_sync(data->sysmmu); pm_runtime_get_sync(data->sysmmu);
ret = __sysmmu_enable(data, pagetable, domain); ret = __sysmmu_enable(data, pagetable, domain);
...@@ -856,44 +895,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, ...@@ -856,44 +895,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
return ret; return ret;
} }
owner->domain = iommu_domain;
dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n", dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n",
__func__, &pagetable, (ret == 0) ? "" : ", again"); __func__, &pagetable, (ret == 0) ? "" : ", again");
return ret; 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, static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,
sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter) sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment