18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Port on Texas Instruments TMS320C6x architecture
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
68c2ecf20Sopenharmony_ci *  Author: Aurelien Jacquiot <aurelien.jacquiot@ti.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  DMA uncached mapping support.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Using code pulled from ARM
118c2ecf20Sopenharmony_ci *  Copyright (C) 2000-2004 Russell King
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h>
198c2ecf20Sopenharmony_ci#include <linux/memblock.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
228c2ecf20Sopenharmony_ci#include <asm/page.h>
238c2ecf20Sopenharmony_ci#include <asm/setup.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * DMA coherent memory management, can be redefined using the memdma=
278c2ecf20Sopenharmony_ci * kernel command line
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* none by default */
318c2ecf20Sopenharmony_cistatic phys_addr_t dma_base;
328c2ecf20Sopenharmony_cistatic u32 dma_size;
338c2ecf20Sopenharmony_cistatic u32 dma_pages;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic unsigned long *dma_bitmap;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* bitmap lock */
388c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(dma_lock);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Return a DMA coherent and contiguous memory chunk from the DMA memory
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistatic inline u32 __alloc_dma_pages(int order)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	unsigned long flags;
468c2ecf20Sopenharmony_ci	u32 pos;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dma_lock, flags);
498c2ecf20Sopenharmony_ci	pos = bitmap_find_free_region(dma_bitmap, dma_pages, order);
508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dma_lock, flags);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return dma_base + (pos << PAGE_SHIFT);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void __free_dma_pages(u32 addr, int order)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	unsigned long flags;
588c2ecf20Sopenharmony_ci	u32 pos = (addr - dma_base) >> PAGE_SHIFT;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (addr < dma_base || (pos + (1 << order)) >= dma_pages) {
618c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: freeing outside range.\n", __func__);
628c2ecf20Sopenharmony_ci		BUG();
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dma_lock, flags);
668c2ecf20Sopenharmony_ci	bitmap_release_region(dma_bitmap, pos, order);
678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dma_lock, flags);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * Allocate DMA coherent memory space and return both the kernel
728c2ecf20Sopenharmony_ci * virtual and DMA address for that space.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_civoid *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
758c2ecf20Sopenharmony_ci		gfp_t gfp, unsigned long attrs)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	void *ret;
788c2ecf20Sopenharmony_ci	u32 paddr;
798c2ecf20Sopenharmony_ci	int order;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (!dma_size || !size)
828c2ecf20Sopenharmony_ci		return NULL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	paddr = __alloc_dma_pages(order);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (handle)
898c2ecf20Sopenharmony_ci		*handle = paddr;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (!paddr)
928c2ecf20Sopenharmony_ci		return NULL;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = phys_to_virt(paddr);
958c2ecf20Sopenharmony_ci	memset(ret, 0, 1 << order);
968c2ecf20Sopenharmony_ci	return ret;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * Free DMA coherent memory as defined by the above mapping.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_civoid arch_dma_free(struct device *dev, size_t size, void *vaddr,
1038c2ecf20Sopenharmony_ci		dma_addr_t dma_handle, unsigned long attrs)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int order;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!dma_size || !size)
1088c2ecf20Sopenharmony_ci		return;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	__free_dma_pages(virt_to_phys(vaddr), order);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Initialise the coherent DMA memory allocator using the given uncached region.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_civoid __init coherent_mem_init(phys_addr_t start, u32 size)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	if (!size)
1218c2ecf20Sopenharmony_ci		return;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	printk(KERN_INFO
1248c2ecf20Sopenharmony_ci	       "Coherent memory (DMA) region start=0x%x size=0x%x\n",
1258c2ecf20Sopenharmony_ci	       start, size);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	dma_base = start;
1288c2ecf20Sopenharmony_ci	dma_size = size;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* allocate bitmap */
1318c2ecf20Sopenharmony_ci	dma_pages = dma_size >> PAGE_SHIFT;
1328c2ecf20Sopenharmony_ci	if (dma_size & (PAGE_SIZE - 1))
1338c2ecf20Sopenharmony_ci		++dma_pages;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	dma_bitmap = memblock_alloc(BITS_TO_LONGS(dma_pages) * sizeof(long),
1368c2ecf20Sopenharmony_ci				    sizeof(long));
1378c2ecf20Sopenharmony_ci	if (!dma_bitmap)
1388c2ecf20Sopenharmony_ci		panic("%s: Failed to allocate %zu bytes align=0x%zx\n",
1398c2ecf20Sopenharmony_ci		      __func__, BITS_TO_LONGS(dma_pages) * sizeof(long),
1408c2ecf20Sopenharmony_ci		      sizeof(long));
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void c6x_dma_sync(phys_addr_t paddr, size_t size,
1448c2ecf20Sopenharmony_ci		enum dma_data_direction dir)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	BUG_ON(!valid_dma_direction(dir));
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	switch (dir) {
1498c2ecf20Sopenharmony_ci	case DMA_FROM_DEVICE:
1508c2ecf20Sopenharmony_ci		L2_cache_block_invalidate(paddr, paddr + size);
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case DMA_TO_DEVICE:
1538c2ecf20Sopenharmony_ci		L2_cache_block_writeback(paddr, paddr + size);
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case DMA_BIDIRECTIONAL:
1568c2ecf20Sopenharmony_ci		L2_cache_block_writeback_invalidate(paddr, paddr + size);
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci	default:
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_civoid arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
1648c2ecf20Sopenharmony_ci		enum dma_data_direction dir)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return c6x_dma_sync(paddr, size, dir);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_civoid arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
1708c2ecf20Sopenharmony_ci		enum dma_data_direction dir)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return c6x_dma_sync(paddr, size, dir);
1738c2ecf20Sopenharmony_ci}
174