Re: [PATCH 1/2] dma-mapping: introduce relaxed version of dma sync
On Tue, Aug 18, 2020 at 05:10:06PM +0100, Christoph Hellwig wrote: > On Tue, Aug 18, 2020 at 11:07:57AM +0100, Will Deacon wrote: > > > > so I'm not sure > > > > that we should be complicating the implementation like this to try to > > > > make it "fast". > > > > > > > I agree that this patch makes the implementation of dma API a bit more > > > but I don't think this does not impact its complication seriously. > > > > It's death by a thousand cuts; this patch further fragments the architecture > > backends and leads to arm64-specific behaviour which consequently won't get > > well tested by anybody else. Now, it might be worth it, but there's not > > enough information here to make that call. > > So it turns out I misread the series (*cough*, crazy long lines, > *cough*), and it does not actually expose a new API as I thought, but > it still makes a total mess of the internal interface. It turns out > that on the for cpu side we already have arch_sync_dma_for_cpu_all, > which should do all that is needed. We could do the equivalent for > the to device side, but only IFF there really is a major benefit for > something that actually is mainstream and matters. > Indeed, arch_sync_dma_for_cpu_all() is used where the new internal API arch_sync_barrier_for_cpu() should be called. I just thought it is a special hook for MIPS. In the next version of the patch series, I should consider using arch_sync_dma_for_cpu_all() and introducting its 'for_dev' version with some performance data to show the benefit of the change. Thank you for the proposal. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 1/2] dma-mapping: introduce relaxed version of dma sync
On Tue, Aug 18, 2020 at 11:07:57AM +0100, Will Deacon wrote: > On Tue, Aug 18, 2020 at 06:37:39PM +0900, Cho KyongHo wrote: > > On Tue, Aug 18, 2020 at 09:28:53AM +0100, Will Deacon wrote: > > > On Tue, Aug 18, 2020 at 04:43:10PM +0900, Cho KyongHo wrote: > > > > Cache maintenance operations in the most of CPU architectures needs > > > > memory barrier after the cache maintenance for the DMAs to view the > > > > region of the memory correctly. The problem is that memory barrier is > > > > very expensive and dma_[un]map_sg() and dma_sync_sg_for_{device|cpu}() > > > > involves the memory barrier per every single cache sg entry. In some > > > > CPU micro-architecture, a single memory barrier consumes more time than > > > > cache clean on 4KiB. It becomes more serious if the number of CPU cores > > > > are larger. > > > > > > Have you got higher-level performance data for this change? It's more > > > likely > > > that the DSB is what actually forces the prior cache maintenance to > > > complete, > > > > This patch does not skip necessary DSB after cache maintenance. It just > > remove repeated dsb per every single sg entry and call dsb just once > > after cache maintenance on all sg entries is completed. > > Yes, I realise that, but what I'm saying is that a big part of your > justification for this change is: > > | The problem is that memory barrier is very expensive and dma_[un]map_sg() > | and dma_sync_sg_for_{device|cpu}() involves the memory barrier per every > | single cache sg entry. In some CPU micro-architecture, a single memory > | barrier consumes more time than cache clean on 4KiB. > > and my point is that the DSB is likely completing the cache maintenance, > so as cache maintenance instructions retire faster in the micro-architecture, > the DSB becomes absolutely slower. In other words, it doesn't make much > sense to me to compare the cost of the DSB with the cost of the cache > maintenance; what matters more is the code of the high-level unmap() > operation for the sglist. > I now understand your point. But I still believe that repeated DSB in the middle of cache maintenance wastes redundant CPU cycles. Avoiding that redundancy causes extra complexity to implmentation of dma API. But I think it is valuable. > > > so it's important to look at the bigger picture, not just the > > > apparent relative cost of these instructions. > > > > > If you mean bigger picture is the performance impact of this patch to a > > complete user scenario, we are evaluating it in some latency sensitve > > scenario. But I wonder if a performance gain in a platform/SoC specific > > scenario is also persuasive. > > Latency is fine too, but phrasing the numbers (and we really need those) > in terms of things like "The interrupt response time for this in-tree > driver is improved by xxx ns (yy %) after this change" or "Throughput > for this in-tree driver goes from xxx mb/s to yyy mb/s" would be really > helpful. > Unfortunately, we have no in-tree driver to show the performance. Instead, we just evaluated the speed of dma_sync_sg_for_device() to see the improvements of this patch. For example, Cortex-A55 in our 2-cluster, big-mid-little system gains 28% (130.9 usec. -> 94.5 usec.) during dma_sync_sg_for_device(sg, nents, DMA_TO_DEVICE) is running with nents = 256 and length of each sg entrh is 4KiB. Let me describe the detailed performance results in the next patch series which will include some fixes to errata in commit messages. > > > Also, it's a miracle that non-coherent DMA even works, > > > > I am sorry, Will. I don't understand this. Can you let me know what do > > you mena with the above sentence? > > Non-coherent DMA sucks for software. I agree. But due to the H/W cost, proposals about coherent DMA are always challenging. > For the most part, Linux does a nice > job of hiding this from device drivers, and I think _that_ is the primary > concern, rather than performance. If performance is a problem, then the > solution is cache coherence or a shared non-cacheable buffer (rather than > the streaming API). We are also trying to use non-cacheable buffers for the non-coherent DMAs. But the problem with the non-cacheable buffer is CPU access speed. > > > > so I'm not sure > > > that we should be complicating the implementation like this to try to > > > make it "fast". > > > > > I agree that this patch makes the implementation of dma API a bit more > > but I don't think this does not impact its complication seriously. > > It's death by a thousand cuts; this patch further fragments the architecture > backends and leads to arm64-specific behaviour which consequently won't get > well tested by anybody else. Now, it might be worth it, but there's not > enough information here to make that call. > Will > ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 1/2] dma-mapping: introduce relaxed version of dma sync
On Tue, Aug 18, 2020 at 09:37:20AM +0100, Christoph Hellwig wrote: > On Tue, Aug 18, 2020 at 09:28:53AM +0100, Will Deacon wrote: > > On Tue, Aug 18, 2020 at 04:43:10PM +0900, Cho KyongHo wrote: > > > Cache maintenance operations in the most of CPU architectures needs > > > memory barrier after the cache maintenance for the DMAs to view the > > > region of the memory correctly. The problem is that memory barrier is > > > very expensive and dma_[un]map_sg() and dma_sync_sg_for_{device|cpu}() > > > involves the memory barrier per every single cache sg entry. In some > > > CPU micro-architecture, a single memory barrier consumes more time than > > > cache clean on 4KiB. It becomes more serious if the number of CPU cores > > > are larger. > > > > Have you got higher-level performance data for this change? It's more likely > > that the DSB is what actually forces the prior cache maintenance to > > complete, so it's important to look at the bigger picture, not just the > > apparent relative cost of these instructions. > > > > Also, it's a miracle that non-coherent DMA even works, so I'm not sure > > that we should be complicating the implementation like this to try to > > make it "fast". > > And without not just an important in-tree user but one that actually > matters and can show how this is correct the whole proposal is complete > nonstarter. > The patch introduces new kernel configurations ARCH_HAS_SYNC_DMA_FOR_CPU_RELAXED and ARCH_HAS_SYNC_DMA_FOR_CPU_RELAXED not to affect the rest of the system. I also confirmed that the patch does not break some other architectures including arm and x86 which do not define the new kernel configurations. Would you let me know some other things to confirm this patch is correct? Thank you. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 1/2] dma-mapping: introduce relaxed version of dma sync
On Tue, Aug 18, 2020 at 09:28:53AM +0100, Will Deacon wrote: > On Tue, Aug 18, 2020 at 04:43:10PM +0900, Cho KyongHo wrote: > > Cache maintenance operations in the most of CPU architectures needs > > memory barrier after the cache maintenance for the DMAs to view the > > region of the memory correctly. The problem is that memory barrier is > > very expensive and dma_[un]map_sg() and dma_sync_sg_for_{device|cpu}() > > involves the memory barrier per every single cache sg entry. In some > > CPU micro-architecture, a single memory barrier consumes more time than > > cache clean on 4KiB. It becomes more serious if the number of CPU cores > > are larger. > > Have you got higher-level performance data for this change? It's more likely > that the DSB is what actually forces the prior cache maintenance to > complete, This patch does not skip necessary DSB after cache maintenance. It just remove repeated dsb per every single sg entry and call dsb just once after cache maintenance on all sg entries is completed. > so it's important to look at the bigger picture, not just the > apparent relative cost of these instructions. > If you mean bigger picture is the performance impact of this patch to a complete user scenario, we are evaluating it in some latency sensitve scenario. But I wonder if a performance gain in a platform/SoC specific scenario is also persuasive. > Also, it's a miracle that non-coherent DMA even works, I am sorry, Will. I don't understand this. Can you let me know what do you mena with the above sentence? > so I'm not sure > that we should be complicating the implementation like this to try to > make it "fast". > I agree that this patch makes the implementation of dma API a bit more but I don't think this does not impact its complication seriously. > Will > Thank you. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] dma-mapping: introduce relaxed version of dma sync
Cache maintenance operations in the most of CPU architectures needs memory barrier after the cache maintenance for the DMAs to view the region of the memory correctly. The problem is that memory barrier is very expensive and dma_[un]map_sg() and dma_sync_sg_for_{device|cpu}() involves the memory barrier per every single cache sg entry. In some CPU micro-architecture, a single memory barrier consumes more time than cache clean on 4KiB. It becomes more serious if the number of CPU cores are larger. This patch introduces arch_sync_dma_for_device_relaxed() and arch_sync_dma_for_cpu_relaxed() which do not involve memory barrier. So the users called those functions require explicitly calling arch_sync_barrier_for_device() and arch_sync_barrier_for_cpu(), respectively to confirm the view of memory is consistent between the CPUs and DMAs. Signed-off-by: Cho KyongHo --- drivers/iommu/dma-iommu.c | 6 +++-- include/linux/dma-direct.h | 29 +- include/linux/dma-noncoherent.h | 54 + kernel/dma/Kconfig | 8 ++ kernel/dma/direct.c | 25 +++ 5 files changed, 109 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 5141d49..4f9c9cb 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -705,7 +705,8 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev, return; for_each_sg(sgl, sg, nelems, i) - arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); + arch_sync_dma_for_cpu_relaxed(sg_phys(sg), sg->length, dir); + arch_sync_barrier_for_cpu(dir); } static void iommu_dma_sync_sg_for_device(struct device *dev, @@ -719,7 +720,8 @@ static void iommu_dma_sync_sg_for_device(struct device *dev, return; for_each_sg(sgl, sg, nelems, i) - arch_sync_dma_for_device(sg_phys(sg), sg->length, dir); + arch_sync_dma_for_device_relaxed(sg_phys(sg), sg->length, dir); + arch_sync_barrier_for_device(dir); } static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page, diff --git a/include/linux/dma-direct.h b/include/linux/dma-direct.h index 6e87225..f5b1fee 100644 --- a/include/linux/dma-direct.h +++ b/include/linux/dma-direct.h @@ -152,7 +152,7 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev, swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU); } -static inline dma_addr_t dma_direct_map_page(struct device *dev, +static inline dma_addr_t __dma_direct_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, unsigned long attrs) { @@ -172,20 +172,37 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev, return DMA_MAPPING_ERROR; } - if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) - arch_sync_dma_for_device(phys, size, dir); return dma_addr; } -static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr, +static inline dma_addr_t dma_direct_map_page(struct device *dev, + struct page *page, unsigned long offset, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + dma_addr_t dma_addr = __dma_direct_map_page(dev, page, offset, size, dir, attrs); + + if (dma_addr != DMA_MAPPING_ERROR && !dev_is_dma_coherent(dev) && + !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) + arch_sync_dma_for_device(page_to_phys(page) + offset, size, dir); + + return dma_addr; +} + +static inline void __dma_direct_unmap_page(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { phys_addr_t phys = dma_to_phys(dev, addr); + if (unlikely(is_swiotlb_buffer(phys))) + swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs); +} + +static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir, unsigned long attrs) +{ if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) dma_direct_sync_single_for_cpu(dev, addr, size, dir); - if (unlikely(is_swiotlb_buffer(phys))) - swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs); + __dma_direct_unmap_page(dev, addr, size, dir, attrs); } #endif /* _LINUX_DMA_DIRECT_H */ diff --git a/include/linux/dma-noncoherent.h b/include/linux/dma-noncoherent.h index ca09a4e..0a31e6c 100644 --- a/include/linux/dma-noncoherent.h +++ b/include/linux/dma-noncoherent.h @@ -73,23 +73,77 @@ static inline void arch_dma_cache_sync(struct device *dev, void *vaddr, #endif /* CONFIG_DMA_NONCOHERENT_CACHE_SYNC */ #i
[PATCH 2/2] arm64: dma-mapping: add relaxed DMA sync
__dma_[un]map_area() is the implementation of cache maintenance operations for DMA in arm64. 'dsb sy' in the subroutine guarantees the view of given memory area is consistent to all memory observers. So, it is required. However, dma_sync_sg_for_{device|cpu}() and dma_[un]map_sg() calls __dma_[un]map_area() nents number of times and 'dsb sy' instruction is executed the same number of times. We have observed that 'dsb sy' consumes more time than cleaning or invalidating 4KiB area. arch_sync_dma_for_{device|cpu}_relaxed() and arch_sync_barrier_for_{device|cpu}() are introduced since commit 6a9356234 ("dma-mapping: introduce relaxed version of dma sync") to reduce redundant memory barriers in sg versions of DMA sync API. Implementing relaxed version of DMA sync API will dramatically increase the performance of dma_sync_sg_for_{device|cpu}(). Signed-off-by: Cho KyongHo --- arch/arm64/Kconfig | 4 ++-- arch/arm64/include/asm/assembler.h | 33 - arch/arm64/include/asm/barrier.h | 13 + arch/arm64/mm/cache.S | 34 +++--- arch/arm64/mm/dma-mapping.c| 4 ++-- include/linux/dma-noncoherent.h| 1 + 6 files changed, 61 insertions(+), 28 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6d23283..4fc7ef4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -31,8 +31,8 @@ config ARM64 select ARCH_HAS_SET_MEMORY select ARCH_HAS_STRICT_KERNEL_RWX select ARCH_HAS_STRICT_MODULE_RWX - select ARCH_HAS_SYNC_DMA_FOR_DEVICE - select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SYNC_DMA_FOR_DEVICE_RELAXED + select ARCH_HAS_SYNC_DMA_FOR_CPU_RELAXED select ARCH_HAS_SYSCALL_WRAPPER select ARCH_HAS_TEARDOWN_DMA_OPS if IOMMU_SUPPORT select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 54d1811..1f87d98 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -345,6 +345,33 @@ alternative_endif .endm /* + * Macro to perform a data cache invalidation for the interval + * [kaddr, kaddr + size) + * + * kaddr: starting virtual address of the region + * size: size of the region + * Corrupts: kaddr, size, tmp1, tmp2 + */ + .macro __dcache_inv_by_line kaddr, size, tmp1, tmp2 + add \size, \size, \kaddr + dcache_line_size \tmp1, \tmp2 + sub \tmp2, \tmp1, #1 + tst \size, \tmp2// end cache line aligned? + bic \size, \size, \tmp2 + b.eq9997f + dc civac, \size// clean & invalidate D / U line +9997: tst \kaddr, \tmp2 // start cache line aligned? + bic \kaddr, \kaddr, \tmp2 + b.eq9998f + dc civac, \kaddr // clean & invalidate D / U line + b f +9998: dc ivac, \kaddr// invalidate D / U line +: add \kaddr, \kaddr, \tmp1 + cmp \kaddr, \size + b.lo9998b + .endm + +/* * Macro to perform a data cache maintenance for the interval * [kaddr, kaddr + size) * @@ -362,7 +389,7 @@ alternative_else alternative_endif .endm - .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2 + .macro __dcache_by_line_op op, kaddr, size, tmp1, tmp2 dcache_line_size \tmp1, \tmp2 add \size, \kaddr, \size sub \tmp2, \tmp1, #1 @@ -388,6 +415,10 @@ alternative_endif add \kaddr, \kaddr, \tmp1 cmp \kaddr, \size b.lo9998b + .endm + + .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2 + __dcache_by_line_op \op, \kaddr, \size, \tmp1, \tmp2 dsb \domain .endm diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h index fb4c275..96bbbf6 100644 --- a/arch/arm64/include/asm/barrier.h +++ b/arch/arm64/include/asm/barrier.h @@ -167,6 +167,19 @@ do { \ #include +#include + +static inline void arch_sync_barrier_for_device(enum dma_data_direction dir) +{ + dsb(sy); +} + +static inline void arch_sync_barrier_for_cpu(enum dma_data_direction dir) +{ + if (dir == DMA_FROM_DEVICE) + dsb(sy); +} + #endif /* __ASSEMBLY__ */ #endif /* __ASM_BARRIER_H */ diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 2d881f3..7180256 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -138,34 +138,20 @@ SYM_FUNC_END(__clean_dcache_area_pou) * - kaddr - kernel address * - size- size in question */ -SYM_FUNC_START_LOCAL(__dma_inv_area) SYM_FUNC_START_PI(__inval_dcache_area) - /* FALLTHROUGH */ + __dcache_inv_by_line x0, x1, x2, x3 + dsb
Re: [PATCH v6 05/25] iommu: exynos: don't read version register on every tlb operation
On Mon, 04 May 2015 10:16:00 +0200 Marek Szyprowski m.szyprow...@samsung.com wrote: 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 m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 13 + 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3e898504a7c4..3861485f0689 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; + int version; Why don't you define its type as unsigned int or u32? Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v6 06/25] iommu: exynos: remove unused functions
On Mon, 04 May 2015 10:16:01 +0200 Marek Szyprowski m.szyprow...@samsung.com wrote: 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 m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 31 --- 1 file changed, 31 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3861485f0689..98aa7e9c2507 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; Actually they are for some drivers that are not upstreamed. But I agree removing them. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v6 08/25] iommu: exynos: refactor function parameters to simplify code
On Mon, 04 May 2015 10:16:03 +0200 Marek Szyprowski m.szyprow...@samsung.com wrote: 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 Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 93 ++-- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c307c400613c..0c23b69022cd 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; int version; }; @@ -514,12 +513,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); @@ -533,14 +530,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)) { @@ -570,8 +563,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); } @@ -711,7 +704,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; unsigned long flags; int i; @@ -719,14 +712,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(data, priv-clients, domain_node) { Use list_for_each_entry_safe() or you will get panic. + 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++) @@ -744,20 +735,26 @@ 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); - owner-domain = domain; + data = dev_get_drvdata(owner-sysmmu); + if (data) { Is there a case that a probed System MMU without driver data? + ret = __sysmmu_enable(data, pagetable, domain);
Re: [PATCH v6 11/25] iommu: exynos: add support for binding more than one sysmmu to master device
On Mon, 04 May 2015 10:16:06 +0200 Marek Szyprowski m.szyprow...@samsung.com wrote: 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 m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 11 +-- 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f2eceb6605c5..598660c87410 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,7 +186,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { /* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct device *sysmmu; + struct list_head clients; 'clients' is the list of System MMUs that are assigned to the same iommu domain. I don't think clients is not a good list name for exynos_iommu_owner even though the elements are the same because they are used in different contexts. -- samsung.com pullip@samsung.com ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] devicetree: Add generic IOMMU device tree bindings
On Fri, 16 May 2014 14:23:18 +0200, Thierry Reding wrote: From: Thierry Reding tred...@nvidia.com This commit introduces a generic device tree binding for IOMMU devices. Only a very minimal subset is described here, but it is enough to cover the requirements of both the Exynos System MMU and Tegra SMMU as discussed here: https://lkml.org/lkml/2014/4/27/346 More advanced functionality such as the dma-ranges property can easily be added in a backwards-compatible way. In the absence of a dma-ranges property it should be safe to default to the whole address space. Signed-off-by: Thierry Reding tred...@nvidia.com --- Documentation/devicetree/bindings/iommu/iommu.txt | 109 ++ 1 file changed, 109 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/iommu.txt diff --git a/Documentation/devicetree/bindings/iommu/iommu.txt b/Documentation/devicetree/bindings/iommu/iommu.txt new file mode 100644 index ..2d67b52b656e --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/iommu.txt @@ -0,0 +1,109 @@ +This document describes the generic device tree binding for IOMMUs and their +master(s). + + +IOMMU device node: +== + +An IOMMU can provide the following services: + +* Remap address space to allow devices to access physical memory ranges that + they otherwise wouldn't be capable of accessing. + + Example: 32-bit DMA to 64-bit physical addresses + +* Implement scatter-gather at page level granularity so that the device does + not have to. + +* Provide system protection against rogue DMA by forcing all accesses to go + through the IOMMU and faulting when encountering accesses to unmapped + address regions. + +* Provide address space isolation between multiple contexts. + + Example: Virtualization + +Device nodes compatible with this binding represent hardware with some of the +above capabilities. + +IOMMUs can be single-master or multiple-master. Single-master IOMMU devices +typically have a fixed association to the master device, whereas multiple- +master IOMMU devices can translate accesses from more than one master. + +Required properties: + +- #iommu-cells: The number of cells in an IOMMU specifier. The meaning of the + cells is defined by the binding for the IOMMU device. + + Typical values include: + * 0: Single-master IOMMU devices are often not configurable, therefore the +specifying doesn't need to encode any information and can be empty. + + * 1: Multiple-master IOMMU devices need to know for which master they should +enable translation. Typically the single cell in the specifier corresponds +to the master device's ID. + + +IOMMU master node: +== + +Devices that access memory through an IOMMU are called masters. A device can +have multiple master interfaces (to one or more IOMMU devices). + +Required properties: + +- iommus: A list of phandle and IOMMU specifier pairs that describe the IOMMU + master interfaces of the device. One entry in the list describes one master + interface of the device. + +Optional properties: + +- iommu-names: A list of names identifying each entry in the iommus property. + + +Examples: += + +Single-master IOMMU: + + + iommu { + #iommu-cells = 0; + }; + + master { + iommu = /iommu; + }; + Great work, Thierry. One simple comment. This should be also applicable to multi-master IOMMUs that the masters of an IOMMU is not configurable with ID or something. I think the title needs to be changed to cover such IOMMUs which always translate master's transactions and unable to change the configuration of the relationship between the masters and IOMMUs by S/W. Regards, KyongHo +Multi-master IOMMU: +--- + + iommu { + /* the specifier represents the ID of the master */ + #iommu-cells = 1; + }; + + master { + /* device has master ID 42 in the IOMMU */ + iommu = /iommu 42; + }; + +Multi-master device: + + + /* single-master IOMMU */ + iommu@1 { + #iommu-cells = 0; + }; + + /* multi-master IOMMU */ + iommu@2 { + /* the specifier represents the ID of the master */ + #iommu-cells = 1; + }; + + /* device with two master interfaces */ + master { + iommus = /iommu@1,/* master of the single-master IOMMU */ + /iommu@2 42; /* ID 42 in multi-master IOMMU */ + }; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v12 11/31] documentation: iommu: add binding document of Exynos System MMU
On Thu, 15 May 2014 22:37:31 +0200, Thierry Reding wrote: On Mon, Apr 28, 2014 at 02:05:30PM +0200, Arnd Bergmann wrote: [...] let me clarify by example: iommu@1 { compatible = some,simple-iommu; reg = 1; #iommu-cells = 0; /* supports only one master */ }; iommu@2 { compatible = some,other-iommu; reg = 3; #iommu-cells = 1; /* contains master ID */ }; iommu@3 { compatible = some,windowed-iommu; reg = 2; #iommu-cells = 2; /* contains dma-window */ }; device@4 { compatible = some,ethernet; iommus = /iommu@1; }; device@5 { compatible = some,dmaengine; iommus = /iommu@2 0x4000 0x100, /iommu@3 0x101; }; The device at address 4 has a one-one relationship with iommu@1, so there is no need for any data. device@5 has two master ports. One is connected to an IOMMU that has a per-device aperture, device@5 can only issue transfers to the 256MB area at 0x4000, and the IOMMU will have to put entries for this device into that address. The second master port is connected to iommu@3, which uses a master ID that gets passed along with each transfer, so that needs to be put into the IOTLBs. iommu@3 and the second port of device@5 seem to match what we need for Tegra (and as I understand also Exynos). Can we settle on this for now so that Hiroshi and Cho can go update their drivers for this binding? Currently, Exynos IOMMU is the case of iommu@1. But in the near future, it will support multiple masters with a single context that means all masters that shares a single System MMU also views the same address space. For some cases, we may need iommu@3 that supports dma-window. So, I have no other opinion. By the way, iommu framework should allow to process the parameters to 'iommus' property in the master nodes by iommu driver implementations because it is depended on implementations. A variation would be to not use #iommu-cells at all, but provide a #address-cells / #size-cells pair in the IOMMU, and have a translation as we do for dma-ranges. This is probably most flexible. The remainder of this discussion seems to indicate that #iommu-cells and dma-ranges don't have to be mutually exclusive. For some IOMMUs it might make sense to use both. In fact perhaps we should require every IOMMU user to also specify a dma-ranges property, even if for some cases the range would be simply the complete physical address space. Perhaps in analogy to the ranges property an empty dma-ranges property could be taken to mean all of the physical address space. I'm aware that this doesn't cover any of the more exotic cases out there, but the fact is that we have real devices out there that ship with some variations of these simple IOMMUs and I don't think we're doing ourselves a favour by blocking support for these to be added on the hope of merging the perfect solution that covers all use-cases. Patches for Tegra have already been around for close to half a year. Thierry ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v12 18/31] iommu/exynos: allow having multiple System MMUs for a master H/W
On Tue, 06 May 2014 20:05:14 +0200, Tomasz Figa wrote: On 27.04.2014 09:37, Shaik Ameer Basha wrote: From: Cho KyongHo pullip@samsung.com Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 545 ++ 1 file changed, 335 insertions(+), 210 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index fefedec3..c2e6365 100755 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -117,6 +117,10 @@ #define REG_PB1_EADDR 0x058 #define has_sysmmu(dev) (dev-archdata.iommu != NULL) +#define for_each_sysmmu_list(dev, list_data) \ + list_for_each_entry(list_data, \ + ((struct exynos_iommu_owner *)dev-archdata.iommu)-mmu_list, \ + entry) Sorry, NAK. Please don't add this kind of complexity and business logic to low level code. We want the configuration functions to be simple, easy to read, maintain and extend. The proper way to do it is to let the IOMMUs be grouped together on IOMMU subsystem level, so that each IOMMU consumer driver would see just one IOMMU, but then IOMMU driver callbacks would handle just particular instances of the IOMMU IP blocks, without any loops, lists and other crazy code... It is done in IOMMU driver internally. IOMMU consumer driver(IOMMU client device driver?) sees IOMMU domain but IOMMU itself. How to handle IOMMUs of the client device is just in charge of IOMMU driver according to the hardwired bus topology. If a master and IOMMU device should be 1:1 relationship as you contend, the master device driver should be responsible for express the bus topology in its business logic. I think it is not good as I told earlier. IOMMU group is different from the grouping System MMUs in Exynos IOMMU driver. IOMMU group is invented to group several IOMMUs on different bus with different masters to assign the same IOMMU domain. It is useful for user level virtualization but for expressing bus topology. Exynos IOMMU driver just groups System MMUs that have the same master device. Even though Exynos IOMMU driver implements IOMMU group which is already implemented by 20/31 patch, such looping is still required. However, I will consider other elegant way of interation of multiple System MMUs. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v12 00/31] iommu/exynos: Fixes and Enhancements of System MMU driver with DT
On Tue, 06 May 2014 20:08:47 +0200, Tomasz Figa wrote: On 06.05.2014 19:59, Joerg Roedel wrote: On Wed, Apr 30, 2014 at 04:27:10PM +0530, Shaik Ameer Basha wrote: This series is going on for quite a long time and most of the patches here doesn't depend on dt bindings. As Exynos IOMMU h/w is introducing new versions very frequently, maintaining and reviewing all these patches again and again is quite a hard job. If it is acceptable, I can post one more series with the subset of above patches, which doesn't depend on dt-bindings. As all the patches which doesn't depend on DT bindings are already tested, I hope merging these subset of patches may help in reducing the rework and review effort every time. Once we finalize the generic DT bindings for the IOMMU devices, the driver can be updated with the proposed DT bindings in mind. Sounds reasonable to me if the patch subset is self-contained. When Arnd is OK with that and no one else has objections I can take the dt-independend parts. +1. I would also suggest dropping patch 18/31, for the reasons I mentioned in my reply to it. Great. We will prepare the next patch series that are independent upon DT bindings soon. Thank you. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v12 18/31] iommu/exynos: allow having multiple System MMUs for a master H/W
On Mon, 28 Apr 2014 16:08:14 +0530, Tushar Behera wrote: On 04/27/2014 01:07 PM, Shaik Ameer Basha wrote: From: Cho KyongHo pullip@samsung.com Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. Signed-off-by: Cho KyongHo pullip@samsung.com Since you are posting the patches, you should also add your Signed-of-by. --- drivers/iommu/exynos-iommu.c | 545 ++ 1 file changed, 335 insertions(+), 210 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index fefedec3..c2e6365 100755 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c [ ... ] static int sysmmu_pm_genpd_save_state(struct device *dev) @@ -1215,7 +1349,7 @@ static int sysmmu_pm_genpd_save_state(struct device *dev) ret = cb(dev); if (ret == 0) - sysmmu_save_state(client-sysmmu); + sysmmu_save_state(dev); client is now unused, remove the variable. return ret; } @@ -1238,13 +1372,13 @@ static int sysmmu_pm_genpd_restore_state(struct device *dev) if (!cb dev-driver dev-driver-pm) cb = dev-driver-pm-runtime_resume; - sysmmu_restore_state(client-sysmmu); + sysmmu_restore_state(dev); if (cb) ret = cb(dev); if (ret) - sysmmu_save_state(client-sysmmu); + sysmmu_restore_state(dev); client is now unused, remove the variable. Ok. Thanks. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v12 30/31] ARM: dts: add System MMU nodes of exynos5250
On Mon, 28 Apr 2014 16:13:19 -0700, Doug Anderson wrote: Vikas, On Sun, Apr 27, 2014 at 10:39 AM, Vikas Sajjan sajjan.li...@gmail.com wrote: Hi shaik, +Doug, Abhilash, On Sun, Apr 27, 2014 at 1:08 PM, Shaik Ameer Basha shaik.am...@samsung.com wrote: From: Cho KyongHo pullip@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- arch/arm/boot/dts/exynos5250.dtsi | 270 - 1 file changed, 267 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 3742331..eebd397 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -82,6 +82,16 @@ reg = 0x10044040 0x20; }; + pd_isp: isp-power-domain@0x10044020 { + compatible = samsung,exynos4210-pd; + reg = 0x10044020 0x20; + }; + + pd_disp1: disp1-power-domain@0x100440A0 { + compatible = samsung,exynos4210-pd; + reg = 0x100440A0 0x20; + }; + As per subject add System MMU nodes of exynos5250, it should only add SysMMU node. So, I think adding power domain nodes should go in a separate patch. Adding power domain nodes can break the system, if powering ON/OFF of the given power domain is NOT taken care well. I can see ISP is one such case. With this series I can see S2R breaks [1] on 5250 chromebook with current mainline kernel (same applies for arndale and smdk5250, but I have tested on these boards yet) Thanks for catching that! Let's make sure not to break suspend/resume. Given that these power domains are actually used as the domains for some of the System MMU nodes I don't totally object to adding them in the same patch, but if they're breaking things then I agree that we could leave them out and add them in a later patch (once issues are sorted out). Thank you, Vikas. Let me consider adding ISP power doamin after checking power down sequence of ISP domain. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Tue, 22 Apr 2014 18:53:51 +0530, Shaik Ameer Basha wrote: Hi KyongHo Cho, On Fri, Mar 14, 2014 at 10:40 AM, Cho KyongHo pullip@samsung.com wrote: Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 534 ++ 1 file changed, 333 insertions(+), 201 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 84ba29a..7489343 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -128,6 +128,10 @@ #define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) [snip] +static int __init __sysmmu_init_master(struct device *dev) +{ + int ret; + int i = 0; + struct device_node *node; + + while ((node = of_parse_phandle(dev-of_node, mmu-masters, i++))) { struct platform_device *master = of_find_device_by_node(node); + struct exynos_iommu_owner *owner; + struct sysmmu_list_data *list_data; if (!master) { dev_err(dev, %s: mmu-master '%s' not found\n, __func__, node-name); - return -EINVAL; + ret = -EINVAL; + goto err; } - if (master-dev.archdata.iommu != NULL) { - dev_err(dev, %s: '%s' is master of other MMU\n, - __func__, node-name); - return -EINVAL; + owner = master-dev.archdata.iommu; + if (!owner) { + owner = devm_kzalloc(dev, sizeof(*owner), GFP_KERNEL); + if (!owner) { + dev_err(dev, + %s: Failed to allocate owner structure\n, + __func__); + ret = -ENOMEM; + goto err; + } + + INIT_LIST_HEAD(owner-mmu_list); + INIT_LIST_HEAD(owner-client); + owner-dev = master-dev; + spin_lock_init(owner-lock); + + master-dev.archdata.iommu = owner; } + list_data = devm_kzalloc(dev, sizeof(*list_data), GFP_KERNEL); + if (!list_data) { + dev_err(dev, + %s: Failed to allocate sysmmu_list_data\n, + __func__); + ret = -ENOMEM; + goto err; + } + + INIT_LIST_HEAD(list_data-entry); + list_data-sysmmu = dev; + /* -* archdata.iommu will be initialized with exynos_iommu_client -* in sysmmu_hook_driver_register(). +* System MMUs are attached in the order of the presence +* in device tree */ - master-dev.archdata.iommu = dev; + list_add_tail(list_data-entry, owner-mmu_list); } - data-sysmmu = dev; - rwlock_init(data-lock); + return 0; +err: + while ((node = of_parse_phandle(dev-of_node, mmu-masters, i++))) { Don't we need to reinitialize variable 'i' here before using? i = 0; Oh. You are right. Thanks. + struct platform_device *master = of_find_device_by_node(node); + struct exynos_iommu_owner *owner; + struct sysmmu_list_data *list_data; - platform_set_drvdata(pdev, data); + if (!master) + continue; - pm_runtime_enable(dev); - data-runtime_active = !pm_runtime_enabled(dev); + owner = master-dev.archdata.iommu; + if (!owner) + continue; - dev_dbg(dev, Probed and initialized\n); - return 0; + for_each_sysmmu_list(owner-dev, list_data) { + if (list_data-sysmmu == dev) { + list_del(list_data-entry); + kfree(list_data
Re: [PATCH v11 24/27] iommu/exynos: use exynos-iommu specific typedef
On Tue, 18 Mar 2014 18:33:20 -0700, Grant Grundler wrote: On Thu, Mar 13, 2014 at 10:13 PM, Cho KyongHo pullip@samsung.com wrote: This commit introduces sysmmu_pte_t for page table entries and sysmmu_iova_t vor I/O virtual address that is manipulated by exynos-iommu driver. The purpose of the typedef is to remove dependencies to the driver code from the change of CPU architecture from 32 bit to 64 bit. hi Cho, I noticed this before but understood this code was only compiled for ILP-32 programming model. I'm assuming that is going to change in the not-to-distant future. Good. :) Thanks. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 103 ++ 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index e375501..6e716cc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -56,19 +56,19 @@ #define lv2ent_large(pent) ((*(pent) 3) == 1) #define section_phys(sent) (*(sent) SECT_MASK) -#define section_offs(iova) ((iova) 0xF) +#define section_offs(iova) ((sysmmu_iova_t)(iova) 0xF) The cast will mask abuses of iova. Define section_offs as a static function and GCC can type check iova parameter to make sure it's a sysmmu_iova_t. Thoughts? I was thinking ((iova) (sysmmu_iova_t) 0XF) might do what you want but it doesn't warn on abuse that I tried. I believe GCC knows the upper bits are being ignored. Thank you for advice. I agree that type checking by compiler will be more helpful as you mentioned. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 10/27] iommu/exynos: use managed device helper functions
On Wed, 19 Mar 2014 13:08:42 +0100, Tomasz Figa wrote: On 19.03.2014 10:01, Sachin Kamat wrote: On 19 March 2014 14:29, Cho KyongHo pullip@samsung.com wrote: On Tue, 18 Mar 2014 16:14:53 +0100, Tomasz Figa wrote: On 18.03.2014 12:09, Cho KyongHo wrote: On Fri, 14 Mar 2014 20:52:43 +0530, Sachin Kamat wrote: Hi KyongHo, On 14 March 2014 10:35, Cho KyongHo pullip@samsung.com wrote: This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo pullip@samsung.com --- [snip] + data-clk = devm_clk_get(dev, sysmmu); + if (IS_ERR(data-clk)) { + dev_info(dev, No gate clock found!\n); + data-clk = NULL; + } Why aren't you returning from here upon error? It is for the case of a System MMU which does not need clock gating. Are there really such cases? Yes. Especially in the case of initial stage of new SoC development. I have experianced some software workaround for H/W restriction needs prevention of clock gating for some devices. So aren't these basically some exceptions/hacks rather than the usual way of functioning of the device? This actually raises a good question, whether we really need to support such early development SoC versions in mainline. Another thing is that if you need to assure that a clock is ungated, you must acquire it and prepare_enable explicitly, so I don't think this kind of handling is correct. On early development step of a new SoC, clock related stuffs and some device drivers like display controller are usually developed in parallel. In that case, -ENOENT from clk_get() must not treated as an error. [PATCH v11 20/17] iommu/exynos: allow having multiple System MMUs for a master H/W patch distinguishes -ENOENT from other error values returned by devm_clk_get(). Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Wed, 19 Mar 2014 16:14:57 +0100, Tomasz Figa wrote: On 19.03.2014 14:20, Tomasz Figa wrote: On 19.03.2014 01:39, Cho KyongHo wrote: On Tue, 18 Mar 2014 15:26:48 +0100, Tomasz Figa wrote: On 18.03.2014 14:01, Cho KyongHo wrote: On Fri, 14 Mar 2014 17:12:03 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. NAK. A device driver should handle particular hardware instances separately, without abstracting a virtual hardware instance consisting of multiple physical ones. If such abstraction is needed, it should be done above the exynos-iommu driver, e.g. by something like iommu-composite driver that would aggregate several IOMMUs. Keep in mind that such IOMMUs in a group could be different, e.g. different Exynos SysMMU versions or even completely different IPs handled by different drivers. Still, I don't think there is a real need for such abstraction. Instead, related drivers shall be fixed to properly handle multiple memory masters and their IOMMUs. G2D, Scalers and FIMD of Exynos5420 has 2 System MMUs while aother SoC like Exynos5250 does not. I don't understand why you are negative to this approach. This is the simplest than the others. Let me show you an example. FIMC-IS driver just controls MCU in FIMC-IS subsystem and the firmware of the MCU controls all other peripherals in the subsystem. Each peripherals have their own System MMU. Moreover, the configuration of the peripherals varies according to the SoCs. If System MMU driver accepts multiple masters, everything is done in DT. But I worry that it is not easy if System MMU driver does not support multiple masters. I believe I have stated enough reasons why this kind of implementation is bad. I'm not going to waste time repeating myself. Your concerns presented above are valid, however they are not related to what is wrong with this patch. I have given you two proper ways to handle this, none should be forced upon particular IOMMU master drivers - their authors should have the chance to select the method that works best for them. I don't still understand why you think this patch is wrong. I think this is the best way not to think for all the driver developers about other things than their business logic. I agree, but one of the ways I proposed (an iommu-composite layer above the IOMMU low level drivers) doesn't add any extra responsibility of driver developers. Moreover, it's this kind of business logic in low level drivers that is adding more responsibility, because it introduces additional complexity and makes the driver harder to read, maintain and extend in future. This does not hurt anyone and I think this is good enough. Well, it is barely good enough. It is a good practice to make a low level driver handle a single device instance and this is how Linux driver model is designed. Moreover, a single device tree node _must_ represent a single hardware block, so you can't group multiple SysMMUs into a single device tree node. OK, you add nodes for single SysMMUs devices which is fine, sorry. I was under impression that one kernel device (struct device) corresponds to multiple SysMMUs, but this was before your patches, sorry. So one issue less, but it's still not good. Ok. Understood why you have mentioned such. Furthermore, if you force grouping of SysMMUs into a single virtual one, you enforce using the same address space for all masters of some particular hardware blocks, while potentially driver developers would like to separate them. Probably some clarification is needed. Your other patch adds: sysmmu_fimd0w04: sysmmu@1464 { compatible = samsung,sysmmu-v3.3; reg = 0x1464 0x1000; interrupt-parent = combiner; interrupts = 3 2; clock-names = sysmmu, master; clocks = clock 422, clock 421; samsung,power-domain = disp_pd; mmu-masters = fimd; }; sysmmu_fimd0w123: sysmmu@1468 { compatible = samsung,sysmmu-v3.3; reg = 0x1468 0x1000; interrupt-parent = combiner; interrupts = 3 0; clock-names = sysmmu, master; clocks = clock 423, clock 421; samsung
Re: [PATCH v11 17/27] iommu/exynos: remove calls to Runtime PM API functions
On Wed, 19 Mar 2014 09:54:39 -0700, Grant Grundler wrote: On Wed, Mar 19, 2014 at 6:12 AM, Tomasz Figa t.f...@samsung.com wrote: ... Device driver is not only for the scholarship but also for the real use. Huh? I'm not sure what kind of comment is this. I'm guessing Cho meant: This isn't an academic exercise - I have a real use case that requires reference counting. That is what I meant; Sorry for my poor English :-) Cho needs to be more specific about his Some driver needs enabling sysmmu example. Then others would understand why/when the reference counting is needed. Ie walk through a real driver that exists today that depends on reference counting. One of my recent experience is that a display controller (FIMD) driver of a SoC manages two different context of power management: One is turning on and off display screen (LCD) (which is as usual as previous SoCs) and the other is gating its internal clock including System MMU very frequently to reduce power consumption. Because System MMU driver holds its clock ungated while it is enabled, FIMD driver explicitely disable System MMU. Yes, well designed FIMD driver must care about balancing of disabling and enabling System MMU between different contexts. But the design of some complex driver may be poor in few features due to agressive development schedule sometimes. Please let me think about the counting. Now I also think the system mmu driver does not need to make an extra effort for some special cases. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 17/27] iommu/exynos: remove calls to Runtime PM API functions
On Wed, 19 Mar 2014 19:51:21 +0100, Tomasz Figa wrote: On 19.03.2014 19:37, Grant Grundler wrote: On Wed, Mar 19, 2014 at 10:30 AM, Tomasz Figa t.f...@samsung.com wrote: ... As I said, AFAIK the trend is to get rid of ordering by initcalls and make sure that drivers can handle missing dependencies properly, even for services such as DMA, GPIO, clocks and so on, which after all are provided by normal drivers like other. Ok - I'm not following the general kernel dev trends. initcall() levels are easy to understand and implement. So I would not be in a hurry to replace them. Well, initcall level is still a way to satisfy most of dependencies, i.e. all client devices with higher initcall levels will probe successfully. However the other case needs to be handled as well - in this case the IOMMU binding code needs to defer probe of client driver if respective IOMMU is not yet available. I now understand what is deferred probing you mentioned. However, I worry that many existing drivers are not ready for deferred probing. But still I wonder if System MMU driver need to be probed in the same initcall level. ps. I've written IOMMU support for four different IOMMUs on three operating systems (See drivers/parisc for two linux examples). But I still feel like I at best have 80% understanding of how this one is organized/works. Abstract descriptions and convoluted code have been handicapping me (and lack of time to dig further). Well, this is one of my concerns with this driver. It isn't easy to read (and so review, maintain, extend and debug found issues). My postscript comment was more to explain why I'm not confident in my opinion - not a reason to reject the patch series. I still consider the whole series as a step forward. But I'm not the expert here. I fully agree with you. Other than the issues mentioned in review, the patches are definitely a step forward. I'd even say that all the patches that have nothing to do with device tree could be merged in their current form and the code refined later. It doesn't mean that patches shouldn't be reviewed now and issues spotted reported, even if they could be fixed later - this is for the IOMMU subsystem maintainer to decide. As for patches related to DT support, more care needs to be taken, as bindings should be designed with stability in mind, so the refining process should happen at review stage. Right now, with ~30 patches posted by the exynos iommu (official?) maintainer, no one else who has a clue will attempt to fix or clean up those kinds of problems. i.e. it's useful to enable others to fix what are essentially unspecified design pattern issues. Agreed. Let me wait for the way of binding System MMU and its master developed by Marek. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Thu, 20 Mar 2014 11:54:58 +0100, Tomasz Figa wrote: On 20.03.2014 11:22, Cho KyongHo wrote: On Wed, 19 Mar 2014 16:14:57 +0100, Tomasz Figa wrote: On 19.03.2014 14:20, Tomasz Figa wrote: On 19.03.2014 01:39, Cho KyongHo wrote: On Tue, 18 Mar 2014 15:26:48 +0100, Tomasz Figa wrote: On 18.03.2014 14:01, Cho KyongHo wrote: On Fri, 14 Mar 2014 17:12:03 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. NAK. A device driver should handle particular hardware instances separately, without abstracting a virtual hardware instance consisting of multiple physical ones. If such abstraction is needed, it should be done above the exynos-iommu driver, e.g. by something like iommu-composite driver that would aggregate several IOMMUs. Keep in mind that such IOMMUs in a group could be different, e.g. different Exynos SysMMU versions or even completely different IPs handled by different drivers. Still, I don't think there is a real need for such abstraction. Instead, related drivers shall be fixed to properly handle multiple memory masters and their IOMMUs. G2D, Scalers and FIMD of Exynos5420 has 2 System MMUs while aother SoC like Exynos5250 does not. I don't understand why you are negative to this approach. This is the simplest than the others. Let me show you an example. FIMC-IS driver just controls MCU in FIMC-IS subsystem and the firmware of the MCU controls all other peripherals in the subsystem. Each peripherals have their own System MMU. Moreover, the configuration of the peripherals varies according to the SoCs. If System MMU driver accepts multiple masters, everything is done in DT. But I worry that it is not easy if System MMU driver does not support multiple masters. I believe I have stated enough reasons why this kind of implementation is bad. I'm not going to waste time repeating myself. Your concerns presented above are valid, however they are not related to what is wrong with this patch. I have given you two proper ways to handle this, none should be forced upon particular IOMMU master drivers - their authors should have the chance to select the method that works best for them. I don't still understand why you think this patch is wrong. I think this is the best way not to think for all the driver developers about other things than their business logic. I agree, but one of the ways I proposed (an iommu-composite layer above the IOMMU low level drivers) doesn't add any extra responsibility of driver developers. Moreover, it's this kind of business logic in low level drivers that is adding more responsibility, because it introduces additional complexity and makes the driver harder to read, maintain and extend in future. This does not hurt anyone and I think this is good enough. Well, it is barely good enough. It is a good practice to make a low level driver handle a single device instance and this is how Linux driver model is designed. Moreover, a single device tree node _must_ represent a single hardware block, so you can't group multiple SysMMUs into a single device tree node. OK, you add nodes for single SysMMUs devices which is fine, sorry. I was under impression that one kernel device (struct device) corresponds to multiple SysMMUs, but this was before your patches, sorry. So one issue less, but it's still not good. Ok. Understood why you have mentioned such. Furthermore, if you force grouping of SysMMUs into a single virtual one, you enforce using the same address space for all masters of some particular hardware blocks, while potentially driver developers would like to separate them. Probably some clarification is needed. Your other patch adds: sysmmu_fimd0w04: sysmmu@1464 { compatible = samsung,sysmmu-v3.3; reg = 0x1464 0x1000; interrupt-parent = combiner; interrupts = 3 2; clock-names = sysmmu, master; clocks = clock 422, clock 421; samsung,power-domain = disp_pd; mmu-masters = fimd; }; sysmmu_fimd0w123: sysmmu@1468 { compatible = samsung,sysmmu-v3.3; reg = 0x1468 0x1000; interrupt-parent = combiner; interrupts = 3 0; clock-names = sysmmu, master
Re: [PATCH v11 13/27] iommu/exynos: support for device tree
On Tue, 18 Mar 2014 16:46:24 +0530, Sachin Kamat wrote: On 18 March 2014 16:44, Cho KyongHo pullip@samsung.com wrote: On Fri, 14 Mar 2014 20:57:42 +0530, Sachin Kamat wrote: Hi KyongHo, On 14 March 2014 10:39, Cho KyongHo pullip@samsung.com wrote: -static struct platform_driver exynos_sysmmu_driver = { - .probe = exynos_sysmmu_probe, - .driver = { +#ifdef CONFIG_OF This is not needed as Exynos is DT only platform. Yes, but I hesitate to remove that. What is the reason for your hesitation? :) Actually, no reason because you mentioned that Exynos is DT only platofrm. I will remove that. Thank you. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 10/27] iommu/exynos: use managed device helper functions
On Wed, 19 Mar 2014 08:59:09 +0900, Jingoo Han wrote: On Wednesday, March 19, 2014 12:12 AM, Tomasz Figa wrote: On 18.03.2014 11:38, Cho KyongHo wrote: On Fri, 14 Mar 2014 14:28:36 +0100, Tomasz Figa wrote: On 14.03.2014 06:05, Cho KyongHo wrote: This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 64 +- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 36e6b73..33b424d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c [.] res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - dev_dbg(dev, Unable to find IOMEM region\n); - ret = -ENOENT; - goto err_init; + dev_err(dev, Unable to find IOMEM region\n); + return -ENOENT; } No need to check for error and print message, because devm_ioremap_resource() already checks the passed resource and handles error cases. Yes but devm_ioremap_resource() just tells that the given 'res' is not correct. I think the message in the driver is more informative. The common practice used in Linux kernel is to not duplicate such messages. It is obvious that devm_ioremap_resource() printing such message is related to an IOMEM resource anyway, as you can't used it with other types of resources. +1 I agree with Tomasz Figa's opinion. These messages have been being removed from Linux kernel. Thank you. Ok. Thank you for the advice. Kyong Ho ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 10/27] iommu/exynos: use managed device helper functions
On Tue, 18 Mar 2014 16:14:53 +0100, Tomasz Figa wrote: On 18.03.2014 12:09, Cho KyongHo wrote: On Fri, 14 Mar 2014 20:52:43 +0530, Sachin Kamat wrote: Hi KyongHo, On 14 March 2014 10:35, Cho KyongHo pullip@samsung.com wrote: This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo pullip@samsung.com --- [snip] + data-clk = devm_clk_get(dev, sysmmu); + if (IS_ERR(data-clk)) { + dev_info(dev, No gate clock found!\n); + data-clk = NULL; + } Why aren't you returning from here upon error? It is for the case of a System MMU which does not need clock gating. Are there really such cases? Yes. Especially in the case of initial stage of new SoC development. I have experianced some software workaround for H/W restriction needs prevention of clock gating for some devices. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 13/27] iommu/exynos: support for device tree
On Tue, 18 Mar 2014 16:25:11 +0100, Tomasz Figa wrote: On 18.03.2014 11:52, Cho KyongHo wrote: On Fri, 14 Mar 2014 14:39:33 +0100, Tomasz Figa wrote: @@ -557,11 +558,23 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) return 0; } -static struct platform_driver exynos_sysmmu_driver = { - .probe = exynos_sysmmu_probe, - .driver = { +#ifdef CONFIG_OF +static struct of_device_id sysmmu_of_match[] __initconst = { + { .compatible = samsung,sysmmu-v1, }, + { .compatible = samsung,sysmmu-v2, }, + { .compatible = samsung,sysmmu-v3.1, }, + { .compatible = samsung,sysmmu-v3.2, }, + { .compatible = samsung,sysmmu-v3.3, }, Do you need all these compatible strings? I mean, are there any implementation differences that can't be identified by reading IP registers, such as REG_MMU_VERSION? Unfortunately, there is a SoC which overrides REG_MMU_VERSION with a value for RTL designers and it is not related to System MMU versions. OK. What about having a generic compatible string for Samsung SysMMU then, but an additional property that can override the version to account for such brokenness? If not provided, the version would be read from REG_MMU_VERSION. Yes it is one of possible idea. Let me think what better way is. Thank you. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 19/27] iommu/exynos: add support for power management subsystems.
On Tue, 18 Mar 2014 16:33:04 +0100, Tomasz Figa wrote: On 18.03.2014 12:23, Cho KyongHo wrote: On Fri, 14 Mar 2014 17:07:53 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: [snip] @@ -677,11 +679,40 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); pm_runtime_enable(dev); + data-runtime_active = !pm_runtime_enabled(dev); Hmm, this seems to be a bit misleading. The field is named runtime_active, but the assignment makes it true if PM runtime is _not_ enabled (i.e. inactive). Is this correct? I agree that it may lead misunderstood. data-runtime_active actually indicates if electric power is asserted to the System MMU. pm_runtime_enable() call must enable runtime pm for the given device. If runtime pm is not enabled although pm_runtime_enable() is called, CONFIG_PM_RUNTIME is not configured. Actually, it is replacible with if (IS_ENABLED(CONFIG_PM_RUNTIME)) data-runtime_active = true; I would keep it as !pm_runtime_enabled(dev), but rename the field to something more meaningful, like data-is_powered_on. That is good idea. thanks for advice. KyongHo. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 08/27] iommu/exynos: always use a single clock descriptor
On Fri, 14 Mar 2014 14:07:32 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:05, Cho KyongHo wrote: 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, there is no need to obtain two or more clock descriptors. This patch does much more than just making the driver use a single clock descriptor. Please update the subject and description accordingly. Ok. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 223 ++ 1 file changed, 72 insertions(+), 151 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 8dc7031..a4499b2 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -171,9 +171,8 @@ 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; @@ -294,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); @@ -354,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 (data-clk) I know this is already in the driver, but checking (struct clk *) for NULL is incorrect. NULL is a valid pointer for dummy clocks on platforms which do not provide particular clocks, to make this transparent to drivers. IS_ERR() should be used to check whether a clock pointer is valid. This patch is changing all the clock code anyway, so this change could be squashed into it to fix this. Ok. Thank you for the information. + clk_disable(data-clk); disabled
Re: [PATCH v11 10/27] iommu/exynos: use managed device helper functions
On Fri, 14 Mar 2014 14:28:36 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:05, Cho KyongHo wrote: This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 64 +- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 36e6b73..33b424d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -499,51 +499,48 @@ 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; + dev_err(dev, Unable to find IOMEM region\n); + return -ENOENT; } No need to check for error and print message, because devm_ioremap_resource() already checks the passed resource and handles error cases. Yes but devm_ioremap_resource() just tells that the given 'res' is not correct. I think the message in the driver is more informative. Thanks. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 13/27] iommu/exynos: support for device tree
On Fri, 14 Mar 2014 14:39:33 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:09, Cho KyongHo wrote: This commit adds device tree support for System MMU. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/Kconfig|5 ++--- drivers/iommu/exynos-iommu.c | 21 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index df56e4c..22af807 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -178,16 +178,15 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool Exynos IOMMU Support - depends on ARCH_EXYNOS EXYNOS_DEV_SYSMMU + depends on ARCH_EXYNOS select IOMMU_API + default n help Support for the IOMMU(System MMU) of Samsung Exynos application nit: There should be a white space before the opening parenthesis. Ok. :) processor family. This enables H/W multimedia accellerators to see typo: s/accellerators/accelerators/ Ok. non-linear physical memory chunks as a linear memory in their address spaces - If unsure, say N here. - config EXYNOS_IOMMU_DEBUG bool Debugging log for Exynos IOMMU depends on EXYNOS_IOMMU diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 33b424d..34feb04 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -26,6 +26,7 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/of.h #include asm/cacheflush.h #include asm/pgtable.h @@ -497,7 +498,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_unlock_irqrestore(data-lock, flags); } -static int exynos_sysmmu_probe(struct platform_device *pdev) +static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; struct device *dev = pdev-dev; @@ -557,11 +558,23 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) return 0; } -static struct platform_driver exynos_sysmmu_driver = { - .probe = exynos_sysmmu_probe, - .driver = { +#ifdef CONFIG_OF +static struct of_device_id sysmmu_of_match[] __initconst = { + { .compatible = samsung,sysmmu-v1, }, + { .compatible = samsung,sysmmu-v2, }, + { .compatible = samsung,sysmmu-v3.1, }, + { .compatible = samsung,sysmmu-v3.2, }, + { .compatible = samsung,sysmmu-v3.3, }, Do you need all these compatible strings? I mean, are there any implementation differences that can't be identified by reading IP registers, such as REG_MMU_VERSION? Unfortunately, there is a SoC which overrides REG_MMU_VERSION with a value for RTL designers and it is not related to System MMU versions. Thank you. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 15/27] iommu/exynos: use convenient macro to handle gate clocks
On Fri, 14 Mar 2014 22:27:59 +0530, Sachin Kamat wrote: Hi KyongHo, On 14 March 2014 19:13, Tomasz Figa t.f...@samsung.com wrote: Hi KyongHo, On 14.03.2014 06:09, Cho KyongHo wrote: exynos-iommu driver must care about master H/W's gate clock as well as System MMU's gate clock. To enhance readability of the source code, macros to gate/ungate those clocks are defined. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 34 ++ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 71e77f1..cef62d0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -101,6 +101,16 @@ #define REG_PB1_SADDR 0x054 #define REG_PB1_EADDR 0x058 +#define __clk_gate_ctrl(data, clk, en) do {\ + if (data-clk) \ + clk_##en##able(data-clk); \ + } while (0) + +#define __sysmmu_clk_enable(data) __clk_gate_ctrl(data, clk, en) +#define __sysmmu_clk_disable(data) __clk_gate_ctrl(data, clk, dis) +#define __master_clk_enable(data) __clk_gate_ctrl(data, clk_master, en) +#define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) + I'd say that such macros only obfuscate code, without any gains, as you can see in diffstat - this patch adds more lines than it removes. Please drop this change. I agree with Tomasz here. Are you concerning about using macros or more insertions than deletions? The deletions in this patch are only clk_enable() and clk_disable() but they must be if (!IS_ERR(clk)) clk_enable(clk) and if (!IS_ERR(clk)) clk_disable(clk). I think use of macro is fancier in that case. Thank you. KyongHo. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 13/27] iommu/exynos: support for device tree
On Fri, 14 Mar 2014 20:57:42 +0530, Sachin Kamat wrote: Hi KyongHo, On 14 March 2014 10:39, Cho KyongHo pullip@samsung.com wrote: This commit adds device tree support for System MMU. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/Kconfig|5 ++--- drivers/iommu/exynos-iommu.c | 21 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index df56e4c..22af807 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -178,16 +178,15 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool Exynos IOMMU Support - depends on ARCH_EXYNOS EXYNOS_DEV_SYSMMU + depends on ARCH_EXYNOS select IOMMU_API + default n This is not needed as it is the default choice. OK. help Support for the IOMMU(System MMU) of Samsung Exynos application processor family. This enables H/W multimedia accellerators to see non-linear physical memory chunks as a linear memory in their address spaces - If unsure, say N here. - config EXYNOS_IOMMU_DEBUG bool Debugging log for Exynos IOMMU depends on EXYNOS_IOMMU diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 33b424d..34feb04 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -26,6 +26,7 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/of.h #include asm/cacheflush.h #include asm/pgtable.h @@ -497,7 +498,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_unlock_irqrestore(data-lock, flags); } -static int exynos_sysmmu_probe(struct platform_device *pdev) +static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; struct device *dev = pdev-dev; @@ -557,11 +558,23 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) return 0; } -static struct platform_driver exynos_sysmmu_driver = { - .probe = exynos_sysmmu_probe, - .driver = { +#ifdef CONFIG_OF This is not needed as Exynos is DT only platform. Yes, but I hesitate to remove that. +static struct of_device_id sysmmu_of_match[] __initconst = { + { .compatible = samsung,sysmmu-v1, }, + { .compatible = samsung,sysmmu-v2, }, + { .compatible = samsung,sysmmu-v3.1, }, + { .compatible = samsung,sysmmu-v3.2, }, + { .compatible = samsung,sysmmu-v3.3, }, + { }, +}; +#endif + +static struct platform_driver exynos_sysmmu_driver __refdata = { + .probe = exynos_sysmmu_probe, + .driver = { .owner = THIS_MODULE, .name = exynos-sysmmu, + .of_match_table = of_match_ptr(sysmmu_of_match), of_match_ptr is not needed for the same reason as above. Ditto. Thanks. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 19/27] iommu/exynos: add support for power management subsystems.
On Fri, 14 Mar 2014 17:07:53 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: This adds support for Suspend to RAM and Runtime Power Management. Since System MMU is located in the same local power domain of its master H/W, System MMU must be initialized before it is working if its power domain was ever turned off. TLB invalidation according to unmapping on page tables must also be performed while power domain is turned on. This patch ensures that resume and runtime_resume(restore_state) functions in this driver is called before the calls to resume and runtime_resume callback functions in the drivers of master H/Ws. Likewise, suspend and runtime_suspend(save_state) functions in this driver is called after the calls to suspend and runtime_suspend in the drivers of master H/Ws. In order to get benefit of this support, the master H/W and its System MMU must resides in the same power domain in terms of Linux kernel. If a master H/W does not use generic I/O power domain, its driver must call iommu_attach_device() after its local power domain is turned on, iommu_detach_device before turned off. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 220 ++ 1 file changed, 201 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9037da0..84ba29a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -28,6 +28,7 @@ #include linux/export.h #include linux/of.h #include linux/of_platform.h +#include linux/pm_domain.h #include linux/notifier.h #include asm/cacheflush.h @@ -203,6 +204,7 @@ struct sysmmu_drvdata { int activations; rwlock_t lock; struct iommu_domain *domain; + bool runtime_active; unsigned long pgtable; }; @@ -388,7 +390,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data-pgtable = 0; data-domain = NULL; - __sysmmu_disable_nocount(data); + if (data-runtime_active) + __sysmmu_disable_nocount(data); dev_dbg(data-sysmmu, Disabled\n); } else { @@ -449,7 +452,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, data-pgtable = pgtable; data-domain = domain; - __sysmmu_enable_nocount(data); + if (data-runtime_active) + __sysmmu_enable_nocount(data); dev_dbg(data-sysmmu, Enabled\n); } else { @@ -534,13 +538,11 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, data = dev_get_drvdata(owner-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { - unsigned int maj; + if (is_sysmmu_active(data) data-runtime_active) { unsigned int num_inv = 1; __master_clk_enable(data); - maj = __raw_readl(data-sfrbase + REG_MMU_VERSION); /* * L2TLB invalidation required * 4KB page: 1 invalidation @@ -551,7 +553,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, * 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 */ + if (__sysmmu_version(data, NULL) == 2) /* major version number */ num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data-sfrbase)) { @@ -576,7 +578,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) data = dev_get_drvdata(owner-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) data-runtime_active) { __master_clk_enable(data); if (sysmmu_block(data-sfrbase)) { __sysmmu_tlb_invalidate(data-sfrbase); @@ -677,11 +679,40 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); pm_runtime_enable(dev); + data-runtime_active = !pm_runtime_enabled(dev); Hmm, this seems to be a bit misleading. The field is named runtime_active, but the assignment makes it true if PM runtime is _not_ enabled (i.e. inactive). Is this correct? I agree that it may lead misunderstood. data-runtime_active actually indicates if electric power is asserted to the System MMU. pm_runtime_enable() call must enable runtime pm for the given device. If runtime pm is not enabled although pm_runtime_enable() is called, CONFIG_PM_RUNTIME is not configured. Actually, it is replacible with if (IS_ENABLED(CONFIG_PM_RUNTIME)) data-runtime_active = true; dev_dbg(dev, Probed
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Fri, 14 Mar 2014 17:12:03 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. NAK. A device driver should handle particular hardware instances separately, without abstracting a virtual hardware instance consisting of multiple physical ones. If such abstraction is needed, it should be done above the exynos-iommu driver, e.g. by something like iommu-composite driver that would aggregate several IOMMUs. Keep in mind that such IOMMUs in a group could be different, e.g. different Exynos SysMMU versions or even completely different IPs handled by different drivers. Still, I don't think there is a real need for such abstraction. Instead, related drivers shall be fixed to properly handle multiple memory masters and their IOMMUs. G2D, Scalers and FIMD of Exynos5420 has 2 System MMUs while aother SoC like Exynos5250 does not. I don't understand why you are negative to this approach. This is the simplest than the others. Let me show you an example. FIMC-IS driver just controls MCU in FIMC-IS subsystem and the firmware of the MCU controls all other peripherals in the subsystem. Each peripherals have their own System MMU. Moreover, the configuration of the peripherals varies according to the SoCs. If System MMU driver accepts multiple masters, everything is done in DT. But I worry that it is not easy if System MMU driver does not support multiple masters. Thank you. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Tue, 18 Mar 2014 15:26:48 +0100, Tomasz Figa wrote: On 18.03.2014 14:01, Cho KyongHo wrote: On Fri, 14 Mar 2014 17:12:03 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:10, Cho KyongHo wrote: Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. NAK. A device driver should handle particular hardware instances separately, without abstracting a virtual hardware instance consisting of multiple physical ones. If such abstraction is needed, it should be done above the exynos-iommu driver, e.g. by something like iommu-composite driver that would aggregate several IOMMUs. Keep in mind that such IOMMUs in a group could be different, e.g. different Exynos SysMMU versions or even completely different IPs handled by different drivers. Still, I don't think there is a real need for such abstraction. Instead, related drivers shall be fixed to properly handle multiple memory masters and their IOMMUs. G2D, Scalers and FIMD of Exynos5420 has 2 System MMUs while aother SoC like Exynos5250 does not. I don't understand why you are negative to this approach. This is the simplest than the others. Let me show you an example. FIMC-IS driver just controls MCU in FIMC-IS subsystem and the firmware of the MCU controls all other peripherals in the subsystem. Each peripherals have their own System MMU. Moreover, the configuration of the peripherals varies according to the SoCs. If System MMU driver accepts multiple masters, everything is done in DT. But I worry that it is not easy if System MMU driver does not support multiple masters. I believe I have stated enough reasons why this kind of implementation is bad. I'm not going to waste time repeating myself. Your concerns presented above are valid, however they are not related to what is wrong with this patch. I have given you two proper ways to handle this, none should be forced upon particular IOMMU master drivers - their authors should have the chance to select the method that works best for them. I don't still understand why you think this patch is wrong. I think this is the best way not to think for all the driver developers about other things than their business logic. This does not hurt anyone and I think this is good enough. If you want to provide another layer between master device and system mmu as you mentioned, you do that. This patch does not restrict it. Regards, KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 17/27] iommu/exynos: remove calls to Runtime PM API functions
On Tue, 18 Mar 2014 16:09:50 +0100, Tomasz Figa wrote: On 18.03.2014 10:56, Cho KyongHo wrote: On Fri, 14 Mar 2014 13:59:00 +0100, Tomasz Figa wrote: Hi KyongHo, On 14.03.2014 06:08, Cho KyongHo wrote: [snip] -static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) +static void __sysmmu_disable_nocount(struct sysmmu_drvdata *data) If you are changing the names anyway, it would be probably a good idea to reduce code obfuscation a bit and drop the underscores from beginnings of function names. Also I'd suggest keeping the exynos_ prefix. Thanks for the suggestion. __exynos_sysmmu_disable is splitted into 2 functions: __sysmmu_disable and __sysmmu_disable_nocount. I agree with you that it is good idea to reduce code obfuscation but I don't think dropping beginning underscores of function names reduces obfuscation. Well, if you are ending up with a function like __sysmmu_enable_nocount() below with every line starting with two underscores, do you think this improves code readability? Of course this is a minor issue, but let's keep some code quality level in Linux kernel. Ok. understood what your are concerning about. { - unsigned long flags; - bool disabled = false; - - write_lock_irqsave(data-lock, flags); [snip] Here's the function mentioned above: + +static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) +{ + __master_clk_enable(data); + __sysmmu_clk_enable(data); + + __raw_writel(CTRL_BLOCK, data-sfrbase + REG_MMU_CTRL); + + __sysmmu_init_config(data); + + __sysmmu_set_ptbase(data-sfrbase, data-pgtable); + + __raw_writel(CTRL_ENABLE, data-sfrbase + REG_MMU_CTRL); + + __master_clk_disable(data); +} + [snip] @@ -629,7 +700,7 @@ err_pgtable: static void exynos_iommu_domain_destroy(struct iommu_domain *domain) { struct exynos_iommu_domain *priv = domain-priv; - struct sysmmu_drvdata *data; + struct exynos_iommu_owner *owner; unsigned long flags; int i; @@ -637,11 +708,14 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) spin_lock_irqsave(priv-lock, flags); - list_for_each_entry(data, priv-clients, node) { - while (!exynos_sysmmu_disable(data-dev)) + list_for_each_entry(owner, priv-clients, client) { + while (!exynos_sysmmu_disable(owner-dev)) ; /* until System MMU is actually disabled */ What about using list_for_each_entry_safe() and calling list_del_init() here directly? That require another variable to be defined. Is it a problem? That is not a problem. But I think using list_for_each_entry() is not a problem likewise. I just wanted to avoid that because I think it is prettier. Moreover, list_del_init() below the empty while() clause may make the source code readers misunderstood.. This raises another question, why the loop above is even needed. exynos_sysmmu_disable() should make sure that SYSMMU is actually disabled, without any need for looping like this. Some driver needs enabling sysmmu to be counted due to its complex structure. It can be also removed by the driver with an extra effort but the reality is important. Device driver is not only for the scholarship but also for the real use. } + while (!list_empty(priv-clients)) + list_del_init(priv-clients.next); + spin_unlock_irqrestore(priv-lock, flags); for (i = 0; i NUM_LV1ENTRIES; i++) [snip] +static int sysmmu_hook_driver_register(struct notifier_block *nb, + unsigned long val, + void *p) +{ + struct device *dev = p; + + switch (val) { + case BUS_NOTIFY_BIND_DRIVER: + { + struct exynos_iommu_owner *owner; Please move this variable to the top of the function and drop the braces around case blocks. I don't think it is required because this function is modified by the following patches. OK, if so, and similar issue is not present after further patches. + + /* No System MMU assigned. See exynos_sysmmu_probe(). */ + if (dev-archdata.iommu == NULL) + break; This looks strange... (see below) Also this looks racy. There are no guarantees about device probing order, so you may end up with master devices being probed before the IOMMUs. Deferred probing should be used to handle this correctly. System MMU driver must be probed earlier than the drivers of master devices because the drivers may want to use System MMU for their initial task. As I said, there are no guarantees about platform device probe order in Linux kernel. Code must be designed to check whether required dependencies are met and if not, deferred probing must be used. I told that System MMU driver must be probed earlier. That's why
Re: [PATCH v11 01/27] iommu/exynos: do not include removed header
On Fri, 14 Mar 2014 17:29:36 +0530, Sachin Kamat wrote: On 14 March 2014 17:19, Cho KyongHo pullip@samsung.com wrote: From: Sachin Kamat [mailto:sachin.ka...@linaro.org] Sent: Friday, March 14, 2014 7:00 PM On 14 March 2014 10:31, Cho KyongHo pullip@samsung.com wrote: 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. CC: Tomasz Figa t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0740189..4876d35 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/kernel.h This change doesn't look related to the patch subject/description. Yes. But it is simply added without any side-effect. Do you think it should be in a separate patch?. Actually, the added line is a redundant. If it is redundant, then you shouldn't be adding it. If it is required, then please mention about the need in the commit description if not a separate patch. Ok. Thanks for the advice. KyongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH v11 01/27] iommu/exynos: do not include removed header
From: Sachin Kamat [mailto:sachin.ka...@linaro.org] Sent: Friday, March 14, 2014 7:00 PM On 14 March 2014 10:31, Cho KyongHo pullip@samsung.com wrote: 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. CC: Tomasz Figa t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0740189..4876d35 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/kernel.h This change doesn't look related to the patch subject/description. Yes. But it is simply added without any side-effect. Do you think it should be in a separate patch?. Actually, the added line is a redundant. Regards, KyongHo. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 01/27] iommu/exynos: do not include removed header
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. CC: Tomasz Figa t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0740189..4876d35 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/kernel.h #include linux/io.h #include linux/interrupt.h #include linux/platform_device.h @@ -29,8 +30,6 @@ #include asm/cacheflush.h #include asm/pgtable.h -#include mach/sysmmu.h - /* We does not consider super section mapping (16MB) */ #define SECT_ORDER 20 #define LPAGE_ORDER 16 -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 04/27] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 31 ++- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4a74ed8..0d26aeb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -225,9 +225,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, @@ -477,7 +482,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); @@ -487,9 +493,24 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) if (is_sysmmu_active(data)) { int i; for (i = 0; i data-nsfrs; i++) { + unsigned int maj; + unsigned int num_inv = 1; + maj = __raw_readl(data-sfrbases[i] + 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-sfrbases[i])) { __sysmmu_tlb_invalidate_entry( - data-sfrbases[i], iova); + data-sfrbases[i], iova, num_inv); sysmmu_unblock(data-sfrbases[i]); } } @@ -999,7 +1020,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; -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 02/27] 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 t.f...@samsung.com Tested-by: Grant Grundler grund...@chromium.org Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4876d35..1c3a397 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -958,6 +958,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; } @@ -966,6 +967,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, BUG_ON(size LPAGE_SIZE); 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; -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 03/27] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 58 -- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 1c3a397..4a74ed8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -812,13 +812,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(__pa(pent)); *pgcounter = NUM_LV2ENTRIES; @@ -829,14 +834,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)); @@ -854,8 +866,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); @@ -864,7 +878,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; } @@ -892,7 +909,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; @@ -900,17 +917,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); @@ -924,6 +940,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); @@ -932,7 +949,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
[PATCH v11 05/27] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 16 1 file changed, 16 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0d26aeb..647fc46 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -244,13 +244,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) { @@ -424,15 +417,6 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, 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); } -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 06/27] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 34 -- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 647fc46..bee1bb1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -100,6 +100,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); @@ -726,7 +728,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(__va(lv2table_base(priv-pgtable + i))); + kmem_cache_free(lv2table_kmem_cache, + __va(lv2table_base(priv-pgtable + i))); free_pages((unsigned long)priv-pgtable, 2); free_pages((unsigned long)priv-lv2entcnt, 1); @@ -825,7 +828,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); @@ -855,8 +858,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; } @@ -1061,11 +1063,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); -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 07/27] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index bee1bb1..8dc7031 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -632,8 +632,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) __set_fault_handler(data, default_fault_handler); - if (dev-parent) - pm_runtime_enable(dev); + pm_runtime_enable(dev); dev_dbg(dev, (%s) Initialized\n, data-dbgname); return 0; -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 09/27] 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. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 32 +--- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a4499b2..36e6b73 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 %#lx\n, - data-dbgname, data-pgtable); + dev_err(data-sysmmu, Already enabled with page table %#lx\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); @@ -562,7 +556,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); -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 11/27] clk: exynos: add gate clock descriptions of System MMU
This adds gate clocks of all System MMUs and their master IPs that are not apeared in clk-exynos5250.c and clk-exynos5420.c Also fixes GATE_IP_ACP to 0x18800 and changed GATE_DA to GATE for System MMU clocks in clk-exynos4.c Signed-off-by: Cho KyongHo pullip@samsung.com --- .../devicetree/bindings/clock/exynos5250-clock.txt |3 +++ .../devicetree/bindings/clock/exynos5420-clock.txt |6 +- drivers/clk/samsung/clk-exynos5250.c |5 + drivers/clk/samsung/clk-exynos5420.c | 13 +++-- include/dt-bindings/clock/exynos5250.h |4 include/dt-bindings/clock/exynos5420.h |6 +- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt index 72ce617..67e50ba 100644 --- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt @@ -162,6 +162,9 @@ clock which they consume. g2d 345 mdma0346 smmu_mdma0 347 + smmu_tv 348 + smmu_fimd1 349 + smmu_2d 350 [Clock Muxes] diff --git a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt index 458f347..62dabc3 100644 --- a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt @@ -146,7 +146,8 @@ clock which they consume. hdmi 413 aclk300_disp1420 fimd1421 - smmu_fimd1 422 + smmu_fimd1m0 422 + smmu_fimd1m1 423 aclk166 430 mixer431 aclk266 440 @@ -172,12 +173,15 @@ clock which they consume. mdma0473 aclk333_g2d 480 g2d 481 + smmu_g2d 482 aclk333_432_gscl 490 smmu_3aa 491 smmu_fimcl0 492 smmu_fimcl1 493 smmu_fimcl3 494 fimc_lite3 495 + fimc_lite0 496 + fimc_lite1 497 aclk_g3d 500 g3d 501 smmu_mixer 502 diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index e7ee442..6605733 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -615,6 +615,11 @@ static struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { GATE(CLK_WDT, wdt, div_aclk66, GATE_IP_PERIS, 19, 0, 0), GATE(CLK_RTC, rtc, div_aclk66, GATE_IP_PERIS, 20, 0, 0), GATE(CLK_TMU, tmu, div_aclk66, GATE_IP_PERIS, 21, 0, 0), + GATE(CLK_SMMU_TV, smmu_tv, mout_aclk200_disp1_sub, + GATE_IP_DISP1, 2, 0, 0), + GATE(CLK_SMMU_FIMD1, smmu_fimd1, mout_aclk200_disp1_sub, + GATE_IP_DISP1, 8, 0, 0), + GATE(CLK_SMMU_2D, smmu_2d, div_aclk200, GATE_IP_ACP, 7, 0, 0), }; static struct samsung_pll_rate_table vpll_24mhz_tbl[] __initdata = { diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 60b2681..b58e4d3 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -82,6 +82,7 @@ #define GATE_BUS_PERIC10x10754 #define GATE_BUS_PERIS00x10760 #define GATE_BUS_PERIS10x10764 +#define GATE_IP_G2D0x08800 #define GATE_IP_GSCL0 0x10910 #define GATE_IP_GSCL1 0x10920 #define GATE_IP_MFC0x1092c @@ -707,6 +708,10 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_GSCL_WB, gscl_wb, aclk300_gscl, GATE_IP_GSCL1, 13, 0, 0), GATE(CLK_SMMU_FIMCL3, smmu_fimcl3,, aclk333_432_gscl, GATE_IP_GSCL1, 16, 0, 0), + GATE(CLK_FIMC_LITE0, fimc_lite0, aclk333_432_gscl, + GATE_IP_GSCL0, 5, 0, 0), + GATE(CLK_FIMC_LITE1, fimc_lite1, aclk333_432_gscl, + GATE_IP_GSCL0, 6, 0, 0), GATE(CLK_FIMC_LITE3, fimc_lite3, aclk333_432_gscl, GATE_IP_GSCL1, 17, 0, 0), @@ -715,8 +720,10 @@ static struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(CLK_DP1, dp1, aclk200_disp1, GATE_IP_DISP1, 4, 0, 0), GATE(CLK_MIXER, mixer, aclk166, GATE_IP_DISP1, 5, 0, 0), GATE(CLK_HDMI, hdmi, aclk200_disp1, GATE_IP_DISP1, 6, 0, 0), - GATE(CLK_SMMU_FIMD1, smmu_fimd1, aclk300_disp1, GATE_IP_DISP1, 8, 0, - 0), + GATE(CLK_SMMU_FIMD1M0, smmu_fimd1m0, aclk300_disp1, GATE_IP_DISP1, + 7, 0, 0), + GATE(CLK_SMMU_FIMD1M1, smmu_fimd1m1, aclk300_disp1, GATE_IP_DISP1, + 8, 0, 0), GATE(CLK_MFC, mfc, aclk333
[PATCH v11 12/27] ARM: dts: Add description of System MMU of Exynos SoCs
This patch adds dts entries for the System MMU devices found on Exynos4 and Exynos5 SoC series and the System MMU binding documentation. CC: Rob Herring robherri...@gmail.com CC: Sylwester Nawrocki s.nawro...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 86 +++ arch/arm/boot/dts/exynos4.dtsi | 107 arch/arm/boot/dts/exynos4210.dtsi | 23 +- arch/arm/boot/dts/exynos4x12.dtsi | 77 +- arch/arm/boot/dts/exynos5250.dtsi | 266 +++- arch/arm/boot/dts/exynos5420.dtsi | 205 ++- 6 files changed, 758 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt diff --git a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt new file mode 100644 index 000..e4417bb --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt @@ -0,0 +1,86 @@ +Samsung Exynos IOMMU H/W, System MMU (System Memory Management Unit) + +Samsung's Exynos architecture contains System MMUs that enables scattered +physical memory chunks visible as a contiguous region to DMA-capable peripheral +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. + +System MMU is an IOMMU and supports identical translation table format to +ARMv7 translation tables with minimum set of page properties including access +permissions, shareability and security protection. In addition, System MMU has +another capabilities like L2 TLB or block-fetch buffers to minimize translation +latency. + +System MMUs are in many to one relation with peripheral devices, i.e. single +peripheral device might have multiple System MMUs (usually one for each bus +master), but one System MMU can handle transactions from only one peripheral +device. The relation between a System MMU and the peripheral device needs to be +defined in device node of the peripheral device. + +MFC in all Exynos SoCs and FIMD, M2M Scalers and G2D in Exynos5420 has 2 System +MMUs. +* MFC has one System MMU on its left and right bus. +* FIMD in Exynos5420 has one System MMU for window 0 and 4, the other system MMU + for window 1, 2 and 3. +* M2M Scalers and G2D in Exynos5420 has one System MMU on the read channel and + the other System MMU on the write channel. +The drivers must consider how to handle those System MMUs. One of the idea is +to implement child devices or sub-devices which are the client devices of the +System MMU. + +Required properties: +- compatible: Should be one of: + samsung,sysmmu-v1 + samsung,sysmmu-v2 + samsung,sysmmu-v3.1 + samsung,sysmmu-v3.2 + samsung,sysmmu-v3.3 + +- reg: A tuple of base address and size of System MMU registers. +- interrupt-parent: The phandle of the interrupt controller of System MMU +- interrupts: An interrupt specifier for interrupt signal of System MMU, + according to the format defined by a particular interrupt + controller. +- clock-names: Should be sysmmu if the System MMU is needed to gate its clock. + Please refer to the following documents: + Documentation/devicetree/bindings/clock/clock-bindings.txt + Documentation/devicetree/bindings/clock/exynos4-clock.txt + Documentation/devicetree/bindings/clock/exynos5250-clock.txt + Documentation/devicetree/bindings/clock/exynos5420-clock.txt + Optional master if the clock to the System MMU is gated by + another gate clock other than sysmmu. The System MMU driver + sets master the parent of sysmmu. + Exynos4 SoCs, there needs no master clockj. + Exynos5 SoCs, some System MMUs must have master clocks. +- clocks: Required if the System MMU is needed to gate its clock. + Please refer to the documents listed above. +- samsung,power-domain: Required if the System MMU is needed to gate its power. + Please refer to the following document: + Documentation/devicetree/bindings/arm/exynos/power_domain.txt +- mmu-masters: A phandle to device nodes representing the master for which + the System MMU can provide a translation. Any additional values + after the phandle will be ignored because a System MMU never + have two or more masters. #stream-id-cells specified in the + master's node will be also ignored. + If more than one phandle is specified, only the first phandle + will be treated. + +Examples: + gsc_0: gsc@13e0 { + compatible = samsung,exynos5-gsc; + reg = 0x13e0 0x1000; + interrupts = 0 85 0; + samsung,power-domain = pd_gsc
[PATCH v11 16/27] 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 grund...@chromium.org Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 80 +- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index cef62d0..3458349 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -136,16 +136,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, - unsigned long pgtable_base, unsigned long fault_addr); - static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { REG_PAGE_FAULT_ADDR, REG_AR_FAULT_ADDR, @@ -187,7 +177,6 @@ struct sysmmu_drvdata { int activations; rwlock_t lock; struct iommu_domain *domain; - sysmmu_fault_handler_t fault_handler; unsigned long pgtable; }; @@ -256,34 +245,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, -unsigned long pgtable_base, unsigned long fault_addr) +static void show_fault_information(const char *name, + enum exynos_sysmmu_inttype itype, + unsigned long 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 by %s(Page table base: 0x%lx)\n, + sysmmu_fault_name[itype], fault_addr, name, pgtable_base); ent = section_entry(__va(pgtable_base), fault_addr); pr_err(\tLv1 entry: 0x%lx\n, *ent); @@ -292,12 +264,6 @@ static int default_fault_handler(enum exynos_sysmmu_inttype itype, ent = page_entry(ent, fault_addr); pr_err(\t Lv2 entry: 0x%lx\n, *ent); } - - pr_err(Generating Kernel OOPS... because it is unrecoverable.\n); - - BUG(); - - return 0; } static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) @@ -320,24 +286,28 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) else addr = __raw_readl(data-sfrbase + fault_reg_offset[itype]); - if (data-domain) - 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-sfrbase + REG_PT_BASE_ADDR); - ret = data-fault_handler(itype, base, addr); + if (itype == SYSMMU_FAULT_UNKNOWN) { + pr_err(%s: Fault is not occurred by System MMU '%s'!\n, + __func__, dev_name(data-sysmmu)); + pr_err(%s: Please check if IRQ is correctly configured.\n, + __func__); + BUG(); + } else { + unsigned long base = + __raw_readl(data-sfrbase + REG_PT_BASE_ADDR); + show_fault_information(dev_name(data-sysmmu), + itype, base, addr); + if (data-domain) + ret = report_iommu_fault(data-domain, + data-dev, addr, itype); } - if (!ret (itype != SYSMMU_FAULT_UNKNOWN)) - __raw_writel(1 itype, data-sfrbase + REG_INT_CLEAR); - else - dev_dbg(data-sysmmu, %s is not handled.\n, - sysmmu_fault_name[itype]); + /* fault is not recovered by fault handler */ + BUG_ON(ret != 0); - if (itype
[PATCH v11 17/27] iommu/exynos: remove calls to Runtime PM API functions
Runtime power management by exynos-iommu driver independently from master H/W's runtime pm is not useful for power saving since attaching master H/W in probing time turns on its local power endlessly. Thus this removes runtime pm API calls. Runtime PM support is added in the following commits to exynos-iommu driver. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 369 +++--- 1 file changed, 238 insertions(+), 131 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3458349..6834556 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,8 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -111,6 +113,8 @@ #define __master_clk_enable(data) __clk_gate_ctrl(data, clk_master, en) #define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) +#define has_sysmmu(dev)(dev-archdata.iommu != NULL) + static struct kmem_cache *lv2table_kmem_cache; static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) @@ -159,6 +163,16 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +/* 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; + 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 { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -168,9 +182,8 @@ struct exynos_iommu_domain { }; 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 */ + struct device *master; /* Owner of system MMU */ void __iomem *sfrbase; struct clk *clk; struct clk *clk_master; @@ -239,7 +252,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -299,7 +311,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) itype, base, addr); if (data-domain) ret = report_iommu_fault(data-domain, - data-dev, addr, itype); + data-master, addr, itype); } /* fault is not recovered by fault handler */ @@ -316,116 +328,148 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) +static void __sysmmu_disable_nocount(struct sysmmu_drvdata *data) { - unsigned long flags; - bool disabled = false; - - write_lock_irqsave(data-lock, flags); - - if (!set_sysmmu_inactive(data)) - goto finish; - - __master_clk_enable(data); + clk_enable(data-clk_master); __raw_writel(CTRL_DISABLE, data-sfrbase + REG_MMU_CTRL); + __raw_writel(0, data-sfrbase + REG_MMU_CFG); __sysmmu_clk_disable(data); __master_clk_disable(data); +} - disabled = true; - data-pgtable = 0; - data-domain = NULL; -finish: - write_unlock_irqrestore(data-lock, flags); +static bool __sysmmu_disable(struct sysmmu_drvdata *data) +{ + bool disabled; + unsigned long flags; + + write_lock_irqsave(data-lock, flags); + + disabled = set_sysmmu_inactive(data); + + if (disabled) { + data-pgtable = 0; + data-domain = NULL; + + __sysmmu_disable_nocount(data); - if (disabled) dev_dbg(data-sysmmu, Disabled\n); - else - dev_dbg(data-sysmmu, %d times left to be disabled\n, + } else { + dev_dbg(data-sysmmu, %d times left to disable\n, data-activations); + } + + write_unlock_irqrestore(data-lock, flags); return disabled; } -/* __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
[PATCH v11 18/27] iommu/exynos: turn on useful configuration options
This turns on FLPD_CACHE, ACGEN and SYSSEL. FLPD_CACHE is a cache of 1st level page table entries that contains the address of a 2nd level page table to reduce latency of page table walking. ACGEN is architectural clock gating that gates clocks by System MMU itself if it is not active. Note that ACGEN is different from clock gating by the CPU. ACGEN just gates clocks to the internal logic of System MMU while clock gating by the CPU gates clocks to the System MMU. SYSSEL selects System MMU version in some Exynos SoCs. Some Exynos SoCs have an option to select System MMU versions exclusively because the SoCs adopts new System MMU version experimentally. This also always selects LRU as TLB replacement policy. Selecting TLB replacement policy is deprecated from System MMU 3.2. TLB in System MMU 3.3 has single TLB replacement policy, LRU. The bit of MMU_CFG selecting TLB replacement policy is remained as reserved. QoS value of page table walking is set to 15 (highst value). System MMU 3.3 can inherit QoS value of page table walking from its master H/W's transaction. This new feature is enabled by default and QoS value written to MMU_CFG is ignored. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 52 +- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 6834556..9037da0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -82,6 +82,13 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_LRU0x1 +#define CFG_QOS(n) ((n 0xF) 7) +#define CFG_MASK 0x0150 /* Selecting bit 0-15, 20, 22 and 24 */ +#define CFG_ACGEN (1 24) /* System MMU 3.3 only */ +#define CFG_SYSSEL (1 22) /* System MMU 3.2 only */ +#define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ + #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 #define REG_MMU_STATUS 0x008 @@ -98,6 +105,12 @@ #define REG_MMU_VERSION0x034 +#define MMU_MAJ_VER(val) ((val) 7) +#define MMU_MIN_VER(val) ((val) 0x7F) +#define MMU_RAW_VER(reg) (((reg) 21) ((1 11) - 1)) /* 11 bits */ + +#define MAKE_MMU_VER(maj, min) maj) 0xF) 7) | ((min) 0x7F)) + #define REG_PB0_SADDR 0x04C #define REG_PB0_EADDR 0x050 #define REG_PB1_SADDR 0x054 @@ -217,6 +230,29 @@ 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 unsigned int __sysmmu_version(struct sysmmu_drvdata *data, +unsigned int *minor) +{ + unsigned int ver = 0; + + ver = __raw_sysmmu_version(data); + if (ver MAKE_MMU_VER(3, 3)) { + dev_err(data-sysmmu, %s: version(%d.%d) is higher than 3.3\n, + __func__, MMU_MAJ_VER(ver), MMU_MIN_VER(ver)); + BUG(); + } + + if (minor) + *minor = MMU_MIN_VER(ver); + + return MMU_MAJ_VER(ver); +} + static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -367,7 +403,21 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) static void __sysmmu_init_config(struct sysmmu_drvdata *data) { - unsigned long cfg = 0; + unsigned long cfg = CFG_LRU | CFG_QOS(15); + int maj, min = 0; + + maj = __sysmmu_version(data, min); + if (maj == 3) { + if (min = 2) { + cfg |= CFG_FLPDCACHE; + if (min == 3) { + cfg |= CFG_ACGEN; + cfg = ~CFG_LRU; + } else { + cfg |= CFG_SYSSEL; + } + } + } __raw_writel(cfg, data-sfrbase + REG_MMU_CFG); } -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 15/27] iommu/exynos: use convenient macro to handle gate clocks
exynos-iommu driver must care about master H/W's gate clock as well as System MMU's gate clock. To enhance readability of the source code, macros to gate/ungate those clocks are defined. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 34 ++ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 71e77f1..cef62d0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -101,6 +101,16 @@ #define REG_PB1_SADDR 0x054 #define REG_PB1_EADDR 0x058 +#define __clk_gate_ctrl(data, clk, en) do {\ + if (data-clk) \ + clk_##en##able(data-clk); \ + } while (0) + +#define __sysmmu_clk_enable(data) __clk_gate_ctrl(data, clk, en) +#define __sysmmu_clk_disable(data) __clk_gate_ctrl(data, clk, dis) +#define __master_clk_enable(data) __clk_gate_ctrl(data, clk_master, en) +#define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) + static struct kmem_cache *lv2table_kmem_cache; static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) @@ -302,7 +312,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) WARN_ON(!is_sysmmu_active(data)); - clk_enable(data-clk_master); + __master_clk_enable(data); itype = (enum exynos_sysmmu_inttype) __ffs(__raw_readl(data-sfrbase + REG_INT_STATUS)); if (WARN_ON(!((itype = 0) (itype SYSMMU_FAULT_UNKNOWN @@ -329,7 +339,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data-sfrbase); - clk_disable(data-clk_master); + __master_clk_disable(data); read_unlock(data-lock); @@ -346,12 +356,12 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) if (!set_sysmmu_inactive(data)) goto finish; - clk_enable(data-clk_master); + __master_clk_enable(data); __raw_writel(CTRL_DISABLE, data-sfrbase + REG_MMU_CTRL); - clk_disable(data-clk); - clk_disable(data-clk_master); + __sysmmu_clk_disable(data); + __master_clk_disable(data); disabled = true; data-pgtable = 0; @@ -396,14 +406,14 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, data-pgtable = pgtable; - clk_enable(data-clk_master); - clk_enable(data-clk); + __master_clk_enable(data); + __sysmmu_clk_enable(data); __sysmmu_set_ptbase(data-sfrbase, pgtable); __raw_writel(CTRL_ENABLE, data-sfrbase + REG_MMU_CTRL); - clk_disable(data-clk_master); + __master_clk_disable(data); data-domain = domain; @@ -462,7 +472,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, unsigned int maj; unsigned int num_inv = 1; - clk_enable(data-clk_master); + __master_clk_enable(data); maj = __raw_readl(data-sfrbase + REG_MMU_VERSION); /* @@ -483,7 +493,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, num_inv); sysmmu_unblock(data-sfrbase); } - clk_disable(data-clk_master); + __master_clk_disable(data); } else { dev_dbg(data-sysmmu, Disabled. Skipping invalidating TLB.\n); } @@ -499,12 +509,12 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_lock_irqsave(data-lock, flags); if (is_sysmmu_active(data)) { - clk_enable(data-clk_master); + __master_clk_enable(data); if (sysmmu_block(data-sfrbase)) { __sysmmu_tlb_invalidate(data-sfrbase); sysmmu_unblock(data-sfrbase); } - clk_disable(data-clk_master); + __master_clk_disable(data); } else { dev_dbg(data-sysmmu, Disabled. Skipping invalidating TLB.\n); } -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 13/27] iommu/exynos: support for device tree
This commit adds device tree support for System MMU. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/Kconfig|5 ++--- drivers/iommu/exynos-iommu.c | 21 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index df56e4c..22af807 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -178,16 +178,15 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool Exynos IOMMU Support - depends on ARCH_EXYNOS EXYNOS_DEV_SYSMMU + depends on ARCH_EXYNOS select IOMMU_API + default n help Support for the IOMMU(System MMU) of Samsung Exynos application processor family. This enables H/W multimedia accellerators to see non-linear physical memory chunks as a linear memory in their address spaces - If unsure, say N here. - config EXYNOS_IOMMU_DEBUG bool Debugging log for Exynos IOMMU depends on EXYNOS_IOMMU diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 33b424d..34feb04 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -26,6 +26,7 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/of.h #include asm/cacheflush.h #include asm/pgtable.h @@ -497,7 +498,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_unlock_irqrestore(data-lock, flags); } -static int exynos_sysmmu_probe(struct platform_device *pdev) +static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; struct device *dev = pdev-dev; @@ -557,11 +558,23 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) return 0; } -static struct platform_driver exynos_sysmmu_driver = { - .probe = exynos_sysmmu_probe, - .driver = { +#ifdef CONFIG_OF +static struct of_device_id sysmmu_of_match[] __initconst = { + { .compatible = samsung,sysmmu-v1, }, + { .compatible = samsung,sysmmu-v2, }, + { .compatible = samsung,sysmmu-v3.1, }, + { .compatible = samsung,sysmmu-v3.2, }, + { .compatible = samsung,sysmmu-v3.3, }, + { }, +}; +#endif + +static struct platform_driver exynos_sysmmu_driver __refdata = { + .probe = exynos_sysmmu_probe, + .driver = { .owner = THIS_MODULE, .name = exynos-sysmmu, + .of_match_table = of_match_ptr(sysmmu_of_match), } }; -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 14/27] 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 t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 35 ++- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 34feb04..71e77f1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -173,6 +173,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; @@ -301,6 +302,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) WARN_ON(!is_sysmmu_active(data)); + 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 @@ -327,6 +329,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data-sfrbase); + clk_disable(data-clk_master); + read_unlock(data-lock); return IRQ_HANDLED; @@ -342,10 +346,12 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) if (!set_sysmmu_inactive(data)) goto finish; + clk_enable(data-clk_master); + __raw_writel(CTRL_DISABLE, data-sfrbase + REG_MMU_CTRL); - if (data-clk) - clk_disable(data-clk); + clk_disable(data-clk); + clk_disable(data-clk_master); disabled = true; data-pgtable = 0; @@ -388,15 +394,17 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - if (data-clk) - clk_enable(data-clk); - data-pgtable = pgtable; + clk_enable(data-clk_master); + clk_enable(data-clk); + __sysmmu_set_ptbase(data-sfrbase, pgtable); __raw_writel(CTRL_ENABLE, data-sfrbase + REG_MMU_CTRL); + clk_disable(data-clk_master); + data-domain = domain; dev_dbg(data-sysmmu, Enabled\n); @@ -453,6 +461,9 @@ 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; + + clk_enable(data-clk_master); + maj = __raw_readl(data-sfrbase + REG_MMU_VERSION); /* * L2TLB invalidation required @@ -472,6 +483,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, num_inv); sysmmu_unblock(data-sfrbase); } + clk_disable(data-clk_master); } else { dev_dbg(data-sysmmu, Disabled. Skipping invalidating TLB.\n); } @@ -487,10 +499,12 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_lock_irqsave(data-lock, flags); if (is_sysmmu_active(data)) { + clk_enable(data-clk_master); if (sysmmu_block(data-sfrbase)) { __sysmmu_tlb_invalidate(data-sfrbase); sysmmu_unblock(data-sfrbase); } + clk_disable(data-clk_master); } else { dev_dbg(data-sysmmu, Disabled. Skipping invalidating TLB.\n); } @@ -544,6 +558,17 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) return ret; } + data-clk_master = devm_clk_get(dev, master); + if (IS_ERR(data-clk_master)) + data-clk_master = NULL; + + 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); -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 19/27] iommu/exynos: add support for power management subsystems.
This adds support for Suspend to RAM and Runtime Power Management. Since System MMU is located in the same local power domain of its master H/W, System MMU must be initialized before it is working if its power domain was ever turned off. TLB invalidation according to unmapping on page tables must also be performed while power domain is turned on. This patch ensures that resume and runtime_resume(restore_state) functions in this driver is called before the calls to resume and runtime_resume callback functions in the drivers of master H/Ws. Likewise, suspend and runtime_suspend(save_state) functions in this driver is called after the calls to suspend and runtime_suspend in the drivers of master H/Ws. In order to get benefit of this support, the master H/W and its System MMU must resides in the same power domain in terms of Linux kernel. If a master H/W does not use generic I/O power domain, its driver must call iommu_attach_device() after its local power domain is turned on, iommu_detach_device before turned off. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 220 ++ 1 file changed, 201 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9037da0..84ba29a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -28,6 +28,7 @@ #include linux/export.h #include linux/of.h #include linux/of_platform.h +#include linux/pm_domain.h #include linux/notifier.h #include asm/cacheflush.h @@ -203,6 +204,7 @@ struct sysmmu_drvdata { int activations; rwlock_t lock; struct iommu_domain *domain; + bool runtime_active; unsigned long pgtable; }; @@ -388,7 +390,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data-pgtable = 0; data-domain = NULL; - __sysmmu_disable_nocount(data); + if (data-runtime_active) + __sysmmu_disable_nocount(data); dev_dbg(data-sysmmu, Disabled\n); } else { @@ -449,7 +452,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, data-pgtable = pgtable; data-domain = domain; - __sysmmu_enable_nocount(data); + if (data-runtime_active) + __sysmmu_enable_nocount(data); dev_dbg(data-sysmmu, Enabled\n); } else { @@ -534,13 +538,11 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, data = dev_get_drvdata(owner-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { - unsigned int maj; + if (is_sysmmu_active(data) data-runtime_active) { unsigned int num_inv = 1; __master_clk_enable(data); - maj = __raw_readl(data-sfrbase + REG_MMU_VERSION); /* * L2TLB invalidation required * 4KB page: 1 invalidation @@ -551,7 +553,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, * 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 */ + if (__sysmmu_version(data, NULL) == 2) /* major version number */ num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data-sfrbase)) { @@ -576,7 +578,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) data = dev_get_drvdata(owner-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) data-runtime_active) { __master_clk_enable(data); if (sysmmu_block(data-sfrbase)) { __sysmmu_tlb_invalidate(data-sfrbase); @@ -677,11 +679,40 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); pm_runtime_enable(dev); + data-runtime_active = !pm_runtime_enabled(dev); dev_dbg(dev, Probed and initialized\n); return 0; } +#ifdef CONFIG_PM_SLEEP +static int sysmmu_suspend(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + unsigned long flags; + read_lock_irqsave(data-lock, flags); + if (is_sysmmu_active(data) + (!pm_runtime_enabled(dev) || data-runtime_active)) + __sysmmu_disable_nocount(data); + read_unlock_irqrestore(data-lock, flags); + return 0; +} + +static int sysmmu_resume(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + unsigned long flags; + read_lock_irqsave(data-lock, flags); + if (is_sysmmu_active(data
[PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
Some master device descriptor like fimc-is which is an abstraction of very complex H/W may have multiple System MMUs. For those devices, the design of the link between System MMU and its master H/W is needed to be reconsidered. A link structure, sysmmu_list_data is introduced that provides a link to master H/W and that has a pointer to the device descriptor of a System MMU. Given a device descriptor of a master H/W, it is possible to traverse all System MMUs that must be controlled along with the master H/W. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 534 ++ 1 file changed, 333 insertions(+), 201 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 84ba29a..7489343 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -128,6 +128,10 @@ #define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) #define has_sysmmu(dev)(dev-archdata.iommu != NULL) +#define for_each_sysmmu_list(dev, list_data) \ + list_for_each_entry(list_data, \ + ((struct exynos_iommu_owner *)dev-archdata.iommu)-mmu_list, \ + entry) static struct kmem_cache *lv2table_kmem_cache; @@ -181,7 +185,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; - struct device *sysmmu; + struct list_head mmu_list; /* list of sysmmu_list_data.entry */ struct iommu_domain *domain; void *vmm_data; /* IO virtual memory manager's data */ spinlock_t lock;/* Lock to preserve consistency of System MMU */ @@ -195,6 +199,11 @@ struct exynos_iommu_domain { spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ }; +struct sysmmu_list_data { + struct list_head entry; /* entry of exynos_iommu_owner.mmu_list */ + struct device *sysmmu; +}; + struct sysmmu_drvdata { struct device *sysmmu; /* System MMU's device descriptor */ struct device *master; /* Owner of system MMU */ @@ -205,6 +214,7 @@ struct sysmmu_drvdata { rwlock_t lock; struct iommu_domain *domain; bool runtime_active; + bool suspended; unsigned long pgtable; }; @@ -471,28 +481,39 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, } /* __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. - */ +* +* 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, unsigned long pgtable, struct iommu_domain *domain) { int ret = 0; unsigned long flags; struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct sysmmu_drvdata *data; + struct sysmmu_list_data *list; 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) + for_each_sysmmu_list(dev, list) { + struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu); data-master = dev; + ret = __sysmmu_enable(data, pgtable, domain); + if (ret 0) { + struct sysmmu_list_data *iter; + for_each_sysmmu_list(dev, iter) { + if (iter-sysmmu == list-sysmmu) + break; + data = dev_get_drvdata(iter-sysmmu); + __sysmmu_disable(data); + data-master = NULL; + } + break; + } + } spin_unlock_irqrestore(owner-lock, flags); @@ -511,17 +532,19 @@ 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; + struct sysmmu_list_data *list; 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; + /* Every call to __sysmmu_disable() must return same result */ + for_each_sysmmu_list(dev, list) { + struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu
[PATCH v11 21/27] iommu/exynos: change rwlock to spinlock
Since acquiring read_lock is not more frequent than write_lock, it is not beneficial to use rwlock, this commit changes rwlock to spinlock. Reviewed-by: Grant Grundler grund...@chromium.org Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 39 --- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7489343..543ea2e0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -211,7 +211,7 @@ struct sysmmu_drvdata { struct clk *clk; struct clk *clk_master; int activations; - rwlock_t lock; + spinlock_t lock; struct iommu_domain *domain; bool runtime_active; bool suspended; @@ -334,11 +334,12 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) unsigned long addr = -1; int ret = -ENOSYS; - read_lock(data-lock); - WARN_ON(!is_sysmmu_active(data)); + spin_lock(data-lock); + __master_clk_enable(data); + itype = (enum exynos_sysmmu_inttype) __ffs(__raw_readl(data-sfrbase + REG_INT_STATUS)); if (WARN_ON(!((itype = 0) (itype SYSMMU_FAULT_UNKNOWN @@ -371,7 +372,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) __master_clk_disable(data); - read_unlock(data-lock); + spin_unlock(data-lock); return IRQ_HANDLED; } @@ -392,7 +393,7 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) bool disabled; unsigned long flags; - write_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); disabled = set_sysmmu_inactive(data); @@ -409,7 +410,7 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data-activations); } - write_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); return disabled; } @@ -457,7 +458,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, int ret = 0; unsigned long flags; - write_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (set_sysmmu_active(data)) { data-pgtable = pgtable; data-domain = domain; @@ -475,7 +476,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, if (WARN_ON(ret 0)) set_sysmmu_inactive(data); /* decrement count */ - write_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); return ret; } @@ -562,7 +563,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, for_each_sysmmu_list(dev, list) { struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu); - read_lock(data-lock); + spin_lock(data-lock); if (is_sysmmu_active(data) data-runtime_active) { unsigned int num_inv = 1; @@ -594,7 +595,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, iova); } - read_unlock(data-lock); + spin_unlock(data-lock); } spin_unlock_irqrestore(owner-lock, flags); @@ -610,7 +611,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) for_each_sysmmu_list(dev, list) { struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu); - read_lock(data-lock); + spin_lock(data-lock); if (is_sysmmu_active(data) data-runtime_active) { __master_clk_enable(data); if (sysmmu_block(data-sfrbase)) { @@ -621,7 +622,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) } else { dev_dbg(dev, disabled. Skipping TLB invalidation\n); } - read_unlock(data-lock); + spin_unlock(data-lock); } spin_unlock_irqrestore(owner-lock, flags); @@ -819,7 +820,7 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) if (!ret) { data-runtime_active = !pm_runtime_enabled(dev); data-sysmmu = dev; - rwlock_init(data-lock); + spin_lock_init(data-lock); platform_set_drvdata(pdev, data); } @@ -1269,12 +1270,12 @@ static int sysmmu_pm_genpd_suspend(struct device *dev) for_each_sysmmu_list(dev, list) { struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu); unsigned long flags; - write_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (!data-suspended is_sysmmu_active(data) (!pm_runtime_enabled(dev
[PATCH v11 22/27] iommu/exynos: add devices attached to the System MMU to an IOMMU group
Patch written by Antonios Motakis a.mota...@virtualopensystems.com: IOMMU groups are expected by certain users of the IOMMU API, e.g. VFIO. Since each device is behind its own System MMU, we can allocate a new IOMMU group for each device. Reviewd-by: Cho KyongHo pullip@samsung.com Signed-off-by: Antonios Motakis a.mota...@virtualopensystems.com --- drivers/iommu/exynos-iommu.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 543ea2e0..2beb197 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1213,6 +1213,32 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } +static int exynos_iommu_add_device(struct device *dev) +{ + struct iommu_group *group; + int ret; + + group = iommu_group_get(dev); + + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(dev, Failed to allocate IOMMU group\n); + return PTR_ERR(group); + } + } + + ret = iommu_group_add_device(group, dev); + iommu_group_put(group); + + return ret; +} + +static void exynos_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); +} + static struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1221,6 +1247,8 @@ static struct iommu_ops exynos_iommu_ops = { .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys, + .add_device = exynos_iommu_add_device, + .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 24/27] iommu/exynos: use exynos-iommu specific typedef
This commit introduces sysmmu_pte_t for page table entries and sysmmu_iova_t vor I/O virtual address that is manipulated by exynos-iommu driver. The purpose of the typedef is to remove dependencies to the driver code from the change of CPU architecture from 32 bit to 64 bit. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 103 ++ 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index e375501..6e716cc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -56,19 +56,19 @@ #define lv2ent_large(pent) ((*(pent) 3) == 1) #define section_phys(sent) (*(sent) SECT_MASK) -#define section_offs(iova) ((iova) 0xF) +#define section_offs(iova) ((sysmmu_iova_t)(iova) 0xF) #define lpage_phys(pent) (*(pent) LPAGE_MASK) -#define lpage_offs(iova) ((iova) 0x) +#define lpage_offs(iova) ((sysmmu_iova_t)(iova) 0x) #define spage_phys(pent) (*(pent) SPAGE_MASK) -#define spage_offs(iova) ((iova) 0xFFF) +#define spage_offs(iova) ((sysmmu_iova_t)(iova) 0xFFF) -#define lv1ent_offset(iova) ((iova) SECT_ORDER) -#define lv2ent_offset(iova) (((iova) 0xFF000) SPAGE_ORDER) +#define lv1ent_offset(iova) ((sysmmu_iova_t)(iova) SECT_ORDER) +#define lv2ent_offset(iova) (((sysmmu_iova_t)(iova) 0xFF000) SPAGE_ORDER) #define NUM_LV1ENTRIES 4096 -#define NUM_LV2ENTRIES 256 +#define NUM_LV2ENTRIES (SECT_SIZE / SPAGE_SIZE) -#define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(long)) +#define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(sysmmu_pte_t)) #define SPAGES_PER_LPAGE (LPAGE_SIZE / SPAGE_SIZE) @@ -133,16 +133,19 @@ ((struct exynos_iommu_owner *)dev-archdata.iommu)-mmu_list, \ entry) +typedef u32 sysmmu_iova_t; +typedef u32 sysmmu_pte_t; + static struct kmem_cache *lv2table_kmem_cache; -static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) +static sysmmu_pte_t *section_entry(sysmmu_pte_t *pgtable, sysmmu_iova_t iova) { return pgtable + lv1ent_offset(iova); } -static unsigned long *page_entry(unsigned long *sent, unsigned long iova) +static sysmmu_pte_t *page_entry(sysmmu_pte_t *sent, sysmmu_iova_t iova) { - return (unsigned long *)phys_to_virt( + return (sysmmu_pte_t *)phys_to_virt( lv2table_base(sent)) + lv2ent_offset(iova); } @@ -194,7 +197,7 @@ struct exynos_iommu_owner { struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ - unsigned long *pgtable; /* lv1 page table, 16KB */ + 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 */ @@ -288,7 +291,7 @@ static void __sysmmu_tlb_invalidate(void __iomem *sfrbase) } static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, - unsigned long iova, unsigned int num_inv) + sysmmu_iova_t iova, unsigned int num_inv) { unsigned int i; for (i = 0; i num_inv; i++) { @@ -299,7 +302,7 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, } static void __sysmmu_set_ptbase(void __iomem *sfrbase, - unsigned long pgd) + phys_addr_t pgd) { __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); @@ -308,22 +311,22 @@ static void __sysmmu_set_ptbase(void __iomem *sfrbase, static void show_fault_information(const char *name, enum exynos_sysmmu_inttype itype, - phys_addr_t pgtable_base, unsigned long fault_addr) + phys_addr_t pgtable_base, sysmmu_iova_t fault_addr) { - unsigned long *ent; + sysmmu_pte_t *ent; if ((itype = SYSMMU_FAULTS_NUM) || (itype SYSMMU_PAGEFAULT)) itype = SYSMMU_FAULT_UNKNOWN; - pr_err(%s occurred at %#lx by %s(Page table base: %pa)\n, + pr_err(%s occurred at %#x 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(\tLv1 entry: 0x%lx\n, *ent); + pr_err(\tLv1 entry: %#x\n, *ent); if (lv1ent_page(ent)) { ent = page_entry(ent, fault_addr); - pr_err(\t Lv2 entry: 0x%lx\n, *ent); + pr_err(\t Lv2 entry: %#x\n, *ent); } } @@ -332,7 +335,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) /* SYSMMU is in blocked when interrupt occurred. */ struct sysmmu_drvdata *data = dev_id; enum exynos_sysmmu_inttype itype; - unsigned long addr = -1; + sysmmu_iova_t addr = -1; int ret
[PATCH v11 26/27] iommu/exynos: apply workaround of caching fault page table entries
This patch contains 2 workaround for the System MMU v3.x. System MMU v3.2 and v3.3 has FLPD cache that caches first level page table entries to reduce page table walking latency. However, the FLPD cache is filled with a first level page table entry even though it is not accessed by a master H/W because System MMU v3.3 speculatively prefetches page table entries that may be accessed in the near future by the master H/W. The prefetched FLPD cache entries are not invalidated by iommu_unmap() because iommu_unmap() only unmaps and invalidates the page table entries that is mapped. Because exynos-iommu driver discards a second level page table when it needs to be replaced with another second level page table or a first level page table entry with 1MB mapping, It is required to invalidate FLPD cache that may contain the first level page table entry that points to the second level page table. Another workaround of System MMU v3.3 is initializing the first level page table entries with the second level page table which is filled with all zeros. This prevents System MMU prefetches 'fault' first level page table entry which may lead page fault on access to 16MiB wide. System MMU 3.x fetches consecutive page table entries by a page table walking to maximize bus utilization and to minimize TLB miss panelty. Unfortunately, functional problem is raised with the fetching behavior because it fetches 'fault' page table entries that specifies no translation information and that a valid translation information will be written to in the near future. The logic in the System MMU generates page fault with the cached fault entries that is no longer coherent with the page table which is updated. There is another workaround that must be implemented by I/O virtual memory manager: any two consecutive I/O virtual memory area must have a hole between the two that is larger than or equal to 128KiB. Also, next I/O virtual memory area must be started from the next 128KiB boundary. 0128K 256K 384K 512K |-|---|-|| |area1|.hole...|--- area2 - The constraint is depicted above. The size is selected by the calculation followed: - System MMU can fetch consecutive 64 page table entries at once 64 * 4KiB = 256KiB. This is the size between 128K ~ 384K of the above picture. This style of fetching is 'block fetch'. It fetches the page table entries predefined consecutive page table entries including the entry that is the reason of the page table walking. - System MMU can prefetch upto consecutive 32 page table entries. This is the size between 256K ~ 384K. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 164 +- 1 file changed, 147 insertions(+), 17 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 3d4dabb..4888383 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -47,8 +47,12 @@ #define LPAGE_MASK (~(LPAGE_SIZE - 1)) #define SPAGE_MASK (~(SPAGE_SIZE - 1)) -#define lv1ent_fault(sent) (((*(sent) 3) == 0) || ((*(sent) 3) == 3)) -#define lv1ent_page(sent) ((*(sent) 3) == 1) +#define lv1ent_fault(sent) ((*(sent) == ZERO_LV2LINK) || \ + ((*(sent) 3) == 0) || ((*(sent) 3) == 3)) +#define lv1ent_zero(sent) (*(sent) == ZERO_LV2LINK) +#define lv1ent_page_zero(sent) ((*(sent) 3) == 1) +#define lv1ent_page(sent) ((*(sent) != ZERO_LV2LINK) \ + ((*(sent) 3) == 1)) #define lv1ent_section(sent) ((*(sent) 3) == 2) #define lv2ent_fault(pent) ((*(pent) 3) == 0) @@ -137,6 +141,8 @@ typedef u32 sysmmu_iova_t; typedef u32 sysmmu_pte_t; static struct kmem_cache *lv2table_kmem_cache; +static sysmmu_pte_t *zero_lv2_table; +#define ZERO_LV2LINK mk_lv1ent_page(virt_to_phys(zero_lv2_table)) static sysmmu_pte_t *section_entry(sysmmu_pte_t *pgtable, sysmmu_iova_t iova) { @@ -538,6 +544,33 @@ static bool exynos_sysmmu_disable(struct device *dev) return disabled; } +static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, + sysmmu_iova_t iova) +{ + if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + __raw_writel(iova | 0x1, data-sfrbase + REG_MMU_FLUSH_ENTRY); +} + +static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, + sysmmu_iova_t iova) +{ + struct sysmmu_list_data *list; + + for_each_sysmmu_list(dev, list) { + unsigned long flags; + struct sysmmu_drvdata *data = dev_get_drvdata(list-sysmmu); + + __master_clk_enable(data); + + spin_lock_irqsave(data-lock, flags); + if (is_sysmmu_active(data) data-runtime_active
[PATCH v11 25/27] iommu/exynos: use simpler function to get MMU version
This commit changes the function to get MMU version simpler. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 30 ++ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 6e716cc..3d4dabb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -251,24 +251,6 @@ static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) return MMU_RAW_VER(__raw_readl(data-sfrbase + REG_MMU_VERSION)); } -static unsigned int __sysmmu_version(struct sysmmu_drvdata *data, -unsigned int *minor) -{ - unsigned int ver = 0; - - ver = __raw_sysmmu_version(data); - if (ver MAKE_MMU_VER(3, 3)) { - dev_err(data-sysmmu, %s: version(%d.%d) is higher than 3.3\n, - __func__, MMU_MAJ_VER(ver), MMU_MIN_VER(ver)); - BUG(); - } - - if (minor) - *minor = MMU_MIN_VER(ver); - - return MMU_MAJ_VER(ver); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -422,13 +404,13 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) static void __sysmmu_init_config(struct sysmmu_drvdata *data) { unsigned int cfg = CFG_LRU | CFG_QOS(15); - int maj, min = 0; + unsigned int ver; - maj = __sysmmu_version(data, min); - if (maj == 3) { - if (min = 2) { + ver = __raw_sysmmu_version(data); + if (MMU_MAJ_VER(ver) == 3) { + if (MMU_MIN_VER(ver) = 2) { cfg |= CFG_FLPDCACHE; - if (min == 3) { + if (MMU_MIN_VER(ver) == 3) { cfg |= CFG_ACGEN; cfg = ~CFG_LRU; } else { @@ -583,7 +565,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 (__sysmmu_version(data, NULL) == 2) + if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v11 27/27] iommu/exynos: enhanced error messages
Some redundant error message is removed and some error messages are changed to error level from debug level. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 23 +-- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4888383..b7f7731 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1012,7 +1012,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, dev_dbg(dev, %s: Detached IOMMU with pgtable %pa\n, __func__, pgtable); } else { - dev_dbg(dev, %s: No IOMMU is attached\n, __func__); + dev_err(dev, %s: No IOMMU is attached\n, __func__); } } @@ -1112,10 +1112,8 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, short *pgcnt) { if (size == SPAGE_SIZE) { - if (!lv2ent_fault(pent)) { - WARN(1, Trying mapping on 4KiB where mapping exists); + if (WARN_ON(!lv2ent_fault(pent))) return -EADDRINUSE; - } *pent = mk_lv2ent_spage(paddr); pgtable_flush(pent, pent + 1); @@ -1123,9 +1121,7 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, } else { /* size == LPAGE_SIZE */ int i; for (i = 0; i SPAGES_PER_LPAGE; i++, pent++) { - if (!lv2ent_fault(pent)) { - WARN(1, - Trying mapping on 64KiB where mapping exists); + if (WARN_ON(!lv2ent_fault(pent))) { if (i 0) memset(pent - i, 0, sizeof(*pent) * i); return -EADDRINUSE; @@ -1198,8 +1194,8 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, } if (ret) - pr_debug(%s: Failed to map iova %#x/%#zx bytes\n, - __func__, iova, size); + pr_err(%s: Failed(%d) to map %#zx bytes @ %#x\n, + __func__, ret, size, iova); spin_unlock_irqrestore(priv-pgtablelock, flags); @@ -1236,7 +1232,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, ent = section_entry(priv-pgtable, iova); if (lv1ent_section(ent)) { - if (size SECT_SIZE) { + if (WARN_ON(size SECT_SIZE)) { err_pgsize = SECT_SIZE; goto err; } @@ -1271,7 +1267,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, } /* lv1ent_large(ent) == true here */ - if (size LPAGE_SIZE) { + if (WARN_ON(size LPAGE_SIZE)) { err_pgsize = LPAGE_SIZE; goto err; } @@ -1290,9 +1286,8 @@ done: err: spin_unlock_irqrestore(priv-pgtablelock, flags); - WARN(1, - %s: Failed due to size(%#zx) @ %#x is smaller than page size %#zx\n, - __func__, size, iova, err_pgsize); + pr_err(%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n, + __func__, size, iova, err_pgsize); return 0; } -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH] iommu/exynos: Remove driver
-Original Message- From: Olof Johansson [mailto:o...@lixom.net] Sent: Friday, February 14, 2014 4:34 AM On Mon, Feb 10, 2014 at 10:21 PM, Kukjin Kim kgene@gmail.com wrote: Just adding KyongHo Cho. If he can fixup for this time, it would be best solution because he knows well than others, I think. It's not so much a matter of fixup for this time, it's a about having ownership of the driver, making sure it works (and keeps working if there is related development). The posted patches have not been followed through on and the result is a broken driver. :( I definitely appreciate his expertise, and we should make sure that he gets to review the code, but if someone else is able to spend time on reworking the driver (or rewriting a newer one) and maintaining it longer-term, then we should not stop them from doing so. And there is no reason to keep broken stale code in the kernel meanwhile. Thank you for your concerning. I also definitely agree with you that the driver must work. I am always concerning about it but it was not easy to make some time for the patches. I will continue to post the next version of patches, of course. I think it is not far from now to show it. Regards, Ky0ongHo ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v10 00/20] iommu/exynos: Fixes and Enhancements of System MMU driver with DT
On Fri, 01 Nov 2013 12:42:24 +0100, Joerg Roedel wrote: On Mon, Oct 07, 2013 at 10:52:12AM +0900, Cho KyongHo wrote: Patch summary: [PATCH 01/20] iommu/exynos: do not include removed header [PATCH 02/20] iommu/exynos: add missing cache flush for removed page table entries [PATCH 03/20] iommu/exynos: change error handling when page table update is failed [PATCH 04/20] iommu/exynos: fix L2TLB invalidation [PATCH 05/20] iommu/exynos: allocate lv2 page table from own slab [PATCH 06/20] iommu/exynos: always enable runtime PM [PATCH 07/20] iommu/exynos: always use a single clock descriptor [PATCH 08/20] iommu/exynos: remove dbgname from drvdata of a System MMU [PATCH 09/20] iommu/exynos: use managed device helper functions [PATCH 10/20] clk: exynos: add gate clock descriptions of System MMU [PATCH 11/20] ARM: dts: Add description of System MMU of Exynos SoCs [PATCH 12/20] iommu/exynos: support for device tree [PATCH 13/20] iommu/exynos: gating clocks of master H/W [PATCH 14/20] iommu/exynos: remove custom fault handler [PATCH 15/20] iommu/exynos: remove calls to Runtime PM API functions [PATCH 16/20] iommu/exynos: turn on useful configuration options [PATCH 17/20] iommu/exynos: add support for power management subsystems. [PATCH 18/20] iommu/exynos: change rwlock to spinlock [PATCH 19/20] iommu/exynos: return 0 if iommu_attach_device() successes [PATCH 20/20] iommu/exynos: add devices attached to the System MMU to an IOMMU group Diffstats: .../devicetree/bindings/clock/exynos5250-clock.txt | 28 + .../devicetree/bindings/clock/exynos5420-clock.txt |3 + .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 76 ++ arch/arm/boot/dts/exynos4.dtsi | 105 ++ arch/arm/boot/dts/exynos4210.dtsi | 21 + arch/arm/boot/dts/exynos4x12.dtsi | 82 ++ arch/arm/boot/dts/exynos5250.dtsi | 262 + arch/arm/boot/dts/exynos5420.dtsi | 296 ++ drivers/clk/samsung/clk-exynos5250.c | 49 +- drivers/clk/samsung/clk-exynos5420.c | 12 +- drivers/iommu/Kconfig |5 +- drivers/iommu/exynos-iommu.c | 1033 +--- 12 files changed, 1585 insertions(+), 387 deletions(-) What is the state of this series? It would be good to have some Acked-bys and/or Reviewed-bys on it. I am preparing next patches to apply Alex Williamson's comment that description of IOMMU's masters must be aligned with ARM SMMU. It is delayed due to my jobs in the office. I will post the next patche series in two weeks. Thanks. Joerg ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v10 20/20] iommu/exynos: add devices attached to the System MMU to an IOMMU group
On Thu, 10 Oct 2013 14:54:29 -0600, Alex Williamson wrote: On Mon, 2013-10-07 at 10:58 +0900, Cho KyongHo wrote: Patch written by Antonios Motakis a.mota...@virtualopensystems.com: IOMMU groups are expected by certain users of the IOMMU API, e.g. VFIO. Since each device is behind its own System MMU, we can allocate a new IOMMU group for each device. Reviewd-by: Cho KyongHo pullip@samsung.com Signed-off-by: Antonios Motakis a.mota...@virtualopensystems.com --- drivers/iommu/exynos-iommu.c | 28 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5025338..24505a0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1028,6 +1028,32 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } +static int exynos_iommu_add_device(struct device *dev) +{ + struct iommu_group *group; + int ret; + + group = iommu_group_get(dev); Seems reasonable, my only nit would be whether it's really an error to get a group back from the above call. If devices are always isolated and IOMMU groups are always singleton, it would be an error to find one already associated with the device. Right? Thanks, Do you mean that calling iommu_group_add_device() with the group that is returned by the above iommu_group_get() will return -EEXIST? I didn't think about that. Alex Thank you. KyongHo. + + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(dev, Failed to allocate IOMMU group\n); + return PTR_ERR(group); + } + } + + ret = iommu_group_add_device(group, dev); + iommu_group_put(group); + + return ret; +} + +static void exynos_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); +} + static struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1036,6 +1062,8 @@ static struct iommu_ops exynos_iommu_ops = { .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys, + .add_device = exynos_iommu_add_device, + .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v8 06/12] ARM: dts: Add description of System MMU of Exynos SoCs
On Mon, 07 Oct 2013 08:44:54 -0500, Rob Herring wrote: On Fri, Jul 26, 2013 at 6:28 AM, Cho KyongHo pullip@samsung.com wrote: Signed-off-by: Cho KyongHo pullip@samsung.com --- .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 103 +++ arch/arm/boot/dts/exynos4.dtsi | 122 arch/arm/boot/dts/exynos4210.dtsi | 25 ++ arch/arm/boot/dts/exynos4x12.dtsi | 76 + arch/arm/boot/dts/exynos5250.dtsi | 291 5 files changed, 617 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt diff --git a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt new file mode 100644 index 000..92f0a33 --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt @@ -0,0 +1,103 @@ +Samsung Exynos4210 IOMMU H/W, System MMU (System Memory Management Unit) + +Samsung's Exynos architecture contains System MMU that enables scattered +physical memory chunks visible as a contiguous region to DMA-capable peripheral +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. + +System MMU is a sort of IOMMU and support identical translation table format to +ARMv7 translation tables with minimum set of page properties including access +permissions, shareability and security protection. In addition, System MMU has +another capabilities like L2 TLB or block-fetch buffers to minimize translation +latency. + +A System MMU is dedicated to a single master peripheral device. Thus, it is +important to specify the correct System MMU in the device node of its master +device. Whereas a System MMU is dedicated to a master device, the master device +may have more than one System MMU. + +Required properties: +- compatible: Should be samsung,exynos4210-sysmmu +- reg: A tuple of base address and size of System MMU registers. +- interrupt-parent: The phandle of the interrupt controller of System MMU +- interrupts: A tuple of numbers that indicates the interrupt source. +- clock-names: Should be sysmmu if the System MMU is needed to gate its clock. + Please refer to the following documents: + Documentation/devicetree/bindings/clock/clock-bindings.txt + Documentation/devicetree/bindings/clock/exynos4-clock.txt + Documentation/devicetree/bindings/clock/exynos5250-clock.txt + Optional master if the clock to the System MMU is gated by + another gate clock other than sysmmu. The System MMU driver + sets master the parent of sysmmu. + Exynos4 SoCs, there needs no master clocks. + Exynos5 SoCs, some System MMUs must have master clocks. +- clocks: Required if the System MMU is needed to gate its clock. + Please refer to the documents listed above. +- samsung,power-domain: Required if the System MMU is needed to gate its power. + Please refer to the following document: + Documentation/devicetree/bindings/arm/exynos/power_domain.txt + +Required properties for the master peripheral devices: +- iommu: phandles to the System MMUs of the device You have not addressed my comments from the last version. We do not need 2 (or more) different ways to describe the connection between masters and iommu's. Use mmu-masters property here to describe the connection. Sorry, I forgot to reply. I just thought the meaning of your comment that it should be align with ARM System MMU. I now understand and it should be changed to mmu-masters property because it is now in the kernel. Thank you. Rob ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 00/20] iommu/exynos: Fixes and Enhancements of System MMU driver with DT
The current exynos-iommu(System MMU) driver does not work autonomously since it is lack of support for power management of peripheral blocks. For example, MFC device driver must ensure that its System MMU is disabled before MFC block is power-down not to invalidate IOTLB in the System MMU when I/O memory mapping is changed. Because a System MMU resides in the same H/W block, access to control registers of System MMU while the H/W block is turned off must be prohibited. This set of changes solves the above problem with setting each System MMUs as the parent of the device which owns the System MMU to receive the information when the device is turned off or turned on. Another big change to the driver is the support for devicetree. The bindings for System MMU is described in Documentation/devicetree/bindings/arm/samsung/system-mmu.txt In addition, this patchset also includes several bug fixes and enhancements of the current driver. Change log: v10: - Rebased on the following branches git.linaro.org/git-ro/people/mturquette/linux.git/clk-next git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git/for-next git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git/next git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/master (3.12-rc3) - Set parent clock to all System MMU clocks. - Add clock and DT descriptos for Exynos5420 - Modified error handling in exynos_iommu_init() - Split iommu/exynos: support for device tree patch into the following 6 patches iommu/exynos: handle only one instance of System MMU iommu/exynos: always enable runtime PM iommu/exynos: always use a single clock descriptor iommu/exynos: remove dbgname from drvdata of a System MMU iommu/exynos: use managed driver helper functions iommu/exynos: support for device tree - Remove 'interrupt-names' and 'status' properties from DT - Change n:1 relationship between master:System MMU into 1:1 relationship. - Removed custom fault handler and print the status of System MMU whenever System MMU fault is occurred. - Post Antonios Motakis's commit together: iommu/exynos: add devices attached to the System MMU to an IOMMU group v9: - Rebased on the following branches git.linaro.org/git-ro/people/mturquette/linux.git/clk-next git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git/samsung-next git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/master (3.11-rc4) - Split add bus notifier for registering System MMU into 5 patches - Call clk_prepare() that was missing in v8. - Fixed base address of sysmmu_tv in exynos4210.dtsi - BUG_ON() instead of return -EADDRINUSE when trying mapping on an mapped area - Moved camif_top to 317 in drivers/clk/samsung/clk-exynos5250.c - Removed 'iommu' property from 'codec'(mfc) node - Does not make 'master' clock to be the parent of 'sysmmu' clock. 'master' clock is enabled before accessing control registers of System MMU and disabled after the access. v8: - Reordered patch list: moved change rwloc to spinlock to the last. - Fixed remained bug in fix page table maintenance. - Always return 0 from exynos_iommu_attach_device(). - Removed prefetch buffer setting when System MMU is enabled due to the restriction of prefetch buffers: A prefetch buffer must not hit from more than one DMA. For instance with GScalers, if a single prefetch buffer is initialized with 0x0 ~ 0x and a GScaler works on source buffer at 0x1000 and target buffer @ 0x2000, the System MMU may be got deadlock. Clients must initialize prefetch buffers with custom function defined in exynos-iommu drivers whenever they need to enable prefetch buffers. - The clock of System MMU has no relationship with the clock of its master H/W. The clock of master H/W is always enabled when exynos-iommu driver needs to access MMIO area and disabled as soon as the access finishes. - Removed err_page variable used in exynos_iommu_unmap() in the previous patch fix page table maintenance. - Split a big patch add bus notifier for registering System MMU. Extracted the following 2 patches: 9/12 and 10/12. - And some additional fixes... v7: - Rebased on the stable 3.10 - Registered PM domains and gate clocks with DT - Changed connection method between a System MMU and its master H/W 'mmu-master' property in the node of System MMU -- 'iommu' property in the node of master H/W - Marking device descriptor of master H/W of a System MMU with bus notifier. - Power management (PM_RUNTIME, PM_SLEEP) of System MMUs with gpd_dev_ops of Generic IO Powerdomain. gpd_dev_ops are set to the master H/Ws before they are probed in the bus notifier. - Removed additional debugging features like debugfs entries and version names. - Removed support for advanced features of System MMU 3.2 and 3.3 the current IOMMU API cannot handle the feature (A kind of L2 TLB that fetches several consequence page table entries. It must be initialized by the driver of master H/W whenever it
[PATCH v10 02/20] 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 t.f...@samsung.com Tested-by: Grant Grundler grund...@chromium.org Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4876d35..1c3a397 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -958,6 +958,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; } @@ -966,6 +967,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, BUG_ON(size LPAGE_SIZE); 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; -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 01/20] iommu/exynos: do not include removed header
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. CC: Tomasz Figa t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0740189..4876d35 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/kernel.h #include linux/io.h #include linux/interrupt.h #include linux/platform_device.h @@ -29,8 +30,6 @@ #include asm/cacheflush.h #include asm/pgtable.h -#include mach/sysmmu.h - /* We does not consider super section mapping (16MB) */ #define SECT_ORDER 20 #define LPAGE_ORDER 16 -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 04/20] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 27 ++- 1 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 4a74ed8..cbe1e5a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -225,9 +225,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, @@ -477,7 +482,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); @@ -487,9 +493,20 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) if (is_sysmmu_active(data)) { int i; for (i = 0; i data-nsfrs; i++) { + unsigned int maj; + unsigned int num_inv = 1; + maj = __raw_readl(data-sfrbases[i] + REG_MMU_VERSION); + /* +* L2TLB invalidation required +* 4KB page: 1 invalidation +* 64KB page: 16 invalidation +* 1MB page: 64 invalidation +*/ + if ((maj 28) == 2) /* major version number */ + num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data-sfrbases[i])) { __sysmmu_tlb_invalidate_entry( - data-sfrbases[i], iova); + data-sfrbases[i], iova, num_inv); sysmmu_unblock(data-sfrbases[i]); } } @@ -999,7 +1016,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; -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 03/20] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 58 +++-- 1 files changed, 44 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 1c3a397..4a74ed8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -812,13 +812,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(__pa(pent)); *pgcounter = NUM_LV2ENTRIES; @@ -829,14 +834,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)); @@ -854,8 +866,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); @@ -864,7 +878,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; } @@ -892,7 +909,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; @@ -900,17 +917,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); @@ -924,6 +940,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); @@ -932,7 +949,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
[PATCH v10 05/20] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 34 -- 1 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index cbe1e5a..191cb3f 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -100,6 +100,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); @@ -738,7 +740,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(__va(lv2table_base(priv-pgtable + i))); + kmem_cache_free(lv2table_kmem_cache, + __va(lv2table_base(priv-pgtable + i))); free_pages((unsigned long)priv-pgtable, 2); free_pages((unsigned long)priv-lv2entcnt, 1); @@ -837,7 +840,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); @@ -867,8 +870,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; } @@ -1073,11 +1075,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); -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 07/20] iommu/exynos: always use a single clock descriptor
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, there is no need to obtain two or more clock descriptors. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 222 ++ 1 files changed, 73 insertions(+), 149 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 20b032f..0092359 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -171,9 +171,8 @@ 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; @@ -301,56 +300,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); @@ -368,13 +350,10 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) 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 (data-clk) + clk_disable(data-clk); disabled = true; data-pgtable = 0; @@ -417,27 +396,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 (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
[PATCH v10 06/20] 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 pullip@samsung.com --- drivers/iommu/exynos-iommu.c |3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 191cb3f..20b032f 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -644,8 +644,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) __set_fault_handler(data, default_fault_handler); - if (dev-parent) - pm_runtime_enable(dev); + pm_runtime_enable(dev); dev_dbg(dev, (%s) Initialized\n, data-dbgname); return 0; -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 08/20] 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. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 34 +- 1 files changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0092359..735d75e 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; @@ -328,8 +327,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); @@ -362,10 +361,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; } @@ -392,7 +391,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; } @@ -414,7 +413,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); @@ -430,16 +429,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 %#lx\n, - data-dbgname, data-pgtable); + dev_err(data-sysmmu, Already enabled with page table %#lx\n, + data-pgtable); } else { data-dev = dev; } @@ -485,9 +483,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); @@ -506,9 +502,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); @@ -563,8 +557,6 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) data-clk = NULL; dev_dbg(dev, No clock descriptor registered\n); } - - data-dbgname = platdata-dbgname; } data-sysmmu = dev; @@ -577,7 +569,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); -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https
[PATCH v10 09/20] iommu/exynos: use managed device helper functions
This patch uses managed device helper functions in the probe(). Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 64 - 1 files changed, 25 insertions(+), 39 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 735d75e..6fdb3836 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -510,53 +510,48 @@ 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_res; + dev_err(dev, Unable to find IOMEM region\n); + return -ENOENT; } - 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; - } + data-sfrbase = devm_ioremap_resource(dev, res); + if (IS_ERR(data-sfrbase)) + return PTR_ERR(data-sfrbase); - 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)) { - struct sysmmu_platform_data *platdata = dev_get_platdata(dev); + data-clk = devm_clk_get(dev, sysmmu); + if (IS_ERR(data-clk)) { + dev_info(dev, No gate clock found!\n); + data-clk = NULL; + } - data-clk = clk_get(dev, sysmmu); - if (IS_ERR(data-clk)) { - data-clk = NULL; - dev_dbg(dev, No clock descriptor registered\n); - } + ret = clk_prepare(data-clk); + if (ret) { + dev_err(dev, Failed to prepare clk\n); + return ret; } data-sysmmu = dev; @@ -569,17 +564,8 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) pm_runtime_enable(dev); - dev_dbg(dev, Initialized\n); + dev_dbg(dev, Probed and 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 = { -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v10 10/20] clk: exynos: add gate clock descriptions of System MMU
This adds gate clocks of all System MMUs and their master IPs that are not apeared in clk-exynos5250.c and clk-exynos5420.c Also fixes GATE_IP_ACP to 0x18800 and changed GATE_DA to GATE for System MMU clocks in clk-exynos4.c Signed-off-by: Cho KyongHo pullip@samsung.com --- .../devicetree/bindings/clock/exynos5250-clock.txt | 28 +++ .../devicetree/bindings/clock/exynos5420-clock.txt |3 + drivers/clk/samsung/clk-exynos5250.c | 49 ++- drivers/clk/samsung/clk-exynos5420.c | 12 - 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt index 24765c1..929cfba 100644 --- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt @@ -159,6 +159,34 @@ clock which they consume. mixer343 hdmi 344 g2d 345 + smmu_fimc_lite0 346 + smmu_fimc_lite1 347 + smmu_fimc_lite2 348 + smmu_tv 349 + smmu_fimd1 350 + smmu_2d 351 + fimc_isp 352 + fimc_drc 353 + fimc_fd 354 + fimc_scc 355 + fimc_scp 356 + fimc_mcuctl 357 + fimc_odc 358 + fimc_dis 359 + fimc_3dnr360 + smmu_fimc_isp361 + smmu_fimc_drc362 + smmu_fimc_fd 363 + smmu_fimc_scc364 + smmu_fimc_scp365 + smmu_fimc_mcuctl 366 + smmu_fimc_odc367 + smmu_fimc_dis0 368 + smmu_fimc_dis1 369 + smmu_fimc_3dnr 370 + camif_top371 + mdma0372 + smmu_mdma0 373 [Clock Muxes] diff --git a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt index 32aa34e..09dfa44 100644 --- a/Documentation/devicetree/bindings/clock/exynos5420-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5420-clock.txt @@ -172,12 +172,15 @@ clock which they consume. mdma0473 aclk333_g2d 480 g2d 481 + smmu_g2d 482 aclk333_432_gscl 490 smmu_3aa 491 smmu_fimcl0 492 smmu_fimcl1 493 smmu_fimcl3 494 fimc_lite3 495 + fimc_lite0 496 + fimc_lite1 497 aclk_g3d 500 g3d 501 smmu_mixer 502 diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index adf3234..c0312db 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -34,6 +34,7 @@ #define VPLL_CON0 0x10140 #define GPLL_CON0 0x10150 #define SRC_TOP0 0x10210 +#define SRC_TOP1 0x10214 #define SRC_TOP2 0x10218 #define SRC_GSCL 0x10220 #define SRC_DISP1_00x1022c @@ -64,6 +65,8 @@ #define DIV_PERIC3 0x10564 #define DIV_PERIC4 0x10568 #define DIV_PERIC5 0x1056c +#define GATE_IP_ISP0 0x0C800 +#define GATE_IP_ISP1 0x0C800 #define GATE_IP_GSCL 0x10920 #define GATE_IP_MFC0x1092c #define GATE_IP_GEN0x10934 @@ -75,7 +78,7 @@ #define SRC_CDREX 0x20200 #define PLL_DIV2_SEL 0x20a24 #define GATE_IP_DISP1 0x10928 -#define GATE_IP_ACP0x1 +#define GATE_IP_ACP0x18800 /* list of PLLs to be registered */ enum exynos5250_plls { @@ -121,6 +124,13 @@ enum exynos5250_clks { hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, g2d, + smmu_fimc_lite0 = 346, smmu_fimc_lite1, smmu_fimc_lite2, + smmu_tv, smmu_fimd1, smmu_2d, + fimc_isp, fimc_drc, fimc_fd, fimc_scc, fimc_scp, fimc_mcuctl, fimc_odc, + fimc_dis, fimc_3dnr, + smmu_fimc_isp, smmu_fimc_drc, smmu_fimc_fd, smmu_fimc_scc, + smmu_fimc_scp, smmu_fimc_mcuctl, smmu_fimc_odc, smmu_fimc_dis0, + smmu_fimc_dis1, smmu_fimc_3dnr, camif_top, mdma0, smmu_mdma0, /* mux clocks */ mout_hdmi = 1024, @@ -194,6 +204,7 @@ PNAME(mout_mpll_user_p) = { fin_pll, sclk_mpll }; PNAME(mout_bpll_user_p)= { fin_pll, sclk_bpll }; PNAME(mout_aclk166_p) = { sclk_cpll, sclk_mpll_user }; PNAME(mout_aclk200_p) = { sclk_mpll_user, sclk_bpll_user }; +PNAME(mout_aclk400_isp_p) = { sclk_mpll_user, sclk_bpll_user }; PNAME(mout_hdmi_p) = { div_hdmi_pixel, sclk_hdmiphy }; PNAME(mout_usb3_p) = { sclk_mpll_user, sclk_cpll }; PNAME
[PATCH v10 15/20] iommu/exynos: remove calls to Runtime PM API functions
Runtime power management by exynos-iommu driver independently from master H/W's runtime pm is not useful for power saving since attaching master H/W in probing time turns on its local power endlessly. Thus this removes runtime pm API calls. Runtime PM support is added in the following commits to exynos-iommu driver. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 337 +- 1 files changed, 201 insertions(+), 136 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 976b88a..d9c5416 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,8 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -154,6 +156,12 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +struct exynos_iommu_client { + struct list_head node; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + struct device *sysmmu; +}; + struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -163,9 +171,8 @@ struct exynos_iommu_domain { }; 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 */ + struct device *master; /* Owner of system MMU */ void __iomem *sfrbase; struct clk *clk; struct clk *clk_master; @@ -250,7 +257,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -310,7 +316,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) itype, base, addr); if (data-domain) ret = report_iommu_fault(data-domain, - data-dev, addr, itype); + data-master, addr, itype); } /* fault is not recovered by fault handler */ @@ -327,125 +333,145 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) +static void __sysmmu_disable_nocount(struct sysmmu_drvdata *data) { - unsigned long flags; - bool disabled = false; - - write_lock_irqsave(data-lock, flags); - - if (!set_sysmmu_inactive(data)) - goto finish; - clk_enable(data-clk_master); __raw_writel(CTRL_DISABLE, data-sfrbase + REG_MMU_CTRL); + __raw_writel(0, data-sfrbase + REG_MMU_CFG); + clk_disable(data-clk); clk_disable(data-clk_master); +} - clk_disable(data-clk); +static bool __sysmmu_disable(struct sysmmu_drvdata *data) +{ + bool disabled; + unsigned long flags; - disabled = true; - data-pgtable = 0; - data-domain = NULL; -finish: - write_unlock_irqrestore(data-lock, flags); + write_lock_irqsave(data-lock, flags); + + disabled = set_sysmmu_inactive(data); + + if (disabled) { + data-pgtable = 0; + data-domain = NULL; + + __sysmmu_disable_nocount(data); - if (disabled) dev_dbg(data-sysmmu, Disabled\n); - else - dev_dbg(data-sysmmu, %d times left to be disabled\n, + } else { + dev_dbg(data-sysmmu, %d times left to disable\n, data-activations); + } + + write_unlock_irqrestore(data-lock, flags); return disabled; } -/* __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 sysmmu_drvdata *data, - unsigned long pgtable, struct iommu_domain *domain) +static void __sysmmu_init_config(struct sysmmu_drvdata *data) { - int ret = 0; - unsigned long flags; - unsigned int min; + unsigned long cfg = 0; + int maj, min = 0; - write_lock_irqsave(data-lock, flags); + maj = __sysmmu_version(data, min); + if ((maj == 3) (min 1)) + cfg |= CFG_FLPDCACHE; - if (!set_sysmmu_active(data)) { - if (WARN_ON(pgtable != data-pgtable)) { - ret = -EBUSY
[PATCH v10 13/20] 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 t.f...@samsung.com Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 75 +++-- 1 files changed, 56 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index cf30519..75efdb81 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -80,6 +80,8 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ + #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 #define REG_MMU_STATUS 0x008 @@ -96,6 +98,9 @@ #define REG_MMU_VERSION0x034 +#define MMU_MAJ_VER(reg) (reg 28) +#define MMU_MIN_VER(reg) ((reg 21) 0x7F) + #define REG_PB0_SADDR 0x04C #define REG_PB0_EADDR 0x050 #define REG_PB1_SADDR 0x054 @@ -173,6 +178,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; @@ -199,6 +205,22 @@ static bool is_sysmmu_active(struct sysmmu_drvdata *data) return data-activations 0; } +static unsigned int __sysmmu_version(struct sysmmu_drvdata *data, +unsigned int *minor) +{ + unsigned long major; + + major = readl(data-sfrbase + REG_MMU_VERSION); + + if (minor) + *minor = MMU_MIN_VER(major); + + if (MMU_MAJ_VER(major) 3) + return 1; + + return MMU_MAJ_VER(major); +} + static void sysmmu_unblock(void __iomem *sfrbase) { __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); @@ -245,13 +267,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) { @@ -308,6 +323,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) WARN_ON(!is_sysmmu_active(data)); + 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 @@ -334,6 +350,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data-sfrbase); + clk_disable(data-clk_master); + read_unlock(data-lock); return IRQ_HANDLED; @@ -349,10 +367,13 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) if (!set_sysmmu_inactive(data)) goto finish; + clk_enable(data-clk_master); + __raw_writel(CTRL_DISABLE, data-sfrbase + REG_MMU_CTRL); - if (data-clk) - clk_disable(data-clk); + clk_disable(data-clk_master); + + clk_disable(data-clk); disabled = true; data-pgtable = 0; @@ -380,6 +401,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, { int ret = 0; unsigned long flags; + unsigned int min; write_lock_irqsave(data-lock, flags); @@ -395,22 +417,24 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - if (data-clk) - clk_enable(data-clk); data-pgtable = pgtable; + clk_enable(data-clk); + clk_enable(data-clk_master); + __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); + if ((__sysmmu_version(data, min) == 3) (min 1)) { + unsigned long cfg; + cfg = __raw_readl(data-sfrbase + REG_MMU_CFG); + __raw_writel(cfg | CFG_FLPDCACHE, data-sfrbase + REG_MMU_CFG); } __raw_writel(CTRL_ENABLE, data-sfrbase + REG_MMU_CTRL
[PATCH v10 17/20] iommu/exynos: add support for power management subsystems.
This adds support for Suspend to RAM and Runtime Power Management. Since System MMU is located in the same local power domain of its master H/W, System MMU must be initialized before it is working if its power domain was ever turned off. TLB invalidation according to unmapping on page tables must also be performed while power domain is turned on. This patch ensures that resume and runtime_resume(restore_state) functions in this driver is called before the calls to resume and runtime_resume callback functions in the drivers of master H/Ws. Likewise, suspend and runtime_suspend(save_state) functions in this driver is called after the calls to suspend and runtime_suspend in the drivers of master H/Ws. In order to get benefit of this support, the master H/W and its System MMU must resides in the same power domain in terms of Linux kernel. If a master H/W does not use generic I/O power domain, its driver must call iommu_attach_device() after its local power domain is turned on, iommu_detach_device before turned off. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 190 +- 1 files changed, 186 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 03031dc..e48c2fb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -28,6 +28,7 @@ #include linux/export.h #include linux/of.h #include linux/of_platform.h +#include linux/pm_domain.h #include linux/notifier.h #include asm/cacheflush.h @@ -184,6 +185,7 @@ struct sysmmu_drvdata { int activations; rwlock_t lock; struct iommu_domain *domain; + bool runtime_active; unsigned long pgtable; }; @@ -362,7 +364,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data-pgtable = 0; data-domain = NULL; - __sysmmu_disable_nocount(data); + if (data-runtime_active) + __sysmmu_disable_nocount(data); dev_dbg(data-sysmmu, Disabled\n); } else { @@ -423,7 +426,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, data-pgtable = pgtable; data-domain = domain; - __sysmmu_enable_nocount(data); + if (data-runtime_active) + __sysmmu_enable_nocount(data); dev_dbg(data-sysmmu, Enabled\n); } else { @@ -500,7 +504,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, data = dev_get_drvdata(client-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) data-runtime_active) { unsigned int num_inv = 1; /* * L2TLB invalidation required @@ -534,7 +538,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) data = dev_get_drvdata(client-sysmmu); read_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) data-runtime_active) { clk_enable(data-clk_master); if (sysmmu_block(data-sfrbase)) { __sysmmu_tlb_invalidate(data-sfrbase); @@ -610,11 +614,40 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); pm_runtime_enable(dev); + data-runtime_active = !pm_runtime_enabled(dev); dev_dbg(dev, Probed and initialized\n); return 0; } +#ifdef CONFIG_PM_SLEEP +static int sysmmu_suspend(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + unsigned long flags; + read_lock_irqsave(data-lock, flags); + if (is_sysmmu_active(data) + (!pm_runtime_enabled(dev) || data-runtime_active)) + __sysmmu_disable_nocount(data); + read_unlock_irqrestore(data-lock, flags); + return 0; +} + +static int sysmmu_resume(struct device *dev) +{ + struct sysmmu_drvdata *data = dev_get_drvdata(dev); + unsigned long flags; + read_lock_irqsave(data-lock, flags); + if (is_sysmmu_active(data) + (!pm_runtime_enabled(dev) || data-runtime_active)) + __sysmmu_enable_nocount(data); + read_unlock_irqrestore(data-lock, flags); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sysmmu_pm_ops, sysmmu_suspend, sysmmu_resume); + #ifdef CONFIG_OF static struct of_device_id sysmmu_of_match[] __initconst = { { .compatible = samsung,exynos4210-sysmmu, }, @@ -627,6 +660,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { .driver = { .owner = THIS_MODULE, .name = exynos-sysmmu, + .pm = sysmmu_pm_ops, .of_match_table = of_match_ptr(sysmmu_of_match
[PATCH v10 20/20] iommu/exynos: add devices attached to the System MMU to an IOMMU group
Patch written by Antonios Motakis a.mota...@virtualopensystems.com: IOMMU groups are expected by certain users of the IOMMU API, e.g. VFIO. Since each device is behind its own System MMU, we can allocate a new IOMMU group for each device. Reviewd-by: Cho KyongHo pullip@samsung.com Signed-off-by: Antonios Motakis a.mota...@virtualopensystems.com --- drivers/iommu/exynos-iommu.c | 28 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5025338..24505a0 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1028,6 +1028,32 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } +static int exynos_iommu_add_device(struct device *dev) +{ + struct iommu_group *group; + int ret; + + group = iommu_group_get(dev); + + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(dev, Failed to allocate IOMMU group\n); + return PTR_ERR(group); + } + } + + ret = iommu_group_add_device(group, dev); + iommu_group_put(group); + + return ret; +} + +static void exynos_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); +} + static struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1036,6 +1062,8 @@ static struct iommu_ops exynos_iommu_ops = { .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys, + .add_device = exynos_iommu_add_device, + .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; -- 1.7.2.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v6 07/12] ARM: EXYNOS: remove system mmu initialization from exynos tree
On Fri, 27 Sep 2013 14:55:51 -0400, Sean Paul wrote: On Tue, Dec 25, 2012 at 8:54 PM, Cho KyongHo pullip@samsung.com wrote: This removes System MMU initialization from arch/arm/mach-exynos/ to move them to DT and the exynos-iommu driver except gating clock definitions. exynos with iommu support no longer compiles with this patch: CC drivers/iommu/exynos-iommu.o /mnt/host/source/src/third_party/kernel-next/drivers/iommu/exynos-iommu.c:32:25: fatal error: mach/sysmmu.h: No such file or directory compilation terminated. make[3]: *** [drivers/iommu/exynos-iommu.o] Error 1 It is my fault. I think you need to apply all v6 patches to compile correctly. Since v8 patches, I checked that every patch is compiled successfully. iommu/exynos: do not include removed header patch from v7 patch removes including 'mach/sysmmu.h'. Thank you. Sean Signed-off-by: KyongHo Cho pullip@samsung.com --- arch/arm/mach-exynos/Kconfig | 5 - arch/arm/mach-exynos/Makefile | 1 - arch/arm/mach-exynos/clock-exynos4.c | 41 +++-- arch/arm/mach-exynos/clock-exynos4210.c| 9 +- arch/arm/mach-exynos/clock-exynos4212.c| 23 ++- arch/arm/mach-exynos/clock-exynos5.c | 62 --- arch/arm/mach-exynos/dev-sysmmu.c | 274 - arch/arm/mach-exynos/include/mach/sysmmu.h | 66 --- arch/arm/mach-exynos/mach-exynos4-dt.c | 34 arch/arm/mach-exynos/mach-exynos5-dt.c | 30 10 files changed, 137 insertions(+), 408 deletions(-) delete mode 100644 arch/arm/mach-exynos/dev-sysmmu.c delete mode 100644 arch/arm/mach-exynos/include/mach/sysmmu.h diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 91d5b6f..eba6eb5 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -103,11 +103,6 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS_DEV_SYSMMU - bool - help - Common setup code for SYSTEM MMU in EXYNOS platforms - config EXYNOS4_DEV_USB_OHCI bool help diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index b189881..435757e 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_ARCH_EXYNOS4)+= dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o obj-$(CONFIG_EXYNOS_DEV_DMA) += dma.o obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o -obj-$(CONFIG_EXYNOS_DEV_SYSMMU)+= dev-sysmmu.o obj-$(CONFIG_ARCH_EXYNOS) += setup-i2c0.o obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index bbcb3de..8a8468d 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -24,7 +24,6 @@ #include mach/map.h #include mach/regs-clock.h -#include mach/sysmmu.h #include common.h #include clock-exynos4.h @@ -709,53 +708,53 @@ static struct clk exynos4_init_clocks_off[] = { .enable = exynos4_clk_ip_peril_ctrl, .ctrlbit= (1 14), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .name = sysmmu, + .devname= exynos-sysmmu.0, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit= (1 1), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .name = sysmmu, + .devname= exynos-sysmmu.1, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit= (1 2), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(tv, 2), + .name = sysmmu, + .devname= exynos-sysmmu.2, .enable = exynos4_clk_ip_tv_ctrl, .ctrlbit= (1 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .name = sysmmu, + .devname= exynos-sysmmu.3, .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit= (1 11), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(rot, 4), + .name = sysmmu, + .devname= exynos-sysmmu.4, .enable
Re: [PATCH v6 07/12] ARM: EXYNOS: remove system mmu initialization from exynos tree
On Sun, 29 Sep 2013 20:38:38 -0400, Sean Paul wrote: On Sun, Sep 29, 2013 at 8:35 PM, Cho KyongHo pullip@samsung.com wrote: On Fri, 27 Sep 2013 14:55:51 -0400, Sean Paul wrote: On Tue, Dec 25, 2012 at 8:54 PM, Cho KyongHo pullip@samsung.com wrote: This removes System MMU initialization from arch/arm/mach-exynos/ to move them to DT and the exynos-iommu driver except gating clock definitions. exynos with iommu support no longer compiles with this patch: CC drivers/iommu/exynos-iommu.o /mnt/host/source/src/third_party/kernel-next/drivers/iommu/exynos-iommu.c:32:25: fatal error: mach/sysmmu.h: No such file or directory compilation terminated. make[3]: *** [drivers/iommu/exynos-iommu.o] Error 1 It is my fault. I think you need to apply all v6 patches to compile correctly. Since v8 patches, I checked that every patch is compiled successfully. I'm sorry I don't think I explained properly. This patch has been merged to mainline without the rest of the series. As such, mainline with exynos iommu does not currently compile. Ah, I remember that this is merged. I agreed to merge this patch because iommu driver need to be completely changed. Whenever I change exynos-iommu driver, synchronizing samsung-next and iommu-next branches is a big challenge. Thus I decided to remove dependencies to samsung-next branch. But I didn't know that the rest of the new driver is not merged soon. :( I am sorry about that. new patch (v10) will be posted soon. Thank you. Sean iommu/exynos: do not include removed header patch from v7 patch removes including 'mach/sysmmu.h'. Thank you. Sean Signed-off-by: KyongHo Cho pullip@samsung.com --- arch/arm/mach-exynos/Kconfig | 5 - arch/arm/mach-exynos/Makefile | 1 - arch/arm/mach-exynos/clock-exynos4.c | 41 +++-- arch/arm/mach-exynos/clock-exynos4210.c| 9 +- arch/arm/mach-exynos/clock-exynos4212.c| 23 ++- arch/arm/mach-exynos/clock-exynos5.c | 62 --- arch/arm/mach-exynos/dev-sysmmu.c | 274 - arch/arm/mach-exynos/include/mach/sysmmu.h | 66 --- arch/arm/mach-exynos/mach-exynos4-dt.c | 34 arch/arm/mach-exynos/mach-exynos5-dt.c | 30 10 files changed, 137 insertions(+), 408 deletions(-) delete mode 100644 arch/arm/mach-exynos/dev-sysmmu.c delete mode 100644 arch/arm/mach-exynos/include/mach/sysmmu.h diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 91d5b6f..eba6eb5 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -103,11 +103,6 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS_DEV_SYSMMU - bool - help - Common setup code for SYSTEM MMU in EXYNOS platforms - config EXYNOS4_DEV_USB_OHCI bool help diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index b189881..435757e 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_ARCH_EXYNOS4)+= dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o obj-$(CONFIG_EXYNOS_DEV_DMA) += dma.o obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o -obj-$(CONFIG_EXYNOS_DEV_SYSMMU)+= dev-sysmmu.o obj-$(CONFIG_ARCH_EXYNOS) += setup-i2c0.o obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index bbcb3de..8a8468d 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -24,7 +24,6 @@ #include mach/map.h #include mach/regs-clock.h -#include mach/sysmmu.h #include common.h #include clock-exynos4.h @@ -709,53 +708,53 @@ static struct clk exynos4_init_clocks_off[] = { .enable = exynos4_clk_ip_peril_ctrl, .ctrlbit= (1 14), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .name = sysmmu, + .devname= exynos-sysmmu.0, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit= (1 1), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .name = sysmmu, + .devname= exynos-sysmmu.1, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit
Re: [PATCH v9 06/16] ARM: dts: Add description of System MMU of Exynos SoCs
On Fri, 09 Aug 2013 00:26:51 +0200, Tomasz Figa wrote: Hi KyongHo, On Thursday 08 of August 2013 18:38:35 Cho KyongHo wrote: Signed-off-by: Cho KyongHo pullip@samsung.com --- .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 103 +++ arch/arm/boot/dts/exynos4.dtsi | 122 arch/arm/boot/dts/exynos4210.dtsi | 25 ++ arch/arm/boot/dts/exynos4x12.dtsi | 82 ++ arch/arm/boot/dts/exynos5250.dtsi | 290 5 files changed, 622 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt diff --git a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt new file mode 100644 index 000..92f0a33 --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt @@ -0,0 +1,103 @@ +Samsung Exynos4210 IOMMU H/W, System MMU (System Memory Management Unit) + +Samsung's Exynos architecture contains System MMU that enables scattered +physical memory chunks visible as a contiguous region to DMA-capable peripheral +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. + +System MMU is a sort of IOMMU and support identical translation table format to +ARMv7 translation tables with minimum set of page properties including access +permissions, shareability and security protection. In addition, System MMU has +another capabilities like L2 TLB or block-fetch buffers to minimize translation +latency. + +A System MMU is dedicated to a single master peripheral device. Thus, it is +important to specify the correct System MMU in the device node of its master +device. Whereas a System MMU is dedicated to a master device, the master device +may have more than one System MMU. This paragraph is still not clear. What about something among these lines: Yes. It is my fault. It is still unchanged... even though Grant sugessted clear and simple sentence. It must be changed :) System MMUs are in many to one relation with peripheral devices, i.e. single peripheral device might have multiple System MMUs (usually one for each bus master), but one System MMU can handle only one peripheral device. The relation between a System MMU and the peripheral device it handles needs to be defined in device node of this peripheral device. That looks good to me. Please let me use your sentences. +Required properties: +- compatible: Should be samsung,exynos4210-sysmmu +- reg: A tuple of base address and size of System MMU registers. +- interrupt-parent: The phandle of the interrupt controller of System MMU +- interrupts: A tuple of numbers that indicates the interrupt source. interrupts: An interrupt specifier for interrupt signal of System MMU, according to format defined for particular interrupt parent. Yes.. it is not changed.. +- clock-names: Should be sysmmu if the System MMU is needed to gate its clock. + Please refer to the following documents: + Documentation/devicetree/bindings/clock/clock-bindings.txt + Documentation/devicetree/bindings/clock/exynos4-clock.txt + Documentation/devicetree/bindings/clock/exynos5250- clock.txt + Optional master if the clock to the System MMU is gated by + another gate clock other than sysmmu. The System MMU driver + sets master the parent of sysmmu. + Exynos4 SoCs, there needs no master clocks. + Exynos5 SoCs, some System MMUs must have master clocks. +- clocks: Required if the System MMU is needed to gate its clock. + Please refer to the documents listed above. +- samsung,power-domain: Required if the System MMU is needed to gate its power. + Please refer to the following document: + Documentation/devicetree/bindings/arm/exynos/power_domain.txt + +Required properties for the master peripheral devices: +- iommu: phandles to the System MMUs of the device + +Examples: +A System MMU is dedicated to a single master device. + gsc_0: gsc@0x13e0 { nit: duplicated space after gsc_0: and incorrect 0x prefix in node unit- address. Ok. + compatible = samsung,exynos5-gsc; + reg = 0x13e0 0x1000; + interrupts = 0 85 0; + samsung,power-domain = pd_gsc; + clocks = clock 256; + clock-names = gscl; + iommu = sysmmu_gsc1; + }; + + sysmmu_gsc0: sysmmu@13E8 { + compatible = samsung,exynos4210-sysmmu; + reg = 0x13E8 0x1000; + interrupt-parent = combiner; + interrupt-names = sysmmu-gsc0; Hmm? interrupt-names property is not defined in your binding documentation and also the value here looks wrong. This should
Re: [PATCH v9 06/16] ARM: dts: Add description of System MMU of Exynos SoCs
On Fri, 09 Aug 2013 10:04:03 +0200, Tomasz Figa wrote: On Friday 09 of August 2013 15:15:57 Cho KyongHo wrote: On Fri, 09 Aug 2013 00:26:51 +0200, Tomasz Figa wrote: Hi KyongHo, On Thursday 08 of August 2013 18:38:35 Cho KyongHo wrote: Signed-off-by: Cho KyongHo pullip@samsung.com --- .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 103 +++ arch/arm/boot/dts/exynos4.dtsi | 122 arch/arm/boot/dts/exynos4210.dtsi | 25 ++ arch/arm/boot/dts/exynos4x12.dtsi | 82 ++ arch/arm/boot/dts/exynos5250.dtsi | 290 5 files changed, 622 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.tx t diff --git a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu. txt b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu. txt new file mode 100644 index 000..92f0a33 --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu. txt @@ -0,0 +1,103 @@ +Samsung Exynos4210 IOMMU H/W, System MMU (System Memory Management Unit) + +Samsung's Exynos architecture contains System MMU that enables scattered +physical memory chunks visible as a contiguous region to DMA-capable peripheral +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. + +System MMU is a sort of IOMMU and support identical translation table format to +ARMv7 translation tables with minimum set of page properties including access +permissions, shareability and security protection. In addition, System MMU has +another capabilities like L2 TLB or block-fetch buffers to minimize translation +latency. + +A System MMU is dedicated to a single master peripheral device. Thus, it is +important to specify the correct System MMU in the device node of its master +device. Whereas a System MMU is dedicated to a master device, the master device +may have more than one System MMU. This paragraph is still not clear. What about something among these lines: Yes. It is my fault. It is still unchanged... even though Grant sugessted clear and simple sentence. It must be changed :) System MMUs are in many to one relation with peripheral devices, i.e. single peripheral device might have multiple System MMUs (usually one for each bus master), but one System MMU can handle only one peripheral device. The relation between a System MMU and the peripheral device it handles needs to be defined in device node of this peripheral device. That looks good to me. Please let me use your sentences. OK. +Required properties: +- compatible: Should be samsung,exynos4210-sysmmu +- reg: A tuple of base address and size of System MMU registers. +- interrupt-parent: The phandle of the interrupt controller of System MMU +- interrupts: A tuple of numbers that indicates the interrupt source. interrupts: An interrupt specifier for interrupt signal of System MMU, according to format defined for particular interrupt parent. Yes.. it is not changed.. +- clock-names: Should be sysmmu if the System MMU is needed to gate its clock. + Please refer to the following documents: + Documentation/devicetree/bindings/clock/clock-bindings.txt + Documentation/devicetree/bindings/clock/exynos4-clock.txt + Documentation/devicetree/bindings/clock/exynos5250- clock.txt + Optional master if the clock to the System MMU is gated by + another gate clock other than sysmmu. The System MMU driver + sets master the parent of sysmmu. + Exynos4 SoCs, there needs no master clocks. + Exynos5 SoCs, some System MMUs must have master clocks. +- clocks: Required if the System MMU is needed to gate its clock. + Please refer to the documents listed above. +- samsung,power-domain: Required if the System MMU is needed to gate its power. + Please refer to the following document: + Documentation/devicetree/bindings/arm/exynos/power_domain.txt + +Required properties for the master peripheral devices: +- iommu: phandles to the System MMUs of the device + +Examples: +A System MMU is dedicated to a single master device. + gsc_0: gsc@0x13e0 { nit: duplicated space after gsc_0: and incorrect 0x prefix in node unit- address. Ok. + compatible = samsung,exynos5-gsc; + reg = 0x13e0 0x1000; + interrupts = 0 85 0; + samsung,power-domain
RE: [PATCH v8 10/12] iommu/exynos: add bus notifier for registering System MMU
-Original Message- From: Bartlomiej Zolnierkiewicz [mailto:b.zolnier...@samsung.com] Sent: Saturday, August 03, 2013 2:32 AM Hi, On Friday, July 26, 2013 08:30:17 PM Cho KyongHo wrote: When a device driver is registered, all constructs to handle System MMU is prepared by bus notifier call. Patch description could be improved greatly by documenting what the patch is doing and it does a lot more than simply adding bus notifier. The patch can be also split further (at least code adding support for muplitple System MMUs per struct exynos_iommu_client instance could be separate which would make review a lot easier as the code wouldn't be moved around so much). Could you also tell me how (on which SoC/board and with what drivers) are you testing these changes? I've tried to test v7 with FIMC and MFC devices (on EXYNOS4210 based Universal C210 board) but as soon as I added actual IOMMU mappings (none of your patches in this series does it BTW) I've encountered problems (namely lockup related to clocks usage, probably related to master clock, I haven't had time to debug it further yet). I am just testing if each System MMU is correctly enabled and disabled or page tables are consistently managed. I does not run MFC nor FIMC with System MMU. Please let me know if you found the problem. Best regards, -- Bartlomiej Zolnierkiewicz Samsung RD Institute Poland Samsung Electronics Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 708 - 1 files changed, 552 insertions(+), 156 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f9853fe..c62c244 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,9 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/pm_domain.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -80,7 +83,13 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_LRU0x1 +#define CFG_QOS(n) ((n 0xF) 7) +#define CFG_MASK 0x0150 /* Selecting bit 0-15, 20, 22 and 24 */ +#define CFG_ACGEN (1 24) /* System MMU 3.3 only */ +#define CFG_SYSSEL (1 22) /* System MMU 3.2 only */ #define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ +#define CFG_SHAREABLE (1 12) /* System MMU 3.x only */ #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 @@ -154,6 +163,14 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +struct exynos_iommu_client { + struct list_head node; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + spinlock_t lock; + int num_sysmmu; + struct device *sysmmu[0]; +}; + struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -165,12 +182,14 @@ struct exynos_iommu_domain { 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 */ + struct device *master; /* Owner of system MMU */ int nsfrs; struct clk *clk; + struct clk *clk_master; int activations; rwlock_t lock; struct iommu_domain *domain; + bool runtime_active; unsigned long pgtable; void __iomem *sfrbases[0]; }; @@ -245,7 +264,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -273,6 +291,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, if (!is_sysmmu_active(data)) goto finish; + clk_enable(data-clk_master); for (i = 0; i data-nsfrs; i++) { if ((readl(data-sfrbases[i] + REG_MMU_VERSION) 28) == 3) { if (!sysmmu_block(data-sfrbases[i])) @@ -298,6 +317,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, sysmmu_unblock(data-sfrbases[i]); } } + clk_disable(data-clk_master); finish: read_unlock_irqrestore(data-lock, flags); } @@ -331,12 +351,13 @@ 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 exynos_iommu_client *client = NULL; enum exynos_sysmmu_inttype itype; unsigned long addr = -1; - int i, ret = -ENOSYS
RE: [PATCH v8 10/12] iommu/exynos: add bus notifier for registering System MMU
-Original Message- From: Rahul Sharma [mailto:r.sh.o...@gmail.com] Sent: Friday, August 02, 2013 2:18 PM Hi Cho, On Fri, Jul 26, 2013 at 5:00 PM, Cho KyongHo pullip@samsung.com wrote: When a device driver is registered, all constructs to handle System MMU is prepared by bus notifier call. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 708 - 1 files changed, 552 insertions(+), 156 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index f9853fe..c62c244 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,9 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/pm_domain.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -80,7 +83,13 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_LRU0x1 +#define CFG_QOS(n) ((n 0xF) 7) +#define CFG_MASK 0x0150 /* Selecting bit 0-15, 20, 22 and 24 */ +#define CFG_ACGEN (1 24) /* System MMU 3.3 only */ +#define CFG_SYSSEL (1 22) /* System MMU 3.2 only */ #define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ +#define CFG_SHAREABLE (1 12) /* System MMU 3.x only */ #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 @@ -154,6 +163,14 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +struct exynos_iommu_client { + struct list_head node; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + spinlock_t lock; + int num_sysmmu; + struct device *sysmmu[0]; Shouldn't we keep limit on the max number of iommu's to a single client ? Even this way, you will hit array overrun with multiple iommu's. I don't think it is needed to fix the size of the array because we know the size of the array in booting time. sysmmu[0] is just a placeholder of array. +}; + struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -165,12 +182,14 @@ struct exynos_iommu_domain { 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 */ + struct device *master; /* Owner of system MMU */ int nsfrs; struct clk *clk; + struct clk *clk_master; int activations; rwlock_t lock; struct iommu_domain *domain; + bool runtime_active; unsigned long pgtable; void __iomem *sfrbases[0]; }; @@ -245,7 +264,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -273,6 +291,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, if (!is_sysmmu_active(data)) goto finish; + clk_enable(data-clk_master); for (i = 0; i data-nsfrs; i++) { if ((readl(data-sfrbases[i] + REG_MMU_VERSION) 28) == 3) { if (!sysmmu_block(data-sfrbases[i])) @@ -298,6 +317,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, sysmmu_unblock(data-sfrbases[i]); } } + clk_disable(data-clk_master); finish: read_unlock_irqrestore(data-lock, flags); } @@ -331,12 +351,13 @@ 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 exynos_iommu_client *client = NULL; enum exynos_sysmmu_inttype itype; unsigned long addr = -1; - int i, ret = -ENOSYS; - read_lock(data-lock); + if (data-master) + client = data-master-archdata.iommu; WARN_ON(!is_sysmmu_active(data)); @@ -348,6 +369,10 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) break; } + if (client) + spin_lock(client-lock); + read_lock(data-lock); + if (i == data-nsfrs) { itype = SYSMMU_FAULT_UNKNOWN; } else { @@ -361,7 +386,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) } if (data-domain) - ret
RE: [PATCH v8 06/12] ARM: dts: Add description of System MMU of Exynos SoCs
-Original Message- From: Rob Herring [mailto:robherri...@gmail.com] Sent: Saturday, July 27, 2013 10:55 PM On Fri, Jul 26, 2013 at 6:28 AM, Cho KyongHo pullip@samsung.com wrote: Signed-off-by: Cho KyongHo pullip@samsung.com --- .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 103 +++ arch/arm/boot/dts/exynos4.dtsi | 122 arch/arm/boot/dts/exynos4210.dtsi | 25 ++ arch/arm/boot/dts/exynos4x12.dtsi | 76 + arch/arm/boot/dts/exynos5250.dtsi | 291 5 files changed, 617 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt diff --git a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt new file mode 100644 index 000..92f0a33 --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt @@ -0,0 +1,103 @@ +Samsung Exynos4210 IOMMU H/W, System MMU (System Memory Management Unit) + +Samsung's Exynos architecture contains System MMU that enables scattered +physical memory chunks visible as a contiguous region to DMA-capable peripheral +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. + +System MMU is a sort of IOMMU and support identical translation table format to +ARMv7 translation tables with minimum set of page properties including access +permissions, shareability and security protection. In addition, System MMU has +another capabilities like L2 TLB or block-fetch buffers to minimize translation +latency. + +A System MMU is dedicated to a single master peripheral device. Thus, it is +important to specify the correct System MMU in the device node of its master +device. Whereas a System MMU is dedicated to a master device, the master device +may have more than one System MMU. I don't follow the last sentence. Can you elaborate on the type of connection you are talking about. Grant also addressed that. He corrected the sentence like the following: Can I suggest rewriting the last two sentences to: The master device node must correctly specify at least one SystemMMU. A master device may have more than one System MMU. I will change the sentence Also, please align with the ARM system MMU binding that Will Deacon has submitted particularly in terms of how master connections are described. I didn't check it. Should this align with ARM System MMU bindings? System MMU in Exynos SoC is different from ARM System MMU. It does not follows the specifications of ARM System MMU. Rob + +Required properties: +- compatible: Should be samsung,exynos4210-sysmmu +- reg: A tuple of base address and size of System MMU registers. +- interrupt-parent: The phandle of the interrupt controller of System MMU +- interrupts: A tuple of numbers that indicates the interrupt source. +- clock-names: Should be sysmmu if the System MMU is needed to gate its clock. + Please refer to the following documents: + Documentation/devicetree/bindings/clock/clock-bindings.txt + Documentation/devicetree/bindings/clock/exynos4-clock.txt + Documentation/devicetree/bindings/clock/exynos5250-clock.txt + Optional master if the clock to the System MMU is gated by + another gate clock other than sysmmu. The System MMU driver + sets master the parent of sysmmu. + Exynos4 SoCs, there needs no master clocks. + Exynos5 SoCs, some System MMUs must have master clocks. +- clocks: Required if the System MMU is needed to gate its clock. + Please refer to the documents listed above. +- samsung,power-domain: Required if the System MMU is needed to gate its power. + Please refer to the following document: + Documentation/devicetree/bindings/arm/exynos/power_domain.txt + +Required properties for the master peripheral devices: +- iommu: phandles to the System MMUs of the device + +Examples: +A System MMU is dedicated to a single master device. + gsc_0: gsc@0x13e0 { + compatible = samsung,exynos5-gsc; + reg = 0x13e0 0x1000; + interrupts = 0 85 0; + samsung,power-domain = pd_gsc; + clocks = clock 256; + clock-names = gscl; + iommu = sysmmu_gsc1; + }; + + sysmmu_gsc0: sysmmu@13E8 { + compatible = samsung,exynos4210-sysmmu; + reg = 0x13E8 0x1000; + interrupt-parent = combiner; + interrupt-names = sysmmu-gsc0; + interrupts = 2 0; + clock-names = sysmmu, master
RE: [PATCH v7 9/9] iommu/exynos: add bus notifier for registering System MMU
From: Prathyush K [mailto:prathy...@chromium.org] Sent: Thursday, July 11, 2013 12:03 AM I think this patch can be split further. There is a lot more added in this patch (suspend/resume functions etc) than just adding a bus notifier. Oh, Sorry for that ;-) Actually, adding bus notifier is to register gpd_pm_ops to master peripheral devices, it results in big change in the driver. I will review further and also, test this patchset. Thank you very much. Regards, Prathyush On Fri, Jul 5, 2013 at 5:59 PM, Cho KyongHo pullip@samsung.com wrote: When a device driver is registered, all constructs to handle System MMU is prepared by bus notifier call. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 778 ++--- 1 files changed, 569 insertions(+), 209 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b151e51..51d43bb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,9 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/pm_domain.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -80,6 +83,14 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_LRU0x1 +#define CFG_QOS(n) ((n 0xF) 7) +#define CFG_MASK 0x0150 /* Selecting bit 0-15, 20, 22 and 24 */ +#define CFG_ACGEN (1 24) /* System MMU 3.3 only */ +#define CFG_SYSSEL (1 22) /* System MMU 3.2 only */ +#define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ +#define CFG_SHAREABLE (1 12) /* System MMU 3.x only */ + #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 #define REG_MMU_STATUS 0x008 @@ -96,6 +107,9 @@ #define REG_MMU_VERSION0x034 +#define MMU_MAJ_VER(reg) (reg 28) +#define MMU_MIN_VER(reg) ((reg 21) 0x7F) + #define REG_PB0_SADDR 0x04C #define REG_PB0_EADDR 0x050 #define REG_PB1_SADDR 0x054 @@ -126,16 +140,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, - unsigned long pgtable_base, unsigned long fault_addr); - static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { REG_PAGE_FAULT_ADDR, REG_AR_FAULT_ADDR, @@ -159,6 +163,14 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +struct exynos_iommu_client { + struct list_head node; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + spinlock_t lock; + int num_sysmmu; + struct device *sysmmu[0]; +}; + struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -170,13 +182,13 @@ struct exynos_iommu_domain { 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 */ + struct device *master; /* Owner of system MMU */ int nsfrs; struct clk *clk; int activations; spinlock_t lock; struct iommu_domain *domain; - sysmmu_fault_handler_t fault_handler; + bool runtime_active; unsigned long pgtable; void __iomem *sfrbases[0]; }; @@ -200,6 +212,20 @@ static bool is_sysmmu_active(struct sysmmu_drvdata *data) return data-activations 0; } +static unsigned int __sysmmu_version(struct sysmmu_drvdata *data, + int idx, unsigned int *minor) +{ + unsigned int major; + + major = readl(data-sfrbases[idx] + REG_MMU_VERSION); + + if (minor) + *minor = MMU_MIN_VER(major); + major = MMU_MAJ_VER(major); + + return major; +} + static void sysmmu_unblock(void __iomem *sfrbase) { __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); @@ -235,7 +261,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -292,34 +317,17 @@ finish
[PATCH v7 9/9] iommu/exynos: add bus notifier for registering System MMU
When a device driver is registered, all constructs to handle System MMU is prepared by bus notifier call. Signed-off-by: Cho KyongHo pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 778 ++--- 1 files changed, 569 insertions(+), 209 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b151e51..51d43bb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -27,6 +27,9 @@ #include linux/memblock.h #include linux/export.h #include linux/of.h +#include linux/of_platform.h +#include linux/pm_domain.h +#include linux/notifier.h #include asm/cacheflush.h #include asm/pgtable.h @@ -80,6 +83,14 @@ #define CTRL_BLOCK 0x7 #define CTRL_DISABLE 0x0 +#define CFG_LRU0x1 +#define CFG_QOS(n) ((n 0xF) 7) +#define CFG_MASK 0x0150 /* Selecting bit 0-15, 20, 22 and 24 */ +#define CFG_ACGEN (1 24) /* System MMU 3.3 only */ +#define CFG_SYSSEL (1 22) /* System MMU 3.2 only */ +#define CFG_FLPDCACHE (1 20) /* System MMU 3.2+ only */ +#define CFG_SHAREABLE (1 12) /* System MMU 3.x only */ + #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG0x004 #define REG_MMU_STATUS 0x008 @@ -96,6 +107,9 @@ #define REG_MMU_VERSION0x034 +#define MMU_MAJ_VER(reg) (reg 28) +#define MMU_MIN_VER(reg) ((reg 21) 0x7F) + #define REG_PB0_SADDR 0x04C #define REG_PB0_EADDR 0x050 #define REG_PB1_SADDR 0x054 @@ -126,16 +140,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, - unsigned long pgtable_base, unsigned long fault_addr); - static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { REG_PAGE_FAULT_ADDR, REG_AR_FAULT_ADDR, @@ -159,6 +163,14 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; +struct exynos_iommu_client { + struct list_head node; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + spinlock_t lock; + int num_sysmmu; + struct device *sysmmu[0]; +}; + struct exynos_iommu_domain { struct list_head clients; /* list of sysmmu_drvdata.node */ unsigned long *pgtable; /* lv1 page table, 16KB */ @@ -170,13 +182,13 @@ struct exynos_iommu_domain { 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 */ + struct device *master; /* Owner of system MMU */ int nsfrs; struct clk *clk; int activations; spinlock_t lock; struct iommu_domain *domain; - sysmmu_fault_handler_t fault_handler; + bool runtime_active; unsigned long pgtable; void __iomem *sfrbases[0]; }; @@ -200,6 +212,20 @@ static bool is_sysmmu_active(struct sysmmu_drvdata *data) return data-activations 0; } +static unsigned int __sysmmu_version(struct sysmmu_drvdata *data, + int idx, unsigned int *minor) +{ + unsigned int major; + + major = readl(data-sfrbases[idx] + REG_MMU_VERSION); + + if (minor) + *minor = MMU_MIN_VER(major); + major = MMU_MAJ_VER(major); + + return major; +} + static void sysmmu_unblock(void __iomem *sfrbase) { __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); @@ -235,7 +261,6 @@ static void __sysmmu_tlb_invalidate_entry(void __iomem *sfrbase, static void __sysmmu_set_ptbase(void __iomem *sfrbase, unsigned long pgd) { - __raw_writel(0x1, sfrbase + REG_MMU_CFG); /* 16KB LV1, LRU */ __raw_writel(pgd, sfrbase + REG_PT_BASE_ADDR); __sysmmu_tlb_invalidate(sfrbase); @@ -292,34 +317,17 @@ finish: spin_unlock_irqrestore(data-lock, flags); } -static void __set_fault_handler(struct sysmmu_drvdata *data, - sysmmu_fault_handler_t handler) -{ - unsigned long flags; - - spin_lock_irqsave(data-lock, flags); - data-fault_handler = handler; - spin_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
회신: Re: [PATCH v6 12/12] iommu/exynos: add debugfs entries for System MMU
Hi. Sorry for the late update. I am in a very tough situation in the both of work and life :) I will soon post the patches. Anyway, the last patches inserts the device descriptors of System MMU on the top of the device descriptor of its master device to hook the events of Runtime PM. But I think it may not proper for some drivers if a master device of a System MMU must have a parent that is not a System MMU. I found that hooking of Runtime PM events is possible with gpd_dev_ops but there is a big restriction that the master device must use Generic IO Power Domain. Is there any better option? Thank you. Cho KyongHo 원본 메시지 발신: Prathyush K prathy...@chromium.org 날짜: 2013/05/18 14:26 (GMT+09:00) 수신: Cho KyongHo pullip@samsung.com 참조: Kukjin Kim kgene@samsung.com,Hyunwoong Kim khw0178@samsung.com,Prathyush prathyus...@samsung.com,Subash Patel supash.ramasw...@linaro.org,Linux Kernel linux-ker...@vger.kernel.org,Linux IOMMU iommu@lists.linux-foundation.org,Linux Samsung SOC linux-samsung-...@vger.kernel.org,Linux ARM Kernel linux-arm-ker...@lists.infradead.org,Rahul Sharma rahul.sha...@samsung.com 제목: Re: [PATCH v6 12/12] iommu/exynos: add debugfs entries for System MMU Hi Mr. Cho, any update on this patchset? Are you working on v7? Regards, Prathyush On Wed, Dec 26, 2012 at 7:24 AM, Cho KyongHo pullip@samsung.com wrote: This commit adds debugfs directory and nodes for inspecting internal state of System MMU. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 204 +-- 1 file changed, 198 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9753e0e..78c0eb7 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -26,12 +26,17 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/fs.h +#include linux/seq_file.h +#include linux/debugfs.h #include linux/string.h #include linux/of.h #include linux/of_platform.h #include asm/cacheflush.h +#define MODULE_NAME exynos-sysmmu + /* We does not consider super section mapping (16MB) */ #define SECT_ORDER 20 #define LPAGE_ORDER 16 @@ -223,6 +228,7 @@ struct sysmmu_drvdata { struct sysmmu_prefbuf pbufs[MAX_NUM_PBUF]; int num_pbufs; struct sysmmu_version ver; + struct dentry *debugfs_root; struct iommu_domain *domain; unsigned long pgtable; bool runtime_active; @@ -1066,6 +1072,8 @@ static void __init __sysmmu_init_mmuname(struct device *sysmmu, } } +static void __create_debugfs_entry(struct sysmmu_drvdata *drvdata); + static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int i, ret; @@ -1134,6 +1142,8 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) data-runtime_active = !pm_runtime_enabled(dev); + __create_debugfs_entry(data); + platform_set_drvdata(pdev, data); dev-archdata.iommu = sysmmu_placeholder; @@ -1238,7 +1248,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { .probe = exynos_sysmmu_probe, .driver = { .owner = THIS_MODULE, - .name = exynos-sysmmu, + .name = MODULE_NAME, .pm = __pm_ops, .of_match_table = of_match_ptr(sysmmu_of_match), } @@ -1631,6 +1641,8 @@ static struct iommu_ops exynos_iommu_ops = { .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; +static struct dentry *sysmmu_debugfs_root; /* /sys/kernel/debug/sysmmu */ + static int __init exynos_iommu_init(void) { int ret; @@ -1642,17 +1654,197 @@ static int __init exynos_iommu_init(void) return -ENOMEM; } - ret = platform_driver_register(exynos_sysmmu_driver); + ret = bus_set_iommu(platform_bus_type, exynos_iommu_ops); + if (ret) { + kmem_cache_destroy(lv2table_kmem_cache); + pr_err(%s: Failed to register IOMMU ops\n, __func__); + return -EFAULT; + } - if (ret == 0) - ret = bus_set_iommu(platform_bus_type, exynos_iommu_ops); + sysmmu_debugfs_root = debugfs_create_dir(sysmmu, NULL); + if (!sysmmu_debugfs_root) + pr_err(%s: Failed to create debugfs entry, 'sysmmu'\n, + __func__); + if (IS_ERR(sysmmu_debugfs_root)) + sysmmu_debugfs_root = NULL; + ret = platform_driver_register(exynos_sysmmu_driver); if (ret) { - pr_err(%s: Failed to register exynos-iommu driver.\n, - __func__); kmem_cache_destroy(lv2table_kmem_cache); + pr_err(%s: Failed
[PATCH v6 00/12] iommu/exynos: Fixes and Enhancements of System MMU driver with DT
notice: v6 patch-set is rebased on next/iommu-exynos branch of linux-samsung.git. This patch-set does not include 2 patches (05 and 06 patches in v5 patch-se) because they alread exist already in the branch. The current exynos-iommu(System MMU) driver does not work autonomously since it is lack of support for power management of peripheral blocks. For example, MFC device driver must ensure that its System MMU is disabled before MFC block is power-down not to invalidate IOTLB in the System MMU when I/O memory mapping is changed. Because A System MMU is resides in the same H/W block, access to control registers of System MMU while the H/W block is turned off must be prohibited. This set of changes solves the above problem with setting each System MMUs as the parent of the device which owns the System MMU to recieve the information when the device is turned off or turned on. Another big change to the driver is the support for devicetree. The bindings for System MMU is described in Documentation/devicetree/bindings/arm/samsung/system-mmu.txt In addition, this patchset also includes several bug fixes and enhancements of the current driver. Change log: v6: - Rebased on the branch, next/iommu-exynos of git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git v5: - new bugfix: patch 01 - Reordered patches * patch 01 ~ 05: Bugfix and enhancements of the existing driver * patch 06 ~ 10: Device Tree support and callbacks for power management * patch 11 : System MMU 3.2 and 3.3 support * patch 12 ~ 14: Debugging features - Additional code compaction v4: - Remove Change-Id from v3 patches - Change the order of the third and the first patch Thanks to Kukjin Kim. - Fix memory leak when allocating and assigning exynos_iommu_owner to client device if the client device has multiple System MMUs. Thanks to Rahul Sharma. v3: - Fix prefetch buffer flag definition for System MMU 3.3 (patch 10/12) - Fix incorrect setting for SET_RUNTIME_PM_OPS (patch 09/12) Thanks to Prathyush. v2: - Split the patch to iommu/exynos into 9 patches - Support for System MMU 3.3 - Some code compaction Patch summary: [PATCH v6 01/12] iommu/exynos: add missing cache flush for removed pagetable entries [PATCH v6 02/12] iommu/exynos: always use iommu fault handler [PATCH v6 03/12] iommu/exynos: allocate lv2 page table from own slab [PATCH v6 04/12] iommu/exynos: change rwlock to spinlock [PATCH v6 05/12] iommu/exynos: support for device tree [PATCH v6 06/12] iommu/exynos: set System MMU as the parent of client device [PATCH v6 07/12] ARM: EXYNOS: remove system mmu initialization from exynos tree [PATCH v6 08/12] iommu/exynos: add support for runtime pm and suspend/resume [PATCH v6 09/12] iommu/exynos: add support for System MMU 3.2 and 3.3 [PATCH v6 10/12] iommu/exynos: pass version information from DT [PATCH v6 11/12] iommu/exynos: add literal name of System MMU for debugging [PATCH v6 12/12] iommu/exynos: add debugfs entries for System MMU Diffstats: arch/arm/boot/dts/exynos5250-smdk5250.dts |2 +- arch/arm/boot/dts/exynos5250.dtsi | 27 +- arch/arm/mach-exynos/Kconfig |5 - arch/arm/mach-exynos/Makefile |1 - arch/arm/mach-exynos/clock-exynos4.c | 41 +- arch/arm/mach-exynos/clock-exynos4210.c|9 +- arch/arm/mach-exynos/clock-exynos4212.c| 23 +- arch/arm/mach-exynos/clock-exynos5.c | 87 +- arch/arm/mach-exynos/dev-sysmmu.c | 274 -- arch/arm/mach-exynos/include/mach/sysmmu.h | 66 -- arch/arm/mach-exynos/mach-exynos4-dt.c | 34 + arch/arm/mach-exynos/mach-exynos5-dt.c | 30 + drivers/iommu/Kconfig |2 +- drivers/iommu/Makefile |2 +- drivers/iommu/exynos-iommu.c | 1477 +--- 15 files changed, 1284 insertions(+), 796 deletions(-) ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v6 04/12] iommu/exynos: change rwlock to spinlock
Since acquiring read_lock is not more frequent than write_lock, it is not beneficial to use rwlock, this commit changes rwlock to spinlock. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 28 ++-- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 58d2a24..5847508 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -166,7 +166,7 @@ struct sysmmu_drvdata { void __iomem **sfrbases; struct clk *clk[2]; int activations; - rwlock_t lock; + spinlock_t lock; struct iommu_domain *domain; unsigned long pgtable; }; @@ -249,7 +249,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, BUG_ON((base0 + size0) = base0); BUG_ON((size1 0) ((base1 + size1) = base1)); - read_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (!is_sysmmu_active(data)) goto finish; @@ -279,7 +279,7 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, } } finish: - read_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); } static void __show_fault_information(unsigned long *pgtable, unsigned long iova, @@ -312,7 +312,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) int i, ret = -ENOSYS; - read_lock(data-lock); + spin_lock(data-lock); WARN_ON(!is_sysmmu_active(data)); @@ -354,7 +354,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) sysmmu_unblock(data-sfrbases[i]); - read_unlock(data-lock); + spin_unlock(data-lock); return IRQ_HANDLED; } @@ -365,7 +365,7 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) bool disabled = false; int i; - write_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (!set_sysmmu_inactive(data)) goto finish; @@ -382,7 +382,7 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) data-pgtable = 0; data-domain = NULL; finish: - write_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); if (disabled) dev_dbg(data-sysmmu, (%s) Disabled\n, data-dbgname); @@ -405,7 +405,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, int i, ret = 0; unsigned long flags; - write_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (!set_sysmmu_active(data)) { if (WARN_ON(pgtable != data-pgtable)) { @@ -444,7 +444,7 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, dev_dbg(data-sysmmu, (%s) Enabled\n, data-dbgname); finish: - write_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); return ret; } @@ -491,7 +491,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) unsigned long flags; struct sysmmu_drvdata *data = dev_get_drvdata(dev-archdata.iommu); - read_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (is_sysmmu_active(data)) { int i; @@ -508,7 +508,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) data-dbgname); } - read_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); } void exynos_sysmmu_tlb_invalidate(struct device *dev) @@ -516,7 +516,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) unsigned long flags; struct sysmmu_drvdata *data = dev_get_drvdata(dev-archdata.iommu); - read_lock_irqsave(data-lock, flags); + spin_lock_irqsave(data-lock, flags); if (is_sysmmu_active(data)) { int i; @@ -532,7 +532,7 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) data-dbgname); } - read_unlock_irqrestore(data-lock, flags); + spin_unlock_irqrestore(data-lock, flags); } static int exynos_sysmmu_probe(struct platform_device *pdev) @@ -629,7 +629,7 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) } data-sysmmu = dev; - rwlock_init(data-lock); + spin_lock_init(data-lock); INIT_LIST_HEAD(data-node); if (dev-parent) -- 1.8.0 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v6 05/12] iommu/exynos: support for device tree
This commit adds device tree support for System MMU. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/Kconfig| 2 +- drivers/iommu/exynos-iommu.c | 282 ++- 2 files changed, 174 insertions(+), 110 deletions(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e39f9db..64586f1 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -168,7 +168,7 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool Exynos IOMMU Support - depends on ARCH_EXYNOS EXYNOS_DEV_SYSMMU + depends on ARCH_EXYNOS select IOMMU_API help Support for the IOMMU(System MMU) of Samsung Exynos application diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5847508..5b35820 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1,6 +1,6 @@ -/* linux/drivers/iommu/exynos_iommu.c +/* linux/drivers/iommu/exynos-iommu.c * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/kernel.h #include linux/io.h #include linux/interrupt.h #include linux/platform_device.h @@ -25,11 +26,10 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/of.h +#include linux/of_platform.h #include asm/cacheflush.h -#include asm/pgtable.h - -#include mach/sysmmu.h /* We does not consider super section mapping (16MB) */ #define SECT_ORDER 20 @@ -161,14 +161,13 @@ 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; int nsfrs; - void __iomem **sfrbases; - struct clk *clk[2]; + struct clk *clk; int activations; spinlock_t lock; struct iommu_domain *domain; unsigned long pgtable; + void __iomem *sfrbases[0]; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -373,10 +372,8 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) for (i = 0; i data-nsfrs; i++) __raw_writel(CTRL_DISABLE, data-sfrbases[i] + REG_MMU_CTRL); - if (data-clk[1]) - clk_disable(data-clk[1]); - if (data-clk[0]) - clk_disable(data-clk[0]); + if (data-clk) + clk_disable(data-clk); disabled = true; data-pgtable = 0; @@ -385,10 +382,10 @@ finish: spin_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; } @@ -415,14 +412,12 @@ 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; } - if (data-clk[0]) - clk_enable(data-clk[0]); - if (data-clk[1]) - clk_enable(data-clk[1]); + if (data-clk) + clk_enable(data-clk); data-pgtable = pgtable; @@ -442,7 +437,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: spin_unlock_irqrestore(data-lock, flags); @@ -458,7 +453,7 @@ 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; } @@ -466,8 +461,8 @@ 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, - data-dbgname, data-pgtable); + Already enabled with page table %#lx\n, + data-pgtable); } else { data-dev = dev; } @@ -504,8 +499,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned
[PATCH v6 06/12] iommu/exynos: set System MMU as the parent of client device
This commit sets System MM as the parent of the client device for power management. If System MMU is the parent of a device, it is guaranteed that System MMU is suspended later than the device and resumed earlier. Runtime suspend/resume on the device is also propagated to the System MMU. If a device is configured to have more than one System MMU, the advantage of power management also works and the System MMUs are also have relationships of parent and child. In this situation, the client device is still the descendant of its System MMUs. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 510 +-- 1 file changed, 342 insertions(+), 168 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5b35820..a0e5ee1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -101,6 +101,17 @@ #define REG_PB1_SADDR 0x054 #define REG_PB1_EADDR 0x058 +static void *sysmmu_placeholder; /* Inidcate if a device is System MMU */ + +#define is_sysmmu(sysmmu) (sysmmu-archdata.iommu == sysmmu_placeholder) +#define has_sysmmu(dev) \ + (dev-parent dev-archdata.iommu is_sysmmu(dev-parent)) +#define for_each_sysmmu(dev, sysmmu) \ + for (sysmmu = dev-parent; sysmmu is_sysmmu(sysmmu); \ + sysmmu = sysmmu-parent) +#define for_each_sysmmu_until(dev, sysmmu, until) \ + for (sysmmu = dev-parent; sysmmu != until; sysmmu = sysmmu-parent) + static struct kmem_cache *lv2table_kmem_cache; static unsigned long *section_entry(unsigned long *pgtable, unsigned long iova) @@ -157,10 +168,19 @@ struct exynos_iommu_domain { spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ }; +/* exynos_iommu_owner + * Metadata attached to the owner of a group of System MMUs that belong + * to the same owner device. + */ +struct exynos_iommu_owner { + struct list_head client; /* entry of exynos_iommu_domain.clients */ + struct device *dev; + spinlock_t lock;/* Lock to preserve consistency of System MMU */ +}; + 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 */ + struct device *master; /* Client device that needs System MMU */ int nsfrs; struct clk *clk; int activations; @@ -241,44 +261,50 @@ void exynos_sysmmu_set_prefbuf(struct device *dev, unsigned long base0, unsigned long size0, unsigned long base1, unsigned long size1) { - struct sysmmu_drvdata *data = dev_get_drvdata(dev-archdata.iommu); - unsigned long flags; - int i; + struct device *sysmmu; - BUG_ON((base0 + size0) = base0); - BUG_ON((size1 0) ((base1 + size1) = base1)); + for_each_sysmmu(dev, sysmmu) { + int i; + unsigned long flags; + struct sysmmu_drvdata *data = dev_get_drvdata(sysmmu); - spin_lock_irqsave(data-lock, flags); - if (!is_sysmmu_active(data)) - goto finish; + BUG_ON((base0 + size0) = base0); + BUG_ON((size1 0) ((base1 + size1) = base1)); - for (i = 0; i data-nsfrs; i++) { - if ((readl(data-sfrbases[i] + REG_MMU_VERSION) 28) == 3) { - if (!sysmmu_block(data-sfrbases[i])) - continue; + spin_lock_irqsave(data-lock, flags); + if (!is_sysmmu_active(data)) { + spin_unlock_irqrestore(data-lock, flags); + continue; + } - if (size1 == 0) { - if (size0 = SZ_128K) { - base1 = base0; - size1 = size0; - } else { - size1 = size0 - + for (i = 0; i data-nsfrs; i++) { + if ((readl(data-sfrbases[i] + REG_MMU_VERSION) 28) + == 3) { + if (!sysmmu_block(data-sfrbases[i])) + continue; + + if (size1 == 0) { + if (size0 = SZ_128K) { + base1 = base0; + size1 = size0; + } else { + size1 = size0 - ALIGN(size0 / 2, SZ_64K);
[PATCH v6 07/12] ARM: EXYNOS: remove system mmu initialization from exynos tree
This removes System MMU initialization from arch/arm/mach-exynos/ to move them to DT and the exynos-iommu driver except gating clock definitions. Signed-off-by: KyongHo Cho pullip@samsung.com --- arch/arm/mach-exynos/Kconfig | 5 - arch/arm/mach-exynos/Makefile | 1 - arch/arm/mach-exynos/clock-exynos4.c | 41 +++-- arch/arm/mach-exynos/clock-exynos4210.c| 9 +- arch/arm/mach-exynos/clock-exynos4212.c| 23 ++- arch/arm/mach-exynos/clock-exynos5.c | 62 --- arch/arm/mach-exynos/dev-sysmmu.c | 274 - arch/arm/mach-exynos/include/mach/sysmmu.h | 66 --- arch/arm/mach-exynos/mach-exynos4-dt.c | 34 arch/arm/mach-exynos/mach-exynos5-dt.c | 30 10 files changed, 137 insertions(+), 408 deletions(-) delete mode 100644 arch/arm/mach-exynos/dev-sysmmu.c delete mode 100644 arch/arm/mach-exynos/include/mach/sysmmu.h diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 91d5b6f..eba6eb5 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -103,11 +103,6 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS_DEV_SYSMMU - bool - help - Common setup code for SYSTEM MMU in EXYNOS platforms - config EXYNOS4_DEV_USB_OHCI bool help diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index b189881..435757e 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_ARCH_EXYNOS4)+= dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o obj-$(CONFIG_EXYNOS_DEV_DMA) += dma.o obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o -obj-$(CONFIG_EXYNOS_DEV_SYSMMU)+= dev-sysmmu.o obj-$(CONFIG_ARCH_EXYNOS) += setup-i2c0.o obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index bbcb3de..8a8468d 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -24,7 +24,6 @@ #include mach/map.h #include mach/regs-clock.h -#include mach/sysmmu.h #include common.h #include clock-exynos4.h @@ -709,53 +708,53 @@ static struct clk exynos4_init_clocks_off[] = { .enable = exynos4_clk_ip_peril_ctrl, .ctrlbit= (1 14), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .name = sysmmu, + .devname= exynos-sysmmu.0, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit= (1 1), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .name = sysmmu, + .devname= exynos-sysmmu.1, .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit= (1 2), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(tv, 2), + .name = sysmmu, + .devname= exynos-sysmmu.2, .enable = exynos4_clk_ip_tv_ctrl, .ctrlbit= (1 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .name = sysmmu, + .devname= exynos-sysmmu.3, .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit= (1 11), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(rot, 4), + .name = sysmmu, + .devname= exynos-sysmmu.4, .enable = exynos4_clk_ip_image_ctrl, .ctrlbit= (1 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(fimc0, 5), + .name = sysmmu, + .devname= exynos-sysmmu.5, .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit= (1 7), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(fimc1, 6), + .name = sysmmu, + .devname= exynos-sysmmu.6, .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit= (1 8), }, { - .name = SYSMMU_CLOCK_NAME, - .devname= SYSMMU_CLOCK_DEVNAME(fimc2, 7), + .name = sysmmu, + .devname=
[PATCH v6 08/12] iommu/exynos: add support for runtime pm and suspend/resume
This change enables the client device drivers not to care about the state of System MMU since the internal state of System MMU is controlled by the runtime PM and suspend/resume callback functions. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 176 ++- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a0e5ee1..c3c5b7b 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -187,6 +187,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; unsigned long pgtable; + bool runtime_active; void __iomem *sfrbases[0]; }; @@ -409,7 +410,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data-pgtable = 0; data-domain = NULL; - __sysmmu_disable_nocount(data); + if (data-runtime_active) + __sysmmu_disable_nocount(data); dev_dbg(data-sysmmu, Disabled\n); } else { @@ -422,30 +424,6 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) return disabled; } -static bool __exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct device *sysmmu; - - BUG_ON(!has_sysmmu(dev)); - - spin_lock_irqsave(owner-lock, flags); - - /* Every call to __sysmmu_disable() must return same result */ - for_each_sysmmu(dev, sysmmu) { - struct sysmmu_drvdata *data = dev_get_drvdata(sysmmu); - disabled = __sysmmu_disable(data); - if (disabled) - data-master = NULL; - } - - spin_unlock_irqrestore(owner-lock, flags); - - return disabled; -} - static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) { int i; @@ -481,7 +459,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, data-pgtable = pgtable; data-domain = domain; - __sysmmu_enable_nocount(data); + if (data-runtime_active) + __sysmmu_enable_nocount(data); dev_dbg(data-sysmmu, Enabled\n); } else { @@ -537,42 +516,31 @@ static int __exynos_sysmmu_enable(struct device *dev, unsigned long pgtable, int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) { - int ret; - struct device *sysmmu; - BUG_ON(!memblock_is_memory(pgtable)); - for_each_sysmmu(dev, sysmmu) { - ret = pm_runtime_get_sync(sysmmu); - if (ret 0) - break; - } - - if (ret 0) { - struct device *start; - for_each_sysmmu_until(dev, start, sysmmu) - pm_runtime_put(start); - - return ret; - } - - ret = __exynos_sysmmu_enable(dev, pgtable, NULL); - if (ret 0) - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); - - return ret; + return __exynos_sysmmu_enable(dev, pgtable, NULL); } bool exynos_sysmmu_disable(struct device *dev) { - bool disabled; + unsigned long flags; + bool disabled = true; + struct exynos_iommu_owner *owner = dev-archdata.iommu; struct device *sysmmu; - disabled = __exynos_sysmmu_disable(dev); + BUG_ON(!has_sysmmu(dev)); + + spin_lock_irqsave(owner-lock, flags); + + /* Every call to __sysmmu_disable() must return same result */ + for_each_sysmmu(dev, sysmmu) { + struct sysmmu_drvdata *drvdata = dev_get_drvdata(sysmmu); + disabled = __sysmmu_disable(drvdata); + if (disabled) + drvdata-master = NULL; + } - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); + spin_unlock_irqrestore(owner-lock, flags); return disabled; } @@ -588,7 +556,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) data = dev_get_drvdata(sysmmu); spin_lock_irqsave(data-lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) + data-runtime_active) { int i; for (i = 0; i data-nsfrs; i++) { if (sysmmu_block(data-sfrbases[i])) { @@ -817,7 +786,7 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) data-sysmmu = dev; spin_lock_init(data-lock); - pm_runtime_enable(dev); + data-runtime_active = !pm_runtime_enabled(dev); platform_set_drvdata(pdev, data); @@ -828,6 +797,64 @@ static
[PATCH v6 10/12] iommu/exynos: pass version information from DT
System MMUs in some implementation of Exynos core does not include correct version information in the System MMU. If the version information is not correct, exynos-iommu driver cannot take advantages of feature of higher versions of System MMu like prefetching page table entries prior to TLB miss. This commit allows passing version information from DT to the driver. If DT does not pass version information, the driver will read the information from System MMU. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 28 1 file changed, 28 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a558c51..70921e9 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -205,6 +205,12 @@ struct sysmmu_prefbuf { unsigned long config; }; +struct sysmmu_version { + unsigned char major; /* major = 0 means that driver must use MMU_VERSION + register instead of this structure */ + unsigned char minor; +}; + struct sysmmu_drvdata { struct device *sysmmu; /* System MMU's device descriptor */ struct device *master; /* Client device that needs System MMU */ @@ -214,6 +220,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct sysmmu_prefbuf pbufs[MAX_NUM_PBUF]; int num_pbufs; + struct sysmmu_version ver; struct iommu_domain *domain; unsigned long pgtable; bool runtime_active; @@ -246,6 +253,20 @@ static unsigned int __sysmmu_version(struct sysmmu_drvdata *drvdata, major = readl(drvdata-sfrbases[idx] + REG_MMU_VERSION); + if ((MMU_MAJ_VER(major) == 0) || (MMU_MAJ_VER(major) 3)) { + /* register MMU_VERSION is used for special purpose */ + if (drvdata-ver.major == 0) { + /* min ver. is not important for System MMU 1 and 2 */ + major = 1; + } else { + if (minor) + *minor = drvdata-ver.minor; + major = drvdata-ver.major; + } + + return major; + } + if (minor) *minor = MMU_MIN_VER(major); @@ -933,8 +954,15 @@ static int __init __sysmmu_setup(struct device *sysmmu, const char *compat; struct platform_device *pmaster = NULL; u32 master_inst_no = -1; + u32 ver[2]; int ret; + if (!of_property_read_u32_array(sysmmu-of_node, version, ver, 2)) { + drvdata-ver.major = (unsigned char)ver[0]; + drvdata-ver.minor = (unsigned char)ver[1]; + dev_dbg(sysmmu, Found version %d.%d\n, ver[0], ver[1]); + } + drvdata-pbufs[0].base = 0; drvdata-pbufs[0].size = ~0; drvdata-num_pbufs = 1; -- 1.8.0 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v6 11/12] iommu/exynos: add literal name of System MMU for debugging
This commit adds System MMU name to the driver data of each System MMU. It is used by fault information. Signed-off-by: KyongHo Cho pullip@samsung.com --- drivers/iommu/exynos-iommu.c | 72 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 70921e9..9753e0e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -26,6 +26,7 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/string.h #include linux/of.h #include linux/of_platform.h @@ -180,6 +181,7 @@ struct exynos_iommu_domain { 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 */ + const char *fault_info; /* debugging information for fault */ }; /* exynos_iommu_owner @@ -224,6 +226,7 @@ struct sysmmu_drvdata { struct iommu_domain *domain; unsigned long pgtable; bool runtime_active; + const char **mmuname; void __iomem *sfrbases[0]; }; @@ -601,25 +604,29 @@ 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; + struct exynos_iommu_owner *owner = NULL; enum exynos_sysmmu_inttype itype = SYSMMU_FAULT_UNKNOWN; unsigned long addr = -1; - int i, ret = -ENOSYS; - spin_lock(data-lock); + if (data-master) + owner = data-master-archdata.iommu; + + if (owner) + spin_lock(owner-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); + for (i = 0; i data-nsfrs; i++) { + struct resource *irqres; + irqres = platform_get_resource( + to_platform_device(data-sysmmu), + IORESOURCE_IRQ, i); if (irqres ((int)irqres-start == irq)) break; } - if (i pdev-num_resources) { + if (i data-nsfrs) { itype = (enum exynos_sysmmu_inttype) __ffs(__raw_readl(data-sfrbases[i] + REG_INT_STATUS)); if (WARN_ON(!((itype = 0) (itype SYSMMU_FAULT_UNKNOWN @@ -629,11 +636,17 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) data-sfrbases[i] + fault_reg_offset[itype]); } - if (data-domain) + if ((itype = SYSMMU_FAULTS_NUM) || (itype SYSMMU_PAGEFAULT)) + itype = SYSMMU_FAULT_UNKNOWN; + + if (data-domain) { + struct exynos_iommu_domain *priv = data-domain-priv; + priv-fault_info = data-mmuname[i]; ret = report_iommu_fault(data-domain, data-master, addr, itype); - else + } else { __show_fault_information(__va(data-pgtable), itype, addr); + } if (ret == -ENOSYS) pr_err(NO SYSTEM MMU FAULT HANDLER REGISTERED FOR %s\n, @@ -651,7 +664,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) sysmmu_unblock(data-sfrbases[i]); - spin_unlock(data-lock); + if (owner) + spin_unlock(owner-lock); return IRQ_HANDLED; } @@ -1028,6 +1042,30 @@ err_dev_put: return ret; } +static void __init __sysmmu_init_mmuname(struct device *sysmmu, + struct sysmmu_drvdata *drvdata) +{ + int i; + const char *mmuname; + + if (of_property_count_strings(sysmmu-of_node, mmuname) != + drvdata-nsfrs) + return; + + drvdata-mmuname = (void *)drvdata-sfrbases + + sizeof(drvdata-sfrbases[0]) * drvdata-nsfrs; + + for (i = 0; i drvdata-nsfrs; i++) { + if (of_property_read_string_index(sysmmu-of_node, + mmuname, i, mmuname)) + dev_err(sysmmu, Failed read mmuname[%d]\n, i); + else + drvdata-mmuname[i] = kstrdup(mmuname, GFP_KERNEL); + if (!drvdata-mmuname[i]) + drvdata-mmuname[i] = noname; + } +} + static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int i, ret; @@ -1039,9 +1077,14 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) return -ENODEV; } + ret = of_property_count_strings(pdev-dev.of_node, mmuname); +