2008-06-29 14:18:46 +04:00
/*
* 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 ;
2008-08-06 00:01:31 +04:00
size + = device_addr & ~ PAGE_MASK ;
2008-06-29 14:18:46 +04:00
if ( ! mem )
return ERR_PTR ( - EINVAL ) ;
pos = ( device_addr - mem - > device_base ) > > PAGE_SHIFT ;
2008-08-06 00:01:31 +04:00
err = bitmap_allocate_region ( mem - > bitmap , pos , get_order ( size ) ) ;
2008-06-29 14:18:46 +04:00
if ( err ! = 0 )
return ERR_PTR ( err ) ;
return mem - > virt_base + ( pos < < PAGE_SHIFT ) ;
}
EXPORT_SYMBOL ( dma_mark_declared_memory_occupied ) ;
2008-07-20 15:01:10 +04:00
/**
2008-07-30 14:46:50 +04:00
* dma_alloc_from_coherent ( ) - try to allocate memory from the per - device coherent area
2008-07-20 15:01:10 +04:00
*
* @ dev : device from which we allocate memory
* @ size : size of requested memory area
* @ dma_handle : This will be filled with the correct dma handle
* @ ret : This pointer will be filled with the virtual address
* to allocated area .
*
2008-07-30 14:46:50 +04:00
* This function should be only called from per - arch dma_alloc_coherent ( )
2008-07-20 15:01:10 +04:00
* to support allocation from per - device coherent memory pools .
*
* Returns 0 if dma_alloc_coherent should continue with allocating from
2008-07-30 14:46:50 +04:00
* generic memory areas , or ! 0 if dma_alloc_coherent should return @ ret .
2008-07-20 15:01:10 +04:00
*/
2008-06-29 14:18:46 +04:00
int dma_alloc_from_coherent ( struct device * dev , ssize_t size ,
dma_addr_t * dma_handle , void * * ret )
{
2009-01-07 01:43:09 +03:00
struct dma_coherent_mem * mem ;
2008-06-29 14:18:46 +04:00
int order = get_order ( size ) ;
2009-01-07 01:43:09 +03:00
int pageno ;
2008-06-29 14:18:46 +04:00
2009-01-07 01:43:09 +03:00
if ( ! dev )
return 0 ;
mem = dev - > dma_mem ;
if ( ! mem )
return 0 ;
2009-01-07 01:43:10 +03:00
if ( unlikely ( size > mem - > size ) )
return 0 ;
2009-01-07 01:43:09 +03:00
pageno = bitmap_find_free_region ( mem - > bitmap , mem - > size , order ) ;
if ( pageno > = 0 ) {
/*
* Memory was found in the per - device arena .
*/
* dma_handle = mem - > device_base + ( pageno < < PAGE_SHIFT ) ;
* ret = mem - > virt_base + ( pageno < < PAGE_SHIFT ) ;
memset ( * ret , 0 , size ) ;
} else if ( mem - > flags & DMA_MEMORY_EXCLUSIVE ) {
/*
* The per - device arena is exhausted and we are not
* permitted to fall back to generic memory .
*/
* ret = NULL ;
} else {
/*
* The per - device arena is exhausted and we are
* permitted to fall back to generic memory .
*/
return 0 ;
2008-06-29 14:18:46 +04:00
}
2009-01-07 01:43:09 +03:00
return 1 ;
2008-06-29 14:18:46 +04:00
}
2008-08-20 14:16:09 +04:00
EXPORT_SYMBOL ( dma_alloc_from_coherent ) ;
2008-06-29 14:18:46 +04:00
2008-07-20 15:01:10 +04:00
/**
2008-07-30 14:46:50 +04:00
* dma_release_from_coherent ( ) - try to free the memory allocated from per - device coherent memory pool
2008-07-20 15:01:10 +04:00
* @ dev : device from which the memory was allocated
* @ order : the order of pages allocated
* @ vaddr : virtual address of allocated pages
*
* This checks whether the memory was allocated from the per - device
* coherent memory pool and if so , releases that memory .
*
* Returns 1 if we correctly released the memory , or 0 if
2008-07-30 14:46:50 +04:00
* dma_release_coherent ( ) should proceed with releasing memory from
2008-07-20 15:01:10 +04:00
* generic pools .
*/
2008-06-29 14:18:46 +04:00
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 ;
}
2008-08-20 14:16:09 +04:00
EXPORT_SYMBOL ( dma_release_from_coherent ) ;