From 1bf1b431d98d7e5b5419876d4c219469e60693e1 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 16 Apr 2015 17:08:44 +0300 Subject: iommu/amd: Fix bug in put_pasid_state_wait This patch fixes a bug in put_pasid_state_wait that appeared in kernel 4.0 The bug is that pasid_state->count wasn't decremented before entering the wait_event. Thus, the condition in wait_event will never be true. The fix is to decrement (atomically) the pasid_state->count before the wait_event. Signed-off-by: Oded Gabbay Cc: stable@vger.kernel.org #v4.0 Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_v2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index a1cbba9056fd..3465faf1809e 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -266,6 +266,7 @@ static void put_pasid_state(struct pasid_state *pasid_state) static void put_pasid_state_wait(struct pasid_state *pasid_state) { + atomic_dec(&pasid_state->count); wait_event(pasid_state->wq, !atomic_read(&pasid_state->count)); free_pasid_state(pasid_state); } -- cgit v1.2.3 From d9e7eb152bb24f06028a0d10b054e39ebdf14f9c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 10 Apr 2015 23:58:24 +0200 Subject: iommu/rockchip: Fix build without CONFIG_OF The rockchip iommu driver references its of_device_id table from the init function, which fails to build when the table is undefined: iommu/rockchip-iommu.c: In function 'rk_iommu_init': iommu/rockchip-iommu.c:1029:35: error: 'rk_iommu_dt_ids' undeclared (first use in this function) np = of_find_matching_node(NULL, rk_iommu_dt_ids); This removes the #ifdef and the corresponding of_match_ptr wrapper to make it build both with CONFIG_OF enabled or disabled. Signed-off-by: Arnd Bergmann Fixes: 425061b0f5074 ("iommu/rockchip: Play nice in multi-platform builds") Reviewed-by: Thierry Reding Reviewed-by: Heiko Stuebner Signed-off-by: Joerg Roedel --- drivers/iommu/rockchip-iommu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 4015560bf486..cab214544237 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -1004,20 +1004,18 @@ static int rk_iommu_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id rk_iommu_dt_ids[] = { { .compatible = "rockchip,iommu" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rk_iommu_dt_ids); -#endif static struct platform_driver rk_iommu_driver = { .probe = rk_iommu_probe, .remove = rk_iommu_remove, .driver = { .name = "rk_iommu", - .of_match_table = of_match_ptr(rk_iommu_dt_ids), + .of_match_table = rk_iommu_dt_ids, }, }; -- cgit v1.2.3 From 733cac2ade2fbb73740a1d22c05c7b006a6cf3d2 Mon Sep 17 00:00:00 2001 From: Robert Callicotte Date: Thu, 16 Apr 2015 23:32:47 -0500 Subject: iommu: Fix checkpatch warnings for Missing a blank line after declarations Fixed checkpatch warnings for missing blank line after declaration of struct. Signed-off-by: Robert Callicotte Signed-off-by: Joerg Roedel --- drivers/iommu/iova.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 9dd8208312c2..b7c3d923f3e1 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -227,6 +227,7 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova) /* Figure out where to put new node */ while (*new) { struct iova *this = container_of(*new, struct iova, node); + parent = *new; if (iova->pfn_lo < this->pfn_lo) @@ -350,6 +351,7 @@ void free_iova(struct iova_domain *iovad, unsigned long pfn) { struct iova *iova = find_iova(iovad, pfn); + if (iova) __free_iova(iovad, iova); @@ -369,6 +371,7 @@ void put_iova_domain(struct iova_domain *iovad) node = rb_first(&iovad->rbroot); while (node) { struct iova *iova = container_of(node, struct iova, node); + rb_erase(node, &iovad->rbroot); free_iova_mem(iova); node = rb_first(&iovad->rbroot); @@ -482,6 +485,7 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to) for (node = rb_first(&from->rbroot); node; node = rb_next(node)) { struct iova *iova = container_of(node, struct iova, node); struct iova *new_iova; + new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi); if (!new_iova) printk(KERN_ERR "Reserve iova range %lx@%lx failed\n", -- cgit v1.2.3 From 4ed6a540fab8ea4388c1703b73ecfed68a2009d1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 11 May 2015 14:59:20 +0100 Subject: iommu/vt-d: Fix passthrough mode with translation-disabled devices When we use 'intel_iommu=igfx_off' to disable translation for the graphics, and when we discover that the BIOS has misconfigured the DMAR setup for I/OAT, we use a special DUMMY_DEVICE_DOMAIN_INFO value in dev->archdata.iommu to indicate that translation is disabled. With passthrough mode, we were attempting to dereference that as a normal pointer to a struct device_domain_info when setting up an identity mapping for the affected device. This fixes the problem by making device_to_iommu() explicitly check for the special value and indicate that no IOMMU was found to handle the devices in question. Signed-off-by: David Woodhouse Cc: stable@vger.kernel.org (which means you can pick up 18436afdc now too) --- drivers/iommu/intel-iommu.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 68d43beccb7e..2ffe58969944 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -696,6 +696,11 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu return &context[devfn]; } +static int iommu_dummy(struct device *dev) +{ + return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO; +} + static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) { struct dmar_drhd_unit *drhd = NULL; @@ -705,6 +710,9 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf u16 segment = 0; int i; + if (iommu_dummy(dev)) + return NULL; + if (dev_is_pci(dev)) { pdev = to_pci_dev(dev); segment = pci_domain_nr(pdev->bus); @@ -2969,11 +2977,6 @@ static inline struct dmar_domain *get_valid_domain_for_dev(struct device *dev) return __get_valid_domain_for_dev(dev); } -static int iommu_dummy(struct device *dev) -{ - return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO; -} - /* Check if the dev needs to go through non-identity map and unmap process.*/ static int iommu_no_mapping(struct device *dev) { -- cgit v1.2.3 From 5dc5616ee850eaba055bb469a6c4a471d489140e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 8 May 2015 17:44:22 +0100 Subject: iommu/arm-smmu: Fix sign-extension of upstream bus addresses at stage 1 Stage 1 translation is controlled by two sets of page tables (TTBR0 and TTBR1) which grow up and down from zero respectively in the ARMv8 translation regime. For the SMMU, we only care about TTBR0 and, in the case of a 48-bit virtual space, we expect to map virtual addresses 0x0 through to 0xffff_ffff_ffff. Given that some masters may be incapable of emitting virtual addresses targetting TTBR1 (e.g. because they sit on a 48-bit bus), the SMMU architecture allows bit 47 to be sign-extended, halving the virtual range of TTBR0 but allowing TTBR1 to be used. This is controlled by the SEP field in TTBCR2. The SMMU driver incorrectly enables this sign-extension feature, which causes problems when userspace addresses are programmed into a master device with the SMMU expecting to map the incoming transactions via TTBR0; if the top bit of address is set, we will instead get a translation fault since TTBR1 walks are disabled in the TTBCR. This patch fixes the issue by disabling sign-extension of a fixed virtual address bit and instead basing the behaviour on the upstream bus size: the incoming address is zero extended unless the upstream bus is only 49 bits wide, in which case bit 48 is used as the sign bit and is replicated to the upper bits. Cc: # v4.0+ Reported-by: Varun Sethi Signed-off-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/arm-smmu.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 9f7e1d34a32b..66a803b9dd3a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -224,14 +224,7 @@ #define RESUME_TERMINATE (1 << 0) #define TTBCR2_SEP_SHIFT 15 -#define TTBCR2_SEP_MASK 0x7 - -#define TTBCR2_ADDR_32 0 -#define TTBCR2_ADDR_36 1 -#define TTBCR2_ADDR_40 2 -#define TTBCR2_ADDR_42 3 -#define TTBCR2_ADDR_44 4 -#define TTBCR2_ADDR_48 5 +#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) #define TTBRn_HI_ASID_SHIFT 16 @@ -793,26 +786,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR); if (smmu->version > ARM_SMMU_V1) { reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; - switch (smmu->va_size) { - case 32: - reg |= (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT); - break; - case 36: - reg |= (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT); - break; - case 40: - reg |= (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT); - break; - case 42: - reg |= (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT); - break; - case 44: - reg |= (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT); - break; - case 48: - reg |= (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT); - break; - } + reg |= TTBCR2_SEP_UPSTREAM; writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2); } } else { -- cgit v1.2.3 From dc3814f404dfb82fc40f403725ad0548c9541d09 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 8 May 2015 17:05:44 -0700 Subject: iommu/exynos: Tell kmemleak to ignore 2nd level page tables The pointers to the 2nd level page tables are converted to 1st level page table entries, which means kmemleak can't find them and assumes they have been leaked. Call kmemleak_ignore on the 2nd level page tables to prevent them from showing up in kmemleak reports. Signed-off-by: Colin Cross Signed-off-by: Dmitry Torokhov Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3e898504a7c4..e54c1befc193 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -862,6 +862,7 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, return ERR_PTR(-ENOMEM); *sent = mk_lv1ent_page(virt_to_phys(pent)); + kmemleak_ignore(pent); *pgcounter = NUM_LV2ENTRIES; pgtable_flush(pent, pent + NUM_LV2ENTRIES); pgtable_flush(sent, sent + 1); -- cgit v1.2.3 From d7ef9995f1d9e394f994b9a1755cccb21ba3e421 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:23 +0200 Subject: iommu: Init iommu-groups support earlier, in core_initcall iommu_group_alloc might be called very early in case of iommu controllers activated from of_iommu, so ensure that this part of subsystem is ready when devices are being populated from device-tree (core_initcall seems to be okay for this case). Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index d4f527e56679..37a6aa8f318b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1207,7 +1207,7 @@ static int __init iommu_init(void) return 0; } -arch_initcall(iommu_init); +core_initcall(iommu_init); int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) -- cgit v1.2.3 From 512bd0c6cced8264dae4c7439fef54507548a72d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:24 +0200 Subject: iommu/exynos: Don't read version register on every tlb operation This patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index e54c1befc193..829931cd105a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -213,6 +213,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; phys_addr_t pgtable; + unsigned int version; }; static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom) @@ -244,11 +245,6 @@ static void sysmmu_unblock(void __iomem *sfrbase) __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); } -static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) -{ - return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -408,7 +404,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) unsigned int cfg = CFG_LRU | CFG_QOS(15); unsigned int ver; - ver = __raw_sysmmu_version(data); + ver = MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); if (MMU_MAJ_VER(ver) == 3) { if (MMU_MIN_VER(ver) >= 2) { cfg |= CFG_FLPDCACHE; @@ -422,6 +418,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) } __raw_writel(cfg, data->sfrbase + REG_MMU_CFG); + data->version = ver; } static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) @@ -531,7 +528,7 @@ static bool exynos_sysmmu_disable(struct device *dev) static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { - if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + if (data->version == MAKE_MMU_VER(3, 3)) __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); } @@ -580,7 +577,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, * 1MB page can be cached in one of all sets. * 64KB page can be one of 16 consecutive sets. */ - if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) + if (MMU_MAJ_VER(data->version) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data->sfrbase)) { -- cgit v1.2.3 From 3211cf20421c216426a05fc072f6131122800784 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:25 +0200 Subject: iommu/exynos: Remove unused functions This patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 829931cd105a..278eb19d6463 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -496,13 +496,6 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, return ret; } -int exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable) -{ - BUG_ON(!memblock_is_memory(pgtable)); - - return __exynos_sysmmu_enable(dev, pgtable, NULL); -} - static bool exynos_sysmmu_disable(struct device *dev) { unsigned long flags; @@ -594,30 +587,6 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, spin_unlock_irqrestore(&data->lock, flags); } -void exynos_sysmmu_tlb_invalidate(struct device *dev) -{ - struct exynos_iommu_owner *owner = dev->archdata.iommu; - unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu); - - spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data)) { - if (!IS_ERR(data->clk_master)) - clk_enable(data->clk_master); - if (sysmmu_block(data->sfrbase)) { - __sysmmu_tlb_invalidate(data->sfrbase); - sysmmu_unblock(data->sfrbase); - } - if (!IS_ERR(data->clk_master)) - clk_disable(data->clk_master); - } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation\n"); - } - spin_unlock_irqrestore(&data->lock, flags); -} - static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; -- cgit v1.2.3 From 73db56989664c5daccb5424bc99483d3a099659e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:26 +0200 Subject: iommu/exynos: Remove useless members from exynos_iommu_owner structure This patch removes useless spinlocks and other unused members from struct exynos_iommu_owner. There is no point is protecting this structure by spinlock because content of this structure doesn't change and other structures have their own spinlocks. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 278eb19d6463..311d4edd2658 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -189,9 +189,6 @@ struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; struct device *sysmmu; - struct iommu_domain *domain; - void *vmm_data; /* IO virtual memory manager's data */ - spinlock_t lock; /* Lock to preserve consistency of System MMU */ }; struct exynos_iommu_domain { @@ -477,44 +474,34 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, struct iommu_domain *domain) { int ret = 0; - unsigned long flags; struct exynos_iommu_owner *owner = dev->archdata.iommu; struct sysmmu_drvdata *data; BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu); ret = __sysmmu_enable(data, pgtable, domain); if (ret >= 0) data->master = dev; - spin_unlock_irqrestore(&owner->lock, flags); - return ret; } static bool exynos_sysmmu_disable(struct device *dev) { - unsigned long flags; bool disabled = true; struct exynos_iommu_owner *owner = dev->archdata.iommu; struct sysmmu_drvdata *data; BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu); disabled = __sysmmu_disable(data); if (disabled) data->master = NULL; - spin_unlock_irqrestore(&owner->lock, flags); - return disabled; } @@ -762,10 +749,8 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, spin_lock_irqsave(&priv->lock, flags); ret = __exynos_sysmmu_enable(dev, pagetable, domain); - if (ret == 0) { + if (ret == 0) list_add_tail(&owner->client, &priv->clients); - owner->domain = domain; - } spin_unlock_irqrestore(&priv->lock, flags); @@ -793,10 +778,8 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, list_for_each_entry(owner, &priv->clients, client) { if (owner == dev->archdata.iommu) { - if (exynos_sysmmu_disable(dev)) { + if (exynos_sysmmu_disable(dev)) list_del_init(&owner->client); - owner->domain = NULL; - } break; } } -- cgit v1.2.3 From 469acebe4a68902bf5b43daed61465eae1ce04ec Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:27 +0200 Subject: iommu/exynos: Refactor function parameters to simplify code This patch simplifies the code by: - refactoring function parameters from struct device pointer to direct pointer to struct sysmmu drvdata - moving list_head enteries from struct exynos_iommu_owner directly to struct sysmmu_drvdata After above refactoring some functions were never used, so remove also them completely. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 134 ++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 86 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 311d4edd2658..5d7dbd0c8ae8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,8 +186,6 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { /* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct list_head client; /* entry of exynos_iommu_domain.clients */ - struct device *dev; struct device *sysmmu; }; @@ -209,6 +207,7 @@ struct sysmmu_drvdata { int activations; spinlock_t lock; struct iommu_domain *domain; + struct list_head domain_node; phys_addr_t pgtable; unsigned int version; }; @@ -464,47 +463,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, return ret; } -/* __exynos_sysmmu_enable: Enables System MMU - * - * returns -error if an error occurred and System MMU is not enabled, - * 0 if the System MMU has been just enabled and 1 if System MMU was already - * enabled before. - */ -static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, - struct iommu_domain *domain) -{ - int ret = 0; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - ret = __sysmmu_enable(data, pgtable, domain); - if (ret >= 0) - data->master = dev; - - return ret; -} - -static bool exynos_sysmmu_disable(struct device *dev) -{ - bool disabled = true; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - disabled = __sysmmu_disable(data); - if (disabled) - data->master = NULL; - - return disabled; -} - static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { @@ -512,12 +470,10 @@ static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); } -static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, +static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { unsigned long flags; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data = dev_get_drvdata(owner->sysmmu); if (!IS_ERR(data->clk_master)) clk_enable(data->clk_master); @@ -531,14 +487,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, clk_disable(data->clk_master); } -static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, - size_t size) +static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, + sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner = dev->archdata.iommu; unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu); spin_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { @@ -568,8 +520,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, if (!IS_ERR(data->clk_master)) clk_disable(data->clk_master); } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation @ %#x\n", - iova); + dev_dbg(data->master, + "disabled. Skipping TLB invalidation @ %#x\n", iova); } spin_unlock_irqrestore(&data->lock, flags); } @@ -709,7 +661,7 @@ err_pgtable: static void exynos_iommu_domain_free(struct iommu_domain *domain) { struct exynos_iommu_domain *priv = to_exynos_domain(domain); - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data, *next; unsigned long flags; int i; @@ -717,14 +669,12 @@ static void exynos_iommu_domain_free(struct iommu_domain *domain) spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(owner, &priv->clients, client) { - while (!exynos_sysmmu_disable(owner->dev)) - ; /* until System MMU is actually disabled */ + list_for_each_entry_safe(data, next, &priv->clients, domain_node) { + if (__sysmmu_disable(data)) + data->master = NULL; + list_del_init(&data->domain_node); } - while (!list_empty(&priv->clients)) - list_del_init(priv->clients.next); - spin_unlock_irqrestore(&priv->lock, flags); for (i = 0; i < NUM_LV1ENTRIES; i++) @@ -742,17 +692,25 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, { struct exynos_iommu_owner *owner = dev->archdata.iommu; struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct sysmmu_drvdata *data; phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; - int ret; + int ret = -ENODEV; - spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return -ENODEV; - ret = __exynos_sysmmu_enable(dev, pagetable, domain); - if (ret == 0) - list_add_tail(&owner->client, &priv->clients); - - spin_unlock_irqrestore(&priv->lock, flags); + data = dev_get_drvdata(owner->sysmmu); + if (data) { + ret = __sysmmu_enable(data, pagetable, domain); + if (ret >= 0) { + data->master = dev; + + spin_lock_irqsave(&priv->lock, flags); + list_add_tail(&data->domain_node, &priv->clients); + spin_unlock_irqrestore(&priv->lock, flags); + } + } if (ret < 0) { dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n", @@ -769,24 +727,29 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, static void exynos_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { - struct exynos_iommu_owner *owner; struct exynos_iommu_domain *priv = to_exynos_domain(domain); phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct sysmmu_drvdata *data; unsigned long flags; + bool found = false; - spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return; - list_for_each_entry(owner, &priv->clients, client) { - if (owner == dev->archdata.iommu) { - if (exynos_sysmmu_disable(dev)) - list_del_init(&owner->client); + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(data, &priv->clients, domain_node) { + if (data->master == dev) { + if (__sysmmu_disable(data)) { + data->master = NULL; + list_del_init(&data->domain_node); + } + found = true; break; } } - spin_unlock_irqrestore(&priv->lock, flags); - if (owner == dev->archdata.iommu) + if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", __func__, &pagetable); else @@ -834,12 +797,11 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, * not currently mapped. */ if (need_flush_flpd_cache) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; spin_lock(&priv->lock); - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache( - owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); spin_unlock(&priv->lock); } } @@ -874,13 +836,13 @@ static int lv1set_section(struct exynos_iommu_domain *priv, spin_lock(&priv->lock); if (lv1ent_page_zero(sent)) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache(owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); } spin_unlock(&priv->lock); @@ -985,13 +947,13 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_entry(owner->dev, iova, size); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_entry(data, iova, size); spin_unlock_irqrestore(&priv->lock, flags); } -- cgit v1.2.3 From bfa004893c55ead8ac1f2784601dac3609deb406 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:28 +0200 Subject: iommu/exynos: Rename variables to reflect their purpose This patch renames some variables to make the code easier to understand. 'domain' is replaced by 'iommu_domain' (more generic entity) and really meaningless 'priv' by 'domain' to reflect its purpose. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 187 ++++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 93 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5d7dbd0c8ae8..f5475dcd6cd1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -435,8 +435,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) clk_disable(data->clk_master); } -static int __sysmmu_enable(struct sysmmu_drvdata *data, - phys_addr_t pgtable, struct iommu_domain *domain) +static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, + struct iommu_domain *iommu_domain) { int ret = 0; unsigned long flags; @@ -444,7 +444,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, spin_lock_irqsave(&data->lock, flags); if (set_sysmmu_active(data)) { data->pgtable = pgtable; - data->domain = domain; + data->domain = iommu_domain; __sysmmu_enable_nocount(data); @@ -609,91 +609,91 @@ static inline void pgtable_flush(void *vastart, void *vaend) static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type) { - struct exynos_iommu_domain *exynos_domain; + struct exynos_iommu_domain *domain; int i; if (type != IOMMU_DOMAIN_UNMANAGED) return NULL; - exynos_domain = kzalloc(sizeof(*exynos_domain), GFP_KERNEL); - if (!exynos_domain) + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return NULL; - exynos_domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); - if (!exynos_domain->pgtable) + domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); + if (!domain->pgtable) goto err_pgtable; - exynos_domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!exynos_domain->lv2entcnt) + domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!domain->lv2entcnt) goto err_counter; /* Workaround for System MMU v3.3 to prevent caching 1MiB mapping */ for (i = 0; i < NUM_LV1ENTRIES; i += 8) { - exynos_domain->pgtable[i + 0] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 1] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 2] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 3] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 4] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 5] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 6] = ZERO_LV2LINK; - exynos_domain->pgtable[i + 7] = ZERO_LV2LINK; + domain->pgtable[i + 0] = ZERO_LV2LINK; + domain->pgtable[i + 1] = ZERO_LV2LINK; + domain->pgtable[i + 2] = ZERO_LV2LINK; + domain->pgtable[i + 3] = ZERO_LV2LINK; + domain->pgtable[i + 4] = ZERO_LV2LINK; + domain->pgtable[i + 5] = ZERO_LV2LINK; + domain->pgtable[i + 6] = ZERO_LV2LINK; + domain->pgtable[i + 7] = ZERO_LV2LINK; } - pgtable_flush(exynos_domain->pgtable, exynos_domain->pgtable + NUM_LV1ENTRIES); + pgtable_flush(domain->pgtable, domain->pgtable + NUM_LV1ENTRIES); - spin_lock_init(&exynos_domain->lock); - spin_lock_init(&exynos_domain->pgtablelock); - INIT_LIST_HEAD(&exynos_domain->clients); + spin_lock_init(&domain->lock); + spin_lock_init(&domain->pgtablelock); + INIT_LIST_HEAD(&domain->clients); - exynos_domain->domain.geometry.aperture_start = 0; - exynos_domain->domain.geometry.aperture_end = ~0UL; - exynos_domain->domain.geometry.force_aperture = true; + domain->domain.geometry.aperture_start = 0; + domain->domain.geometry.aperture_end = ~0UL; + domain->domain.geometry.force_aperture = true; - return &exynos_domain->domain; + return &domain->domain; err_counter: - free_pages((unsigned long)exynos_domain->pgtable, 2); + free_pages((unsigned long)domain->pgtable, 2); err_pgtable: - kfree(exynos_domain); + kfree(domain); return NULL; } -static void exynos_iommu_domain_free(struct iommu_domain *domain) +static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); struct sysmmu_drvdata *data, *next; unsigned long flags; int i; - WARN_ON(!list_empty(&priv->clients)); + WARN_ON(!list_empty(&domain->clients)); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry_safe(data, next, &priv->clients, domain_node) { + list_for_each_entry_safe(data, next, &domain->clients, domain_node) { if (__sysmmu_disable(data)) data->master = NULL; list_del_init(&data->domain_node); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); for (i = 0; i < NUM_LV1ENTRIES; i++) - if (lv1ent_page(priv->pgtable + i)) + if (lv1ent_page(domain->pgtable + i)) kmem_cache_free(lv2table_kmem_cache, - phys_to_virt(lv2table_base(priv->pgtable + i))); + phys_to_virt(lv2table_base(domain->pgtable + i))); - free_pages((unsigned long)priv->pgtable, 2); - free_pages((unsigned long)priv->lv2entcnt, 1); - kfree(priv); + free_pages((unsigned long)domain->pgtable, 2); + free_pages((unsigned long)domain->lv2entcnt, 1); + kfree(domain); } -static int exynos_iommu_attach_device(struct iommu_domain *domain, +static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, struct device *dev) { struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); struct sysmmu_drvdata *data; - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + phys_addr_t pagetable = virt_to_phys(domain->pgtable); unsigned long flags; int ret = -ENODEV; @@ -702,13 +702,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, data = dev_get_drvdata(owner->sysmmu); if (data) { - ret = __sysmmu_enable(data, pagetable, domain); + ret = __sysmmu_enable(data, pagetable, iommu_domain); if (ret >= 0) { data->master = dev; - spin_lock_irqsave(&priv->lock, flags); - list_add_tail(&data->domain_node, &priv->clients); - spin_unlock_irqrestore(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); + list_add_tail(&data->domain_node, &domain->clients); + spin_unlock_irqrestore(&domain->lock, flags); } } @@ -724,11 +724,11 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return ret; } -static void exynos_iommu_detach_device(struct iommu_domain *domain, +static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, struct device *dev) { - struct exynos_iommu_domain *priv = to_exynos_domain(domain); - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); + phys_addr_t pagetable = virt_to_phys(domain->pgtable); struct sysmmu_drvdata *data; unsigned long flags; bool found = false; @@ -736,8 +736,8 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return; - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) { + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry(data, &domain->clients, domain_node) { if (data->master == dev) { if (__sysmmu_disable(data)) { data->master = NULL; @@ -747,7 +747,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, break; } } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", @@ -756,7 +756,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, dev_err(dev, "%s: No IOMMU is attached\n", __func__); } -static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, +static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter) { if (lv1ent_section(sent)) { @@ -799,17 +799,17 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, if (need_flush_flpd_cache) { struct sysmmu_drvdata *data; - spin_lock(&priv->lock); - list_for_each_entry(data, &priv->clients, domain_node) + spin_lock(&domain->lock); + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); - spin_unlock(&priv->lock); + spin_unlock(&domain->lock); } } return page_entry(sent, iova); } -static int lv1set_section(struct exynos_iommu_domain *priv, +static int lv1set_section(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, phys_addr_t paddr, short *pgcnt) { @@ -834,17 +834,17 @@ static int lv1set_section(struct exynos_iommu_domain *priv, pgtable_flush(sent, sent + 1); - spin_lock(&priv->lock); + spin_lock(&domain->lock); if (lv1ent_page_zero(sent)) { struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); } - spin_unlock(&priv->lock); + spin_unlock(&domain->lock); return 0; } @@ -904,74 +904,75 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, * than or equal to 128KiB. * - Start address of an I/O virtual region must be aligned by 128KiB. */ -static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, - phys_addr_t paddr, size_t size, int prot) +static int exynos_iommu_map(struct iommu_domain *iommu_domain, + unsigned long l_iova, phys_addr_t paddr, size_t size, + int prot) { - struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); sysmmu_pte_t *entry; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; unsigned long flags; int ret = -ENOMEM; - BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL); - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova); if (size == SECT_SIZE) { - ret = lv1set_section(priv, entry, iova, paddr, - &priv->lv2entcnt[lv1ent_offset(iova)]); + ret = lv1set_section(domain, entry, iova, paddr, + &domain->lv2entcnt[lv1ent_offset(iova)]); } else { sysmmu_pte_t *pent; - pent = alloc_lv2entry(priv, entry, iova, - &priv->lv2entcnt[lv1ent_offset(iova)]); + pent = alloc_lv2entry(domain, entry, iova, + &domain->lv2entcnt[lv1ent_offset(iova)]); if (IS_ERR(pent)) ret = PTR_ERR(pent); else ret = lv2set_page(pent, paddr, size, - &priv->lv2entcnt[lv1ent_offset(iova)]); + &domain->lv2entcnt[lv1ent_offset(iova)]); } if (ret) pr_err("%s: Failed(%d) to map %#zx bytes @ %#x\n", __func__, ret, size, iova); - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); return ret; } -static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, - sysmmu_iova_t iova, size_t size) +static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain, + sysmmu_iova_t iova, size_t size) { struct sysmmu_drvdata *data; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_entry(data, iova, size); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); } -static size_t exynos_iommu_unmap(struct iommu_domain *domain, - unsigned long l_iova, size_t size) +static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, + unsigned long l_iova, size_t size) { - struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; sysmmu_pte_t *ent; size_t err_pgsize; unsigned long flags; - BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL); - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - ent = section_entry(priv->pgtable, iova); + ent = section_entry(domain->pgtable, iova); if (lv1ent_section(ent)) { if (WARN_ON(size < SECT_SIZE)) { @@ -1005,7 +1006,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, *ent = 0; size = SPAGE_SIZE; pgtable_flush(ent, ent + 1); - priv->lv2entcnt[lv1ent_offset(iova)] += 1; + domain->lv2entcnt[lv1ent_offset(iova)] += 1; goto done; } @@ -1019,15 +1020,15 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, pgtable_flush(ent, ent + SPAGES_PER_LPAGE); size = LPAGE_SIZE; - priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; + domain->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; done: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); - exynos_iommu_tlb_invalidate_entry(priv, iova, size); + exynos_iommu_tlb_invalidate_entry(domain, iova, size); return size; err: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); pr_err("%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n", __func__, size, iova, err_pgsize); @@ -1035,17 +1036,17 @@ err: return 0; } -static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, +static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, dma_addr_t iova) { - struct exynos_iommu_domain *priv = to_exynos_domain(domain); + struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); sysmmu_pte_t *entry; unsigned long flags; phys_addr_t phys = 0; - spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags); - entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova); if (lv1ent_section(entry)) { phys = section_phys(entry) + section_offs(iova); @@ -1058,7 +1059,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, phys = spage_phys(entry) + spage_offs(iova); } - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags); return phys; } -- cgit v1.2.3 From a9133b9936ca43a93c45bfeabf0eef2ce091ee63 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:29 +0200 Subject: iommu/exynos: Use struct exynos_iommu_domain in internal structures Replace all remaining usage of struct iommu_domain with struct exynos_iommu_domain in all internal structures and functions. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f5475dcd6cd1..ba1271f51948 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -206,7 +206,7 @@ struct sysmmu_drvdata { struct clk *clk_master; int activations; spinlock_t lock; - struct iommu_domain *domain; + struct exynos_iommu_domain *domain; struct list_head domain_node; phys_addr_t pgtable; unsigned int version; @@ -337,7 +337,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) show_fault_information(dev_name(data->sysmmu), itype, base, addr); if (data->domain) - ret = report_iommu_fault(data->domain, + ret = report_iommu_fault(&data->domain->domain, data->master, addr, itype); } @@ -436,7 +436,7 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) } static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, - struct iommu_domain *iommu_domain) + struct exynos_iommu_domain *domain) { int ret = 0; unsigned long flags; @@ -444,7 +444,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, spin_lock_irqsave(&data->lock, flags); if (set_sysmmu_active(data)) { data->pgtable = pgtable; - data->domain = iommu_domain; + data->domain = domain; __sysmmu_enable_nocount(data); @@ -702,7 +702,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, data = dev_get_drvdata(owner->sysmmu); if (data) { - ret = __sysmmu_enable(data, pagetable, iommu_domain); + ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; -- cgit v1.2.3 From 312900c605a7fc108e9d290745a5da6212062bce Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:30 +0200 Subject: iommu/exynos: Remove excessive includes and sort others alphabetically Removed following unused includes: , , and . Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ba1271f51948..9d9fea2156cb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,19 +12,15 @@ #define DEBUG #endif -#include -#include -#include -#include -#include #include #include -#include +#include #include -#include +#include #include -#include -#include +#include +#include +#include #include #include -- cgit v1.2.3 From 2860af3c8f7ac9e2569378653c3948c422bd30c2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:31 +0200 Subject: iommu/exynos: Document internal structures Add a few words of comment to all internal structures used by the driver. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 53 +++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 18 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9d9fea2156cb..f6ed59cad90b 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -180,32 +180,49 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "UNKNOWN FAULT" }; -/* attached to dev.archdata.iommu of the master device */ +/* + * This structure is attached to dev.archdata.iommu of the master device + * on device add, contains a list of SYSMMU controllers defined by device tree, + * which are bound to given master device. It is usually referenced by 'owner' + * pointer. +*/ struct exynos_iommu_owner { - struct device *sysmmu; + struct device *sysmmu; /* sysmmu controller for given master */ }; +/* + * This structure exynos specific generalization of struct iommu_domain. + * It contains list of SYSMMU controllers from all master devices, which has + * been attached to this domain and page tables of IO address space defined by + * it. It is usually referenced by 'domain' pointer. + */ struct exynos_iommu_domain { - struct list_head clients; /* list of sysmmu_drvdata.node */ - sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */ - short *lv2entcnt; /* free lv2 entry counter for each section */ - spinlock_t lock; /* lock for this structure */ - spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ + struct list_head clients; /* list of sysmmu_drvdata.domain_node */ + sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */ + short *lv2entcnt; /* free lv2 entry counter for each section */ + spinlock_t lock; /* lock for modyfying list of clients */ + spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ struct iommu_domain domain; /* generic domain data structure */ }; +/* + * This structure hold all data of a single SYSMMU controller, this includes + * hw resources like registers and clocks, pointers and list nodes to connect + * it to all other structures, internal state and parameters read from device + * tree. It is usually referenced by 'data' pointer. + */ struct sysmmu_drvdata { - struct device *sysmmu; /* System MMU's device descriptor */ - struct device *master; /* Owner of system MMU */ - void __iomem *sfrbase; - struct clk *clk; - struct clk *clk_master; - int activations; - spinlock_t lock; - struct exynos_iommu_domain *domain; - struct list_head domain_node; - phys_addr_t pgtable; - unsigned int version; + struct device *sysmmu; /* SYSMMU controller device */ + struct device *master; /* master device (owner) */ + void __iomem *sfrbase; /* our registers */ + struct clk *clk; /* SYSMMU's clock */ + struct clk *clk_master; /* master's device clock */ + int activations; /* number of calls to sysmmu_enable */ + spinlock_t lock; /* lock for modyfying state */ + struct exynos_iommu_domain *domain; /* domain we belong to */ + struct list_head domain_node; /* node for domain clients list */ + phys_addr_t pgtable; /* assigned page table structure */ + unsigned int version; /* our version */ }; static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom) -- cgit v1.2.3 From 06801db0d3bba2b4e585130981fadf71bbce4159 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:32 +0200 Subject: iommu/exynos: Add/remove callbacks should fail if no iommu is available Return fail if given master device passed to add_device/remove_device callbacks doesn't has associated any sysmmu controller. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f6ed59cad90b..99ed39c89e0b 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1082,6 +1082,9 @@ static int exynos_iommu_add_device(struct device *dev) struct iommu_group *group; int ret; + if (!has_sysmmu(dev)) + return -ENODEV; + group = iommu_group_get(dev); if (!group) { @@ -1100,6 +1103,9 @@ static int exynos_iommu_add_device(struct device *dev) static void exynos_iommu_remove_device(struct device *dev) { + if (!has_sysmmu(dev)) + return; + iommu_group_remove_device(dev); } -- cgit v1.2.3 From 1b09205436847897da1826a88e5cefd9cde5eed7 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:33 +0200 Subject: iommu/exynos: Add support for binding more than one sysmmu to master device This patch adds support for assigning more than one SYSMMU controller to the master device. This has been achieved simply by chaning the struct device pointer in struct exynos_iommu_owner into the list of struct sysmmu_drvdata of all controllers assigned to the given master device. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 99ed39c89e0b..b7f679c4659c 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -187,7 +187,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { * pointer. */ struct exynos_iommu_owner { - struct device *sysmmu; /* sysmmu controller for given master */ + struct list_head controllers; /* list of sysmmu_drvdata.owner_node */ }; /* @@ -221,6 +221,7 @@ struct sysmmu_drvdata { spinlock_t lock; /* lock for modyfying state */ struct exynos_iommu_domain *domain; /* domain we belong to */ struct list_head domain_node; /* node for domain clients list */ + struct list_head owner_node; /* node for owner controllers list */ phys_addr_t pgtable; /* assigned page table structure */ unsigned int version; /* our version */ }; @@ -713,8 +714,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, if (!has_sysmmu(dev)) return -ENODEV; - data = dev_get_drvdata(owner->sysmmu); - if (data) { + list_for_each_entry(data, &owner->controllers, owner_node) { ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -742,7 +742,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, { struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); phys_addr_t pagetable = virt_to_phys(domain->pgtable); - struct sysmmu_drvdata *data; + struct sysmmu_drvdata *data, *next; unsigned long flags; bool found = false; @@ -750,14 +750,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, return; spin_lock_irqsave(&domain->lock, flags); - list_for_each_entry(data, &domain->clients, domain_node) { + 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); } found = true; - break; } } spin_unlock_irqrestore(&domain->lock, flags); -- cgit v1.2.3 From ce70ca562b8d17cd995e3a3a44ad23d97dfbb38e Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:34 +0200 Subject: iommu/exynos: Add support for runtime_pm This patch fixes support for runtime power management for SYSMMU controllers, so they are enabled when master device is attached. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b7f679c4659c..c65826847819 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -715,6 +715,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, return -ENODEV; list_for_each_entry(data, &owner->controllers, owner_node) { + pm_runtime_get_sync(data->sysmmu); ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -756,6 +757,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, data->master = NULL; list_del_init(&data->domain_node); } + pm_runtime_put(data->sysmmu); found = true; } } -- cgit v1.2.3 From 622015e407b0824805bb07416b8d9cdfbdc00182 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:35 +0200 Subject: iommu/exynos: Add system suspend/resume support When system goes into suspend state, iommu should save it's state and restore after system resume. This is handled by 'late' pm ops to ensure that sysmmu will be suspended after its master devices and restored before them. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c65826847819..5d2033b5a5b3 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -601,6 +601,36 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int exynos_sysmmu_suspend(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + + dev_dbg(dev, "suspend\n"); + if (is_sysmmu_active(data)) { + __sysmmu_disable_nocount(data); + pm_runtime_put(dev); + } + return 0; +} + +static int exynos_sysmmu_resume(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + + dev_dbg(dev, "resume\n"); + if (is_sysmmu_active(data)) { + pm_runtime_get_sync(dev); + __sysmmu_enable_nocount(data); + } + return 0; +} +#endif + +static const struct dev_pm_ops sysmmu_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_sysmmu_suspend, exynos_sysmmu_resume) +}; + static const struct of_device_id sysmmu_of_match[] __initconst = { { .compatible = "samsung,exynos-sysmmu", }, { }, @@ -611,6 +641,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { .driver = { .name = "exynos-sysmmu", .of_match_table = sysmmu_of_match, + .pm = &sysmmu_pm_ops, } }; -- cgit v1.2.3 From 8ed55c812fa8c8ab790bda52dea4a1a87d94fb2b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:36 +0200 Subject: iommu/exynos: Init from dt-specific callback instead of initcall This patch introduces IOMMU_OF_DECLARE-based initialization to the driver, which replaces subsys_initcall-based procedure. exynos_iommu_of_setup ensures that each sysmmu controller is probed before its master device. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5d2033b5a5b3..0fb40ee9d728 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -13,16 +13,21 @@ #endif #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include #include +#include #include typedef u32 sysmmu_iova_t; @@ -1141,7 +1146,7 @@ static void exynos_iommu_remove_device(struct device *dev) iommu_group_remove_device(dev); } -static const struct iommu_ops exynos_iommu_ops = { +static struct iommu_ops exynos_iommu_ops = { .domain_alloc = exynos_iommu_domain_alloc, .domain_free = exynos_iommu_domain_free, .attach_dev = exynos_iommu_attach_device, @@ -1155,17 +1160,12 @@ static const struct iommu_ops exynos_iommu_ops = { .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; +static bool init_done; + static int __init exynos_iommu_init(void) { - struct device_node *np; int ret; - np = of_find_matching_node(NULL, sysmmu_of_match); - if (!np) - return 0; - - of_node_put(np); - lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table", LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL); if (!lv2table_kmem_cache) { @@ -1194,6 +1194,8 @@ static int __init exynos_iommu_init(void) goto err_set_iommu; } + init_done = true; + return 0; err_set_iommu: kmem_cache_free(lv2table_kmem_cache, zero_lv2_table); @@ -1203,4 +1205,21 @@ err_reg_driver: kmem_cache_destroy(lv2table_kmem_cache); return ret; } -subsys_initcall(exynos_iommu_init); + +static int __init exynos_iommu_of_setup(struct device_node *np) +{ + struct platform_device *pdev; + + if (!init_done) + exynos_iommu_init(); + + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + of_iommu_set_ops(np, &exynos_iommu_ops); + return 0; +} + +IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", + exynos_iommu_of_setup); -- cgit v1.2.3 From aa759fd376fbbfc34d5bf21b8f280ce68c17d064 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 19 May 2015 15:20:37 +0200 Subject: iommu/exynos: Add callback for initializing devices from device tree This patch adds implementation of of_xlate callback, which prepares masters device for attaching to IOMMU. This callback is called during creating devices from device tree. Signed-off-by: Marek Szyprowski Tested-by: Javier Martinez Canillas Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0fb40ee9d728..97c41b8ab5d9 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1146,6 +1146,33 @@ static void exynos_iommu_remove_device(struct device *dev) iommu_group_remove_device(dev); } +static int exynos_iommu_of_xlate(struct device *dev, + struct of_phandle_args *spec) +{ + struct exynos_iommu_owner *owner = dev->archdata.iommu; + struct platform_device *sysmmu = of_find_device_by_node(spec->np); + struct sysmmu_drvdata *data; + + if (!sysmmu) + return -ENODEV; + + data = platform_get_drvdata(sysmmu); + if (!data) + return -ENODEV; + + if (!owner) { + owner = kzalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) + return -ENOMEM; + + INIT_LIST_HEAD(&owner->controllers); + dev->archdata.iommu = owner; + } + + list_add_tail(&data->owner_node, &owner->controllers); + return 0; +} + static struct iommu_ops exynos_iommu_ops = { .domain_alloc = exynos_iommu_domain_alloc, .domain_free = exynos_iommu_domain_free, @@ -1158,6 +1185,7 @@ static struct iommu_ops exynos_iommu_ops = { .add_device = exynos_iommu_add_device, .remove_device = exynos_iommu_remove_device,