diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-10-31 12:52:16 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-02-15 10:21:43 -0500 |
commit | 18eabe2347ae7a11b3db768695913724166dfb0e (patch) | |
tree | 2f6a9bb654d01e07a62be75adc1282e97b5c16d4 | |
parent | bf32eb85492af197ea5ff20e0be56f667a80584d (diff) |
ARM: dma-mapping: introduce the idea of buffer ownership
The DMA API has the notion of buffer ownership; make it explicit in the
ARM implementation of this API. This gives us a set of hooks to allow
us to deal with CPU cache issues arising from non-cache coherent DMA.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Tested-By: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-By: Jamie Iles <jamie@jamieiles.com>
-rw-r--r-- | arch/arm/common/dmabounce.c | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/dma-mapping.h | 64 | ||||
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 13 |
3 files changed, 58 insertions, 23 deletions
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index bc90364a96c7..51499d68b161 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c | |||
@@ -277,7 +277,7 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size, | |||
277 | * We don't need to sync the DMA buffer since | 277 | * We don't need to sync the DMA buffer since |
278 | * it was allocated via the coherent allocators. | 278 | * it was allocated via the coherent allocators. |
279 | */ | 279 | */ |
280 | dma_cache_maint(ptr, size, dir); | 280 | __dma_single_cpu_to_dev(ptr, size, dir); |
281 | } | 281 | } |
282 | 282 | ||
283 | return dma_addr; | 283 | return dma_addr; |
@@ -315,6 +315,8 @@ static inline void unmap_single(struct device *dev, dma_addr_t dma_addr, | |||
315 | __cpuc_flush_kernel_dcache_area(ptr, size); | 315 | __cpuc_flush_kernel_dcache_area(ptr, size); |
316 | } | 316 | } |
317 | free_safe_buffer(dev->archdata.dmabounce, buf); | 317 | free_safe_buffer(dev->archdata.dmabounce, buf); |
318 | } else { | ||
319 | __dma_single_dev_to_cpu(dma_to_virt(dev, dma_addr), size, dir); | ||
318 | } | 320 | } |
319 | } | 321 | } |
320 | 322 | ||
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index a96300bf83fd..e850f5c1607b 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h | |||
@@ -57,20 +57,49 @@ static inline dma_addr_t virt_to_dma(struct device *dev, void *addr) | |||
57 | #endif | 57 | #endif |
58 | 58 | ||
59 | /* | 59 | /* |
60 | * DMA-consistent mapping functions. These allocate/free a region of | 60 | * Private support functions: these are not part of the API and are |
61 | * uncached, unwrite-buffered mapped memory space for use with DMA | 61 | * liable to change. Drivers must not use these. |
62 | * devices. This is the "generic" version. The PCI specific version | ||
63 | * is in pci.h | ||
64 | * | ||
65 | * Note: Drivers should NOT use this function directly, as it will break | ||
66 | * platforms with CONFIG_DMABOUNCE. | ||
67 | * Use the driver DMA support - see dma-mapping.h (dma_sync_*) | ||
68 | */ | 62 | */ |
69 | extern void dma_cache_maint(const void *kaddr, size_t size, int rw); | 63 | extern void dma_cache_maint(const void *kaddr, size_t size, int rw); |
70 | extern void dma_cache_maint_page(struct page *page, unsigned long offset, | 64 | extern void dma_cache_maint_page(struct page *page, unsigned long offset, |
71 | size_t size, int rw); | 65 | size_t size, int rw); |
72 | 66 | ||
73 | /* | 67 | /* |
68 | * The DMA API is built upon the notion of "buffer ownership". A buffer | ||
69 | * is either exclusively owned by the CPU (and therefore may be accessed | ||
70 | * by it) or exclusively owned by the DMA device. These helper functions | ||
71 | * represent the transitions between these two ownership states. | ||
72 | * | ||
73 | * As above, these are private support functions and not part of the API. | ||
74 | * Drivers must not use these. | ||
75 | */ | ||
76 | static inline void __dma_single_cpu_to_dev(const void *kaddr, size_t size, | ||
77 | enum dma_data_direction dir) | ||
78 | { | ||
79 | if (!arch_is_coherent()) | ||
80 | dma_cache_maint(kaddr, size, dir); | ||
81 | } | ||
82 | |||
83 | static inline void __dma_single_dev_to_cpu(const void *kaddr, size_t size, | ||
84 | enum dma_data_direction dir) | ||
85 | { | ||
86 | /* nothing to do */ | ||
87 | } | ||
88 | |||
89 | static inline void __dma_page_cpu_to_dev(struct page *page, unsigned long off, | ||
90 | size_t size, enum dma_data_direction dir) | ||
91 | { | ||
92 | if (!arch_is_coherent()) | ||
93 | dma_cache_maint_page(page, off, size, dir); | ||
94 | } | ||
95 | |||
96 | static inline void __dma_page_dev_to_cpu(struct page *page, unsigned long off, | ||
97 | size_t size, enum dma_data_direction dir) | ||
98 | { | ||
99 | /* nothing to do */ | ||
100 | } | ||
101 | |||
102 | /* | ||
74 | * Return whether the given device DMA address mask can be supported | 103 | * Return whether the given device DMA address mask can be supported |
75 | * properly. For example, if your device can only drive the low 24-bits | 104 | * properly. For example, if your device can only drive the low 24-bits |
76 | * during bus mastering, then you would pass 0x00ffffff as the mask | 105 | * during bus mastering, then you would pass 0x00ffffff as the mask |
@@ -304,8 +333,7 @@ static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, | |||
304 | { | 333 | { |
305 | BUG_ON(!valid_dma_direction(dir)); | 334 | BUG_ON(!valid_dma_direction(dir)); |
306 | 335 | ||
307 | if (!arch_is_coherent()) | 336 | __dma_single_cpu_to_dev(cpu_addr, size, dir); |
308 | dma_cache_maint(cpu_addr, size, dir); | ||
309 | 337 | ||
310 | return virt_to_dma(dev, cpu_addr); | 338 | return virt_to_dma(dev, cpu_addr); |
311 | } | 339 | } |
@@ -329,8 +357,7 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, | |||
329 | { | 357 | { |
330 | BUG_ON(!valid_dma_direction(dir)); | 358 | BUG_ON(!valid_dma_direction(dir)); |
331 | 359 | ||
332 | if (!arch_is_coherent()) | 360 | __dma_page_cpu_to_dev(page, offset, size, dir); |
333 | dma_cache_maint_page(page, offset, size, dir); | ||
334 | 361 | ||
335 | return page_to_dma(dev, page) + offset; | 362 | return page_to_dma(dev, page) + offset; |
336 | } | 363 | } |
@@ -352,7 +379,7 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, | |||
352 | static inline void dma_unmap_single(struct device *dev, dma_addr_t handle, | 379 | static inline void dma_unmap_single(struct device *dev, dma_addr_t handle, |
353 | size_t size, enum dma_data_direction dir) | 380 | size_t size, enum dma_data_direction dir) |
354 | { | 381 | { |
355 | /* nothing to do */ | 382 | __dma_single_dev_to_cpu(dma_to_virt(dev, handle), size, dir); |
356 | } | 383 | } |
357 | 384 | ||
358 | /** | 385 | /** |
@@ -372,7 +399,8 @@ static inline void dma_unmap_single(struct device *dev, dma_addr_t handle, | |||
372 | static inline void dma_unmap_page(struct device *dev, dma_addr_t handle, | 399 | static inline void dma_unmap_page(struct device *dev, dma_addr_t handle, |
373 | size_t size, enum dma_data_direction dir) | 400 | size_t size, enum dma_data_direction dir) |
374 | { | 401 | { |
375 | /* nothing to do */ | 402 | __dma_page_dev_to_cpu(dma_to_page(dev, handle), handle & ~PAGE_MASK, |
403 | size, dir); | ||
376 | } | 404 | } |
377 | #endif /* CONFIG_DMABOUNCE */ | 405 | #endif /* CONFIG_DMABOUNCE */ |
378 | 406 | ||
@@ -400,7 +428,10 @@ static inline void dma_sync_single_range_for_cpu(struct device *dev, | |||
400 | { | 428 | { |
401 | BUG_ON(!valid_dma_direction(dir)); | 429 | BUG_ON(!valid_dma_direction(dir)); |
402 | 430 | ||
403 | dmabounce_sync_for_cpu(dev, handle, offset, size, dir); | 431 | if (!dmabounce_sync_for_cpu(dev, handle, offset, size, dir)) |
432 | return; | ||
433 | |||
434 | __dma_single_dev_to_cpu(dma_to_virt(dev, handle) + offset, size, dir); | ||
404 | } | 435 | } |
405 | 436 | ||
406 | static inline void dma_sync_single_range_for_device(struct device *dev, | 437 | static inline void dma_sync_single_range_for_device(struct device *dev, |
@@ -412,8 +443,7 @@ static inline void dma_sync_single_range_for_device(struct device *dev, | |||
412 | if (!dmabounce_sync_for_device(dev, handle, offset, size, dir)) | 443 | if (!dmabounce_sync_for_device(dev, handle, offset, size, dir)) |
413 | return; | 444 | return; |
414 | 445 | ||
415 | if (!arch_is_coherent()) | 446 | __dma_single_cpu_to_dev(dma_to_virt(dev, handle) + offset, size, dir); |
416 | dma_cache_maint(dma_to_virt(dev, handle) + offset, size, dir); | ||
417 | } | 447 | } |
418 | 448 | ||
419 | static inline void dma_sync_single_for_cpu(struct device *dev, | 449 | static inline void dma_sync_single_for_cpu(struct device *dev, |
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 26325cb5d368..a316c9459526 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
@@ -573,8 +573,12 @@ void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | |||
573 | int i; | 573 | int i; |
574 | 574 | ||
575 | for_each_sg(sg, s, nents, i) { | 575 | for_each_sg(sg, s, nents, i) { |
576 | dmabounce_sync_for_cpu(dev, sg_dma_address(s), 0, | 576 | if (!dmabounce_sync_for_cpu(dev, sg_dma_address(s), 0, |
577 | sg_dma_len(s), dir); | 577 | sg_dma_len(s), dir)) |
578 | continue; | ||
579 | |||
580 | __dma_page_dev_to_cpu(sg_page(s), s->offset, | ||
581 | s->length, dir); | ||
578 | } | 582 | } |
579 | } | 583 | } |
580 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | 584 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); |
@@ -597,9 +601,8 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
597 | sg_dma_len(s), dir)) | 601 | sg_dma_len(s), dir)) |
598 | continue; | 602 | continue; |
599 | 603 | ||
600 | if (!arch_is_coherent()) | 604 | __dma_page_cpu_to_dev(sg_page(s), s->offset, |
601 | dma_cache_maint_page(sg_page(s), s->offset, | 605 | s->length, dir); |
602 | s->length, dir); | ||
603 | } | 606 | } |
604 | } | 607 | } |
605 | EXPORT_SYMBOL(dma_sync_sg_for_device); | 608 | EXPORT_SYMBOL(dma_sync_sg_for_device); |