xref: /linux/arch/csky/mm/dma-mapping.c (revision 52338415)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3 
4 #include <linux/cache.h>
5 #include <linux/dma-mapping.h>
6 #include <linux/dma-contiguous.h>
7 #include <linux/dma-noncoherent.h>
8 #include <linux/genalloc.h>
9 #include <linux/highmem.h>
10 #include <linux/io.h>
11 #include <linux/mm.h>
12 #include <linux/scatterlist.h>
13 #include <linux/types.h>
14 #include <linux/version.h>
15 #include <asm/cache.h>
16 
17 static inline void cache_op(phys_addr_t paddr, size_t size,
18 			    void (*fn)(unsigned long start, unsigned long end))
19 {
20 	struct page *page    = phys_to_page(paddr);
21 	void *start          = __va(page_to_phys(page));
22 	unsigned long offset = offset_in_page(paddr);
23 	size_t left          = size;
24 
25 	do {
26 		size_t len = left;
27 
28 		if (offset + len > PAGE_SIZE)
29 			len = PAGE_SIZE - offset;
30 
31 		if (PageHighMem(page)) {
32 			start = kmap_atomic(page);
33 
34 			fn((unsigned long)start + offset,
35 					(unsigned long)start + offset + len);
36 
37 			kunmap_atomic(start);
38 		} else {
39 			fn((unsigned long)start + offset,
40 					(unsigned long)start + offset + len);
41 		}
42 		offset = 0;
43 
44 		page++;
45 		start += PAGE_SIZE;
46 		left -= len;
47 	} while (left);
48 }
49 
50 static void dma_wbinv_set_zero_range(unsigned long start, unsigned long end)
51 {
52 	memset((void *)start, 0, end - start);
53 	dma_wbinv_range(start, end);
54 }
55 
56 void arch_dma_prep_coherent(struct page *page, size_t size)
57 {
58 	cache_op(page_to_phys(page), size, dma_wbinv_set_zero_range);
59 }
60 
61 void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
62 			      size_t size, enum dma_data_direction dir)
63 {
64 	switch (dir) {
65 	case DMA_TO_DEVICE:
66 		cache_op(paddr, size, dma_wb_range);
67 		break;
68 	case DMA_FROM_DEVICE:
69 	case DMA_BIDIRECTIONAL:
70 		cache_op(paddr, size, dma_wbinv_range);
71 		break;
72 	default:
73 		BUG();
74 	}
75 }
76 
77 void arch_sync_dma_for_cpu(struct device *dev, phys_addr_t paddr,
78 			   size_t size, enum dma_data_direction dir)
79 {
80 	switch (dir) {
81 	case DMA_TO_DEVICE:
82 		return;
83 	case DMA_FROM_DEVICE:
84 	case DMA_BIDIRECTIONAL:
85 		cache_op(paddr, size, dma_inv_range);
86 		break;
87 	default:
88 		BUG();
89 	}
90 }
91