From 6b1a96a1f33a04958f8cad140868f28143242c9a Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 30 Jun 2004 11:02:11 -0500 Subject: Add dma_declare_coherent_memory() API This adds the description and a null prototype. Signed-off-by: James Bottomley --- include/linux/dma-mapping.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index c1620bb06051..024a29c1f37b 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -42,6 +42,32 @@ static inline u64 dma_get_required_mask(struct device *dev) } #endif +/* flags for the coherent memory api */ +#define DMA_MEMORY_MAP 0x01 +#define DMA_MEMORY_IO 0x02 +#define DMA_MEMORY_INCLUDES_CHILDREN 0x04 +#define DMA_MEMORY_EXCLUSIVE 0x08 + +#ifndef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY +static inline int +dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + return 0; +} + +static inline void +dma_release_declared_memory(struct device *dev) +{ +} + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + return ERR_PTR(-EBUSY); +} +#endif + #endif -- cgit v1.2.3 From 29ad27a5c803f50b6d9129d54871f4887a71f8f9 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 30 Jun 2004 11:08:15 -0500 Subject: Add memory region bitmap implementations These APIs deal with bitmaps representing contiguous memory regions. The idea is to set, free and find a contiguous area. For ease of implementation (as well as to conform to the standard requirements), the bitmaps always return n aligned n length regions. The implementation is also limited to BITS_PER_LONG contiguous regions. Signed-off-by: James Bottomley --- include/linux/bitmap.h | 3 ++ lib/bitmap.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) (limited to 'include/linux') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 9dadd08e2b53..77401c15510b 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -98,6 +98,9 @@ extern int bitmap_scnprintf(char *buf, unsigned int len, const unsigned long *src, int nbits); extern int bitmap_parse(const char __user *ubuf, unsigned int ulen, unsigned long *dst, int nbits); +extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order); +extern void bitmap_release_region(unsigned long *bitmap, int pos, int order); +extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order); #define BITMAP_LAST_WORD_MASK(nbits) \ ( \ diff --git a/lib/bitmap.c b/lib/bitmap.c index 7eb16be309b5..96343aaf23e5 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -408,3 +408,79 @@ int bitmap_parse(const char __user *ubuf, unsigned int ubuflen, return 0; } EXPORT_SYMBOL(bitmap_parse); + +/** + * bitmap_find_free_region - find a contiguous aligned mem region + * @bitmap: an array of unsigned longs corresponding to the bitmap + * @bits: number of bits in the bitmap + * @order: region size to find (size is actually 1< BITS_PER_LONG) + return -EINVAL; + + /* make a mask of the order */ + mask = (1ul << (pages - 1)); + mask += mask - 1; + + /* run up the bitmap pages bits at a time */ + for (i = 0; i < bits; i += pages) { + int index = BITS_TO_LONGS(i); + int offset = i - (index * BITS_PER_LONG); + if((bitmap[index] & (mask << offset)) == 0) { + /* set region in bimap */ + bitmap[index] |= (mask << offset); + return i; + } + } + return -ENOMEM; +} +EXPORT_SYMBOL(bitmap_find_free_region); + +/** + * bitmap_release_region - release allocated bitmap region + * @bitmap: a pointer to the bitmap + * @pos: the beginning of the region + * @order: the order of the bits to release (number is 1< Date: Wed, 30 Jun 2004 11:38:34 -0500 Subject: Add x86 implementation of dma_declare_coherent_memory This actually implements the API (all except for DMA_MEMORY_INCLUDES_CHILDREN). Signed-off-by: James Bottomley --- arch/i386/kernel/pci-dma.c | 111 ++++++++++++++++++++++++++++++++++++++++- include/asm-i386/dma-mapping.h | 12 +++++ include/linux/device.h | 3 ++ 3 files changed, 124 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c index 10dc6b79dcbb..f8255684921d 100644 --- a/arch/i386/kernel/pci-dma.c +++ b/arch/i386/kernel/pci-dma.c @@ -13,17 +13,40 @@ #include #include +struct dma_coherent_mem { + void *virt_base; + u32 device_base; + int size; + int flags; + unsigned long *bitmap; +}; + void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp) { void *ret; + struct dma_coherent_mem *mem = dev->dma_mem; + int order = get_order(size); /* ignore region specifiers */ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + 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); + return ret; + } + if (mem->flags & DMA_MEMORY_EXCLUSIVE) + return NULL; + } + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); + ret = (void *)__get_free_pages(gfp, order); if (ret != NULL) { memset(ret, 0, size); @@ -35,5 +58,89 @@ void *dma_alloc_coherent(struct device *dev, size_t size, void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle) { - free_pages((unsigned long)vaddr, get_order(size)); + struct dma_coherent_mem *mem = dev->dma_mem; + int order = get_order(size); + + 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); + } else + free_pages((unsigned long)vaddr, order); +} + +int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void *mem_base; + int pages = size >> PAGE_SHIFT; + int bitmap_size = (pages + 31)/32; + + 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 = kmalloc(GFP_KERNEL, sizeof(struct dma_coherent_mem)); + if (!dev->dma_mem) + goto out; + memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); + dev->dma_mem->bitmap = kmalloc(GFP_KERNEL, bitmap_size); + if (!dev->dma_mem->bitmap) + goto free1_out; + memset(dev->dma_mem->bitmap, 0, bitmap_size); + + 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->bitmap); + out: + 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; + 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 pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + int pos, err; + + 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); diff --git a/include/asm-i386/dma-mapping.h b/include/asm-i386/dma-mapping.h index 79bfab5cb8f9..eced63913860 100644 --- a/include/asm-i386/dma-mapping.h +++ b/include/asm-i386/dma-mapping.h @@ -163,4 +163,16 @@ dma_cache_sync(void *vaddr, size_t size, flush_write_buffers(); } +#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); + #endif diff --git a/include/linux/device.h b/include/linux/device.h index 3f6e14c099e2..7796ce318fc2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -283,6 +283,9 @@ struct device { struct list_head dma_pools; /* dma pools (if dma'ble) */ + struct dma_coherent_mem *dma_mem; /* internal for coherent mem + override */ + void (*release)(struct device * dev); }; -- cgit v1.2.3 From 31400932a26b2788439fd59d0eabfb0f2cb77468 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 7 Jul 2004 01:46:31 -0500 Subject: Fix incorrect prototype in the dma_declare_coherent_memory API dma_mark_declared_memory_occupied() wasn't declared static inline in the NULL (default) implementation leading to compile failures. --- include/linux/dma-mapping.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 024a29c1f37b..4ee5a1c03361 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -61,8 +61,9 @@ dma_release_declared_memory(struct device *dev) { } -void *dma_mark_declared_memory_occupied(struct device *dev, - dma_addr_t device_addr, size_t size) +static inline void * +dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) { return ERR_PTR(-EBUSY); } -- cgit v1.2.3 From 053b945d2f85db8fb22773e91ea624b511b1694a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 9 Jul 2004 07:16:25 -0500 Subject: [PATCH] Fix sparc compile error in dma-mapping.h William Lee Irwin III wrote: > > SPLIT include/linux/autoconf.h -> include/config/* > CHK include/linux/compile.h > UPD include/linux/compile.h > In file included from include/asm/sbus.h:10, > from arch/sparc64/kernel/auxio.c:15: It needs err.h. Signed-off-by: James Bottomley --- include/linux/dma-mapping.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 4ee5a1c03361..4d68f7cf9d3e 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -1,6 +1,8 @@ #ifndef _ASM_LINUX_DMA_MAPPING_H #define _ASM_LINUX_DMA_MAPPING_H +#include + /* These definitions mirror those in pci.h, so they can be used * interchangeably with their PCI_ counterparts */ enum dma_data_direction { -- cgit v1.2.3