2005-04-17 02:20:36 +04:00
/*
* arch / parisc / mm / ioremap . c
*
* ( C ) Copyright 1995 1996 Linus Torvalds
2006-03-27 23:52:14 +04:00
* ( C ) Copyright 2001 - 2006 Helge Deller < deller @ gmx . de >
2006-01-11 04:47:52 +03:00
* ( C ) Copyright 2005 Kyle McMartin < kyle @ parisc - linux . org >
2005-04-17 02:20:36 +04:00
*/
# include <linux/vmalloc.h>
# include <linux/errno.h>
# include <linux/module.h>
2006-12-08 13:38:05 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <asm/pgalloc.h>
/*
* Generic mapping function ( not visible outside ) :
*/
/*
* Remap an arbitrary physical address space into the kernel virtual
2006-01-11 04:47:52 +03:00
* address space .
2005-04-17 02:20:36 +04:00
*
* NOTE ! We need to allow non - page - aligned mappings too : we will obviously
* have to convert them into an offset in a page - aligned mapping , but the
* caller shouldn ' t need to know that small detail .
*/
void __iomem * __ioremap ( unsigned long phys_addr , unsigned long size , unsigned long flags )
{
2006-11-06 01:24:48 +03:00
void __iomem * addr ;
2006-03-23 10:40:10 +03:00
struct vm_struct * area ;
unsigned long offset , last_addr ;
2006-12-08 13:38:05 +03:00
pgprot_t pgprot ;
2006-03-23 10:40:10 +03:00
2006-03-23 10:32:46 +03:00
# ifdef CONFIG_EISA
2005-04-17 02:20:36 +04:00
unsigned long end = phys_addr + size - 1 ;
/* Support EISA addresses */
2006-03-26 12:54:16 +04:00
if ( ( phys_addr > = 0x00080000 & & end < 0x000fffff ) | |
( phys_addr > = 0x00500000 & & end < 0x03bfffff ) ) {
phys_addr | = F_EXTEND ( 0xfc000000 ) ;
2006-03-27 23:52:14 +04:00
flags | = _PAGE_NO_CACHE ;
2005-04-17 02:20:36 +04:00
}
# endif
/* Don't allow wraparound or zero size */
last_addr = phys_addr + size - 1 ;
if ( ! size | | last_addr < phys_addr )
return NULL ;
/*
* Don ' t allow anybody to remap normal RAM that we ' re using . .
*/
if ( phys_addr < virt_to_phys ( high_memory ) ) {
char * t_addr , * t_end ;
struct page * page ;
t_addr = __va ( phys_addr ) ;
t_end = t_addr + ( size - 1 ) ;
2006-01-11 04:47:52 +03:00
for ( page = virt_to_page ( t_addr ) ;
page < = virt_to_page ( t_end ) ; page + + ) {
2005-04-17 02:20:36 +04:00
if ( ! PageReserved ( page ) )
return NULL ;
2006-01-11 04:47:52 +03:00
}
2005-04-17 02:20:36 +04:00
}
2006-12-08 13:38:05 +03:00
pgprot = __pgprot ( _PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY |
_PAGE_ACCESSED | flags ) ;
2005-04-17 02:20:36 +04:00
/*
* Mappings have to be page - aligned
*/
offset = phys_addr & ~ PAGE_MASK ;
phys_addr & = PAGE_MASK ;
2010-08-12 01:17:33 +04:00
size = PAGE_ALIGN ( last_addr + 1 ) - phys_addr ;
2005-04-17 02:20:36 +04:00
/*
* Ok , go for it . .
*/
area = get_vm_area ( size , VM_IOREMAP ) ;
if ( ! area )
return NULL ;
2006-01-11 04:47:52 +03:00
2006-11-06 01:24:48 +03:00
addr = ( void __iomem * ) area - > addr ;
2006-12-08 13:38:05 +03:00
if ( ioremap_page_range ( ( unsigned long ) addr , ( unsigned long ) addr + size ,
phys_addr , pgprot ) ) {
2005-04-17 02:20:36 +04:00
vfree ( addr ) ;
return NULL ;
}
2006-01-11 04:47:52 +03:00
2006-11-06 01:24:48 +03:00
return ( void __iomem * ) ( offset + ( char __iomem * ) addr ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-30 02:18:32 +04:00
EXPORT_SYMBOL ( __ioremap ) ;
2005-04-17 02:20:36 +04:00
2006-09-20 02:37:01 +04:00
void iounmap ( const volatile void __iomem * addr )
2005-04-17 02:20:36 +04:00
{
if ( addr > high_memory )
return vfree ( ( void * ) ( PAGE_MASK & ( unsigned long __force ) addr ) ) ;
}
2006-03-30 02:18:32 +04:00
EXPORT_SYMBOL ( iounmap ) ;