2006-11-23 00:46:54 +01:00
/*
* PS3 pagetable management routines .
*
* Copyright ( C ) 2006 Sony Computer Entertainment Inc .
2007-02-12 00:55:26 -08:00
* Copyright 2006 , 2007 Sony Corporation
2006-11-23 00:46:54 +01:00
*
* 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 ; version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/kernel.h>
# include <asm/machdep.h>
# include <asm/lmb.h>
# include <asm/udbg.h>
# include <asm/lv1call.h>
2007-02-12 00:55:26 -08:00
# include <asm/ps3fb.h>
2006-11-23 00:46:54 +01:00
# include "platform.h"
# if defined(DEBUG)
2007-06-16 07:19:23 +10:00
# define DBG udbg_printf
2006-11-23 00:46:54 +01:00
# else
2007-06-16 07:19:23 +10:00
# define DBG pr_debug
2006-11-23 00:46:54 +01:00
# endif
2007-06-13 14:52:56 +10:00
static struct hash_pte * htab ;
2006-11-23 00:46:54 +01:00
static unsigned long htab_addr ;
static unsigned char * bolttab ;
static unsigned char * inusetab ;
2007-04-29 16:10:39 +00:00
static DEFINE_SPINLOCK ( ps3_bolttab_lock ) ;
2006-11-23 00:46:54 +01:00
# define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \
_debug_dump_hpte ( _a , _b , _c , _d , _e , _f , _g , __func__ , __LINE__ )
static void _debug_dump_hpte ( unsigned long pa , unsigned long va ,
2007-06-13 14:52:56 +10:00
unsigned long group , unsigned long bitmap , struct hash_pte lhpte ,
int psize , unsigned long slot , const char * func , int line )
2006-11-23 00:46:54 +01:00
{
DBG ( " %s:%d: pa = %lxh \n " , func , line , pa ) ;
DBG ( " %s:%d: lpar = %lxh \n " , func , line ,
ps3_mm_phys_to_lpar ( pa ) ) ;
DBG ( " %s:%d: va = %lxh \n " , func , line , va ) ;
DBG ( " %s:%d: group = %lxh \n " , func , line , group ) ;
DBG ( " %s:%d: bitmap = %lxh \n " , func , line , bitmap ) ;
DBG ( " %s:%d: hpte.v = %lxh \n " , func , line , lhpte . v ) ;
DBG ( " %s:%d: hpte.r = %lxh \n " , func , line , lhpte . r ) ;
DBG ( " %s:%d: psize = %xh \n " , func , line , psize ) ;
DBG ( " %s:%d: slot = %lxh \n " , func , line , slot ) ;
}
static long ps3_hpte_insert ( unsigned long hpte_group , unsigned long va ,
2007-10-11 20:37:10 +10:00
unsigned long pa , unsigned long rflags , unsigned long vflags ,
int psize , int ssize )
2006-11-23 00:46:54 +01:00
{
unsigned long slot ;
2007-06-13 14:52:56 +10:00
struct hash_pte lhpte ;
2006-11-23 00:46:54 +01:00
int secondary = 0 ;
unsigned long result ;
unsigned long bitmap ;
unsigned long flags ;
unsigned long p_pteg , s_pteg , b_index , b_mask , cb , ci ;
vflags & = ~ HPTE_V_SECONDARY ; /* this bit is ignored */
2007-10-11 20:37:10 +10:00
lhpte . v = hpte_encode_v ( va , psize , MMU_SEGSIZE_256M ) |
vflags | HPTE_V_VALID ;
2006-11-23 00:46:54 +01:00
lhpte . r = hpte_encode_r ( ps3_mm_phys_to_lpar ( pa ) , psize ) | rflags ;
p_pteg = hpte_group / HPTES_PER_GROUP ;
s_pteg = ~ p_pteg & htab_hash_mask ;
spin_lock_irqsave ( & ps3_bolttab_lock , flags ) ;
BUG_ON ( bolttab [ p_pteg ] = = 0xff & & bolttab [ s_pteg ] = = 0xff ) ;
bitmap = ( inusetab [ p_pteg ] < < 8 ) | inusetab [ s_pteg ] ;
if ( bitmap = = 0xffff ) {
/*
* PTEG is full . Search for victim .
*/
bitmap & = ~ ( ( bolttab [ p_pteg ] < < 8 ) | bolttab [ s_pteg ] ) ;
do {
ci = mftb ( ) & 15 ;
cb = 0x8000UL > > ci ;
} while ( ( cb & bitmap ) = = 0 ) ;
} else {
/*
* search free slot in hardware order
* [ primary ] 0 , 2 , 4 , 6 , 1 , 3 , 5 , 7
* [ secondary ] 0 , 2 , 4 , 6 , 1 , 3 , 5 , 7
*/
for ( ci = 0 ; ci < HPTES_PER_GROUP ; ci + = 2 ) {
cb = 0x8000UL > > ci ;
if ( ( cb & bitmap ) = = 0 )
goto found ;
}
for ( ci = 1 ; ci < HPTES_PER_GROUP ; ci + = 2 ) {
cb = 0x8000UL > > ci ;
if ( ( cb & bitmap ) = = 0 )
goto found ;
}
for ( ci = HPTES_PER_GROUP ; ci < HPTES_PER_GROUP * 2 ; ci + = 2 ) {
cb = 0x8000UL > > ci ;
if ( ( cb & bitmap ) = = 0 )
goto found ;
}
for ( ci = HPTES_PER_GROUP + 1 ; ci < HPTES_PER_GROUP * 2 ; ci + = 2 ) {
cb = 0x8000UL > > ci ;
if ( ( cb & bitmap ) = = 0 )
goto found ;
}
}
found :
if ( ci < HPTES_PER_GROUP ) {
slot = p_pteg * HPTES_PER_GROUP + ci ;
} else {
slot = s_pteg * HPTES_PER_GROUP + ( ci & 7 ) ;
/* lhpte.dw0.dw0.h = 1; */
vflags | = HPTE_V_SECONDARY ;
lhpte . v | = HPTE_V_SECONDARY ;
}
result = lv1_write_htab_entry ( 0 , slot , lhpte . v , lhpte . r ) ;
if ( result ) {
debug_dump_hpte ( pa , va , hpte_group , bitmap , lhpte , psize , slot ) ;
BUG ( ) ;
}
/*
* If used slot is not in primary HPTE group ,
* the slot should be in secondary HPTE group .
*/
if ( ( hpte_group ^ slot ) & ~ ( HPTES_PER_GROUP - 1 ) ) {
secondary = 1 ;
b_index = s_pteg ;
} else {
secondary = 0 ;
b_index = p_pteg ;
}
b_mask = ( lhpte . v & HPTE_V_BOLTED ) ? 1 < < 7 : 0 < < 7 ;
bolttab [ b_index ] | = b_mask > > ( slot & 7 ) ;
b_mask = 1 < < 7 ;
inusetab [ b_index ] | = b_mask > > ( slot & 7 ) ;
spin_unlock_irqrestore ( & ps3_bolttab_lock , flags ) ;
return ( slot & 7 ) | ( secondary < < 3 ) ;
}
static long ps3_hpte_remove ( unsigned long hpte_group )
{
panic ( " ps3_hpte_remove() not implemented " ) ;
return 0 ;
}
static long ps3_hpte_updatepp ( unsigned long slot , unsigned long newpp ,
2007-10-11 20:37:10 +10:00
unsigned long va , int psize , int ssize , int local )
2006-11-23 00:46:54 +01:00
{
unsigned long flags ;
unsigned long result ;
unsigned long pteg , bit ;
unsigned long hpte_v , want_v ;
2007-10-11 20:37:10 +10:00
want_v = hpte_encode_v ( va , psize , MMU_SEGSIZE_256M ) ;
2006-11-23 00:46:54 +01:00
spin_lock_irqsave ( & ps3_bolttab_lock , flags ) ;
hpte_v = htab [ slot ] . v ;
if ( ! HPTE_V_COMPARE ( hpte_v , want_v ) | | ! ( hpte_v & HPTE_V_VALID ) ) {
spin_unlock_irqrestore ( & ps3_bolttab_lock , flags ) ;
/* ps3_hpte_insert() will be used to update PTE */
return - 1 ;
}
result = lv1_write_htab_entry ( 0 , slot , 0 , 0 ) ;
if ( result ) {
DBG ( " %s: va=%lx slot=%lx psize=%d result = %ld (0x%lx) \n " ,
__func__ , va , slot , psize , result , result ) ;
BUG ( ) ;
}
pteg = slot / HPTES_PER_GROUP ;
bit = slot % HPTES_PER_GROUP ;
inusetab [ pteg ] & = ~ ( 0x80 > > bit ) ;
spin_unlock_irqrestore ( & ps3_bolttab_lock , flags ) ;
/* ps3_hpte_insert() will be used to update PTE */
return - 1 ;
}
static void ps3_hpte_updateboltedpp ( unsigned long newpp , unsigned long ea ,
2007-10-11 20:37:10 +10:00
int psize , int ssize )
2006-11-23 00:46:54 +01:00
{
panic ( " ps3_hpte_updateboltedpp() not implemented " ) ;
}
static void ps3_hpte_invalidate ( unsigned long slot , unsigned long va ,
2007-10-11 20:37:10 +10:00
int psize , int ssize , int local )
2006-11-23 00:46:54 +01:00
{
unsigned long flags ;
unsigned long result ;
unsigned long pteg , bit ;
spin_lock_irqsave ( & ps3_bolttab_lock , flags ) ;
result = lv1_write_htab_entry ( 0 , slot , 0 , 0 ) ;
if ( result ) {
DBG ( " %s: va=%lx slot=%lx psize=%d result = %ld (0x%lx) \n " ,
__func__ , va , slot , psize , result , result ) ;
BUG ( ) ;
}
pteg = slot / HPTES_PER_GROUP ;
bit = slot % HPTES_PER_GROUP ;
inusetab [ pteg ] & = ~ ( 0x80 > > bit ) ;
spin_unlock_irqrestore ( & ps3_bolttab_lock , flags ) ;
}
static void ps3_hpte_clear ( void )
{
2007-06-16 07:19:32 +10:00
int result ;
2007-02-12 00:55:26 -08:00
2007-06-16 07:19:32 +10:00
DBG ( " -> %s:%d \n " , __func__ , __LINE__ ) ;
result = lv1_unmap_htab ( htab_addr ) ;
BUG_ON ( result ) ;
ps3_mm_shutdown ( ) ;
ps3_mm_vas_destroy ( ) ;
DBG ( " <- %s:%d \n " , __func__ , __LINE__ ) ;
2006-11-23 00:46:54 +01:00
}
void __init ps3_hpte_init ( unsigned long htab_size )
{
long bitmap_size ;
DBG ( " -> %s:%d \n " , __func__ , __LINE__ ) ;
ppc_md . hpte_invalidate = ps3_hpte_invalidate ;
ppc_md . hpte_updatepp = ps3_hpte_updatepp ;
ppc_md . hpte_updateboltedpp = ps3_hpte_updateboltedpp ;
ppc_md . hpte_insert = ps3_hpte_insert ;
ppc_md . hpte_remove = ps3_hpte_remove ;
ppc_md . hpte_clear_all = ps3_hpte_clear ;
ppc64_pft_size = __ilog2 ( htab_size ) ;
2007-06-13 14:52:56 +10:00
bitmap_size = htab_size / sizeof ( struct hash_pte ) / 8 ;
2006-11-23 00:46:54 +01:00
bolttab = __va ( lmb_alloc ( bitmap_size , 1 ) ) ;
inusetab = __va ( lmb_alloc ( bitmap_size , 1 ) ) ;
memset ( bolttab , 0 , bitmap_size ) ;
memset ( inusetab , 0 , bitmap_size ) ;
DBG ( " <- %s:%d \n " , __func__ , __LINE__ ) ;
}
void __init ps3_map_htab ( void )
{
long result ;
unsigned long htab_size = ( 1UL < < ppc64_pft_size ) ;
result = lv1_map_htab ( 0 , & htab_addr ) ;
2007-06-16 07:18:56 +10:00
htab = ( __force struct hash_pte * ) ioremap_flags ( htab_addr , htab_size ,
2007-06-13 14:52:56 +10:00
pgprot_val ( PAGE_READONLY_X ) ) ;
2006-11-23 00:46:54 +01:00
DBG ( " %s:%d: lpar %016lxh, virt %016lxh \n " , __func__ , __LINE__ ,
htab_addr , ( unsigned long ) htab ) ;
}