diff options
Diffstat (limited to 'arch/arm/mm/dma-mapping.c')
| -rw-r--r-- | arch/arm/mm/dma-mapping.c | 480 |
1 files changed, 385 insertions, 95 deletions
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 613c1d06316a..4cee11317048 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -28,6 +28,8 @@ #include <linux/vmalloc.h> #include <linux/sizes.h> #include <linux/cma.h> +#include <linux/msm_dma_iommu_mapping.h> +#include <linux/dma-mapping-fast.h> #include <asm/memory.h> #include <asm/highmem.h> @@ -59,6 +61,49 @@ static void __dma_page_cpu_to_dev(struct page *, unsigned long, static void __dma_page_dev_to_cpu(struct page *, unsigned long, size_t, enum dma_data_direction); +static void * +__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, + const void *caller); + +static void __dma_free_remap(void *cpu_addr, size_t size, bool no_warn); + +static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, + bool coherent); + +static void *arm_dma_remap(struct device *dev, void *cpu_addr, + dma_addr_t handle, size_t size, + struct dma_attrs *attrs); + +static void arm_dma_unremap(struct device *dev, void *remapped_addr, + size_t size); + +static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs) +{ + bool is_coherent; + + if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs)) + is_coherent = true; + else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs)) + is_coherent = false; + else if (is_device_dma_coherent(dev)) + is_coherent = true; + else + is_coherent = false; + + return is_coherent; +} + +static int __get_iommu_pgprot(struct dma_attrs *attrs, int prot, + bool coherent) +{ + if (!dma_get_attr(DMA_ATTR_EXEC_MAPPING, attrs)) + prot |= IOMMU_NOEXEC; + if (coherent) + prot |= IOMMU_CACHE; + + return prot; +} + /** * 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 @@ -142,6 +187,8 @@ struct dma_map_ops arm_dma_ops = { .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, .sync_sg_for_device = arm_dma_sync_sg_for_device, .set_dma_mask = arm_dma_set_mask, + .remap = arm_dma_remap, + .unremap = arm_dma_unremap, }; EXPORT_SYMBOL(arm_dma_ops); @@ -225,7 +272,8 @@ static u64 get_coherent_dma_mask(struct device *dev) return mask; } -static void __dma_clear_buffer(struct page *page, size_t size) +static void __dma_clear_buffer(struct page *page, size_t size, + struct dma_attrs *attrs, bool is_coherent) { /* * Ensure that the allocated pages are zeroed, and that any data @@ -236,18 +284,24 @@ static void __dma_clear_buffer(struct page *page, size_t size) phys_addr_t end = base + size; while (size > 0) { void *ptr = kmap_atomic(page); - memset(ptr, 0, PAGE_SIZE); - dmac_flush_range(ptr, ptr + PAGE_SIZE); + if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)) + memset(ptr, 0, PAGE_SIZE); + if (!is_coherent) + dmac_flush_range(ptr, ptr + PAGE_SIZE); kunmap_atomic(ptr); page++; size -= PAGE_SIZE; } - outer_flush_range(base, end); + if (!is_coherent) + outer_flush_range(base, end); } else { void *ptr = page_address(page); - memset(ptr, 0, size); - dmac_flush_range(ptr, ptr + size); - outer_flush_range(__pa(ptr), __pa(ptr) + size); + if (!dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs)) + memset(ptr, 0, size); + if (!is_coherent) { + dmac_flush_range(ptr, ptr + size); + outer_flush_range(__pa(ptr), __pa(ptr) + size); + } } } @@ -255,7 +309,8 @@ static void __dma_clear_buffer(struct page *page, size_t size) * Allocate a DMA buffer for 'dev' of size 'size' using the * specified gfp mask. Note that 'size' must be page aligned. */ -static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gfp) +static struct page *__dma_alloc_buffer(struct device *dev, size_t size, + gfp_t gfp, bool coherent) { unsigned long order = get_order(size); struct page *page, *p, *e; @@ -271,7 +326,7 @@ static struct page *__dma_alloc_buffer(struct device *dev, size_t size, gfp_t gf for (p = page + (size >> PAGE_SHIFT), e = page + (1 << order); p < e; p++) __free_page(p); - __dma_clear_buffer(page, size); + __dma_clear_buffer(page, size, NULL, coherent); return page; } @@ -293,7 +348,8 @@ static void __dma_free_buffer(struct page *page, size_t size) static void *__alloc_from_contiguous(struct device *dev, size_t size, pgprot_t prot, struct page **ret_page, - const void *caller, bool want_vaddr); + const void *caller, + struct dma_attrs *attrs); static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, pgprot_t prot, struct page **ret_page, @@ -312,10 +368,10 @@ __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, prot, caller); } -static void __dma_free_remap(void *cpu_addr, size_t size) +static void __dma_free_remap(void *cpu_addr, size_t size, bool no_warn) { dma_common_free_remap(cpu_addr, size, - VM_ARM_DMA_CONSISTENT | VM_USERMAP); + VM_ARM_DMA_CONSISTENT | VM_USERMAP, no_warn); } #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K @@ -361,7 +417,7 @@ static int __init atomic_pool_init(void) if (dev_get_cma_area(NULL)) ptr = __alloc_from_contiguous(NULL, atomic_pool_size, prot, - &page, atomic_pool_init, true); + &page, atomic_pool_init, NULL); else ptr = __alloc_remap_buffer(NULL, atomic_pool_size, gfp, prot, &page, atomic_pool_init, true); @@ -420,6 +476,15 @@ void __init dma_contiguous_remap(void) struct map_desc map; unsigned long addr; + /* + * Make start and end PMD_SIZE aligned, observing memory + * boundaries + */ + if (memblock_is_memory(start & PMD_MASK)) + start = start & PMD_MASK; + if (memblock_is_memory(ALIGN(end, PMD_SIZE))) + end = ALIGN(end, PMD_SIZE); + if (end > arm_lowmem_limit) end = arm_lowmem_limit; if (start >= end) @@ -440,8 +505,12 @@ void __init dma_contiguous_remap(void) * and ensures that this code is architecturally compliant. */ for (addr = __phys_to_virt(start); addr < __phys_to_virt(end); - addr += PMD_SIZE) - pmd_clear(pmd_off_k(addr)); + addr += PMD_SIZE) { + pmd_t *pmd; + pmd = pmd_off_k(addr); + if (pmd_bad(*pmd)) + pmd_clear(pmd); + } flush_tlb_kernel_range(__phys_to_virt(start), __phys_to_virt(end)); @@ -460,12 +529,28 @@ static int __dma_update_pte(pte_t *pte, pgtable_t token, unsigned long addr, return 0; } -static void __dma_remap(struct page *page, size_t size, pgprot_t prot) +static int __dma_clear_pte(pte_t *pte, pgtable_t token, unsigned long addr, + void *data) +{ + pte_clear(&init_mm, addr, pte); + return 0; +} + +static void __dma_remap(struct page *page, size_t size, pgprot_t prot, + bool want_vaddr) { unsigned long start = (unsigned long) page_address(page); unsigned end = start + size; + int (*func)(pte_t *pte, pgtable_t token, unsigned long addr, + void *data); - apply_to_page_range(&init_mm, start, size, __dma_update_pte, &prot); + if (!want_vaddr) + func = __dma_clear_pte; + else + func = __dma_update_pte; + + apply_to_page_range(&init_mm, start, size, func, &prot); + dsb(); flush_tlb_kernel_range(start, end); } @@ -475,7 +560,7 @@ static void *__alloc_remap_buffer(struct device *dev, size_t size, gfp_t gfp, { struct page *page; void *ptr = NULL; - page = __dma_alloc_buffer(dev, size, gfp); + page = __dma_alloc_buffer(dev, size, gfp, false); if (!page) return NULL; if (!want_vaddr) @@ -528,36 +613,55 @@ static int __free_from_pool(void *start, size_t size) return 1; } +#define NO_KERNEL_MAPPING_DUMMY 0x2222 static void *__alloc_from_contiguous(struct device *dev, size_t size, pgprot_t prot, struct page **ret_page, - const void *caller, bool want_vaddr) + const void *caller, + struct dma_attrs *attrs) { unsigned long order = get_order(size); size_t count = size >> PAGE_SHIFT; struct page *page; void *ptr = NULL; + bool want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, + attrs); page = dma_alloc_from_contiguous(dev, count, order); if (!page) return NULL; - __dma_clear_buffer(page, size); - - if (!want_vaddr) - goto out; + /* + * skip completely if we neither need to zero nor sync. + */ + if (!(dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs) && + dma_get_attr(DMA_ATTR_SKIP_ZEROING, attrs))) + __dma_clear_buffer(page, size, attrs, false); if (PageHighMem(page)) { - ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, caller); - if (!ptr) { - dma_release_from_contiguous(dev, page, count); - return NULL; + if (!want_vaddr) { + /* + * Something non-NULL needs to be returned here. Give + * back a dummy address that is unmapped to catch + * clients trying to use the address incorrectly + */ + ptr = (void *)NO_KERNEL_MAPPING_DUMMY; + + /* also flush out the stale highmem mappings */ + kmap_flush_unused(); + kmap_atomic_flush_unused(); + } else { + ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, + caller); + if (!ptr) { + dma_release_from_contiguous(dev, page, count); + return NULL; + } } } else { - __dma_remap(page, size, prot); + __dma_remap(page, size, prot, want_vaddr); ptr = page_address(page); } - out: *ret_page = page; return ptr; } @@ -565,20 +669,21 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size, static void __free_from_contiguous(struct device *dev, struct page *page, void *cpu_addr, size_t size, bool want_vaddr) { - if (want_vaddr) { - if (PageHighMem(page)) - __dma_free_remap(cpu_addr, size); - else - __dma_remap(page, size, PAGE_KERNEL); - } + if (PageHighMem(page)) + __dma_free_remap(cpu_addr, size, true); + else + __dma_remap(page, size, PAGE_KERNEL, true); dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT); } -static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) +static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, + bool coherent) { - prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ? - pgprot_writecombine(prot) : - pgprot_dmacoherent(prot); + if (dma_get_attr(DMA_ATTR_STRONGLY_ORDERED, attrs)) + prot = pgprot_stronglyordered(prot); + else if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) + prot = pgprot_writecombine(prot); + return prot; } @@ -594,15 +699,15 @@ static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot) #define __alloc_from_contiguous(dev, size, prot, ret, c, wv) NULL #define __free_from_pool(cpu_addr, size) 0 #define __free_from_contiguous(dev, page, cpu_addr, size, wv) do { } while (0) -#define __dma_free_remap(cpu_addr, size) do { } while (0) +#define __dma_free_remap(cpu_addr, size, w) do { } while (0) #endif /* CONFIG_MMU */ static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp, - struct page **ret_page) + struct page **ret_page, bool coherent) { struct page *page; - page = __dma_alloc_buffer(dev, size, gfp); + page = __dma_alloc_buffer(dev, size, gfp, coherent); if (!page) return NULL; @@ -650,12 +755,14 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs); if (nommu()) - addr = __alloc_simple_buffer(dev, size, gfp, &page); + addr = __alloc_simple_buffer(dev, size, gfp, &page, + is_coherent); else if (dev_get_cma_area(dev) && (gfp & __GFP_DIRECT_RECLAIM)) addr = __alloc_from_contiguous(dev, size, prot, &page, - caller, want_vaddr); + caller, attrs); else if (is_coherent) - addr = __alloc_simple_buffer(dev, size, gfp, &page); + addr = __alloc_simple_buffer(dev, size, gfp, &page, + is_coherent); else if (!gfpflags_allow_blocking(gfp)) addr = __alloc_from_pool(size, &page); else @@ -675,7 +782,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) { - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false); return __dma_alloc(dev, size, handle, gfp, prot, false, attrs, __builtin_return_address(0)); @@ -713,6 +820,42 @@ static int __arm_dma_mmap(struct device *dev, struct vm_area_struct *vma, return ret; } +static void *arm_dma_remap(struct device *dev, void *cpu_addr, + dma_addr_t handle, size_t size, + struct dma_attrs *attrs) +{ + void *ptr; + bool is_coherent = is_dma_coherent(dev, attrs); + struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent); + unsigned long offset = handle & ~PAGE_MASK; + + size = PAGE_ALIGN(size + offset); + ptr = __dma_alloc_remap(page, size, GFP_KERNEL, prot, + __builtin_return_address(0)); + return ptr ? ptr + offset : ptr; +} + +static void arm_dma_unremap(struct device *dev, void *remapped_addr, + size_t size) +{ + unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP; + struct vm_struct *area; + + size = PAGE_ALIGN(size); + remapped_addr = (void *)((unsigned long)remapped_addr & PAGE_MASK); + + area = find_vm_area(remapped_addr); + if (!area || (area->flags & flags) != flags) { + WARN(1, "trying to free invalid coherent area: %p\n", + remapped_addr); + return; + } + + vunmap(remapped_addr); + flush_tlb_kernel_range((unsigned long)remapped_addr, + (unsigned long)(remapped_addr + size)); +} /* * Create userspace mapping for the DMA-coherent memory. */ @@ -728,7 +871,8 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma, struct dma_attrs *attrs) { #ifdef CONFIG_MMU - vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); + vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, + false); #endif /* CONFIG_MMU */ return __arm_dma_mmap(dev, vma, cpu_addr, dma_addr, size, attrs); } @@ -751,7 +895,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr, return; } else if (!dev_get_cma_area(dev)) { if (want_vaddr && !is_coherent) - __dma_free_remap(cpu_addr, size); + __dma_free_remap(cpu_addr, size, false); __dma_free_buffer(page, size); } else { /* @@ -929,7 +1073,7 @@ static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, 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); + const struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i, j; @@ -963,7 +1107,7 @@ 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); + const struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i; @@ -982,7 +1126,7 @@ 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); + const struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i; @@ -1001,7 +1145,7 @@ 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); + const struct dma_map_ops *ops = get_dma_ops(dev); struct scatterlist *s; int i; @@ -1144,9 +1288,10 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, gfp_t gfp, struct dma_attrs *attrs) { struct page **pages; - int count = size >> PAGE_SHIFT; - int array_size = count * sizeof(struct page *); + size_t count = size >> PAGE_SHIFT; + size_t array_size = count * sizeof(struct page *); int i = 0; + bool is_coherent = is_dma_coherent(dev, attrs); if (array_size <= PAGE_SIZE) pages = kzalloc(array_size, GFP_KERNEL); @@ -1164,7 +1309,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, if (!page) goto error; - __dma_clear_buffer(page, size); + __dma_clear_buffer(page, size, attrs, is_coherent); for (i = 0; i < count; i++) pages[i] = page + i; @@ -1208,7 +1353,8 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, pages[i + j] = pages[i] + j; } - __dma_clear_buffer(pages[i], PAGE_SIZE << order); + __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs, + is_coherent); i += 1 << order; count -= 1 << order; } @@ -1262,16 +1408,20 @@ __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot, * Create a mapping in device IO address space for specified pages */ static dma_addr_t -__iommu_create_mapping(struct device *dev, struct page **pages, size_t size) +__iommu_create_mapping(struct device *dev, struct page **pages, size_t size, + struct dma_attrs *attrs) { struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; dma_addr_t dma_addr, iova; int i; + int prot = IOMMU_READ | IOMMU_WRITE; dma_addr = __alloc_iova(mapping, size); if (dma_addr == DMA_ERROR_CODE) return dma_addr; + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); iova = dma_addr; for (i = 0; i < count; ) { @@ -1286,8 +1436,7 @@ __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, - IOMMU_READ|IOMMU_WRITE); + ret = iommu_map(mapping->domain, iova, phys, len, prot); if (ret < 0) goto fail; iova += len; @@ -1344,23 +1493,52 @@ static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs) } static void *__iommu_alloc_atomic(struct device *dev, size_t size, - dma_addr_t *handle) + dma_addr_t *handle, gfp_t gfp, + struct dma_attrs *attrs) { struct page *page; + struct page **pages; + size_t count = size >> PAGE_SHIFT; + size_t array_size = count * sizeof(struct page *); void *addr; + int i; + bool coherent = is_dma_coherent(dev, attrs); - addr = __alloc_from_pool(size, &page); - if (!addr) + if (array_size <= PAGE_SIZE) + pages = kzalloc(array_size, gfp); + else + pages = vzalloc(array_size); + + if (!pages) return NULL; - *handle = __iommu_create_mapping(dev, &page, size); + if (coherent) { + page = alloc_pages(gfp, get_order(size)); + addr = page ? page_address(page) : NULL; + } else { + addr = __alloc_from_pool(size, &page); + } + + if (!addr) + goto err_free; + + for (i = 0; i < count ; i++) + pages[i] = page + i; + + *handle = __iommu_create_mapping(dev, pages, size, attrs); if (*handle == DMA_ERROR_CODE) goto err_mapping; + kvfree(pages); return addr; err_mapping: - __free_from_pool(addr, size); + if (coherent) + __free_pages(page, get_order(size)); + else + __free_from_pool(addr, size); +err_free: + kvfree(pages); return NULL; } @@ -1374,7 +1552,9 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr, static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs) { - pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL); + + bool coherent = is_dma_coherent(dev, attrs); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent); struct page **pages; void *addr = NULL; @@ -1382,7 +1562,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, size = PAGE_ALIGN(size); if (!gfpflags_allow_blocking(gfp)) - return __iommu_alloc_atomic(dev, size, handle); + return __iommu_alloc_atomic(dev, size, handle, gfp, attrs); /* * Following is a work-around (a.k.a. hack) to prevent pages @@ -1397,7 +1577,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, if (!pages) return NULL; - *handle = __iommu_create_mapping(dev, pages, size); + *handle = __iommu_create_mapping(dev, pages, size, attrs); if (*handle == DMA_ERROR_CODE) goto err_buffer; @@ -1427,8 +1607,10 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, struct page **pages = __iommu_get_pages(cpu_addr, attrs); unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; unsigned long off = vma->vm_pgoff; + bool coherent = is_dma_coherent(dev, attrs); - vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot); + vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, + coherent); if (!pages) return -ENXIO; @@ -1474,7 +1656,7 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { dma_common_free_remap(cpu_addr, size, - VM_ARM_DMA_CONSISTENT | VM_USERMAP); + VM_ARM_DMA_CONSISTENT | VM_USERMAP, true); } __iommu_remove_mapping(dev, handle, size); @@ -1642,7 +1824,39 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg, int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { - return __iommu_map_sg(dev, sg, nents, dir, attrs, false); + struct scatterlist *s; + int i; + size_t ret; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int total_length = 0, current_offset = 0; + dma_addr_t iova; + int prot = __dma_direction_to_prot(dir); + + for_each_sg(sg, s, nents, i) + total_length += s->length; + + iova = __alloc_iova(mapping, total_length); + + if (iova == DMA_ERROR_CODE) { + dev_err(dev, "Couldn't allocate iova for sg %p\n", sg); + return 0; + } + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); + + ret = iommu_map_sg(mapping->domain, iova, sg, nents, prot); + if (ret != total_length) { + __free_iova(mapping, iova, total_length); + return 0; + } + + for_each_sg(sg, s, nents, i) { + s->dma_address = iova + current_offset; + s->dma_length = total_length - current_offset; + current_offset += s->length; + } + + return nents; } static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg, @@ -1692,7 +1906,15 @@ void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs) { - __iommu_unmap_sg(dev, sg, nents, dir, attrs, false); + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + unsigned int total_length = sg_dma_len(sg); + dma_addr_t iova = sg_dma_address(sg); + + total_length = PAGE_ALIGN((iova & ~PAGE_MASK) + total_length); + iova &= PAGE_MASK; + + iommu_unmap(mapping->domain, iova, total_length); + __free_iova(mapping, iova, total_length); } /** @@ -1707,6 +1929,13 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, { struct scatterlist *s; int i; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + dma_addr_t iova = sg_dma_address(sg); + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova); + + if (iova_coherent) + return; + for_each_sg(sg, s, nents, i) __dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir); @@ -1725,6 +1954,12 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg, { struct scatterlist *s; int i; + struct dma_iommu_mapping *mapping = dev->archdata.mapping; + dma_addr_t iova = sg_dma_address(sg); + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, iova); + + if (iova_coherent) + return; for_each_sg(sg, s, nents, i) __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); @@ -1747,19 +1982,26 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p { struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); dma_addr_t dma_addr; - int ret, prot, len = PAGE_ALIGN(size + offset); + int ret, prot, len, start_offset, map_offset; + + map_offset = offset & ~PAGE_MASK; + start_offset = offset & PAGE_MASK; + len = PAGE_ALIGN(map_offset + size); dma_addr = __alloc_iova(mapping, len); if (dma_addr == DMA_ERROR_CODE) return dma_addr; prot = __dma_direction_to_prot(dir); + prot = __get_iommu_pgprot(attrs, prot, + is_dma_coherent(dev, attrs)); - ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot); + ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page) + + start_offset, len, prot); if (ret < 0) goto fail; - return dma_addr + offset; + return dma_addr + map_offset; fail: __free_iova(mapping, dma_addr, len); return DMA_ERROR_CODE; @@ -1779,7 +2021,8 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction dir, struct dma_attrs *attrs) { - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!is_dma_coherent(dev, attrs) && + !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) __dma_page_cpu_to_dev(page, offset, size, dir); return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs); @@ -1803,9 +2046,6 @@ static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle, int offset = handle & ~PAGE_MASK; int len = PAGE_ALIGN(size + offset); - if (!iova) - return; - iommu_unmap(mapping->domain, iova, len); __free_iova(mapping, iova, len); } @@ -1829,10 +2069,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle, int offset = handle & ~PAGE_MASK; int len = PAGE_ALIGN(size + offset); - if (!iova) - return; - - if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) + if (!(is_dma_coherent(dev, attrs) || + dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))) __dma_page_dev_to_cpu(page, offset, size, dir); iommu_unmap(mapping->domain, iova, len); @@ -1846,11 +2084,10 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, dma_addr_t iova = handle & PAGE_MASK; struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle); - if (!iova) - return; - - __dma_page_dev_to_cpu(page, offset, size, dir); + if (!iova_coherent) + __dma_page_dev_to_cpu(page, offset, size, dir); } static void arm_iommu_sync_single_for_device(struct device *dev, @@ -1860,14 +2097,32 @@ static void arm_iommu_sync_single_for_device(struct device *dev, dma_addr_t iova = handle & PAGE_MASK; struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova)); unsigned int offset = handle & ~PAGE_MASK; + bool iova_coherent = iommu_is_iova_coherent(mapping->domain, handle); - if (!iova) - return; + if (!iova_coherent) + __dma_page_cpu_to_dev(page, offset, size, dir); +} - __dma_page_cpu_to_dev(page, offset, size, dir); +static int arm_iommu_dma_supported(struct device *dev, u64 mask) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + if (!mapping) { + dev_warn(dev, "No IOMMU mapping for device\n"); + return 0; + } + + return iommu_dma_supported(mapping->domain, dev, mask); +} + +static int arm_iommu_mapping_error(struct device *dev, + dma_addr_t dma_addr) +{ + return dma_addr == DMA_ERROR_CODE; } -struct dma_map_ops iommu_ops = { + +const struct dma_map_ops iommu_ops = { .alloc = arm_iommu_alloc_attrs, .free = arm_iommu_free_attrs, .mmap = arm_iommu_mmap_attrs, @@ -1884,9 +2139,11 @@ struct dma_map_ops iommu_ops = { .sync_sg_for_device = arm_iommu_sync_sg_for_device, .set_dma_mask = arm_dma_set_mask, + .dma_supported = arm_iommu_dma_supported, + .mapping_error = arm_iommu_mapping_error, }; -struct dma_map_ops iommu_coherent_ops = { +const struct dma_map_ops iommu_coherent_ops = { .alloc = arm_iommu_alloc_attrs, .free = arm_iommu_free_attrs, .mmap = arm_iommu_mmap_attrs, @@ -1930,6 +2187,9 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, u64 size) if (!bitmap_size) return ERR_PTR(-EINVAL); + WARN(!IS_ALIGNED(size, SZ_128M), + "size is not aligned to 128M, alignment enforced"); + if (bitmap_size > PAGE_SIZE) { extensions = bitmap_size / PAGE_SIZE; bitmap_size = PAGE_SIZE; @@ -2044,12 +2304,21 @@ int arm_iommu_attach_device(struct device *dev, struct dma_iommu_mapping *mapping) { int err; + int s1_bypass = 0; + int is_fast = 0; + + iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast); + if (is_fast) + return fast_smmu_attach_device(dev, mapping); err = __arm_iommu_attach_device(dev, mapping); if (err) return err; - set_dma_ops(dev, &iommu_ops); + iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS, + &s1_bypass); + if (!s1_bypass) + set_dma_ops(dev, &iommu_ops); return 0; } EXPORT_SYMBOL_GPL(arm_iommu_attach_device); @@ -2057,6 +2326,7 @@ EXPORT_SYMBOL_GPL(arm_iommu_attach_device); static void __arm_iommu_detach_device(struct device *dev) { struct dma_iommu_mapping *mapping; + int is_fast; mapping = to_dma_iommu_mapping(dev); if (!mapping) { @@ -2064,6 +2334,12 @@ static void __arm_iommu_detach_device(struct device *dev) return; } + if (msm_dma_unmap_all_for_dev(dev)) + dev_warn(dev, "IOMMU detach with outstanding mappings\n"); + iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast); + if (is_fast) + return fast_smmu_detach_device(dev, mapping); + iommu_detach_device(mapping->domain, dev); kref_put(&mapping->kref, release_iommu_mapping); to_dma_iommu_mapping(dev) = NULL; @@ -2080,12 +2356,25 @@ static void __arm_iommu_detach_device(struct device *dev) */ void arm_iommu_detach_device(struct device *dev) { + struct dma_iommu_mapping *mapping; + int s1_bypass = 0; + + mapping = to_dma_iommu_mapping(dev); + if (!mapping) { + dev_warn(dev, "Not attached\n"); + return; + } + __arm_iommu_detach_device(dev); - set_dma_ops(dev, NULL); + + iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS, + &s1_bypass); + if (!s1_bypass) + set_dma_ops(dev, NULL); } EXPORT_SYMBOL_GPL(arm_iommu_detach_device); -static struct dma_map_ops *arm_get_iommu_dma_map_ops(bool coherent) +static const struct dma_map_ops *arm_get_iommu_dma_map_ops(bool coherent) { return coherent ? &iommu_coherent_ops : &iommu_ops; } @@ -2148,7 +2437,7 @@ static struct dma_map_ops *arm_get_dma_map_ops(bool coherent) void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, struct iommu_ops *iommu, bool coherent) { - struct dma_map_ops *dma_ops; + const struct dma_map_ops *dma_ops; dev->archdata.dma_coherent = coherent; if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu)) @@ -2158,6 +2447,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, set_dma_ops(dev, dma_ops); } +EXPORT_SYMBOL(arch_setup_dma_ops); void arch_teardown_dma_ops(struct device *dev) { |
