2005-09-26 10:04:21 +04:00
/*
* Modifications by Matt Porter ( mporter @ mvista . com ) to support
* PPC44x Book E processors .
*
* This file contains the routines for initializing the MMU
* on the 4 xx series of chips .
* - - 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/init.h>
2010-07-07 02:39:02 +04:00
# include <linux/memblock.h>
2005-09-26 10:04:21 +04:00
# include <asm/mmu.h>
2007-04-30 08:06:25 +04:00
# include <asm/page.h>
2008-07-08 09:54:40 +04:00
# include <asm/cacheflush.h>
2005-09-26 10:04:21 +04:00
# include "mmu_decl.h"
/* Used by the 44x TLB replacement exception handler.
* Just needed it declared someplace .
*/
2007-04-30 08:06:25 +04:00
unsigned int tlb_44x_index ; /* = 0 */
unsigned int tlb_44x_hwater = PPC44x_TLB_SIZE - 1 - PPC44x_EARLY_TLBS ;
2007-10-31 08:42:19 +03:00
int icache_44x_need_flush ;
2005-09-26 10:04:21 +04:00
2010-03-05 13:43:12 +03:00
unsigned long tlb_47x_boltmap [ 1024 / 8 ] ;
2013-06-24 23:30:09 +04:00
static void ppc44x_update_tlb_hwater ( void )
2008-07-08 09:54:40 +04:00
{
extern unsigned int tlb_44x_patch_hwater_D [ ] ;
extern unsigned int tlb_44x_patch_hwater_I [ ] ;
/* The TLB miss handlers hard codes the watermark in a cmpli
* instruction to improve performances rather than loading it
* from the global variable . Thus , we patch the instructions
* in the 2 TLB miss handlers when updating the value
*/
tlb_44x_patch_hwater_D [ 0 ] = ( tlb_44x_patch_hwater_D [ 0 ] & 0xffff0000 ) |
tlb_44x_hwater ;
flush_icache_range ( ( unsigned long ) & tlb_44x_patch_hwater_D [ 0 ] ,
( unsigned long ) & tlb_44x_patch_hwater_D [ 1 ] ) ;
tlb_44x_patch_hwater_I [ 0 ] = ( tlb_44x_patch_hwater_I [ 0 ] & 0xffff0000 ) |
tlb_44x_hwater ;
flush_icache_range ( ( unsigned long ) & tlb_44x_patch_hwater_I [ 0 ] ,
( unsigned long ) & tlb_44x_patch_hwater_I [ 1 ] ) ;
}
2005-09-26 10:04:21 +04:00
/*
2010-03-05 13:43:12 +03:00
* " Pins " a 256 MB TLB entry in AS0 for kernel lowmem for 44 x type MMU
2005-09-26 10:04:21 +04:00
*/
2007-04-30 08:06:25 +04:00
static void __init ppc44x_pin_tlb ( unsigned int virt , unsigned int phys )
2005-09-26 10:04:21 +04:00
{
2008-07-08 09:54:40 +04:00
unsigned int entry = tlb_44x_hwater - - ;
ppc44x_update_tlb_hwater ( ) ;
2010-03-05 13:43:12 +03:00
mtspr ( SPRN_MMUCR , 0 ) ;
2007-04-30 08:06:25 +04:00
__asm__ __volatile__ (
" tlbwe %2,%3,%4 \n "
" tlbwe %1,%3,%5 \n "
" tlbwe %0,%3,%6 \n "
2005-09-26 10:04:21 +04:00
:
2007-04-30 08:06:25 +04:00
: " r " ( PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G ) ,
" r " ( phys ) ,
" r " ( virt | PPC44x_TLB_VALID | PPC44x_TLB_256M ) ,
2008-07-08 09:54:40 +04:00
" r " ( entry ) ,
2005-09-26 10:04:21 +04:00
" i " ( PPC44x_TLB_PAGEID ) ,
" i " ( PPC44x_TLB_XLAT ) ,
" i " ( PPC44x_TLB_ATTRIB ) ) ;
}
2010-03-05 13:43:12 +03:00
static int __init ppc47x_find_free_bolted ( void )
{
unsigned int mmube0 = mfspr ( SPRN_MMUBE0 ) ;
unsigned int mmube1 = mfspr ( SPRN_MMUBE1 ) ;
if ( ! ( mmube0 & MMUBE0_VBE0 ) )
return 0 ;
if ( ! ( mmube0 & MMUBE0_VBE1 ) )
return 1 ;
if ( ! ( mmube0 & MMUBE0_VBE2 ) )
return 2 ;
if ( ! ( mmube1 & MMUBE1_VBE3 ) )
return 3 ;
if ( ! ( mmube1 & MMUBE1_VBE4 ) )
return 4 ;
if ( ! ( mmube1 & MMUBE1_VBE5 ) )
return 5 ;
return - 1 ;
}
static void __init ppc47x_update_boltmap ( void )
{
unsigned int mmube0 = mfspr ( SPRN_MMUBE0 ) ;
unsigned int mmube1 = mfspr ( SPRN_MMUBE1 ) ;
if ( mmube0 & MMUBE0_VBE0 )
__set_bit ( ( mmube0 > > MMUBE0_IBE0_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
if ( mmube0 & MMUBE0_VBE1 )
__set_bit ( ( mmube0 > > MMUBE0_IBE1_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
if ( mmube0 & MMUBE0_VBE2 )
__set_bit ( ( mmube0 > > MMUBE0_IBE2_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
if ( mmube1 & MMUBE1_VBE3 )
__set_bit ( ( mmube1 > > MMUBE1_IBE3_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
if ( mmube1 & MMUBE1_VBE4 )
__set_bit ( ( mmube1 > > MMUBE1_IBE4_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
if ( mmube1 & MMUBE1_VBE5 )
__set_bit ( ( mmube1 > > MMUBE1_IBE5_SHIFT ) & 0xff ,
tlb_47x_boltmap ) ;
}
/*
* " Pins " a 256 MB TLB entry in AS0 for kernel lowmem for 47 x type MMU
*/
2013-06-24 23:30:09 +04:00
static void ppc47x_pin_tlb ( unsigned int virt , unsigned int phys )
2010-03-05 13:43:12 +03:00
{
unsigned int rA ;
int bolted ;
/* Base rA is HW way select, way 0, bolted bit set */
rA = 0x88000000 ;
/* Look for a bolted entry slot */
bolted = ppc47x_find_free_bolted ( ) ;
BUG_ON ( bolted < 0 ) ;
/* Insert bolted slot number */
rA | = bolted < < 24 ;
pr_debug ( " 256M TLB entry for 0x%08x->0x%08x in bolt slot %d \n " ,
virt , phys , bolted ) ;
mtspr ( SPRN_MMUCR , 0 ) ;
__asm__ __volatile__ (
" tlbwe %2,%3,0 \n "
" tlbwe %1,%3,1 \n "
" tlbwe %0,%3,2 \n "
:
: " r " ( PPC47x_TLB2_SW | PPC47x_TLB2_SR |
PPC47x_TLB2_SX
# ifdef CONFIG_SMP
| PPC47x_TLB2_M
# endif
) ,
" r " ( phys ) ,
" r " ( virt | PPC47x_TLB0_VALID | PPC47x_TLB0_256M ) ,
" r " ( rA ) ) ;
}
2005-09-26 10:04:21 +04:00
void __init MMU_init_hw ( void )
{
2010-03-05 13:43:12 +03:00
/* This is not useful on 47x but won't hurt either */
2008-07-08 09:54:40 +04:00
ppc44x_update_tlb_hwater ( ) ;
2005-09-26 10:04:21 +04:00
flush_instruction_cache ( ) ;
}
2009-12-14 19:04:24 +03:00
unsigned long __init mmu_mapin_ram ( unsigned long top )
2005-09-26 10:04:21 +04:00
{
2007-04-30 08:06:25 +04:00
unsigned long addr ;
2011-07-04 22:36:57 +04:00
unsigned long memstart = memstart_addr & ~ ( PPC_PIN_SIZE - 1 ) ;
2005-09-26 10:04:21 +04:00
2007-04-30 08:06:25 +04:00
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256 M mapping established in head_44x . S */
2011-07-04 22:36:57 +04:00
for ( addr = memstart + PPC_PIN_SIZE ; addr < lowmem_end_addr ;
2010-03-05 13:43:12 +03:00
addr + = PPC_PIN_SIZE ) {
if ( mmu_has_feature ( MMU_FTR_TYPE_47x ) )
ppc47x_pin_tlb ( addr + PAGE_OFFSET , addr ) ;
else
ppc44x_pin_tlb ( addr + PAGE_OFFSET , addr ) ;
}
if ( mmu_has_feature ( MMU_FTR_TYPE_47x ) ) {
ppc47x_update_boltmap ( ) ;
2005-09-26 10:04:21 +04:00
2010-03-05 13:43:12 +03:00
# ifdef DEBUG
{
int i ;
printk ( KERN_DEBUG " bolted entries: " ) ;
for ( i = 0 ; i < 255 ; i + + ) {
if ( test_bit ( i , tlb_47x_boltmap ) )
printk ( " %d " , i ) ;
}
printk ( " \n " ) ;
}
# endif /* DEBUG */
}
2005-09-26 10:04:21 +04:00
return total_lowmem ;
}
2010-03-05 13:43:12 +03:00
2010-07-07 02:39:02 +04:00
void setup_initial_memory_limit ( phys_addr_t first_memblock_base ,
phys_addr_t first_memblock_size )
{
2011-07-04 22:36:57 +04:00
u64 size ;
2011-12-15 02:57:15 +04:00
# ifndef CONFIG_NONSTATIC_KERNEL
2010-07-07 02:39:02 +04:00
/* We don't currently support the first MEMBLOCK not mapping 0
* physical on those processors
*/
BUG_ON ( first_memblock_base ! = 0 ) ;
2011-07-04 22:36:57 +04:00
# endif
2010-07-07 02:39:02 +04:00
/* 44x has a 256M TLB entry pinned at boot */
2011-07-04 22:36:57 +04:00
size = ( min_t ( u64 , first_memblock_size , PPC_PIN_SIZE ) ) ;
memblock_set_current_limit ( first_memblock_base + size ) ;
2010-07-07 02:39:02 +04:00
}
2010-03-05 13:43:12 +03:00
# ifdef CONFIG_SMP
2013-06-24 23:30:09 +04:00
void mmu_init_secondary ( int cpu )
2010-03-05 13:43:12 +03:00
{
unsigned long addr ;
2011-07-04 22:36:57 +04:00
unsigned long memstart = memstart_addr & ~ ( PPC_PIN_SIZE - 1 ) ;
2010-03-05 13:43:12 +03:00
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256 M mapping established in head_44x . S
*
* WARNING : This is called with only the first 256 M of the
* linear mapping in the TLB and we can ' t take faults yet
* so beware of what this code uses . It runs off a temporary
* stack . current ( r2 ) isn ' t initialized , smp_processor_id ( )
* will not work , current thread info isn ' t accessible , . . .
*/
2011-07-04 22:36:57 +04:00
for ( addr = memstart + PPC_PIN_SIZE ; addr < lowmem_end_addr ;
2010-03-05 13:43:12 +03:00
addr + = PPC_PIN_SIZE ) {
if ( mmu_has_feature ( MMU_FTR_TYPE_47x ) )
ppc47x_pin_tlb ( addr + PAGE_OFFSET , addr ) ;
else
ppc44x_pin_tlb ( addr + PAGE_OFFSET , addr ) ;
}
}
# endif /* CONFIG_SMP */