Branch data Line data Source code
1 : : /*
2 : : * drivers/base/dma-mapping.c - arch-independent dma-mapping routines
3 : : *
4 : : * Copyright (c) 2006 SUSE Linux Products GmbH
5 : : * Copyright (c) 2006 Tejun Heo <teheo@suse.de>
6 : : *
7 : : * This file is released under the GPLv2.
8 : : */
9 : :
10 : : #include <linux/dma-mapping.h>
11 : : #include <linux/export.h>
12 : : #include <linux/gfp.h>
13 : : #include <asm-generic/dma-coherent.h>
14 : :
15 : : /*
16 : : * Managed DMA API
17 : : */
18 : : struct dma_devres {
19 : : size_t size;
20 : : void *vaddr;
21 : : dma_addr_t dma_handle;
22 : : };
23 : :
24 : 0 : static void dmam_coherent_release(struct device *dev, void *res)
25 : : {
26 : : struct dma_devres *this = res;
27 : :
28 : 0 : dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle);
29 : 0 : }
30 : :
31 : 0 : static void dmam_noncoherent_release(struct device *dev, void *res)
32 : : {
33 : : struct dma_devres *this = res;
34 : :
35 : : dma_free_noncoherent(dev, this->size, this->vaddr, this->dma_handle);
36 : 0 : }
37 : :
38 : 0 : static int dmam_match(struct device *dev, void *res, void *match_data)
39 : : {
40 : : struct dma_devres *this = res, *match = match_data;
41 : :
42 [ # # ]: 0 : if (this->vaddr == match->vaddr) {
43 [ # # ][ # # ]: 0 : WARN_ON(this->size != match->size ||
[ # # ]
44 : : this->dma_handle != match->dma_handle);
45 : : return 1;
46 : : }
47 : : return 0;
48 : : }
49 : :
50 : : /**
51 : : * dmam_alloc_coherent - Managed dma_alloc_coherent()
52 : : * @dev: Device to allocate coherent memory for
53 : : * @size: Size of allocation
54 : : * @dma_handle: Out argument for allocated DMA handle
55 : : * @gfp: Allocation flags
56 : : *
57 : : * Managed dma_alloc_coherent(). Memory allocated using this function
58 : : * will be automatically released on driver detach.
59 : : *
60 : : * RETURNS:
61 : : * Pointer to allocated memory on success, NULL on failure.
62 : : */
63 : 0 : void * dmam_alloc_coherent(struct device *dev, size_t size,
64 : : dma_addr_t *dma_handle, gfp_t gfp)
65 : : {
66 : : struct dma_devres *dr;
67 : : void *vaddr;
68 : :
69 : 0 : dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp);
70 [ # # ]: 0 : if (!dr)
71 : : return NULL;
72 : :
73 : : vaddr = dma_alloc_coherent(dev, size, dma_handle, gfp);
74 [ # # ]: 0 : if (!vaddr) {
75 : 0 : devres_free(dr);
76 : 0 : return NULL;
77 : : }
78 : :
79 : 0 : dr->vaddr = vaddr;
80 : 0 : dr->dma_handle = *dma_handle;
81 : 0 : dr->size = size;
82 : :
83 : 0 : devres_add(dev, dr);
84 : :
85 : 0 : return vaddr;
86 : : }
87 : : EXPORT_SYMBOL(dmam_alloc_coherent);
88 : :
89 : : /**
90 : : * dmam_free_coherent - Managed dma_free_coherent()
91 : : * @dev: Device to free coherent memory for
92 : : * @size: Size of allocation
93 : : * @vaddr: Virtual address of the memory to free
94 : : * @dma_handle: DMA handle of the memory to free
95 : : *
96 : : * Managed dma_free_coherent().
97 : : */
98 : 0 : void dmam_free_coherent(struct device *dev, size_t size, void *vaddr,
99 : : dma_addr_t dma_handle)
100 : : {
101 : 0 : struct dma_devres match_data = { size, vaddr, dma_handle };
102 : :
103 : : dma_free_coherent(dev, size, vaddr, dma_handle);
104 [ # # ]: 0 : WARN_ON(devres_destroy(dev, dmam_coherent_release, dmam_match,
105 : : &match_data));
106 : 0 : }
107 : : EXPORT_SYMBOL(dmam_free_coherent);
108 : :
109 : : /**
110 : : * dmam_alloc_non_coherent - Managed dma_alloc_non_coherent()
111 : : * @dev: Device to allocate non_coherent memory for
112 : : * @size: Size of allocation
113 : : * @dma_handle: Out argument for allocated DMA handle
114 : : * @gfp: Allocation flags
115 : : *
116 : : * Managed dma_alloc_non_coherent(). Memory allocated using this
117 : : * function will be automatically released on driver detach.
118 : : *
119 : : * RETURNS:
120 : : * Pointer to allocated memory on success, NULL on failure.
121 : : */
122 : 0 : void *dmam_alloc_noncoherent(struct device *dev, size_t size,
123 : : dma_addr_t *dma_handle, gfp_t gfp)
124 : : {
125 : : struct dma_devres *dr;
126 : : void *vaddr;
127 : :
128 : 0 : dr = devres_alloc(dmam_noncoherent_release, sizeof(*dr), gfp);
129 [ # # ]: 0 : if (!dr)
130 : : return NULL;
131 : :
132 : : vaddr = dma_alloc_noncoherent(dev, size, dma_handle, gfp);
133 : : if (!vaddr) {
134 : 0 : devres_free(dr);
135 : 0 : return NULL;
136 : : }
137 : :
138 : : dr->vaddr = vaddr;
139 : : dr->dma_handle = *dma_handle;
140 : : dr->size = size;
141 : :
142 : : devres_add(dev, dr);
143 : :
144 : : return vaddr;
145 : : }
146 : : EXPORT_SYMBOL(dmam_alloc_noncoherent);
147 : :
148 : : /**
149 : : * dmam_free_coherent - Managed dma_free_noncoherent()
150 : : * @dev: Device to free noncoherent memory for
151 : : * @size: Size of allocation
152 : : * @vaddr: Virtual address of the memory to free
153 : : * @dma_handle: DMA handle of the memory to free
154 : : *
155 : : * Managed dma_free_noncoherent().
156 : : */
157 : 0 : void dmam_free_noncoherent(struct device *dev, size_t size, void *vaddr,
158 : : dma_addr_t dma_handle)
159 : : {
160 : 0 : struct dma_devres match_data = { size, vaddr, dma_handle };
161 : :
162 : : dma_free_noncoherent(dev, size, vaddr, dma_handle);
163 [ # # ]: 0 : WARN_ON(!devres_destroy(dev, dmam_noncoherent_release, dmam_match,
164 : : &match_data));
165 : 0 : }
166 : : EXPORT_SYMBOL(dmam_free_noncoherent);
167 : :
168 : : #ifdef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
169 : :
170 : 0 : static void dmam_coherent_decl_release(struct device *dev, void *res)
171 : : {
172 : 0 : dma_release_declared_memory(dev);
173 : 0 : }
174 : :
175 : : /**
176 : : * dmam_declare_coherent_memory - Managed dma_declare_coherent_memory()
177 : : * @dev: Device to declare coherent memory for
178 : : * @bus_addr: Bus address of coherent memory to be declared
179 : : * @device_addr: Device address of coherent memory to be declared
180 : : * @size: Size of coherent memory to be declared
181 : : * @flags: Flags
182 : : *
183 : : * Managed dma_declare_coherent_memory().
184 : : *
185 : : * RETURNS:
186 : : * 0 on success, -errno on failure.
187 : : */
188 : 0 : int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
189 : : dma_addr_t device_addr, size_t size, int flags)
190 : : {
191 : : void *res;
192 : : int rc;
193 : :
194 : 0 : res = devres_alloc(dmam_coherent_decl_release, 0, GFP_KERNEL);
195 [ # # ]: 0 : if (!res)
196 : : return -ENOMEM;
197 : :
198 : 0 : rc = dma_declare_coherent_memory(dev, bus_addr, device_addr, size,
199 : : flags);
200 [ # # ]: 0 : if (rc == 0)
201 : 0 : devres_add(dev, res);
202 : : else
203 : 0 : devres_free(res);
204 : :
205 : 0 : return rc;
206 : : }
207 : : EXPORT_SYMBOL(dmam_declare_coherent_memory);
208 : :
209 : : /**
210 : : * dmam_release_declared_memory - Managed dma_release_declared_memory().
211 : : * @dev: Device to release declared coherent memory for
212 : : *
213 : : * Managed dmam_release_declared_memory().
214 : : */
215 : 0 : void dmam_release_declared_memory(struct device *dev)
216 : : {
217 [ # # ]: 0 : WARN_ON(devres_destroy(dev, dmam_coherent_decl_release, NULL, NULL));
218 : 0 : }
219 : : EXPORT_SYMBOL(dmam_release_declared_memory);
220 : :
221 : : #endif
222 : :
223 : : /*
224 : : * Create scatter-list for the already allocated DMA buffer.
225 : : */
226 : 0 : int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
227 : : void *cpu_addr, dma_addr_t handle, size_t size)
228 : : {
229 : 0 : struct page *page = virt_to_page(cpu_addr);
230 : : int ret;
231 : :
232 : 0 : ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
233 [ # # ]: 0 : if (unlikely(ret))
234 : : return ret;
235 : :
236 : 0 : sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
237 : 0 : return 0;
238 : : }
239 : : EXPORT_SYMBOL(dma_common_get_sgtable);
240 : :
241 : : /*
242 : : * Create userspace mapping for the DMA-coherent memory.
243 : : */
244 : 0 : int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
245 : : void *cpu_addr, dma_addr_t dma_addr, size_t size)
246 : : {
247 : 0 : int ret = -ENXIO;
248 : : #ifdef CONFIG_MMU
249 : 0 : unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
250 : 0 : unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
251 : 0 : unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
252 : 0 : unsigned long off = vma->vm_pgoff;
253 : :
254 : 0 : vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
255 : :
256 [ # # ]: 0 : if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
257 : 0 : return ret;
258 : :
259 [ # # ][ # # ]: 0 : if (off < count && user_count <= (count - off)) {
260 : 0 : ret = remap_pfn_range(vma, vma->vm_start,
261 : : pfn + off,
262 : : user_count << PAGE_SHIFT,
263 : : vma->vm_page_prot);
264 : : }
265 : : #endif /* CONFIG_MMU */
266 : :
267 : 0 : return ret;
268 : : }
269 : : EXPORT_SYMBOL(dma_common_mmap);
|