Skip to content

Commit 6137e41

Browse files
committed
xtensa: support DMA buffers in high memory
If a DMA buffer is allocated in high memory and kernel mapping is required use dma_common_contiguous_remap to map buffer to the vmalloc region and dma_common_free_remap to unmap it. Signed-off-by: Max Filippov <[email protected]>
1 parent 6ac5a11 commit 6137e41

File tree

1 file changed

+30
-10
lines changed

1 file changed

+30
-10
lines changed

arch/xtensa/kernel/pci-dma.c

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include <linux/dma-contiguous.h>
19+
#include <linux/dma-direct.h>
1920
#include <linux/gfp.h>
2021
#include <linux/highmem.h>
2122
#include <linux/mm.h>
@@ -123,7 +124,7 @@ static void *xtensa_dma_alloc(struct device *dev, size_t size,
123124
unsigned long attrs)
124125
{
125126
unsigned long ret;
126-
unsigned long uncached = 0;
127+
unsigned long uncached;
127128
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
128129
struct page *page = NULL;
129130

@@ -144,15 +145,27 @@ static void *xtensa_dma_alloc(struct device *dev, size_t size,
144145
if (!page)
145146
return NULL;
146147

147-
ret = (unsigned long)page_address(page);
148+
*handle = phys_to_dma(dev, page_to_phys(page));
148149

149-
/* We currently don't support coherent memory outside KSEG */
150+
#ifdef CONFIG_MMU
151+
if (PageHighMem(page)) {
152+
void *p;
150153

154+
p = dma_common_contiguous_remap(page, size, VM_MAP,
155+
pgprot_noncached(PAGE_KERNEL),
156+
__builtin_return_address(0));
157+
if (!p) {
158+
if (!dma_release_from_contiguous(dev, page, count))
159+
__free_pages(page, get_order(size));
160+
}
161+
return p;
162+
}
163+
#endif
164+
ret = (unsigned long)page_address(page);
151165
BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
152166
ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
153167

154168
uncached = ret + XCHAL_KSEG_BYPASS_VADDR - XCHAL_KSEG_CACHED_VADDR;
155-
*handle = virt_to_bus((void *)ret);
156169
__invalidate_dcache_range(ret, size);
157170

158171
return (void *)uncached;
@@ -161,13 +174,20 @@ static void *xtensa_dma_alloc(struct device *dev, size_t size,
161174
static void xtensa_dma_free(struct device *dev, size_t size, void *vaddr,
162175
dma_addr_t dma_handle, unsigned long attrs)
163176
{
164-
unsigned long addr = (unsigned long)vaddr +
165-
XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
166-
struct page *page = virt_to_page(addr);
167177
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
168-
169-
BUG_ON(addr < XCHAL_KSEG_CACHED_VADDR ||
170-
addr > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
178+
unsigned long addr = (unsigned long)vaddr;
179+
struct page *page;
180+
181+
if (addr >= XCHAL_KSEG_BYPASS_VADDR &&
182+
addr - XCHAL_KSEG_BYPASS_VADDR < XCHAL_KSEG_SIZE) {
183+
addr += XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
184+
page = virt_to_page(addr);
185+
} else {
186+
#ifdef CONFIG_MMU
187+
dma_common_free_remap(vaddr, size, VM_MAP);
188+
#endif
189+
page = pfn_to_page(PHYS_PFN(dma_to_phys(dev, dma_handle)));
190+
}
171191

172192
if (!dma_release_from_contiguous(dev, page, count))
173193
__free_pages(page, get_order(size));

0 commit comments

Comments
 (0)