Commit c8339002 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-voyager.bkbits.net/dma-declare-coherent-memory-2.6

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 2ac5f852 75e1802f
...@@ -444,4 +444,83 @@ dma_alloc_noncoherent(), starting at virtual address vaddr and ...@@ -444,4 +444,83 @@ dma_alloc_noncoherent(), starting at virtual address vaddr and
continuing on for size. Again, you *must* observe the cache line continuing on for size. Again, you *must* observe the cache line
boundaries when doing this. boundaries when doing this.
int
dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
dma_addr_t device_addr, size_t size, int
flags)
Declare region of memory to be handed out by dma_alloc_coherent when
it's asked for coherent memory for this device.
bus_addr is the physical address to which the memory is currently
assigned in the bus responding region (this will be used by the
platform to perform the mapping)
device_addr is the physical address the device needs to be programmed
with actually to address this memory (this will be handed out as the
dma_addr_t in dma_alloc_coherent())
size is the size of the area (must be multiples of PAGE_SIZE).
flags can be or'd together and are
DMA_MEMORY_MAP - request that the memory returned from
dma_alloc_coherent() be directly writeable.
DMA_MEMORY_IO - request that the memory returned from
dma_alloc_coherent() be addressable using read/write/memcpy_toio etc.
One or both of these flags must be present
DMA_MEMORY_INCLUDES_CHILDREN - make the declared memory be allocated by
dma_alloc_coherent of any child devices of this one (for memory residing
on a bridge).
DMA_MEMORY_EXCLUSIVE - only allocate memory from the declared regions.
Do not allow dma_alloc_coherent() to fall back to system memory when
it's out of memory in the declared region.
The return value will be either DMA_MEMORY_MAP or DMA_MEMORY_IO and
must correspond to a passed in flag (i.e. no returning DMA_MEMORY_IO
if only DMA_MEMORY_MAP were passed in) for success or zero for
failure.
Note, for DMA_MEMORY_IO returns, all subsequent memory returned by
dma_alloc_coherent() may no longer be accessed directly, but instead
must be accessed using the correct bus functions. If your driver
isn't prepared to handle this contingency, it should not specify
DMA_MEMORY_IO in the input flags.
As a simplification for the platforms, only *one* such region of
memory may be declared per device.
For reasons of efficiency, most platforms choose to track the declared
region only at the granularity of a page. For smaller allocations,
you should use the dma_pool() API.
void
dma_release_declared_memory(struct device *dev)
Remove the memory region previously declared from the system. This
API performs *no* in-use checking for this region and will return
unconditionally having removed all the required structures. It is the
drivers job to ensure that no parts of this memory region are
currently in use.
void *
dma_mark_declared_memory_occupied(struct device *dev,
dma_addr_t device_addr, size_t size)
This is used to occupy specific regions of the declared space
(dma_alloc_coherent() will hand out the first free region it finds).
device_addr is the *device* address of the region requested
size is the size (and should be a page sized multiple).
The return value will be either a pointer to the processor virtual
address of the memory, or an error (via PTR_ERR()) if any part of the
region is occupied.
...@@ -13,17 +13,40 @@ ...@@ -13,17 +13,40 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/io.h> #include <asm/io.h>
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, void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, int gfp) dma_addr_t *dma_handle, int gfp)
{ {
void *ret; void *ret;
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
int order = get_order(size);
/* ignore region specifiers */ /* ignore region specifiers */
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); 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)) if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
gfp |= GFP_DMA; gfp |= GFP_DMA;
ret = (void *)__get_free_pages(gfp, get_order(size)); ret = (void *)__get_free_pages(gfp, order);
if (ret != NULL) { if (ret != NULL) {
memset(ret, 0, size); memset(ret, 0, size);
...@@ -35,5 +58,89 @@ void *dma_alloc_coherent(struct device *dev, size_t 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 dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle) void *vaddr, dma_addr_t dma_handle)
{ {
free_pages((unsigned long)vaddr, get_order(size)); struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
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 + (device_addr & ~PAGE_MASK) + 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);
...@@ -216,7 +216,21 @@ NCR_Q720_probe(struct device *dev) ...@@ -216,7 +216,21 @@ NCR_Q720_probe(struct device *dev)
goto out_free; goto out_free;
} }
mem_base = (__u32)ioremap(base_addr, mem_size); if (dma_declare_coherent_memory(dev, base_addr, base_addr,
mem_size, DMA_MEMORY_MAP)
!= DMA_MEMORY_MAP) {
printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n");
goto out_release_region;
}
/* The first 1k of the memory buffer is a memory map of the registers
*/
mem_base = (__u32)dma_mark_declared_memory_occupied(dev, base_addr,
1024);
if (IS_ERR((void *)mem_base)) {
printk("NCR_Q720 failed to reserve memory mapped region\n");
goto out_release;
}
/* now also enable accesses in asr 2 */ /* now also enable accesses in asr 2 */
asr2 = inb(io_base + 0x0a); asr2 = inb(io_base + 0x0a);
...@@ -296,7 +310,8 @@ NCR_Q720_probe(struct device *dev) ...@@ -296,7 +310,8 @@ NCR_Q720_probe(struct device *dev)
return 0; return 0;
out_release: out_release:
iounmap((void *)mem_base); dma_release_declared_memory(dev);
out_release_region:
release_mem_region(base_addr, mem_size); release_mem_region(base_addr, mem_size);
out_free: out_free:
kfree(p); kfree(p);
...@@ -321,7 +336,7 @@ NCR_Q720_remove(struct device *dev) ...@@ -321,7 +336,7 @@ NCR_Q720_remove(struct device *dev)
if(p->hosts[i]) if(p->hosts[i])
NCR_Q720_remove_one(p->hosts[i]); NCR_Q720_remove_one(p->hosts[i]);
iounmap((void *)p->mem_base); dma_release_declared_memory(dev);
release_mem_region(p->phys_mem_base, p->mem_size); release_mem_region(p->phys_mem_base, p->mem_size);
free_irq(p->irq, p); free_irq(p->irq, p);
kfree(p); kfree(p);
......
...@@ -163,4 +163,16 @@ dma_cache_sync(void *vaddr, size_t size, ...@@ -163,4 +163,16 @@ dma_cache_sync(void *vaddr, size_t size,
flush_write_buffers(); 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 #endif
...@@ -98,6 +98,9 @@ extern int bitmap_scnprintf(char *buf, unsigned int len, ...@@ -98,6 +98,9 @@ extern int bitmap_scnprintf(char *buf, unsigned int len,
const unsigned long *src, int nbits); const unsigned long *src, int nbits);
extern int bitmap_parse(const char __user *ubuf, unsigned int ulen, extern int bitmap_parse(const char __user *ubuf, unsigned int ulen,
unsigned long *dst, int nbits); 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) \ #define BITMAP_LAST_WORD_MASK(nbits) \
( \ ( \
......
...@@ -284,6 +284,9 @@ struct device { ...@@ -284,6 +284,9 @@ struct device {
struct list_head dma_pools; /* dma pools (if dma'ble) */ 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); void (*release)(struct device * dev);
}; };
......
#ifndef _ASM_LINUX_DMA_MAPPING_H #ifndef _ASM_LINUX_DMA_MAPPING_H
#define _ASM_LINUX_DMA_MAPPING_H #define _ASM_LINUX_DMA_MAPPING_H
#include <linux/err.h>
/* These definitions mirror those in pci.h, so they can be used /* These definitions mirror those in pci.h, so they can be used
* interchangeably with their PCI_ counterparts */ * interchangeably with their PCI_ counterparts */
enum dma_data_direction { enum dma_data_direction {
...@@ -21,6 +23,33 @@ enum dma_data_direction { ...@@ -21,6 +23,33 @@ enum dma_data_direction {
extern u64 dma_get_required_mask(struct device *dev); extern u64 dma_get_required_mask(struct device *dev);
/* 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)
{
}
static inline void *
dma_mark_declared_memory_occupied(struct device *dev,
dma_addr_t device_addr, size_t size)
{
return ERR_PTR(-EBUSY);
}
#endif
#endif #endif
...@@ -408,3 +408,85 @@ int bitmap_parse(const char __user *ubuf, unsigned int ubuflen, ...@@ -408,3 +408,85 @@ int bitmap_parse(const char __user *ubuf, unsigned int ubuflen,
return 0; return 0;
} }
EXPORT_SYMBOL(bitmap_parse); 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<<order)
*
* This is used to allocate a memory region from a bitmap. The idea is
* that the region has to be 1<<order sized and 1<<order aligned (this
* makes the search algorithm much faster).
*
* The region is marked as set bits in the bitmap if a free one is
* found.
*
* Returns either beginning of region or negative error
*/
int bitmap_find_free_region(unsigned long *bitmap, int bits, int order)
{
unsigned long mask;
int pages = 1 << order;
int i;
if(pages > 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 = i/BITS_PER_LONG;
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<<order)
*
* This is the complement to __bitmap_find_free_region and releases
* the found region (by clearing it in the bitmap).
*/
void bitmap_release_region(unsigned long *bitmap, int pos, int order)
{
int pages = 1 << order;
unsigned long mask = (1ul << (pages - 1));
int index = pos/BITS_PER_LONG;
int offset = pos - (index * BITS_PER_LONG);
mask += mask - 1;
bitmap[index] &= ~(mask << offset);
}
EXPORT_SYMBOL(bitmap_release_region);
int bitmap_allocate_region(unsigned long *bitmap, int pos, int order)
{
int pages = 1 << order;
unsigned long mask = (1ul << (pages - 1));
int index = pos/BITS_PER_LONG;
int offset = pos - (index * BITS_PER_LONG);
/* We don't do regions of pages > BITS_PER_LONG. The
* algorithm would be a simple look for multiple zeros in the
* array, but there's no driver today that needs this. If you
* trip this BUG(), you get to code it... */
BUG_ON(pages > BITS_PER_LONG);
mask += mask - 1;
if (bitmap[index] & (mask << offset))
return -EBUSY;
bitmap[index] |= (mask << offset);
return 0;
}
EXPORT_SYMBOL(bitmap_allocate_region);
...@@ -179,11 +179,26 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) ...@@ -179,11 +179,26 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)
return err; return err;
} }
#define IOREMAP_MAX_ORDER (7 + PAGE_SHIFT) /* 128 pages */
struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
unsigned long start, unsigned long end) unsigned long start, unsigned long end)
{ {
struct vm_struct **p, *tmp, *area; struct vm_struct **p, *tmp, *area;
unsigned long addr = start; unsigned long align = 1;
unsigned long addr;
if (flags & VM_IOREMAP) {
int bit = fls(size);
if (bit > IOREMAP_MAX_ORDER)
bit = IOREMAP_MAX_ORDER;
else if (bit < PAGE_SHIFT)
bit = PAGE_SHIFT;
align = 1ul << bit;
}
addr = ALIGN(start, align);
area = kmalloc(sizeof(*area), GFP_KERNEL); area = kmalloc(sizeof(*area), GFP_KERNEL);
if (unlikely(!area)) if (unlikely(!area))
...@@ -200,13 +215,17 @@ struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, ...@@ -200,13 +215,17 @@ struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
write_lock(&vmlist_lock); write_lock(&vmlist_lock);
for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) { for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {
if ((unsigned long)tmp->addr < addr) if ((unsigned long)tmp->addr < addr) {
if((unsigned long)tmp->addr + tmp->size >= addr)
addr = ALIGN(tmp->size +
(unsigned long)tmp->addr, align);
continue; continue;
}
if ((size + addr) < addr) if ((size + addr) < addr)
goto out; goto out;
if (size + addr <= (unsigned long)tmp->addr) if (size + addr <= (unsigned long)tmp->addr)
goto found; goto found;
addr = tmp->size + (unsigned long)tmp->addr; addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);
if (addr > end - size) if (addr > end - size)
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment