2008-05-19 16:52:27 -07:00
/*
2005-04-16 15:20:36 -07:00
* generic . c : Generic Sparc mm routines that are not dependent upon
* MMU type but are Sparc specific .
*
* Copyright ( C ) 1996 David S . Miller ( davem @ caip . rutgers . edu )
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/swap.h>
# include <linux/pagemap.h>
# include <asm/pgalloc.h>
# include <asm/pgtable.h>
# include <asm/page.h>
# include <asm/tlbflush.h>
/* Remap IO memory, the same way as remap_pfn_range(), but use
* the obio memory space .
*
* They use a pgprot that sets PAGE_IO and does not check the
* mem_map table as this is independent of normal memory .
*/
static inline void io_remap_pte_range ( struct mm_struct * mm , pte_t * pte ,
unsigned long address ,
unsigned long size ,
unsigned long offset , pgprot_t prot ,
int space )
{
unsigned long end ;
/* clear hack bit that was used as a write_combine side-effect flag */
offset & = ~ 0x1UL ;
address & = ~ PMD_MASK ;
end = address + size ;
if ( end > PMD_SIZE )
end = PMD_SIZE ;
do {
pte_t entry ;
unsigned long curend = address + PAGE_SIZE ;
2006-02-11 21:57:54 -08:00
entry = mk_pte_io ( offset , prot , space , PAGE_SIZE ) ;
2005-04-16 15:20:36 -07:00
if ( ! ( address & 0xffff ) ) {
2006-02-11 21:57:54 -08:00
if ( PAGE_SIZE < ( 4 * 1024 * 1024 ) & &
! ( address & 0x3fffff ) & &
! ( offset & 0x3ffffe ) & &
end > = address + 0x400000 ) {
entry = mk_pte_io ( offset , prot , space ,
4 * 1024 * 1024 ) ;
2005-04-16 15:20:36 -07:00
curend = address + 0x400000 ;
offset + = 0x400000 ;
2006-02-11 21:57:54 -08:00
} else if ( PAGE_SIZE < ( 512 * 1024 ) & &
! ( address & 0x7ffff ) & &
! ( offset & 0x7fffe ) & &
end > = address + 0x80000 ) {
entry = mk_pte_io ( offset , prot , space ,
512 * 1024 * 1024 ) ;
2005-04-16 15:20:36 -07:00
curend = address + 0x80000 ;
offset + = 0x80000 ;
2006-02-11 21:57:54 -08:00
} else if ( PAGE_SIZE < ( 64 * 1024 ) & &
! ( offset & 0xfffe ) & &
end > = address + 0x10000 ) {
entry = mk_pte_io ( offset , prot , space ,
64 * 1024 ) ;
2005-04-16 15:20:36 -07:00
curend = address + 0x10000 ;
offset + = 0x10000 ;
} else
offset + = PAGE_SIZE ;
} else
offset + = PAGE_SIZE ;
2006-08-28 00:33:03 -07:00
if ( pte_write ( entry ) )
entry = pte_mkdirty ( entry ) ;
2005-04-16 15:20:36 -07:00
do {
BUG_ON ( ! pte_none ( * pte ) ) ;
set_pte_at ( mm , address , pte , entry ) ;
address + = PAGE_SIZE ;
2005-11-29 13:59:03 -08:00
pte_val ( entry ) + = PAGE_SIZE ;
2005-04-16 15:20:36 -07:00
pte + + ;
} while ( address < curend ) ;
} while ( address < end ) ;
}
static inline int io_remap_pmd_range ( struct mm_struct * mm , pmd_t * pmd , unsigned long address , unsigned long size ,
unsigned long offset , pgprot_t prot , int space )
{
unsigned long end ;
address & = ~ PGDIR_MASK ;
end = address + size ;
if ( end > PGDIR_SIZE )
end = PGDIR_SIZE ;
offset - = address ;
do {
pte_t * pte = pte_alloc_map ( mm , pmd , address ) ;
if ( ! pte )
return - ENOMEM ;
io_remap_pte_range ( mm , pte , address , end - address , address + offset , prot , space ) ;
pte_unmap ( pte ) ;
address = ( address + PMD_SIZE ) & PMD_MASK ;
pmd + + ;
} while ( address < end ) ;
return 0 ;
}
static inline int io_remap_pud_range ( struct mm_struct * mm , pud_t * pud , unsigned long address , unsigned long size ,
unsigned long offset , pgprot_t prot , int space )
{
unsigned long end ;
address & = ~ PUD_MASK ;
end = address + size ;
if ( end > PUD_SIZE )
end = PUD_SIZE ;
offset - = address ;
do {
pmd_t * pmd = pmd_alloc ( mm , pud , address ) ;
if ( ! pud )
return - ENOMEM ;
io_remap_pmd_range ( mm , pmd , address , end - address , address + offset , prot , space ) ;
address = ( address + PUD_SIZE ) & PUD_MASK ;
pud + + ;
} while ( address < end ) ;
return 0 ;
}
int io_remap_pfn_range ( struct vm_area_struct * vma , unsigned long from ,
unsigned long pfn , unsigned long size , pgprot_t prot )
{
int error = 0 ;
pgd_t * dir ;
unsigned long beg = from ;
unsigned long end = from + size ;
struct mm_struct * mm = vma - > vm_mm ;
int space = GET_IOSPACE ( pfn ) ;
unsigned long offset = GET_PFN ( pfn ) < < PAGE_SHIFT ;
2005-11-28 14:02:10 -08:00
unsigned long phys_base ;
phys_base = offset | ( ( ( unsigned long ) space ) < < 32UL ) ;
2005-04-16 15:20:36 -07:00
2005-10-29 18:16:12 -07:00
/* See comment in mm/memory.c remap_pfn_range */
2005-11-28 14:02:10 -08:00
vma - > vm_flags | = VM_IO | VM_RESERVED | VM_PFNMAP ;
vma - > vm_pgoff = phys_base > > PAGE_SHIFT ;
2005-10-29 18:16:12 -07:00
2005-04-16 15:20:36 -07:00
offset - = from ;
dir = pgd_offset ( mm , from ) ;
flush_cache_range ( vma , beg , end ) ;
while ( from < end ) {
2005-10-29 18:16:24 -07:00
pud_t * pud = pud_alloc ( mm , dir , from ) ;
2005-04-16 15:20:36 -07:00
error = - ENOMEM ;
if ( ! pud )
break ;
error = io_remap_pud_range ( mm , pud , from , end - from , offset + from , prot , space ) ;
if ( error )
break ;
from = ( from + PGDIR_SIZE ) & PGDIR_MASK ;
dir + + ;
}
2005-10-29 18:16:24 -07:00
flush_tlb_range ( vma , beg , end ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
2009-01-08 16:58:20 -08:00
EXPORT_SYMBOL ( io_remap_pfn_range ) ;