2005-04-16 15:20:36 -07:00
/*
* r2300 . c : R2000 and R3000 specific mmu / cache code .
*
* Copyright ( C ) 1996 David S . Miller ( dm @ engr . sgi . com )
*
* with a lot of changes to make this thing work for R3000s
* Tx39XX R4k style caches added . HK
* Copyright ( C ) 1998 , 1999 , 2000 Harald Koerfgen
* Copyright ( C ) 1998 Gleb Raiko & Vladimir Roganov
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <asm/cacheops.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <asm/mmu_context.h>
# include <asm/system.h>
# include <asm/isadep.h>
# include <asm/io.h>
# include <asm/bootinfo.h>
# include <asm/cpu.h>
/* For R3000 cores with R4000 style caches */
static unsigned long icache_size , dcache_size ; /* Size in bytes */
# include <asm/r4kcache.h>
extern int r3k_have_wired_reg ; /* in r3k-tlb.c */
/* This sequence is required to ensure icache is disabled immediately */
# define TX39_STOP_STREAMING() \
__asm__ __volatile__ ( \
" .set push \n \t " \
" .set noreorder \n \t " \
" b 1f \n \t " \
" nop \n \t " \
" 1: \n \t " \
" .set pop " \
)
/* TX39H-style cache flush routines. */
static void tx39h_flush_icache_all ( void )
{
unsigned long flags , config ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
2006-02-10 00:39:06 +09:00
blast_icache16 ( ) ;
2005-04-16 15:20:36 -07:00
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
static void tx39h_dma_cache_wback_inv ( unsigned long addr , unsigned long size )
{
/* Catch bad driver code */
BUG_ON ( size = = 0 ) ;
iob ( ) ;
2006-02-10 00:39:06 +09:00
blast_inv_dcache_range ( addr , addr + size ) ;
2005-04-16 15:20:36 -07:00
}
/* TX39H2,TX39H3 */
static inline void tx39_blast_dcache_page ( unsigned long addr )
{
2007-10-11 23:46:15 +01:00
if ( current_cpu_type ( ) ! = CPU_TX3912 )
2005-04-16 15:20:36 -07:00
blast_dcache16_page ( addr ) ;
}
static inline void tx39_blast_dcache_page_indexed ( unsigned long addr )
{
blast_dcache16_page_indexed ( addr ) ;
}
static inline void tx39_blast_dcache ( void )
{
blast_dcache16 ( ) ;
}
static inline void tx39_blast_icache_page ( unsigned long addr )
{
unsigned long flags , config ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
blast_icache16_page ( addr ) ;
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
static inline void tx39_blast_icache_page_indexed ( unsigned long addr )
{
unsigned long flags , config ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
blast_icache16_page_indexed ( addr ) ;
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
static inline void tx39_blast_icache ( void )
{
unsigned long flags , config ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
blast_icache16 ( ) ;
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
2008-04-05 15:13:23 +01:00
static void tx39__flush_cache_vmap ( void )
{
tx39_blast_dcache ( ) ;
}
static void tx39__flush_cache_vunmap ( void )
{
tx39_blast_dcache ( ) ;
}
2005-04-16 15:20:36 -07:00
static inline void tx39_flush_cache_all ( void )
{
if ( ! cpu_has_dc_aliases )
return ;
tx39_blast_dcache ( ) ;
}
static inline void tx39___flush_cache_all ( void )
{
tx39_blast_dcache ( ) ;
tx39_blast_icache ( ) ;
}
static void tx39_flush_cache_mm ( struct mm_struct * mm )
{
if ( ! cpu_has_dc_aliases )
return ;
2007-03-05 00:41:39 +09:00
if ( cpu_context ( smp_processor_id ( ) , mm ) ! = 0 )
tx39_blast_dcache ( ) ;
2005-04-16 15:20:36 -07:00
}
static void tx39_flush_cache_range ( struct vm_area_struct * vma ,
unsigned long start , unsigned long end )
{
2007-03-05 00:41:39 +09:00
if ( ! cpu_has_dc_aliases )
return ;
2005-09-28 20:24:58 +09:00
if ( ! ( cpu_context ( smp_processor_id ( ) , vma - > vm_mm ) ) )
2005-04-16 15:20:36 -07:00
return ;
2007-03-05 00:41:39 +09:00
tx39_blast_dcache ( ) ;
2005-04-16 15:20:36 -07:00
}
static void tx39_flush_cache_page ( struct vm_area_struct * vma , unsigned long page , unsigned long pfn )
{
int exec = vma - > vm_flags & VM_EXEC ;
struct mm_struct * mm = vma - > vm_mm ;
pgd_t * pgdp ;
2005-02-10 12:19:59 +00:00
pud_t * pudp ;
2005-04-16 15:20:36 -07:00
pmd_t * pmdp ;
pte_t * ptep ;
/*
* If ownes no valid ASID yet , cannot possibly have gotten
* this page into the cache .
*/
if ( cpu_context ( smp_processor_id ( ) , mm ) = = 0 )
return ;
page & = PAGE_MASK ;
pgdp = pgd_offset ( mm , page ) ;
2005-02-10 12:19:59 +00:00
pudp = pud_offset ( pgdp , page ) ;
pmdp = pmd_offset ( pudp , page ) ;
2005-04-16 15:20:36 -07:00
ptep = pte_offset ( pmdp , page ) ;
/*
* If the page isn ' t marked valid , the page cannot possibly be
* in the cache .
*/
if ( ! ( pte_val ( * ptep ) & _PAGE_PRESENT ) )
return ;
/*
* Doing flushes for another ASID than the current one is
* too difficult since stupid R4k caches do a TLB translation
* for every cache flush operation . So we do indexed flushes
* in that case , which doesn ' t overly flush the cache too much .
*/
if ( ( mm = = current - > active_mm ) & & ( pte_val ( * ptep ) & _PAGE_VALID ) ) {
if ( cpu_has_dc_aliases | | exec )
tx39_blast_dcache_page ( page ) ;
if ( exec )
tx39_blast_icache_page ( page ) ;
return ;
}
/*
* Do indexed flush , too much work to get the ( possible ) TLB refills
* to work correctly .
*/
if ( cpu_has_dc_aliases | | exec )
tx39_blast_dcache_page_indexed ( page ) ;
if ( exec )
tx39_blast_icache_page_indexed ( page ) ;
}
2006-04-05 20:42:04 +01:00
static void local_tx39_flush_data_cache_page ( void * addr )
{
2007-03-05 00:41:39 +09:00
tx39_blast_dcache_page ( ( unsigned long ) addr ) ;
2006-04-05 20:42:04 +01:00
}
2005-04-16 15:20:36 -07:00
static void tx39_flush_data_cache_page ( unsigned long addr )
{
tx39_blast_dcache_page ( addr ) ;
}
static void tx39_flush_icache_range ( unsigned long start , unsigned long end )
{
if ( end - start > dcache_size )
tx39_blast_dcache ( ) ;
2006-02-10 00:39:06 +09:00
else
protected_blast_dcache_range ( start , end ) ;
2005-04-16 15:20:36 -07:00
if ( end - start > icache_size )
tx39_blast_icache ( ) ;
else {
unsigned long flags , config ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
2006-02-10 00:39:06 +09:00
protected_blast_icache_range ( start , end ) ;
2005-04-16 15:20:36 -07:00
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
}
static void tx39_dma_cache_wback_inv ( unsigned long addr , unsigned long size )
{
2006-02-10 00:39:06 +09:00
unsigned long end ;
2005-04-16 15:20:36 -07:00
if ( ( ( size | addr ) & ( PAGE_SIZE - 1 ) ) = = 0 ) {
end = addr + size ;
do {
tx39_blast_dcache_page ( addr ) ;
addr + = PAGE_SIZE ;
} while ( addr ! = end ) ;
} else if ( size > dcache_size ) {
tx39_blast_dcache ( ) ;
} else {
2006-02-10 00:39:06 +09:00
blast_dcache_range ( addr , addr + size ) ;
2005-04-16 15:20:36 -07:00
}
}
static void tx39_dma_cache_inv ( unsigned long addr , unsigned long size )
{
2006-02-10 00:39:06 +09:00
unsigned long end ;
2005-04-16 15:20:36 -07:00
if ( ( ( size | addr ) & ( PAGE_SIZE - 1 ) ) = = 0 ) {
end = addr + size ;
do {
tx39_blast_dcache_page ( addr ) ;
addr + = PAGE_SIZE ;
} while ( addr ! = end ) ;
} else if ( size > dcache_size ) {
tx39_blast_dcache ( ) ;
} else {
2006-02-10 00:39:06 +09:00
blast_inv_dcache_range ( addr , addr + size ) ;
2005-04-16 15:20:36 -07:00
}
}
static void tx39_flush_cache_sigtramp ( unsigned long addr )
{
unsigned long ic_lsize = current_cpu_data . icache . linesz ;
unsigned long dc_lsize = current_cpu_data . dcache . linesz ;
unsigned long config ;
unsigned long flags ;
protected_writeback_dcache_line ( addr & ~ ( dc_lsize - 1 ) ) ;
/* disable icache (set ICE#) */
local_irq_save ( flags ) ;
config = read_c0_conf ( ) ;
write_c0_conf ( config & ~ TX39_CONF_ICE ) ;
TX39_STOP_STREAMING ( ) ;
protected_flush_icache_line ( addr & ~ ( ic_lsize - 1 ) ) ;
write_c0_conf ( config ) ;
local_irq_restore ( flags ) ;
}
static __init void tx39_probe_cache ( void )
{
unsigned long config ;
config = read_c0_conf ( ) ;
icache_size = 1 < < ( 10 + ( ( config & TX39_CONF_ICS_MASK ) > >
TX39_CONF_ICS_SHIFT ) ) ;
dcache_size = 1 < < ( 10 + ( ( config & TX39_CONF_DCS_MASK ) > >
TX39_CONF_DCS_SHIFT ) ) ;
current_cpu_data . icache . linesz = 16 ;
2007-10-11 23:46:15 +01:00
switch ( current_cpu_type ( ) ) {
2005-04-16 15:20:36 -07:00
case CPU_TX3912 :
current_cpu_data . icache . ways = 1 ;
current_cpu_data . dcache . ways = 1 ;
current_cpu_data . dcache . linesz = 4 ;
break ;
case CPU_TX3927 :
current_cpu_data . icache . ways = 2 ;
current_cpu_data . dcache . ways = 2 ;
current_cpu_data . dcache . linesz = 16 ;
break ;
case CPU_TX3922 :
default :
current_cpu_data . icache . ways = 1 ;
current_cpu_data . dcache . ways = 1 ;
current_cpu_data . dcache . linesz = 16 ;
break ;
}
}
2008-03-08 09:56:28 +00:00
void __cpuinit tx39_cache_init ( void )
2005-04-16 15:20:36 -07:00
{
extern void build_clear_page ( void ) ;
extern void build_copy_page ( void ) ;
unsigned long config ;
config = read_c0_conf ( ) ;
config & = ~ TX39_CONF_WBON ;
write_c0_conf ( config ) ;
tx39_probe_cache ( ) ;
2007-10-11 23:46:15 +01:00
switch ( current_cpu_type ( ) ) {
2005-04-16 15:20:36 -07:00
case CPU_TX3912 :
/* TX39/H core (writethru direct-map cache) */
2008-04-05 15:13:23 +01:00
__flush_cache_vmap = tx39__flush_cache_vmap ;
__flush_cache_vunmap = tx39__flush_cache_vunmap ;
2005-04-16 15:20:36 -07:00
flush_cache_all = tx39h_flush_icache_all ;
__flush_cache_all = tx39h_flush_icache_all ;
flush_cache_mm = ( void * ) tx39h_flush_icache_all ;
flush_cache_range = ( void * ) tx39h_flush_icache_all ;
flush_cache_page = ( void * ) tx39h_flush_icache_all ;
flush_icache_range = ( void * ) tx39h_flush_icache_all ;
2008-08-04 20:53:57 +02:00
local_flush_icache_range = ( void * ) tx39h_flush_icache_all ;
2005-04-16 15:20:36 -07:00
flush_cache_sigtramp = ( void * ) tx39h_flush_icache_all ;
2006-04-05 20:42:04 +01:00
local_flush_data_cache_page = ( void * ) tx39h_flush_icache_all ;
2005-04-16 15:20:36 -07:00
flush_data_cache_page = ( void * ) tx39h_flush_icache_all ;
_dma_cache_wback_inv = tx39h_dma_cache_wback_inv ;
shm_align_mask = PAGE_SIZE - 1 ;
break ;
case CPU_TX3922 :
case CPU_TX3927 :
default :
/* TX39/H2,H3 core (writeback 2way-set-associative cache) */
r3k_have_wired_reg = 1 ;
write_c0_wired ( 0 ) ; /* set 8 on reset... */
/* board-dependent init code may set WBON */
2008-04-05 15:13:23 +01:00
__flush_cache_vmap = tx39__flush_cache_vmap ;
__flush_cache_vunmap = tx39__flush_cache_vunmap ;
2005-04-16 15:20:36 -07:00
flush_cache_all = tx39_flush_cache_all ;
__flush_cache_all = tx39___flush_cache_all ;
flush_cache_mm = tx39_flush_cache_mm ;
flush_cache_range = tx39_flush_cache_range ;
flush_cache_page = tx39_flush_cache_page ;
flush_icache_range = tx39_flush_icache_range ;
2008-08-26 22:30:41 +09:00
local_flush_icache_range = tx39_flush_icache_range ;
2005-04-16 15:20:36 -07:00
flush_cache_sigtramp = tx39_flush_cache_sigtramp ;
2006-04-05 20:42:04 +01:00
local_flush_data_cache_page = local_tx39_flush_data_cache_page ;
2005-04-16 15:20:36 -07:00
flush_data_cache_page = tx39_flush_data_cache_page ;
_dma_cache_wback_inv = tx39_dma_cache_wback_inv ;
_dma_cache_wback = tx39_dma_cache_wback_inv ;
_dma_cache_inv = tx39_dma_cache_inv ;
shm_align_mask = max_t ( unsigned long ,
( dcache_size / current_cpu_data . dcache . ways ) - 1 ,
PAGE_SIZE - 1 ) ;
break ;
}
current_cpu_data . icache . waysize = icache_size / current_cpu_data . icache . ways ;
current_cpu_data . dcache . waysize = dcache_size / current_cpu_data . dcache . ways ;
current_cpu_data . icache . sets =
current_cpu_data . icache . waysize / current_cpu_data . icache . linesz ;
current_cpu_data . dcache . sets =
current_cpu_data . dcache . waysize / current_cpu_data . dcache . linesz ;
if ( current_cpu_data . dcache . waysize > PAGE_SIZE )
current_cpu_data . dcache . flags | = MIPS_CACHE_ALIASES ;
current_cpu_data . icache . waybit = 0 ;
current_cpu_data . dcache . waybit = 0 ;
printk ( " Primary instruction cache %ldkB, linesize %d bytes \n " ,
icache_size > > 10 , current_cpu_data . icache . linesz ) ;
printk ( " Primary data cache %ldkB, linesize %d bytes \n " ,
dcache_size > > 10 , current_cpu_data . dcache . linesz ) ;
build_clear_page ( ) ;
build_copy_page ( ) ;
2005-07-15 15:23:23 +00:00
tx39h_flush_icache_all ( ) ;
2005-04-16 15:20:36 -07:00
}