From 8a7f431221602fcde573dfdba26de1990ec195a0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 19 Aug 2013 12:20:37 +0100 Subject: iommu/arm-smmu: replace devm_request_and_ioremap by devm_ioremap_resource Use devm_ioremap_resource instead of devm_request_and_ioremap. This was partly done using the semantic patch scripts/coccinelle/api/devm_ioremap_resource.cocci The error-handling code on the call to platform_get_resource was removed manually, and the initialization of smmu->size was manually moved lower, to take advantage of the NULL test on res performed by devm_ioremap_resource. Signed-off-by: Julia Lawall Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 181c9ba929cd..abe83c3757ab 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1781,15 +1781,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) smmu->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing base address/size\n"); - return -ENODEV; - } - + smmu->base = devm_ioremap_resource(dev, res); + if (IS_ERR(smmu->base)) + return PTR_ERR(smmu->base); smmu->size = resource_size(res); - smmu->base = devm_request_and_ioremap(dev, res); - if (!smmu->base) - return -EADDRNOTAVAIL; if (of_property_read_u32(dev->of_node, "#global-interrupts", &smmu->num_global_irqs)) { -- cgit v1.2.3 From 25724841dfaed05f23a3ddaaaed5c9b61ceea7bd Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 21 Aug 2013 13:49:53 +0100 Subject: iommu/arm-smmu: use relaxed accessors where possible Apart from fault handling and page table manipulation, we don't care about memory ordering between SMMU control registers and normal, cacheable memory, so use the _relaxed I/O accessors wherever possible. Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index abe83c3757ab..293192150f5a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -778,7 +778,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) #ifdef __BIG_ENDIAN reg |= SCTLR_E; #endif - writel(reg, cb_base + ARM_SMMU_CB_SCTLR); + writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR); } static int arm_smmu_init_domain_context(struct iommu_domain *domain, @@ -1595,7 +1595,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) /* Push the button */ arm_smmu_tlb_sync(smmu); - writel(scr0, gr0_base + ARM_SMMU_GR0_sCR0); + writel_relaxed(scr0, gr0_base + ARM_SMMU_GR0_sCR0); } static int arm_smmu_id_size_to_bits(int size) @@ -1928,7 +1928,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev) free_irq(smmu->irqs[i], smmu); /* Turn the thing off */ - writel(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0); + writel_relaxed(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0); return 0; } -- cgit v1.2.3 From b1950b2796da80b66df02db39cc3417266b73767 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 1 Oct 2013 13:39:05 +0100 Subject: iommu/arm-smmu: Switch to subsys_initcall for driver registration This should ensure that arm-smmu is initialized before other drivers start handling devices that propably need smmu support. Signed-off-by: Andreas Herrmann Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 293192150f5a..878a5b926fe5 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1976,7 +1976,7 @@ static void __exit arm_smmu_exit(void) return platform_driver_unregister(&arm_smmu_driver); } -module_init(arm_smmu_init); +subsys_initcall(arm_smmu_init); module_exit(arm_smmu_exit); MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); -- cgit v1.2.3 From c55af7f719cbb0f0b28f42b3f98f662278f063c2 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 1 Oct 2013 13:39:06 +0100 Subject: iommu/arm-smmu: Refine check for proper size of mapped region There is already a check to print a warning if the size of SMMU address space (calculated from SMMU register values) is greater than the size of the mapped memory region (e.g. passed via DT to the driver). Adapt this check to print also a warning in case the mapped region is larger than the SMMU address space. Such a mismatch could be intentional (to fix wrong register values). If its not intentional (e.g. due to wrong DT information) this will very likely cause a malfunction of the driver as SMMU_CB_BASE is derived from the size of the mapped region. The warning helps to identify the root cause in this case. Signed-off-by: Andreas Herrmann Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 878a5b926fe5..1f79daa5f4b1 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1700,13 +1700,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1); smmu->pagesize = (id & ID1_PAGESIZE) ? SZ_64K : SZ_4K; - /* Check that we ioremapped enough */ + /* Check for size mismatch of SMMU address space from mapped region */ size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); size *= (smmu->pagesize << 1); - if (smmu->size < size) - dev_warn(smmu->dev, - "device is 0x%lx bytes but only mapped 0x%lx!\n", - size, smmu->size); + if (smmu->size != size) + dev_warn(smmu->dev, "SMMU address space size (0x%lx) differs " + "from mapped region size (0x%lx)!\n", size, smmu->size); smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & ID1_NUMS2CB_MASK; -- cgit v1.2.3 From 44a08de2aaf7f4cf86dfcf04bee32536e4a2b5b8 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 1 Oct 2013 13:39:07 +0100 Subject: iommu/arm-smmu: Check for num_context_irqs > 0 to avoid divide by zero exception With the right (or wrong;-) definition of v1 SMMU node in DTB it is possible to trigger a division by zero in arm_smmu_init_domain_context (if number of context irqs is 0): if (smmu->version == 1) { root_cfg->irptndx = atomic_inc_return(&smmu->irptndx); => root_cfg->irptndx %= smmu->num_context_irqs; } else { Avoid this by checking for num_context_irqs > 0 when probing for SMMU devices. Signed-off-by: Andreas Herrmann [will: changed to dev_err on probe failure path] Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 1f79daa5f4b1..e4693cee5f2c 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1798,12 +1798,11 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) smmu->num_context_irqs++; } - if (num_irqs < smmu->num_global_irqs) { - dev_warn(dev, "found %d interrupts but expected at least %d\n", - num_irqs, smmu->num_global_irqs); - smmu->num_global_irqs = num_irqs; + if (!smmu->num_context_irqs) { + dev_err(dev, "found %d interrupts but expected at least %d\n", + num_irqs, smmu->num_global_irqs + 1); + return -ENODEV; } - smmu->num_context_irqs = num_irqs - smmu->num_global_irqs; smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs, GFP_KERNEL); -- cgit v1.2.3 From 2ef0f03120ea2ad64d0e70f032a58e6c13603cdc Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 1 Oct 2013 13:39:08 +0100 Subject: iommu/arm-smmu: Print context fault information Print context fault information when the fault was not handled by report_iommu_fault. Signed-off-by: Andreas Herrmann [will: fixed string formatting] Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index e4693cee5f2c..a984e08eb084 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -590,6 +590,9 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) ret = IRQ_HANDLED; resume = RESUME_RETRY; } else { + dev_err_ratelimited(smmu->dev, + "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n", + iova, fsynr, root_cfg->cbndx); ret = IRQ_NONE; resume = RESUME_TERMINATE; } -- cgit v1.2.3 From 659db6f6beacae6fe49b5566debc4e82f678ff63 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 1 Oct 2013 13:39:09 +0100 Subject: iommu/arm-smmu: Clear global and context bank fault status registers After reset these registers have unknown values. This might cause problems when evaluating SMMU_GFSR and/or SMMU_CB_FSR in handlers for combined interrupts. Signed-off-by: Andreas Herrmann Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'drivers/iommu/arm-smmu.c') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index a984e08eb084..0f45a489ccf9 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1562,9 +1562,13 @@ static struct iommu_ops arm_smmu_ops = { static void arm_smmu_device_reset(struct arm_smmu_device *smmu) { void __iomem *gr0_base = ARM_SMMU_GR0(smmu); - void __iomem *sctlr_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB_SCTLR; + void __iomem *cb_base; int i = 0; - u32 scr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0); + u32 reg; + + /* Clear Global FSR */ + reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR); + writel(reg, gr0_base + ARM_SMMU_GR0_sGFSR); /* Mark all SMRn as invalid and all S2CRn as bypass */ for (i = 0; i < smmu->num_mapping_groups; ++i) { @@ -1572,33 +1576,38 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) writel_relaxed(S2CR_TYPE_BYPASS, gr0_base + ARM_SMMU_GR0_S2CR(i)); } - /* Make sure all context banks are disabled */ - for (i = 0; i < smmu->num_context_banks; ++i) - writel_relaxed(0, sctlr_base + ARM_SMMU_CB(smmu, i)); + /* Make sure all context banks are disabled and clear CB_FSR */ + for (i = 0; i < smmu->num_context_banks; ++i) { + cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i); + writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); + writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR); + } /* Invalidate the TLB, just in case */ writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL); writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH); writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); + reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0); + /* Enable fault reporting */ - scr0 |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE); + reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE); /* Disable TLB broadcasting. */ - scr0 |= (sCR0_VMIDPNE | sCR0_PTM); + reg |= (sCR0_VMIDPNE | sCR0_PTM); /* Enable client access, but bypass when no mapping is found */ - scr0 &= ~(sCR0_CLIENTPD | sCR0_USFCFG); + reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG); /* Disable forced broadcasting */ - scr0 &= ~sCR0_FB; + reg &= ~sCR0_FB; /* Don't upgrade barriers */ - scr0 &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT); + reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT); /* Push the button */ arm_smmu_tlb_sync(smmu); - writel_relaxed(scr0, gr0_base + ARM_SMMU_GR0_sCR0); + writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sCR0); } static int arm_smmu_id_size_to_bits(int size) -- cgit v1.2.3