2005-04-17 02:20:36 +04:00
/*
* sc - rm7k . c : RM7000 cache management functions .
*
* Copyright ( C ) 1997 , 2001 , 2003 , 2004 Ralf Baechle ( ralf @ linux - mips . org )
*/
# undef DEBUG
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/mm.h>
2006-02-15 12:25:48 +03:00
# include <linux/bitops.h>
2005-04-17 02:20:36 +04:00
# include <asm/addrspace.h>
# include <asm/bcache.h>
# include <asm/cacheops.h>
# include <asm/mipsregs.h>
# include <asm/processor.h>
2010-07-19 08:00:00 +04:00
# include <asm/sections.h>
2005-04-25 20:36:23 +04:00
# include <asm/cacheflush.h> /* for run_uncached() */
2005-04-17 02:20:36 +04:00
/* Primary cache parameters. */
# define sc_lsize 32
# define tc_pagesize (32*128)
/* Secondary cache parameters. */
# define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */
2010-07-19 08:00:00 +04:00
/* Tertiary cache parameters */
# define tc_lsize 32
2005-04-17 02:20:36 +04:00
extern unsigned long icache_way_size , dcache_way_size ;
2010-07-19 08:00:00 +04:00
unsigned long tcache_size ;
2005-04-17 02:20:36 +04:00
# include <asm/r4kcache.h>
2010-07-19 08:00:00 +04:00
static int rm7k_tcache_init ;
2005-04-17 02:20:36 +04:00
/*
* Writeback and invalidate the primary cache dcache before DMA .
* ( XXX These need to be fixed . . . )
*/
static void rm7k_sc_wback_inv ( unsigned long addr , unsigned long size )
{
unsigned long end , a ;
pr_debug ( " rm7k_sc_wback_inv[%08lx,%08lx] " , addr , size ) ;
/* Catch bad driver code */
BUG_ON ( size = = 0 ) ;
2006-02-15 12:25:48 +03:00
blast_scache_range ( addr , addr + size ) ;
2005-04-17 02:20:36 +04:00
2010-07-19 08:00:00 +04:00
if ( ! rm7k_tcache_init )
2005-04-17 02:20:36 +04:00
return ;
a = addr & ~ ( tc_pagesize - 1 ) ;
end = ( addr + size - 1 ) & ~ ( tc_pagesize - 1 ) ;
while ( 1 ) {
invalidate_tcache_page ( a ) ; /* Page_Invalidate_T */
if ( a = = end )
break ;
a + = tc_pagesize ;
}
}
static void rm7k_sc_inv ( unsigned long addr , unsigned long size )
{
unsigned long end , a ;
pr_debug ( " rm7k_sc_inv[%08lx,%08lx] " , addr , size ) ;
/* Catch bad driver code */
BUG_ON ( size = = 0 ) ;
2006-02-15 12:25:48 +03:00
blast_inv_scache_range ( addr , addr + size ) ;
2005-04-17 02:20:36 +04:00
2010-07-19 08:00:00 +04:00
if ( ! rm7k_tcache_init )
2005-04-17 02:20:36 +04:00
return ;
a = addr & ~ ( tc_pagesize - 1 ) ;
end = ( addr + size - 1 ) & ~ ( tc_pagesize - 1 ) ;
while ( 1 ) {
invalidate_tcache_page ( a ) ; /* Page_Invalidate_T */
if ( a = = end )
break ;
a + = tc_pagesize ;
}
}
2010-07-19 08:00:00 +04:00
static void blast_rm7k_tcache ( void )
{
unsigned long start = CKSEG0ADDR ( 0 ) ;
unsigned long end = start + tcache_size ;
write_c0_taglo ( 0 ) ;
while ( start < end ) {
cache_op ( Page_Invalidate_T , start ) ;
start + = tc_pagesize ;
}
}
/*
* This function is executed in uncached address space .
*/
static __cpuinit void __rm7k_tc_enable ( void )
{
int i ;
set_c0_config ( RM7K_CONF_TE ) ;
write_c0_taglo ( 0 ) ;
write_c0_taghi ( 0 ) ;
for ( i = 0 ; i < tcache_size ; i + = tc_lsize )
cache_op ( Index_Store_Tag_T , CKSEG0ADDR ( i ) ) ;
}
static __cpuinit void rm7k_tc_enable ( void )
{
if ( read_c0_config ( ) & RM7K_CONF_TE )
return ;
BUG_ON ( tcache_size = = 0 ) ;
run_uncached ( __rm7k_tc_enable ) ;
}
2005-04-17 02:20:36 +04:00
/*
2005-04-25 20:36:23 +04:00
* This function is executed in uncached address space .
2005-04-17 02:20:36 +04:00
*/
2008-07-06 03:19:42 +04:00
static __cpuinit void __rm7k_sc_enable ( void )
2005-04-17 02:20:36 +04:00
{
int i ;
2005-06-20 17:09:49 +04:00
set_c0_config ( RM7K_CONF_SE ) ;
2005-04-17 02:20:36 +04:00
write_c0_taglo ( 0 ) ;
write_c0_taghi ( 0 ) ;
2010-07-19 07:59:59 +04:00
for ( i = 0 ; i < scache_size ; i + = sc_lsize )
cache_op ( Index_Store_Tag_SD , CKSEG0ADDR ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-06 03:19:42 +04:00
static __cpuinit void rm7k_sc_enable ( void )
2005-04-17 02:20:36 +04:00
{
2005-06-20 17:09:49 +04:00
if ( read_c0_config ( ) & RM7K_CONF_SE )
2005-04-17 02:20:36 +04:00
return ;
2010-07-19 08:00:00 +04:00
pr_info ( " Enabling secondary cache... \n " ) ;
2005-04-25 20:36:23 +04:00
run_uncached ( __rm7k_sc_enable ) ;
2010-07-19 08:00:00 +04:00
if ( rm7k_tcache_init )
rm7k_tc_enable ( ) ;
}
static void rm7k_tc_disable ( void )
{
unsigned long flags ;
local_irq_save ( flags ) ;
blast_rm7k_tcache ( ) ;
clear_c0_config ( RM7K_CONF_TE ) ;
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
}
static void rm7k_sc_disable ( void )
{
2005-06-20 17:09:49 +04:00
clear_c0_config ( RM7K_CONF_SE ) ;
2010-07-19 08:00:00 +04:00
if ( rm7k_tcache_init )
rm7k_tc_disable ( ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-30 23:53:23 +04:00
static struct bcache_ops rm7k_sc_ops = {
2005-04-17 02:20:36 +04:00
. bc_enable = rm7k_sc_enable ,
. bc_disable = rm7k_sc_disable ,
. bc_wback_inv = rm7k_sc_wback_inv ,
. bc_inv = rm7k_sc_inv
} ;
2010-07-19 08:00:00 +04:00
/*
* This is a probing function like the one found in c - r4k . c , we look for the
* wrap around point with different addresses .
*/
static __cpuinit void __probe_tcache ( void )
{
unsigned long flags , addr , begin , end , pow2 ;
begin = ( unsigned long ) & _stext ;
begin & = ~ ( ( 8 * 1024 * 1024 ) - 1 ) ;
end = begin + ( 8 * 1024 * 1024 ) ;
local_irq_save ( flags ) ;
set_c0_config ( RM7K_CONF_TE ) ;
/* Fill size-multiple lines with a valid tag */
pow2 = ( 256 * 1024 ) ;
for ( addr = begin ; addr < = end ; addr = ( begin + pow2 ) ) {
unsigned long * p = ( unsigned long * ) addr ;
__asm__ __volatile__ ( " nop " : : " r " ( * p ) ) ;
pow2 < < = 1 ;
}
/* Load first line with a 0 tag, to check after */
write_c0_taglo ( 0 ) ;
write_c0_taghi ( 0 ) ;
cache_op ( Index_Store_Tag_T , begin ) ;
/* Look for the wrap-around */
pow2 = ( 512 * 1024 ) ;
for ( addr = begin + ( 512 * 1024 ) ; addr < = end ; addr = begin + pow2 ) {
cache_op ( Index_Load_Tag_T , addr ) ;
if ( ! read_c0_taglo ( ) )
break ;
pow2 < < = 1 ;
}
addr - = begin ;
tcache_size = addr ;
clear_c0_config ( RM7K_CONF_TE ) ;
local_irq_restore ( flags ) ;
}
2008-03-08 12:56:28 +03:00
void __cpuinit rm7k_sc_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-02-15 12:25:48 +03:00
struct cpuinfo_mips * c = & current_cpu_data ;
2005-04-17 02:20:36 +04:00
unsigned int config = read_c0_config ( ) ;
2005-06-20 17:09:49 +04:00
if ( ( config & RM7K_CONF_SC ) )
2005-04-17 02:20:36 +04:00
return ;
2006-02-15 12:25:48 +03:00
c - > scache . linesz = sc_lsize ;
c - > scache . ways = 4 ;
2006-04-07 20:33:31 +04:00
c - > scache . waybit = __ffs ( scache_size / c - > scache . ways ) ;
2006-02-15 12:25:48 +03:00
c - > scache . waysize = scache_size / c - > scache . ways ;
c - > scache . sets = scache_size / ( c - > scache . linesz * c - > scache . ways ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " Secondary cache size %dK, linesize %d bytes. \n " ,
( scache_size > > 10 ) , sc_lsize ) ;
2005-06-20 17:09:49 +04:00
if ( ! ( config & RM7K_CONF_SE ) )
2005-04-17 02:20:36 +04:00
rm7k_sc_enable ( ) ;
2010-07-19 08:00:00 +04:00
bcops = & rm7k_sc_ops ;
2005-04-17 02:20:36 +04:00
/*
* While we ' re at it let ' s deal with the tertiary cache .
*/
2010-07-19 08:00:00 +04:00
rm7k_tcache_init = 0 ;
tcache_size = 0 ;
if ( config & RM7K_CONF_TC )
return ;
/*
* No efficient way to ask the hardware for the size of the tcache ,
* so must probe for it .
*/
run_uncached ( __probe_tcache ) ;
rm7k_tc_enable ( ) ;
rm7k_tcache_init = 1 ;
c - > tcache . linesz = tc_lsize ;
c - > tcache . ways = 1 ;
pr_info ( " Tertiary cache size %ldK. \n " , ( tcache_size > > 10 ) ) ;
2005-04-17 02:20:36 +04:00
}