2006-10-01 10:29:12 +04:00
/*
* Re - map IO memory to kernel address space so that we can access it .
* This is needed for high PCI addresses that aren ' t mapped in the
* 640 k - 1 MB IO memory area on PC ' s
*
* ( C ) Copyright 1995 1996 Linus Torvalds
*/
# include <linux/vmalloc.h>
# include <linux/mm.h>
# include <asm/cacheflush.h>
# include <asm/pgtable.h>
static int ioremap_pte_range ( pmd_t * pmd , unsigned long addr ,
unsigned long end , unsigned long phys_addr , pgprot_t prot )
{
pte_t * pte ;
unsigned long pfn ;
pfn = phys_addr > > PAGE_SHIFT ;
pte = pte_alloc_kernel ( pmd , addr ) ;
if ( ! pte )
return - ENOMEM ;
do {
BUG_ON ( ! pte_none ( * pte ) ) ;
set_pte_at ( & init_mm , addr , pte , pfn_pte ( pfn , prot ) ) ;
pfn + + ;
} while ( pte + + , addr + = PAGE_SIZE , addr ! = end ) ;
return 0 ;
}
static inline int ioremap_pmd_range ( pud_t * pud , unsigned long addr ,
unsigned long end , unsigned long phys_addr , pgprot_t prot )
{
pmd_t * pmd ;
unsigned long next ;
phys_addr - = addr ;
pmd = pmd_alloc ( & init_mm , pud , addr ) ;
if ( ! pmd )
return - ENOMEM ;
do {
next = pmd_addr_end ( addr , end ) ;
if ( ioremap_pte_range ( pmd , addr , next , phys_addr + addr , prot ) )
return - ENOMEM ;
} while ( pmd + + , addr = next , addr ! = end ) ;
return 0 ;
}
static inline int ioremap_pud_range ( pgd_t * pgd , unsigned long addr ,
unsigned long end , unsigned long phys_addr , pgprot_t prot )
{
pud_t * pud ;
unsigned long next ;
phys_addr - = addr ;
pud = pud_alloc ( & init_mm , pgd , addr ) ;
if ( ! pud )
return - ENOMEM ;
do {
next = pud_addr_end ( addr , end ) ;
if ( ioremap_pmd_range ( pud , addr , next , phys_addr + addr , prot ) )
return - ENOMEM ;
} while ( pud + + , addr = next , addr ! = end ) ;
return 0 ;
}
int ioremap_page_range ( unsigned long addr ,
unsigned long end , unsigned long phys_addr , pgprot_t prot )
{
pgd_t * pgd ;
unsigned long start ;
unsigned long next ;
int err ;
BUG_ON ( addr > = end ) ;
start = addr ;
phys_addr - = addr ;
pgd = pgd_offset_k ( addr ) ;
do {
next = pgd_addr_end ( addr , end ) ;
err = ioremap_pud_range ( pgd , addr , next , phys_addr + addr , prot ) ;
if ( err )
break ;
} while ( pgd + + , addr = next , addr ! = end ) ;
2006-10-01 10:29:14 +04:00
flush_cache_vmap ( start , end ) ;
2006-10-01 10:29:12 +04:00
return err ;
}