-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
generic: per-device coherent dma allocator
Currently x86_32, sh and cris-v32 provide per-device coherent dma memory allocator. However their implementation is nearly identical. Refactor out common code to be reused by them. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
- Loading branch information
Dmitry Baryshkov
authored and
Ingo Molnar
committed
Jun 30, 2008
1 parent
543cf4c
commit ee7e551
Showing
4 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#ifndef DMA_COHERENT_H | ||
#define DMA_COHERENT_H | ||
|
||
#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT | ||
/* | ||
* These two functions are only for dma allocator. | ||
* Don't use them in device drivers. | ||
*/ | ||
int dma_alloc_from_coherent(struct device *dev, ssize_t size, | ||
dma_addr_t *dma_handle, void **ret); | ||
int dma_release_from_coherent(struct device *dev, int order, void *vaddr); | ||
|
||
/* | ||
* Standard interface | ||
*/ | ||
#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY | ||
extern int | ||
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, | ||
dma_addr_t device_addr, size_t size, int flags); | ||
|
||
extern void | ||
dma_release_declared_memory(struct device *dev); | ||
|
||
extern void * | ||
dma_mark_declared_memory_occupied(struct device *dev, | ||
dma_addr_t device_addr, size_t size); | ||
#else | ||
#define dma_alloc_from_coherent(dev, size, handle, ret) (0) | ||
#define dma_release_from_coherent(dev, order, vaddr) (0) | ||
#endif | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Coherent per-device memory handling. | ||
* Borrowed from i386 | ||
*/ | ||
#include <linux/kernel.h> | ||
#include <linux/dma-mapping.h> | ||
|
||
struct dma_coherent_mem { | ||
void *virt_base; | ||
u32 device_base; | ||
int size; | ||
int flags; | ||
unsigned long *bitmap; | ||
}; | ||
|
||
int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, | ||
dma_addr_t device_addr, size_t size, int flags) | ||
{ | ||
void __iomem *mem_base = NULL; | ||
int pages = size >> PAGE_SHIFT; | ||
int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); | ||
|
||
if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) | ||
goto out; | ||
if (!size) | ||
goto out; | ||
if (dev->dma_mem) | ||
goto out; | ||
|
||
/* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ | ||
|
||
mem_base = ioremap(bus_addr, size); | ||
if (!mem_base) | ||
goto out; | ||
|
||
dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); | ||
if (!dev->dma_mem) | ||
goto out; | ||
dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); | ||
if (!dev->dma_mem->bitmap) | ||
goto free1_out; | ||
|
||
dev->dma_mem->virt_base = mem_base; | ||
dev->dma_mem->device_base = device_addr; | ||
dev->dma_mem->size = pages; | ||
dev->dma_mem->flags = flags; | ||
|
||
if (flags & DMA_MEMORY_MAP) | ||
return DMA_MEMORY_MAP; | ||
|
||
return DMA_MEMORY_IO; | ||
|
||
free1_out: | ||
kfree(dev->dma_mem); | ||
out: | ||
if (mem_base) | ||
iounmap(mem_base); | ||
return 0; | ||
} | ||
EXPORT_SYMBOL(dma_declare_coherent_memory); | ||
|
||
void dma_release_declared_memory(struct device *dev) | ||
{ | ||
struct dma_coherent_mem *mem = dev->dma_mem; | ||
|
||
if (!mem) | ||
return; | ||
dev->dma_mem = NULL; | ||
iounmap(mem->virt_base); | ||
kfree(mem->bitmap); | ||
kfree(mem); | ||
} | ||
EXPORT_SYMBOL(dma_release_declared_memory); | ||
|
||
void *dma_mark_declared_memory_occupied(struct device *dev, | ||
dma_addr_t device_addr, size_t size) | ||
{ | ||
struct dma_coherent_mem *mem = dev->dma_mem; | ||
int pos, err; | ||
int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1); | ||
|
||
pages >>= PAGE_SHIFT; | ||
|
||
if (!mem) | ||
return ERR_PTR(-EINVAL); | ||
|
||
pos = (device_addr - mem->device_base) >> PAGE_SHIFT; | ||
err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); | ||
if (err != 0) | ||
return ERR_PTR(err); | ||
return mem->virt_base + (pos << PAGE_SHIFT); | ||
} | ||
EXPORT_SYMBOL(dma_mark_declared_memory_occupied); | ||
|
||
int dma_alloc_from_coherent(struct device *dev, ssize_t size, | ||
dma_addr_t *dma_handle, void **ret) | ||
{ | ||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; | ||
int order = get_order(size); | ||
|
||
if (mem) { | ||
int page = bitmap_find_free_region(mem->bitmap, mem->size, | ||
order); | ||
if (page >= 0) { | ||
*dma_handle = mem->device_base + (page << PAGE_SHIFT); | ||
*ret = mem->virt_base + (page << PAGE_SHIFT); | ||
memset(*ret, 0, size); | ||
} | ||
if (mem->flags & DMA_MEMORY_EXCLUSIVE) | ||
*ret = NULL; | ||
} | ||
return (mem != NULL); | ||
} | ||
|
||
int dma_release_from_coherent(struct device *dev, int order, void *vaddr) | ||
{ | ||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; | ||
|
||
if (mem && vaddr >= mem->virt_base && vaddr < | ||
(mem->virt_base + (mem->size << PAGE_SHIFT))) { | ||
int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; | ||
|
||
bitmap_release_region(mem->bitmap, page, order); | ||
return 1; | ||
} | ||
return 0; | ||
} |