From adeb25905c644350baf1f446bcd856517e58060e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 9 Apr 2014 10:20:39 +0800 Subject: iommu/vt-d: fix memory leakage caused by commit ea8ea46 Commit ea8ea46 "iommu/vt-d: Clean up and fix page table clear/free behaviour" introduces possible leakage of DMA page tables due to: for (pte = page_address(pg); !first_pte_in_page(pte); pte++) { if (dma_pte_present(pte) && !dma_pte_superpage(pte)) freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist); } For the first pte in a page, first_pte_in_page(pte) will always be true, thus dma_pte_list_pagetables() will never be called and leak DMA page tables if level is bigger than 1. Signed-off-by: Jiang Liu Signed-off-by: David Woodhouse --- drivers/iommu/intel-iommu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 69fa7da5e48b..13dc2318e17a 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1009,11 +1009,13 @@ static struct page *dma_pte_list_pagetables(struct dmar_domain *domain, if (level == 1) return freelist; - for (pte = page_address(pg); !first_pte_in_page(pte); pte++) { + pte = page_address(pg); + do { if (dma_pte_present(pte) && !dma_pte_superpage(pte)) freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist); - } + pte++; + } while (!first_pte_in_page(pte)); return freelist; } -- cgit v1.2.3 From 9f05d3fb644bf178c169d9c70dcfe360e3a006ae Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 14 Apr 2014 22:01:30 -0700 Subject: iommu/vt-d: Fix get_domain_for_dev() handling of upstream PCIe bridges Commit 146922ec79 ("iommu/vt-d: Make get_domain_for_dev() take struct device") introduced new variables bridge_bus and bridge_devfn to identify the upstream PCIe to PCI bridge responsible for the given target device. Leaving the original bus/devfn variables to identify the target device itself, now that it is no longer assumed to be PCI and we can no longer trivially find that information. However, the patch failed to correctly use the new variables in all cases; instead using the as-yet-uninitialised 'bus' and 'devfn' variables. Reported-by: Alex Williamson Signed-off-by: David Woodhouse --- drivers/iommu/intel-iommu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 13dc2318e17a..f256ffc02e29 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2237,7 +2237,9 @@ static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) bridge_devfn = dev_tmp->devfn; } spin_lock_irqsave(&device_domain_lock, flags); - info = dmar_search_domain_by_dev_info(segment, bus, devfn); + info = dmar_search_domain_by_dev_info(segment, + bridge_bus, + bridge_devfn); if (info) { iommu = info->iommu; domain = info->domain; -- cgit v1.2.3 From 5ae0566a0fffa09a77ac5996e3854fe91cd87167 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 15 Apr 2014 10:35:35 +0800 Subject: iommu/vt-d: fix bug in matching PCI devices with DRHD/RMRR descriptors Commit "59ce0515cdaf iommu/vt-d: Update DRHD/RMRR/ATSR device scope caches when PCI hotplug happens" introduces a bug, which fails to match PCI devices with DMAR device scope entries if PCI path array in the entry has more than one level. For example, it fails to handle [1D2h 0466 1] Device Scope Entry Type : 01 [1D3h 0467 1] Entry Length : 0A [1D4h 0468 2] Reserved : 0000 [1D6h 0470 1] Enumeration ID : 00 [1D7h 0471 1] PCI Bus Number : 00 [1D8h 0472 2] PCI Path : 1C,04 [1DAh 0474 2] PCI Path : 00,02 And cause DMA failure on HP DL980 as: DMAR:[fault reason 02] Present bit in context entry is clear dmar: DRHD: handling fault status reg 602 dmar: DMAR:[DMA Read] Request device [02:00.2] fault addr 7f61e000 Reported-and-tested-by: Davidlohr Bueso Signed-off-by: Jiang Liu Signed-off-by: David Woodhouse --- drivers/iommu/dmar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index f445c10df8df..39f8b717fe84 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -152,7 +152,8 @@ dmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event) info->seg = pci_domain_nr(dev->bus); info->level = level; if (event == BUS_NOTIFY_ADD_DEVICE) { - for (tmp = dev, level--; tmp; tmp = tmp->bus->self) { + for (tmp = dev; tmp; tmp = tmp->bus->self) { + level--; info->path[level].device = PCI_SLOT(tmp->devfn); info->path[level].function = PCI_FUNC(tmp->devfn); if (pci_is_root_bus(tmp->bus)) -- cgit v1.2.3 From 16c50dcfc4c186ed09a4d80fbd511492d024a1c5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 28 Feb 2014 15:37:10 +0000 Subject: iommu/arm-smmu: Return 0 on unmap failure The IOMMU core expects the unmap operation to return the number of bytes that have been unmapped or 0 on failure, a negative return value being treated like a number of bytes. Signed-off-by: Laurent Pinchart Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 8b89e33a89fe..69d001a71b22 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1499,7 +1499,7 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0); arm_smmu_tlb_inv_context(&smmu_domain->root_cfg); - return ret ? ret : size; + return ret ? 0 : size; } static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, -- cgit v1.2.3 From aca1bc4595c5757f01167ab5bfef2a4f8edfcf4f Mon Sep 17 00:00:00 2001 From: Bin Wang Date: Fri, 21 Mar 2014 10:06:07 +0000 Subject: iommu/arm-smmu: fix panic in arm_smmu_alloc_init_pte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kernel panic happened when iommu_unmap a buffer larger than 2MB, more than expected pmd entries got “invalidated”, due to a wrong range passed to arm_smmu_alloc_init_pte. it was likely a typo, now we fix it, passing the correct "end" address to arm_smmu_alloc_init_pte. Signed-off-by: Bin Wang Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 69d001a71b22..647c3c7fd742 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1381,7 +1381,7 @@ static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud, do { next = pmd_addr_end(addr, end); - ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn, + ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, next, pfn, prot, stage); phys += next - addr; } while (pmd++, addr = next, addr < end); -- cgit v1.2.3 From ac1ef1fed3d2c1cd4902ecb92876cb239c97a870 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 21 Feb 2014 16:54:53 +0100 Subject: iommu/shmobile: Don't ignore the ipmmu_iommu_init() return value The function can fail, don't ignore its error value. Signed-off-by: Laurent Pinchart --- drivers/iommu/shmobile-ipmmu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c index e3bc2e19b6dd..6ba611002c6f 100644 --- a/drivers/iommu/shmobile-ipmmu.c +++ b/drivers/iommu/shmobile-ipmmu.c @@ -117,8 +117,7 @@ static int ipmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ipmmu); ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */ ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */ - ipmmu_iommu_init(ipmmu); - return 0; + return ipmmu_iommu_init(ipmmu); } static struct platform_driver ipmmu_driver = { -- cgit v1.2.3 From b11762f8431b3a5cf1c156876e79efab7d7f80c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 21 Feb 2014 16:54:53 +0100 Subject: iommu/shmobile: Use devm_ioremap_resource() Replace the devm_ioremap_nocache() call with devm_ioremap_resource(). This simplifies error checking. Signed-off-by: Laurent Pinchart --- drivers/iommu/shmobile-ipmmu.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c index 6ba611002c6f..bd97adecb1fd 100644 --- a/drivers/iommu/shmobile-ipmmu.c +++ b/drivers/iommu/shmobile-ipmmu.c @@ -94,11 +94,6 @@ static int ipmmu_probe(struct platform_device *pdev) struct resource *res; struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "cannot get platform resources\n"); - return -ENOENT; - } ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL); if (!ipmmu) { dev_err(&pdev->dev, "cannot allocate device data\n"); @@ -106,12 +101,12 @@ static int ipmmu_probe(struct platform_device *pdev) } spin_lock_init(&ipmmu->flush_lock); ipmmu->dev = &pdev->dev; - ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!ipmmu->ipmmu_base) { - dev_err(&pdev->dev, "ioremap_nocache failed\n"); - return -ENOMEM; - } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ipmmu->ipmmu_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ipmmu->ipmmu_base)) + return PTR_ERR(ipmmu->ipmmu_base); + ipmmu->dev_names = pdata->dev_names; ipmmu->num_dev_names = pdata->num_dev_names; platform_set_drvdata(pdev, ipmmu); -- cgit v1.2.3 From c44308413b92a555d1501a6c0db9489e288bb81b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 13 Mar 2014 11:46:57 +0000 Subject: iommu/arm-smmu: allow 42-bit stage-1 output size with 64K pages The output size of stage-1 is currently limited by the input size of stage-2, which is further limited by VA_BITS since we make use of the standard pgd_alloc functions for creating page tables. This patch ensures that we use VA_BITS instead of a hardcoded '39' for the stage-1 output size limit. Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 647c3c7fd742..afcb0e3492cf 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1804,7 +1804,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) * allocation (PTRS_PER_PGD). */ #ifdef CONFIG_64BIT - smmu->s1_output_size = min(39UL, size); + smmu->s1_output_size = min((unsigned long)VA_BITS, size); #else smmu->s1_output_size = min(32UL, size); #endif -- cgit v1.2.3 From 6069d23ce0e568909d847f28af0257b085e8e77a Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Fri, 18 Apr 2014 10:20:48 +0800 Subject: iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT There is already S2CR_TYPE_SHIFT in S2CR_TYPE_TRANS macro, so drop the second shift. Note that, since S2CR_TYPE_SHIFT is 0x0, there is no functional change introduced by this patch. Signed-off-by: Kefeng Wang Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index afcb0e3492cf..1599354e974d 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, for (i = 0; i < master->num_streamids; ++i) { u32 idx, s2cr; idx = master->smrs ? master->smrs[i].idx : master->streamids[i]; - s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) | + s2cr = S2CR_TYPE_TRANS | (smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT); writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); } -- cgit v1.2.3 From e028a9e6b8a637af09ac4114083280df4a7045f1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 22 Apr 2014 10:08:40 -0600 Subject: iommu/amd: Fix interrupt remapping for aliased devices An apparent cut and paste error prevents the correct flags from being set on the alias device resulting in MSI on conventional PCI devices failing to work. This also produces error events from the IOMMU like: AMD-Vi: Event logged [INVALID_DEVICE_REQUEST device=00:14.4 address=0x000000fdf8000000 flags=0x0a00] Where 14.4 is a PCIe-to-PCI bridge with a device behind it trying to use MSI interrupts. Signed-off-by: Alex Williamson Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c949520bd196..57068e8035b5 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3999,7 +3999,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) iommu_flush_dte(iommu, devid); if (devid != alias) { irq_lookup_table[alias] = table; - set_dte_irq_entry(devid, table); + set_dte_irq_entry(alias, table); iommu_flush_dte(iommu, alias); } -- cgit v1.2.3 From 4378d99295fae5fd42b0ed1a3eeef2a1684e0c17 Mon Sep 17 00:00:00 2001 From: Jay Cornwall Date: Mon, 28 Apr 2014 17:27:46 -0500 Subject: iommu/amd: Take mmap_sem when calling get_user_pages get_user_pages requires caller to hold a read lock on mmap_sem. Signed-off-by: Jay Cornwall Signed-off-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_v2.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 5208828792e6..203b2e6a91cf 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -504,8 +504,10 @@ static void do_fault(struct work_struct *work) write = !!(fault->flags & PPR_FAULT_WRITE); + down_read(&fault->state->mm->mmap_sem); npages = get_user_pages(fault->state->task, fault->state->mm, fault->address, 1, write, 0, &page, NULL); + up_read(&fault->state->mm->mmap_sem); if (npages == 1) { put_page(page); -- cgit v1.2.3 From 2c16c9fdacbbf3bd8ff6390e12c5d28b3f2c78ac Mon Sep 17 00:00:00 2001 From: Su Friendy Date: Wed, 7 May 2014 13:54:52 +0800 Subject: iommu/amd: fix enabling exclusion range for an exact device set_device_exclusion_range(u16 devid, struct ivmd_header *m) enables exclusion range for ONE device. IOMMU does not translate the access to the exclusion range from the device. The device is specified by input argument 'devid'. But 'devid' is not passed to the actual set function set_dev_entry_bit(), instead 'm->devid' is passed. 'm->devid' does not specify the exact device which needs enable the exclusion range. 'm->devid' represents DeviceID field of IVMD, which has different meaning depends on IVMD type. The caller init_exclusion_range() sets 'devid' for ONE device. When m->type is equal to ACPI_IVMD_TYPE_ALL or ACPI_IVMD_TYPE_RANGE, 'm->devid' is not equal to 'devid'. This patch fixes 'm->devid' to 'devid'. Signed-off-by: Su Friendy Signed-off-by: Tamori Masahiro Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index b76c58dbe30c..0e08545d7298 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -788,7 +788,7 @@ static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m) * per device. But we can enable the exclusion range per * device. This is done here */ - set_dev_entry_bit(m->devid, DEV_ENTRY_EX); + set_dev_entry_bit(devid, DEV_ENTRY_EX); iommu->exclusion_start = m->range_start; iommu->exclusion_length = m->range_length; } -- cgit v1.2.3 From 7222e8db2d506197ee183de0f9b76b3ad97e8c18 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:46 +0530 Subject: iommu/exynos: Fix build errors Commit 25e9d28d92 (ARM: EXYNOS: remove system mmu initialization from exynos tree) removed arch/arm/mach-exynos/mach/sysmmu.h header without removing remaining use of it from exynos-iommu driver, thus causing a compilation error. This patch fixes the error by removing respective include line from exynos-iommu.c. Use of __pa and __va macro is changed to virt_to_phys and phys_to_virt which are recommended in driver code. printk formatting of physical address is also fixed to %pa. Also System MMU driver is changed to control only a single instance of System MMU at a time. Since a single instance of System MMU has only a single clock descriptor for its clock gating, single address range for control registers, there is no need to obtain two or more clock descriptors and ioremaped region. CC: Tomasz Figa Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 255 +++++++++++++++---------------------------- 1 file changed, 85 insertions(+), 170 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 074018979cdf..8d7c3f9632f8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -29,8 +29,6 @@ #include #include -#include - /* We does not consider super section mapping (16MB) */ #define SECT_ORDER 20 #define LPAGE_ORDER 16 @@ -108,7 +106,8 @@ static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) static unsigned long *page_entry(unsigned long *sent, unsigned long iova) { - return (unsigned long *)__va(lv2table_base(sent)) + lv2ent_offset(iova); + return (unsigned long *)phys_to_virt( + lv2table_base(sent)) + lv2ent_offset(iova); } enum exynos_sysmmu_inttype { @@ -132,7 +131,7 @@ enum exynos_sysmmu_inttype { * translated. This is 0 if @itype is SYSMMU_BUSERROR. */ typedef int (*sysmmu_fault_handler_t)(enum exynos_sysmmu_inttype itype, - unsigned long pgtable_base, unsigned long fault_addr); + phys_addr_t pgtable_base, unsigned long fault_addr); static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { REG_PAGE_FAULT_ADDR, @@ -170,14 +169,13 @@ struct sysmmu_drvdata { struct device *sysmmu; /* System MMU's device descriptor */ struct device *dev; /* Owner of system MMU */ char *dbgname; - int nsfrs; - void __iomem **sfrbases; - struct clk *clk[2]; + void __iomem *sfrbase; + struct clk *clk; int activations; rwlock_t lock; struct iommu_domain *domain; sysmmu_fault_handler_t fault_handler; - unsigned long pgtable; + phys_addr_t pgtable; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -266,17 +264,17 @@ void exynos_sysmmu_set_fault_handler(struct device *dev, } static int default_fault_handler(enum exynos_sysmmu_inttype itype, - unsigned long pgtable_base, unsigned long fault_addr) + phys_addr_t pgtable_base, unsigned long fault_addr) { unsigned long *ent; if ((itype >= SYSMMU_FAULTS_NUM) || (itype < SYSMMU_PAGEFAULT)) itype = SYSMMU_FAULT_UNKNOWN; - pr_err("%s occurred at 0x%lx(Page table base: 0x%lx)\n", - sysmmu_fault_name[itype], fault_addr, pgtable_base); + pr_err("%s occurred at 0x%lx(Page table base: %pa)\n", + sysmmu_fault_name[itype], fault_addr, &pgtable_base); - ent = section_entry(__va(pgtable_base), fault_addr); + ent = section_entry(phys_to_virt(pgtable_base), fault_addr); pr_err("\tLv1 entry: 0x%lx\n", *ent); if (lv1ent_page(ent)) { @@ -295,56 +293,39 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) { /* SYSMMU is in blocked when interrupt occurred. */ struct sysmmu_drvdata *data = dev_id; - struct resource *irqres; - struct platform_device *pdev; enum exynos_sysmmu_inttype itype; unsigned long addr = -1; - - int i, ret = -ENOSYS; + int ret = -ENOSYS; read_lock(&data->lock); WARN_ON(!is_sysmmu_active(data)); - pdev = to_platform_device(data->sysmmu); - for (i = 0; i < (pdev->num_resources / 2); i++) { - irqres = platform_get_resource(pdev, IORESOURCE_IRQ, i); - if (irqres && ((int)irqres->start == irq)) - break; - } - - if (i == pdev->num_resources) { + itype = (enum exynos_sysmmu_inttype) + __ffs(__raw_readl(data->sfrbase + REG_INT_STATUS)); + if (WARN_ON(!((itype >= 0) && (itype < SYSMMU_FAULT_UNKNOWN)))) itype = SYSMMU_FAULT_UNKNOWN; - } else { - itype = (enum exynos_sysmmu_inttype) - __ffs(__raw_readl(data->sfrbases[i] + REG_INT_STATUS)); - if (WARN_ON(!((itype >= 0) && (itype < SYSMMU_FAULT_UNKNOWN)))) - itype = SYSMMU_FAULT_UNKNOWN; - else - addr = __raw_readl( - data->sfrbases[i] + fault_reg_offset[itype]); - } + else + addr = __raw_readl(data->sfrbase + fault_reg_offset[itype]); if (data->domain) - ret = report_iommu_fault(data->domain, data->dev, - addr, itype); + ret = report_iommu_fault(data->domain, data->dev, addr, itype); if ((ret == -ENOSYS) && data->fault_handler) { unsigned long base = data->pgtable; if (itype != SYSMMU_FAULT_UNKNOWN) - base = __raw_readl( - data->sfrbases[i] + REG_PT_BASE_ADDR); + base = __raw_readl(data->sfrbase + REG_PT_BASE_ADDR); ret = data->fault_handler(itype, base, addr); } if (!ret && (itype != SYSMMU_FAULT_UNKNOWN)) - __raw_writel(1 << itype, data->sfrbases[i] + REG_INT_CLEAR); + __raw_writel(1 << itype, data->sfrbase + REG_INT_CLEAR); else dev_dbg(data->sysmmu, "(%s) %s is not handled.\n", data->dbgname, sysmmu_fault_name[itype]); if (itype != SYSMMU_FAULT_UNKNOWN) - sysmmu_unblock(data->sfrbases[i]); + sysmmu_unblock(data->sfrbase); read_unlock(&data->lock); @@ -355,20 +336,16 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) { unsigned long flags; bool disabled = false; - int i; write_lock_irqsave(&data->lock, flags); if (!set_sysmmu_inactive(data)) goto finish; - for (i = 0; i < data->nsfrs; i++) - __raw_writel(CTRL_DISABLE, data->sfrbases[i] + REG_MMU_CTRL); + __raw_writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); - if (data->clk[1]) - clk_disable(data->clk[1]); - if (data->clk[0]) - clk_disable(data->clk[0]); + if (!IS_ERR(data->clk)) + clk_disable(data->clk); disabled = true; data->pgtable = 0; @@ -394,7 +371,7 @@ finish: static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, unsigned long pgtable, struct iommu_domain *domain) { - int i, ret = 0; + int ret = 0; unsigned long flags; write_lock_irqsave(&data->lock, flags); @@ -411,27 +388,22 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - if (data->clk[0]) - clk_enable(data->clk[0]); - if (data->clk[1]) - clk_enable(data->clk[1]); + if (!IS_ERR(data->clk)) + clk_enable(data->clk); data->pgtable = pgtable; - for (i = 0; i < data->nsfrs; i++) { - __sysmmu_set_ptbase(data->sfrbases[i], pgtable); - - if ((readl(data->sfrbases[i] + REG_MMU_VERSION) >> 28) == 3) { - /* System MMU version is 3.x */ - __raw_writel((1 << 12) | (2 << 28), - data->sfrbases[i] + REG_MMU_CFG); - __sysmmu_set_prefbuf(data->sfrbases[i], 0, -1, 0); - __sysmmu_set_prefbuf(data->sfrbases[i], 0, -1, 1); - } - - __raw_writel(CTRL_ENABLE, data->sfrbases[i] + REG_MMU_CTRL); + __sysmmu_set_ptbase(data->sfrbase, pgtable); + if ((readl(data->sfrbase + REG_MMU_VERSION) >> 28) == 3) { + /* System MMU version is 3.x */ + __raw_writel((1 << 12) | (2 << 28), + data->sfrbase + REG_MMU_CFG); + __sysmmu_set_prefbuf(data->sfrbase, 0, -1, 0); + __sysmmu_set_prefbuf(data->sfrbase, 0, -1, 1); } + __raw_writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); + data->domain = domain; dev_dbg(data->sysmmu, "(%s) Enabled\n", data->dbgname); @@ -458,7 +430,7 @@ int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) if (WARN_ON(ret < 0)) { pm_runtime_put(data->sysmmu); dev_err(data->sysmmu, - "(%s) Already enabled with page table %#lx\n", + "(%s) Already enabled with page table %#x\n", data->dbgname, data->pgtable); } else { data->dev = dev; @@ -486,13 +458,10 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) read_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { - int i; - for (i = 0; i < data->nsfrs; i++) { - if (sysmmu_block(data->sfrbases[i])) { - __sysmmu_tlb_invalidate_entry( - data->sfrbases[i], iova); - sysmmu_unblock(data->sfrbases[i]); - } + if (sysmmu_block(data->sfrbase)) { + __sysmmu_tlb_invalidate_entry( + data->sfrbase, iova); + sysmmu_unblock(data->sfrbase); } } else { dev_dbg(data->sysmmu, @@ -511,12 +480,9 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { - int i; - for (i = 0; i < data->nsfrs; i++) { - if (sysmmu_block(data->sfrbases[i])) { - __sysmmu_tlb_invalidate(data->sfrbases[i]); - sysmmu_unblock(data->sfrbases[i]); - } + if (sysmmu_block(data->sfrbase)) { + __sysmmu_tlb_invalidate(data->sfrbase); + sysmmu_unblock(data->sfrbase); } } else { dev_dbg(data->sysmmu, @@ -529,11 +495,10 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) static int exynos_sysmmu_probe(struct platform_device *pdev) { - int i, ret; - struct device *dev; + int ret; + struct device *dev = &pdev->dev; struct sysmmu_drvdata *data; - - dev = &pdev->dev; + struct resource *res; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { @@ -542,82 +507,37 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) goto err_alloc; } - ret = dev_set_drvdata(dev, data); - if (ret) { - dev_dbg(dev, "Unabled to initialize driver data\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(dev, "Unable to find IOMEM region\n"); + ret = -ENOENT; goto err_init; } - data->nsfrs = pdev->num_resources / 2; - data->sfrbases = kmalloc(sizeof(*data->sfrbases) * data->nsfrs, - GFP_KERNEL); - if (data->sfrbases == NULL) { - dev_dbg(dev, "Not enough memory\n"); - ret = -ENOMEM; - goto err_init; + data->sfrbase = ioremap(res->start, resource_size(res)); + if (!data->sfrbase) { + dev_dbg(dev, "Unable to map IOMEM @ PA:%#x\n", res->start); + ret = -ENOENT; + goto err_res; } - for (i = 0; i < data->nsfrs; i++) { - struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) { - dev_dbg(dev, "Unable to find IOMEM region\n"); - ret = -ENOENT; - goto err_res; - } - - data->sfrbases[i] = ioremap(res->start, resource_size(res)); - if (!data->sfrbases[i]) { - dev_dbg(dev, "Unable to map IOMEM @ PA:%#x\n", - res->start); - ret = -ENOENT; - goto err_res; - } + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_dbg(dev, "Unable to find IRQ resource\n"); + goto err_irq; } - for (i = 0; i < data->nsfrs; i++) { - ret = platform_get_irq(pdev, i); - if (ret <= 0) { - dev_dbg(dev, "Unable to find IRQ resource\n"); - goto err_irq; - } - - ret = request_irq(ret, exynos_sysmmu_irq, 0, - dev_name(dev), data); - if (ret) { - dev_dbg(dev, "Unabled to register interrupt handler\n"); - goto err_irq; - } + ret = request_irq(ret, exynos_sysmmu_irq, 0, + dev_name(dev), data); + if (ret) { + dev_dbg(dev, "Unabled to register interrupt handler\n"); + goto err_irq; } if (dev_get_platdata(dev)) { - char *deli, *beg; - struct sysmmu_platform_data *platdata = dev_get_platdata(dev); - - beg = platdata->clockname; - - for (deli = beg; (*deli != '\0') && (*deli != ','); deli++) - /* NOTHING */; - - if (*deli == '\0') - deli = NULL; - else - *deli = '\0'; - - data->clk[0] = clk_get(dev, beg); - if (IS_ERR(data->clk[0])) { - data->clk[0] = NULL; + data->clk = clk_get(dev, "sysmmu"); + if (IS_ERR(data->clk)) dev_dbg(dev, "No clock descriptor registered\n"); - } - - if (data->clk[0] && deli) { - *deli = ','; - data->clk[1] = clk_get(dev, deli + 1); - if (IS_ERR(data->clk[1])) - data->clk[1] = NULL; - } - - data->dbgname = platdata->dbgname; } data->sysmmu = dev; @@ -626,22 +546,17 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) __set_fault_handler(data, &default_fault_handler); + platform_set_drvdata(pdev, data); + if (dev->parent) pm_runtime_enable(dev); dev_dbg(dev, "(%s) Initialized\n", data->dbgname); return 0; err_irq: - while (i-- > 0) { - int irq; - - irq = platform_get_irq(pdev, i); - free_irq(irq, data); - } + free_irq(platform_get_irq(pdev, 0), data); err_res: - while (data->nsfrs-- > 0) - iounmap(data->sfrbases[data->nsfrs]); - kfree(data->sfrbases); + iounmap(data->sfrbase); err_init: kfree(data); err_alloc: @@ -722,7 +637,7 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) for (i = 0; i < NUM_LV1ENTRIES; i++) if (lv1ent_page(priv->pgtable + i)) - kfree(__va(lv2table_base(priv->pgtable + i))); + kfree(phys_to_virt(lv2table_base(priv->pgtable + i))); free_pages((unsigned long)priv->pgtable, 2); free_pages((unsigned long)priv->lv2entcnt, 1); @@ -735,6 +650,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, { struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); struct exynos_iommu_domain *priv = domain->priv; + phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; int ret; @@ -746,7 +662,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, spin_lock_irqsave(&priv->lock, flags); - ret = __exynos_sysmmu_enable(data, __pa(priv->pgtable), domain); + ret = __exynos_sysmmu_enable(data, pagetable, domain); if (ret == 0) { /* 'data->node' must not be appeared in priv->clients */ @@ -758,17 +674,15 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, spin_unlock_irqrestore(&priv->lock, flags); if (ret < 0) { - dev_err(dev, "%s: Failed to attach IOMMU with pgtable %#lx\n", - __func__, __pa(priv->pgtable)); + dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n", + __func__, &pagetable); pm_runtime_put(data->sysmmu); - } else if (ret > 0) { - dev_dbg(dev, "%s: IOMMU with pgtable 0x%lx already attached\n", - __func__, __pa(priv->pgtable)); - } else { - dev_dbg(dev, "%s: Attached new IOMMU with pgtable 0x%lx\n", - __func__, __pa(priv->pgtable)); + return ret; } + dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n", + __func__, &pagetable, (ret == 0) ? "" : ", again"); + return ret; } @@ -778,6 +692,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); struct exynos_iommu_domain *priv = domain->priv; struct list_head *pos; + phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; bool found = false; @@ -794,13 +709,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, goto finish; if (__exynos_sysmmu_disable(data)) { - dev_dbg(dev, "%s: Detached IOMMU with pgtable %#lx\n", - __func__, __pa(priv->pgtable)); + dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", + __func__, &pagetable); list_del_init(&data->node); } else { - dev_dbg(dev, "%s: Detaching IOMMU with pgtable %#lx delayed", - __func__, __pa(priv->pgtable)); + dev_dbg(dev, "%s: Detaching IOMMU with pgtable %pa delayed", + __func__, &pagetable); } finish: @@ -821,7 +736,7 @@ static unsigned long *alloc_lv2entry(unsigned long *sent, unsigned long iova, if (!pent) return NULL; - *sent = mk_lv1ent_page(__pa(pent)); + *sent = mk_lv1ent_page(virt_to_phys(pent)); *pgcounter = NUM_LV2ENTRIES; pgtable_flush(pent, pent + NUM_LV2ENTRIES); pgtable_flush(sent, sent + 1); -- cgit v1.2.3 From 61128f08fc110fc27cd50f5a8ca57c6a8c3bcddc Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:47 +0530 Subject: iommu/exynos: Change error handling when page table update is failed This patch changes not to panic on any error when updating page table. Instead prints error messages with callstack. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 58 +++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 8d7c3f9632f8..aec7fd75f400 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -728,13 +728,18 @@ finish: static unsigned long *alloc_lv2entry(unsigned long *sent, unsigned long iova, short *pgcounter) { + if (lv1ent_section(sent)) { + WARN(1, "Trying mapping on %#08lx mapped with 1MiB page", iova); + return ERR_PTR(-EADDRINUSE); + } + if (lv1ent_fault(sent)) { unsigned long *pent; pent = kzalloc(LV2TABLE_SIZE, GFP_ATOMIC); BUG_ON((unsigned long)pent & (LV2TABLE_SIZE - 1)); if (!pent) - return NULL; + return ERR_PTR(-ENOMEM); *sent = mk_lv1ent_page(virt_to_phys(pent)); *pgcounter = NUM_LV2ENTRIES; @@ -745,14 +750,21 @@ static unsigned long *alloc_lv2entry(unsigned long *sent, unsigned long iova, return page_entry(sent, iova); } -static int lv1set_section(unsigned long *sent, phys_addr_t paddr, short *pgcnt) +static int lv1set_section(unsigned long *sent, unsigned long iova, + phys_addr_t paddr, short *pgcnt) { - if (lv1ent_section(sent)) + if (lv1ent_section(sent)) { + WARN(1, "Trying mapping on 1MiB@%#08lx that is mapped", + iova); return -EADDRINUSE; + } if (lv1ent_page(sent)) { - if (*pgcnt != NUM_LV2ENTRIES) + if (*pgcnt != NUM_LV2ENTRIES) { + WARN(1, "Trying mapping on 1MiB@%#08lx that is mapped", + iova); return -EADDRINUSE; + } kfree(page_entry(sent, 0)); @@ -770,8 +782,10 @@ static int lv2set_page(unsigned long *pent, phys_addr_t paddr, size_t size, short *pgcnt) { if (size == SPAGE_SIZE) { - if (!lv2ent_fault(pent)) + if (!lv2ent_fault(pent)) { + WARN(1, "Trying mapping on 4KiB where mapping exists"); return -EADDRINUSE; + } *pent = mk_lv2ent_spage(paddr); pgtable_flush(pent, pent + 1); @@ -780,7 +794,10 @@ static int lv2set_page(unsigned long *pent, phys_addr_t paddr, size_t size, int i; for (i = 0; i < SPAGES_PER_LPAGE; i++, pent++) { if (!lv2ent_fault(pent)) { - memset(pent, 0, sizeof(*pent) * i); + WARN(1, + "Trying mapping on 64KiB where mapping exists"); + if (i > 0) + memset(pent - i, 0, sizeof(*pent) * i); return -EADDRINUSE; } @@ -808,7 +825,7 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long iova, entry = section_entry(priv->pgtable, iova); if (size == SECT_SIZE) { - ret = lv1set_section(entry, paddr, + ret = lv1set_section(entry, iova, paddr, &priv->lv2entcnt[lv1ent_offset(iova)]); } else { unsigned long *pent; @@ -816,17 +833,16 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long iova, pent = alloc_lv2entry(entry, iova, &priv->lv2entcnt[lv1ent_offset(iova)]); - if (!pent) - ret = -ENOMEM; + if (IS_ERR(pent)) + ret = PTR_ERR(pent); else ret = lv2set_page(pent, paddr, size, &priv->lv2entcnt[lv1ent_offset(iova)]); } - if (ret) { + if (ret) pr_debug("%s: Failed to map iova 0x%lx/0x%x bytes\n", __func__, iova, size); - } spin_unlock_irqrestore(&priv->pgtablelock, flags); @@ -840,6 +856,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, struct sysmmu_drvdata *data; unsigned long flags; unsigned long *ent; + size_t err_pgsize; BUG_ON(priv->pgtable == NULL); @@ -848,7 +865,10 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, ent = section_entry(priv->pgtable, iova); if (lv1ent_section(ent)) { - BUG_ON(size < SECT_SIZE); + if (size < SECT_SIZE) { + err_pgsize = SECT_SIZE; + goto err; + } *ent = 0; pgtable_flush(ent, ent + 1); @@ -879,7 +899,10 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, } /* lv1ent_large(ent) == true here */ - BUG_ON(size < LPAGE_SIZE); + if (size < LPAGE_SIZE) { + err_pgsize = LPAGE_SIZE; + goto err; + } memset(ent, 0, sizeof(*ent) * SPAGES_PER_LPAGE); @@ -893,8 +916,15 @@ done: sysmmu_tlb_invalidate_entry(data->dev, iova); spin_unlock_irqrestore(&priv->lock, flags); - return size; +err: + spin_unlock_irqrestore(&priv->pgtablelock, flags); + + WARN(1, + "%s: Failed due to size(%#x) @ %#08lx is smaller than page size %#x\n", + __func__, size, iova, err_pgsize); + + return 0; } static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, -- cgit v1.2.3 From 734c3c732ca91adbe705f2a289509627810231a3 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:48 +0530 Subject: iommu/exynos: Allocate lv2 page table from own slab Since kmalloc() does not guarantee that the allignment of 1KiB when it allocates 1KiB, it is required to allocate lv2 page table from own slab that guarantees alignment of 1KiB Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index aec7fd75f400..4ff4b0b81575 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -99,6 +99,8 @@ #define REG_PB1_SADDR 0x054 #define REG_PB1_EADDR 0x058 +static struct kmem_cache *lv2table_kmem_cache; + static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) { return pgtable + lv1ent_offset(iova); @@ -637,7 +639,8 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) for (i = 0; i < NUM_LV1ENTRIES; i++) if (lv1ent_page(priv->pgtable + i)) - kfree(phys_to_virt(lv2table_base(priv->pgtable + i))); + kmem_cache_free(lv2table_kmem_cache, + phys_to_virt(lv2table_base(priv->pgtable + i))); free_pages((unsigned long)priv->pgtable, 2); free_pages((unsigned long)priv->lv2entcnt, 1); @@ -736,7 +739,7 @@ static unsigned long *alloc_lv2entry(unsigned long *sent, unsigned long iova, if (lv1ent_fault(sent)) { unsigned long *pent; - pent = kzalloc(LV2TABLE_SIZE, GFP_ATOMIC); + pent = kmem_cache_zalloc(lv2table_kmem_cache, GFP_ATOMIC); BUG_ON((unsigned long)pent & (LV2TABLE_SIZE - 1)); if (!pent) return ERR_PTR(-ENOMEM); @@ -766,8 +769,7 @@ static int lv1set_section(unsigned long *sent, unsigned long iova, return -EADDRINUSE; } - kfree(page_entry(sent, 0)); - + kmem_cache_free(lv2table_kmem_cache, page_entry(sent, 0)); *pgcnt = 0; } @@ -970,11 +972,31 @@ static int __init exynos_iommu_init(void) { int ret; + lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table", + LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL); + if (!lv2table_kmem_cache) { + pr_err("%s: Failed to create kmem cache\n", __func__); + return -ENOMEM; + } + ret = platform_driver_register(&exynos_sysmmu_driver); + if (ret) { + pr_err("%s: Failed to register driver\n", __func__); + goto err_reg_driver; + } - if (ret == 0) - bus_set_iommu(&platform_bus_type, &exynos_iommu_ops); + ret = bus_set_iommu(&platform_bus_type, &exynos_iommu_ops); + if (ret) { + pr_err("%s: Failed to register exynos-iommu driver.\n", + __func__); + goto err_set_iommu; + } + return 0; +err_set_iommu: + platform_driver_unregister(&exynos_sysmmu_driver); +err_reg_driver: + kmem_cache_destroy(lv2table_kmem_cache); return ret; } subsys_initcall(exynos_iommu_init); -- cgit v1.2.3 From 3ad6b7f3a41ba6bf4503be442802072ca9dc8336 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:49 +0530 Subject: iommu/exynos: Fix L2TLB invalidation L2TLB is 8-way set-associative TLB with 512 entries. The number of sets is 64. A single 4KB(small page) translation information is cached only to a set whose index is the same with the lower 6 bits of the page frame number. A single 64KB(large page) translation information can be cached to any 16 sets whose top two bits of their indices are the same with the bit [5:4] of the page frame number. A single 1MB(section) or larger translation information can be cached to any set in the TLB. It is required to invalidate entire sets that may cache the target translation information to guarantee that the L2TLB has no stale data. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4ff4b0b81575..06fc70ee4389 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -226,9 +226,14 @@ static void __sysmmu_tlb_invalidate(void __iomem *sfrbase) } static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, - unsigned long iova) + unsigned long iova, unsigned int num_inv) { - __raw_writel((iova & SPAGE_MASK) | 1, sfrbase + REG_MMU_FLUSH_ENTRY); + unsigned int i; + for (i = 0; i < num_inv; i++) { + __raw_writel((iova & SPAGE_MASK) | 1, + sfrbase + REG_MMU_FLUSH_ENTRY); + iova += SPAGE_SIZE; + } } static void __sysmmu_set_ptbase(void __iomem *sfrbase, @@ -452,7 +457,8 @@ static bool exynos_sysmmu_disable(struct device *dev) return disabled; } -static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) +static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, + size_t size) { unsigned long flags; struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); @@ -460,9 +466,25 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) read_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { + unsigned int maj; + unsigned int num_inv = 1; + maj = __raw_readl(data->sfrbase + REG_MMU_VERSION); + /* + * L2TLB invalidation required + * 4KB page: 1 invalidation + * 64KB page: 16 invalidation + * 1MB page: 64 invalidation + * because it is set-associative TLB + * with 8-way and 64 sets. + * 1MB page can be cached in one of all sets. + * 64KB page can be one of 16 consecutive sets. + */ + if ((maj >> 28) == 2) /* major version number */ + num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); + if (sysmmu_block(data->sfrbase)) { __sysmmu_tlb_invalidate_entry( - data->sfrbase, iova); + data->sfrbase, iova, num_inv); sysmmu_unblock(data->sfrbase); } } else { @@ -915,7 +937,7 @@ done: spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(data, &priv->clients, node) - sysmmu_tlb_invalidate_entry(data->dev, iova); + sysmmu_tlb_invalidate_entry(data->dev, iova, size); spin_unlock_irqrestore(&priv->lock, flags); return size; -- cgit v1.2.3 From 8f8fcf970e5866150af98233ae7ebf25ffe8cba7 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:50 +0530 Subject: iommu/exynos: Remove prefetch buffer setting Prefetch buffer is a cache of System MMU 3.x and caches a block of page table entries to make effect of larger page with small pages. However, how to control prefetch buffers and the specifications of prefetch buffers different from minor versions of System MMU v3. Prefetch buffers must be controled with care because there are some restrictions in H/W design. The interface and implementation to initiate prefetch buffers will be prepared later. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 06fc70ee4389..4fc31fc6a49a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -245,13 +245,6 @@ static void __sysmmu_set_ptbase(void __iomem *sfrbase, __sysmmu_tlb_invalidate(sfrbase); } -static void __sysmmu_set_prefbuf(void __iomem *sfrbase, unsigned long base, - unsigned long size, int idx) -{ - __raw_writel(base, sfrbase + REG_PB0_SADDR + idx * 8); - __raw_writel(size - 1 + base, sfrbase + REG_PB0_EADDR + idx * 8); -} - static void __set_fault_handler(struct sysmmu_drvdata *data, sysmmu_fault_handler_t handler) { @@ -401,13 +394,6 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, data->pgtable = pgtable; __sysmmu_set_ptbase(data->sfrbase, pgtable); - if ((readl(data->sfrbase + REG_MMU_VERSION) >> 28) == 3) { - /* System MMU version is 3.x */ - __raw_writel((1 << 12) | (2 << 28), - data->sfrbase + REG_MMU_CFG); - __sysmmu_set_prefbuf(data->sfrbase, 0, -1, 0); - __sysmmu_set_prefbuf(data->sfrbase, 0, -1, 1); - } __raw_writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); -- cgit v1.2.3 From 6cb47ed7397e0881afede05161ccaf5b70f95414 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:51 +0530 Subject: iommu/exynos: Add missing cache flush for removed page table entries This commit adds cache flush for removed small and large page entries in exynos_iommu_unmap(). Missing cache flush of removed page table entries can cause missing page fault interrupt when a master IP accesses an unmapped area. Reviewed-by: Tomasz Figa Tested-by: Grant Grundler Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha 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 4fc31fc6a49a..6915235f9e86 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -904,6 +904,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, if (lv2ent_small(ent)) { *ent = 0; size = SPAGE_SIZE; + pgtable_flush(ent, ent + 1); priv->lv2entcnt[lv1ent_offset(iova)] += 1; goto done; } @@ -915,6 +916,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, } memset(ent, 0, sizeof(*ent) * SPAGES_PER_LPAGE); + pgtable_flush(ent, ent + SPAGES_PER_LPAGE); size = LPAGE_SIZE; priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; -- cgit v1.2.3 From f4723ec1723c0925c4191f5f33192ec09c6164f8 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:52 +0530 Subject: iommu/exynos: Always enable runtime PM Checking if the probing device has a parent device was just to discover if the probing device is involved in a power domain when the power domain controlled by Samsung's custom implementation. Since generic IO power domain is applied, it is required to remove the condition to see if the probing device has a parent device. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 6915235f9e86..ef771a22c09d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -558,8 +558,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - if (dev->parent) - pm_runtime_enable(dev); + pm_runtime_enable(dev); dev_dbg(dev, "(%s) Initialized\n", data->dbgname); return 0; -- cgit v1.2.3 From e5cf63c3025c2cb7e1db78658827f6ab2a7a039f Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:53 +0530 Subject: iommu/exynos: Remove dbgname from drvdata of a System MMU This patch removes dbgname member from sysmmu_drvdata structure. Kernel message for debugging already has the name of a single System MMU node. It also removes some compilation warnings. Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ef771a22c09d..be7a7b9c2d59 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -170,7 +170,6 @@ struct sysmmu_drvdata { struct list_head node; /* entry of exynos_iommu_domain.clients */ struct device *sysmmu; /* System MMU's device descriptor */ struct device *dev; /* Owner of system MMU */ - char *dbgname; void __iomem *sfrbase; struct clk *clk; int activations; @@ -321,8 +320,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (!ret && (itype != SYSMMU_FAULT_UNKNOWN)) __raw_writel(1 << itype, data->sfrbase + REG_INT_CLEAR); else - dev_dbg(data->sysmmu, "(%s) %s is not handled.\n", - data->dbgname, sysmmu_fault_name[itype]); + dev_dbg(data->sysmmu, "%s is not handled.\n", + sysmmu_fault_name[itype]); if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data->sfrbase); @@ -354,10 +353,10 @@ finish: write_unlock_irqrestore(&data->lock, flags); if (disabled) - dev_dbg(data->sysmmu, "(%s) Disabled\n", data->dbgname); + dev_dbg(data->sysmmu, "Disabled\n"); else - dev_dbg(data->sysmmu, "(%s) %d times left to be disabled\n", - data->dbgname, data->activations); + dev_dbg(data->sysmmu, "%d times left to be disabled\n", + data->activations); return disabled; } @@ -384,7 +383,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, ret = 1; } - dev_dbg(data->sysmmu, "(%s) Already enabled\n", data->dbgname); + dev_dbg(data->sysmmu, "Already enabled\n"); goto finish; } @@ -399,7 +398,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, data->domain = domain; - dev_dbg(data->sysmmu, "(%s) Enabled\n", data->dbgname); + dev_dbg(data->sysmmu, "Enabled\n"); finish: write_unlock_irqrestore(&data->lock, flags); @@ -415,16 +414,15 @@ int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) ret = pm_runtime_get_sync(data->sysmmu); if (ret < 0) { - dev_dbg(data->sysmmu, "(%s) Failed to enable\n", data->dbgname); + dev_dbg(data->sysmmu, "Failed to enable\n"); return ret; } ret = __exynos_sysmmu_enable(data, pgtable, NULL); if (WARN_ON(ret < 0)) { pm_runtime_put(data->sysmmu); - dev_err(data->sysmmu, - "(%s) Already enabled with page table %#x\n", - data->dbgname, data->pgtable); + dev_err(data->sysmmu, "Already enabled with page table %#x\n", + data->pgtable); } else { data->dev = dev; } @@ -474,9 +472,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, sysmmu_unblock(data->sfrbase); } } else { - dev_dbg(data->sysmmu, - "(%s) Disabled. Skipping invalidating TLB.\n", - data->dbgname); + dev_dbg(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } read_unlock_irqrestore(&data->lock, flags); @@ -495,9 +491,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) sysmmu_unblock(data->sfrbase); } } else { - dev_dbg(data->sysmmu, - "(%s) Disabled. Skipping invalidating TLB.\n", - data->dbgname); + dev_dbg(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } read_unlock_irqrestore(&data->lock, flags); @@ -560,7 +554,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) pm_runtime_enable(dev); - dev_dbg(dev, "(%s) Initialized\n", data->dbgname); + dev_dbg(dev, "Initialized\n"); return 0; err_irq: free_irq(platform_get_irq(pdev, 0), data); -- cgit v1.2.3 From 46c16d1e4c11ffa0481b1dd6841e0bcabcf278e7 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:54 +0530 Subject: iommu/exynos: Use managed device helper functions This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 68 ++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index be7a7b9c2d59..c86e3745e98f 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -343,8 +343,7 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) __raw_writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); - if (!IS_ERR(data->clk)) - clk_disable(data->clk); + clk_disable(data->clk); disabled = true; data->pgtable = 0; @@ -387,8 +386,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - if (!IS_ERR(data->clk)) - clk_enable(data->clk); + clk_enable(data->clk); data->pgtable = pgtable; @@ -499,49 +497,43 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) static int exynos_sysmmu_probe(struct platform_device *pdev) { - int ret; + int irq, ret; struct device *dev = &pdev->dev; struct sysmmu_drvdata *data; struct resource *res; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - dev_dbg(dev, "Not enough memory\n"); - ret = -ENOMEM; - goto err_alloc; - } + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_dbg(dev, "Unable to find IOMEM region\n"); - ret = -ENOENT; - goto err_init; - } + data->sfrbase = devm_ioremap_resource(dev, res); + if (IS_ERR(data->sfrbase)) + return PTR_ERR(data->sfrbase); - data->sfrbase = ioremap(res->start, resource_size(res)); - if (!data->sfrbase) { - dev_dbg(dev, "Unable to map IOMEM @ PA:%#x\n", res->start); - ret = -ENOENT; - goto err_res; - } - - ret = platform_get_irq(pdev, 0); - if (ret <= 0) { + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { dev_dbg(dev, "Unable to find IRQ resource\n"); - goto err_irq; + return irq; } - ret = request_irq(ret, exynos_sysmmu_irq, 0, + ret = devm_request_irq(dev, irq, exynos_sysmmu_irq, 0, dev_name(dev), data); if (ret) { - dev_dbg(dev, "Unabled to register interrupt handler\n"); - goto err_irq; + dev_err(dev, "Unabled to register handler of irq %d\n", irq); + return ret; } - if (dev_get_platdata(dev)) { - data->clk = clk_get(dev, "sysmmu"); - if (IS_ERR(data->clk)) - dev_dbg(dev, "No clock descriptor registered\n"); + data->clk = devm_clk_get(dev, "sysmmu"); + if (IS_ERR(data->clk)) { + dev_err(dev, "Failed to get clock!\n"); + return PTR_ERR(data->clk); + } else { + ret = clk_prepare(data->clk); + if (ret) { + dev_err(dev, "Failed to prepare clk\n"); + return ret; + } } data->sysmmu = dev; @@ -554,17 +546,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) pm_runtime_enable(dev); - dev_dbg(dev, "Initialized\n"); return 0; -err_irq: - free_irq(platform_get_irq(pdev, 0), data); -err_res: - iounmap(data->sfrbase); -err_init: - kfree(data); -err_alloc: - dev_err(dev, "Failed to initialize\n"); - return ret; } static struct platform_driver exynos_sysmmu_driver = { -- cgit v1.2.3 From 7060587052e0370ea1b7a41c84d5ad364be16f51 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:55 +0530 Subject: iommu/exynos: Gating clocks of master H/W This patch gates clocks of master H/W as well as clocks of System MMU if master clocks are specified. Some Exynos SoCs (i.e. GScalers in Exynos5250) have dependencies in the gating clocks of master H/W and its System MMU. If a H/W is the case, accessing control registers of System MMU is prohibited unless both of the gating clocks of System MMU and its master H/W. CC: Tomasz Figa Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c86e3745e98f..5af5c5c16f49 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -172,6 +172,7 @@ struct sysmmu_drvdata { struct device *dev; /* Owner of system MMU */ void __iomem *sfrbase; struct clk *clk; + struct clk *clk_master; int activations; rwlock_t lock; struct iommu_domain *domain; @@ -300,6 +301,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) WARN_ON(!is_sysmmu_active(data)); + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); itype = (enum exynos_sysmmu_inttype) __ffs(__raw_readl(data->sfrbase + REG_INT_STATUS)); if (WARN_ON(!((itype >= 0) && (itype < SYSMMU_FAULT_UNKNOWN)))) @@ -326,6 +329,9 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data->sfrbase); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); + read_unlock(&data->lock); return IRQ_HANDLED; @@ -341,9 +347,14 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) if (!set_sysmmu_inactive(data)) goto finish; + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + __raw_writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); clk_disable(data->clk); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); disabled = true; data->pgtable = 0; @@ -386,14 +397,19 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - clk_enable(data->clk); - data->pgtable = pgtable; + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + clk_enable(data->clk); + __sysmmu_set_ptbase(data->sfrbase, pgtable); __raw_writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); + data->domain = domain; dev_dbg(data->sysmmu, "Enabled\n"); @@ -450,6 +466,10 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, if (is_sysmmu_active(data)) { unsigned int maj; unsigned int num_inv = 1; + + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + maj = __raw_readl(data->sfrbase + REG_MMU_VERSION); /* * L2TLB invalidation required @@ -469,6 +489,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, data->sfrbase, iova, num_inv); sysmmu_unblock(data->sfrbase); } + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); } else { dev_dbg(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } @@ -484,10 +506,14 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_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(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } @@ -536,6 +562,16 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) } } + data->clk_master = devm_clk_get(dev, "master"); + if (!IS_ERR(data->clk_master)) { + ret = clk_prepare(data->clk_master); + if (ret) { + clk_unprepare(data->clk); + dev_err(dev, "Failed to prepare master's clk\n"); + return ret; + } + } + data->sysmmu = dev; rwlock_init(&data->lock); INIT_LIST_HEAD(&data->node); -- cgit v1.2.3 From 1fab7fa7230fa0422ca81cea7d1bfbf3f9b0d3f9 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Mon, 12 May 2014 11:44:56 +0530 Subject: iommu/exynos: Remove custom fault handler This commit removes custom fault handler. The device drivers that need to register fault handler can register with iommu_set_fault_handler(). CC: Grant Grundler Signed-off-by: Cho KyongHo Signed-off-by: Shaik Ameer Basha Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 80 +++++++++++++------------------------------- 1 file changed, 24 insertions(+), 56 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5af5c5c16f49..c1be65fd0af5 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -125,16 +125,6 @@ enum exynos_sysmmu_inttype { SYSMMU_FAULTS_NUM }; -/* - * @itype: type of fault. - * @pgtable_base: the physical address of page table base. This is 0 if @itype - * is SYSMMU_BUSERROR. - * @fault_addr: the device (virtual) address that the System MMU tried to - * translated. This is 0 if @itype is SYSMMU_BUSERROR. - */ -typedef int (*sysmmu_fault_handler_t)(enum exynos_sysmmu_inttype itype, - phys_addr_t pgtable_base, unsigned long fault_addr); - static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { REG_PAGE_FAULT_ADDR, REG_AR_FAULT_ADDR, @@ -176,7 +166,6 @@ struct sysmmu_drvdata { int activations; rwlock_t lock; struct iommu_domain *domain; - sysmmu_fault_handler_t fault_handler; phys_addr_t pgtable; }; @@ -245,34 +234,17 @@ static void __sysmmu_set_ptbase(void __iomem *sfrbase, __sysmmu_tlb_invalidate(sfrbase); } -static void __set_fault_handler(struct sysmmu_drvdata *data, - sysmmu_fault_handler_t handler) -{ - unsigned long flags; - - write_lock_irqsave(&data->lock, flags); - data->fault_handler = handler; - write_unlock_irqrestore(&data->lock, flags); -} - -void exynos_sysmmu_set_fault_handler(struct device *dev, - sysmmu_fault_handler_t handler) -{ - struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); - - __set_fault_handler(data, handler); -} - -static int default_fault_handler(enum exynos_sysmmu_inttype itype, - phys_addr_t pgtable_base, unsigned long fault_addr) +static void show_fault_information(const char *name, + enum exynos_sysmmu_inttype itype, + phys_addr_t pgtable_base, unsigned long fault_addr) { unsigned long *ent; if ((itype >= SYSMMU_FAULTS_NUM) || (itype < SYSMMU_PAGEFAULT)) itype = SYSMMU_FAULT_UNKNOWN; - pr_err("%s occurred at 0x%lx(Page table base: %pa)\n", - sysmmu_fault_name[itype], fault_addr, &pgtable_base); + pr_err("%s occurred at %#lx by %s(Page table base: %pa)\n", + sysmmu_fault_name[itype], fault_addr, name, &pgtable_base); ent = section_entry(phys_to_virt(pgtable_base), fault_addr); pr_err("\tLv