2005-09-26 10:04:21 +04:00
/*
* This file contains the routines for handling the MMU on those
* PowerPC implementations where the MMU substantially follows the
* architecture specification . This includes the 6 xx , 7 xx , 7 xxx ,
* 8260 , and POWER3 implementations but excludes the 8 xx and 4 xx .
* - - paulus
*
* Derived from arch / ppc / mm / init . c :
* Copyright ( C ) 1995 - 1996 Gary Thomas ( gdt @ linuxppc . org )
*
* Modifications by Paul Mackerras ( PowerMac ) ( paulus @ cs . anu . edu . au )
* and Cort Dougan ( PReP ) ( cort @ cs . nmt . edu )
* Copyright ( C ) 1996 Paul Mackerras
*
* Derived from " arch/i386/mm/init.c "
* Copyright ( C ) 1991 , 1992 , 1993 , 1994 Linus Torvalds
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/init.h>
# include <linux/highmem.h>
2008-02-14 03:56:49 +03:00
# include <linux/lmb.h>
2005-09-26 10:04:21 +04:00
# include <asm/prom.h>
# include <asm/mmu.h>
# include <asm/machdep.h>
# include "mmu_decl.h"
2007-06-13 08:52:56 +04:00
struct hash_pte * Hash , * Hash_end ;
2005-09-26 10:04:21 +04:00
unsigned long Hash_size , Hash_mask ;
unsigned long _SDR1 ;
2008-06-14 03:41:43 +04:00
struct ppc_bat BATS [ 8 ] [ 2 ] ; /* 8 pairs of IBAT, DBAT */
2005-09-26 10:04:21 +04:00
struct batrange { /* stores address ranges mapped by BATs */
unsigned long start ;
unsigned long limit ;
2008-06-14 03:41:42 +04:00
phys_addr_t phys ;
2006-06-18 02:52:44 +04:00
} bat_addrs [ 8 ] ;
2005-09-26 10:04:21 +04:00
/*
* Return PA for this VA if it is mapped by a BAT , or 0
*/
2008-06-14 03:41:42 +04:00
phys_addr_t v_mapped_by_bats ( unsigned long va )
2005-09-26 10:04:21 +04:00
{
int b ;
for ( b = 0 ; b < 4 ; + + b )
if ( va > = bat_addrs [ b ] . start & & va < bat_addrs [ b ] . limit )
return bat_addrs [ b ] . phys + ( va - bat_addrs [ b ] . start ) ;
return 0 ;
}
/*
* Return VA for a given PA or 0 if not mapped
*/
2008-06-14 03:41:42 +04:00
unsigned long p_mapped_by_bats ( phys_addr_t pa )
2005-09-26 10:04:21 +04:00
{
int b ;
for ( b = 0 ; b < 4 ; + + b )
if ( pa > = bat_addrs [ b ] . phys
& & pa < ( bat_addrs [ b ] . limit - bat_addrs [ b ] . start )
+ bat_addrs [ b ] . phys )
return bat_addrs [ b ] . start + ( pa - bat_addrs [ b ] . phys ) ;
return 0 ;
}
unsigned long __init mmu_mapin_ram ( void )
{
unsigned long tot , bl , done ;
unsigned long max_size = ( 256 < < 20 ) ;
2007-04-12 09:30:22 +04:00
if ( __map_without_bats ) {
printk ( KERN_DEBUG " RAM mapped without BATs \n " ) ;
2005-09-26 10:04:21 +04:00
return 0 ;
2007-04-12 09:30:22 +04:00
}
2005-09-26 10:04:21 +04:00
/* Set up BAT2 and if necessary BAT3 to cover RAM. */
/* Make sure we don't map a block larger than the
smallest alignment of the physical address . */
tot = total_lowmem ;
for ( bl = 128 < < 10 ; bl < max_size ; bl < < = 1 ) {
if ( bl * 2 > tot )
break ;
}
2009-03-19 22:34:08 +03:00
setbat ( 2 , PAGE_OFFSET , 0 , bl , PAGE_KERNEL_X ) ;
2008-12-17 13:09:13 +03:00
done = ( unsigned long ) bat_addrs [ 2 ] . limit - PAGE_OFFSET + 1 ;
2005-09-26 10:04:21 +04:00
if ( ( done < tot ) & & ! bat_addrs [ 3 ] . limit ) {
/* use BAT3 to cover a bit more */
tot - = done ;
for ( bl = 128 < < 10 ; bl < max_size ; bl < < = 1 )
if ( bl * 2 > tot )
break ;
2009-03-19 22:34:08 +03:00
setbat ( 3 , PAGE_OFFSET + done , done , bl , PAGE_KERNEL_X ) ;
2008-12-17 13:09:13 +03:00
done = ( unsigned long ) bat_addrs [ 3 ] . limit - PAGE_OFFSET + 1 ;
2005-09-26 10:04:21 +04:00
}
return done ;
}
/*
* Set up one of the I / D BAT ( block address translation ) register pairs .
* The parameters are not checked ; in particular size must be a power
* of 2 between 128 k and 256 M .
*/
2008-06-14 03:41:42 +04:00
void __init setbat ( int index , unsigned long virt , phys_addr_t phys ,
2005-09-26 10:04:21 +04:00
unsigned int size , int flags )
{
unsigned int bl ;
int wimgxpp ;
2008-06-14 03:41:43 +04:00
struct ppc_bat * bat = BATS [ index ] ;
2005-09-26 10:04:21 +04:00
2009-01-23 09:51:28 +03:00
if ( ( flags & _PAGE_NO_CACHE ) | |
( cpu_has_feature ( CPU_FTR_NEED_COHERENT ) = = 0 ) )
flags & = ~ _PAGE_COHERENT ;
2005-09-26 10:04:21 +04:00
bl = ( size > > 17 ) - 1 ;
if ( PVR_VER ( mfspr ( SPRN_PVR ) ) ! = 1 ) {
/* 603, 604, etc. */
/* Do DBAT first */
wimgxpp = flags & ( _PAGE_WRITETHRU | _PAGE_NO_CACHE
| _PAGE_COHERENT | _PAGE_GUARDED ) ;
wimgxpp | = ( flags & _PAGE_RW ) ? BPP_RW : BPP_RX ;
2008-06-14 03:41:43 +04:00
bat [ 1 ] . batu = virt | ( bl < < 2 ) | 2 ; /* Vs=1, Vp=0 */
bat [ 1 ] . batl = BAT_PHYS_ADDR ( phys ) | wimgxpp ;
2005-09-26 10:04:21 +04:00
if ( flags & _PAGE_USER )
2008-06-14 03:41:43 +04:00
bat [ 1 ] . batu | = 1 ; /* Vp = 1 */
2005-09-26 10:04:21 +04:00
if ( flags & _PAGE_GUARDED ) {
/* G bit must be zero in IBATs */
2008-06-14 03:41:43 +04:00
bat [ 0 ] . batu = bat [ 0 ] . batl = 0 ;
2005-09-26 10:04:21 +04:00
} else {
/* make IBAT same as DBAT */
bat [ 0 ] = bat [ 1 ] ;
}
} else {
/* 601 cpu */
if ( bl > BL_8M )
bl = BL_8M ;
wimgxpp = flags & ( _PAGE_WRITETHRU | _PAGE_NO_CACHE
| _PAGE_COHERENT ) ;
wimgxpp | = ( flags & _PAGE_RW ) ?
( ( flags & _PAGE_USER ) ? PP_RWRW : PP_RWXX ) : PP_RXRX ;
2008-06-14 03:41:43 +04:00
bat - > batu = virt | wimgxpp | 4 ; /* Ks=0, Ku=1 */
bat - > batl = phys | bl | 0x40 ; /* V=1 */
2005-09-26 10:04:21 +04:00
}
bat_addrs [ index ] . start = virt ;
bat_addrs [ index ] . limit = virt + ( ( bl + 1 ) < < 17 ) - 1 ;
bat_addrs [ index ] . phys = phys ;
}
2005-11-07 03:06:55 +03:00
/*
* Preload a translation in the hash table
*/
void hash_preload ( struct mm_struct * mm , unsigned long ea ,
unsigned long access , unsigned long trap )
{
pmd_t * pmd ;
if ( Hash = = 0 )
return ;
2007-05-09 09:20:37 +04:00
pmd = pmd_offset ( pud_offset ( pgd_offset ( mm , ea ) , ea ) , ea ) ;
2005-11-07 03:06:55 +03:00
if ( ! pmd_none ( * pmd ) )
2006-06-11 08:15:17 +04:00
add_hash_page ( mm - > context . id , ea , pmd_val ( * pmd ) ) ;
2005-11-07 03:06:55 +03:00
}
2005-09-26 10:04:21 +04:00
/*
* Initialize the hash table and patch the instructions in hashtable . S .
*/
void __init MMU_init_hw ( void )
{
unsigned int hmask , mb , mb2 ;
unsigned int n_hpteg , lg_n_hpteg ;
extern unsigned int hash_page_patch_A [ ] ;
extern unsigned int hash_page_patch_B [ ] , hash_page_patch_C [ ] ;
extern unsigned int hash_page [ ] ;
extern unsigned int flush_hash_patch_A [ ] , flush_hash_patch_B [ ] ;
2008-12-18 22:13:32 +03:00
if ( ! mmu_has_feature ( MMU_FTR_HPTE_TABLE ) ) {
2005-09-26 10:04:21 +04:00
/*
* Put a blr ( procedure return ) instruction at the
* start of hash_page , since we can still get DSI
* exceptions on a 603.
*/
hash_page [ 0 ] = 0x4e800020 ;
flush_icache_range ( ( unsigned long ) & hash_page [ 0 ] ,
( unsigned long ) & hash_page [ 1 ] ) ;
return ;
}
if ( ppc_md . progress ) ppc_md . progress ( " hash:enter " , 0x105 ) ;
# define LG_HPTEG_SIZE 6 /* 64 bytes per HPTEG */
# define SDR1_LOW_BITS ((n_hpteg - 1) >> 10)
# define MIN_N_HPTEG 1024 /* min 64kB hash table */
/*
* Allow 1 HPTE ( 1 / 8 HPTEG ) for each page of memory .
* This is less than the recommended amount , but then
* Linux ain ' t AIX .
*/
n_hpteg = total_memory / ( PAGE_SIZE * 8 ) ;
if ( n_hpteg < MIN_N_HPTEG )
n_hpteg = MIN_N_HPTEG ;
lg_n_hpteg = __ilog2 ( n_hpteg ) ;
if ( n_hpteg & ( n_hpteg - 1 ) ) {
+ + lg_n_hpteg ; /* round up if not power of 2 */
n_hpteg = 1 < < lg_n_hpteg ;
}
Hash_size = n_hpteg < < LG_HPTEG_SIZE ;
/*
* Find some memory for the hash table .
*/
if ( ppc_md . progress ) ppc_md . progress ( " hash:find piece " , 0x322 ) ;
2005-10-06 06:23:33 +04:00
Hash = __va ( lmb_alloc_base ( Hash_size , Hash_size ,
2008-04-15 23:52:25 +04:00
__initial_memory_limit_addr ) ) ;
2005-09-26 10:04:21 +04:00
cacheable_memzero ( Hash , Hash_size ) ;
_SDR1 = __pa ( Hash ) | SDR1_LOW_BITS ;
2007-06-13 08:52:56 +04:00
Hash_end = ( struct hash_pte * ) ( ( unsigned long ) Hash + Hash_size ) ;
2005-09-26 10:04:21 +04:00
2008-08-01 05:38:39 +04:00
printk ( " Total memory = %lldMB; using %ldkB for hash table (at %p) \n " ,
( unsigned long long ) ( total_memory > > 20 ) , Hash_size > > 10 , Hash ) ;
2005-09-26 10:04:21 +04:00
/*
* Patch up the instructions in hashtable . S : create_hpte
*/
if ( ppc_md . progress ) ppc_md . progress ( " hash:patch " , 0x345 ) ;
Hash_mask = n_hpteg - 1 ;
hmask = Hash_mask > > ( 16 - LG_HPTEG_SIZE ) ;
mb2 = mb = 32 - LG_HPTEG_SIZE - lg_n_hpteg ;
if ( lg_n_hpteg > 16 )
mb2 = 16 - LG_HPTEG_SIZE ;
hash_page_patch_A [ 0 ] = ( hash_page_patch_A [ 0 ] & ~ 0xffff )
| ( ( unsigned int ) ( Hash ) > > 16 ) ;
hash_page_patch_A [ 1 ] = ( hash_page_patch_A [ 1 ] & ~ 0x7c0 ) | ( mb < < 6 ) ;
hash_page_patch_A [ 2 ] = ( hash_page_patch_A [ 2 ] & ~ 0x7c0 ) | ( mb2 < < 6 ) ;
hash_page_patch_B [ 0 ] = ( hash_page_patch_B [ 0 ] & ~ 0xffff ) | hmask ;
hash_page_patch_C [ 0 ] = ( hash_page_patch_C [ 0 ] & ~ 0xffff ) | hmask ;
/*
* Ensure that the locations we ' ve patched have been written
* out from the data cache and invalidated in the instruction
* cache , on those machines with split caches .
*/
flush_icache_range ( ( unsigned long ) & hash_page_patch_A [ 0 ] ,
( unsigned long ) & hash_page_patch_C [ 1 ] ) ;
/*
* Patch up the instructions in hashtable . S : flush_hash_page
*/
flush_hash_patch_A [ 0 ] = ( flush_hash_patch_A [ 0 ] & ~ 0xffff )
| ( ( unsigned int ) ( Hash ) > > 16 ) ;
flush_hash_patch_A [ 1 ] = ( flush_hash_patch_A [ 1 ] & ~ 0x7c0 ) | ( mb < < 6 ) ;
flush_hash_patch_A [ 2 ] = ( flush_hash_patch_A [ 2 ] & ~ 0x7c0 ) | ( mb2 < < 6 ) ;
flush_hash_patch_B [ 0 ] = ( flush_hash_patch_B [ 0 ] & ~ 0xffff ) | hmask ;
flush_icache_range ( ( unsigned long ) & flush_hash_patch_A [ 0 ] ,
( unsigned long ) & flush_hash_patch_B [ 1 ] ) ;
if ( ppc_md . progress ) ppc_md . progress ( " hash:done " , 0x205 ) ;
}