diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/dma-mapping.c | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 6cd08e145bfa..9e8bbdd470ca 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c | |||
@@ -10,6 +10,8 @@ | |||
10 | #include <linux/dma-mapping.h> | 10 | #include <linux/dma-mapping.h> |
11 | #include <linux/export.h> | 11 | #include <linux/export.h> |
12 | #include <linux/gfp.h> | 12 | #include <linux/gfp.h> |
13 | #include <linux/slab.h> | ||
14 | #include <linux/vmalloc.h> | ||
13 | #include <asm-generic/dma-coherent.h> | 15 | #include <asm-generic/dma-coherent.h> |
14 | 16 | ||
15 | /* | 17 | /* |
@@ -267,3 +269,73 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, | |||
267 | return ret; | 269 | return ret; |
268 | } | 270 | } |
269 | EXPORT_SYMBOL(dma_common_mmap); | 271 | EXPORT_SYMBOL(dma_common_mmap); |
272 | |||
273 | #ifdef CONFIG_MMU | ||
274 | /* | ||
275 | * remaps an array of PAGE_SIZE pages into another vm_area | ||
276 | * Cannot be used in non-sleeping contexts | ||
277 | */ | ||
278 | void *dma_common_pages_remap(struct page **pages, size_t size, | ||
279 | unsigned long vm_flags, pgprot_t prot, | ||
280 | const void *caller) | ||
281 | { | ||
282 | struct vm_struct *area; | ||
283 | |||
284 | area = get_vm_area_caller(size, vm_flags, caller); | ||
285 | if (!area) | ||
286 | return NULL; | ||
287 | |||
288 | area->pages = pages; | ||
289 | |||
290 | if (map_vm_area(area, prot, pages)) { | ||
291 | vunmap(area->addr); | ||
292 | return NULL; | ||
293 | } | ||
294 | |||
295 | return area->addr; | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * remaps an allocated contiguous region into another vm_area. | ||
300 | * Cannot be used in non-sleeping contexts | ||
301 | */ | ||
302 | |||
303 | void *dma_common_contiguous_remap(struct page *page, size_t size, | ||
304 | unsigned long vm_flags, | ||
305 | pgprot_t prot, const void *caller) | ||
306 | { | ||
307 | int i; | ||
308 | struct page **pages; | ||
309 | void *ptr; | ||
310 | unsigned long pfn; | ||
311 | |||
312 | pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL); | ||
313 | if (!pages) | ||
314 | return NULL; | ||
315 | |||
316 | for (i = 0, pfn = page_to_pfn(page); i < (size >> PAGE_SHIFT); i++) | ||
317 | pages[i] = pfn_to_page(pfn + i); | ||
318 | |||
319 | ptr = dma_common_pages_remap(pages, size, vm_flags, prot, caller); | ||
320 | |||
321 | kfree(pages); | ||
322 | |||
323 | return ptr; | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * unmaps a range previously mapped by dma_common_*_remap | ||
328 | */ | ||
329 | void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags) | ||
330 | { | ||
331 | struct vm_struct *area = find_vm_area(cpu_addr); | ||
332 | |||
333 | if (!area || (area->flags & vm_flags) != vm_flags) { | ||
334 | WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | unmap_kernel_range((unsigned long)cpu_addr, size); | ||
339 | vunmap(cpu_addr); | ||
340 | } | ||
341 | #endif | ||