0001
0002
0003
0004 #include <linux/cache.h>
0005 #include <linux/dma-map-ops.h>
0006 #include <linux/genalloc.h>
0007 #include <linux/highmem.h>
0008 #include <linux/io.h>
0009 #include <linux/mm.h>
0010 #include <linux/scatterlist.h>
0011 #include <linux/types.h>
0012 #include <asm/cache.h>
0013
0014 static inline void cache_op(phys_addr_t paddr, size_t size,
0015 void (*fn)(unsigned long start, unsigned long end))
0016 {
0017 struct page *page = phys_to_page(paddr);
0018 void *start = __va(page_to_phys(page));
0019 unsigned long offset = offset_in_page(paddr);
0020 size_t left = size;
0021
0022 do {
0023 size_t len = left;
0024
0025 if (offset + len > PAGE_SIZE)
0026 len = PAGE_SIZE - offset;
0027
0028 if (PageHighMem(page)) {
0029 start = kmap_atomic(page);
0030
0031 fn((unsigned long)start + offset,
0032 (unsigned long)start + offset + len);
0033
0034 kunmap_atomic(start);
0035 } else {
0036 fn((unsigned long)start + offset,
0037 (unsigned long)start + offset + len);
0038 }
0039 offset = 0;
0040
0041 page++;
0042 start += PAGE_SIZE;
0043 left -= len;
0044 } while (left);
0045 }
0046
0047 static void dma_wbinv_set_zero_range(unsigned long start, unsigned long end)
0048 {
0049 memset((void *)start, 0, end - start);
0050 dma_wbinv_range(start, end);
0051 }
0052
0053 void arch_dma_prep_coherent(struct page *page, size_t size)
0054 {
0055 cache_op(page_to_phys(page), size, dma_wbinv_set_zero_range);
0056 }
0057
0058 void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
0059 enum dma_data_direction dir)
0060 {
0061 switch (dir) {
0062 case DMA_TO_DEVICE:
0063 cache_op(paddr, size, dma_wb_range);
0064 break;
0065 case DMA_FROM_DEVICE:
0066 case DMA_BIDIRECTIONAL:
0067 cache_op(paddr, size, dma_wbinv_range);
0068 break;
0069 default:
0070 BUG();
0071 }
0072 }
0073
0074 void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
0075 enum dma_data_direction dir)
0076 {
0077 switch (dir) {
0078 case DMA_TO_DEVICE:
0079 return;
0080 case DMA_FROM_DEVICE:
0081 case DMA_BIDIRECTIONAL:
0082 cache_op(paddr, size, dma_inv_range);
0083 break;
0084 default:
0085 BUG();
0086 }
0087 }