From 0e5bee4422ce26dbbcae3c246208028a8da75c79 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Mon, 24 Feb 2020 10:00:12 -0800 Subject: [PATCH] ANDROID: GKI: add dma_map_ops remap/unremap operations Trimmed down version of: 4008eb493aa2 "iommu/arm-smmu: Merge of smmu changes from 4.14" which includes only changes required for removed-dma-pool driver. Bug: 145617272 Signed-off-by: Swathi Sridhar Signed-off-by: Suren Baghdasaryan Change-Id: I4b9ae665aaf8aabf89c3b158bc38db458f31657a --- arch/arm64/mm/dma-mapping.c | 53 +++++++++++++++++++++++++++++++++++++ include/linux/dma-mapping.h | 38 ++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index d3a5bb16f0b2..518a8c27ce2f 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -31,6 +31,7 @@ #include #include +#include static int swiotlb __ro_after_init; @@ -321,6 +322,56 @@ static int __swiotlb_dma_supported(struct device *hwdev, u64 mask) return 1; } +static void *arm64_dma_remap(struct device *dev, void *cpu_addr, + dma_addr_t handle, size_t size, + unsigned long attrs) +{ + struct page *page = phys_to_page(dma_to_phys(dev, handle)); + bool coherent = is_device_dma_coherent(dev); + pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, coherent); + unsigned long offset = handle & ~PAGE_MASK; + struct vm_struct *area; + unsigned long addr; + + size = PAGE_ALIGN(size + offset); + + /* + * DMA allocation can be mapped to user space, so lets + * set VM_USERMAP flags too. + */ + area = get_vm_area(size, VM_USERMAP); + if (!area) + return NULL; + + addr = (unsigned long)area->addr; + area->phys_addr = __pfn_to_phys(page_to_pfn(page)); + + if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) { + vunmap((void *)addr); + return NULL; + } + return (void *)addr + offset; +} + +static void arm64_dma_unremap(struct device *dev, void *remapped_addr, + size_t size) +{ + 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) { + WARN(1, "trying to free invalid coherent area: %pK\n", + remapped_addr); + return; + } + vunmap(remapped_addr); + flush_tlb_kernel_range((unsigned long)remapped_addr, + (unsigned long)(remapped_addr + size)); +} + static int __swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t addr) { if (swiotlb) @@ -343,6 +394,8 @@ static const struct dma_map_ops arm64_swiotlb_dma_ops = { .sync_sg_for_device = __swiotlb_sync_sg_for_device, .dma_supported = __swiotlb_dma_supported, .mapping_error = __swiotlb_dma_mapping_error, + .remap = arm64_dma_remap, + .unremap = arm64_dma_unremap, }; static int __init atomic_pool_init(void) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 669cde2fa872..d042a15e9aa7 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -70,6 +70,11 @@ */ #define DMA_ATTR_PRIVILEGED (1UL << 9) +/* + * DMA_ATTR_SKIP_ZEROING: Do not zero mapping. + */ +#define DMA_ATTR_SKIP_ZEROING (1UL << 11) + /* * A dma_addr_t can hold any valid DMA or bus address for the platform. * It can be given to a device to use as a DMA source or target. A CPU cannot @@ -130,6 +135,10 @@ struct dma_map_ops { enum dma_data_direction direction); int (*mapping_error)(struct device *dev, dma_addr_t dma_addr); int (*dma_supported)(struct device *dev, u64 mask); + void *(*remap)(struct device *dev, void *cpu_addr, dma_addr_t handle, + size_t size, unsigned long attrs); + void (*unremap)(struct device *dev, void *remapped_address, + size_t size); #ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK u64 (*get_required_mask)(struct device *dev); #endif @@ -608,6 +617,35 @@ static inline int dma_set_mask(struct device *dev, u64 mask) return 0; } #endif +static inline void *dma_remap(struct device *dev, void *cpu_addr, + dma_addr_t dma_handle, size_t size, unsigned long attrs) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + if (!ops->remap) { + WARN_ONCE(1, "Remap function not implemented for %pS\n", + ops->remap); + return NULL; + } + + return ops->remap(dev, cpu_addr, dma_handle, size, attrs); +} + + +static inline void dma_unremap(struct device *dev, void *remapped_addr, + size_t size) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + if (!ops->unremap) { + WARN_ONCE(1, "unremap function not implemented for %pS\n", + ops->unremap); + return; + } + + return ops->unremap(dev, remapped_addr, size); +} + static inline u64 dma_get_mask(struct device *dev) {