2005-04-16 15:20:36 -07:00
/*
2006-10-03 23:01:26 +02:00
* linux / arch / m68k / sun3 / sun3dvma . c
2005-04-16 15:20:36 -07:00
*
* Copyright ( C ) 2000 Sam Creasey
*
* Contains common routines for sun3 / sun3x DVMA management .
*/
2006-10-11 17:28:27 +01:00
# include <linux/module.h>
2005-04-16 15:20:36 -07:00
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/list.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/dvma.h>
# undef DVMA_DEBUG
# ifdef CONFIG_SUN3X
extern void dvma_unmap_iommu ( unsigned long baddr , int len ) ;
# else
static inline void dvma_unmap_iommu ( unsigned long a , int b )
{
}
# endif
# ifdef CONFIG_SUN3
extern void sun3_dvma_init ( void ) ;
# endif
unsigned long iommu_use [ IOMMU_TOTAL_ENTRIES ] ;
# define dvma_index(baddr) ((baddr - DVMA_START) >> DVMA_PAGE_SHIFT)
# define dvma_entry_use(baddr) (iommu_use[dvma_index(baddr)])
struct hole {
unsigned long start ;
unsigned long end ;
unsigned long size ;
struct list_head list ;
} ;
static struct list_head hole_list ;
static struct list_head hole_cache ;
static struct hole initholes [ 64 ] ;
# ifdef DVMA_DEBUG
static unsigned long dvma_allocs ;
static unsigned long dvma_frees ;
static unsigned long long dvma_alloc_bytes ;
static unsigned long long dvma_free_bytes ;
static void print_use ( void )
{
int i ;
int j = 0 ;
printk ( " dvma entry usage: \n " ) ;
for ( i = 0 ; i < IOMMU_TOTAL_ENTRIES ; i + + ) {
if ( ! iommu_use [ i ] )
continue ;
j + + ;
printk ( " dvma entry: %08lx len %08lx \n " ,
( i < < DVMA_PAGE_SHIFT ) + DVMA_START ,
iommu_use [ i ] ) ;
}
printk ( " %d entries in use total \n " , j ) ;
printk ( " allocation/free calls: %lu/%lu \n " , dvma_allocs , dvma_frees ) ;
printk ( " allocation/free bytes: %Lx/%Lx \n " , dvma_alloc_bytes ,
dvma_free_bytes ) ;
}
static void print_holes ( struct list_head * holes )
{
struct list_head * cur ;
struct hole * hole ;
printk ( " listing dvma holes \n " ) ;
list_for_each ( cur , holes ) {
hole = list_entry ( cur , struct hole , list ) ;
if ( ( hole - > start = = 0 ) & & ( hole - > end = = 0 ) & & ( hole - > size = = 0 ) )
continue ;
printk ( " hole: start %08lx end %08lx size %08lx \n " , hole - > start , hole - > end , hole - > size ) ;
}
printk ( " end of hole listing... \n " ) ;
}
# endif /* DVMA_DEBUG */
static inline int refill ( void )
{
struct hole * hole ;
struct hole * prev = NULL ;
struct list_head * cur ;
int ret = 0 ;
list_for_each ( cur , & hole_list ) {
hole = list_entry ( cur , struct hole , list ) ;
if ( ! prev ) {
prev = hole ;
continue ;
}
if ( hole - > end = = prev - > start ) {
hole - > size + = prev - > size ;
hole - > end = prev - > end ;
2006-06-26 00:24:39 -07:00
list_move ( & ( prev - > list ) , & hole_cache ) ;
2005-04-16 15:20:36 -07:00
ret + + ;
}
}
return ret ;
}
static inline struct hole * rmcache ( void )
{
struct hole * ret ;
if ( list_empty ( & hole_cache ) ) {
if ( ! refill ( ) ) {
printk ( " out of dvma hole cache! \n " ) ;
BUG ( ) ;
}
}
ret = list_entry ( hole_cache . next , struct hole , list ) ;
list_del ( & ( ret - > list ) ) ;
return ret ;
}
static inline unsigned long get_baddr ( int len , unsigned long align )
{
struct list_head * cur ;
struct hole * hole ;
if ( list_empty ( & hole_list ) ) {
# ifdef DVMA_DEBUG
printk ( " out of dvma holes! (printing hole cache) \n " ) ;
print_holes ( & hole_cache ) ;
print_use ( ) ;
# endif
BUG ( ) ;
}
list_for_each ( cur , & hole_list ) {
unsigned long newlen ;
hole = list_entry ( cur , struct hole , list ) ;
if ( align > DVMA_PAGE_SIZE )
newlen = len + ( ( hole - > end - len ) & ( align - 1 ) ) ;
else
newlen = len ;
if ( hole - > size > newlen ) {
hole - > end - = newlen ;
hole - > size - = newlen ;
dvma_entry_use ( hole - > end ) = newlen ;
# ifdef DVMA_DEBUG
dvma_allocs + + ;
dvma_alloc_bytes + = newlen ;
# endif
return hole - > end ;
} else if ( hole - > size = = newlen ) {
2006-06-26 00:24:39 -07:00
list_move ( & ( hole - > list ) , & hole_cache ) ;
2005-04-16 15:20:36 -07:00
dvma_entry_use ( hole - > start ) = newlen ;
# ifdef DVMA_DEBUG
dvma_allocs + + ;
dvma_alloc_bytes + = newlen ;
# endif
return hole - > start ;
}
}
printk ( " unable to find dvma hole! \n " ) ;
BUG ( ) ;
return 0 ;
}
static inline int free_baddr ( unsigned long baddr )
{
unsigned long len ;
struct hole * hole ;
struct list_head * cur ;
unsigned long orig_baddr ;
orig_baddr = baddr ;
len = dvma_entry_use ( baddr ) ;
dvma_entry_use ( baddr ) = 0 ;
baddr & = DVMA_PAGE_MASK ;
dvma_unmap_iommu ( baddr , len ) ;
# ifdef DVMA_DEBUG
dvma_frees + + ;
dvma_free_bytes + = len ;
# endif
list_for_each ( cur , & hole_list ) {
hole = list_entry ( cur , struct hole , list ) ;
if ( hole - > end = = baddr ) {
hole - > end + = len ;
hole - > size + = len ;
return 0 ;
} else if ( hole - > start = = ( baddr + len ) ) {
hole - > start = baddr ;
hole - > size + = len ;
return 0 ;
}
}
hole = rmcache ( ) ;
hole - > start = baddr ;
hole - > end = baddr + len ;
hole - > size = len ;
// list_add_tail(&(hole->list), cur);
list_add ( & ( hole - > list ) , cur ) ;
return 0 ;
}
void dvma_init ( void )
{
struct hole * hole ;
int i ;
INIT_LIST_HEAD ( & hole_list ) ;
INIT_LIST_HEAD ( & hole_cache ) ;
/* prepare the hole cache */
for ( i = 0 ; i < 64 ; i + + )
list_add ( & ( initholes [ i ] . list ) , & hole_cache ) ;
hole = rmcache ( ) ;
hole - > start = DVMA_START ;
hole - > end = DVMA_END ;
hole - > size = DVMA_SIZE ;
list_add ( & ( hole - > list ) , & hole_list ) ;
memset ( iommu_use , 0 , sizeof ( iommu_use ) ) ;
dvma_unmap_iommu ( DVMA_START , DVMA_SIZE ) ;
# ifdef CONFIG_SUN3
sun3_dvma_init ( ) ;
# endif
}
inline unsigned long dvma_map_align ( unsigned long kaddr , int len , int align )
{
unsigned long baddr ;
unsigned long off ;
if ( ! len )
len = 0x800 ;
if ( ! kaddr | | ! len ) {
// printk("error: kaddr %lx len %x\n", kaddr, len);
// *(int *)4 = 0;
return 0 ;
}
# ifdef DEBUG
printk ( " dvma_map request %08lx bytes from %08lx \n " ,
len , kaddr ) ;
# endif
off = kaddr & ~ DVMA_PAGE_MASK ;
kaddr & = PAGE_MASK ;
len + = off ;
len = ( ( len + ( DVMA_PAGE_SIZE - 1 ) ) & DVMA_PAGE_MASK ) ;
if ( align = = 0 )
align = DVMA_PAGE_SIZE ;
else
align = ( ( align + ( DVMA_PAGE_SIZE - 1 ) ) & DVMA_PAGE_MASK ) ;
baddr = get_baddr ( len , align ) ;
// printk("using baddr %lx\n", baddr);
if ( ! dvma_map_iommu ( kaddr , baddr , len ) )
return ( baddr + off ) ;
printk ( " dvma_map failed kaddr %lx baddr %lx len %x \n " , kaddr , baddr , len ) ;
BUG ( ) ;
return 0 ;
}
2006-10-11 17:28:27 +01:00
EXPORT_SYMBOL ( dvma_map_align ) ;
2005-04-16 15:20:36 -07:00
void dvma_unmap ( void * baddr )
{
unsigned long addr ;
addr = ( unsigned long ) baddr ;
/* check if this is a vme mapping */
if ( ! ( addr & 0x00f00000 ) )
addr | = 0xf00000 ;
free_baddr ( addr ) ;
return ;
}
2006-10-11 17:28:27 +01:00
EXPORT_SYMBOL ( dvma_unmap ) ;
2005-04-16 15:20:36 -07:00
void * dvma_malloc_align ( unsigned long len , unsigned long align )
{
unsigned long kaddr ;
unsigned long baddr ;
unsigned long vaddr ;
if ( ! len )
return NULL ;
# ifdef DEBUG
printk ( " dvma_malloc request %lx bytes \n " , len ) ;
# endif
len = ( ( len + ( DVMA_PAGE_SIZE - 1 ) ) & DVMA_PAGE_MASK ) ;
if ( ( kaddr = __get_free_pages ( GFP_ATOMIC , get_order ( len ) ) ) = = 0 )
return NULL ;
if ( ( baddr = ( unsigned long ) dvma_map_align ( kaddr , len , align ) ) = = 0 ) {
free_pages ( kaddr , get_order ( len ) ) ;
return NULL ;
}
vaddr = dvma_btov ( baddr ) ;
if ( dvma_map_cpu ( kaddr , vaddr , len ) < 0 ) {
dvma_unmap ( ( void * ) baddr ) ;
free_pages ( kaddr , get_order ( len ) ) ;
return NULL ;
}
# ifdef DEBUG
printk ( " mapped %08lx bytes %08lx kern -> %08lx bus \n " ,
len , kaddr , baddr ) ;
# endif
return ( void * ) vaddr ;
}
2006-10-11 17:28:27 +01:00
EXPORT_SYMBOL ( dvma_malloc_align ) ;
2005-04-16 15:20:36 -07:00
void dvma_free ( void * vaddr )
{
return ;
}
2006-10-11 17:28:27 +01:00
EXPORT_SYMBOL ( dvma_free ) ;