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