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