2005-09-26 10:04:21 +04:00
/*
2005-11-14 03:06:30 +03:00
* Modifications by Kumar Gala ( galak @ kernel . crashing . org ) to support
2005-09-26 10:04:21 +04:00
* E500 Book E processors .
*
* Copyright 2004 Freescale Semiconductor , Inc
*
* 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/signal.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/ptrace.h>
# include <linux/mman.h>
# include <linux/mm.h>
# include <linux/swap.h>
# include <linux/stddef.h>
# include <linux/vmalloc.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/highmem.h>
# include <asm/pgalloc.h>
# include <asm/prom.h>
# include <asm/io.h>
# include <asm/mmu_context.h>
# include <asm/pgtable.h>
# include <asm/mmu.h>
# include <asm/uaccess.h>
# include <asm/smp.h>
# include <asm/machdep.h>
# include <asm/setup.h>
2008-04-15 23:52:21 +04:00
# include "mmu_decl.h"
2005-09-26 10:04:21 +04:00
extern void loadcam_entry ( unsigned int index ) ;
unsigned int tlbcam_index ;
2008-12-09 06:34:58 +03:00
static unsigned long cam [ CONFIG_LOWMEM_CAM_NUM ] ;
2005-09-26 10:04:21 +04:00
# define NUM_TLBCAMS (16)
2008-12-09 06:34:58 +03:00
# if defined(CONFIG_LOWMEM_CAM_NUM_BOOL) && (CONFIG_LOWMEM_CAM_NUM >= NUM_TLBCAMS)
# error "LOWMEM_CAM_NUM must be less than NUM_TLBCAMS"
# endif
2008-12-09 06:34:55 +03:00
struct tlbcam TLBCAM [ NUM_TLBCAMS ] ;
2005-09-26 10:04:21 +04:00
struct tlbcamrange {
unsigned long start ;
unsigned long limit ;
phys_addr_t phys ;
} tlbcam_addrs [ NUM_TLBCAMS ] ;
extern unsigned int tlbcam_index ;
/*
* Return PA for this VA if it is mapped by a CAM , or 0
*/
2009-02-10 06:08:07 +03:00
phys_addr_t v_mapped_by_tlbcam ( unsigned long va )
2005-09-26 10:04:21 +04:00
{
int b ;
for ( b = 0 ; b < tlbcam_index ; + + b )
if ( va > = tlbcam_addrs [ b ] . start & & va < tlbcam_addrs [ b ] . limit )
return tlbcam_addrs [ b ] . phys + ( va - tlbcam_addrs [ b ] . start ) ;
return 0 ;
}
/*
* Return VA for a given PA or 0 if not mapped
*/
2009-02-10 06:08:07 +03:00
unsigned long p_mapped_by_tlbcam ( phys_addr_t pa )
2005-09-26 10:04:21 +04:00
{
int b ;
for ( b = 0 ; b < tlbcam_index ; + + b )
if ( pa > = tlbcam_addrs [ b ] . phys
& & pa < ( tlbcam_addrs [ b ] . limit - tlbcam_addrs [ b ] . start )
+ tlbcam_addrs [ b ] . phys )
return tlbcam_addrs [ b ] . start + ( pa - tlbcam_addrs [ b ] . phys ) ;
return 0 ;
}
/*
* 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 4 between 4 k and 256 M .
*/
void settlbcam ( int index , unsigned long virt , phys_addr_t phys ,
unsigned int size , int flags , unsigned int pid )
{
unsigned int tsize , lz ;
asm ( " cntlzw %0,%1 " : " =r " ( lz ) : " r " ( size ) ) ;
2009-02-11 03:10:50 +03:00
tsize = 21 - lz ;
2005-09-26 10:04:21 +04:00
# ifdef CONFIG_SMP
if ( ( flags & _PAGE_NO_CACHE ) = = 0 )
flags | = _PAGE_COHERENT ;
# endif
TLBCAM [ index ] . MAS0 = MAS0_TLBSEL ( 1 ) | MAS0_ESEL ( index ) | MAS0_NV ( index + 1 ) ;
TLBCAM [ index ] . MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE ( tsize ) | MAS1_TID ( pid ) ;
TLBCAM [ index ] . MAS2 = virt & PAGE_MASK ;
TLBCAM [ index ] . MAS2 | = ( flags & _PAGE_WRITETHRU ) ? MAS2_W : 0 ;
TLBCAM [ index ] . MAS2 | = ( flags & _PAGE_NO_CACHE ) ? MAS2_I : 0 ;
TLBCAM [ index ] . MAS2 | = ( flags & _PAGE_COHERENT ) ? MAS2_M : 0 ;
TLBCAM [ index ] . MAS2 | = ( flags & _PAGE_GUARDED ) ? MAS2_G : 0 ;
TLBCAM [ index ] . MAS2 | = ( flags & _PAGE_ENDIAN ) ? MAS2_E : 0 ;
TLBCAM [ index ] . MAS3 = ( phys & PAGE_MASK ) | MAS3_SX | MAS3_SR ;
TLBCAM [ index ] . MAS3 | = ( ( flags & _PAGE_RW ) ? MAS3_SW : 0 ) ;
# ifndef CONFIG_KGDB /* want user access for breakpoints */
if ( flags & _PAGE_USER ) {
TLBCAM [ index ] . MAS3 | = MAS3_UX | MAS3_UR ;
TLBCAM [ index ] . MAS3 | = ( ( flags & _PAGE_RW ) ? MAS3_UW : 0 ) ;
}
# else
TLBCAM [ index ] . MAS3 | = MAS3_UX | MAS3_UR ;
TLBCAM [ index ] . MAS3 | = ( ( flags & _PAGE_RW ) ? MAS3_UW : 0 ) ;
# endif
tlbcam_addrs [ index ] . start = virt ;
tlbcam_addrs [ index ] . limit = virt + size - 1 ;
tlbcam_addrs [ index ] . phys = phys ;
loadcam_entry ( index ) ;
}
void invalidate_tlbcam_entry ( int index )
{
TLBCAM [ index ] . MAS0 = MAS0_TLBSEL ( 1 ) | MAS0_ESEL ( index ) ;
TLBCAM [ index ] . MAS1 = ~ MAS1_VALID ;
loadcam_entry ( index ) ;
}
2008-12-09 06:34:57 +03:00
unsigned long __init mmu_mapin_ram ( void )
2005-09-26 10:04:21 +04:00
{
2008-12-09 06:34:57 +03:00
unsigned long virt = PAGE_OFFSET ;
phys_addr_t phys = memstart_addr ;
while ( cam [ tlbcam_index ] & & tlbcam_index < ARRAY_SIZE ( cam ) ) {
2009-03-19 22:34:08 +03:00
settlbcam ( tlbcam_index , virt , phys , cam [ tlbcam_index ] , PAGE_KERNEL_X , 0 ) ;
2008-12-09 06:34:57 +03:00
virt + = cam [ tlbcam_index ] ;
phys + = cam [ tlbcam_index ] ;
2005-09-26 10:04:21 +04:00
tlbcam_index + + ;
}
2008-12-09 06:34:57 +03:00
return virt - PAGE_OFFSET ;
2005-09-26 10:04:21 +04:00
}
/*
* MMU_init_hw does the chip - specific initialization of the MMU hardware .
*/
void __init MMU_init_hw ( void )
{
flush_instruction_cache ( ) ;
}
void __init
adjust_total_lowmem ( void )
{
2008-04-15 23:52:23 +04:00
phys_addr_t ram ;
2008-12-09 06:34:59 +03:00
unsigned int max_cam = ( mfspr ( SPRN_TLB1CFG ) > > 16 ) & 0xff ;
2008-12-09 06:34:57 +03:00
char buf [ ARRAY_SIZE ( cam ) * 5 + 1 ] , * p = buf ;
int i ;
2008-12-09 06:34:59 +03:00
unsigned long virt = PAGE_OFFSET & 0xffffffffUL ;
unsigned long phys = memstart_addr & 0xffffffffUL ;
2005-09-26 10:04:21 +04:00
2008-12-09 06:34:59 +03:00
/* Convert (4^max) kB to (2^max) bytes */
max_cam = max_cam * 2 + 10 ;
2005-09-26 10:04:21 +04:00
2008-12-09 06:34:57 +03:00
/* adjust lowmem size to __max_low_memory */
ram = min ( ( phys_addr_t ) __max_low_memory , ( phys_addr_t ) total_lowmem ) ;
2005-09-26 10:04:21 +04:00
/* Calculate CAM values */
2008-12-09 06:34:57 +03:00
__max_low_memory = 0 ;
for ( i = 0 ; ram & & i < ARRAY_SIZE ( cam ) ; i + + ) {
unsigned int camsize = __ilog2 ( ram ) & ~ 1U ;
2008-12-09 06:34:59 +03:00
unsigned int align = __ffs ( virt | phys ) & ~ 1U ;
if ( camsize > align )
camsize = align ;
2008-12-09 06:34:57 +03:00
if ( camsize > max_cam )
camsize = max_cam ;
2008-12-09 06:34:59 +03:00
2008-12-09 06:34:57 +03:00
cam [ i ] = 1UL < < camsize ;
ram - = cam [ i ] ;
__max_low_memory + = cam [ i ] ;
2008-12-09 06:34:59 +03:00
virt + = cam [ i ] ;
phys + = cam [ i ] ;
2008-12-09 06:34:57 +03:00
p + = sprintf ( p , " %lu/ " , cam [ i ] > > 20 ) ;
2005-09-26 10:04:21 +04:00
}
2008-12-09 06:34:57 +03:00
for ( ; i < ARRAY_SIZE ( cam ) ; i + + )
p + = sprintf ( p , " 0/ " ) ;
p [ - 1 ] = ' \0 ' ;
2005-09-26 10:04:21 +04:00
2009-02-12 18:39:23 +03:00
pr_info ( " Memory CAM mapping: %s Mb, residual: %dMb \n " , buf ,
( unsigned int ) ( ( total_lowmem - __max_low_memory ) > > 20 ) ) ;
2008-04-15 23:52:25 +04:00
__initial_memory_limit_addr = memstart_addr + __max_low_memory ;
2005-09-26 10:04:21 +04:00
}