diff options
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 57 | ||||
-rw-r--r-- | drivers/base/dma-mapping.c | 72 | ||||
-rw-r--r-- | include/asm-generic/dma-mapping-common.h | 9 |
3 files changed, 90 insertions, 48 deletions
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 7a996aaa061e..eecc8e60deea 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
@@ -298,37 +298,19 @@ static void * | |||
298 | __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, | 298 | __dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot, |
299 | const void *caller) | 299 | const void *caller) |
300 | { | 300 | { |
301 | struct vm_struct *area; | ||
302 | unsigned long addr; | ||
303 | |||
304 | /* | 301 | /* |
305 | * DMA allocation can be mapped to user space, so lets | 302 | * DMA allocation can be mapped to user space, so lets |
306 | * set VM_USERMAP flags too. | 303 | * set VM_USERMAP flags too. |
307 | */ | 304 | */ |
308 | area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP, | 305 | return dma_common_contiguous_remap(page, size, |
309 | caller); | 306 | VM_ARM_DMA_CONSISTENT | VM_USERMAP, |
310 | if (!area) | 307 | prot, caller); |
311 | return NULL; | ||
312 | addr = (unsigned long)area->addr; | ||
313 | area->phys_addr = __pfn_to_phys(page_to_pfn(page)); | ||
314 | |||
315 | if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) { | ||
316 | vunmap((void *)addr); | ||
317 | return NULL; | ||
318 | } | ||
319 | return (void *)addr; | ||
320 | } | 308 | } |
321 | 309 | ||
322 | static void __dma_free_remap(void *cpu_addr, size_t size) | 310 | static void __dma_free_remap(void *cpu_addr, size_t size) |
323 | { | 311 | { |
324 | unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP; | 312 | dma_common_free_remap(cpu_addr, size, |
325 | struct vm_struct *area = find_vm_area(cpu_addr); | 313 | VM_ARM_DMA_CONSISTENT | VM_USERMAP); |
326 | if (!area || (area->flags & flags) != flags) { | ||
327 | WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); | ||
328 | return; | ||
329 | } | ||
330 | unmap_kernel_range((unsigned long)cpu_addr, size); | ||
331 | vunmap(cpu_addr); | ||
332 | } | 314 | } |
333 | 315 | ||
334 | #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K | 316 | #define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K |
@@ -1271,29 +1253,8 @@ static void * | |||
1271 | __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot, | 1253 | __iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot, |
1272 | const void *caller) | 1254 | const void *caller) |
1273 | { | 1255 | { |
1274 | unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; | 1256 | return dma_common_pages_remap(pages, size, |
1275 | struct vm_struct *area; | 1257 | VM_ARM_DMA_CONSISTENT | VM_USERMAP, prot, caller); |
1276 | unsigned long p; | ||
1277 | |||
1278 | area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP, | ||
1279 | caller); | ||
1280 | if (!area) | ||
1281 | return NULL; | ||
1282 | |||
1283 | area->pages = pages; | ||
1284 | area->nr_pages = nr_pages; | ||
1285 | p = (unsigned long)area->addr; | ||
1286 | |||
1287 | for (i = 0; i < nr_pages; i++) { | ||
1288 | phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i])); | ||
1289 | if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot)) | ||
1290 | goto err; | ||
1291 | p += PAGE_SIZE; | ||
1292 | } | ||
1293 | return area->addr; | ||
1294 | err: | ||
1295 | unmap_kernel_range((unsigned long)area->addr, size); | ||
1296 | vunmap(area->addr); | ||
1297 | return NULL; | 1258 | return NULL; |
1298 | } | 1259 | } |
1299 | 1260 | ||
@@ -1501,8 +1462,8 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, | |||
1501 | } | 1462 | } |
1502 | 1463 | ||
1503 | if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { | 1464 | if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) { |
1504 | unmap_kernel_range((unsigned long)cpu_addr, size); | 1465 | dma_common_free_remap(cpu_addr, size, |
1505 | vunmap(cpu_addr); | 1466 | VM_ARM_DMA_CONSISTENT | VM_USERMAP); |
1506 | } | 1467 | } |
1507 | 1468 | ||
1508 | __iommu_remove_mapping(dev, handle, size); | 1469 | __iommu_remove_mapping(dev, handle, size); |
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 | ||
diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h index de8bf89940f8..a9fd248f5d48 100644 --- a/include/asm-generic/dma-mapping-common.h +++ b/include/asm-generic/dma-mapping-common.h | |||
@@ -179,6 +179,15 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
179 | extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, | 179 | extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, |
180 | void *cpu_addr, dma_addr_t dma_addr, size_t size); | 180 | void *cpu_addr, dma_addr_t dma_addr, size_t size); |
181 | 181 | ||
182 | void *dma_common_contiguous_remap(struct page *page, size_t size, | ||
183 | unsigned long vm_flags, | ||
184 | pgprot_t prot, const void *caller); | ||
185 | |||
186 | void *dma_common_pages_remap(struct page **pages, size_t size, | ||
187 | unsigned long vm_flags, pgprot_t prot, | ||
188 | const void *caller); | ||
189 | void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags); | ||
190 | |||
182 | /** | 191 | /** |
183 | * dma_mmap_attrs - map a coherent DMA allocation into user space | 192 | * dma_mmap_attrs - map a coherent DMA allocation into user space |
184 | * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices | 193 | * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices |