[PATCHv6 2/7] ARM: dma-mapping: use asm-generic/dma-mapping-common.h
This patch modifies dma-mapping implementation on ARM architecture to use common dma_map_ops structure and asm-generic/dma-mapping-common.h helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Signed-off-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/Kconfig |1 + arch/arm/include/asm/device.h |1 + arch/arm/include/asm/dma-mapping.h | 197 +--- arch/arm/mm/dma-mapping.c | 149 --- 4 files changed, 117 insertions(+), 231 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a48aecc..59102fb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -4,6 +4,7 @@ config ARM select HAVE_AOUT select HAVE_DMA_API_DEBUG select HAVE_IDE if PCI || ISA || PCMCIA + select HAVE_DMA_ATTRS select HAVE_MEMBLOCK select RTC_LIB select SYS_SUPPORTS_APM_EMULATION diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h index 7aa3680..6e2cb0e 100644 --- a/arch/arm/include/asm/device.h +++ b/arch/arm/include/asm/device.h @@ -7,6 +7,7 @@ #define ASMARM_DEVICE_H struct dev_archdata { + struct dma_map_ops *dma_ops; #ifdef CONFIG_DMABOUNCE struct dmabounce_device_info *dmabounce; #endif diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 6bc056c..cf7b77c 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -10,6 +10,28 @@ #include asm-generic/dma-coherent.h #include asm/memory.h +extern struct dma_map_ops arm_dma_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ + if (dev dev-archdata.dma_ops) + return dev-archdata.dma_ops; + return arm_dma_ops; +} + +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ + BUG_ON(!dev); + dev-archdata.dma_ops = ops; +} + +#include asm-generic/dma-mapping-common.h + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + return get_dma_ops(dev)-set_dma_mask(dev, mask); +} + #ifdef __arch_page_to_dma #error Please update to __arch_pfn_to_dma #endif @@ -117,7 +139,6 @@ static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, extern int dma_supported(struct device *, u64); extern int dma_set_mask(struct device *, u64); - /* * DMA errors are defined by all-bits-set in the DMA address. */ @@ -295,179 +316,17 @@ static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, } #endif /* CONFIG_DMABOUNCE */ -/** - * dma_map_single - map a single buffer for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @cpu_addr: CPU direct mapped address of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_single() or - * dma_sync_single_for_cpu(). - */ -static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, - size_t size, enum dma_data_direction dir) -{ - unsigned long offset; - struct page *page; - dma_addr_t addr; - - BUG_ON(!virt_addr_valid(cpu_addr)); - BUG_ON(!virt_addr_valid(cpu_addr + size - 1)); - BUG_ON(!valid_dma_direction(dir)); - - page = virt_to_page(cpu_addr); - offset = (unsigned long)cpu_addr ~PAGE_MASK; - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, true); - - return addr; -} - -/** - * dma_map_page - map a portion of a page for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @page: page that buffer resides in - * @offset: offset into page for start of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_page(). - */ -static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, -unsigned long offset, size_t size, enum dma_data_direction dir) -{ - dma_addr_t addr; - - BUG_ON(!valid_dma_direction(dir)); - - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, false); - - return addr; -} - -/** - * dma_unmap_single - unmap a single buffer previously mapped - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @handle: DMA address of buffer - * @size: size of buffer (same as passed to dma_map_single) - * @dir: DMA transfer direction (same as passed to dma_map_single
RE: [PATCHv6 7/7] ARM: dma-mapping: add support for IOMMU mapper
Hello, On Friday, February 24, 2012 1:50 PM Arnd Bergmann wrote: On Friday 24 February 2012, Marek Szyprowski wrote: +static struct page **__iommu_alloc_buffer(struct device *dev, size_t +size, gfp_t gfp) { + struct page **pages; + int count = size PAGE_SHIFT; + int i=0; + + pages = kzalloc(count * sizeof(struct page*), gfp); + if (!pages) + return NULL; kzalloc can fail for any size bigger than PAGE_SIZE, if the system memory is fully fragmented. If there is a request for size bigger than 4MB, then the pages pointer array won't Fit in one page and kzalloc may fail. we should use vzalloc()/vfree() when pages pointer array size needed is bigger than PAGE_SIZE. Right, thanks for spotting this. I will fix this in the next version. It's not clear though if that is the best solution. vzalloc comes at the price of using up space in the vmalloc area and as well as extra TLB entries, so we try to limit its use where possible. The other current code might fail in out of memory situations, but if a user wants to allocate a 4MB buffer (using up more than one physically contiguous page of pointers to pages), the following allocation of 1024 pages will likely fail as well, so we might just fail early. I want to use some kind of chained arrays, each of at most of PAGE_SIZE. This code doesn't really need to keep these page pointers in contiguous virtual memory area, so it will not be a problem here. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv7 4/9] ARM: dma-mapping: use asm-generic/dma-mapping-common.h
This patch modifies dma-mapping implementation on ARM architecture to use common dma_map_ops structure and asm-generic/dma-mapping-common.h helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Signed-off-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/Kconfig |1 + arch/arm/include/asm/device.h |1 + arch/arm/include/asm/dma-mapping.h | 196 +--- arch/arm/mm/dma-mapping.c | 148 --- 4 files changed, 115 insertions(+), 231 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a48aecc..59102fb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -4,6 +4,7 @@ config ARM select HAVE_AOUT select HAVE_DMA_API_DEBUG select HAVE_IDE if PCI || ISA || PCMCIA + select HAVE_DMA_ATTRS select HAVE_MEMBLOCK select RTC_LIB select SYS_SUPPORTS_APM_EMULATION diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h index 7aa3680..6e2cb0e 100644 --- a/arch/arm/include/asm/device.h +++ b/arch/arm/include/asm/device.h @@ -7,6 +7,7 @@ #define ASMARM_DEVICE_H struct dev_archdata { + struct dma_map_ops *dma_ops; #ifdef CONFIG_DMABOUNCE struct dmabounce_device_info *dmabounce; #endif diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index ba422f2..6aea6ca 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -11,6 +11,27 @@ #include asm/memory.h #define ARM_DMA_ERROR ~0 +extern struct dma_map_ops arm_dma_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ + if (dev dev-archdata.dma_ops) + return dev-archdata.dma_ops; + return arm_dma_ops; +} + +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ + BUG_ON(!dev); + dev-archdata.dma_ops = ops; +} + +#include asm-generic/dma-mapping-common.h + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + return get_dma_ops(dev)-set_dma_mask(dev, mask); +} #ifdef __arch_page_to_dma #error Please update to __arch_pfn_to_dma @@ -119,7 +140,6 @@ static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, extern int dma_supported(struct device *, u64); extern int dma_set_mask(struct device *, u64); - /* * DMA errors are defined by all-bits-set in the DMA address. */ @@ -297,179 +317,17 @@ static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, } #endif /* CONFIG_DMABOUNCE */ -/** - * dma_map_single - map a single buffer for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @cpu_addr: CPU direct mapped address of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_single() or - * dma_sync_single_for_cpu(). - */ -static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, - size_t size, enum dma_data_direction dir) -{ - unsigned long offset; - struct page *page; - dma_addr_t addr; - - BUG_ON(!virt_addr_valid(cpu_addr)); - BUG_ON(!virt_addr_valid(cpu_addr + size - 1)); - BUG_ON(!valid_dma_direction(dir)); - - page = virt_to_page(cpu_addr); - offset = (unsigned long)cpu_addr ~PAGE_MASK; - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, true); - - return addr; -} - -/** - * dma_map_page - map a portion of a page for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @page: page that buffer resides in - * @offset: offset into page for start of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_page(). - */ -static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, -unsigned long offset, size_t size, enum dma_data_direction dir) -{ - dma_addr_t addr; - - BUG_ON(!valid_dma_direction(dir)); - - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, false); - - return addr; -} - -/** - * dma_unmap_single - unmap a single buffer previously mapped - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @handle: DMA address of buffer - * @size: size of buffer (same as passed to dma_map_single) - * @dir: DMA transfer direction (same as passed to dma_map_single) - * - * Unmap a single
[PATCHv7 8/9] ARM: dma-mapping: use alloc, mmap, free from dma_ops
This patch converts dma_alloc/free/mmap_{coherent,writecombine} functions to use generic alloc/free/mmap methods from dma_map_ops structure. A new DMA_ATTR_WRITE_COMBINE DMA attribute have been introduced to implement writecombine methods. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Signed-off-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/common/dmabounce.c|3 + arch/arm/include/asm/dma-mapping.h | 107 ++-- arch/arm/mm/dma-mapping.c | 53 ++ 3 files changed, 98 insertions(+), 65 deletions(-) diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index 119f487..dbae5ad 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -449,6 +449,9 @@ static int dmabounce_set_mask(struct device *dev, u64 dma_mask) } static struct dma_map_ops dmabounce_ops = { + .alloc = arm_dma_alloc, + .free = arm_dma_free, + .mmap = arm_dma_mmap, .map_page = dmabounce_map_page, .unmap_page = dmabounce_unmap_page, .sync_single_for_cpu= dmabounce_sync_for_cpu, diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 266cba6..4342b75 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -5,6 +5,7 @@ #include linux/mm_types.h #include linux/scatterlist.h +#include linux/dma-attrs.h #include linux/dma-debug.h #include asm-generic/dma-coherent.h @@ -110,68 +111,115 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size, extern int dma_supported(struct device *dev, u64 mask); /** - * dma_alloc_coherent - allocate consistent memory for DMA + * arm_dma_alloc - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size * @handle: bus-specific DMA address + * @attrs: optinal attributes that specific mapping properties * - * Allocate some uncached, unbuffered memory for a device for - * performing DMA. This function allocates pages, and will - * return the CPU-viewed address, and sets @handle to be the - * device-viewed address. + * Allocate some memory for a device for performing DMA. This function + * allocates pages, and will return the CPU-viewed address, and sets @handle + * to be the device-viewed address. */ -extern void *dma_alloc_coherent(struct device *, size_t, dma_addr_t *, gfp_t); +extern void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, + gfp_t gfp, struct dma_attrs *attrs); + +#define dma_alloc_coherent(d,s,h,f) dma_alloc_attrs(d,s,h,f,NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) +{ + struct dma_map_ops *ops = get_dma_ops(dev); + void *cpu_addr; + BUG_ON(!ops); + + cpu_addr = ops-alloc(dev, size, dma_handle, flag, attrs); + debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr); + return cpu_addr; +} /** - * dma_free_coherent - free memory allocated by dma_alloc_coherent + * arm_dma_free - free memory allocated by arm_dma_alloc * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: size of memory originally requested in dma_alloc_coherent * @cpu_addr: CPU-view address returned from dma_alloc_coherent * @handle: device-view address returned from dma_alloc_coherent + * @attrs: optinal attributes that specific mapping properties * * Free (and unmap) a DMA buffer previously allocated by - * dma_alloc_coherent(). + * arm_dma_alloc(). * * References to memory and mappings associated with cpu_addr/handle * during and after this call executing are illegal. */ -extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t); +extern void arm_dma_free(struct device *dev, size_t size, void *cpu_addr, +dma_addr_t handle, struct dma_attrs *attrs); + +#define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, +void *cpu_addr, dma_addr_t dma_handle, +struct dma_attrs *attrs) +{ + struct dma_map_ops *ops = get_dma_ops(dev); + BUG_ON(!ops); + + debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); + ops-free(dev, size, cpu_addr, dma_handle, attrs); +} /** - * dma_mmap_coherent - map a coherent DMA allocation into user space + * arm_dma_mmap - map a coherent DMA allocation into user space * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @vma: vm_area_struct describing requested user mapping * @cpu_addr: kernel CPU-view address returned from
RE: [PATCH v9 0/2] iommu/exynos: Add IOMMU/System MMU driver for Samsung Exynos
Hi, On Tuesday, February 28, 2012 7:37 AM KyongHo Cho wrote: Changes since v8: - exynos_iommu_map/unmap() just works for the page sizes that System MMU supports. (Joerg's comment) - 1 platform device for 1 H/W though a multimedia accelerator with several System MMUs attached. This make controlling System MMU simpler. - Information between System MMU and the accelerators: Shifted to accelerator's device structure from System MMU's Could you tell which kernel tree did you use as a base for this patch? It doesn't apply onto any of the known kernel trees (I've tried v3.2, v3.3-rc5 and kgene/for-next). It looks that you have used some internal tree because cannot find arch/arm/mach-exynos/clock-exynos5.c or arch/arm/mach-exynos/clock-exynos4212.c files in any of the public git repositories. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH v12 3/3] iommu/exynos: Add iommu driver for Exynos Platforms
-start); + ret = -ENOENT; + goto err_res; + } + } + + for (i = 0; i data-nsfrs; i++) { + ret = platform_get_irq(pdev, i); + if (ret = 0) { + dev_dbg(dev, Unable to find IRQ resource\n); + goto err_irq; + } + + ret = request_irq(ret, exynos_sysmmu_irq, 0, + dev_name(dev), data); + if (ret) { + dev_dbg(dev, Unabled to register interrupt handler\n); + goto err_irq; + } + } Please look at those loops and the interrupt function above. Those for-loops over all the resources looks really ugly and makes the driver less portable. The previous version (v8 afair) had much cleaned design. It would be better not to squash multiple sysmmu controllers into a single instance of the driver. Please keep the driver simple. Sysmmu driver's resource should consist only of one io register area and one irq, nothing more. Such approach will remove the need for all that custom data in platform_data and it also make the future integration much easier for other non-samsung platforms (the sysmmu hardware is also used by other hw vendors). Support for device tree is also easier to add if the device driver is kept simple (supporting only one instance of the hardware block). The only reason I can see, which might have suggested you to squash hardware sysmmu controllers into one single instance was to provide the same virtual io mappings for all these sub-devices (it is mainly used by fims-isp complex block). This can be achieved in a much clearer design by using one, common iommu domain and attaching all these separate iommu controllers to it. Of course this will also require having a separate platform devices for each sub-device, but sooner or later you will need them anyway. The good example here is the MFC module, which already have separate devices: mfc_l and mfc_r for each memory controller. + if (dev_get_platdata(dev)) { + char *deli, *beg; + struct sysmmu_platform_data *platdata = dev_get_platdata(dev); + + beg = platdata-clockname; + + for (deli = beg; (*deli != '\0') (*deli != ','); deli++) + /* NOTHING */; + + if (*deli == '\0') + deli = NULL; + else + *deli = '\0'; + + data-clk[0] = clk_get(dev, beg); + if (IS_ERR(data-clk[0])) { + data-clk[0] = NULL; + dev_dbg(dev, No clock descriptor registered\n); + } + + if (data-clk[0] deli) { + *deli = ','; + data-clk[1] = clk_get(dev, deli + 1); + if (IS_ERR(data-clk[1])) + data-clk[1] = NULL; + } + + data-dbgname = platdata-dbgname; + } Passing clocks via platform data is also considered as a bad idea, especially if you need to parse string to extract the clock names. It would be much cleaner to have 2 (or more if required) variants of sysmmu driver, each one for a different version of the sysmmu hardware (I noticed only that some of the sysmmu units require one clock, the other require 2 clocks). + + data-sysmmu = dev; + rwlock_init(data-lock); + INIT_LIST_HEAD(data-node); + + __set_fault_handler(data, default_fault_handler); + + if (dev-parent) + pm_runtime_enable(dev); + + dev_dbg(dev, (%s) Initialized\n, data-dbgname); + return 0; +err_irq: + while (i-- 0) { + int irq; + + irq = platform_get_irq(pdev, i); + free_irq(irq, data); + } +err_res: + while (data-nsfrs-- 0) + iounmap(data-sfrbases[data-nsfrs]); + kfree(data-sfrbases); +err_init: + kfree(data); +err_alloc: + dev_err(dev, Failed to initialize\n); + return ret; +} + +static struct platform_driver exynos_sysmmu_driver = { + .probe = exynos_sysmmu_probe, + .driver = { + .owner = THIS_MODULE, + .name = exynos-sysmmu, + } +}; + (snipped) Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv7 9/9] ARM: dma-mapping: add support for IOMMU mapper
Hello, On Thursday, March 29, 2012 9:19 AM Hiroshi Doyu wrote: On Wed, 29 Feb 2012 16:04:22 +0100 Marek Szyprowski m.szyprow...@samsung.com wrote: This patch add a complete implementation of DMA-mapping API for devices that have IOMMU support. All DMA-mapping calls are supported. This patch contains some of the code kindly provided by Krishna Reddy vdu...@nvidia.com and Andrzej Pietrasiewicz andrze...@samsung.com Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Signed-off-by: Kyungmin Park kyungmin.p...@samsung.com Reviewed-by: Konrad Rzeszutek Wilk konrad.w...@oracle.com --- arch/arm/Kconfig |8 + arch/arm/include/asm/device.h|3 + arch/arm/include/asm/dma-iommu.h | 34 ++ arch/arm/mm/dma-mapping.c| 726 +- arch/arm/mm/vmregion.h |2 +- 5 files changed, 758 insertions(+), 15 deletions(-) create mode 100644 arch/arm/include/asm/dma-iommu.h snip +/* + * Map a part of the scatter-gather list into contiguous io address space + */ +static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, + size_t size, dma_addr_t *handle, + enum dma_data_direction dir) +{ + struct dma_iommu_mapping *mapping = dev-archdata.mapping; + dma_addr_t iova, iova_base; + int ret = 0; + unsigned int count; + struct scatterlist *s; + + size = PAGE_ALIGN(size); + *handle = ARM_DMA_ERROR; + + iova_base = iova = __alloc_iova(mapping, size); + if (iova == ARM_DMA_ERROR) + return -ENOMEM; + + for (count = 0, s = sg; count (size PAGE_SHIFT); s = sg_next(s)) + { + phys_addr_t phys = page_to_phys(sg_page(s)); + unsigned int len = PAGE_ALIGN(s-offset + s-length); + + if (!arch_is_coherent()) + __dma_page_cpu_to_dev(sg_page(s), s-offset, s-length, dir); + + ret = iommu_map(mapping-domain, iova, phys, len, 0); + if (ret 0) + goto fail; + count += len PAGE_SHIFT; + iova += len; + } + *handle = iova_base; + + return 0; +fail: + iommu_unmap(mapping-domain, iova_base, count * PAGE_SIZE); + __free_iova(mapping, iova_base, size); + return ret; +} Do we need to set dma_address as below? Nope, this one is not correct. Please check the arm_iommu_map_sg() function. It calls __map_sg_chunk() and gives it dma-dma_address as one of the arguments, so the dma address is correctly stored in the scatter list. Please note that scatterlist is really non-trivial structure and information about physical memory pages/chunks is disjoint from the information about dma address space chunks, although both are stored on the same list. In arm_iommu_map_sg() 's' pointer is used for physical memory chunks and 'dma' pointer is used for dma address space chunks. The number of dma address space chunks (returned by arm_iommu_map_sq) can be lower than the number of physical memory chunks (given as nents argument). In case of IOMMU you usually construct the scatter list in such a way, that you get only one dma address chunk (so in fact you get a buffer mapped contiguously in virtual io address space). From e8bcc3cdac5375b5d7f5ac5b3f716a11c1008f38 Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU hd...@nvidia.com Date: Thu, 29 Mar 2012 09:59:04 +0300 Subject: [PATCH 1/1] ARM: dma-mapping: Fix dma_address in sglist s-dma_address wasn't set at mapping. Signed-off-by: Hiroshi DOYU hd...@nvidia.com --- arch/arm/mm/dma-mapping.c |2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 3347c37..11a9d65 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -,6 +,8 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, ret = iommu_map(mapping-domain, iova, phys, len, 0); if (ret 0) goto fail; + s-dma_address = iova; + count += len PAGE_SHIFT; iova += len; } The above patch is not needed at all. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv7 9/9] ARM: dma-mapping: add support for IOMMU mapper
Hello, On Friday, March 30, 2012 4:24 AM Krishna Reddy wrote: Hi, I have found a bug in arm_iommu_map_sg(). +int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, +enum dma_data_direction dir, struct dma_attrs *attrs) { + struct scatterlist *s = sg, *dma = sg, *start = sg; + int i, count = 0; + unsigned int offset = s-offset; + unsigned int size = s-offset + s-length; + unsigned int max = dma_get_max_seg_size(dev); + + for (i = 1; i nents; i++) { + s-dma_address = ARM_DMA_ERROR; + s-dma_length = 0; + + s = sg_next(s); With above code, the last sg element's dma_length is not getting set to zero. This causing additional incorrect unmapping during arm_iommu_unmap_sg call and leading to random crashes. The order of above three lines should be as follows. s = sg_next(s); s-dma_address = ARM_DMA_ERROR; s-dma_length = 0; You are right, the order of those lines must be reversed. In all my test codes the scatter list was initially cleared, so I missed this typical off-by-one error. Thanks for spotting it! Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv8 08/10] ARM: dma-mapping: remove redundant code and cleanup
This patch just performs a global cleanup in DMA mapping implementation for ARM architecture. Some of the tiny helper functions have been moved to the caller code, some have been merged together. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/mm/dma-mapping.c | 88 1 files changed, 24 insertions(+), 64 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 695c219..485c693 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -40,64 +40,12 @@ * the CPU does do speculative prefetches, which means we clean caches * before transfers and delay cache invalidation until transfer completion. * - * Private support functions: these are not part of the API and are - * liable to change. Drivers must not use these. */ -static inline void __dma_single_cpu_to_dev(const void *kaddr, size_t size, - enum dma_data_direction dir) -{ - extern void ___dma_single_cpu_to_dev(const void *, size_t, - enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_single_cpu_to_dev(kaddr, size, dir); -} - -static inline void __dma_single_dev_to_cpu(const void *kaddr, size_t size, - enum dma_data_direction dir) -{ - extern void ___dma_single_dev_to_cpu(const void *, size_t, - enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_single_dev_to_cpu(kaddr, size, dir); -} - -static inline void __dma_page_cpu_to_dev(struct page *page, unsigned long off, - size_t size, enum dma_data_direction dir) -{ - extern void ___dma_page_cpu_to_dev(struct page *, unsigned long, +static void __dma_page_cpu_to_dev(struct page *, unsigned long, size_t, enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_page_cpu_to_dev(page, off, size, dir); -} - -static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, - size_t size, enum dma_data_direction dir) -{ - extern void ___dma_page_dev_to_cpu(struct page *, unsigned long, +static void __dma_page_dev_to_cpu(struct page *, unsigned long, size_t, enum dma_data_direction); - if (!arch_is_coherent()) - ___dma_page_dev_to_cpu(page, off, size, dir); -} - - -static inline dma_addr_t __dma_map_page(struct device *dev, struct page *page, -unsigned long offset, size_t size, enum dma_data_direction dir) -{ - __dma_page_cpu_to_dev(page, offset, size, dir); - return pfn_to_dma(dev, page_to_pfn(page)) + offset; -} - -static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, - size_t size, enum dma_data_direction dir) -{ - __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)), - handle ~PAGE_MASK, size, dir); -} - /** * arm_dma_map_page - map a portion of a page for streaming DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices @@ -112,11 +60,13 @@ static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, * The device owns this memory once this call has completed. The CPU * can regain ownership by calling dma_unmap_page(). */ -static inline dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, +static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - return __dma_map_page(dev, page, offset, size, dir); + if (!arch_is_coherent()) + __dma_page_cpu_to_dev(page, offset, size, dir); + return pfn_to_dma(dev, page_to_pfn(page)) + offset; } /** @@ -133,27 +83,31 @@ static inline dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, * After this call, reads by the CPU to the buffer are guaranteed to see * whatever the device wrote there. */ -static inline void arm_dma_unmap_page(struct device *dev, dma_addr_t handle, +static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - __dma_unmap_page(dev, handle, size, dir); + if (!arch_is_coherent()) + __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)), + handle ~PAGE_MASK, size, dir); } -static inline void arm_dma_sync_single_for_cpu(struct device *dev, +static void arm_dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { unsigned int offset = handle (PAGE_SIZE - 1); struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset)); - __dma_page_dev_to_cpu(page, offset, size, dir); + if (!arch_is_coherent()) + __dma_page_dev_to_cpu(page, offset
[PATCHv8 01/10] common: add dma_mmap_from_coherent() function
Add a common helper for dma-mapping core for mapping a coherent buffer to userspace. Reported-by: Subash Patel subas...@gmail.com Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- drivers/base/dma-coherent.c| 42 include/asm-generic/dma-coherent.h |4 ++- 2 files changed, 45 insertions(+), 1 deletions(-) diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index bb0025c..1b85949 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -10,6 +10,7 @@ struct dma_coherent_mem { void*virt_base; dma_addr_t device_base; + phys_addr_t pfn_base; int size; int flags; unsigned long *bitmap; @@ -44,6 +45,7 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, dev-dma_mem-virt_base = mem_base; dev-dma_mem-device_base = device_addr; + dev-dma_mem-pfn_base = PFN_DOWN(bus_addr); dev-dma_mem-size = pages; dev-dma_mem-flags = flags; @@ -176,3 +178,43 @@ int dma_release_from_coherent(struct device *dev, int order, void *vaddr) return 0; } EXPORT_SYMBOL(dma_release_from_coherent); + +/** + * dma_mmap_from_coherent() - try to mmap the memory allocated from + * per-device coherent memory pool to userspace + * @dev: device from which the memory was allocated + * @vma: vm_area for the userspace memory + * @vaddr: cpu address returned by dma_alloc_from_coherent + * @size: size of the memory buffer allocated by dma_alloc_from_coherent + * + * This checks whether the memory was allocated from the per-device + * coherent memory pool and if so, maps that memory to the provided vma. + * + * Returns 1 if we correctly mapped the memory, or 0 if + * dma_release_coherent() should proceed with mapping memory from + * generic pools. + */ +int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, + void *vaddr, size_t size, int *ret) +{ + struct dma_coherent_mem *mem = dev ? dev-dma_mem : NULL; + + if (mem vaddr = mem-virt_base vaddr + size = + (mem-virt_base + (mem-size PAGE_SHIFT))) { + unsigned long off = vma-vm_pgoff; + int start = (vaddr - mem-virt_base) PAGE_SHIFT; + int user_count = (vma-vm_end - vma-vm_start) PAGE_SHIFT; + int count = size PAGE_SHIFT; + + *ret = -ENXIO; + if (off count user_count = count - off) { + unsigned pfn = mem-pfn_base + start + off; + *ret = remap_pfn_range(vma, vma-vm_start, pfn, + user_count PAGE_SHIFT, + vma-vm_page_prot); + } + return 1; + } + return 0; +} +EXPORT_SYMBOL(dma_mmap_from_coherent); diff --git a/include/asm-generic/dma-coherent.h b/include/asm-generic/dma-coherent.h index 85a3ffa..abfb268 100644 --- a/include/asm-generic/dma-coherent.h +++ b/include/asm-generic/dma-coherent.h @@ -3,13 +3,15 @@ #ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT /* - * These two functions are only for dma allocator. + * These three functions are only for dma allocator. * Don't use them in device drivers. */ int dma_alloc_from_coherent(struct device *dev, ssize_t size, dma_addr_t *dma_handle, void **ret); int dma_release_from_coherent(struct device *dev, int order, void *vaddr); +int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, size_t size, int *ret); /* * Standard interface */ -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv8 05/10] ARM: dma-mapping: use asm-generic/dma-mapping-common.h
This patch modifies dma-mapping implementation on ARM architecture to use common dma_map_ops structure and asm-generic/dma-mapping-common.h helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/Kconfig |1 + arch/arm/include/asm/device.h |1 + arch/arm/include/asm/dma-mapping.h | 196 +--- arch/arm/mm/dma-mapping.c | 148 --- 4 files changed, 115 insertions(+), 231 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index cf006d4..0fd27d4 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -4,6 +4,7 @@ config ARM select HAVE_AOUT select HAVE_DMA_API_DEBUG select HAVE_IDE if PCI || ISA || PCMCIA + select HAVE_DMA_ATTRS select HAVE_MEMBLOCK select RTC_LIB select SYS_SUPPORTS_APM_EMULATION diff --git a/arch/arm/include/asm/device.h b/arch/arm/include/asm/device.h index 7aa3680..6e2cb0e 100644 --- a/arch/arm/include/asm/device.h +++ b/arch/arm/include/asm/device.h @@ -7,6 +7,7 @@ #define ASMARM_DEVICE_H struct dev_archdata { + struct dma_map_ops *dma_ops; #ifdef CONFIG_DMABOUNCE struct dmabounce_device_info *dmabounce; #endif diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 02d651f..424b67a 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -11,6 +11,27 @@ #include asm/memory.h #define ARM_DMA_ERROR (~0) +extern struct dma_map_ops arm_dma_ops; + +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ + if (dev dev-archdata.dma_ops) + return dev-archdata.dma_ops; + return arm_dma_ops; +} + +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ + BUG_ON(!dev); + dev-archdata.dma_ops = ops; +} + +#include asm-generic/dma-mapping-common.h + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + return get_dma_ops(dev)-set_dma_mask(dev, mask); +} #ifdef __arch_page_to_dma #error Please update to __arch_pfn_to_dma @@ -119,7 +140,6 @@ static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, extern int dma_supported(struct device *, u64); extern int dma_set_mask(struct device *, u64); - /* * DMA errors are defined by all-bits-set in the DMA address. */ @@ -297,179 +317,17 @@ static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, } #endif /* CONFIG_DMABOUNCE */ -/** - * dma_map_single - map a single buffer for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @cpu_addr: CPU direct mapped address of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_single() or - * dma_sync_single_for_cpu(). - */ -static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, - size_t size, enum dma_data_direction dir) -{ - unsigned long offset; - struct page *page; - dma_addr_t addr; - - BUG_ON(!virt_addr_valid(cpu_addr)); - BUG_ON(!virt_addr_valid(cpu_addr + size - 1)); - BUG_ON(!valid_dma_direction(dir)); - - page = virt_to_page(cpu_addr); - offset = (unsigned long)cpu_addr ~PAGE_MASK; - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, true); - - return addr; -} - -/** - * dma_map_page - map a portion of a page for streaming DMA - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @page: page that buffer resides in - * @offset: offset into page for start of buffer - * @size: size of buffer to map - * @dir: DMA transfer direction - * - * Ensure that any data held in the cache is appropriately discarded - * or written back. - * - * The device owns this memory once this call has completed. The CPU - * can regain ownership by calling dma_unmap_page(). - */ -static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, -unsigned long offset, size_t size, enum dma_data_direction dir) -{ - dma_addr_t addr; - - BUG_ON(!valid_dma_direction(dir)); - - addr = __dma_map_page(dev, page, offset, size, dir); - debug_dma_map_page(dev, page, offset, size, dir, addr, false); - - return addr; -} - -/** - * dma_unmap_single - unmap a single buffer previously mapped - * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices - * @handle: DMA address of buffer - * @size: size of buffer (same as passed to dma_map_single) - * @dir: DMA transfer direction (same as passed to dma_map_single) - * - * Unmap a single streaming
[PATCHv8 04/10] ARM: dma-mapping: remove offset parameter to prepare for generic dma_ops
This patch removes the need for offset parameter in dma bounce functions. This is required to let dma-mapping framework on ARM architecture use common, generic dma-mapping helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/common/dmabounce.c| 13 +-- arch/arm/include/asm/dma-mapping.h | 67 +-- arch/arm/mm/dma-mapping.c |4 +- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index a1abdc9..c9f54b6 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -173,7 +173,8 @@ find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_ read_lock_irqsave(device_info-lock, flags); list_for_each_entry(b, device_info-safe_buffers, node) - if (b-safe_dma_addr == safe_dma_addr) { + if (b-safe_dma_addr = safe_dma_addr + b-safe_dma_addr + b-size safe_dma_addr) { rb = b; break; } @@ -362,9 +363,10 @@ void __dma_unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size, EXPORT_SYMBOL(__dma_unmap_page); int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr, - unsigned long off, size_t sz, enum dma_data_direction dir) + size_t sz, enum dma_data_direction dir) { struct safe_buffer *buf; + unsigned long off; dev_dbg(dev, %s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n, __func__, addr, off, sz, dir); @@ -373,6 +375,8 @@ int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr, if (!buf) return 1; + off = addr - buf-safe_dma_addr; + BUG_ON(buf-direction != dir); dev_dbg(dev, %s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n, @@ -391,9 +395,10 @@ int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr, EXPORT_SYMBOL(dmabounce_sync_for_cpu); int dmabounce_sync_for_device(struct device *dev, dma_addr_t addr, - unsigned long off, size_t sz, enum dma_data_direction dir) + size_t sz, enum dma_data_direction dir) { struct safe_buffer *buf; + unsigned long off; dev_dbg(dev, %s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n, __func__, addr, off, sz, dir); @@ -402,6 +407,8 @@ int dmabounce_sync_for_device(struct device *dev, dma_addr_t addr, if (!buf) return 1; + off = addr - buf-safe_dma_addr; + BUG_ON(buf-direction != dir); dev_dbg(dev, %s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n, diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 3dbec1d..02d651f 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -266,19 +266,17 @@ extern void __dma_unmap_page(struct device *, dma_addr_t, size_t, /* * Private functions */ -int dmabounce_sync_for_cpu(struct device *, dma_addr_t, unsigned long, - size_t, enum dma_data_direction); -int dmabounce_sync_for_device(struct device *, dma_addr_t, unsigned long, - size_t, enum dma_data_direction); +int dmabounce_sync_for_cpu(struct device *, dma_addr_t, size_t, enum dma_data_direction); +int dmabounce_sync_for_device(struct device *, dma_addr_t, size_t, enum dma_data_direction); #else static inline int dmabounce_sync_for_cpu(struct device *d, dma_addr_t addr, - unsigned long offset, size_t size, enum dma_data_direction dir) + size_t size, enum dma_data_direction dir) { return 1; } static inline int dmabounce_sync_for_device(struct device *d, dma_addr_t addr, - unsigned long offset, size_t size, enum dma_data_direction dir) + size_t size, enum dma_data_direction dir) { return 1; } @@ -401,6 +399,33 @@ static inline void dma_unmap_page(struct device *dev, dma_addr_t handle, __dma_unmap_page(dev, handle, size, dir); } + +static inline void dma_sync_single_for_cpu(struct device *dev, + dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ + BUG_ON(!valid_dma_direction(dir)); + + debug_dma_sync_single_for_cpu(dev, handle, size, dir); + + if (!dmabounce_sync_for_cpu(dev, handle, size, dir)) + return; + + __dma_single_dev_to_cpu(dma_to_virt(dev, handle), size, dir); +} + +static inline void dma_sync_single_for_device(struct device *dev, + dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ + BUG_ON(!valid_dma_direction(dir)); + + debug_dma_sync_single_for_device(dev, handle, size, dir); + + if (!dmabounce_sync_for_device(dev, handle, size, dir)) + return; + + __dma_single_cpu_to_dev(dma_to_virt(dev, handle), size, dir); +} + /** * dma_sync_single_range_for_cpu * @dev
[PATCHv8 09/10] ARM: dma-mapping: use alloc, mmap, free from dma_ops
This patch converts dma_alloc/free/mmap_{coherent,writecombine} functions to use generic alloc/free/mmap methods from dma_map_ops structure. A new DMA_ATTR_WRITE_COMBINE DMA attribute have been introduced to implement writecombine methods. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/common/dmabounce.c|3 + arch/arm/include/asm/dma-mapping.h | 107 ++-- arch/arm/mm/dma-mapping.c | 54 ++ 3 files changed, 98 insertions(+), 66 deletions(-) diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index 119f487..dbae5ad 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -449,6 +449,9 @@ static int dmabounce_set_mask(struct device *dev, u64 dma_mask) } static struct dma_map_ops dmabounce_ops = { + .alloc = arm_dma_alloc, + .free = arm_dma_free, + .mmap = arm_dma_mmap, .map_page = dmabounce_map_page, .unmap_page = dmabounce_unmap_page, .sync_single_for_cpu= dmabounce_sync_for_cpu, diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 5f35f22..d56d6d2 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -5,6 +5,7 @@ #include linux/mm_types.h #include linux/scatterlist.h +#include linux/dma-attrs.h #include linux/dma-debug.h #include asm-generic/dma-coherent.h @@ -110,68 +111,115 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size, extern int dma_supported(struct device *dev, u64 mask); /** - * dma_alloc_coherent - allocate consistent memory for DMA + * arm_dma_alloc - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: required memory size * @handle: bus-specific DMA address + * @attrs: optinal attributes that specific mapping properties * - * Allocate some uncached, unbuffered memory for a device for - * performing DMA. This function allocates pages, and will - * return the CPU-viewed address, and sets @handle to be the - * device-viewed address. + * Allocate some memory for a device for performing DMA. This function + * allocates pages, and will return the CPU-viewed address, and sets @handle + * to be the device-viewed address. */ -extern void *dma_alloc_coherent(struct device *, size_t, dma_addr_t *, gfp_t); +extern void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, + gfp_t gfp, struct dma_attrs *attrs); + +#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag, + struct dma_attrs *attrs) +{ + struct dma_map_ops *ops = get_dma_ops(dev); + void *cpu_addr; + BUG_ON(!ops); + + cpu_addr = ops-alloc(dev, size, dma_handle, flag, attrs); + debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr); + return cpu_addr; +} /** - * dma_free_coherent - free memory allocated by dma_alloc_coherent + * arm_dma_free - free memory allocated by arm_dma_alloc * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @size: size of memory originally requested in dma_alloc_coherent * @cpu_addr: CPU-view address returned from dma_alloc_coherent * @handle: device-view address returned from dma_alloc_coherent + * @attrs: optinal attributes that specific mapping properties * * Free (and unmap) a DMA buffer previously allocated by - * dma_alloc_coherent(). + * arm_dma_alloc(). * * References to memory and mappings associated with cpu_addr/handle * during and after this call executing are illegal. */ -extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t); +extern void arm_dma_free(struct device *dev, size_t size, void *cpu_addr, +dma_addr_t handle, struct dma_attrs *attrs); + +#define dma_free_coherent(d, s, c, h) dma_free_attrs(d, s, c, h, NULL) + +static inline void dma_free_attrs(struct device *dev, size_t size, +void *cpu_addr, dma_addr_t dma_handle, +struct dma_attrs *attrs) +{ + struct dma_map_ops *ops = get_dma_ops(dev); + BUG_ON(!ops); + + debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); + ops-free(dev, size, cpu_addr, dma_handle, attrs); +} /** - * dma_mmap_coherent - map a coherent DMA allocation into user space + * arm_dma_mmap - map a coherent DMA allocation into user space * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @vma: vm_area_struct describing requested user mapping * @cpu_addr: kernel CPU-view address
[PATCHv8 06/10] ARM: dma-mapping: implement dma sg methods on top of any generic dma ops
This patch converts all dma_sg methods to be generic (independent of the current DMA mapping implementation for ARM architecture). All dma sg operations are now implemented on top of respective dma_map_page/dma_sync_single_for* operations from dma_map_ops structure. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/mm/dma-mapping.c | 43 +++ 1 files changed, 19 insertions(+), 24 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 4841fec..d7137bd 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -619,7 +619,7 @@ void ___dma_page_dev_to_cpu(struct page *page, unsigned long off, EXPORT_SYMBOL(___dma_page_dev_to_cpu); /** - * dma_map_sg - map a set of SG buffers for streaming mode DMA + * arm_dma_map_sg - map a set of SG buffers for streaming mode DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @sg: list of buffers * @nents: number of buffers to map @@ -637,12 +637,13 @@ EXPORT_SYMBOL(___dma_page_dev_to_cpu); int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { + struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i, j; for_each_sg(sg, s, nents, i) { - s-dma_address = __dma_map_page(dev, sg_page(s), s-offset, - s-length, dir); + s-dma_address = ops-map_page(dev, sg_page(s), s-offset, + s-length, dir, attrs); if (dma_mapping_error(dev, s-dma_address)) goto bad_mapping; } @@ -650,12 +651,12 @@ int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, bad_mapping: for_each_sg(sg, s, i, j) - __dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir); + ops-unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, attrs); return 0; } /** - * dma_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg + * arm_dma_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @sg: list of buffers * @nents: number of buffers to unmap (same as was passed to dma_map_sg) @@ -667,15 +668,17 @@ int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, void arm_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { + struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; + int i; for_each_sg(sg, s, nents, i) - __dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir); + ops-unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, attrs); } /** - * dma_sync_sg_for_cpu + * arm_dma_sync_sg_for_cpu * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @sg: list of buffers * @nents: number of buffers to map (returned from dma_map_sg) @@ -684,21 +687,17 @@ void arm_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, void arm_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir) { + struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i; - for_each_sg(sg, s, nents, i) { - if (!dmabounce_sync_for_cpu(dev, sg_dma_address(s), - sg_dma_len(s), dir)) - continue; - - __dma_page_dev_to_cpu(sg_page(s), s-offset, - s-length, dir); - } + for_each_sg(sg, s, nents, i) + ops-sync_single_for_cpu(dev, sg_dma_address(s), s-length, +dir); } /** - * dma_sync_sg_for_device + * arm_dma_sync_sg_for_device * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices * @sg: list of buffers * @nents: number of buffers to map (returned from dma_map_sg) @@ -707,17 +706,13 @@ void arm_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, void arm_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir) { + struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i; - for_each_sg(sg, s, nents, i) { - if (!dmabounce_sync_for_device(dev, sg_dma_address(s), - sg_dma_len(s), dir)) - continue; - - __dma_page_cpu_to_dev(sg_page(s), s-offset, - s-length, dir
RE: [PATCHv8 07/10] ARM: dma-mapping: move all dma bounce code to separate dma ops structure
Hi Arnd, On Tuesday, April 10, 2012 2:24 PM Arnd Bergmann wrote: On Tuesday 10 April 2012, Marek Szyprowski wrote: This patch removes dma bounce hooks from the common dma mapping implementation on ARM architecture and creates a separate set of dma_map_ops for dma bounce devices. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com I could be misunderstanding something, but it looks like this one should come before patch 6, where you remove some of the dmabounce functions. Can you clarify? Before patch no 6, there were custom methods for all scatter/gather related operations. They iterated over the whole scatter list and called cache related operations directly (which in turn checked if we use dma bounce code or not and called respective version). Patch no 6 changed them not to use such shortcut for direct calling cache related operations. Instead it provides similar loop over scatter list and calls methods from the current device's dma_map_ops structure. This way, after patch no 7 these functions call simple dma_map_page() method for all standard devices and dma bounce aware version for devices registered for dma bouncing (with use different dma_map_ops). I can provide a separate set of scatter/gather list related functions for the linear dma mapping implementation and dma bouncing implementation if you think that the current approach is too complicated or over-engineered. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv8 10/10] ARM: dma-mapping: add support for IOMMU mapper
Hi Arnd, On Tuesday, April 10, 2012 1:58 PM Arnd Bergmann wrote: On Tuesday 10 April 2012, Marek Szyprowski wrote: +/** + * arm_iommu_create_mapping + * @bus: pointer to the bus holding the client device (for IOMMU calls) + * @base: start address of the valid IO address space + * @size: size of the valid IO address space + * @order: accuracy of the IO addresses allocations + * + * Creates a mapping structure which holds information about used/unused + * IO address ranges, which is required to perform memory allocation and + * mapping with IOMMU aware functions. + * + * The client device need to be attached to the mapping with + * arm_iommu_attach_device function. + */ +struct dma_iommu_mapping * +arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size, +int order) +{ + unsigned int count = size (PAGE_SHIFT + order); + unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); + struct dma_iommu_mapping *mapping; + int err = -ENOMEM; + + if (!count) + return ERR_PTR(-EINVAL); + + mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL); + if (!mapping) + goto err; + + mapping-bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!mapping-bitmap) + goto err2; + + mapping-base = base; + mapping-bits = BITS_PER_BYTE * bitmap_size; + mapping-order = order; + spin_lock_init(mapping-lock); + + mapping-domain = iommu_domain_alloc(bus); + if (!mapping-domain) + goto err3; + + kref_init(mapping-kref); + return mapping; +err3: + kfree(mapping-bitmap); +err2: + kfree(mapping); +err: + return ERR_PTR(err); +} +EXPORT_SYMBOL(arm_iommu_create_mapping); I don't understand this function, mostly I guess because you have not provided any users. A few questions here: * What is ARM specific about it that it is named arm_iommu_create_mapping? Isn't this completely generic, at least on the interface side? * Why is this exported to modules? Which device drivers do you expect to call it? * Why do you pass the bus_type in here? That seems like the completely wrong thing to do when all devices are on the same bus type (e.g. amba or platform) but are connected to different instances that each have their own iommu. I guess this is a question for Jörg, because the base iommu interface provides iommu_domain_alloc(). I will soon post a patch which shows how my IOMMU aware dma-mapping integrates with Samsung Exynos4 SYSMMU driver, so I will be able to answer all your questions by pointing to the respective lines in either IOMMU framework or my integration code. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv8 04/10] ARM: dma-mapping: remove offset parameter to prepare for generic dma_ops
Hi Arnd, On Tuesday, April 10, 2012 1:43 PM Arnd Bergmann wrote: On Tuesday 10 April 2012, Marek Szyprowski wrote: This patch removes the need for offset parameter in dma bounce functions. This is required to let dma-mapping framework on ARM architecture use common, generic dma-mapping helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com This one worries me a little. I always thought that the range sync functions were specifically needed for the dmabounce code. At the very least, I would expect the changeset comment to have an explanation of why this was initially done this way and why it's now safe to do do it otherwise. Well, range sync functions are available from the early days of the dma mapping api (at least that's what I've found reading the change log and old patches). They are the correct way of doing a partial syncs on the buffer (usually used by the network device drivers). This patch changes only the internal implementation of the dma bounce functions to let them tunnel through dma_map_ops structure. The driver api stays unchanged, so driver are obliged to call dma_*_range_* functions to keep code clean and easy to understand. The only drawback I can see from this patch is reduced detection of the dma api abuse. Let us consider the following code: dma_addr = dma_map_single(dev, ptr, 64, DMA_TO_DEVICE); dma_sync_single_range_for_cpu(dev, dma_addr+16, 0, 32, DMA_TO_DEVICE); Without the patch such code fails, because dma bounce code is unable to find the bounce buffer for the given dma_address. After the patch the sync call will be equivalent to: dma_sync_single_range_for_cpu(dev, dma_addr, 16, 32, DMA_TO_DEVICE); which succeeds. I don't consider this as a real problem. DMA API abuse should be caught by debug_dma_* function family, so we can simplify the internal low-level implementation without losing anything. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv8 05/10] ARM: dma-mapping: use asm-generic/dma-mapping-common.h
Hi Arnd, On Tuesday, April 10, 2012 1:47 PM Arnd Bergmann wrote: On Tuesday 10 April 2012, Marek Szyprowski wrote: This patch modifies dma-mapping implementation on ARM architecture to use common dma_map_ops structure and asm-generic/dma-mapping-common.h helpers. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/Kconfig |1 + arch/arm/include/asm/device.h |1 + arch/arm/include/asm/dma-mapping.h | 196 +--- arch/arm/mm/dma-mapping.c | 148 --- 4 files changed, 115 insertions(+), 231 deletions(-) Looks good in principle. One question: Now that many of the functions are only used in the dma_map_ops, can you make them 'static' instead? Some of these non static functions (mainly arm_dma_*_sg_* family) are also used by dma bounce code introduced in the next patch, that's why I left them without static attribute. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCHv8 04/10] ARM: dma-mapping: remove offset parameter to prepare for generic dma_ops
Hi Arnd, On Wednesday, April 11, 2012 2:19 PM Arnd Bergmann wrote: On Wednesday 11 April 2012, Marek Szyprowski wrote: Well, range sync functions are available from the early days of the dma mapping api (at least that's what I've found reading the change log and old patches). They are the correct way of doing a partial syncs on the buffer (usually used by the network device drivers). This patch changes only the internal implementation of the dma bounce functions to let them tunnel through dma_map_ops structure. The driver api stays unchanged, so driver are obliged to call dma_*_range_* functions to keep code clean and easy to understand. The only drawback I can see from this patch is reduced detection of the dma api abuse. Let us consider the following code: dma_addr = dma_map_single(dev, ptr, 64, DMA_TO_DEVICE); dma_sync_single_range_for_cpu(dev, dma_addr+16, 0, 32, DMA_TO_DEVICE); Without the patch such code fails, because dma bounce code is unable to find the bounce buffer for the given dma_address. After the patch the sync call will be equivalent to: dma_sync_single_range_for_cpu(dev, dma_addr, 16, 32, DMA_TO_DEVICE); which succeeds. I don't consider this as a real problem. DMA API abuse should be caught by debug_dma_* function family, so we can simplify the internal low-level implementation without losing anything. Ok, fair enough. Can you put the above text into the changelog? Yes, I will update it in the next release. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 0/2] Exynos: fix SYSMMU driver to work with power domains
Hi! These two patches fixes operation of the SYSMMU driver (v12 version [1]) with the new power domain driver based on generic power domains and runtime pw, which has been merged to Linux kernel v3.4-rc1. [1] https://lkml.org/lkml/2012/3/15/51 Best regards Marek Szyprowski Samsung Poland RD Center Patch summary: Marek Szyprowski (2): iommu/exynos: fix runtime pm support ARM: Exynos4: update SYSMMU setup code for gen_pd power domain driver arch/arm/mach-exynos/dev-sysmmu.c |6 +- arch/arm/mach-exynos/pm_domains.c | 13 + drivers/iommu/exynos-iommu.c | 20 ++-- 3 files changed, 36 insertions(+), 3 deletions(-) -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] iommu/exynos: fix runtime pm support
Fix registration to runtime pw and add missing resume callback. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- drivers/iommu/exynos-iommu.c | 20 ++-- 1 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b8daf7c..eef924d 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -651,8 +651,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; @@ -674,11 +673,28 @@ err_alloc: return ret; } +static int exynos_pm_resume(struct device *dev) +{ + struct sysmmu_drvdata *data; + + data = dev_get_drvdata(dev); + + if (is_sysmmu_active(data)) + __exynos_sysmmu_enable(data, data-pgtable, NULL); + + return 0; +} + +const struct dev_pm_ops exynos_pm_ops = { + .resume = exynos_pm_resume, +}; + static struct platform_driver exynos_sysmmu_driver = { .probe = exynos_sysmmu_probe, .driver = { .owner = THIS_MODULE, .name = exynos-sysmmu, + .pm = exynos_pm_ops, } }; -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 2/2] ARM: Exynos4: update SYSMMU setup code for gen_pd power domain driver
SYSMMU platform devices must be registered before setting up power domains to let power domain driver to correctly register also SYSMMU controller devices. This patch also registers SYSMMU controller devices to respective power domains. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/mach-exynos/dev-sysmmu.c |6 +- arch/arm/mach-exynos/pm_domains.c | 13 + 2 files changed, 18 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-exynos/dev-sysmmu.c b/arch/arm/mach-exynos/dev-sysmmu.c index c5b1ea3..3544638 100644 --- a/arch/arm/mach-exynos/dev-sysmmu.c +++ b/arch/arm/mach-exynos/dev-sysmmu.c @@ -271,4 +271,8 @@ static int __init init_sysmmu_platform_device(void) return 0; } -arch_initcall(init_sysmmu_platform_device); +/* + * SYSMMU platform devices must be registered before power domains + * see pm_domain.c, which use arch_initcall() + */ +core_initcall(init_sysmmu_platform_device); diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 13b3068..51a028d 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -21,6 +21,7 @@ #include linux/of_address.h #include mach/regs-pmu.h +#include mach/sysmmu.h #include plat/devs.h /* @@ -155,6 +156,18 @@ static __init int exynos4_pm_init_power_domain(void) pm_genpd_init(exynos4_pm_domains[idx]-pd, NULL, exynos4_pm_domains[idx]-is_off); +#ifdef CONFIG_EXYNOS_DEV_SYSMMU + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(fimc0), exynos4_pd_cam); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(fimc1), exynos4_pd_cam); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(fimc2), exynos4_pd_cam); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(fimc3), exynos4_pd_cam); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(jpeg), exynos4_pd_cam); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(mfc_l), exynos4_pd_mfc); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(mfc_r), exynos4_pd_mfc); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(fimd0), exynos4_pd_lcd0); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(rot), exynos4_pd_lcd0); + exynos_pm_add_dev_to_genpd(SYSMMU_PLATDEV(tv), exynos4_pd_tv); +#endif #ifdef CONFIG_S5P_DEV_FIMD0 exynos_pm_add_dev_to_genpd(s5p_device_fimd0, exynos4_pd_lcd0); #endif -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH/RFC] ARM: Exynos4: Integrate IOMMU aware DMA-mapping
Hi! This is an example of the IOMMU aware DMA-mapping implementation usage on a Samsung Exynos4 based NURI board. The ARM DMA-mapping IOMMU aware implementation is available in the [1] thread: This patch essentially registers DMA-mmaping/IOMMU support for FIMC and MFC devices and performs some tweaks in clocks hierarchy to get SYSMMU driver working correctly. The drivers have been tested with mainline V4L2 drivers for FIMC and MFC hardware. For easier testing I've created a separate kernel branch with all required prerequisite patches. It is based on lastest kgene/for-next branch and is available on my git repository: git://git.linaro.org/people/mszyprowski/linux-dma-mapping.git 3.4-rc2-arm-dma-v8-samsung This patch requires the following items: 1. ARM DMA-mapping patches [1] 2. Exynos SYSMMU driver v12 [2] 3. Exynos SYSMMU driver runtime pm fixes 4. Exynos4 gen_pd power domain driver fixes Runtime pm and power domain patches are required on Samsung Nuri board, but might be optional on boards where bootloader doesn't disable all devices on boot. [1] http://www.spinics.net/lists/linux-arch/msg17331.html [2] https://lkml.org/lkml/2012/3/15/51 Best regards Marek Szyprowski Samsung Poland RD Center Patch summary: Marek Szyprowski (1): ARM: Exynos4: integrate SYSMMU driver with DMA-mapping interface arch/arm/mach-exynos/Kconfig |1 + arch/arm/mach-exynos/clock-exynos4.c | 64 +++- arch/arm/mach-exynos/dev-sysmmu.c | 44 +++ arch/arm/mach-exynos/include/mach/sysmmu.h |3 + drivers/iommu/Kconfig |1 + 5 files changed, 84 insertions(+), 29 deletions(-) -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH] ARM: Exynos4: integrate SYSMMU driver with DMA-mapping interface
This patch provides an provides setup code which assigns IOMMU controllers to FIMC and MFC devices and enables IOMMU aware DMA-mapping for them. It has been tested on Samsung Exynos4 platform, NURI board. Most of the work is done in the s5p_sysmmu_late_init() function, which first assigns SYSMMU controller to respective client device and then creates IO address space mapping structures. In this example 128 MiB of address space is created at 0x2000 for most of the devices. IO address allocation precision is set to 2^4 pages, so all small allocations will be aligned to 64 pages. This reduces the size of the io address space bitmap to 4 KiB. To solve the clock dependency issues, parent clocks have been added to each SYSMMU controller bus clock. This models the true hardware behavior, because client's device bus clock also gates the respective sysmmu bus clock. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- arch/arm/mach-exynos/Kconfig |1 + arch/arm/mach-exynos/clock-exynos4.c | 64 +++- arch/arm/mach-exynos/dev-sysmmu.c | 44 +++ arch/arm/mach-exynos/include/mach/sysmmu.h |3 + drivers/iommu/Kconfig |1 + 5 files changed, 84 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 801c738..25b9ba5 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -288,6 +288,7 @@ config MACH_NURI select S5P_DEV_USB_EHCI select S5P_SETUP_MIPIPHY select EXYNOS4_DEV_DMA + select EXYNOS_DEV_SYSMMU select EXYNOS4_SETUP_FIMC select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index 29ae4df..fe459a3 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -497,29 +497,6 @@ static struct clk *exynos4_gate_clocks[] = { static struct clk exynos4_init_clocks_off[] = { { - .name = timers, - .parent = exynos4_clk_aclk_100.clk, - .enable = exynos4_clk_ip_peril_ctrl, - .ctrlbit= (124), - }, { - .name = csis, - .devname= s5p-mipi-csis.0, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 4), - .parent = exynos4_clk_gate_cam, - }, { - .name = csis, - .devname= s5p-mipi-csis.1, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 5), - .parent = exynos4_clk_gate_cam, - }, { - .name = jpeg, - .id = 0, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 6), - .parent = exynos4_clk_gate_cam, - }, { .name = fimc, .devname= exynos4-fimc.0, .enable = exynos4_clk_ip_cam_ctrl, @@ -544,6 +521,35 @@ static struct clk exynos4_init_clocks_off[] = { .ctrlbit= (1 3), .parent = exynos4_clk_gate_cam, }, { + .name = mfc, + .devname= s5p-mfc, + .enable = exynos4_clk_ip_mfc_ctrl, + .ctrlbit= (1 0), + .parent = exynos4_clk_gate_mfc, + }, { + .name = timers, + .parent = exynos4_clk_aclk_100.clk, + .enable = exynos4_clk_ip_peril_ctrl, + .ctrlbit= (124), + }, { + .name = csis, + .devname= s5p-mipi-csis.0, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 4), + .parent = exynos4_clk_gate_cam, + }, { + .name = csis, + .devname= s5p-mipi-csis.1, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 5), + .parent = exynos4_clk_gate_cam, + }, { + .name = jpeg, + .id = 0, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 6), + .parent = exynos4_clk_gate_cam, + }, { .name = hsmmc, .devname= exynos4-sdhci.0, .parent = exynos4_clk_aclk_133.clk, @@ -674,12 +680,6 @@ static struct clk exynos4_init_clocks_off[] = { .ctrlbit= (1 0), .parent = exynos4_clk_gate_lcd0
RE: [RFC 4/4] drm: Add NVIDIA Tegra support
Hi Thierry, On Thursday, April 12, 2012 9:18 AM Thierry Reding wrote: * Arnd Bergmann wrote: On Wednesday 11 April 2012, Thierry Reding wrote: Daniel Vetter wrote: Well, you use the iommu api to map/unmap memory into the iommu for tegra, whereas usually device drivers just use the dma api to do that. The usual interface is dma_map_sg/dma_unmap_sg, but there are quite a few variants around. I'm just wondering why this you've choosen this. I don't think this works on ARM. Maybe I'm not seeing the whole picture but judging by a quick look through the kernel tree there aren't any users that map DMA memory through an IOMMU. dma_map_sg is certainly the right interface to use, and Marek Szyprowski has patches to make that work on ARM, hopefully going into v3.5, so you could use those. I've looked at Marek's patches but I don't think they'll work for Tegra 2 or Tegra 3. The corresponding iommu_map() functions only set one PTE, regardless of the number of bytes passed to them. However, the Tegra TRM indicates that mapping needs to be done on a per-page basis so contiguous regions cannot be combined. I suppose the IOMMU driver would have to be fixed to program more than a single page in that case. I assume you want to map a set of pages into contiguous chunk in io address space. This can be done with dma_map_sg() call once IOMMU aware implementation has been assigned to the given device. DMA-mapping implementation is able to merge consecutive chunks of the scatter list in the dma/io address space if possible (i.e. there are no in-page offsets between the chunks). With my implementation of IOMMU aware dma-mapping you usually you get a single DMA chunk from the provided scatter-list. I know that this approach causes a lot of confusion at the first look, but that how dma mapping api has been designed. The scatter list based approach has some drawbacks - it is a bit oversized for most of the typical use cases for the gfx/multimedia buffers, but that's all we have now. Scatter lists were initially designed for the disk based block io operations, hence the presence of the in-page offsets and lengths for each chunk. For multimedia use cases providing an array of struct pages and asking dma-mapping to map them into contiguous memory is probably all we need. I wonder if introducing such new calls is a good idea. Anrd, what do think? It will definitely simplify the drivers and improve the code understanding. On the other hand it requires a significant amount of work in the dma-mapping framework for all architectures, but that's not a big issue for me. Also this doesn't yet solve the vmap() problem that is needed for the kernel virtual mapping. I did try using dma_alloc_writecombine(), but that only works for chunks of 2 MB or smaller, unless I use init_consistent_dma_size() during board setup, which isn't provided for in a DT setup. I couldn't find a better alternative, but I admit I'm not very familiar with all the VM APIs. Do you have any suggestions on how to solve this? Otherwise I'll try and dig in some more. Yes, I'm aware of this issue I'm currently working on solving it. I hope to use standard vmalloc range for all coherent/writecombine allocations and get rid of the custom 'consistent_dma' region at all. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH] ARM: Exynos4: integrate SYSMMU driver with DMA-mapping interface
Hi Subash, On Thursday, April 12, 2012 11:06 AM Subash Patel wrote: On 04/11/2012 08:06 PM, Marek Szyprowski wrote: This patch provides an provides setup code which assigns IOMMU controllers to FIMC and MFC devices and enables IOMMU aware DMA-mapping for them. It has been tested on Samsung Exynos4 platform, NURI board. Most of the work is done in the s5p_sysmmu_late_init() function, which first assigns SYSMMU controller to respective client device and then creates IO address space mapping structures. In this example 128 MiB of address space is created at 0x2000 for most of the devices. IO address allocation precision is set to 2^4 pages, so all small allocations will be aligned to 64 pages. This reduces the size of the io address space bitmap to 4 KiB. To solve the clock dependency issues, parent clocks have been added to each SYSMMU controller bus clock. This models the true hardware behavior, because client's device bus clock also gates the respective sysmmu bus clock. Signed-off-by: Marek Szyprowskim.szyprow...@samsung.com Acked-by: Kyungmin Parkkyungmin.p...@samsung.com --- arch/arm/mach-exynos/Kconfig |1 + arch/arm/mach-exynos/clock-exynos4.c | 64 +++- arch/arm/mach-exynos/dev-sysmmu.c | 44 +++ arch/arm/mach-exynos/include/mach/sysmmu.h |3 + drivers/iommu/Kconfig |1 + 5 files changed, 84 insertions(+), 29 deletions(-) diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 801c738..25b9ba5 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -288,6 +288,7 @@ config MACH_NURI select S5P_DEV_USB_EHCI select S5P_SETUP_MIPIPHY select EXYNOS4_DEV_DMA + select EXYNOS_DEV_SYSMMU select EXYNOS4_SETUP_FIMC select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index 29ae4df..fe459a3 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -497,29 +497,6 @@ static struct clk *exynos4_gate_clocks[] = { static struct clk exynos4_init_clocks_off[] = { { - .name = timers, - .parent =exynos4_clk_aclk_100.clk, - .enable = exynos4_clk_ip_peril_ctrl, - .ctrlbit= (124), - }, { - .name = csis, - .devname= s5p-mipi-csis.0, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 4), - .parent =exynos4_clk_gate_cam, - }, { - .name = csis, - .devname= s5p-mipi-csis.1, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 5), - .parent =exynos4_clk_gate_cam, - }, { - .name = jpeg, - .id = 0, - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit= (1 6), - .parent =exynos4_clk_gate_cam, - }, { .name = fimc, .devname= exynos4-fimc.0, .enable = exynos4_clk_ip_cam_ctrl, @@ -544,6 +521,35 @@ static struct clk exynos4_init_clocks_off[] = { .ctrlbit= (1 3), .parent =exynos4_clk_gate_cam, }, { + .name = mfc, + .devname= s5p-mfc, + .enable = exynos4_clk_ip_mfc_ctrl, + .ctrlbit= (1 0), + .parent =exynos4_clk_gate_mfc, + }, { + .name = timers, + .parent =exynos4_clk_aclk_100.clk, + .enable = exynos4_clk_ip_peril_ctrl, + .ctrlbit= (124), + }, { + .name = csis, + .devname= s5p-mipi-csis.0, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 4), + .parent =exynos4_clk_gate_cam, + }, { + .name = csis, + .devname= s5p-mipi-csis.1, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 5), + .parent =exynos4_clk_gate_cam, + }, { + .name = jpeg, + .id = 0, + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit= (1 6), + .parent =exynos4_clk_gate_cam, + }, { .name = hsmmc, .devname= exynos4-sdhci.0, .parent =exynos4_clk_aclk_133.clk, @@ -674,12 +680,6 @@ static struct clk exynos4_init_clocks_off[] = { .ctrlbit= (1 0
RE: [PATCHv8 10/10] ARM: dma-mapping: add support for IOMMU mapper
Hi Arnd, On Tuesday, April 10, 2012 1:58 PM Arnd Bergmann wrote: On Tuesday 10 April 2012, Marek Szyprowski wrote: +/** + * arm_iommu_create_mapping + * @bus: pointer to the bus holding the client device (for IOMMU calls) + * @base: start address of the valid IO address space + * @size: size of the valid IO address space + * @order: accuracy of the IO addresses allocations + * + * Creates a mapping structure which holds information about used/unused + * IO address ranges, which is required to perform memory allocation and + * mapping with IOMMU aware functions. + * + * The client device need to be attached to the mapping with + * arm_iommu_attach_device function. + */ +struct dma_iommu_mapping * +arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size, +int order) +{ + unsigned int count = size (PAGE_SHIFT + order); + unsigned int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); + struct dma_iommu_mapping *mapping; + int err = -ENOMEM; + + if (!count) + return ERR_PTR(-EINVAL); + + mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL); + if (!mapping) + goto err; + + mapping-bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!mapping-bitmap) + goto err2; + + mapping-base = base; + mapping-bits = BITS_PER_BYTE * bitmap_size; + mapping-order = order; + spin_lock_init(mapping-lock); + + mapping-domain = iommu_domain_alloc(bus); + if (!mapping-domain) + goto err3; + + kref_init(mapping-kref); + return mapping; +err3: + kfree(mapping-bitmap); +err2: + kfree(mapping); +err: + return ERR_PTR(err); +} +EXPORT_SYMBOL(arm_iommu_create_mapping); I don't understand this function, mostly I guess because you have not provided any users. A few questions here: * What is ARM specific about it that it is named arm_iommu_create_mapping? Isn't this completely generic, at least on the interface side? This function is quite generic. It creates 'struct dma_iommu_mapping' object, which is stored in the client's device arch data. This object mainly stores information about io/dma address space: base address, allocation bitmap and respective iommu domain. Please note that more than one device can be assigned to the given dma_iommu_mapping to match different hardware topologies. This function is called by the board/(sub-)platform startup code to initialize iommu based dma-mapping. For the example usage please refer to s5p_create_iommu_mapping() function in arch/arm/mach-exynos/dev-sysmmu.c on 3.4-rc2-arm-dma-v8-samsung branch in git://git.linaro.org/people/mszyprowski/linux-dma-mapping.git GITWeb shortcut: http://git.linaro.org/gitweb?p=people/mszyprowski/linux-dma-mapping.git;a=blob;f=arch/arm/mach-exyno s/dev-sysmmu.c;h=31f2d6caf0e9949def18abd18af3f9d16737ae19;hb=6025093750d41f88406042e6486e331b806dc87 5#l283 * Why is this exported to modules? Which device drivers do you expect to call it? I thought it might be useful to use modules for registering devices, but now I see that no platform use such approach. I will drop these exports unless someone finds a real use case for them. * Why do you pass the bus_type in here? That seems like the completely wrong thing to do when all devices are on the same bus type (e.g. amba or platform) but are connected to different instances that each have their own iommu. I guess this is a question for Jörg, because the base iommu interface provides iommu_domain_alloc(). That's only a consequence of the iommu api. I would also prefer to use client device pointer here instead of the bus id, but maybe I don't have enough knowledge about desktop IOMMUs. I suspect that there is also a need to assign one IOMMU driver to the whole bus (like pci bus) and it originates from such systems. In embedded world we usually have only one iommu driver which operates on the platform bus devices. On Samsung Exynos4 we have over a dozen SYSMMU controllers for various multimedia blocks, but they are all exactly the same. We use one iommu ops structure and store a pointer to the real iommu controller instance inside arch data of the client struct device. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH] ARM: Exynos4: integrate SYSMMU driver with DMA-mapping interface
-Original Message- From: Arnd Bergmann [mailto:a...@arndb.de] Sent: Thursday, April 12, 2012 1:09 PM To: Marek Szyprowski Cc: 'Subash Patel'; linux-arm-ker...@lists.infradead.org; linaro-mm-...@lists.linaro.org; linux...@kvack.org; linux-a...@vger.kernel.org; iommu@lists.linux-foundation.org; 'Kyungmin Park'; 'Joerg Roedel'; 'Russell King - ARM Linux'; 'Chunsang Jeong'; 'Krishna Reddy'; 'KyongHo Cho'; Andrzej Pietrasiewicz; 'Benjamin Herrenschmidt'; 'Konrad Rzeszutek Wilk'; 'Hiroshi Doyu' Subject: Re: [PATCH] ARM: Exynos4: integrate SYSMMU driver with DMA-mapping interface On Thursday 12 April 2012, Marek Szyprowski wrote: + +/* + * s5p_sysmmu_late_init + * Create DMA-mapping IOMMU context for specified devices. This function must + * be called later, once SYSMMU driver gets registered and probed. + */ +static int __init s5p_sysmmu_late_init(void) +{ + platform_set_sysmmu(SYSMMU_PLATDEV(fimc0).dev,s5p_device_fimc0.dev); + platform_set_sysmmu(SYSMMU_PLATDEV(fimc1).dev,s5p_device_fimc1.dev); + platform_set_sysmmu(SYSMMU_PLATDEV(fimc2).dev,s5p_device_fimc2.dev); + platform_set_sysmmu(SYSMMU_PLATDEV(fimc3).dev,s5p_device_fimc3.dev); + platform_set_sysmmu(SYSMMU_PLATDEV(mfc_l).dev,s5p_device_mfc_l.dev); + platform_set_sysmmu(SYSMMU_PLATDEV(mfc_r).dev,s5p_device_mfc_r.dev); + + s5p_create_iommu_mapping(s5p_device_fimc0.dev, 0x2000, SZ_128M, 4); + s5p_create_iommu_mapping(s5p_device_fimc1.dev, 0x2000, SZ_128M, 4); + s5p_create_iommu_mapping(s5p_device_fimc2.dev, 0x2000, SZ_128M, 4); + s5p_create_iommu_mapping(s5p_device_fimc3.dev, 0x2000, SZ_128M, 4); + s5p_create_iommu_mapping(s5p_device_mfc_l.dev, 0x2000, SZ_128M, 4); + s5p_create_iommu_mapping(s5p_device_mfc_r.dev, 0x4000, SZ_128M, 4); + + return 0; +} +device_initcall(s5p_sysmmu_late_init); Shouldn't these things be specific to a SoC? With this RFC, it happens that you will predefine the IOMMU attachment and mapping information for devices in common location (dev-sysmmu.c)? This may lead to problems because there are some IP's with SYSMMU support in exynos5, but not available in exynos4 (eg: GSC, FIMC-LITE, FIMC-ISP) Previously we used to do above declaration in individual machine file, which I think was more meaningful. Right, I simplified the code too much. Keeping these definitions inside machine files was a better idea. I completely forgot that Exynos sub-platform now covers both Exynos4 and Exynos5 SoC families. Ideally the information about iommu attachment should come from the device tree. We have the dma-ranges properties that define how a dma address space is mapped. I am not entirely sure how that works when you have multiple IOMMUs and if that requires defining addititional properties, but I think we should make it so that we don't have to hardcode specific devices in the source. Right, until that time machine/board files are imho ok. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: [PATCH 2/2] ARM: Exynos4: update SYSMMU setup code for gen_pd power domain driver
Hi, On Monday, April 16, 2012 12:10 PM KyongHo Cho wrote: On Wed, Apr 11, 2012 at 11:34 PM, Marek Szyprowski m.szyprow...@samsung.com wrote: SYSMMU platform devices must be registered before setting up power domains to let power domain driver to correctly register also SYSMMU controller devices. This patch also registers SYSMMU controller devices to respective power domains. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com --- Â arch/arm/mach-exynos/dev-sysmmu.c | Â Â 6 +- Â arch/arm/mach-exynos/pm_domains.c | Â 13 + Â 2 files changed, 18 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-exynos/dev-sysmmu.c b/arch/arm/mach-exynos/dev-sysmmu.c index c5b1ea3..3544638 100644 --- a/arch/arm/mach-exynos/dev-sysmmu.c +++ b/arch/arm/mach-exynos/dev-sysmmu.c @@ -271,4 +271,8 @@ static int __init init_sysmmu_platform_device(void) Â Â Â Â return 0; Â } -arch_initcall(init_sysmmu_platform_device); +/* + * SYSMMU platform devices must be registered before power domains + * see pm_domain.c, which use arch_initcall() + */ +core_initcall(init_sysmmu_platform_device); I don't understand why init_sysmmu_platform_device() must be called before exynos4_pm_init_power_domain()? Other devices also added by an arch_initcall() function which calls mdesc-init_machine(). I also was unable to find any reason to add devices before registering the devices to pm domain. mdesc-init_machine is called before exynos4_pm_init_power_domain() from arch/arm/mach-exynos/pm_domain.c, although both are started from arch_initcall. In this case the order of linking defines the order of calls. exynos4_pm_init_power_domain() calls exynos_pm_add_dev_to_genpd() which in turn checks for pdev-dev.bus. All devices which have not been registered yet on the bus are simply skipped. Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv9 02/10] ARM: dma-mapping: use pr_* instread of printk
Replace all calls to printk with pr_* functions family. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com Acked-by: Arnd Bergmann a...@arndb.de Tested-By: Subash Patel subash.ramasw...@linaro.org --- arch/arm/mm/dma-mapping.c | 16 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index db23ae4..366f3a2 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -184,14 +184,14 @@ static int __init consistent_init(void) pud = pud_alloc(init_mm, pgd, base); if (!pud) { - printk(KERN_ERR %s: no pud tables\n, __func__); + pr_err(%s: no pud tables\n, __func__); ret = -ENOMEM; break; } pmd = pmd_alloc(init_mm, pud, base); if (!pmd) { - printk(KERN_ERR %s: no pmd tables\n, __func__); + pr_err(%s: no pmd tables\n, __func__); ret = -ENOMEM; break; } @@ -199,7 +199,7 @@ static int __init consistent_init(void) pte = pte_alloc_kernel(pmd, base); if (!pte) { - printk(KERN_ERR %s: no pte tables\n, __func__); + pr_err(%s: no pte tables\n, __func__); ret = -ENOMEM; break; } @@ -222,7 +222,7 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, int bit; if (!consistent_pte) { - printk(KERN_ERR %s: not initialised\n, __func__); + pr_err(%s: not initialised\n, __func__); dump_stack(); return NULL; } @@ -281,14 +281,14 @@ static void __dma_free_remap(void *cpu_addr, size_t size) c = arm_vmregion_find_remove(consistent_head, (unsigned long)cpu_addr); if (!c) { - printk(KERN_ERR %s: trying to free invalid coherent area: %p\n, + pr_err(%s: trying to free invalid coherent area: %p\n, __func__, cpu_addr); dump_stack(); return; } if ((c-vm_end - c-vm_start) != size) { - printk(KERN_ERR %s: freeing wrong coherent size (%ld != %d)\n, + pr_err(%s: freeing wrong coherent size (%ld != %d)\n, __func__, c-vm_end - c-vm_start, size); dump_stack(); size = c-vm_end - c-vm_start; @@ -310,8 +310,8 @@ static void __dma_free_remap(void *cpu_addr, size_t size) } if (pte_none(pte) || !pte_present(pte)) - printk(KERN_CRIT %s: bad page in kernel page table\n, - __func__); + pr_crit(%s: bad page in kernel page table\n, + __func__); } while (size -= PAGE_SIZE); flush_tlb_kernel_range(c-vm_start, c-vm_end); -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv9 08/10] ARM: dma-mapping: remove redundant code and cleanup
This patch just performs a global cleanup in DMA mapping implementation for ARM architecture. Some of the tiny helper functions have been moved to the caller code, some have been merged together. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com Acked-by: Arnd Bergmann a...@arndb.de Tested-By: Subash Patel subash.ramasw...@linaro.org --- arch/arm/mm/dma-mapping.c | 88 1 files changed, 24 insertions(+), 64 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index a4db5ec..615fabd 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -40,64 +40,12 @@ * the CPU does do speculative prefetches, which means we clean caches * before transfers and delay cache invalidation until transfer completion. * - * Private support functions: these are not part of the API and are - * liable to change. Drivers must not use these. */ -static inline void __dma_single_cpu_to_dev(const void *kaddr, size_t size, - enum dma_data_direction dir) -{ - extern void ___dma_single_cpu_to_dev(const void *, size_t, - enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_single_cpu_to_dev(kaddr, size, dir); -} - -static inline void __dma_single_dev_to_cpu(const void *kaddr, size_t size, - enum dma_data_direction dir) -{ - extern void ___dma_single_dev_to_cpu(const void *, size_t, - enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_single_dev_to_cpu(kaddr, size, dir); -} - -static inline void __dma_page_cpu_to_dev(struct page *page, unsigned long off, - size_t size, enum dma_data_direction dir) -{ - extern void ___dma_page_cpu_to_dev(struct page *, unsigned long, +static void __dma_page_cpu_to_dev(struct page *, unsigned long, size_t, enum dma_data_direction); - - if (!arch_is_coherent()) - ___dma_page_cpu_to_dev(page, off, size, dir); -} - -static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, - size_t size, enum dma_data_direction dir) -{ - extern void ___dma_page_dev_to_cpu(struct page *, unsigned long, +static void __dma_page_dev_to_cpu(struct page *, unsigned long, size_t, enum dma_data_direction); - if (!arch_is_coherent()) - ___dma_page_dev_to_cpu(page, off, size, dir); -} - - -static inline dma_addr_t __dma_map_page(struct device *dev, struct page *page, -unsigned long offset, size_t size, enum dma_data_direction dir) -{ - __dma_page_cpu_to_dev(page, offset, size, dir); - return pfn_to_dma(dev, page_to_pfn(page)) + offset; -} - -static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, - size_t size, enum dma_data_direction dir) -{ - __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)), - handle ~PAGE_MASK, size, dir); -} - /** * arm_dma_map_page - map a portion of a page for streaming DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices @@ -112,11 +60,13 @@ static inline void __dma_unmap_page(struct device *dev, dma_addr_t handle, * The device owns this memory once this call has completed. The CPU * can regain ownership by calling dma_unmap_page(). */ -static inline dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, +static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - return __dma_map_page(dev, page, offset, size, dir); + if (!arch_is_coherent()) + __dma_page_cpu_to_dev(page, offset, size, dir); + return pfn_to_dma(dev, page_to_pfn(page)) + offset; } /** @@ -133,27 +83,31 @@ static inline dma_addr_t arm_dma_map_page(struct device *dev, struct page *page, * After this call, reads by the CPU to the buffer are guaranteed to see * whatever the device wrote there. */ -static inline void arm_dma_unmap_page(struct device *dev, dma_addr_t handle, +static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - __dma_unmap_page(dev, handle, size, dir); + if (!arch_is_coherent()) + __dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)), + handle ~PAGE_MASK, size, dir); } -static inline void arm_dma_sync_single_for_cpu(struct device *dev, +static void arm_dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { unsigned int offset = handle (PAGE_SIZE - 1); struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset)); - __dma_page_dev_to_cpu(page, offset, size, dir
[PATCHv9 00/10] ARM: DMA-mapping framework redesign
Hello, This is a quick update on dma-mapping redesign patches for ARM. I did some minor fixes suggested by Arnd and extended commit messages for a few patches. Like the previous version, the patches have been rebased onto latest Linux v3.4-rc3 which comes with dma_map_ops related preparation changes. The patches are also available on my git repository at: git://git.linaro.org/people/mszyprowski/linux-dma-mapping.git 3.4-rc3-arm-dma-v9 The code has been tested on Samsung Exynos4 'UniversalC210' and NURI boards with IOMMU driver posted by KyongHo Cho. The integration patch has been posted in the following thread: http://www.spinics.net/lists/arm-kernel/msg169030.html History of the development: v1: (initial version of the DMA-mapping redesign patches): http://www.spinics.net/lists/linux-mm/msg21241.html v2: http://lists.linaro.org/pipermail/linaro-mm-sig/2011-September/000571.html http://lists.linaro.org/pipermail/linaro-mm-sig/2011-September/000577.html v3: http://www.spinics.net/lists/linux-mm/msg25490.html v4 and v5: http://www.spinics.net/lists/arm-kernel/msg151147.html http://www.spinics.net/lists/arm-kernel/msg154889.html v6: http://www.spinics.net/lists/linux-mm/msg29903.html v7: http://www.spinics.net/lists/arm-kernel/msg162149.html v8: http://www.spinics.net/lists/arm-kernel/msg168478.html Best regards Marek Szyprowski Samsung Poland RD Center Patch summary: Marek Szyprowski (10): common: add dma_mmap_from_coherent() function ARM: dma-mapping: use pr_* instread of printk ARM: dma-mapping: introduce DMA_ERROR_CODE constant ARM: dma-mapping: remove offset parameter to prepare for generic dma_ops ARM: dma-mapping: use asm-generic/dma-mapping-common.h ARM: dma-mapping: implement dma sg methods on top of any generic dma ops ARM: dma-mapping: move all dma bounce code to separate dma ops structure ARM: dma-mapping: remove redundant code and cleanup ARM: dma-mapping: use alloc, mmap, free from dma_ops ARM: dma-mapping: add support for IOMMU mapper arch/arm/Kconfig |9 + arch/arm/common/dmabounce.c| 84 +++- arch/arm/include/asm/device.h |4 + arch/arm/include/asm/dma-iommu.h | 34 ++ arch/arm/include/asm/dma-mapping.h | 407 --- arch/arm/mm/dma-mapping.c | 1015 ++-- arch/arm/mm/vmregion.h |2 +- drivers/base/dma-coherent.c| 42 ++ include/asm-generic/dma-coherent.h |4 +- 9 files changed, 1134 insertions(+), 467 deletions(-) create mode 100644 arch/arm/include/asm/dma-iommu.h -- 1.7.1.569.g6f426 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv10 00/11] ARM: DMA-mapping framework redesign
Hello, This is another update on dma-mapping redesign patches for ARM. I integrated a few minor fixes that were needed to solve the issues reported after putting these patches for testing in linux-next branch. The patches are also available on my git repository at: git://git.linaro.org/people/mszyprowski/linux-dma-mapping.git 3.4-rc7-arm-dma-v10 History of the development: v1: (initial version of the DMA-mapping redesign patches): http://www.spinics.net/lists/linux-mm/msg21241.html v2: http://lists.linaro.org/pipermail/linaro-mm-sig/2011-September/000571.html http://lists.linaro.org/pipermail/linaro-mm-sig/2011-September/000577.html v3: http://www.spinics.net/lists/linux-mm/msg25490.html v4 and v5: http://www.spinics.net/lists/arm-kernel/msg151147.html http://www.spinics.net/lists/arm-kernel/msg154889.html v6: http://www.spinics.net/lists/linux-mm/msg29903.html v7: http://www.spinics.net/lists/arm-kernel/msg162149.html v8: http://www.spinics.net/lists/arm-kernel/msg168478.html v9: http://www.spinics.net/lists/linux-arch/msg17443.html Best regards Marek Szyprowski Samsung Poland RD Center Patch summary: Marek Szyprowski (11): common: add dma_mmap_from_coherent() function ARM: dma-mapping: use dma_mmap_from_coherent() ARM: dma-mapping: use pr_* instread of printk ARM: dma-mapping: introduce DMA_ERROR_CODE constant ARM: dma-mapping: remove offset parameter to prepare for generic dma_ops ARM: dma-mapping: use asm-generic/dma-mapping-common.h ARM: dma-mapping: implement dma sg methods on top of any generic dma ops ARM: dma-mapping: move all dma bounce code to separate dma ops structure ARM: dma-mapping: remove redundant code and do the cleanup ARM: dma-mapping: use alloc, mmap, free from dma_ops ARM: dma-mapping: add support for IOMMU mapper arch/arm/Kconfig |9 + arch/arm/common/dmabounce.c| 84 ++- arch/arm/include/asm/device.h |4 + arch/arm/include/asm/dma-iommu.h | 34 ++ arch/arm/include/asm/dma-mapping.h | 407 --- arch/arm/mm/dma-mapping.c | 1015 ++-- arch/arm/mm/vmregion.h |2 +- drivers/base/dma-coherent.c| 42 ++ include/asm-generic/dma-coherent.h |4 +- 9 files changed, 1134 insertions(+), 467 deletions(-) create mode 100644 arch/arm/include/asm/dma-iommu.h -- 1.7.10.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv10 02/11] ARM: dma-mapping: use dma_mmap_from_coherent()
Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mm/dma-mapping.c |3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index db23ae4..7ec0863 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -401,6 +401,9 @@ static int dma_mmap(struct device *dev, struct vm_area_struct *vma, unsigned long user_size, kern_size; struct arm_vmregion *c; + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, ret)) + return ret; + user_size = (vma-vm_end - vma-vm_start) PAGE_SHIFT; c = arm_vmregion_find(consistent_head, (unsigned long)cpu_addr); -- 1.7.10.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCHv10 04/11] ARM: dma-mapping: introduce DMA_ERROR_CODE constant
Replace all uses of ~0 with DMA_ERROR_CODE, what should make the code easier to read. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Kyungmin Park kyungmin.p...@samsung.com Tested-By: Subash Patel subash.ramasw...@linaro.org --- arch/arm/common/dmabounce.c|6 +++--- arch/arm/include/asm/dma-mapping.h |4 +++- arch/arm/mm/dma-mapping.c |2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index 595ecd29..210ad1b 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -254,7 +254,7 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size, if (buf == NULL) { dev_err(dev, %s: unable to map unsafe buffer %p!\n, __func__, ptr); - return ~0; + return DMA_ERROR_CODE; } dev_dbg(dev, %s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n, @@ -320,7 +320,7 @@ dma_addr_t __dma_map_page(struct device *dev, struct page *page, ret = needs_bounce(dev, dma_addr, size); if (ret 0) - return ~0; + return DMA_ERROR_CODE; if (ret == 0) { __dma_page_cpu_to_dev(page, offset, size, dir); @@ -329,7 +329,7 @@ dma_addr_t __dma_map_page(struct device *dev, struct page *page, if (PageHighMem(page)) { dev_err(dev, DMA buffer bouncing of HIGHMEM pages is not supported\n); - return ~0; + return DMA_ERROR_CODE; } return map_single(dev, page_address(page) + offset, size, dir); diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index cb3b7c9..6a838da 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -10,6 +10,8 @@ #include asm-generic/dma-coherent.h #include asm/memory.h +#define DMA_ERROR_CODE (~0) + #ifdef __arch_page_to_dma #error Please update to __arch_pfn_to_dma #endif @@ -123,7 +125,7 @@ extern int dma_set_mask(struct device *, u64); */ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - return dma_addr == ~0; + return dma_addr == DMA_ERROR_CODE; } /* diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 322c70a..e4ac5fc 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -342,7 +342,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, */ gfp = ~(__GFP_COMP); - *handle = ~0; + *handle = DMA_ERROR_CODE; size = PAGE_ALIGN(size); page = __dma_alloc_buffer(dev, size, gfp); -- 1.7.10.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
RE: How to specify IOMMU'able devices in DT (was: [RFC 0/5] ARM: dma-mapping: New dma_map_ops to control IOVA more precisely)
Hello, On Monday, September 24, 2012 11:45 AM Hiroshi Doyu wrote: On Mon, 24 Sep 2012 11:28:01 +0200 James Bottomley james.bottom...@hansenpartnership.com wrote: On Mon, 2012-09-24 at 12:04 +0300, Hiroshi Doyu wrote: diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a1a7225..9eae3be 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -21,6 +21,8 @@ #include linux/slab.h #include linux/pm_runtime.h +#include asm/dma-iommu.h + #include base.h #define to_platform_driver(drv)(container_of((drv), struct platform_driver, \ @@ -305,8 +307,19 @@ int platform_device_add(struct platform_device *pdev) dev_name(pdev-dev), dev_name(pdev-dev.parent)); ret = device_add(pdev-dev); - if (ret == 0) - return ret; + if (ret) + goto failed; + +#ifdef CONFIG_PLATFORM_ENABLE_IOMMU + if (platform_bus_type.map !pdev-dev.archdata.mapping) { + ret = arm_iommu_attach_device(pdev-dev, + platform_bus_type.map); + if (ret) + goto failed; This is horrible ... you're adding an architecture specific callback into our generic code; that's really a no-no. If the concept of CONFIG_PLATFORM_ENABE_IOMMU is useful to more than just arm, then this could become a generic callback. As mentioned in the original, this is a heck to explain what is needed. I am looking for some generic solution for how to specify IOMMU info for each platform devices. I'm guessing that some other SoC may have the similar requirements on the above. As you mentioned, this solution should be a generic, not arch specific. Please read more about bus notifiers. IMHO a good example is provided in the following thread: http://www.mail-archive.com/linux-samsung-soc@vger.kernel.org/msg12238.html Best regards -- Marek Szyprowski Samsung Poland RD Center ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 3/9] ARM: dma-mapping: Always pass proper prot flags to iommu_map()
Hello, On 2013-09-27 00:36, Andreas Herrmann wrote: ... otherwise it is impossible for the low level iommu driver to figure out which pte flags should be used. In __map_sg_chunk we can derive the flags from dma_data_direction. In __iommu_create_mapping we should treat the memory like DMA_BIDIRECTIONAL and pass both IOMMU_READ and IOMMU_WRITE to iommu_map. __iommu_create_mapping is used during dma_alloc_coherent (via arm_iommu_alloc_attrs). AFAIK dma_alloc_coherent is responsible for allocation _and_ mapping. I think this implies that access to the mapped pages should be allowed. Cc: Marek Szyprowski m.szyprow...@samsung.com Signed-off-by: Andreas Herrmann andreas.herrm...@calxeda.com Thanks pointing the issue and preparing the patch. I will push it to the dma-mapping fixes branch. --- arch/arm/mm/dma-mapping.c | 43 --- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f5e1a84..1272ed2 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1232,7 +1232,8 @@ __iommu_create_mapping(struct device *dev, struct page **pages, size_t size) break; len = (j - i) PAGE_SHIFT; - ret = iommu_map(mapping-domain, iova, phys, len, 0); + ret = iommu_map(mapping-domain, iova, phys, len, + IOMMU_READ|IOMMU_WRITE); if (ret 0) goto fail; iova += len; @@ -1431,6 +1432,27 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt, GFP_KERNEL); } +static int __dma_direction_to_prot(enum dma_data_direction dir) +{ + int prot; + + switch (dir) { + case DMA_BIDIRECTIONAL: + prot = IOMMU_READ | IOMMU_WRITE; + break; + case DMA_TO_DEVICE: + prot = IOMMU_READ; + break; + case DMA_FROM_DEVICE: + prot = IOMMU_WRITE; + break; + default: + prot = 0; + } + + return prot; +} + /* * Map a part of the scatter-gather list into contiguous io address space */ @@ -1444,6 +1466,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, int ret = 0; unsigned int count; struct scatterlist *s; + int prot; size = PAGE_ALIGN(size); *handle = DMA_ERROR_CODE; @@ -1460,7 +1483,9 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) __dma_page_cpu_to_dev(sg_page(s), s-offset, s-length, dir); - ret = iommu_map(mapping-domain, iova, phys, len, 0); + prot = __dma_direction_to_prot(dir); + + ret = iommu_map(mapping-domain, iova, phys, len, prot); if (ret 0) goto fail; count += len PAGE_SHIFT; @@ -1665,19 +1690,7 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p if (dma_addr == DMA_ERROR_CODE) return dma_addr; - switch (dir) { - case DMA_BIDIRECTIONAL: - prot = IOMMU_READ | IOMMU_WRITE; - break; - case DMA_TO_DEVICE: - prot = IOMMU_READ; - break; - case DMA_FROM_DEVICE: - prot = IOMMU_WRITE; - break; - default: - prot = 0; - } + prot = __dma_direction_to_prot(dir); ret = iommu_map(mapping-domain, dma_addr, page_to_phys(page), len, prot); if (ret 0) Best regards -- Marek Szyprowski Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2 1/5] x86: make dma_alloc_coherent() return zeroed memory if CMA is enabled
Hello, On 2014-01-14 15:13, Akinobu Mita wrote: Calling dma_alloc_coherent() with __GFP_ZERO must return zeroed memory. But when the contiguous memory allocator (CMA) is enabled on x86 and the memory region is allocated by dma_alloc_from_contiguous(), it doesn't return zeroed memory. Because dma_generic_alloc_coherent() forgot to fill the memory region with zero if it was allocated by dma_alloc_from_contiguous() I just wonder how it will work with high mem? I've didn't check the x86 dma mapping code yet, but page_address() works only for pages, which comes from low memory. In other patches you have added an option to place CMA area anywhere in the memory. Is the x86 pci dma code ready for the case when cma area is put into high mem and direct mappings are not available? Most implementations of dma_alloc_coherent() return zeroed memory regardless of whether __GFP_ZERO is specified. So this fixes it by unconditionally zeroing the allocated memory region. Cc: Marek Szyprowski m.szyprow...@samsung.com Cc: Konrad Rzeszutek Wilk konrad.w...@oracle.com Cc: David Woodhouse dw...@infradead.org Cc: Don Dutile ddut...@redhat.com Cc: Thomas Gleixner t...@linutronix.de Cc: Ingo Molnar mi...@redhat.com Cc: H. Peter Anvin h...@zytor.com Cc: Andi Kleen a...@firstfloor.org Cc: x...@kernel.org Cc: iommu@lists.linux-foundation.org Signed-off-by: Akinobu Mita akinobu.m...@gmail.com --- New patch from this version arch/x86/kernel/pci-dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 872079a..9644405 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -97,7 +97,6 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size, dma_mask = dma_alloc_coherent_mask(dev, flag); - flag |= __GFP_ZERO; again: page = NULL; if (!(flag GFP_ATOMIC)) @@ -118,7 +117,7 @@ again: return NULL; } - + memset(page_address(page), 0, size); *dma_addr = addr; return page_address(page); } Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 0/2] arm: dma-mapping: add dynamic resize of IOVA bitmap
Hello, This patchset is a continuation of the work started by Andreas Herrmann to add support for dynamically resized bitmaps for IOMMU based DMA-mapping implementation for ARM architecture. Some more discussion and rationale has been discussed in the following thread: http://www.spinics.net/lists/arm-kernel/msg303732.html The first patch adds support for on-demand extending IO address space bitmap. It is based on the original work by Andreas Herrmann, but I decided to drop arm_iommu_create_mapping() api change part. The second patch removes the 'order' hack, which was used to reduce the size of a bitmap. The first patch solved the problem of too large io address space bitmaps, so the 'order' hack is no longer needed. The parameters of the arm_iommu_create_mapping() function can be then simplified by dropping 'order' parameter without any functional change of the whole subsystem. This parameter was already a bit misunderstood, so the overall result is also a little improvement of the API. Best regards Marek Szyprowski, PhD Samsung RD Institute Poland Andreas Herrmann (1): arm: dma-mapping: Add support to extend DMA IOMMU mappings Marek Szyprowski (1): arm: dma-mapping: remove order parameter from arm_iommu_create_mapping() arch/arm/include/asm/dma-iommu.h | 12 ++- arch/arm/mm/dma-mapping.c | 144 +++-- drivers/gpu/drm/exynos/exynos_drm_drv.h |2 - drivers/gpu/drm/exynos/exynos_drm_iommu.c |6 +- drivers/gpu/drm/exynos/exynos_drm_iommu.h |1 - drivers/iommu/shmobile-iommu.c|2 +- 6 files changed, 124 insertions(+), 43 deletions(-) -- 1.7.9.5 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] arm: dma-mapping: Add support to extend DMA IOMMU mappings
From: Andreas Herrmann andreas.herrm...@calxeda.com Instead of using just one bitmap to keep track of IO virtual addresses (handed out for IOMMU use) introduce an array of bitmaps. This allows us to extend existing mappings when running out of iova space in the initial mapping etc. If there is not enough space in the mapping to service an IO virtual address allocation request, __alloc_iova() tries to extend the mapping -- by allocating another bitmap -- and makes another allocation attempt using the freshly allocated bitmap. This allows arm iommu drivers to start with a decent initial size when an dma_iommu_mapping is created and still to avoid running out of IO virtual addresses for the mapping. Signed-off-by: Andreas Herrmann andreas.herrm...@calxeda.com [mszyprow: removed extensions parameter to arm_iommu_create_mapping() function, which will be modified in the next patch anyway, also some debug messages about extending bitmap] Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/include/asm/dma-iommu.h |8 ++- arch/arm/mm/dma-mapping.c| 123 -- 2 files changed, 110 insertions(+), 21 deletions(-) diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h index a8c56ac..686797c 100644 --- a/arch/arm/include/asm/dma-iommu.h +++ b/arch/arm/include/asm/dma-iommu.h @@ -13,8 +13,12 @@ struct dma_iommu_mapping { /* iommu specific data */ struct iommu_domain *domain; - void*bitmap; - size_t bits; + unsigned long **bitmaps; /* array of bitmaps */ + unsigned intnr_bitmaps; /* nr of elements in array */ + unsigned intextensions; + size_t bitmap_size;/* size of a single bitmap */ + size_t bits; /* per bitmap */ + unsigned intsize; /* per bitmap */ unsigned intorder; dma_addr_t base; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index c9c6acdf..cc42bc2 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1066,6 +1066,8 @@ fs_initcall(dma_debug_do_init); /* IOMMU */ +static int extend_iommu_mapping(struct dma_iommu_mapping *mapping); + static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, size_t size) { @@ -1073,6 +1075,8 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, unsigned int align = 0; unsigned int count, start; unsigned long flags; + dma_addr_t iova; + int i; if (order CONFIG_ARM_DMA_IOMMU_ALIGNMENT) order = CONFIG_ARM_DMA_IOMMU_ALIGNMENT; @@ -1084,30 +1088,78 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, align = (1 (order - mapping-order)) - 1; spin_lock_irqsave(mapping-lock, flags); - start = bitmap_find_next_zero_area(mapping-bitmap, mapping-bits, 0, - count, align); - if (start mapping-bits) { - spin_unlock_irqrestore(mapping-lock, flags); - return DMA_ERROR_CODE; + for (i = 0; i mapping-nr_bitmaps; i++) { + start = bitmap_find_next_zero_area(mapping-bitmaps[i], + mapping-bits, 0, count, align); + + if (start mapping-bits) + continue; + + bitmap_set(mapping-bitmaps[i], start, count); + break; } - bitmap_set(mapping-bitmap, start, count); + /* +* No unused range found. Try to extend the existing mapping +* and perform a second attempt to reserve an IO virtual +* address range of size bytes. +*/ + if (i == mapping-nr_bitmaps) { + if (extend_iommu_mapping(mapping)) { + spin_unlock_irqrestore(mapping-lock, flags); + return DMA_ERROR_CODE; + } + + start = bitmap_find_next_zero_area(mapping-bitmaps[i], + mapping-bits, 0, count, align); + + if (start mapping-bits) { + spin_unlock_irqrestore(mapping-lock, flags); + return DMA_ERROR_CODE; + } + + bitmap_set(mapping-bitmaps[i], start, count); + } spin_unlock_irqrestore(mapping-lock, flags); - return mapping-base + (start (mapping-order + PAGE_SHIFT)); + iova = mapping-base + (mapping-size * i); + iova += start (mapping-order + PAGE_SHIFT); + + return iova; } static inline void __free_iova(struct dma_iommu_mapping *mapping, dma_addr_t addr, size_t size) { - unsigned int start = (addr - mapping-base
Re: [PATCH 0/5] OMAP IOMMU fixes and IOMMU architecture questions
Hello, I'm sorry for a delay, I've got back from my holidays and I'm still busy trying to get thought all the emails. On 2014-03-17 23:44, Laurent Pinchart wrote: Hi Suman and Sakari, On Monday 17 March 2014 14:58:24 Suman Anna wrote: On 03/16/2014 04:54 PM, Sakari Ailus wrote: On Fri, Mar 14, 2014 at 12:00:16PM +0100, Laurent Pinchart wrote: Hi Suman, (CC'ing Joerg Roedel and Marek Szyprowski for the core IOMMU discussion) On Thursday 13 March 2014 21:33:37 Suman Anna wrote: On 03/07/2014 06:46 PM, Laurent Pinchart wrote: Hello, This patch set fixes miscellaneous issues with the OMAP IOMMU driver, found when trying to port the OMAP3 ISP away from omap-iovmm to the ARM DMA API. The biggest issue is fixed by patch 5/5, while the other patches fix smaller problems that I've noticed when reading the code, without experiencing them at runtime. I'd like to take this as an opportunity to discuss OMAP IOMMU integration with the ARM DMA mapping implementation. The idea is to hide the IOMMU completely behind the DMA mapping API, making it completely transparent to drivers. Thanks for starting the discussion. A drivers will only need to allocate memory with dma_alloc_*, and behind the scene the ARM DMA mapping implementation will find out that the device is behind an IOMMU and will map the buffers through the IOMMU, returning an I/O VA address to the driver. No direct call to the OMAP IOMMU driver or to the IOMMU core should be necessary anymore. To use the IOMMU the ARM DMA implementation requires a VA mapping to be created with a call to arm_iommu_create_mapping() and to then be attached to the device with arm_iommu_attach_device(). This is currently not performed by the OMAP IOMMU driver, I have thus added that code to the OMAP3 ISP driver for now. I believe this to still be an improvement compared to the current situation, as it allows getting rid of custom memory allocation code in the OMAP3 ISP driver and custom I/O VA space management in omap-iovmm. However, that code should ideally be removed from the driver. The question is, where should it be moved to ? One possible solution would be to add the code to the OMAP IOMMU driver. However, this would require all OMAP IOMMU users to be converted to the ARM DMA API. I assume there would be issues that I don't foresee though. Yeah, I think this will pose some problems for the other main user of IOMMUs - the remoteproc devices (DSP, Dual-Cortex M3/M4 processors in OMAP4 and beyond). A remoteproc device also needs to map memory at specific addresses for its code and data sections, and not rely on a I/O VA address being given to it. The firmware sections are already linked at specific addresses, and so remoteproc needs to allocate memory, load the firmware and map into appropriate device addresses. We are doing this currently usage a combination of CMA memory to get contiguous memory (there are some restrictions for certain sections) and iommu_map/unmap API to program the MMU with these pages. This usage is different from what is expected from exchanging buffers, which can be allocated from a predefined mapping range. Even that one is tricky if we need to support different cache properties/attributes as the cache configuration is in general local to these processors. Right, we indeed need two levels of API, one for drivers such as remoteproc that need direct control of the IOMMU, and one for drivers that only need to map buffers without any additional requirement. In the second case the drivers should ideally use the DMA mapping API not even be aware that an IOMMU is present. This would require moving the ARM mapping allocation out of the client driver. Actually, I think there is one another use case, even with remoteprocs which is to runtime map buffers. This is different from the firmware management. The memory for buffers could have been allocated from other subsystems, but remoteproc would need just to manage the VA space and map. Right, although you might not always have to manage the VA space in that case. Anyway, if your driver needs to manage the VA space for the firmware, it should be able to manage the VA space for the buffers using the same IOMMU API. I've already seen some use cases which require to give a client device limited access to DMA mapping IOMMU related structures. If we agree on API, I see no problems to add such calls to dma-mapping. However in the most common case I would like to hide the presence of IOMMU from the client device at all. The IOMMU core or the IOMMU driver will need to know whether the driver expects to control the IOMMU directly or to have it managed transparently. As this is a software configuration I don't think the information belongs to DT. The question is, how should this information be conveyed? Can this be done through iommu_domain_set_attr? But that means
Re: [PATCH] arm: dma-mapping: Fix mapping size value
Hello, On 2014-04-21 08:47, Ritesh Harjani wrote: 68efd7d2fb(arm: dma-mapping: remove order parameter from arm_iommu_create_mapping()) is causing kernel panic because it wrongly sets the value of mapping-size: Unable to handle kernel NULL pointer dereference at virtual address 00a0 pgd = e7a84000 [00a0] *pgd= ... PC is at bitmap_clear+0x48/0xd0 LR is at __iommu_remove_mapping+0x130/0x164 Fix it by correcting mapping-size value. Signed-off-by: Ritesh Harjani ritesh.harj...@gmail.com Acked-by: Laurent Pinchart laurent.pinch...@ideasonboard.com Thanks for spotting this issue! I'm really sorry for introducing it. I will push it to the fixes branch asap. --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa06..6b00be1 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1963,8 +1963,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) mapping-nr_bitmaps = 1; mapping-extensions = extensions; mapping-base = base; - mapping-size = bitmap_size PAGE_SHIFT; mapping-bits = BITS_PER_BYTE * bitmap_size; + mapping-size = mapping-bits PAGE_SHIFT; spin_lock_init(mapping-lock); Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] arm: dma-mapping: Fix mapping size value
Hello, On 2014-04-22 10:53, Will Deacon wrote: On Mon, Apr 21, 2014 at 07:47:27AM +0100, Ritesh Harjani wrote: 68efd7d2fb(arm: dma-mapping: remove order parameter from arm_iommu_create_mapping()) is causing kernel panic because it wrongly sets the value of mapping-size: Unable to handle kernel NULL pointer dereference at virtual address 00a0 pgd = e7a84000 [00a0] *pgd= ... PC is at bitmap_clear+0x48/0xd0 LR is at __iommu_remove_mapping+0x130/0x164 Fix it by correcting mapping-size value. Signed-off-by: Ritesh Harjani ritesh.harj...@gmail.com Acked-by: Laurent Pinchart laurent.pinch...@ideasonboard.com --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa06..6b00be1 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1963,8 +1963,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) mapping-nr_bitmaps = 1; mapping-extensions = extensions; mapping-base = base; - mapping-size = bitmap_size PAGE_SHIFT; mapping-bits = BITS_PER_BYTE * bitmap_size; + mapping-size = mapping-bits PAGE_SHIFT; Ok, but given that mapping-size is derived from mapping-bits, do we really need both of these fields in struct dma_iommu_mapping? You are right. I didn't notice this while I was refactoring the code. Ritesh, could you update your patch and simply replace all references of mapping-size with (mapping-bits PAGE_SHIFT), probably with some temporary variable to make the code easier to understand? I've didn't apply your patch yet. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] arm: dma-mapping: Fix mapping size value
Hello, On 2014-04-23 12:04, Ritesh Harjani wrote: Hi Marek/Will On Wed, Apr 23, 2014 at 3:00 PM, Laurent Pinchart laurent.pinch...@ideasonboard.com wrote: Hi Marek, On Wednesday 23 April 2014 10:53:24 Marek Szyprowski wrote: On 2014-04-22 10:53, Will Deacon wrote: On Mon, Apr 21, 2014 at 07:47:27AM +0100, Ritesh Harjani wrote: 68efd7d2fb(arm: dma-mapping: remove order parameter from arm_iommu_create_mapping()) is causing kernel panic because it wrongly sets the value of mapping-size: Unable to handle kernel NULL pointer dereference at virtual address 00a0 pgd = e7a84000 [00a0] *pgd= ... PC is at bitmap_clear+0x48/0xd0 LR is at __iommu_remove_mapping+0x130/0x164 Fix it by correcting mapping-size value. Signed-off-by: Ritesh Harjani ritesh.harj...@gmail.com Acked-by: Laurent Pinchart laurent.pinch...@ideasonboard.com --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa06..6b00be1 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1963,8 +1963,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) mapping-nr_bitmaps = 1; mapping-extensions = extensions; mapping-base = base; - mapping-size = bitmap_size PAGE_SHIFT; mapping-bits = BITS_PER_BYTE * bitmap_size; + mapping-size = mapping-bits PAGE_SHIFT; Ok, but given that mapping-size is derived from mapping-bits, do we really need both of these fields in struct dma_iommu_mapping? You are right. I didn't notice this while I was refactoring the code. Ritesh, could you update your patch and simply replace all references of mapping-size with (mapping-bits PAGE_SHIFT), probably with some temporary variable to make the code easier to understand? I've didn't apply your patch yet. As this patch fixes a v3.15 regression, shouldn't it be applied as-is and ASAP, with the cleanup that removes mapping-size coming in a later, less urgent patch ? I agree with Laurent. Anyway this cleanup can be taken care when we will be doing refactoring of common code to lib/iommu-helper.c. Anyways, if you still insist I can prepare and submit the patch. Let me know again on this. Ok, I've merged the patch as is and I will send pull request soon. Please include the above discussed cleanup while refactoring common code to lib. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v2] arm: dma-iommu: Clean up redundant variable
Hello, On 2014-05-20 06:32, ritesh.harj...@gmail.com wrote: From: Ritesh Harjani ritesh.harj...@gmail.com mapping-size can be derived from mapping-bits PAGE_SHIFT which makes mapping-size as redundant. Clean this up. Signed-off-by: Ritesh Harjani ritesh.harj...@gmail.com Reported-by: Will Deacon will.dea...@arm.com Thanks for this cleanup. I will add it to my tree. --- arch/arm/include/asm/dma-iommu.h | 1 - arch/arm/mm/dma-mapping.c| 11 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h index eec0a12..8e3fcb9 100644 --- a/arch/arm/include/asm/dma-iommu.h +++ b/arch/arm/include/asm/dma-iommu.h @@ -18,7 +18,6 @@ struct dma_iommu_mapping { unsigned intextensions; size_t bitmap_size;/* size of a single bitmap */ size_t bits; /* per bitmap */ - unsigned intsize; /* per bitmap */ dma_addr_t base; spinlock_t lock; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 6b00be1..3d43c41 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1074,6 +1074,7 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, unsigned int order = get_order(size); unsigned int align = 0; unsigned int count, start; + size_t mapping_size = mapping-bits PAGE_SHIFT; unsigned long flags; dma_addr_t iova; int i; @@ -1119,7 +1120,7 @@ static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping, } spin_unlock_irqrestore(mapping-lock, flags); - iova = mapping-base + (mapping-size * i); + iova = mapping-base + (mapping_size * i); iova += start PAGE_SHIFT; return iova; @@ -1129,6 +1130,7 @@ static inline void __free_iova(struct dma_iommu_mapping *mapping, dma_addr_t addr, size_t size) { unsigned int start, count; + size_t mapping_size = mapping-bits PAGE_SHIFT; unsigned long flags; dma_addr_t bitmap_base; u32 bitmap_index; @@ -1136,14 +1138,14 @@ static inline void __free_iova(struct dma_iommu_mapping *mapping, if (!size) return; - bitmap_index = (u32) (addr - mapping-base) / (u32) mapping-size; + bitmap_index = (u32) (addr - mapping-base) / (u32) mapping_size; BUG_ON(addr mapping-base || bitmap_index mapping-extensions); - bitmap_base = mapping-base + mapping-size * bitmap_index; + bitmap_base = mapping-base + mapping_size * bitmap_index; start = (addr - bitmap_base) PAGE_SHIFT; - if (addr + size bitmap_base + mapping-size) { + if (addr + size bitmap_base + mapping_size) { /* * The address range to be freed reaches into the iova * range of the next bitmap. This should not happen as @@ -1964,7 +1966,6 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) mapping-extensions = extensions; mapping-base = base; mapping-bits = BITS_PER_BYTE * bitmap_size; - mapping-size = mapping-bits PAGE_SHIFT; spin_lock_init(mapping-lock); Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 07/29] ARM: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mm/dma-mapping.c | 16 1 file changed, 16 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 7a996aa..8161102 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2051,6 +2051,22 @@ int arm_iommu_attach_device(struct device *dev, { int err; + /* +* if device has no max_seg_size set, we assume that there is no limit +* and force it to DMA_BIT_MASK(32) to always use contiguous mappings +* in DMA address space +*/ + if (!dev-dma_parms) { + dev-dma_parms = kzalloc(sizeof(*dev-dma_parms), GFP_KERNEL); + if (!dev-dma_parms) + return -ENOMEM; + } + if (!dev-dma_parms-max_segment_size) { + err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + if (err) + return err; + } + err = iommu_attach_device(mapping-domain, dev); if (err) return err; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 00/29] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
/host/pci-mvebu.c | 2 +- drivers/pci/host/pci-rcar-gen2.c | 2 +- drivers/pci/host/pci-tegra.c | 2 +- drivers/pci/host/pcie-rcar.c | 2 +- drivers/soc/tegra/pmc.c| 2 +- include/dt-bindings/clock/exynos4.h| 10 +- include/linux/device.h | 12 +- include/linux/iommu.h | 1 + include/linux/pm.h | 2 + include/linux/pm_domain.h | 19 + 33 files changed, 1016 insertions(+), 356 deletions(-) Best regards Marek Szyprowski Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 08/29] ARM: dma-mapping: add helpers for managing default per-device dma mappings
This patch adds 2 helpers: arm_iommu_create_default_mapping and arm_iommu_release_default_mapping for managing default iommu-based dma-mapping address space, created for exlusive use only by the given device. Those helpers are convenient for setting up iommu-based dma-mapping for most typical devices in the system. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/include/asm/dma-iommu.h | 5 + arch/arm/mm/dma-mapping.c| 31 +++ 2 files changed, 36 insertions(+) diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h index 8e3fcb9..ae3dac0 100644 --- a/arch/arm/include/asm/dma-iommu.h +++ b/arch/arm/include/asm/dma-iommu.h @@ -33,5 +33,10 @@ int arm_iommu_attach_device(struct device *dev, struct dma_iommu_mapping *mapping); void arm_iommu_detach_device(struct device *dev); +int arm_iommu_create_default_mapping(struct device *dev, dma_addr_t base, +size_t size); + +void arm_iommu_release_default_mapping(struct device *dev); + #endif /* __KERNEL__ */ #endif diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 8161102..233a8cf 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2106,4 +2106,35 @@ void arm_iommu_detach_device(struct device *dev) } EXPORT_SYMBOL_GPL(arm_iommu_detach_device); +int arm_iommu_create_default_mapping(struct device *dev, dma_addr_t base, +size_t size) +{ + struct dma_iommu_mapping *mapping; + int ret; + + mapping = arm_iommu_create_mapping(dev-bus, base, size); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + arm_iommu_release_mapping(mapping); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(arm_iommu_create_default_mapping); + +void arm_iommu_release_default_mapping(struct device *dev) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + if (!mapping) + return; + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} +EXPORT_SYMBOL_GPL(arm_iommu_release_default_mapping); + #endif -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 02/29] ARM: Exynos: bind power domains earlier, on device creation
This patches change initialization time of power domain driver from client device driver bind to device creation. This lets other core drivers to register power domain notifiers before client driver is bound. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mach-exynos/pm_domains.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index fd76e1b..1d368a2 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -159,13 +159,13 @@ static int exynos_pm_notifier_call(struct notifier_block *nb, struct device *dev = data; switch (event) { - case BUS_NOTIFY_BIND_DRIVER: + case BUS_NOTIFY_ADD_DEVICE: if (dev-of_node) exynos_read_domain_from_dt(dev); break; - case BUS_NOTIFY_UNBOUND_DRIVER: + case BUS_NOTIFY_DEL_DEVICE: exynos_remove_device_from_domain(dev); break; @@ -177,6 +177,13 @@ static struct notifier_block platform_nb = { .notifier_call = exynos_pm_notifier_call, }; +static int exynos_pm_domain_add(struct device *dev, void *priv) +{ + if (dev-of_node) + exynos_read_domain_from_dt(dev); + return 0; +} + static __init int exynos4_pm_init_power_domain(void) { struct platform_device *pdev; @@ -236,6 +243,7 @@ no_clk: } bus_register_notifier(platform_bus_type, platform_nb); + bus_for_each_dev(platform_bus_type, NULL, NULL, exynos_pm_domain_add); return 0; } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 11/29] DRM: exynos: add DRIVER_HAS_OWN_IOMMU_MANAGER flag to all sub-drivers
Exynos DRM drivers do their own management of IO address space of all controlled devices, so set DRIVER_HAS_OWN_IOMMU_MANAGER flag to instruct IOMMU subsystem not to create any defaults for them. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimc.c| 1 + drivers/gpu/drm/exynos/exynos_drm_fimd.c| 1 + drivers/gpu/drm/exynos/exynos_drm_g2d.c | 1 + drivers/gpu/drm/exynos/exynos_drm_gsc.c | 1 + drivers/gpu/drm/exynos/exynos_drm_rotator.c | 1 + drivers/gpu/drm/exynos/exynos_mixer.c | 1 + 6 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 831dde9..fe84215 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1896,6 +1896,7 @@ struct platform_driver fimc_driver = { .name = exynos-drm-fimc, .owner = THIS_MODULE, .pm = fimc_pm_ops, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 33161ad..41904df 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -1031,5 +1031,6 @@ struct platform_driver fimd_driver = { .name = exynos4-fb, .owner = THIS_MODULE, .of_match_table = fimd_driver_dt_match, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 8001587..dddeae3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1555,5 +1555,6 @@ struct platform_driver g2d_driver = { .owner = THIS_MODULE, .pm = g2d_pm_ops, .of_match_table = exynos_g2d_match, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 9e3ff16..76e8b1e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1797,6 +1797,7 @@ struct platform_driver gsc_driver = { .name = exynos-drm-gsc, .owner = THIS_MODULE, .pm = gsc_pm_ops, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index f01fbb6..2da91c4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -852,5 +852,6 @@ struct platform_driver rotator_driver = { .owner = THIS_MODULE, .pm = rotator_pm_ops, .of_match_table = exynos_rotator_match, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 7529946..79b1309a 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1320,6 +1320,7 @@ struct platform_driver mixer_driver = { .name = exynos-mixer, .owner = THIS_MODULE, .of_match_table = mixer_match_types, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, .probe = mixer_probe, .remove = mixer_remove, -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 09/29] ARM: dma-mapping: provide stubs if no ARM_DMA_USE_IOMMU has been selected
This patch provides stubs returing errors for all iommu related arm dma-mapping functions, which are used when CONFIG_ARM_DMA_USE_IOMMU is not set. This let drivers to use common code for iommu and non-iommu cases without additional ifdefs. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/include/asm/dma-iommu.h | 31 +++ 1 file changed, 31 insertions(+) diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h index ae3dac0..1e57569 100644 --- a/arch/arm/include/asm/dma-iommu.h +++ b/arch/arm/include/asm/dma-iommu.h @@ -9,6 +9,8 @@ #include linux/kmemcheck.h #include linux/kref.h +#ifdef CONFIG_ARM_DMA_USE_IOMMU + struct dma_iommu_mapping { /* iommu specific data */ struct iommu_domain *domain; @@ -38,5 +40,34 @@ int arm_iommu_create_default_mapping(struct device *dev, dma_addr_t base, void arm_iommu_release_default_mapping(struct device *dev); +#else + +static inline struct dma_iommu_mapping * +arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void +arm_iommu_release_mapping(struct dma_iommu_mapping *mapping) { } + +static inline int arm_iommu_attach_device(struct device *dev, + struct dma_iommu_mapping *mapping) +{ + return -ENOSYS; +} + +static inline void arm_iommu_detach_device(struct device *dev) { } + +static inline int arm_iommu_create_default_mapping(struct device *dev, + dma_addr_t base, size_t size) +{ + return -ENOSYS; +} + +static inline void arm_iommu_release_default_mapping(struct device *dev) { } + +#endif + #endif /* __KERNEL__ */ #endif -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 18/29] iommu: exynos: remove unused functions
This patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver. Signed-off-by: Marek Szyprowski 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 8927923..ec3c882 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -491,13 +491,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; @@ -589,30 +582,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; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 14/29] devicetree: Update Exynos SYSMMU device tree bindings
This patch describes how generic iommu bindings are implemented by Exynos SYSMMU driver. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- .../devicetree/bindings/iommu/samsung,sysmmu.txt | 93 +++--- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt index 6fa4c73..999ba6d 100644 --- a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt +++ b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt @@ -23,16 +23,33 @@ MMUs. 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. -Note: -The current DT binding for the Exynos System MMU is incomplete. -The following properties can be removed or changed, if found incompatible with -the Generic IOMMU Binding support for attaching devices to the IOMMU. +The drivers must consider how to handle those System MMUs. When device +have more than one SYSMMU controller it is neccessary to add +iommu-names property, which specifies which SYSMMU controller operates +on which bus or memory channel. -Required properties: +It is up to the master device driver to decide how such case will be +handled. It is possible to create separate IO address spaces for each +SYSMMU or to bind them together to one common IO address space. It is +also possible to bind more than one device to one IO address space. All +this has to be handled by master device driver in its initialization +procedure or flags and no changes to device tree nodes are needed. + +In Linux kernel, the general idea is that presence of the SYSMMU +controllers is transparent to master drivers if they use standard DMA +API. When driver wants to use IO separate address spaces for each bus or +memory channel (each SYSMMU) or to bind more than one device to one IO +address space, it has to specify this to SYSMMU driver by +DRIVER_HAS_OWN_IOMMU_MANAGER flag. To get access to each SYSMMU bound to +the given device, additional child devices with special names (matching +parent:bus scheme) have to be registered. Once then, all standard +IOMMU operations can be performed on such child devices, what will +result in respective operations done on IO address space managed by +SYSMMU of the given name. Other operating systems might implement those +features differently. + +Required properties for SYSMMU controller node: - compatible: Should be samsung,exynos-sysmmu - reg: A tuple of base address and size of System MMU registers. - interrupt-parent: The phandle of the interrupt controller of System MMU @@ -45,11 +62,27 @@ Required properties: Exynos4 SoCs, there needs no master clock. Exynos5 SoCs, some System MMUs must have master clocks. - clocks: Required if the System MMU is needed to gate its clock. +- #iommu-cells: Specify number of cells describing IO address space parameters, + can be: 0 (zero), meaning all 32bit address space is available, + or 2, if address space is limited, first cell then stores + base IO address, second cell contains IO window size in bytes. - 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 -Examples: +Required properties for master device: +- iommus: one or more phandles to the SYSMMU controller node, with optionally + specified IO address space (see #iommu-cells property above) +- iommu-names: if more than one SYSMMU controller is specified, this property + must contain names for each of them. Those names are defined by + the bindings for a particular master device. + +For more information, please refer to generic iommu bindings defined in +iommu.txt file. + +Example 1: +GScaller device with one SYSMMU controller + gsc_0: gsc@13e0 { compatible = samsung,exynos5-gsc; reg = 0x13e0 0x1000; @@ -57,6 +90,7 @@ Examples: samsung,power-domain = pd_gsc; clocks = clock CLK_GSCL0; clock-names = gscl; + iommus = sysmmu_gsc0; }; sysmmu_gsc0: sysmmu@13E8 { @@ -67,4 +101,45 @@ Examples: clock-names = sysmmu, master; clocks = clock CLK_SMMU_GSCL0, clock CLK_GSCL0; samsung,power-domain = pd_gsc; + #iommu-cells = 0; + }; + +Example 2: +MFC Codec with two SYSMMU controllers (on left and right bus), with address +space limited to 256MiB each, left bus starts IO address space at 0x2000, +while right bus at 0x3000
[PATCH 05/29] drivers: convert suppress_bind_attrs parameter into flags
This patch extends struct device_driver with a flags member and converts existing suppress_bind_attrs bool field to a flag. This way new flags can be easily added in the future without changing the structure itself. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mach-integrator/impd1.c | 2 +- drivers/base/bus.c | 4 ++-- drivers/base/platform.c | 2 +- drivers/pci/host/pci-mvebu.c | 2 +- drivers/pci/host/pci-rcar-gen2.c | 2 +- drivers/pci/host/pci-tegra.c | 2 +- drivers/pci/host/pcie-rcar.c | 2 +- drivers/soc/tegra/pmc.c | 2 +- include/linux/device.h | 6 -- 9 files changed, 13 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c index 3ce8807..a7e7330 100644 --- a/arch/arm/mach-integrator/impd1.c +++ b/arch/arm/mach-integrator/impd1.c @@ -406,7 +406,7 @@ static struct lm_driver impd1_driver = { * As we're dropping the probe() function, suppress driver * binding from sysfs. */ - .suppress_bind_attrs = true, + .flags = DRIVER_SUPPRESS_BIND_ATTRS, }, .probe = impd1_probe, .remove = impd1_remove, diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 83e910a..f223f26 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -707,7 +707,7 @@ int bus_add_driver(struct device_driver *drv) __func__, drv-name); } - if (!drv-suppress_bind_attrs) { + if (!(drv-flags DRIVER_SUPPRESS_BIND_ATTRS)) { error = add_bind_files(drv); if (error) { /* Ditto */ @@ -740,7 +740,7 @@ void bus_remove_driver(struct device_driver *drv) if (!drv-bus) return; - if (!drv-suppress_bind_attrs) + if (!(drv-flags DRIVER_SUPPRESS_BIND_ATTRS)) remove_bind_files(drv); driver_remove_groups(drv, drv-bus-drv_groups); driver_remove_file(drv, driver_attr_uevent); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 68a8b77..c696058 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -608,7 +608,7 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, drv-prevent_deferred_probe = true; /* make sure driver won't have bind/unbind attributes */ - drv-driver.suppress_bind_attrs = true; + drv-driver.flags = DRIVER_SUPPRESS_BIND_ATTRS; /* temporary section violation during probe() */ drv-probe = probe; diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index a8c6f1a..6815c50 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -1086,7 +1086,7 @@ static struct platform_driver mvebu_pcie_driver = { .name = mvebu-pcie, .of_match_table = mvebu_pcie_of_match_table, /* driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, + .flags = DRIVER_SUPPRESS_BIND_ATTRS, }, .probe = mvebu_pcie_probe, }; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 3ef854f..6f1b890 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -413,7 +413,7 @@ static struct platform_driver rcar_pci_driver = { .driver = { .name = pci-rcar-gen2, .owner = THIS_MODULE, - .suppress_bind_attrs = true, + .flags = DRIVER_SUPPRESS_BIND_ATTRS, .of_match_table = rcar_pci_of_match, }, .probe = rcar_pci_probe, diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0fb0fdb..2e1698d 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -1927,7 +1927,7 @@ static struct platform_driver tegra_pcie_driver = { .name = tegra-pcie, .owner = THIS_MODULE, .of_match_table = tegra_pcie_of_match, - .suppress_bind_attrs = true, + .flags = DRIVER_SUPPRESS_BIND_ATTRS, }, .probe = tegra_pcie_probe, }; diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 4884ee5..9a1936e 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -981,7 +981,7 @@ static struct platform_driver rcar_pcie_driver = { .name = DRV_NAME, .owner = THIS_MODULE, .of_match_table = rcar_pcie_of_match, - .suppress_bind_attrs = true, + .flags = DRIVER_SUPPRESS_BIND_ATTRS, }, .probe = rcar_pcie_probe, }; diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index a2c0ceb..77c3eb3 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -892,7 +892,7 @@ static const struct
[PATCH 20/29] iommu: exynos: refactor function parameters to simplify code
This patch simplifies the code by: - refactoring function parameters from struct device pointer to direct pointer to struct sysmmu drvdata - moving list_head enteries from struct exynos_iommu_owner directly to struct sysmmu_drvdata 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 ed8c518..018a615 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -187,8 +187,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; }; @@ -509,12 +508,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); @@ -528,14 +525,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)) { @@ -565,8 +558,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); } @@ -705,7 +698,7 @@ err_pgtable: static void exynos_iommu_domain_destroy(struct iommu_domain *domain) { struct exynos_iommu_domain *priv = domain-priv; - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags; int i; @@ -713,14 +706,12 @@ static void exynos_iommu_domain_destroy(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) { + 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++) @@ -739,20 +730,26 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, { struct exynos_iommu_owner *owner = dev-archdata.iommu; struct exynos_iommu_domain *priv = domain-priv; + 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) { + ret = __sysmmu_enable(data, pagetable, domain); + if (ret = 0) { + data-master = dev; + + spin_lock_irqsave(priv-lock, flags); + list_add_tail(data-domain_node, priv-clients
[PATCH 13/29] temporary: media: s5p-mfc: remove DT hacks initialization custom memory init code
This patch removes custom initialization of reserved memory regions from s5p-mfc driver. Driver will use main device pointer for all memory allocations. This patch is temporary, do not merge it yet. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 73 +--- 1 file changed, 2 insertions(+), 71 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index d35b041..77b99ae 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -996,55 +996,8 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; -static int match_child(struct device *dev, void *data) -{ - if (!dev_name(dev)) - return 0; - return !strcmp(dev_name(dev), (char *)data); -} - static void *mfc_get_drv_data(struct platform_device *pdev); -static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) -{ - unsigned int mem_info[2] = { }; - - dev-mem_dev_l = devm_kzalloc(dev-plat_dev-dev, - sizeof(struct device), GFP_KERNEL); - if (!dev-mem_dev_l) { - mfc_err(Not enough memory\n); - return -ENOMEM; - } - device_initialize(dev-mem_dev_l); - of_property_read_u32_array(dev-plat_dev-dev.of_node, - samsung,mfc-l, mem_info, 2); - if (dma_declare_coherent_memory(dev-mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err(Failed to declare coherent memory for\n - MFC device\n); - return -ENOMEM; - } - - dev-mem_dev_r = devm_kzalloc(dev-plat_dev-dev, - sizeof(struct device), GFP_KERNEL); - if (!dev-mem_dev_r) { - mfc_err(Not enough memory\n); - return -ENOMEM; - } - device_initialize(dev-mem_dev_r); - of_property_read_u32_array(dev-plat_dev-dev.of_node, - samsung,mfc-r, mem_info, 2); - if (dma_declare_coherent_memory(dev-mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err(Failed to declare coherent memory for\n - MFC device\n); - return -ENOMEM; - } - return 0; -} - /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1096,26 +1049,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - if (pdev-dev.of_node) { - ret = s5p_mfc_alloc_memdevs(dev); - if (ret 0) - goto err_res; - } else { - dev-mem_dev_l = device_find_child(dev-plat_dev-dev, - s5p-mfc-l, match_child); - if (!dev-mem_dev_l) { - mfc_err(Mem child (L) device get failed\n); - ret = -ENODEV; - goto err_res; - } - dev-mem_dev_r = device_find_child(dev-plat_dev-dev, - s5p-mfc-r, match_child); - if (!dev-mem_dev_r) { - mfc_err(Mem child (R) device get failed\n); - ret = -ENODEV; - goto err_res; - } - } + dev-mem_dev_l = dev-plat_dev-dev; + dev-mem_dev_r = dev-plat_dev-dev; dev-alloc_ctx[0] = vb2_dma_contig_init_ctx(dev-mem_dev_l); if (IS_ERR(dev-alloc_ctx[0])) { @@ -1246,10 +1181,6 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[1]); - if (pdev-dev.of_node) { - put_device(dev-mem_dev_l); - put_device(dev-mem_dev_r); - } s5p_mfc_final_pm(dev); return 0; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 12/29] DRM: Exynos: fix window clear code
To correctly disable hardware window during driver init, both enable bits (WINCONx_ENWIN in WINCON and SHADOWCON_CHx_ENABLE in SHADOWCON) must be cleared, otherwise hardware fails to re-enable such window later. While touching this function, also temporarily disable ctx-suspended flag to let fimd_wait_for_vblank function really to do its job. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 25 ++--- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 41904df..7a363d2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -165,27 +165,38 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) DRM_DEBUG_KMS(vblank wait timed out.\n); } - static void fimd_clear_channel(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr-ctx; - int win, ch_enabled = 0; + int state, win, ch_enabled = 0; DRM_DEBUG_KMS(%s\n, __FILE__); /* Check if any channel is enabled. */ for (win = 0; win WINDOWS_NR; win++) { - u32 val = readl(ctx-regs + SHADOWCON); - if (val SHADOWCON_CHx_ENABLE(win)) { - val = ~SHADOWCON_CHx_ENABLE(win); - writel(val, ctx-regs + SHADOWCON); + u32 val = readl(ctx-regs + WINCON(win)); + if (val WINCONx_ENWIN) { + /* wincon */ + val = ~WINCONx_ENWIN; + writel(val, ctx-regs + WINCON(win)); + + /* unprotect windows */ + if (ctx-driver_data-has_shadowcon) { + val = readl(ctx-regs + SHADOWCON); + val = ~SHADOWCON_CHx_ENABLE(win); + writel(val, ctx-regs + SHADOWCON); + } ch_enabled = 1; } } /* Wait for vsync, as disable channel takes effect at next vsync */ - if (ch_enabled) + if (ch_enabled) { + state = ctx-suspended; + ctx-suspended = 0; fimd_wait_for_vblank(mgr); + ctx-suspended = state; + } } static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 04/29] drivers: base: add notifier for failed driver bind
This patch adds support for getting a notify for failed device driver bind, so all the items done in BUS_NOTIFY_BIND_DRIVER event can be cleaned if the driver fails to bind. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/base/dd.c | 10 +++--- include/linux/device.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e4ffbcf..541a41f 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -237,10 +237,14 @@ static int driver_sysfs_add(struct device *dev) return ret; } -static void driver_sysfs_remove(struct device *dev) +static void driver_sysfs_remove(struct device *dev, int failed) { struct device_driver *drv = dev-driver; + if (failed dev-bus) + blocking_notifier_call_chain(dev-bus-p-bus_notifier, +BUS_NOTIFY_DRVBIND_FAILED, dev); + if (drv) { sysfs_remove_link(drv-p-kobj, kobject_name(dev-kobj)); sysfs_remove_link(dev-kobj, driver); @@ -316,7 +320,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) probe_failed: devres_release_all(dev); - driver_sysfs_remove(dev); + driver_sysfs_remove(dev, true); dev-driver = NULL; dev_set_drvdata(dev, NULL); @@ -509,7 +513,7 @@ static void __device_release_driver(struct device *dev) if (drv) { pm_runtime_get_sync(dev); - driver_sysfs_remove(dev); + driver_sysfs_remove(dev, false); if (dev-bus) blocking_notifier_call_chain(dev-bus-p-bus_notifier, diff --git a/include/linux/device.h b/include/linux/device.h index b387710..92daded 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -176,7 +176,7 @@ extern int bus_register_notifier(struct bus_type *bus, extern int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb); -/* All 4 notifers below get called with the target struct device * +/* All 7 notifers below get called with the target struct device * * as an argument. Note that those functions are likely to be called * with the device lock held in the core, so be careful. */ @@ -189,6 +189,8 @@ extern int bus_unregister_notifier(struct bus_type *bus, unbound */ #define BUS_NOTIFY_UNBOUND_DRIVER 0x0006 /* driver is unbound from the device */ +#define BUS_NOTIFY_DRVBIND_FAILED 0x0007 /* driver failed to bind + to device */ extern struct kset *bus_get_kset(struct bus_type *bus); extern struct klist *bus_get_device_klist(struct bus_type *bus); -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 10/29] drivers: add DRIVER_HAS_OWN_IOMMU_MANAGER flag
This patch adds a new flags for device drivers. This flag instructs kernel that the device driver does it own management of IOMMU assisted IO address space translations, so no default dma-mapping structures should be initialized. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 5f4ff02..2e62371 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -253,6 +253,8 @@ struct device_driver { /* disables bind/unbind via sysfs */ #define DRIVER_SUPPRESS_BIND_ATTRS (1 0) +/* driver uses own methods to manage IO address space */ +#define DRIVER_HAS_OWN_IOMMU_MANAGER (1 1) extern int __must_check driver_register(struct device_driver *drv); extern void driver_unregister(struct device_driver *drv); -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 03/29] clk: exynos: add missing smmu_g2d clock and update comments
This patch adds missing smmu_g2d clock implementation and updates comment about Exynos4 clocks from 278-282 range. Those clocks are available on all Exynos4 SoC series, so the misleading comment has been removed. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 10 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ac163d7..12a7cc3 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1183,6 +1183,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_SPI1_ISP, spi1_isp, aclk200, E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_G2D, g2d, aclk200, GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, smmu_g2d, aclk200, GATE_IP_DMC, 24, 0, 0), GATE(CLK_TMU_APBIF, tmu_apbif, aclk100, E4X12_GATE_IP_PERIR, 17, 0, 0), }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 459bd2b..fb981635 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -115,11 +115,11 @@ #define CLK_SMMU_MFCR 275 #define CLK_G3D276 #define CLK_G2D277 -#define CLK_ROTATOR278 /* Exynos4210 only */ -#define CLK_MDMA 279 /* Exynos4210 only */ -#define CLK_SMMU_G2D 280 /* Exynos4210 only */ -#define CLK_SMMU_ROTATOR 281 /* Exynos4210 only */ -#define CLK_SMMU_MDMA 282 /* Exynos4210 only */ +#define CLK_ROTATOR278 +#define CLK_MDMA 279 +#define CLK_SMMU_G2D 280 +#define CLK_SMMU_ROTATOR 281 +#define CLK_SMMU_MDMA 282 #define CLK_FIMD0 283 #define CLK_MIE0 284 #define CLK_MDNIE0 285 /* Exynos4412 only */ -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 17/29] iommu: exynos: don't read version register on every tlb operation
This patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data. Signed-off-by: Marek Szyprowski 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 64b3bc8..8927923 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; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -239,11 +240,6 @@ static void sysmmu_unblock(void __iomem *sfrbase) __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); } -static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) -{ - return MMU_RAW_VER(__raw_readl(data-sfrbase + REG_MMU_VERSION)); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -403,7 +399,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) unsigned int cfg = CFG_LRU | CFG_QOS(15); unsigned int ver; - ver = __raw_sysmmu_version(data); + ver = MMU_RAW_VER(__raw_readl(data-sfrbase + REG_MMU_VERSION)); if (MMU_MAJ_VER(ver) == 3) { if (MMU_MIN_VER(ver) = 2) { cfg |= CFG_FLPDCACHE; @@ -417,6 +413,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) } __raw_writel(cfg, data-sfrbase + REG_MMU_CFG); + data-version = ver; } static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) @@ -526,7 +523,7 @@ static bool exynos_sysmmu_disable(struct device *dev) static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { - if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + if (data-version == MAKE_MMU_VER(3, 3)) __raw_writel(iova | 0x1, data-sfrbase + REG_MMU_FLUSH_ENTRY); } @@ -575,7 +572,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, * 1MB page can be cached in one of all sets. * 64KB page can be one of 16 consecutive sets. */ - if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) + if (MMU_MAJ_VER(data-version) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data-sfrbase)) { -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 25/29] iommu: exynos: add support for runtime_pm
This patch enables support for runtime pm for SYSMMU controllers. State of each controller is saved before master's device power domain is turned off and restored after it has been turned on. exynos_iommu_attach_device() function might be called anytime, even when power domain for master's device has been turned off, so to let SYSMMU controllers to access its registers, a call to pm_runtime_get_sync() has to be done, which turns on the power domain, which SYSMMU belongs to. Later, once SYSMMU has been enabled, a call to pm_runtime_put() lets runtime pm to turn off the power domain if there are no other devices enabled. This way, the SYSMMU drivers get a genpd pm event and save its state with sysmmu_save_state() function. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 54 +++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 336b2f8..5cd91b11 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -28,6 +28,7 @@ #include linux/list.h #include linux/memblock.h #include linux/export.h +#include linux/pm_domain.h #include asm/cacheflush.h #include asm/dma-iommu.h @@ -208,6 +209,7 @@ struct sysmmu_drvdata { struct clk *clk; struct clk *clk_master; int activations; + int suspended; spinlock_t lock; struct iommu_domain *domain; struct list_head domain_node; @@ -217,6 +219,7 @@ struct sysmmu_drvdata { const char *name; dma_addr_t base; size_t size; + struct notifier_block pm_notifier; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -235,7 +238,7 @@ static bool set_sysmmu_inactive(struct sysmmu_drvdata *data) static bool is_sysmmu_active(struct sysmmu_drvdata *data) { - return data-activations 0; + return (!data-suspended data-activations 0); } static void sysmmu_unblock(void __iomem *sfrbase) @@ -528,6 +531,51 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, spin_unlock_irqrestore(data-lock, flags); } +static void sysmmu_restore_state(struct sysmmu_drvdata *data) +{ + unsigned long flags; + + spin_lock_irqsave(data-lock, flags); + if (data-activations 0) { + data-suspended = false; + __sysmmu_enable_nocount(data); + dev_dbg(data-sysmmu, restored state\n); + } + spin_unlock_irqrestore(data-lock, flags); +} + +static void sysmmu_save_state(struct sysmmu_drvdata *data) +{ + unsigned long flags; + + spin_lock_irqsave(data-lock, flags); + if (data-activations 0) { + __sysmmu_disable_nocount(data); + data-suspended = true; + dev_dbg(data-sysmmu, saved state\n); + } + spin_unlock_irqrestore(data-lock, flags); +} + +static int sysmmu_runtime_genpd_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct sysmmu_drvdata *data; + + data = container_of(this, struct sysmmu_drvdata, pm_notifier); + + switch (event) { + case PM_GENPD_POST_POWER_ON: + sysmmu_restore_state(data); + break; + case PM_GENPD_POWER_OFF_PREPARE: + sysmmu_save_state(data); + break; + } + + return NOTIFY_DONE; +} + static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; @@ -580,6 +628,7 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) } data-sysmmu = dev; + data-pm_notifier.notifier_call = sysmmu_runtime_genpd_event; /* default io address space parameters */ data-base = SZ_1G; @@ -708,6 +757,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return -ENODEV; list_for_each_entry(data, owner-clients, owner_node) { + pm_runtime_get_sync(data-sysmmu); ret = __sysmmu_enable(data, pagetable, domain); if (ret = 0) { data-master = dev; @@ -716,6 +766,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, list_add_tail(data-domain_node, priv-clients); spin_unlock_irqrestore(priv-lock, flags); } + pm_runtime_put(data-sysmmu); } if (ret 0) { @@ -1156,6 +1207,7 @@ static int __init_master_sysmmu(struct device *dev) } list_add_tail(data-owner_node, owner-clients); + pm_genpd_register_notifier(dev, data-pm_notifier); i++; } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 16/29] iommu: exynos: make driver multiarch friendly
Initialize all structures and register to iommu subsystem only on Exynos compatible platforms. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index d037e87..64b3bc8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -20,6 +20,7 @@ #include linux/clk.h #include linux/err.h #include linux/mm.h +#include linux/of.h #include linux/iommu.h #include linux/errno.h #include linux/list.h @@ -1187,6 +1188,11 @@ static int __init exynos_iommu_init(void) { int ret; + if (!of_machine_is_compatible(samsung,exynos3) + !of_machine_is_compatible(samsung,exynos4) + !of_machine_is_compatible(samsung,exynos5)) + return -ENODEV; + lv2table_kmem_cache = kmem_cache_create(exynos-iommu-lv2table, LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL); if (!lv2table_kmem_cache) { -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 19/29] iommu: exynos: remove useless spinlock
This patch removes useless spinlocks and other unused members from struct exynos_iommu_owner. There is no point is protecting this structure by spinlock because content of this structure doesn't change and other structures have their own spinlocks. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ec3c882..ed8c518 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -190,9 +190,6 @@ struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; struct device *sysmmu; - struct iommu_domain *domain; - void *vmm_data; /* IO virtual memory manager's data */ - spinlock_t lock;/* Lock to preserve consistency of System MMU */ }; struct exynos_iommu_domain { @@ -478,16 +475,12 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(owner-lock, flags); - data = dev_get_drvdata(owner-sysmmu); ret = __sysmmu_enable(data, pgtable, domain); if (ret = 0) data-master = dev; - spin_unlock_irqrestore(owner-lock, flags); - return ret; } @@ -500,16 +493,12 @@ static bool exynos_sysmmu_disable(struct device *dev) BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(owner-lock, flags); - data = dev_get_drvdata(owner-sysmmu); disabled = __sysmmu_disable(data); if (disabled) data-master = NULL; - spin_unlock_irqrestore(owner-lock, flags); - return disabled; } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 15/29] ARM: DTS: Exynos4: add System MMU nodes
This patch adds System MMU nodes that are specific to Exynos4210/4x12 series. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/boot/dts/exynos4.dtsi| 118 ++ arch/arm/boot/dts/exynos4210.dtsi | 23 arch/arm/boot/dts/exynos4x12.dtsi | 82 ++ 3 files changed, 223 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 3385b17..a76b4e5 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -174,6 +174,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc0; status = disabled; }; @@ -185,6 +186,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc1; status = disabled; }; @@ -196,6 +198,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc2; status = disabled; }; @@ -207,6 +210,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc3; status = disabled; }; @@ -395,6 +399,9 @@ clocks = clock CLK_MFC; clock-names = mfc; status = disabled; + iommus = sysmmu_mfc_l 0x2000 0x1000, +sysmmu_mfc_r 0x3000 0x1000; + iommu-names = left, right; }; serial_0: serial@1380 { @@ -642,6 +649,117 @@ clocks = clock CLK_SCLK_FIMD0, clock CLK_FIMD0; clock-names = sclk_fimd, fimd; samsung,power-domain = pd_lcd0; + iommus = sysmmu_fimd0; status = disabled; }; + + sysmmu_mfc_l: sysmmu@1362 { + compatible = samsung,exynos-sysmmu; + reg = 0x1362 0x1000; + interrupt-parent = combiner; + interrupts = 5 5; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_MFCL, clock CLK_MFC; + samsung,power-domain = pd_mfc; + #iommu-cells = 2; + }; + + sysmmu_mfc_r: sysmmu@1363 { + compatible = samsung,exynos-sysmmu; + reg = 0x1363 0x1000; + interrupt-parent = combiner; + interrupts = 5 6; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_MFCR, clock CLK_MFC; + samsung,power-domain = pd_mfc; + #iommu-cells = 2; + }; + + sysmmu_tv: sysmmu@12E2 { + compatible = samsung,exynos-sysmmu; + reg = 0x12E2 0x1000; + interrupt-parent = combiner; + interrupts = 5 4; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_TV, clock CLK_MIXER; + samsung,power-domain = pd_tv; + #iommu-cells = 0; + }; + + sysmmu_fimc0: sysmmu@11A2 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A2 0x1000; + interrupt-parent = combiner; + interrupts = 4 2; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC0, clock CLK_FIMC0; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc1: sysmmu@11A3 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A3 0x1000; + interrupt-parent = combiner; + interrupts = 4 3; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC1, clock CLK_FIMC1; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc2: sysmmu@11A4 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A4 0x1000; + interrupt-parent = combiner; + interrupts = 4 4; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC2, clock CLK_FIMC2; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc3: sysmmu@11A5 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A5 0x1000; + interrupt-parent = combiner
[PATCH 23/29] iommu: exynos: init iommu controllers from device tree
This patch adds code to initialize and assign SYSMMU controllers to their master devices defined in device tree. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 152 +-- 1 file changed, 145 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 46e0edc..845f547 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -21,6 +21,7 @@ #include linux/err.h #include linux/mm.h #include linux/of.h +#include linux/of_platform.h #include linux/iommu.h #include linux/errno.h #include linux/list.h @@ -211,6 +212,9 @@ struct sysmmu_drvdata { struct list_head owner_node; phys_addr_t pgtable; int version; + const char *name; + dma_addr_t base; + size_t size; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -574,6 +578,11 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) } data-sysmmu = dev; + + /* default io address space parameters */ + data-base = SZ_1G; + data-size = SZ_2G; + spin_lock_init(data-lock); platform_set_drvdata(pdev, data); @@ -1055,30 +1064,159 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } +static void __free_owner_struct(struct exynos_iommu_owner *owner, + struct device *dev) +{ + while (!list_empty(owner-clients)) + list_del_init(owner-clients.next); + + kfree(owner); + dev-archdata.iommu = NULL; +} + +static int __init_master_sysmmu(struct device *dev) +{ + struct of_phandle_args sysmmu_spec; + struct exynos_iommu_owner *owner; + int i = 0; + int ret; + + owner = kzalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) + return -ENOMEM; + INIT_LIST_HEAD(owner-clients); + + while (!of_parse_phandle_with_args(dev-of_node, iommus, + #iommu-cells, i, + sysmmu_spec)) { + struct platform_device *sysmmu; + struct sysmmu_drvdata *data; + + sysmmu = of_find_device_by_node(sysmmu_spec.np); + if (!sysmmu) { + dev_err(dev, sysmmu node not found\n); + ret = -ENODEV; + goto err; + } + data = platform_get_drvdata(sysmmu); + if (!data) { + ret = -ENODEV; + goto err; + } + + of_property_read_string_index(dev-of_node, iommu-names, i, + data-name); + + if (sysmmu_spec.args_count == 2) { + data-base = sysmmu_spec.args[0]; + data-size = sysmmu_spec.args[1]; + } else if (sysmmu_spec.args_count != 0) { + dev_err(dev, incorrect iommu property specified\n); + ret = -EINVAL; + goto err; + } + + list_add_tail(data-owner_node, owner-clients); + + i++; + } + + if (i == 0) { + ret = -ENODEV; + goto err; + } + + dev-archdata.iommu = owner; + dev_dbg(dev, registered %d sysmmu controllers\n, i); + + return 0; +err: + __free_owner_struct(owner, dev); + return ret; +} + +static int __init_subdevice_sysmmu(struct device *dev) +{ + struct device *parent = dev-parent; + struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; + char *name; + + name = strrchr(dev_name(dev), ':'); + if (!name) + return -ENODEV; + name++; + + owner = parent-archdata.iommu; + if (!owner) + return -ENODEV; + + list_for_each_entry(data, owner-clients, owner_node) + if (strcmp(name, data-name) == 0) + break; + if (!data) + return -ENODEV; + + owner = kzalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) + return -ENOMEM; + INIT_LIST_HEAD(owner-clients); + + /* move sysmmu from parent to child device */ + list_del(data-owner_node); + list_add_tail(data-owner_node, owner-clients); + + dev-archdata.iommu = owner; + dev_dbg(dev-parent, + registered sysmmu controller for %s subdevice\n, data-name); + + return 0; +} + static int exynos_iommu_add_device(struct device *dev) { struct iommu_group *group; int ret; - group = iommu_group_get(dev); + BUG_ON(dev-archdata.iommu != NULL); - if (!group) { - group = iommu_group_alloc(); - if (IS_ERR(group)) { - dev_err(dev, Failed
[PATCH 27/29] iommu: exynos: document internal structures
Add a few words of comment to all internal structures used by the driver. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 59 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7600861..78ce618 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -189,37 +189,54 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { UNKNOWN FAULT }; -/* attached to dev.archdata.iommu of the master device */ +/* + * This structure is attached to dev.archdata.iommu of the master device + * on device add, contains a list of SYSMMU controllers defined by device tree, + * which are bound to given master device. It is usually referenced by 'owner' + * pointer. + */ struct exynos_iommu_owner { - struct list_head clients; + struct list_head clients; /* list of sysmmu_drvdata.owner_node */ }; +/* + * This structure is stored in -priv field of generic struct iommu_domain, + * contains list of SYSMMU controllers from all master devices, which has been + * attached to this domain and page tables of IO address space defined by this + * domain. It is usually referenced by 'domain' pointer. + */ struct exynos_iommu_domain { - struct list_head clients; /* list of sysmmu_drvdata.node */ + struct list_head clients; /* list of sysmmu_drvdata.domain_node */ sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */ short *lv2entcnt; /* free lv2 entry counter for each section */ - spinlock_t lock; /* lock for this structure */ + spinlock_t lock; /* lock for modyfying list of clients */ spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ }; +/* + * This structure hold all data of a single SYSMMU controller, this includes + * hw resources like registers and clocks, pointers and list nodes to connect + * it to all other structures, internal state and parameters read from device + * tree. It is usually referenced by 'data' pointer. + */ struct sysmmu_drvdata { - struct device *sysmmu; /* System MMU's device descriptor */ - struct device *master; /* Owner of system MMU */ - void __iomem *sfrbase; - struct clk *clk; - struct clk *clk_master; - int activations; - int suspended; - spinlock_t lock; - struct iommu_domain *domain; - struct list_head domain_node; - struct list_head owner_node; - phys_addr_t pgtable; - int version; - const char *name; - dma_addr_t base; - size_t size; - struct notifier_block pm_notifier; + struct device *sysmmu; /* SYSMMU controller device */ + struct device *master; /* master device (owner of given SYSMMU) */ + void __iomem *sfrbase; /* our registers */ + struct clk *clk; /* SYSMMU's clock */ + struct clk *clk_master; /* master's device clock */ + int activations; /* number of calls to sysmmu_enable */ + int suspended; /* status of the controller (managed by runtime pm) */ + spinlock_t lock; /* lock for modyfying enable/disable state */ + struct iommu_domain *domain; /* domain we belong to */ + struct list_head domain_node; /* node for domain clients list */ + struct list_head owner_node; /* node for owner clients list */ + phys_addr_t pgtable; /* assigned page table structure */ + int version; /* our version */ + const char *name; /* our name from device tree */ + dma_addr_t base; /* base addres of IO address space define in DT */ + size_t size; /* size of IO address space define in DT */ + struct notifier_block pm_notifier; /* notifier for pm domain on/off */ }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 21/29] iommu: exynos: remove unused functions, part 2
After refactoring functions to use pointer to struct sysmmu_drvdata directly, some functions became useless and thus never used, so remove them completely. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 43 --- 1 file changed, 43 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 018a615..674d1fb 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -458,49 +458,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, return ret; } -/* __exynos_sysmmu_enable: Enables System MMU - * - * returns -error if an error occurred and System MMU is not enabled, - * 0 if the System MMU has been just enabled and 1 if System MMU was already - * enabled before. - */ -static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, - struct iommu_domain *domain) -{ - int ret = 0; - unsigned long flags; - struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner-sysmmu); - - ret = __sysmmu_enable(data, pgtable, domain); - if (ret = 0) - data-master = dev; - - return ret; -} - -static bool exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner-sysmmu); - - disabled = __sysmmu_disable(data); - if (disabled) - data-master = NULL; - - return disabled; -} - static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 26/29] iommu: exynos: rename variables to reflect their purpose
This patch renames some variables to make the code easier to understand. 'domain' is replaced by 'iommu_domain' (more generic entity) and really meaning less 'priv' by 'domain' to reflect its purpose. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 191 ++- 1 file changed, 97 insertions(+), 94 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5cd91b11..7600861 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -440,8 +440,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) clk_disable(data-clk_master); } -static int __sysmmu_enable(struct sysmmu_drvdata *data, - phys_addr_t pgtable, struct iommu_domain *domain) +static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, + struct iommu_domain *iommu_domain) { int ret = 0; unsigned long flags; @@ -449,7 +449,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, spin_lock_irqsave(data-lock, flags); if (set_sysmmu_active(data)) { data-pgtable = pgtable; - data-domain = domain; + data-domain = iommu_domain; __sysmmu_enable_nocount(data); @@ -664,92 +664,93 @@ static inline void pgtable_flush(void *vastart, void *vaend) virt_to_phys(vaend)); } -static int exynos_iommu_domain_init(struct iommu_domain *domain) +static int exynos_iommu_domain_init(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv; + struct exynos_iommu_domain *domain; int i; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return -ENOMEM; - priv-pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); - if (!priv-pgtable) + domain-pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); + if (!domain-pgtable) goto err_pgtable; - priv-lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!priv-lv2entcnt) + domain-lv2entcnt = (short *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!domain-lv2entcnt) goto err_counter; /* w/a of System MMU v3.3 to prevent caching 1MiB mapping */ for (i = 0; i NUM_LV1ENTRIES; i += 8) { - priv-pgtable[i + 0] = ZERO_LV2LINK; - priv-pgtable[i + 1] = ZERO_LV2LINK; - priv-pgtable[i + 2] = ZERO_LV2LINK; - priv-pgtable[i + 3] = ZERO_LV2LINK; - priv-pgtable[i + 4] = ZERO_LV2LINK; - priv-pgtable[i + 5] = ZERO_LV2LINK; - priv-pgtable[i + 6] = ZERO_LV2LINK; - priv-pgtable[i + 7] = ZERO_LV2LINK; + domain-pgtable[i + 0] = ZERO_LV2LINK; + domain-pgtable[i + 1] = ZERO_LV2LINK; + domain-pgtable[i + 2] = ZERO_LV2LINK; + domain-pgtable[i + 3] = ZERO_LV2LINK; + domain-pgtable[i + 4] = ZERO_LV2LINK; + domain-pgtable[i + 5] = ZERO_LV2LINK; + domain-pgtable[i + 6] = ZERO_LV2LINK; + domain-pgtable[i + 7] = ZERO_LV2LINK; } - pgtable_flush(priv-pgtable, priv-pgtable + NUM_LV1ENTRIES); + pgtable_flush(domain-pgtable, domain-pgtable + NUM_LV1ENTRIES); - spin_lock_init(priv-lock); - spin_lock_init(priv-pgtablelock); - INIT_LIST_HEAD(priv-clients); + spin_lock_init(domain-lock); + spin_lock_init(domain-pgtablelock); + INIT_LIST_HEAD(domain-clients); - domain-geometry.aperture_start = 0; - domain-geometry.aperture_end = ~0UL; - domain-geometry.force_aperture = true; + iommu_domain-geometry.aperture_start = 0; + iommu_domain-geometry.aperture_end = ~0UL; + iommu_domain-geometry.force_aperture = true; - domain-priv = priv; + iommu_domain-priv = domain; return 0; err_counter: - free_pages((unsigned long)priv-pgtable, 2); + free_pages((unsigned long)domain-pgtable, 2); err_pgtable: - kfree(priv); + kfree(domain); return -ENOMEM; } -static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +static void exynos_iommu_domain_destroy(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv = domain-priv; + struct exynos_iommu_domain *domain = iommu_domain-priv; struct sysmmu_drvdata *data; unsigned long flags; int i; - WARN_ON(!list_empty(priv-clients)); + WARN_ON(!list_empty(domain-clients)); - spin_lock_irqsave(priv-lock, flags); + spin_lock_irqsave(domain-lock, flags); - list_for_each_entry(data, priv-clients, domain_node
[PATCH 24/29] iommu: exynos: create default iommu-based dma-mapping for master devices
This patch adds code to create default per-device iommu-based dma-mapping instance for all master devices, whose driver didn't set DRIVER_HAS_OWN_IOMMU_MANAGER flag. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 49 +++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 845f547..336b2f8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,6 +12,7 @@ #define DEBUG #endif +#include linux/dma-mapping.h #include linux/io.h #include linux/interrupt.h #include linux/platform_device.h @@ -29,6 +30,7 @@ #include linux/export.h #include asm/cacheflush.h +#include asm/dma-iommu.h #include asm/pgtable.h typedef u32 sysmmu_iova_t; @@ -1064,6 +1066,43 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } +static int sysmmu_master_device_event(struct notifier_block *nb, + unsigned long val, void *p) +{ + struct device *dev = p; + struct exynos_iommu_owner *owner = dev-archdata.iommu; + struct sysmmu_drvdata *data; + + if (!owner) + return 0; + + data = list_first_entry(owner-clients, struct sysmmu_drvdata, + owner_node); + if (!data) + return 0; + + switch (val) { + + case IOMMU_GROUP_NOTIFY_BIND_DRIVER: + if (!(dev-driver-flags DRIVER_HAS_OWN_IOMMU_MANAGER)) + arm_iommu_create_default_mapping(dev, data-base, +data-size); + break; + + case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER: + case IOMMU_GROUP_NOTIFY_DRVBIND_FAILED: + if (!(dev-driver-flags DRIVER_HAS_OWN_IOMMU_MANAGER)) + arm_iommu_release_default_mapping(dev); + break; + } + + return 0; +} + +static struct notifier_block sysmmu_master_device_notifier = { + .notifier_call = sysmmu_master_device_event, +}; + static void __free_owner_struct(struct exynos_iommu_owner *owner, struct device *dev) { @@ -1202,6 +1241,7 @@ static int exynos_iommu_add_device(struct device *dev) if (ret != 0) goto err; + iommu_group_register_notifier(group, sysmmu_master_device_notifier); iommu_group_put(group); return 0; @@ -1213,8 +1253,15 @@ err: static void exynos_iommu_remove_device(struct device *dev) { struct exynos_iommu_owner *owner = dev-archdata.iommu; + struct iommu_group *group = iommu_group_get(dev); + + if (group) { + iommu_group_unregister_notifier(group, + sysmmu_master_device_notifier); + iommu_group_remove_device(dev); + iommu_group_put(group); + } - iommu_group_remove_device(dev); if (owner) __free_owner_struct(owner, dev); } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 28/29] iommu: exynos: remove excessive includes and sort others alphabetically
Removed following unused includes: linux/mm.h, linux/errno.h, linux/memblock.h and linux/export.h. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 18 +++--- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 78ce618..1bbb100 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,23 +12,19 @@ #define DEBUG #endif +#include linux/clk.h #include linux/dma-mapping.h +#include linux/err.h #include linux/io.h +#include linux/iommu.h #include linux/interrupt.h -#include linux/platform_device.h -#include linux/slab.h -#include linux/pm_runtime.h -#include linux/clk.h -#include linux/err.h -#include linux/mm.h +#include linux/list.h #include linux/of.h #include linux/of_platform.h -#include linux/iommu.h -#include linux/errno.h -#include linux/list.h -#include linux/memblock.h -#include linux/export.h +#include linux/platform_device.h #include linux/pm_domain.h +#include linux/pm_runtime.h +#include linux/slab.h #include asm/cacheflush.h #include asm/dma-iommu.h -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 22/29] iommu: exynos: add support for binding more than one sysmmu to master device
This patch adds support for assigning more than one SYSMMU controller to the master device. This has been achieved simply by chaning the struct device pointer in struct exynos_iommu_owner into the list of struct sysmmu_drvdata of all controllers assigned to the given master device. Signed-off-by: Marek Szyprowski 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 674d1fb..46e0edc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -187,7 +187,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { /* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct device *sysmmu; + struct list_head clients; }; struct exynos_iommu_domain { @@ -208,6 +208,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; struct list_head domain_node; + struct list_head owner_node; phys_addr_t pgtable; int version; }; @@ -695,8 +696,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return -ENODEV; - data = dev_get_drvdata(owner-sysmmu); - if (data) { + list_for_each_entry(data, owner-clients, owner_node) { ret = __sysmmu_enable(data, pagetable, domain); if (ret = 0) { data-master = dev; @@ -724,7 +724,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, { struct exynos_iommu_domain *priv = domain-priv; phys_addr_t pagetable = virt_to_phys(priv-pgtable); - struct sysmmu_drvdata *data; + struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0; @@ -732,14 +732,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, return; spin_lock_irqsave(priv-lock, flags); - list_for_each_entry(data, priv-clients, domain_node) { + list_for_each_entry_safe(data, next, priv-clients, domain_node) { if (data-master == dev) { if (__sysmmu_disable(data)) { data-master = NULL; list_del_init(data-domain_node); } found = true; - break; } } spin_unlock_irqrestore(priv-lock, flags); -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 29/29] temporary: media: s5p-mfc: add support for IOMMU
This patch is an example how to use more than one IOMMU controller in a device driver for hardware blocks, which have more then one dma master (memory interface with iommu controller). This patch is temporary, do not merge it yet. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 46 ++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 77b99ae..c2e95ab 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -22,7 +22,11 @@ #include media/v4l2-event.h #include linux/workqueue.h #include linux/of.h +#include linux/of_platform.h #include media/videobuf2-core.h + +#include asm/dma-iommu.h + #include s5p_mfc_common.h #include s5p_mfc_ctrl.h #include s5p_mfc_debug.h @@ -996,6 +1000,39 @@ static const struct v4l2_file_operations s5p_mfc_fops = { .mmap = s5p_mfc_mmap, }; +static struct device *s5p_mfc_alloc_memdev(struct device *dev, const char *name) +{ + struct device *child; + int ret; + + child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL); + if (!child) + return NULL; + + device_initialize(child); + dev_set_name(child, %s:%s, dev_name(dev), name); + child-parent = dev; + child-bus = dev-bus; + child-coherent_dma_mask = dev-coherent_dma_mask; + child-dma_mask = dev-dma_mask; + + if (device_add(child) == 0) { + ret = arm_iommu_create_default_mapping(child, 0x2000, + SZ_256M); + if (ret == 0) + return child; + } + + put_device(child); + return NULL; +} + +void s5p_mfc_free_memdev(struct device *dev) +{ + arm_iommu_release_default_mapping(dev); + put_device(dev); +} + static void *mfc_get_drv_data(struct platform_device *pdev); /* MFC probe function */ @@ -1049,8 +1086,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - dev-mem_dev_l = dev-plat_dev-dev; - dev-mem_dev_r = dev-plat_dev-dev; + dev-mem_dev_l = s5p_mfc_alloc_memdev(dev-plat_dev-dev, left); + dev-mem_dev_r = s5p_mfc_alloc_memdev(dev-plat_dev-dev, right); dev-alloc_ctx[0] = vb2_dma_contig_init_ctx(dev-mem_dev_l); if (IS_ERR(dev-alloc_ctx[0])) { @@ -1181,6 +1218,10 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[1]); + if (dev-mem_dev_l) + s5p_mfc_free_memdev(dev-mem_dev_l); + if (dev-mem_dev_r) + s5p_mfc_free_memdev(dev-mem_dev_r); s5p_mfc_final_pm(dev); return 0; @@ -1436,6 +1477,7 @@ static struct platform_driver s5p_mfc_driver = { .owner = THIS_MODULE, .pm = s5p_mfc_pm_ops, .of_match_table = exynos_mfc_match, + .flags = DRIVER_HAS_OWN_IOMMU_MANAGER, }, }; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 00/29] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
Hello, On 2014-08-19 01:32, Joerg Roedel wrote: On Tue, Aug 05, 2014 at 12:47:28PM +0200, Marek Szyprowski wrote: .../devicetree/bindings/iommu/samsung,sysmmu.txt | 93 ++- Documentation/power/notifiers.txt | 14 + arch/arm/boot/dts/exynos4.dtsi | 118 arch/arm/boot/dts/exynos4210.dtsi | 23 + arch/arm/boot/dts/exynos4x12.dtsi | 82 +++ arch/arm/include/asm/dma-iommu.h | 36 ++ arch/arm/mach-exynos/pm_domains.c | 12 +- arch/arm/mach-integrator/impd1.c | 2 +- arch/arm/mm/dma-mapping.c | 47 ++ drivers/base/bus.c | 4 +- drivers/base/dd.c | 10 +- drivers/base/platform.c| 2 +- drivers/base/power/domain.c| 70 ++- drivers/clk/samsung/clk-exynos4.c | 1 + drivers/gpu/drm/exynos/exynos_drm_fimc.c | 1 + drivers/gpu/drm/exynos/exynos_drm_fimd.c | 26 +- drivers/gpu/drm/exynos/exynos_drm_g2d.c| 1 + drivers/gpu/drm/exynos/exynos_drm_gsc.c| 1 + drivers/gpu/drm/exynos/exynos_drm_rotator.c| 1 + drivers/gpu/drm/exynos/exynos_mixer.c | 1 + drivers/iommu/exynos-iommu.c | 663 + drivers/iommu/iommu.c | 3 + drivers/media/platform/s5p-mfc/s5p_mfc.c | 107 ++-- drivers/pci/host/pci-mvebu.c | 2 +- drivers/pci/host/pci-rcar-gen2.c | 2 +- drivers/pci/host/pci-tegra.c | 2 +- drivers/pci/host/pcie-rcar.c | 2 +- drivers/soc/tegra/pmc.c| 2 +- include/dt-bindings/clock/exynos4.h| 10 +- include/linux/device.h | 12 +- include/linux/iommu.h | 1 + include/linux/pm.h | 2 + include/linux/pm_domain.h | 19 + 33 files changed, 1016 insertions(+), 356 deletions(-) This touches a lot of non-iommu stuff. What is your strategy on getting this in, do you plan to get the non-iommu changes merged first or do you want to collect the respective Acks and merge this all through one tree? Those patches are posted as one patchset mainly to demonstrate how to get everything to work together. I also posted this as a single patch series to get some feedback from other iommu developers, especially all those involved in the generic iommu dt bindings. For merging, I will split them into smaller series and try to get respective acks. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 00/29] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
Hello, On 2014-08-19 13:39, Andreas Färber wrote: Hi Marek and Inki, Am 19.08.2014 08:07, schrieb Marek Szyprowski: On 2014-08-19 01:32, Joerg Roedel wrote: On Tue, Aug 05, 2014 at 12:47:28PM +0200, Marek Szyprowski wrote: [...] 33 files changed, 1016 insertions(+), 356 deletions(-) This touches a lot of non-iommu stuff. What is your strategy on getting this in, do you plan to get the non-iommu changes merged first or do you want to collect the respective Acks and merge this all through one tree? Those patches are posted as one patchset mainly to demonstrate how to get everything to work together. I also posted this as a single patch series to get some feedback from other iommu developers, especially all those involved in the generic iommu dt bindings. For merging, I will split them into smaller series and try to get respective acks. I'm working on 5250 based Spring Chromebook and noticed that v3.17-rc1 got some more iommu support. With the new CONFIG_DRM_EXYNOS_IOMMU=y my machine stops booting. So I'm wondering, is any of this a fix for 3.17, or is all of this unrelated -next material? This is probably a side effect of patch 3170447c1f264d51b8d1f3898bf2588588a64fdc (iommu/exynos: Select ARM_DMA_USE_IOMMU). It added selection of ARM_DMA_USE_IOMMU symbol, on which IOMMU support in Exynos DRM subsystem depends. However selecting this symbol is all that this patch does, without providing any code code which implements real support for ARM DMA IOMMU integration, which is needed by Exynos DRM driver. Please disable CONFIG_DRM_EXYNOS_IOMMU in kernel .config and your system should be bootable again. Also, are you or someone working on the respective DT changes for Exynos5? I can prepare DT changes for Exynos5 as well, but first I wanted to clarify if everyone involved in generic IOMMU bindings and Exynos IOMMU driver agrees on my proposal. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 04/29] drivers: base: add notifier for failed driver bind
Hello, On 2014-08-25 22:05, Greg Kroah-Hartman wrote: On Tue, Aug 05, 2014 at 12:47:32PM +0200, Marek Szyprowski wrote: This patch adds support for getting a notify for failed device driver bind, so all the items done in BUS_NOTIFY_BIND_DRIVER event can be cleaned if the driver fails to bind. But doesn't the bus know if the driver fails to bind, so why would a notifier be needed here? Can't it unwind any problems then? Some other subsystems (like IOMMU) might register its own notifiers on the given bus. Such notifier is called before driver probe (BUS_NOTIFY_BIND_DRIVER event), so one can allocate some resources there. However there is no way to do the cleanup if the driver fails to bind, because no notifier is called in such case. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/base/dd.c | 10 +++--- include/linux/device.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e4ffbcf..541a41f 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -237,10 +237,14 @@ static int driver_sysfs_add(struct device *dev) return ret; } -static void driver_sysfs_remove(struct device *dev) +static void driver_sysfs_remove(struct device *dev, int failed) I _hate_ having functions with a flag that does something different depending on it. If you _really_ need this, just make the notifier call before calling this function, which should work just fine, right? Ok, I will fix this. If I remember correctly I followed the driver_sysfs_add() function pattern, which (surprisingly, especially when one considers only the function name) also calls the bus notifiers. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 04/29] drivers: base: add notifier for failed driver bind
Hello, On 2014-08-25 23:18, Joerg Roedel wrote: On Tue, Aug 05, 2014 at 12:47:32PM +0200, Marek Szyprowski wrote: + if (failed dev-bus) + blocking_notifier_call_chain(dev-bus-p-bus_notifier, +BUS_NOTIFY_DRVBIND_FAILED, dev); + Why can't you just use the notifier for BUS_NOTIFY_UNBIND_DRIVER or BUS_NOTIFY_UNBOUND_DRIVER when something goes wrong in driver initialization? Hmmm, you might be right. BUS_NOTIFY_UNBIND_DRIVER event happens before unbinding the driver, but BUS_NOTIFY_UNBOUND_DRIVER is called when driver remove has been finished, so it can be considered as a symmetrical pair for BUS_NOTIFY_BIND_DRIVER. Driver which registered bus notifiers is mainly interested in doing right cleanup, so the code executed for IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER and IOMMU_GROUP_NOTIFY_DRVBIND_FAILED is same. I will remove this additional event and simply add a call to BUS_NOTIFY_UNBOUND_DRIVER event when driver probe fails. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 10/29] drivers: add DRIVER_HAS_OWN_IOMMU_MANAGER flag
Hi Greg, On 2014-08-05 12:47, Marek Szyprowski wrote: This patch adds a new flags for device drivers. This flag instructs kernel that the device driver does it own management of IOMMU assisted IO address space translations, so no default dma-mapping structures should be initialized. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 5f4ff02..2e62371 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -253,6 +253,8 @@ struct device_driver { /* disables bind/unbind via sysfs */ #define DRIVER_SUPPRESS_BIND_ATTRS(1 0) +/* driver uses own methods to manage IO address space */ +#define DRIVER_HAS_OWN_IOMMU_MANAGER (1 1) extern int __must_check driver_register(struct device_driver *drv); extern void driver_unregister(struct device_driver *drv); Could you comment if the approach of using flags in the struct driver could be accepted? I've converted suppress_bind_attrs entry to flags to avoid extending the structure, please see patch [PATCH 05/29] drivers: convert suppress_bind_attrs parameter into flags. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 10/29] drivers: add DRIVER_HAS_OWN_IOMMU_MANAGER flag
Hello, On 2014-09-01 11:38, Arnd Bergmann wrote: On Monday 01 September 2014 09:53:29 Marek Szyprowski wrote: On 2014-09-01 09:07, Thierry Reding wrote: On Mon, Sep 01, 2014 at 07:22:32AM +0200, Marek Szyprowski wrote: Hi Greg, On 2014-08-05 12:47, Marek Szyprowski wrote: This patch adds a new flags for device drivers. This flag instructs kernel that the device driver does it own management of IOMMU assisted IO address space translations, so no default dma-mapping structures should be initialized. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/device.h b/include/linux/device.h index 5f4ff02..2e62371 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -253,6 +253,8 @@ struct device_driver { /* disables bind/unbind via sysfs */ #define DRIVER_SUPPRESS_BIND_ATTRS (1 0) +/* driver uses own methods to manage IO address space */ +#define DRIVER_HAS_OWN_IOMMU_MANAGER (1 1) extern int __must_check driver_register(struct device_driver *drv); extern void driver_unregister(struct device_driver *drv); Could you comment if the approach of using flags in the struct driver could be accepted? I've converted suppress_bind_attrs entry to flags to avoid extending the structure, please see patch [PATCH 05/29] drivers: convert suppress_bind_attrs parameter into flags. Is this really necessary? What I did as part of an RFC series for Tegra IOMMU support is keep this knowledge within the IOMMU driver rather than export it to the driver core.i The problem with embedding the list of drivers that you would need to update it everytime when you modify or extend iommu support in the other drivers. I've tried also other approach, like adding respective notifiers to individual drivers to initialize custom iommu support (or disable default iommu mapping) before iommu driver gets initialized, but such solution is in my opinion too complex and hard to understand if one is not familiar will all this stuff. All in all it turned out that the simplest and most generic way is to simply add the flag to the driver core. Flags might be also used in the future to model other kinds of dependencies between device drivers and/or driver core. I don't get it yet. I would expect that a driver doing its own management of the iommu gets to use the linux/iommu.h interfaces, while a driver using the default iommu setup uses linux/dma-mapping.h. You are right. Who do you think needs to set this flag, and who needs to read it? In the proposed solution Exynos IOMMU driver creates a separate IO address space for every client device in a system and binds it to the default dma-mapping space for the given device. When drivers are doing its own management of IO address space, instead of relying on what is available by default with dma-mapping interface, this will require releasing of the previously created default structures and resources. So this flag is set by the driver doing its own management of io address space. The flags is then checked by Exynos IOMMU driver to avoid creating the default dma-mapping address space for devices which driver does its own management. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 10/29] drivers: add DRIVER_HAS_OWN_IOMMU_MANAGER flag
Hello, On 2014-09-01 13:56, Arnd Bergmann wrote: On Monday 01 September 2014 12:47:08 Marek Szyprowski wrote: Who do you think needs to set this flag, and who needs to read it? In the proposed solution Exynos IOMMU driver creates a separate IO address space for every client device in a system and binds it to the default dma-mapping space for the given device. When drivers are doing its own management of IO address space, instead of relying on what is available by default with dma-mapping interface, this will require releasing of the previously created default structures and resources. So this flag is set by the driver doing its own management of io address space. The flags is then checked by Exynos IOMMU driver to avoid creating the default dma-mapping address space for devices which driver does its own management. I don't completely understand it yet. I would assume the device to be added to the default domain at device creation time (of_platform_populate), way before we know which device driver is going to be used. How can this prevent the iommu driver from doing the association with the domain? of_platform_populate() is too early to do the association, because that time the exynos iommu driver is even not yet probed. The association with default dma-mapping domain is done in IOMMU_GROUP_NOTIFY_BIND_DRIVER notifier, just before binding the driver to the given device. This way iommu driver can check dev-driver-flags and skip creating default dma-mapping domain if driver announces that it wants to handle it by itself. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
Hi Will, On 2014-08-29 17:54, Will Deacon wrote: This patch series is an RFC to implement IOMMU master configuration into of_dma_configure. I haven't yet ported any IOMMU drivers to it, so it remains untested, but I wanted to get some early feedback to ensure that this doesn't end up going in the wrong direction. The idea comes out of my understanding following discussions with Arnd and David at Kernel Summit / LinuxCon in Chicago. Essentially: - Allow IOMMUs to be probed early and set up per-instance data on their of_node - Add a new callback to the iommu_ops structure for adding a device with a set of opaque IDs (e.g. Stream IDs or Requester IDs) - Add an of_iommu_configure function, called from of_dma_configure, which registers the master IDs with the correspond IOMMU before probing the master itself - Where applicable, create an IOMMU domain per device and swizzle the DMA ops for that device to use the IOMMU variants. I haven't bothered doing anything clever with the DMA masks yet, so I wouldn't be surprised if we end up chewing through tonnes of memory allocating IOMMU domains that are far too big at the moment. Again, this is just an RFC and I'd welcome comments on the general direction of the series. Thanks for your patches, I wasn't aware the fact that you are working on this. When do you plan to send a second version? I would like to rebase my Exynos IOMMU patches (https://lkml.org/lkml/2014/8/5/183) on top of your work, but I wonder if I should select this version as a base or wait a bit for an update. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
Hi Will, On 2014-09-02 10:31, Will Deacon wrote: On Tue, Sep 02, 2014 at 07:26:01AM +0100, Marek Szyprowski wrote: On 2014-08-29 17:54, Will Deacon wrote: This patch series is an RFC to implement IOMMU master configuration into of_dma_configure. I haven't yet ported any IOMMU drivers to it, so it remains untested, but I wanted to get some early feedback to ensure that this doesn't end up going in the wrong direction. The idea comes out of my understanding following discussions with Arnd and David at Kernel Summit / LinuxCon in Chicago. Essentially: - Allow IOMMUs to be probed early and set up per-instance data on their of_node - Add a new callback to the iommu_ops structure for adding a device with a set of opaque IDs (e.g. Stream IDs or Requester IDs) - Add an of_iommu_configure function, called from of_dma_configure, which registers the master IDs with the correspond IOMMU before probing the master itself - Where applicable, create an IOMMU domain per device and swizzle the DMA ops for that device to use the IOMMU variants. I haven't bothered doing anything clever with the DMA masks yet, so I wouldn't be surprised if we end up chewing through tonnes of memory allocating IOMMU domains that are far too big at the moment. Again, this is just an RFC and I'd welcome comments on the general direction of the series. Thanks for your patches, I wasn't aware the fact that you are working on this. When do you plan to send a second version? I would like to rebase my Exynos IOMMU patches (https://lkml.org/lkml/2014/8/5/183) on top of your work, but I wonder if I should select this version as a base or wait a bit for an update. I'll try and get something out today/tomorrow depending on how easily the review comments fall out. It would be really great if you get an IOMMU working with this (I was going to look at the ARM SMMU once this stops moving) Great, I will wait then for v2. -- I have concerns that allocating one domain per master might be too much, but it's hard to tell without an IOMMU driver ported over. One domain per master is IMHO a sane default configuration. The only default alternative I see is to have only one domain (related with dma-mapping subsystem) and bind all devices to it. However I really don't see any disadvantage of having separate domain per each master and such configuration gives devices better separation. However we also need to figure out how to let drivers to make their own configuration, like it is required by Exynos DRM subsystem, which consist of several devices, each having its own IOMMU controller, but for convenience those drivers assume that they all have been bound to the same, single domain. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
Hi Arnd, On 2014-09-02 10:56, Arnd Bergmann wrote: On Tuesday 02 September 2014 10:48:02 Marek Szyprowski wrote: -- I have concerns that allocating one domain per master might be too much, but it's hard to tell without an IOMMU driver ported over. One domain per master is IMHO a sane default configuration. The only default alternative I see is to have only one domain (related with dma-mapping subsystem) and bind all devices to it. However I really don't see any disadvantage of having separate domain per each master and such configuration gives devices better separation. I was expecting that the dma-mapping implementation would by default use one domain for all devices, since that is what the simpler IOMMUs without domain support have to do anyway. For isolation purposes, it can only help to have more domains, but I would guess that there is some space overhead in maintaining lots of page tables. I'm okay with both approaches (separate domain for each device vs. single common domain for all devices). Maybe this can be some kind of Kconfig option added to DMA debugging? Separation might be really helpful when debugging strange device behavior. However we also need to figure out how to let drivers to make their own configuration, like it is required by Exynos DRM subsystem, which consist of several devices, each having its own IOMMU controller, but for convenience those drivers assume that they all have been bound to the same, single domain. IIRC with the way we ended up putting the mask into the iommu descriptor of the ARM SMMU, you can put multiple devices into the same iommu group, and have them automatically share a domain. I don't know if the same would work for the Samsung implementation. The question is how to transfer such information from the device drivers, that need/benefit from such configuration to iommu driver, which does all the setup? This is something completely internal to particular drivers and should not be exported to device tree or userspace. Thierry suggested to hardcode this information in the iommu driver, but I'm looking for other approaches. Maybe simply releasing device from the default dma-mapping domain before attaching to custom one will be the easiest solution. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
Hi Will, On 2014-09-02 12:57, Will Deacon wrote: On Tue, Sep 02, 2014 at 11:42:13AM +0100, Marek Szyprowski wrote: On 2014-09-02 10:56, Arnd Bergmann wrote: On Tuesday 02 September 2014 10:48:02 Marek Szyprowski wrote: -- I have concerns that allocating one domain per master might be too much, but it's hard to tell without an IOMMU driver ported over. One domain per master is IMHO a sane default configuration. The only default alternative I see is to have only one domain (related with dma-mapping subsystem) and bind all devices to it. However I really don't see any disadvantage of having separate domain per each master and such configuration gives devices better separation. I was expecting that the dma-mapping implementation would by default use one domain for all devices, since that is what the simpler IOMMUs without domain support have to do anyway. For isolation purposes, it can only help to have more domains, but I would guess that there is some space overhead in maintaining lots of page tables. I'm okay with both approaches (separate domain for each device vs. single common domain for all devices). Maybe this can be some kind of Kconfig option added to DMA debugging? Separation might be really helpful when debugging strange device behavior. One potential problem with a single domain is when you have multiple instances of a given IOMMU, each with different hardware restrictions. Then you can end up with multiple sets of page tables for the domain which, although not impossible to work with, is a bit of a mess. Maybe the default dma-mapping domain should be one per a given IOMMU instance? This will simplify a lot of things in such case. I think having one domain per IOMMU instance would make the most sense, but then you have to teach more of the stack about the IOMMU topology. I think we'll get there in the end, but that's a little way off right now. Right, those seems to be a details. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
Hi Arnd, On 2014-09-02 14:22, Arnd Bergmann wrote: On Tuesday 02 September 2014 12:42:13 Marek Szyprowski wrote: On 2014-09-02 10:56, Arnd Bergmann wrote: On Tuesday 02 September 2014 10:48:02 Marek Szyprowski wrote: -- I have concerns that allocating one domain per master might be too much, but it's hard to tell without an IOMMU driver ported over. One domain per master is IMHO a sane default configuration. The only default alternative I see is to have only one domain (related with dma-mapping subsystem) and bind all devices to it. However I really don't see any disadvantage of having separate domain per each master and such configuration gives devices better separation. I was expecting that the dma-mapping implementation would by default use one domain for all devices, since that is what the simpler IOMMUs without domain support have to do anyway. For isolation purposes, it can only help to have more domains, but I would guess that there is some space overhead in maintaining lots of page tables. I'm okay with both approaches (separate domain for each device vs. single common domain for all devices). Maybe this can be some kind of Kconfig option added to DMA debugging? Separation might be really helpful when debugging strange device behavior. We should probably support the iommu=strict command line option that some other architectures have. This is mainly meant to ensure that IOTLBs are shot down as soon as the driver unmaps some memory, which you often want to avoid for performance reasons. The iommu driver itself can then decide to also use separate domains for iommu=strict but a shared domain otherwise. For hardware on which the shared domain is hard to do, the driver might always use separate domains. Just to let you know, lazy unmapping is not yet implemented in ARM dma-mapping implementation based on IOMMU. However we also need to figure out how to let drivers to make their own configuration, like it is required by Exynos DRM subsystem, which consist of several devices, each having its own IOMMU controller, but for convenience those drivers assume that they all have been bound to the same, single domain. IIRC with the way we ended up putting the mask into the iommu descriptor of the ARM SMMU, you can put multiple devices into the same iommu group, and have them automatically share a domain. I don't know if the same would work for the Samsung implementation. The question is how to transfer such information from the device drivers, that need/benefit from such configuration to iommu driver, which does all the setup? This is something completely internal to particular drivers and should not be exported to device tree or userspace. Thierry suggested to hardcode this information in the iommu driver, but I'm looking for other approaches. Maybe simply releasing device from the default dma-mapping domain before attaching to custom one will be the easiest solution. For the ARM SMMU, the problem is that there is not necessarily a good way to partition the masters into IOMMU groups automatically, therefore we want to provide some hints in DT. On a machine that can have more domains than it has masters, this is not a problem and we can always use an all-ones mask, but for a machine on which this is not the case, the problem is simplified a lot of we hardcode the masks in a way that can always work, putting multiple devices into an iommu group if necessary. Well, I was talking about the Exynos IOMMU case, where there are no hw restrictions and grouping is done just to make things easier for the Exynos DRM drivers (a buffer gets the same DMA address for all devices, which are a part of virtual Exynos DRM device). This is similar to how we do things for pinctrl, where you might have a theoretically endless space of options to set stuff up, but we can simplify it by defining the useful configurations. Right, if hardware is limited, a sane working configuration is something that should be encoded in device tree. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH 0/7] Introduce automatic DMA configuration for IOMMU masters
On 2014-09-02 14:46, Arnd Bergmann wrote: On Tuesday 02 September 2014 14:30:36 Marek Szyprowski wrote: However we also need to figure out how to let drivers to make their own configuration, like it is required by Exynos DRM subsystem, which consist of several devices, each having its own IOMMU controller, but for convenience those drivers assume that they all have been bound to the same, single domain. IIRC with the way we ended up putting the mask into the iommu descriptor of the ARM SMMU, you can put multiple devices into the same iommu group, and have them automatically share a domain. I don't know if the same would work for the Samsung implementation. The question is how to transfer such information from the device drivers, that need/benefit from such configuration to iommu driver, which does all the setup? This is something completely internal to particular drivers and should not be exported to device tree or userspace. Thierry suggested to hardcode this information in the iommu driver, but I'm looking for other approaches. Maybe simply releasing device from the default dma-mapping domain before attaching to custom one will be the easiest solution. For the ARM SMMU, the problem is that there is not necessarily a good way to partition the masters into IOMMU groups automatically, therefore we want to provide some hints in DT. On a machine that can have more domains than it has masters, this is not a problem and we can always use an all-ones mask, but for a machine on which this is not the case, the problem is simplified a lot of we hardcode the masks in a way that can always work, putting multiple devices into an iommu group if necessary. Well, I was talking about the Exynos IOMMU case, where there are no hw restrictions and grouping is done just to make things easier for the Exynos DRM drivers (a buffer gets the same DMA address for all devices, which are a part of virtual Exynos DRM device). Does that mean you don't actually need to use multiple contexts here and could actually just use the normal dma-mapping interface if there is a way to ensure the mappings are always shared across these masters? Well, a default, shared single domain for dma-mapping interface will work with Exynos DRM and its multiple masters, although I never thought about such configuration. Or do you need this in addition to being able to use multiple masters so you can have multiple rendering contexts in user space? Such advanced IO space management is not yet implemented. However there are also devices (like multimedia codec - exynos mfc and camera capture subsystem exynos isp), which have limited DMA/IO window (256MiB in case of video codec), so they will still need to use their own separate domain. Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [RFC PATCH v2 3/7] iommu: add new iommu_ops callback for adding an OF device
Hello, On 2014-09-02 19:56, Will Deacon wrote: This patch adds a new function to the iommu_ops structure to allow an OF device to be added to a specific IOMMU instance using the recently merged generic devicetree binding for IOMMUs. The callback (of_xlate) takes a struct device representing the master and an of_phandle_args representing the IOMMU and the correspondong IDs for the new master. Signed-off-by: Will Deacon will.dea...@arm.com --- include/linux/iommu.h | 5 + 1 file changed, 5 insertions(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index fdddb14cd8f5..3e766b85daa3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -21,6 +21,7 @@ #include linux/errno.h #include linux/err.h +#include linux/of.h #include linux/types.h #include trace/events/iommu.h @@ -136,6 +137,10 @@ struct iommu_ops { /* Get the numer of window per domain */ u32 (*domain_get_windows)(struct iommu_domain *domain); +#ifdef CONFIG_OF_IOMMU + int (*of_xlate)(struct device *dev, struct of_phandle_args *args); If I understand correctly how it is designed to work, then it should be: struct iommu_data *(*of_xlate)(struct device *dev, struct of_phandle_args *args); +#endif + unsigned long pgsize_bitmap; }; Best regards -- Marek Szyprowski, PhD Samsung RD Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 06/18] iommu: exynos: don't read version register on every tlb operation
This patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data. Signed-off-by: Marek Szyprowski 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 d037e87a1fe5..73499b05e62e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -212,6 +212,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; phys_addr_t pgtable; + int version; }; static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -238,11 +239,6 @@ static void sysmmu_unblock(void __iomem *sfrbase) __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); } -static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) -{ - return MMU_RAW_VER(__raw_readl(data-sfrbase + REG_MMU_VERSION)); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -402,7 +398,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) unsigned int cfg = CFG_LRU | CFG_QOS(15); unsigned int ver; - ver = __raw_sysmmu_version(data); + ver = MMU_RAW_VER(__raw_readl(data-sfrbase + REG_MMU_VERSION)); if (MMU_MAJ_VER(ver) == 3) { if (MMU_MIN_VER(ver) = 2) { cfg |= CFG_FLPDCACHE; @@ -416,6 +412,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) } __raw_writel(cfg, data-sfrbase + REG_MMU_CFG); + data-version = ver; } static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) @@ -525,7 +522,7 @@ static bool exynos_sysmmu_disable(struct device *dev) static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { - if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + if (data-version == MAKE_MMU_VER(3, 3)) __raw_writel(iova | 0x1, data-sfrbase + REG_MMU_FLUSH_ENTRY); } @@ -574,7 +571,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, * 1MB page can be cached in one of all sets. * 64KB page can be one of 16 consecutive sets. */ - if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) + if (MMU_MAJ_VER(data-version) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); if (sysmmu_block(data-sfrbase)) { -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 04/18] clk: exynos: add missing smmu_g2d clock and update comments
This patch adds missing smmu_g2d clock implementation and updates comment about Exynos4 clocks from 278-282 range. Those clocks are available on all Exynos4 SoC series, so the misleading comment has been removed. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com Acked-by: Tomasz Figa t.f...@samsung.com --- drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 10 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ac163d7f5bc3..12a7cc3b5953 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1183,6 +1183,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_SPI1_ISP, spi1_isp, aclk200, E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_G2D, g2d, aclk200, GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, smmu_g2d, aclk200, GATE_IP_DMC, 24, 0, 0), GATE(CLK_TMU_APBIF, tmu_apbif, aclk100, E4X12_GATE_IP_PERIR, 17, 0, 0), }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 459bd2bd411f..fb9816354079 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -115,11 +115,11 @@ #define CLK_SMMU_MFCR 275 #define CLK_G3D276 #define CLK_G2D277 -#define CLK_ROTATOR278 /* Exynos4210 only */ -#define CLK_MDMA 279 /* Exynos4210 only */ -#define CLK_SMMU_G2D 280 /* Exynos4210 only */ -#define CLK_SMMU_ROTATOR 281 /* Exynos4210 only */ -#define CLK_SMMU_MDMA 282 /* Exynos4210 only */ +#define CLK_ROTATOR278 +#define CLK_MDMA 279 +#define CLK_SMMU_G2D 280 +#define CLK_SMMU_ROTATOR 281 +#define CLK_SMMU_MDMA 282 #define CLK_FIMD0 283 #define CLK_MIE0 284 #define CLK_MDNIE0 285 /* Exynos4412 only */ -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 00/18] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
Hello Everyone, This is yet another attempt to finally make Exynos SYSMMU driver fully integrated with DMA-mapping subsystem. Previous approach is available here: https://lkml.org/lkml/2014/8/5/183 I meantime, there have been a discussion about the way the iommu driver should be integrated with dma-mapping subsystem, which resulted in [RFC PATCH v3 0/7] Introduce automatic DMA configuration for IOMMU masters patches prepared by Will Deacon: http://www.spinics.net/lists/arm-kernel/msg362076.html Those patches removed the need to use bus-specific notifiers for initialization. Main changes since previous version of my patches: 1. rebased onto [RFC PATCH v3 0/7] Introduce automatic DMA configuration for IOMMU masters patches, changed initialization from bus notifiers to DT related callbacks 2. removed support for separate IO address spaces - this will be discussed separately after the basic support gets merged 3. removed support for power domain notifier-based runtime power management - this also will be discussed separately later I hope that the driver with above changes will be easier to be merged to v3.18. Best regards Marek Szyprowski Samsung RD Institute Poland Patch summary: Marek Szyprowski (18): arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size arm: exynos: bind power domains earlier, on device creation drm: exynos: detach from default dma-mapping domain on init clk: exynos: add missing smmu_g2d clock and update comments ARM: DTS: Exynos4: add System MMU nodes iommu: exynos: don't read version register on every tlb operation iommu: exynos: remove unused functions iommu: exynos: remove useless spinlock iommu: exynos: refactor function parameters to simplify code iommu: exynos: remove unused functions, part 2 iommu: exynos: remove useless device_add/remove callbacks iommu: exynos: add support for binding more than one sysmmu to master device iommu: exynos: add support for runtime_pm iommu: exynos: rename variables to reflect their purpose iommu: exynos: document internal structures iommu: exynos: remove excessive includes and sort others alphabetically iommu: exynos: init from dt-specific callback instead of initcall iommu: exynos: add callback for initializing devices from device tree arch/arm/boot/dts/exynos4.dtsi| 117 +++ arch/arm/boot/dts/exynos4210.dtsi | 23 ++ arch/arm/boot/dts/exynos4x12.dtsi | 82 + arch/arm/mach-exynos/pm_domains.c | 12 +- arch/arm/mm/dma-mapping.c | 16 + drivers/clk/samsung/clk-exynos4.c | 1 + drivers/gpu/drm/exynos/exynos_drm_iommu.c | 3 + drivers/iommu/exynos-iommu.c | 494 ++ include/dt-bindings/clock/exynos4.h | 10 +- 9 files changed, 483 insertions(+), 275 deletions(-) -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 01/18] arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mm/dma-mapping.c | 16 1 file changed, 16 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bcd5f836f27e..84705e24571b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2050,6 +2050,22 @@ int arm_iommu_attach_device(struct device *dev, { int err; + /* +* if device has no max_seg_size set, we assume that there is no limit +* and force it to DMA_BIT_MASK(32) to always use contiguous mappings +* in DMA address space +*/ + if (!dev-dma_parms) { + dev-dma_parms = kzalloc(sizeof(*dev-dma_parms), GFP_KERNEL); + if (!dev-dma_parms) + return -ENOMEM; + } + if (!dev-dma_parms-max_segment_size) { + err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + if (err) + return err; + } + err = iommu_attach_device(mapping-domain, dev); if (err) return err; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 02/18] arm: exynos: bind power domains earlier, on device creation
This patches change initialization time of power domain driver from client device driver bind to device creation. This lets other core drivers to register power domain notifiers before client driver is bound. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/mach-exynos/pm_domains.c | 12 ++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index fd76e1b5a471..1d368a26528c 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -159,13 +159,13 @@ static int exynos_pm_notifier_call(struct notifier_block *nb, struct device *dev = data; switch (event) { - case BUS_NOTIFY_BIND_DRIVER: + case BUS_NOTIFY_ADD_DEVICE: if (dev-of_node) exynos_read_domain_from_dt(dev); break; - case BUS_NOTIFY_UNBOUND_DRIVER: + case BUS_NOTIFY_DEL_DEVICE: exynos_remove_device_from_domain(dev); break; @@ -177,6 +177,13 @@ static struct notifier_block platform_nb = { .notifier_call = exynos_pm_notifier_call, }; +static int exynos_pm_domain_add(struct device *dev, void *priv) +{ + if (dev-of_node) + exynos_read_domain_from_dt(dev); + return 0; +} + static __init int exynos4_pm_init_power_domain(void) { struct platform_device *pdev; @@ -236,6 +243,7 @@ no_clk: } bus_register_notifier(platform_bus_type, platform_nb); + bus_for_each_dev(platform_bus_type, NULL, NULL, exynos_pm_domain_add); return 0; } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 05/18] ARM: DTS: Exynos4: add System MMU nodes
This patch adds System MMU nodes that are specific to Exynos4210/4x12 series. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- arch/arm/boot/dts/exynos4.dtsi| 117 ++ arch/arm/boot/dts/exynos4210.dtsi | 23 arch/arm/boot/dts/exynos4x12.dtsi | 82 ++ 3 files changed, 222 insertions(+) diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index e0278ecbc816..bfc19ec5 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -174,6 +174,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc0; status = disabled; }; @@ -185,6 +186,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc1; status = disabled; }; @@ -196,6 +198,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc2; status = disabled; }; @@ -207,6 +210,7 @@ clock-names = fimc, sclk_fimc; samsung,power-domain = pd_cam; samsung,sysreg = sys_reg; + iommus = sysmmu_fimc3; status = disabled; }; @@ -395,6 +399,8 @@ clocks = clock CLK_MFC; clock-names = mfc; status = disabled; + iommus = sysmmu_mfc_l, sysmmu_mfc_r; + iommu-names = left, right; }; serial_0: serial@1380 { @@ -643,6 +649,117 @@ clock-names = sclk_fimd, fimd; samsung,power-domain = pd_lcd0; samsung,sysreg = sys_reg; + iommus = sysmmu_fimd0; status = disabled; }; + + sysmmu_mfc_l: sysmmu@1362 { + compatible = samsung,exynos-sysmmu; + reg = 0x1362 0x1000; + interrupt-parent = combiner; + interrupts = 5 5; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_MFCL, clock CLK_MFC; + samsung,power-domain = pd_mfc; + #iommu-cells = 0; + }; + + sysmmu_mfc_r: sysmmu@1363 { + compatible = samsung,exynos-sysmmu; + reg = 0x1363 0x1000; + interrupt-parent = combiner; + interrupts = 5 6; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_MFCR, clock CLK_MFC; + samsung,power-domain = pd_mfc; + #iommu-cells = 0; + }; + + sysmmu_tv: sysmmu@12E2 { + compatible = samsung,exynos-sysmmu; + reg = 0x12E2 0x1000; + interrupt-parent = combiner; + interrupts = 5 4; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_TV, clock CLK_MIXER; + samsung,power-domain = pd_tv; + #iommu-cells = 0; + }; + + sysmmu_fimc0: sysmmu@11A2 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A2 0x1000; + interrupt-parent = combiner; + interrupts = 4 2; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC0, clock CLK_FIMC0; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc1: sysmmu@11A3 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A3 0x1000; + interrupt-parent = combiner; + interrupts = 4 3; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC1, clock CLK_FIMC1; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc2: sysmmu@11A4 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A4 0x1000; + interrupt-parent = combiner; + interrupts = 4 4; + clock-names = sysmmu, master; + clocks = clock CLK_SMMU_FIMC2, clock CLK_FIMC2; + samsung,power-domain = pd_cam; + #iommu-cells = 0; + }; + + sysmmu_fimc3: sysmmu@11A5 { + compatible = samsung,exynos-sysmmu; + reg = 0x11A5 0x1000; + interrupt-parent = combiner; + interrupts = 4 5; + clock-names = sysmmu, master
[PATCH v2 07/18] iommu: exynos: remove unused functions
This patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver. Signed-off-by: Marek Szyprowski 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 73499b05e62e..91feeca56abc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -490,13 +490,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; @@ -588,30 +581,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; -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 10/18] iommu: exynos: remove unused functions, part 2
After refactoring functions to use pointer to struct sysmmu_drvdata directly, some functions became useless and thus never used, so remove them completely. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 43 --- 1 file changed, 43 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ef30890f4069..b271348a4ec1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -457,49 +457,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, return ret; } -/* __exynos_sysmmu_enable: Enables System MMU - * - * returns -error if an error occurred and System MMU is not enabled, - * 0 if the System MMU has been just enabled and 1 if System MMU was already - * enabled before. - */ -static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, - struct iommu_domain *domain) -{ - int ret = 0; - unsigned long flags; - struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner-sysmmu); - - ret = __sysmmu_enable(data, pgtable, domain); - if (ret = 0) - data-master = dev; - - return ret; -} - -static bool exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev-archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner-sysmmu); - - disabled = __sysmmu_disable(data); - if (disabled) - data-master = NULL; - - return disabled; -} - static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 08/18] iommu: exynos: remove useless spinlock
This patch removes useless spinlocks and other unused members from struct exynos_iommu_owner. There is no point is protecting this structure by spinlock because content of this structure doesn't change and other structures have their own spinlocks. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 91feeca56abc..ae2703ed91d8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -189,9 +189,6 @@ struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; struct device *sysmmu; - struct iommu_domain *domain; - void *vmm_data; /* IO virtual memory manager's data */ - spinlock_t lock;/* Lock to preserve consistency of System MMU */ }; struct exynos_iommu_domain { @@ -477,16 +474,12 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(owner-lock, flags); - data = dev_get_drvdata(owner-sysmmu); ret = __sysmmu_enable(data, pgtable, domain); if (ret = 0) data-master = dev; - spin_unlock_irqrestore(owner-lock, flags); - return ret; } @@ -499,16 +492,12 @@ static bool exynos_sysmmu_disable(struct device *dev) BUG_ON(!has_sysmmu(dev)); - spin_lock_irqsave(owner-lock, flags); - data = dev_get_drvdata(owner-sysmmu); disabled = __sysmmu_disable(data); if (disabled) data-master = NULL; - spin_unlock_irqrestore(owner-lock, flags); - return disabled; } -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 12/18] iommu: exynos: add support for binding more than one sysmmu to master device
This patch adds support for assigning more than one SYSMMU controller to the master device. This has been achieved simply by chaning the struct device pointer in struct exynos_iommu_owner into the list of struct sysmmu_drvdata of all controllers assigned to the given master device. Signed-off-by: Marek Szyprowski 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 1b3f00726cd4..cf36cdecf335 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; }; struct exynos_iommu_domain { @@ -207,6 +207,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; struct list_head domain_node; + struct list_head owner_node; phys_addr_t pgtable; int version; }; @@ -694,8 +695,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return -ENODEV; - data = dev_get_drvdata(owner-sysmmu); - if (data) { + list_for_each_entry(data, owner-clients, owner_node) { ret = __sysmmu_enable(data, pagetable, domain); if (ret = 0) { data-master = dev; @@ -723,7 +723,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, { struct exynos_iommu_domain *priv = domain-priv; phys_addr_t pagetable = virt_to_phys(priv-pgtable); - struct sysmmu_drvdata *data; + struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0; @@ -731,14 +731,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, return; spin_lock_irqsave(priv-lock, flags); - list_for_each_entry(data, priv-clients, domain_node) { + list_for_each_entry_safe(data, next, priv-clients, domain_node) { if (data-master == dev) { if (__sysmmu_disable(data)) { data-master = NULL; list_del_init(data-domain_node); } found = true; - break; } } spin_unlock_irqrestore(priv-lock, flags); -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 11/18] iommu: exynos: remove useless device_add/remove callbacks
The driver doesn't need to do anything important in device add/remove callbacks, because initialization will be done from device-tree specific callbacks added later. IOMMU groups created by current code were never used. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 28 1 file changed, 28 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b271348a4ec1..1b3f00726cd4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1055,32 +1055,6 @@ 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 const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1089,8 +1063,6 @@ static const 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.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 17/18] iommu: exynos: init from dt-specific callback instead of initcall
This patch introduces IOMMU_OF_DECLARE-based initialization to the driver, which replaces subsys_initcall-based procedure. exynos_iommu_of_setup ensures that each sysmmu controller is probed before its master device. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 32 +++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5eb999d7653a..0d304c62956e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -13,16 +13,21 @@ #endif #include linux/clk.h +#include linux/dma-mapping.h #include linux/err.h #include linux/io.h #include linux/iommu.h #include linux/interrupt.h #include linux/list.h +#include linux/of.h +#include linux/of_iommu.h +#include linux/of_platform.h #include linux/platform_device.h #include linux/pm_runtime.h #include linux/slab.h #include asm/cacheflush.h +#include asm/dma-iommu.h #include asm/pgtable.h typedef u32 sysmmu_iova_t; @@ -1083,6 +1088,8 @@ static const struct iommu_ops exynos_iommu_ops = { .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, }; +static int init_done; + static int __init exynos_iommu_init(void) { int ret; @@ -1115,6 +1122,8 @@ static int __init exynos_iommu_init(void) goto err_set_iommu; } + init_done = true; + return 0; err_set_iommu: kmem_cache_free(lv2table_kmem_cache, zero_lv2_table); @@ -1124,4 +1133,25 @@ err_reg_driver: kmem_cache_destroy(lv2table_kmem_cache); return ret; } -subsys_initcall(exynos_iommu_init); + +static struct iommu_data exynos_of_data = { + .ops = exynos_iommu_ops, +}; + +static int __init exynos_iommu_of_setup(struct device_node *np) +{ + struct platform_device *pdev; + + if (!init_done) + exynos_iommu_init(); + + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + of_iommu_set_data(np, exynos_of_data); + return 0; +} + +IOMMU_OF_DECLARE(exynos_iommu_of, samsung,exynos-sysmmu, +exynos_iommu_of_setup); -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH v2 16/18] iommu: exynos: remove excessive includes and sort others alphabetically
Removed following unused includes: linux/mm.h, linux/errno.h, linux/memblock.h and linux/export.h. Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com --- drivers/iommu/exynos-iommu.c | 14 +- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a75b06365f97..5eb999d7653a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,19 +12,15 @@ #define DEBUG #endif -#include linux/io.h -#include linux/interrupt.h -#include linux/platform_device.h -#include linux/slab.h -#include linux/pm_runtime.h #include linux/clk.h #include linux/err.h -#include linux/mm.h +#include linux/io.h #include linux/iommu.h -#include linux/errno.h +#include linux/interrupt.h #include linux/list.h -#include linux/memblock.h -#include linux/export.h +#include linux/platform_device.h +#include linux/pm_runtime.h +#include linux/slab.h #include asm/cacheflush.h #include asm/pgtable.h -- 1.9.2 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu