xref: /openbmc/linux/arch/hexagon/kernel/dma.c (revision 08dbd0f8)
108dbd0f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
265c3d7c5SRichard Kuo /*
365c3d7c5SRichard Kuo  * DMA implementation for Hexagon
465c3d7c5SRichard Kuo  *
55e115054SRichard Kuo  * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
665c3d7c5SRichard Kuo  */
765c3d7c5SRichard Kuo 
8e0a9317dSChristoph Hellwig #include <linux/dma-noncoherent.h>
957c8a661SMike Rapoport #include <linux/memblock.h>
1065c3d7c5SRichard Kuo #include <linux/genalloc.h>
116bbbc30cSRichard Kuo #include <linux/module.h>
125e115054SRichard Kuo #include <asm/page.h>
1365c3d7c5SRichard Kuo 
1465c3d7c5SRichard Kuo static struct gen_pool *coherent_pool;
1565c3d7c5SRichard Kuo 
1665c3d7c5SRichard Kuo 
1765c3d7c5SRichard Kuo /* Allocates from a pool of uncached memory that was reserved at boot time */
1865c3d7c5SRichard Kuo 
19e0a9317dSChristoph Hellwig void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_addr,
20e0a9317dSChristoph Hellwig 		gfp_t flag, unsigned long attrs)
2165c3d7c5SRichard Kuo {
2265c3d7c5SRichard Kuo 	void *ret;
2365c3d7c5SRichard Kuo 
245e115054SRichard Kuo 	/*
255e115054SRichard Kuo 	 * Our max_low_pfn should have been backed off by 16MB in
265e115054SRichard Kuo 	 * mm/init.c to create DMA coherent space.  Use that as the VA
275e115054SRichard Kuo 	 * for the pool.
285e115054SRichard Kuo 	 */
295e115054SRichard Kuo 
3065c3d7c5SRichard Kuo 	if (coherent_pool == NULL) {
3165c3d7c5SRichard Kuo 		coherent_pool = gen_pool_create(PAGE_SHIFT, -1);
3265c3d7c5SRichard Kuo 
3365c3d7c5SRichard Kuo 		if (coherent_pool == NULL)
3465c3d7c5SRichard Kuo 			panic("Can't create %s() memory pool!", __func__);
3565c3d7c5SRichard Kuo 		else
3665c3d7c5SRichard Kuo 			gen_pool_add(coherent_pool,
37200f351eSRandy Dunlap 				(unsigned long)pfn_to_virt(max_low_pfn),
3865c3d7c5SRichard Kuo 				hexagon_coherent_pool_size, -1);
3965c3d7c5SRichard Kuo 	}
4065c3d7c5SRichard Kuo 
4165c3d7c5SRichard Kuo 	ret = (void *) gen_pool_alloc(coherent_pool, size);
4265c3d7c5SRichard Kuo 
4365c3d7c5SRichard Kuo 	if (ret) {
4465c3d7c5SRichard Kuo 		memset(ret, 0, size);
455e115054SRichard Kuo 		*dma_addr = (dma_addr_t) virt_to_phys(ret);
4665c3d7c5SRichard Kuo 	} else
4765c3d7c5SRichard Kuo 		*dma_addr = ~0;
4865c3d7c5SRichard Kuo 
4965c3d7c5SRichard Kuo 	return ret;
5065c3d7c5SRichard Kuo }
5165c3d7c5SRichard Kuo 
52e0a9317dSChristoph Hellwig void arch_dma_free(struct device *dev, size_t size, void *vaddr,
5300085f1eSKrzysztof Kozlowski 		dma_addr_t dma_addr, unsigned long attrs)
5465c3d7c5SRichard Kuo {
5565c3d7c5SRichard Kuo 	gen_pool_free(coherent_pool, (unsigned long) vaddr, size);
5665c3d7c5SRichard Kuo }
5765c3d7c5SRichard Kuo 
58e0a9317dSChristoph Hellwig void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
59e0a9317dSChristoph Hellwig 		size_t size, enum dma_data_direction dir)
6065c3d7c5SRichard Kuo {
61e0a9317dSChristoph Hellwig 	void *addr = phys_to_virt(paddr);
6265c3d7c5SRichard Kuo 
6365c3d7c5SRichard Kuo 	switch (dir) {
6465c3d7c5SRichard Kuo 	case DMA_TO_DEVICE:
6565c3d7c5SRichard Kuo 		hexagon_clean_dcache_range((unsigned long) addr,
6665c3d7c5SRichard Kuo 		(unsigned long) addr + size);
6765c3d7c5SRichard Kuo 		break;
6865c3d7c5SRichard Kuo 	case DMA_FROM_DEVICE:
6965c3d7c5SRichard Kuo 		hexagon_inv_dcache_range((unsigned long) addr,
7065c3d7c5SRichard Kuo 		(unsigned long) addr + size);
7165c3d7c5SRichard Kuo 		break;
7265c3d7c5SRichard Kuo 	case DMA_BIDIRECTIONAL:
7365c3d7c5SRichard Kuo 		flush_dcache_range((unsigned long) addr,
7465c3d7c5SRichard Kuo 		(unsigned long) addr + size);
7565c3d7c5SRichard Kuo 		break;
7665c3d7c5SRichard Kuo 	default:
7765c3d7c5SRichard Kuo 		BUG();
7865c3d7c5SRichard Kuo 	}
7965c3d7c5SRichard Kuo }
80