Commit | Line | Data |
---|---|---|
5f97f7f9 HS |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/dma-mapping.h> | |
5a0e3ad6 | 10 | #include <linux/gfp.h> |
09cf6a29 | 11 | #include <linux/export.h> |
5f97f7f9 HS |
12 | |
13 | #include <asm/addrspace.h> | |
14 | #include <asm/cacheflush.h> | |
15 | ||
d3fa72e4 | 16 | void dma_cache_sync(struct device *dev, void *vaddr, size_t size, int direction) |
5f97f7f9 HS |
17 | { |
18 | /* | |
19 | * No need to sync an uncached area | |
20 | */ | |
21 | if (PXSEG(vaddr) == P2SEG) | |
22 | return; | |
23 | ||
24 | switch (direction) { | |
25 | case DMA_FROM_DEVICE: /* invalidate only */ | |
622a9edd | 26 | invalidate_dcache_region(vaddr, size); |
5f97f7f9 HS |
27 | break; |
28 | case DMA_TO_DEVICE: /* writeback only */ | |
622a9edd | 29 | clean_dcache_region(vaddr, size); |
5f97f7f9 HS |
30 | break; |
31 | case DMA_BIDIRECTIONAL: /* writeback and invalidate */ | |
622a9edd | 32 | flush_dcache_region(vaddr, size); |
5f97f7f9 HS |
33 | break; |
34 | default: | |
35 | BUG(); | |
36 | } | |
37 | } | |
38 | EXPORT_SYMBOL(dma_cache_sync); | |
39 | ||
40 | static struct page *__dma_alloc(struct device *dev, size_t size, | |
41 | dma_addr_t *handle, gfp_t gfp) | |
42 | { | |
43 | struct page *page, *free, *end; | |
44 | int order; | |
45 | ||
3611553e HS |
46 | /* Following is a work-around (a.k.a. hack) to prevent pages |
47 | * with __GFP_COMP being passed to split_page() which cannot | |
48 | * handle them. The real problem is that this flag probably | |
49 | * should be 0 on AVR32 as it is not supported on this | |
50 | * platform--see CONFIG_HUGETLB_PAGE. */ | |
51 | gfp &= ~(__GFP_COMP); | |
52 | ||
5f97f7f9 HS |
53 | size = PAGE_ALIGN(size); |
54 | order = get_order(size); | |
55 | ||
56 | page = alloc_pages(gfp, order); | |
57 | if (!page) | |
58 | return NULL; | |
59 | split_page(page, order); | |
60 | ||
61 | /* | |
62 | * When accessing physical memory with valid cache data, we | |
63 | * get a cache hit even if the virtual memory region is marked | |
64 | * as uncached. | |
65 | * | |
66 | * Since the memory is newly allocated, there is no point in | |
67 | * doing a writeback. If the previous owner cares, he should | |
68 | * have flushed the cache before releasing the memory. | |
69 | */ | |
70 | invalidate_dcache_region(phys_to_virt(page_to_phys(page)), size); | |
71 | ||
72 | *handle = page_to_bus(page); | |
73 | free = page + (size >> PAGE_SHIFT); | |
74 | end = page + (1 << order); | |
75 | ||
76 | /* | |
77 | * Free any unused pages | |
78 | */ | |
79 | while (free < end) { | |
80 | __free_page(free); | |
81 | free++; | |
82 | } | |
83 | ||
84 | return page; | |
85 | } | |
86 | ||
87 | static void __dma_free(struct device *dev, size_t size, | |
88 | struct page *page, dma_addr_t handle) | |
89 | { | |
90 | struct page *end = page + (PAGE_ALIGN(size) >> PAGE_SHIFT); | |
91 | ||
92 | while (page < end) | |
93 | __free_page(page++); | |
94 | } | |
95 | ||
96 | void *dma_alloc_coherent(struct device *dev, size_t size, | |
97 | dma_addr_t *handle, gfp_t gfp) | |
98 | { | |
99 | struct page *page; | |
100 | void *ret = NULL; | |
101 | ||
102 | page = __dma_alloc(dev, size, handle, gfp); | |
103 | if (page) | |
104 | ret = phys_to_uncached(page_to_phys(page)); | |
105 | ||
106 | return ret; | |
107 | } | |
108 | EXPORT_SYMBOL(dma_alloc_coherent); | |
109 | ||
110 | void dma_free_coherent(struct device *dev, size_t size, | |
111 | void *cpu_addr, dma_addr_t handle) | |
112 | { | |
113 | void *addr = phys_to_cached(uncached_to_phys(cpu_addr)); | |
114 | struct page *page; | |
115 | ||
116 | pr_debug("dma_free_coherent addr %p (phys %08lx) size %u\n", | |
117 | cpu_addr, (unsigned long)handle, (unsigned)size); | |
118 | BUG_ON(!virt_addr_valid(addr)); | |
119 | page = virt_to_page(addr); | |
120 | __dma_free(dev, size, page, handle); | |
121 | } | |
122 | EXPORT_SYMBOL(dma_free_coherent); | |
123 | ||
5f97f7f9 HS |
124 | void *dma_alloc_writecombine(struct device *dev, size_t size, |
125 | dma_addr_t *handle, gfp_t gfp) | |
126 | { | |
127 | struct page *page; | |
a492dbb9 | 128 | dma_addr_t phys; |
5f97f7f9 HS |
129 | |
130 | page = __dma_alloc(dev, size, handle, gfp); | |
a492dbb9 HS |
131 | if (!page) |
132 | return NULL; | |
133 | ||
134 | phys = page_to_phys(page); | |
135 | *handle = phys; | |
5f97f7f9 HS |
136 | |
137 | /* Now, map the page into P3 with write-combining turned on */ | |
a492dbb9 | 138 | return __ioremap(phys, size, _PAGE_BUFFER); |
5f97f7f9 HS |
139 | } |
140 | EXPORT_SYMBOL(dma_alloc_writecombine); | |
141 | ||
142 | void dma_free_writecombine(struct device *dev, size_t size, | |
143 | void *cpu_addr, dma_addr_t handle) | |
144 | { | |
145 | struct page *page; | |
146 | ||
147 | iounmap(cpu_addr); | |
148 | ||
a492dbb9 | 149 | page = phys_to_page(handle); |
5f97f7f9 HS |
150 | __dma_free(dev, size, page, handle); |
151 | } | |
152 | EXPORT_SYMBOL(dma_free_writecombine); |