diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2014-12-03 10:07:28 -0500 |
---|---|---|
committer | Michal Simek <michal.simek@xilinx.com> | 2014-12-17 06:59:59 -0500 |
commit | 3a8e3265179b7e6394d7aab4d6df5651b49e7243 (patch) | |
tree | 82194a886cb05fe27ba3ddf0dca4ec05c3977e4a /arch/microblaze/kernel | |
parent | b2776bf7149bddd1f4161f14f79520f17fc1d71d (diff) |
microblaze: Fix mmap for cache coherent memory
When running in non-cache coherent configuration the memory that was
allocated with dma_alloc_coherent() has a custom mapping and so there is no
1-to-1 relationship between the kernel virtual address and the PFN. This
means that virt_to_pfn() will not work correctly for those addresses and the
default mmap implementation in the form of dma_common_mmap() will map some
random, but not the requested, memory area.
Fix this by providing a custom mmap implementation that looks up the PFN
from the page table rather than using virt_to_pfn.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Diffstat (limited to 'arch/microblaze/kernel')
-rw-r--r-- | arch/microblaze/kernel/dma.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c index 4633c36c1b32..ed7ba8a11822 100644 --- a/arch/microblaze/kernel/dma.c +++ b/arch/microblaze/kernel/dma.c | |||
@@ -154,9 +154,36 @@ dma_direct_sync_sg_for_device(struct device *dev, | |||
154 | __dma_sync(sg->dma_address, sg->length, direction); | 154 | __dma_sync(sg->dma_address, sg->length, direction); |
155 | } | 155 | } |
156 | 156 | ||
157 | int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma, | ||
158 | void *cpu_addr, dma_addr_t handle, size_t size, | ||
159 | struct dma_attrs *attrs) | ||
160 | { | ||
161 | #ifdef CONFIG_MMU | ||
162 | unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
163 | unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; | ||
164 | unsigned long off = vma->vm_pgoff; | ||
165 | unsigned long pfn; | ||
166 | |||
167 | if (off >= count || user_count > (count - off)) | ||
168 | return -ENXIO; | ||
169 | |||
170 | #ifdef NOT_COHERENT_CACHE | ||
171 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
172 | pfn = consistent_virt_to_pfn(cpu_addr); | ||
173 | #else | ||
174 | pfn = virt_to_pfn(cpu_addr); | ||
175 | #endif | ||
176 | return remap_pfn_range(vma, vma->vm_start, pfn + off, | ||
177 | vma->vm_end - vma->vm_start, vma->vm_page_prot); | ||
178 | #else | ||
179 | return -ENXIO; | ||
180 | #endif | ||
181 | } | ||
182 | |||
157 | struct dma_map_ops dma_direct_ops = { | 183 | struct dma_map_ops dma_direct_ops = { |
158 | .alloc = dma_direct_alloc_coherent, | 184 | .alloc = dma_direct_alloc_coherent, |
159 | .free = dma_direct_free_coherent, | 185 | .free = dma_direct_free_coherent, |
186 | .mmap = dma_direct_mmap_coherent, | ||
160 | .map_sg = dma_direct_map_sg, | 187 | .map_sg = dma_direct_map_sg, |
161 | .dma_supported = dma_direct_dma_supported, | 188 | .dma_supported = dma_direct_dma_supported, |
162 | .map_page = dma_direct_map_page, | 189 | .map_page = dma_direct_map_page, |