18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Based on linux/arch/arm/mm/dma-mapping.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2000-2004 Russell King 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-direct.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h> 128c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/cachetype.h> 158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 168c2ecf20Sopenharmony_ci#include <asm/outercache.h> 178c2ecf20Sopenharmony_ci#include <asm/cp15.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "dma.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * The generic direct mapping code is used if 238c2ecf20Sopenharmony_ci * - MMU/MPU is off 248c2ecf20Sopenharmony_ci * - cpu is v7m w/o cache support 258c2ecf20Sopenharmony_ci * - device is coherent 268c2ecf20Sopenharmony_ci * otherwise arm_nommu_dma_ops is used. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * arm_nommu_dma_ops rely on consistent DMA memory (please, refer to 298c2ecf20Sopenharmony_ci * [1] on how to declare such memory). 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * [1] Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void *arm_nommu_dma_alloc(struct device *dev, size_t size, 358c2ecf20Sopenharmony_ci dma_addr_t *dma_handle, gfp_t gfp, 368c2ecf20Sopenharmony_ci unsigned long attrs) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci void *ret = dma_alloc_from_global_coherent(dev, size, dma_handle); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * dma_alloc_from_global_coherent() may fail because: 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * - no consistent DMA region has been defined, so we can't 458c2ecf20Sopenharmony_ci * continue. 468c2ecf20Sopenharmony_ci * - there is no space left in consistent DMA region, so we 478c2ecf20Sopenharmony_ci * only can fallback to generic allocator if we are 488c2ecf20Sopenharmony_ci * advertised that consistency is not required. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret == NULL); 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void arm_nommu_dma_free(struct device *dev, size_t size, 568c2ecf20Sopenharmony_ci void *cpu_addr, dma_addr_t dma_addr, 578c2ecf20Sopenharmony_ci unsigned long attrs) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int ret = dma_release_from_global_coherent(get_order(size), cpu_addr); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret == 0); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int arm_nommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, 658c2ecf20Sopenharmony_ci void *cpu_addr, dma_addr_t dma_addr, size_t size, 668c2ecf20Sopenharmony_ci unsigned long attrs) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (dma_mmap_from_global_coherent(vma, cpu_addr, size, &ret)) 718c2ecf20Sopenharmony_ci return ret; 728c2ecf20Sopenharmony_ci if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret)) 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci return -ENXIO; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void __dma_page_cpu_to_dev(phys_addr_t paddr, size_t size, 798c2ecf20Sopenharmony_ci enum dma_data_direction dir) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci dmac_map_area(__va(paddr), size, dir); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (dir == DMA_FROM_DEVICE) 848c2ecf20Sopenharmony_ci outer_inv_range(paddr, paddr + size); 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci outer_clean_range(paddr, paddr + size); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void __dma_page_dev_to_cpu(phys_addr_t paddr, size_t size, 908c2ecf20Sopenharmony_ci enum dma_data_direction dir) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci if (dir != DMA_TO_DEVICE) { 938c2ecf20Sopenharmony_ci outer_inv_range(paddr, paddr + size); 948c2ecf20Sopenharmony_ci dmac_unmap_area(__va(paddr), size, dir); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic dma_addr_t arm_nommu_dma_map_page(struct device *dev, struct page *page, 998c2ecf20Sopenharmony_ci unsigned long offset, size_t size, 1008c2ecf20Sopenharmony_ci enum dma_data_direction dir, 1018c2ecf20Sopenharmony_ci unsigned long attrs) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci dma_addr_t handle = page_to_phys(page) + offset; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci __dma_page_cpu_to_dev(handle, size, dir); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return handle; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void arm_nommu_dma_unmap_page(struct device *dev, dma_addr_t handle, 1118c2ecf20Sopenharmony_ci size_t size, enum dma_data_direction dir, 1128c2ecf20Sopenharmony_ci unsigned long attrs) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci __dma_page_dev_to_cpu(handle, size, dir); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int arm_nommu_dma_map_sg(struct device *dev, struct scatterlist *sgl, 1198c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir, 1208c2ecf20Sopenharmony_ci unsigned long attrs) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int i; 1238c2ecf20Sopenharmony_ci struct scatterlist *sg; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) { 1268c2ecf20Sopenharmony_ci sg_dma_address(sg) = sg_phys(sg); 1278c2ecf20Sopenharmony_ci sg_dma_len(sg) = sg->length; 1288c2ecf20Sopenharmony_ci __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return nents; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void arm_nommu_dma_unmap_sg(struct device *dev, struct scatterlist *sgl, 1358c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir, 1368c2ecf20Sopenharmony_ci unsigned long attrs) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct scatterlist *sg; 1398c2ecf20Sopenharmony_ci int i; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 1428c2ecf20Sopenharmony_ci __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void arm_nommu_dma_sync_single_for_device(struct device *dev, 1468c2ecf20Sopenharmony_ci dma_addr_t handle, size_t size, enum dma_data_direction dir) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci __dma_page_cpu_to_dev(handle, size, dir); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void arm_nommu_dma_sync_single_for_cpu(struct device *dev, 1528c2ecf20Sopenharmony_ci dma_addr_t handle, size_t size, enum dma_data_direction dir) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci __dma_page_cpu_to_dev(handle, size, dir); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void arm_nommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, 1588c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct scatterlist *sg; 1618c2ecf20Sopenharmony_ci int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 1648c2ecf20Sopenharmony_ci __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void arm_nommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, 1688c2ecf20Sopenharmony_ci int nents, enum dma_data_direction dir) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct scatterlist *sg; 1718c2ecf20Sopenharmony_ci int i; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 1748c2ecf20Sopenharmony_ci __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciconst struct dma_map_ops arm_nommu_dma_ops = { 1788c2ecf20Sopenharmony_ci .alloc = arm_nommu_dma_alloc, 1798c2ecf20Sopenharmony_ci .free = arm_nommu_dma_free, 1808c2ecf20Sopenharmony_ci .alloc_pages = dma_direct_alloc_pages, 1818c2ecf20Sopenharmony_ci .free_pages = dma_direct_free_pages, 1828c2ecf20Sopenharmony_ci .mmap = arm_nommu_dma_mmap, 1838c2ecf20Sopenharmony_ci .map_page = arm_nommu_dma_map_page, 1848c2ecf20Sopenharmony_ci .unmap_page = arm_nommu_dma_unmap_page, 1858c2ecf20Sopenharmony_ci .map_sg = arm_nommu_dma_map_sg, 1868c2ecf20Sopenharmony_ci .unmap_sg = arm_nommu_dma_unmap_sg, 1878c2ecf20Sopenharmony_ci .sync_single_for_device = arm_nommu_dma_sync_single_for_device, 1888c2ecf20Sopenharmony_ci .sync_single_for_cpu = arm_nommu_dma_sync_single_for_cpu, 1898c2ecf20Sopenharmony_ci .sync_sg_for_device = arm_nommu_dma_sync_sg_for_device, 1908c2ecf20Sopenharmony_ci .sync_sg_for_cpu = arm_nommu_dma_sync_sg_for_cpu, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(arm_nommu_dma_ops); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, 1958c2ecf20Sopenharmony_ci const struct iommu_ops *iommu, bool coherent) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_CPU_V7M)) { 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Cache support for v7m is optional, so can be treated as 2008c2ecf20Sopenharmony_ci * coherent if no cache has been detected. Note that it is not 2018c2ecf20Sopenharmony_ci * enough to check if MPU is in use or not since in absense of 2028c2ecf20Sopenharmony_ci * MPU system memory map is used. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci dev->archdata.dma_coherent = (cacheid) ? coherent : true; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Assume coherent DMA in case MMU/MPU has not been set up. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci dev->archdata.dma_coherent = (get_cr() & CR_M) ? coherent : true; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!dev->archdata.dma_coherent) 2138c2ecf20Sopenharmony_ci set_dma_ops(dev, &arm_nommu_dma_ops); 2148c2ecf20Sopenharmony_ci} 215