Commit | Line | Data |
---|---|---|
e23c621f LFT |
1 | /* |
2 | * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> | |
3 | * Copyright (C) 2009 Wind River Systems Inc | |
4 | * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com | |
5 | * | |
6 | * Based on DMA code from MIPS. | |
7 | * | |
8 | * This file is subject to the terms and conditions of the GNU General Public | |
9 | * License. See the file "COPYING" in the main directory of this archive | |
10 | * for more details. | |
11 | */ | |
12 | ||
13 | #include <linux/types.h> | |
14 | #include <linux/mm.h> | |
15 | #include <linux/export.h> | |
16 | #include <linux/string.h> | |
17 | #include <linux/scatterlist.h> | |
18 | #include <linux/dma-mapping.h> | |
19 | #include <linux/io.h> | |
20 | #include <linux/cache.h> | |
21 | #include <asm/cacheflush.h> | |
22 | ||
23 | ||
24 | void *dma_alloc_coherent(struct device *dev, size_t size, | |
25 | dma_addr_t *dma_handle, gfp_t gfp) | |
26 | { | |
27 | void *ret; | |
28 | ||
29 | /* ignore region specifiers */ | |
30 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | |
31 | ||
32 | /* optimized page clearing */ | |
33 | gfp |= __GFP_ZERO; | |
34 | ||
35 | if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) | |
36 | gfp |= GFP_DMA; | |
37 | ||
38 | ret = (void *) __get_free_pages(gfp, get_order(size)); | |
39 | if (ret != NULL) { | |
40 | *dma_handle = virt_to_phys(ret); | |
41 | flush_dcache_range((unsigned long) ret, | |
42 | (unsigned long) ret + size); | |
43 | ret = UNCAC_ADDR(ret); | |
44 | } | |
45 | ||
46 | return ret; | |
47 | } | |
48 | EXPORT_SYMBOL(dma_alloc_coherent); | |
49 | ||
50 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | |
51 | dma_addr_t dma_handle) | |
52 | { | |
53 | unsigned long addr = (unsigned long) CAC_ADDR((unsigned long) vaddr); | |
54 | ||
55 | free_pages(addr, get_order(size)); | |
56 | } | |
57 | EXPORT_SYMBOL(dma_free_coherent); | |
58 | ||
59 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | |
60 | enum dma_data_direction direction) | |
61 | { | |
62 | int i; | |
63 | ||
64 | BUG_ON(!valid_dma_direction(direction)); | |
65 | ||
66 | for_each_sg(sg, sg, nents, i) { | |
67 | void *addr; | |
68 | ||
69 | addr = sg_virt(sg); | |
70 | if (addr) { | |
71 | __dma_sync_for_device(addr, sg->length, direction); | |
72 | sg->dma_address = sg_phys(sg); | |
73 | } | |
74 | } | |
75 | ||
76 | return nents; | |
77 | } | |
78 | EXPORT_SYMBOL(dma_map_sg); | |
79 | ||
80 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | |
81 | unsigned long offset, size_t size, | |
82 | enum dma_data_direction direction) | |
83 | { | |
84 | void *addr; | |
85 | ||
86 | BUG_ON(!valid_dma_direction(direction)); | |
87 | ||
88 | addr = page_address(page) + offset; | |
89 | __dma_sync_for_device(addr, size, direction); | |
90 | ||
91 | return page_to_phys(page) + offset; | |
92 | } | |
93 | EXPORT_SYMBOL(dma_map_page); | |
94 | ||
95 | void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, | |
96 | enum dma_data_direction direction) | |
97 | { | |
98 | BUG_ON(!valid_dma_direction(direction)); | |
99 | ||
100 | __dma_sync_for_cpu(phys_to_virt(dma_address), size, direction); | |
101 | } | |
102 | EXPORT_SYMBOL(dma_unmap_page); | |
103 | ||
104 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, | |
105 | enum dma_data_direction direction) | |
106 | { | |
107 | void *addr; | |
108 | int i; | |
109 | ||
110 | BUG_ON(!valid_dma_direction(direction)); | |
111 | ||
112 | if (direction == DMA_TO_DEVICE) | |
113 | return; | |
114 | ||
115 | for_each_sg(sg, sg, nhwentries, i) { | |
116 | addr = sg_virt(sg); | |
117 | if (addr) | |
118 | __dma_sync_for_cpu(addr, sg->length, direction); | |
119 | } | |
120 | } | |
121 | EXPORT_SYMBOL(dma_unmap_sg); | |
122 | ||
123 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | |
124 | size_t size, enum dma_data_direction direction) | |
125 | { | |
126 | BUG_ON(!valid_dma_direction(direction)); | |
127 | ||
128 | __dma_sync_for_cpu(phys_to_virt(dma_handle), size, direction); | |
129 | } | |
130 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | |
131 | ||
132 | void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | |
133 | size_t size, enum dma_data_direction direction) | |
134 | { | |
135 | BUG_ON(!valid_dma_direction(direction)); | |
136 | ||
137 | __dma_sync_for_device(phys_to_virt(dma_handle), size, direction); | |
138 | } | |
139 | EXPORT_SYMBOL(dma_sync_single_for_device); | |
140 | ||
141 | void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, | |
142 | unsigned long offset, size_t size, | |
143 | enum dma_data_direction direction) | |
144 | { | |
145 | BUG_ON(!valid_dma_direction(direction)); | |
146 | ||
147 | __dma_sync_for_cpu(phys_to_virt(dma_handle), size, direction); | |
148 | } | |
149 | EXPORT_SYMBOL(dma_sync_single_range_for_cpu); | |
150 | ||
151 | void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, | |
152 | unsigned long offset, size_t size, | |
153 | enum dma_data_direction direction) | |
154 | { | |
155 | BUG_ON(!valid_dma_direction(direction)); | |
156 | ||
157 | __dma_sync_for_device(phys_to_virt(dma_handle), size, direction); | |
158 | } | |
159 | EXPORT_SYMBOL(dma_sync_single_range_for_device); | |
160 | ||
161 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, | |
162 | enum dma_data_direction direction) | |
163 | { | |
164 | int i; | |
165 | ||
166 | BUG_ON(!valid_dma_direction(direction)); | |
167 | ||
168 | /* Make sure that gcc doesn't leave the empty loop body. */ | |
169 | for_each_sg(sg, sg, nelems, i) | |
170 | __dma_sync_for_cpu(sg_virt(sg), sg->length, direction); | |
171 | } | |
172 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | |
173 | ||
174 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |
175 | int nelems, enum dma_data_direction direction) | |
176 | { | |
177 | int i; | |
178 | ||
179 | BUG_ON(!valid_dma_direction(direction)); | |
180 | ||
181 | /* Make sure that gcc doesn't leave the empty loop body. */ | |
182 | for_each_sg(sg, sg, nelems, i) | |
183 | __dma_sync_for_device(sg_virt(sg), sg->length, direction); | |
184 | ||
185 | } | |
186 | EXPORT_SYMBOL(dma_sync_sg_for_device); |