From 4ac42c440404fcda2d6baae7db6eb0a75136cf6d Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 5 Aug 2014 20:11:44 -0700 Subject: [PATCH] ANDROID: GKI: arm64: add support for NO_KERNEL_MAPPING and STRONGLY_ORDERED arm64 needs support for both DMA_ATTR_NO_KERNEL_MAPPING and DMA_ATTR_STRONGLY_ORDERD. Add support for both of them. Change-Id: I35d670b8639fed465d2adbe8972ded40bd9f1d1c Signed-off-by: Laura Abbott Signed-off-by: Patrick Daly Bug: 155522481 Signed-off-by: Mark Salyzyn [saravanak snapshot from commit 79efc458af96 that approximately matches commit 28a88c9d84abb3180bc651a29074f79ef0474b48] Signed-off-by: Saravana Kannan --- arch/arm64/mm/dma-mapping.c | 78 ++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index aaea4a7d53e0..7f5557fdab73 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -44,13 +44,16 @@ static int swiotlb __ro_after_init; static pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot, bool coherent) { - if (!coherent || (attrs & DMA_ATTR_WRITE_COMBINE)) + if (attrs & DMA_ATTR_STRONGLY_ORDERED) + return pgprot_noncached(prot); + else if (!coherent || (attrs & DMA_ATTR_WRITE_COMBINE)) return pgprot_writecombine(prot); return prot; } static struct gen_pool *atomic_pool __ro_after_init; +#define NO_KERNEL_MAPPING_DUMMY 0x2222 #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE; @@ -98,6 +101,43 @@ static int __free_from_pool(void *start, size_t size) return 1; } +static int __dma_update_pte(pte_t *pte, pgtable_t token, unsigned long addr, + void *data) +{ + struct page *page = virt_to_page(addr); + pgprot_t prot = *(pgprot_t *)data; + + set_pte(pte, mk_pte(page, prot)); + return 0; +} + +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 no_kernel_map) +{ + unsigned long start = (unsigned long) page_address(page); + unsigned long end = start + size; + int (*func)(pte_t *pte, pgtable_t token, unsigned long addr, + void *data); + + if (no_kernel_map) + func = __dma_clear_pte; + else + func = __dma_update_pte; + + apply_to_page_range(&init_mm, start, size, func, &prot); + /* ensure prot is applied before returning */ + mb(); + flush_tlb_kernel_range(start, end); +} + + static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs) @@ -127,19 +167,31 @@ static void *__dma_alloc(struct device *dev, size_t size, if (coherent) return ptr; - /* remove any dirty cache lines on the kernel alias */ __dma_flush_area(ptr, size); - /* create a coherent mapping */ - page = virt_to_page(ptr); - coherent_ptr = dma_common_contiguous_remap(page, size, VM_USERMAP, - prot, __builtin_return_address(0)); - if (!coherent_ptr) - goto no_map; + if (attrs & DMA_ATTR_NO_KERNEL_MAPPING) { + coherent_ptr = (void *)NO_KERNEL_MAPPING_DUMMY; + __dma_remap(virt_to_page(ptr), size, __pgprot(0), true); + } else { + if ((attrs & DMA_ATTR_STRONGLY_ORDERED)) + __dma_remap(virt_to_page(ptr), size, __pgprot(0), true); + /* create a coherent mapping */ + page = virt_to_page(ptr); + coherent_ptr = dma_common_contiguous_remap( + page, size, VM_USERMAP, prot, + __builtin_return_address(0)); + if (!coherent_ptr) + goto no_map; + } return coherent_ptr; no_map: + if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) || + (attrs & DMA_ATTR_STRONGLY_ORDERED)) + __dma_remap(phys_to_page(dma_to_phys(dev, *dma_handle)), + size, PAGE_KERNEL, false); + swiotlb_free(dev, size, ptr, *dma_handle, attrs); no_mem: return NULL; @@ -153,11 +205,17 @@ static void __dma_free(struct device *dev, size_t size, size = PAGE_ALIGN(size); - if (!is_device_dma_coherent(dev)) { + if (!is_dma_coherent(dev, attrs)) { if (__free_from_pool(vaddr, size)) return; - vunmap(vaddr); + if (!(attrs & DMA_ATTR_NO_KERNEL_MAPPING)) + vunmap(vaddr); } + if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) || + (attrs & DMA_ATTR_STRONGLY_ORDERED)) + __dma_remap(phys_to_page(dma_to_phys(dev, dma_handle)), + size, PAGE_KERNEL, false); + swiotlb_free(dev, size, swiotlb_addr, dma_handle, attrs); }