2005-04-17 02:20:36 +04:00
/*
* DMA memory management for framework level HCD code ( hc_driver )
*
* This implementation plugs in through generic " usb_bus " level methods ,
* and should work with all USB controllers , regardles of bus type .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/mm.h>
# include <asm/io.h>
# include <asm/scatterlist.h>
# include <linux/dma-mapping.h>
# include <linux/dmapool.h>
# include <linux/usb.h>
# include "hcd.h"
/*
* DMA - Coherent Buffers
*/
/* FIXME tune these based on pool statistics ... */
static const size_t pool_max [ HCD_BUFFER_POOLS ] = {
/* platforms without dma-friendly caches might need to
* prevent cacheline sharing . . .
*/
32 ,
128 ,
512 ,
PAGE_SIZE / 2
/* bigger --> allocate pages */
} ;
/* SETUP primitives */
/**
* hcd_buffer_create - initialize buffer pools
* @ hcd : the bus whose buffer pools are to be initialized
* Context : ! in_interrupt ( )
*
* Call this as part of initializing a host controller that uses the dma
* memory allocators . It initializes some pools of dma - coherent memory that
* will be shared by all drivers using that controller , or returns a negative
* errno value on error .
*
* Call hcd_buffer_destroy ( ) to clean up after using those pools .
*/
2007-01-25 13:17:41 +03:00
int hcd_buffer_create ( struct usb_hcd * hcd )
2005-04-17 02:20:36 +04:00
{
2007-01-25 13:17:41 +03:00
char name [ 16 ] ;
2005-04-17 02:20:36 +04:00
int i , size ;
2005-11-28 20:29:23 +03:00
if ( ! hcd - > self . controller - > dma_mask )
return 0 ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < HCD_BUFFER_POOLS ; i + + ) {
if ( ! ( size = pool_max [ i ] ) )
continue ;
2007-01-25 13:17:41 +03:00
snprintf ( name , sizeof name , " buffer-%d " , size ) ;
hcd - > pool [ i ] = dma_pool_create ( name , hcd - > self . controller ,
2005-04-17 02:20:36 +04:00
size , size , 0 ) ;
if ( ! hcd - > pool [ i ] ) {
2007-01-25 13:17:41 +03:00
hcd_buffer_destroy ( hcd ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
}
return 0 ;
}
/**
* hcd_buffer_destroy - deallocate buffer pools
* @ hcd : the bus whose buffer pools are to be destroyed
* Context : ! in_interrupt ( )
*
* This frees the buffer pools created by hcd_buffer_create ( ) .
*/
2007-01-25 13:17:41 +03:00
void hcd_buffer_destroy ( struct usb_hcd * hcd )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < HCD_BUFFER_POOLS ; i + + ) {
2007-01-25 13:17:41 +03:00
struct dma_pool * pool = hcd - > pool [ i ] ;
2005-04-17 02:20:36 +04:00
if ( pool ) {
2007-01-25 13:17:41 +03:00
dma_pool_destroy ( pool ) ;
2005-04-17 02:20:36 +04:00
hcd - > pool [ i ] = NULL ;
}
}
}
2006-12-07 07:33:19 +03:00
/* sometimes alloc/free could use kmalloc with GFP_DMA, for
2005-04-17 02:20:36 +04:00
* better sharing and to leverage mm / slab . c intelligence .
*/
2007-01-25 13:17:41 +03:00
void * hcd_buffer_alloc (
struct usb_bus * bus ,
2005-04-17 02:20:36 +04:00
size_t size ,
2005-10-21 11:21:58 +04:00
gfp_t mem_flags ,
2005-04-17 02:20:36 +04:00
dma_addr_t * dma
)
{
2006-08-30 19:32:52 +04:00
struct usb_hcd * hcd = bus_to_hcd ( bus ) ;
2005-04-17 02:20:36 +04:00
int i ;
/* some USB hosts just use PIO */
if ( ! bus - > controller - > dma_mask ) {
* dma = ~ ( dma_addr_t ) 0 ;
2007-01-25 13:17:41 +03:00
return kmalloc ( size , mem_flags ) ;
2005-04-17 02:20:36 +04:00
}
for ( i = 0 ; i < HCD_BUFFER_POOLS ; i + + ) {
if ( size < = pool_max [ i ] )
2007-01-25 13:17:41 +03:00
return dma_pool_alloc ( hcd - > pool [ i ] , mem_flags , dma ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-25 13:17:41 +03:00
return dma_alloc_coherent ( hcd - > self . controller , size , dma , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2007-01-25 13:17:41 +03:00
void hcd_buffer_free (
struct usb_bus * bus ,
2005-04-17 02:20:36 +04:00
size_t size ,
void * addr ,
dma_addr_t dma
)
{
2006-08-30 19:32:52 +04:00
struct usb_hcd * hcd = bus_to_hcd ( bus ) ;
2005-04-17 02:20:36 +04:00
int i ;
if ( ! addr )
return ;
if ( ! bus - > controller - > dma_mask ) {
2007-01-25 13:17:41 +03:00
kfree ( addr ) ;
2005-04-17 02:20:36 +04:00
return ;
}
for ( i = 0 ; i < HCD_BUFFER_POOLS ; i + + ) {
if ( size < = pool_max [ i ] ) {
2007-01-25 13:17:41 +03:00
dma_pool_free ( hcd - > pool [ i ] , addr , dma ) ;
2005-04-17 02:20:36 +04:00
return ;
}
}
2007-01-25 13:17:41 +03:00
dma_free_coherent ( hcd - > self . controller , size , addr , dma ) ;
2005-04-17 02:20:36 +04:00
}