2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* linux / arch / m68k / mm / motorola . c
2005-04-17 02:20:36 +04:00
*
* Routines specific to the Motorola MMU , originally from :
* linux / arch / m68k / init . c
* which are Copyright ( C ) 1995 Hamish Macdonald
*
* Moved 8 / 20 / 1999 Sam Creasey
*/
# include <linux/module.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/swap.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/bootmem.h>
# include <asm/setup.h>
# include <asm/uaccess.h>
# include <asm/page.h>
# include <asm/pgalloc.h>
# include <asm/system.h>
# include <asm/machdep.h>
# include <asm/io.h>
# include <asm/dma.h>
# ifdef CONFIG_ATARI
# include <asm/atari_stram.h>
# endif
# undef DEBUG
# ifndef mm_cachebits
/*
* Bits to add to page descriptors for " normal " caching mode .
* For 68020 / 030 this is 0.
* For 68040 , this is _PAGE_CACHE040 ( cachable , copyback )
*/
unsigned long mm_cachebits ;
EXPORT_SYMBOL ( mm_cachebits ) ;
# endif
2007-05-31 11:40:54 +04:00
/* size of memory already mapped in head.S */
# define INIT_MAPPED_SIZE (4UL<<20)
extern unsigned long availmem ;
2005-04-17 02:20:36 +04:00
static pte_t * __init kernel_page_table ( void )
{
pte_t * ptablep ;
ptablep = ( pte_t * ) alloc_bootmem_low_pages ( PAGE_SIZE ) ;
clear_page ( ptablep ) ;
__flush_page_to_ram ( ptablep ) ;
flush_tlb_kernel_page ( ptablep ) ;
nocache_page ( ptablep ) ;
return ptablep ;
}
static pmd_t * last_pgtable __initdata = NULL ;
pmd_t * zero_pgtable __initdata = NULL ;
static pmd_t * __init kernel_ptr_table ( void )
{
if ( ! last_pgtable ) {
unsigned long pmd , last ;
int i ;
/* Find the last ptr table that was used in head.S and
* reuse the remaining space in that page for further
* ptr tables .
*/
last = ( unsigned long ) kernel_pg_dir ;
for ( i = 0 ; i < PTRS_PER_PGD ; i + + ) {
if ( ! pgd_present ( kernel_pg_dir [ i ] ) )
continue ;
pmd = __pgd_page ( kernel_pg_dir [ i ] ) ;
if ( pmd > last )
last = pmd ;
}
last_pgtable = ( pmd_t * ) last ;
# ifdef DEBUG
printk ( " kernel_ptr_init: %p \n " , last_pgtable ) ;
# endif
}
last_pgtable + = PTRS_PER_PMD ;
if ( ( ( unsigned long ) last_pgtable & ~ PAGE_MASK ) = = 0 ) {
last_pgtable = ( pmd_t * ) alloc_bootmem_low_pages ( PAGE_SIZE ) ;
clear_page ( last_pgtable ) ;
__flush_page_to_ram ( last_pgtable ) ;
flush_tlb_kernel_page ( last_pgtable ) ;
nocache_page ( last_pgtable ) ;
}
return last_pgtable ;
}
2007-05-31 11:40:54 +04:00
static void __init map_node ( int node )
2005-04-17 02:20:36 +04:00
{
# define PTRTREESIZE (256*1024)
# define ROOTTREESIZE (32*1024*1024)
2007-05-31 11:40:54 +04:00
unsigned long physaddr , virtaddr , size ;
2005-04-17 02:20:36 +04:00
pgd_t * pgd_dir ;
pmd_t * pmd_dir ;
pte_t * pte_dir ;
2007-05-31 11:40:54 +04:00
size = m68k_memory [ node ] . size ;
physaddr = m68k_memory [ node ] . addr ;
virtaddr = ( unsigned long ) phys_to_virt ( physaddr ) ;
physaddr | = m68k_supervisor_cachemode |
_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY ;
2005-04-17 02:20:36 +04:00
if ( CPU_IS_040_OR_060 )
physaddr | = _PAGE_GLOBAL040 ;
while ( size > 0 ) {
# ifdef DEBUG
if ( ! ( virtaddr & ( PTRTREESIZE - 1 ) ) )
printk ( " \n pa=%#lx va=%#lx " , physaddr & PAGE_MASK ,
virtaddr ) ;
# endif
pgd_dir = pgd_offset_k ( virtaddr ) ;
if ( virtaddr & & CPU_IS_020_OR_030 ) {
if ( ! ( virtaddr & ( ROOTTREESIZE - 1 ) ) & &
size > = ROOTTREESIZE ) {
# ifdef DEBUG
printk ( " [very early term] " ) ;
# endif
pgd_val ( * pgd_dir ) = physaddr ;
size - = ROOTTREESIZE ;
virtaddr + = ROOTTREESIZE ;
physaddr + = ROOTTREESIZE ;
continue ;
}
}
if ( ! pgd_present ( * pgd_dir ) ) {
pmd_dir = kernel_ptr_table ( ) ;
# ifdef DEBUG
printk ( " [new pointer %p] " , pmd_dir ) ;
# endif
pgd_set ( pgd_dir , pmd_dir ) ;
} else
pmd_dir = pmd_offset ( pgd_dir , virtaddr ) ;
if ( CPU_IS_020_OR_030 ) {
if ( virtaddr ) {
# ifdef DEBUG
printk ( " [early term] " ) ;
# endif
pmd_dir - > pmd [ ( virtaddr / PTRTREESIZE ) & 15 ] = physaddr ;
physaddr + = PTRTREESIZE ;
} else {
int i ;
# ifdef DEBUG
printk ( " [zero map] " ) ;
# endif
zero_pgtable = kernel_ptr_table ( ) ;
pte_dir = ( pte_t * ) zero_pgtable ;
pmd_dir - > pmd [ 0 ] = virt_to_phys ( pte_dir ) |
_PAGE_TABLE | _PAGE_ACCESSED ;
pte_val ( * pte_dir + + ) = 0 ;
physaddr + = PAGE_SIZE ;
for ( i = 1 ; i < 64 ; physaddr + = PAGE_SIZE , i + + )
pte_val ( * pte_dir + + ) = physaddr ;
}
size - = PTRTREESIZE ;
virtaddr + = PTRTREESIZE ;
} else {
if ( ! pmd_present ( * pmd_dir ) ) {
# ifdef DEBUG
printk ( " [new table] " ) ;
# endif
pte_dir = kernel_page_table ( ) ;
pmd_set ( pmd_dir , pte_dir ) ;
}
pte_dir = pte_offset_kernel ( pmd_dir , virtaddr ) ;
if ( virtaddr ) {
if ( ! pte_present ( * pte_dir ) )
pte_val ( * pte_dir ) = physaddr ;
} else
pte_val ( * pte_dir ) = 0 ;
size - = PAGE_SIZE ;
virtaddr + = PAGE_SIZE ;
physaddr + = PAGE_SIZE ;
}
}
# ifdef DEBUG
printk ( " \n " ) ;
# endif
}
/*
* paging_init ( ) continues the virtual memory environment setup which
* was begun by the code in arch / head . S .
*/
void __init paging_init ( void )
{
2006-06-23 13:04:58 +04:00
unsigned long zones_size [ MAX_NR_ZONES ] = { 0 , } ;
2007-05-31 11:40:54 +04:00
unsigned long min_addr , max_addr ;
unsigned long addr , size , end ;
int i ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG
2007-08-23 01:01:30 +04:00
printk ( " start of paging_init (%p, %lx) \n " , kernel_pg_dir , availmem ) ;
2005-04-17 02:20:36 +04:00
# endif
/* Fix the cache mode in the page descriptors for the 680[46]0. */
if ( CPU_IS_040_OR_060 ) {
int i ;
# ifndef mm_cachebits
mm_cachebits = _PAGE_CACHE040 ;
# endif
for ( i = 0 ; i < 16 ; i + + )
pgprot_val ( protection_map [ i ] ) | = _PAGE_CACHE040 ;
}
2007-05-31 11:40:54 +04:00
min_addr = m68k_memory [ 0 ] . addr ;
max_addr = min_addr + m68k_memory [ 0 ] . size ;
for ( i = 1 ; i < m68k_num_memory ; ) {
if ( m68k_memory [ i ] . addr < min_addr ) {
printk ( " Ignoring memory chunk at 0x%lx:0x%lx before the first chunk \n " ,
m68k_memory [ i ] . addr , m68k_memory [ i ] . size ) ;
printk ( " Fix your bootloader or use a memfile to make use of this area! \n " ) ;
m68k_num_memory - - ;
memmove ( m68k_memory + i , m68k_memory + i + 1 ,
( m68k_num_memory - i ) * sizeof ( struct mem_info ) ) ;
continue ;
}
addr = m68k_memory [ i ] . addr + m68k_memory [ i ] . size ;
if ( addr > max_addr )
max_addr = addr ;
i + + ;
}
m68k_memoffset = min_addr - PAGE_OFFSET ;
m68k_virt_to_node_shift = fls ( max_addr - min_addr - 1 ) - 6 ;
2007-05-31 11:40:50 +04:00
module_fixup ( NULL , __start_fixup , __stop_fixup ) ;
flush_icache ( ) ;
2007-05-31 11:40:54 +04:00
high_memory = phys_to_virt ( max_addr ) ;
min_low_pfn = availmem > > PAGE_SHIFT ;
max_low_pfn = max_addr > > PAGE_SHIFT ;
for ( i = 0 ; i < m68k_num_memory ; i + + ) {
addr = m68k_memory [ i ] . addr ;
end = addr + m68k_memory [ i ] . size ;
m68k_setup_node ( i ) ;
availmem = PAGE_ALIGN ( availmem ) ;
availmem + = init_bootmem_node ( NODE_DATA ( i ) ,
availmem > > PAGE_SHIFT ,
addr > > PAGE_SHIFT ,
end > > PAGE_SHIFT ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Map the physical memory available into the kernel virtual
2007-05-31 11:40:54 +04:00
* address space . First initialize the bootmem allocator with
* the memory we already mapped , so map_node ( ) has something
* to allocate .
2005-04-17 02:20:36 +04:00
*/
2007-05-31 11:40:54 +04:00
addr = m68k_memory [ 0 ] . addr ;
size = m68k_memory [ 0 ] . size ;
free_bootmem_node ( NODE_DATA ( 0 ) , availmem , min ( INIT_MAPPED_SIZE , size ) - ( availmem - addr ) ) ;
map_node ( 0 ) ;
if ( size > INIT_MAPPED_SIZE )
free_bootmem_node ( NODE_DATA ( 0 ) , addr + INIT_MAPPED_SIZE , size - INIT_MAPPED_SIZE ) ;
2005-04-17 02:20:36 +04:00
2007-05-31 11:40:54 +04:00
for ( i = 1 ; i < m68k_num_memory ; i + + )
map_node ( i ) ;
2005-04-17 02:20:36 +04:00
flush_tlb_all ( ) ;
/*
* initialize the bad page table and bad page to point
* to a couple of allocated pages
*/
empty_zero_page = alloc_bootmem_pages ( PAGE_SIZE ) ;
/*
* Set up SFC / DFC registers
*/
set_fs ( KERNEL_DS ) ;
# ifdef DEBUG
printk ( " before free_area_init \n " ) ;
# endif
2007-05-31 11:40:54 +04:00
for ( i = 0 ; i < m68k_num_memory ; i + + ) {
zones_size [ ZONE_DMA ] = m68k_memory [ i ] . size > > PAGE_SHIFT ;
2008-07-24 08:27:20 +04:00
free_area_init_node ( i , zones_size ,
2007-05-31 11:40:54 +04:00
m68k_memory [ i ] . addr > > PAGE_SHIFT , NULL ) ;
}
2005-04-17 02:20:36 +04:00
}
extern char __init_begin , __init_end ;
void free_initmem ( void )
{
unsigned long addr ;
addr = ( unsigned long ) & __init_begin ;
for ( ; addr < ( unsigned long ) & __init_end ; addr + = PAGE_SIZE ) {
virt_to_page ( addr ) - > flags & = ~ ( 1 < < PG_reserved ) ;
2006-03-22 11:08:40 +03:00
init_page_count ( virt_to_page ( addr ) ) ;
2005-04-17 02:20:36 +04:00
free_page ( addr ) ;
totalram_pages + + ;
}
}