2013-01-18 15:12:19 +05:30
/*
* ARC700 VIPT Cache Management
*
* Copyright ( C ) 2004 , 2007 - 2010 , 2011 - 2012 Synopsys , Inc . ( www . synopsys . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* vineetg : May 2011 : for Non - aliasing VIPT D - cache following can be NOPs
* - flush_cache_dup_mm ( fork )
* - likewise for flush_cache_mm ( exit / execve )
* - likewise for flush_cache_range , flush_cache_page ( munmap , exit , COW - break )
*
* vineetg : Apr 2011
* - Now that MMU can support larger pg sz ( 16 K ) , the determiniation of
* aliasing shd not be based on assumption of 8 k pg
*
* vineetg : Mar 2011
* - optimised version of flush_icache_range ( ) for making I / D coherent
* when vaddr is available ( agnostic of num of aliases )
*
* vineetg : Mar 2011
* - Added documentation about I - cache aliasing on ARC700 and the way it
* was handled up until MMU V2 .
* - Spotted a three year old bug when killing the 4 aliases , which needs
* bottom 2 bits , so we need to do paddr | { 0x00 , 0x01 , 0x02 , 0x03 }
* instead of paddr | { 0x00 , 0x01 , 0x10 , 0x11 }
* ( Rajesh you owe me one now )
*
* vineetg : Dec 2010
* - Off - by - one error when computing num_of_lines to flush
* This broke signal handling with bionic which uses synthetic sigret stub
*
* vineetg : Mar 2010
* - GCC can ' t generate ZOL for core cache flush loops .
* Conv them into iterations based as opposed to while ( start < end ) types
*
* Vineetg : July 2009
* - In I - cache flush routine we used to chk for aliasing for every line INV .
* Instead now we setup routines per cache geometry and invoke them
* via function pointers .
*
* Vineetg : Jan 2009
* - Cache Line flush routines used to flush an extra line beyond end addr
* because check was while ( end > = start ) instead of ( end > start )
* = Some call sites had to work around by doing - 1 , - 4 etc to end param
* = Some callers didnt care . This was spec bad in case of INV routines
* which would discard valid data ( cause of the horrible ext2 bug
* in ARC IDE driver )
*
* vineetg : June 11 th 2008 : Fixed flush_icache_range ( )
* - Since ARC700 caches are not coherent ( I $ doesnt snoop D $ ) both need
* to be flushed , which it was not doing .
* - load_module ( ) passes vmalloc addr ( Kernel Virtual Addr ) to the API ,
* however ARC cache maintenance OPs require PHY addr . Thus need to do
* vmalloc_to_phy .
* - Also added optimisation there , that for range > PAGE SIZE we flush the
* entire cache in one shot rather than line by line . For e . g . a module
* with Code sz 600 k , old code flushed 600 k worth of cache ( line - by - line ) ,
* while cache is only 16 or 32 k .
*/
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/cache.h>
# include <linux/mmu_context.h>
# include <linux/syscalls.h>
# include <linux/uaccess.h>
# include <asm/cacheflush.h>
# include <asm/cachectl.h>
# include <asm/setup.h>
# ifdef CONFIG_ARC_HAS_ICACHE
static void __ic_line_inv_no_alias ( unsigned long , int ) ;
static void __ic_line_inv_2_alias ( unsigned long , int ) ;
static void __ic_line_inv_4_alias ( unsigned long , int ) ;
/* Holds the ptr to flush routine, dependign on size due to aliasing issues */
static void ( * ___flush_icache_rtn ) ( unsigned long , int ) ;
# endif
2013-01-18 15:12:24 +05:30
char * arc_cache_mumbojumbo ( int cpu_id , char * buf , int len )
{
int n = 0 ;
unsigned int c = smp_processor_id ( ) ;
# define PR_CACHE(p, enb, str) \
{ \
if ( ! ( p ) - > ver ) \
n + = scnprintf ( buf + n , len - n , str " \t \t : N/A \n " ) ; \
else \
n + = scnprintf ( buf + n , len - n , \
str " \t \t : (%uK) VIPT, %dway set-asc, %ub Line %s \n " , \
TO_KB ( ( p ) - > sz ) , ( p ) - > assoc , ( p ) - > line_len , \
enb ? " " : " DISABLED (kernel-build) " ) ; \
}
PR_CACHE ( & cpuinfo_arc700 [ c ] . icache , __CONFIG_ARC_HAS_ICACHE , " I-Cache " ) ;
PR_CACHE ( & cpuinfo_arc700 [ c ] . dcache , __CONFIG_ARC_HAS_DCACHE , " D-Cache " ) ;
return buf ;
}
2013-01-18 15:12:19 +05:30
/*
* Read the Cache Build Confuration Registers , Decode them and save into
* the cpuinfo structure for later use .
* No Validation done here , simply read / convert the BCRs
*/
2013-04-09 17:18:12 +05:30
void __cpuinit read_decode_cache_bcr ( void )
2013-01-18 15:12:19 +05:30
{
struct bcr_cache ibcr , dbcr ;
struct cpuinfo_arc_cache * p_ic , * p_dc ;
unsigned int cpu = smp_processor_id ( ) ;
p_ic = & cpuinfo_arc700 [ cpu ] . icache ;
READ_BCR ( ARC_REG_IC_BCR , ibcr ) ;
if ( ibcr . config = = 0x3 )
p_ic - > assoc = 2 ;
p_ic - > line_len = 8 < < ibcr . line_len ;
p_ic - > sz = 0x200 < < ibcr . sz ;
p_ic - > ver = ibcr . ver ;
p_dc = & cpuinfo_arc700 [ cpu ] . dcache ;
READ_BCR ( ARC_REG_DC_BCR , dbcr ) ;
if ( dbcr . config = = 0x2 )
p_dc - > assoc = 4 ;
p_dc - > line_len = 16 < < dbcr . line_len ;
p_dc - > sz = 0x200 < < dbcr . sz ;
p_dc - > ver = dbcr . ver ;
}
/*
* 1. Validate the Cache Geomtery ( compile time config matches hardware )
* 2. If I - cache suffers from aliasing , setup work arounds ( difft flush rtn )
* ( aliasing D - cache configurations are not supported YET )
* 3. Enable the Caches , setup default flush mode for D - Cache
* 3. Calculate the SHMLBA used by user space
*/
2013-04-09 17:18:12 +05:30
void __cpuinit arc_cache_init ( void )
2013-01-18 15:12:19 +05:30
{
unsigned int temp ;
unsigned int cpu = smp_processor_id ( ) ;
2013-01-28 15:07:31 +05:30
struct cpuinfo_arc_cache * ic = & cpuinfo_arc700 [ cpu ] . icache ;
struct cpuinfo_arc_cache * dc = & cpuinfo_arc700 [ cpu ] . dcache ;
2013-01-18 15:12:19 +05:30
int way_pg_ratio = way_pg_ratio ;
2013-01-18 15:12:24 +05:30
char str [ 256 ] ;
printk ( arc_cache_mumbojumbo ( 0 , str , sizeof ( str ) ) ) ;
2013-01-18 15:12:19 +05:30
2013-01-28 15:07:31 +05:30
if ( ! ic - > ver )
goto chk_dc ;
2013-01-18 15:12:19 +05:30
2013-01-28 15:07:31 +05:30
# ifdef CONFIG_ARC_HAS_ICACHE
2013-01-18 15:12:24 +05:30
/* 1. Confirm some of I-cache params which Linux assumes */
if ( ( ic - > assoc ! = ARC_ICACHE_WAYS ) | |
( ic - > line_len ! = ARC_ICACHE_LINE_LEN ) ) {
panic ( " Cache H/W doesn't match kernel Config " ) ;
}
# if (CONFIG_ARC_MMU_VER > 2)
if ( ic - > ver ! = 3 ) {
if ( running_on_hw )
panic ( " Cache ver doesn't match MMU ver \n " ) ;
/* For ISS - suggest the toggles to use */
pr_err ( " Use -prop=icache_version=3,-prop=dcache_version=3 \n " ) ;
}
# endif
2013-01-18 15:12:19 +05:30
/*
* if Cache way size is < = page size then no aliasing exhibited
* otherwise ratio determines num of aliases .
* e . g . 32 K I $ , 2 way set assoc , 8 k pg size
* way - sz = 32 k / 2 = 16 k
* way - pg - ratio = 16 k / 8 k = 2 , so 2 aliases possible
* ( meaning 1 line could be in 2 possible locations ) .
*/
way_pg_ratio = ic - > sz / ARC_ICACHE_WAYS / PAGE_SIZE ;
switch ( way_pg_ratio ) {
case 0 :
case 1 :
___flush_icache_rtn = __ic_line_inv_no_alias ;
break ;
case 2 :
___flush_icache_rtn = __ic_line_inv_2_alias ;
break ;
case 4 :
___flush_icache_rtn = __ic_line_inv_4_alias ;
break ;
default :
panic ( " Unsupported I-Cache Sz \n " ) ;
}
# endif
/* Enable/disable I-Cache */
temp = read_aux_reg ( ARC_REG_IC_CTRL ) ;
# ifdef CONFIG_ARC_HAS_ICACHE
temp & = ~ IC_CTRL_CACHE_DISABLE ;
# else
temp | = IC_CTRL_CACHE_DISABLE ;
# endif
write_aux_reg ( ARC_REG_IC_CTRL , temp ) ;
2013-01-28 15:07:31 +05:30
chk_dc :
if ( ! dc - > ver )
return ;
2013-01-18 15:12:19 +05:30
2013-01-28 15:07:31 +05:30
# ifdef CONFIG_ARC_HAS_DCACHE
2013-01-18 15:12:24 +05:30
if ( ( dc - > assoc ! = ARC_DCACHE_WAYS ) | |
( dc - > line_len ! = ARC_DCACHE_LINE_LEN ) ) {
panic ( " Cache H/W doesn't match kernel Config " ) ;
}
2013-01-18 15:12:19 +05:30
/* check for D-Cache aliasing */
if ( ( dc - > sz / ARC_DCACHE_WAYS ) > PAGE_SIZE )
panic ( " D$ aliasing not handled right now \n " ) ;
# endif
/* Set the default Invalidate Mode to "simpy discard dirty lines"
* as this is more frequent then flush before invalidate
* Ofcourse we toggle this default behviour when desired
*/
temp = read_aux_reg ( ARC_REG_DC_CTRL ) ;
temp & = ~ DC_CTRL_INV_MODE_FLUSH ;
# ifdef CONFIG_ARC_HAS_DCACHE
/* Enable D-Cache: Clear Bit 0 */
write_aux_reg ( ARC_REG_DC_CTRL , temp & ~ IC_CTRL_CACHE_DISABLE ) ;
# else
/* Flush D cache */
write_aux_reg ( ARC_REG_DC_FLSH , 0x1 ) ;
/* Disable D cache */
write_aux_reg ( ARC_REG_DC_CTRL , temp | IC_CTRL_CACHE_DISABLE ) ;
# endif
return ;
}
# define OP_INV 0x1
# define OP_FLUSH 0x2
# define OP_FLUSH_N_INV 0x3
# ifdef CONFIG_ARC_HAS_DCACHE
/***************************************************************
* Machine specific helpers for Entire D - Cache or Per Line ops
*/
static inline void wait_for_flush ( void )
{
while ( read_aux_reg ( ARC_REG_DC_CTRL ) & DC_CTRL_FLUSH_STATUS )
;
}
/*
* Operation on Entire D - Cache
* @ cacheop = { OP_INV , OP_FLUSH , OP_FLUSH_N_INV }
* Note that constant propagation ensures all the checks are gone
* in generated code
*/
static inline void __dc_entire_op ( const int cacheop )
{
unsigned long flags , tmp = tmp ;
int aux ;
local_irq_save ( flags ) ;
if ( cacheop = = OP_FLUSH_N_INV ) {
/* Dcache provides 2 cmd: FLUSH or INV
* INV inturn has sub - modes : DISCARD or FLUSH - BEFORE
* flush - n - inv is achieved by INV cmd but with IM = 1
* Default INV sub - mode is DISCARD , which needs to be toggled
*/
tmp = read_aux_reg ( ARC_REG_DC_CTRL ) ;
write_aux_reg ( ARC_REG_DC_CTRL , tmp | DC_CTRL_INV_MODE_FLUSH ) ;
}
if ( cacheop & OP_INV ) /* Inv or flush-n-inv use same cmd reg */
aux = ARC_REG_DC_IVDC ;
else
aux = ARC_REG_DC_FLSH ;
write_aux_reg ( aux , 0x1 ) ;
if ( cacheop & OP_FLUSH ) /* flush / flush-n-inv both wait */
wait_for_flush ( ) ;
/* Switch back the DISCARD ONLY Invalidate mode */
if ( cacheop = = OP_FLUSH_N_INV )
write_aux_reg ( ARC_REG_DC_CTRL , tmp & ~ DC_CTRL_INV_MODE_FLUSH ) ;
local_irq_restore ( flags ) ;
}
/*
* Per Line Operation on D - Cache
* Doesn ' t deal with type - of - op / IRQ - disabling / waiting - for - flush - to - complete
* It ' s sole purpose is to help gcc generate ZOL
*/
static inline void __dc_line_loop ( unsigned long start , unsigned long sz ,
int aux_reg )
{
int num_lines , slack ;
/* Ensure we properly floor/ceil the non-line aligned/sized requests
* and have @ start - aligned to cache line and integral @ num_lines .
* This however can be avoided for page sized since :
* - @ start will be cache - line aligned already ( being page aligned )
* - @ sz will be integral multiple of line size ( being page sized ) .
*/
if ( ! ( __builtin_constant_p ( sz ) & & sz = = PAGE_SIZE ) ) {
slack = start & ~ DCACHE_LINE_MASK ;
sz + = slack ;
start - = slack ;
}
num_lines = DIV_ROUND_UP ( sz , ARC_DCACHE_LINE_LEN ) ;
while ( num_lines - - > 0 ) {
# if (CONFIG_ARC_MMU_VER > 2)
/*
* Just as for I $ , in MMU v3 , D $ ops also require
* " tag " bits in DC_PTAG , " index " bits in FLDL , IVDL ops
* But we pass phy addr for both . This works since Linux
* doesn ' t support aliasing configs for D $ , yet .
* Thus paddr is enough to provide both tag and index .
*/
write_aux_reg ( ARC_REG_DC_PTAG , start ) ;
# endif
write_aux_reg ( aux_reg , start ) ;
start + = ARC_DCACHE_LINE_LEN ;
}
}
/*
* D - Cache : Per Line INV ( discard or wback + discard ) or FLUSH ( wback )
*/
static inline void __dc_line_op ( unsigned long start , unsigned long sz ,
const int cacheop )
{
unsigned long flags , tmp = tmp ;
int aux ;
local_irq_save ( flags ) ;
if ( cacheop = = OP_FLUSH_N_INV ) {
/*
* Dcache provides 2 cmd : FLUSH or INV
* INV inturn has sub - modes : DISCARD or FLUSH - BEFORE
* flush - n - inv is achieved by INV cmd but with IM = 1
* Default INV sub - mode is DISCARD , which needs to be toggled
*/
tmp = read_aux_reg ( ARC_REG_DC_CTRL ) ;
write_aux_reg ( ARC_REG_DC_CTRL , tmp | DC_CTRL_INV_MODE_FLUSH ) ;
}
if ( cacheop & OP_INV ) /* Inv / flush-n-inv use same cmd reg */
aux = ARC_REG_DC_IVDL ;
else
aux = ARC_REG_DC_FLDL ;
__dc_line_loop ( start , sz , aux ) ;
if ( cacheop & OP_FLUSH ) /* flush / flush-n-inv both wait */
wait_for_flush ( ) ;
/* Switch back the DISCARD ONLY Invalidate mode */
if ( cacheop = = OP_FLUSH_N_INV )
write_aux_reg ( ARC_REG_DC_CTRL , tmp & ~ DC_CTRL_INV_MODE_FLUSH ) ;
local_irq_restore ( flags ) ;
}
# else
# define __dc_entire_op(cacheop)
# define __dc_line_op(start, sz, cacheop)
# endif /* CONFIG_ARC_HAS_DCACHE */
# ifdef CONFIG_ARC_HAS_ICACHE
/*
* I - Cache Aliasing in ARC700 VIPT caches
*
* For fetching code from I $ , ARC700 uses vaddr ( embedded in program code )
* to " index " into SET of cache - line and paddr from MMU to match the TAG
* in the WAYS of SET .
*
* However the CDU iterface ( to flush / inv ) lines from software , only takes
* paddr ( to have simpler hardware interface ) . For simpler cases , using paddr
* alone suffices .
* e . g . 2 - way - set - assoc , 16 K I $ ( 8 k MMU pg sz , 32 b cache line size ) :
* way_sz = cache_sz / num_ways = 16 k / 2 = 8 k
* num_sets = way_sz / line_sz = 8 k / 32 = 256 = > 8 bits
* Ignoring the bottom 5 bits corresp to the off within a 32 b cacheline ,
* bits req for calc set - index = bits 12 : 5 ( 0 based ) . Since this range fits
* inside the bottom 13 bits of paddr , which are same for vaddr and paddr
* ( with 8 k pg sz ) , paddr alone can be safely used by CDU to unambigously
* locate a cache - line .
*
* However for a difft sized cache , say 32 k I $ , above math yields need
* for 14 bits of vaddr to locate a cache line , which can ' t be provided by
* paddr , since the bit 13 ( 0 based ) might differ between the two .
*
* This lack of extra bits needed for correct line addressing , defines the
* classical problem of Cache aliasing with VIPT architectures
* num_aliases = 1 < < extra_bits
* e . g . 2 - way - set - assoc , 32 K I $ with 8 k MMU pg sz = > 2 aliases
* 2 - way - set - assoc , 64 K I $ with 8 k MMU pg sz = > 4 aliases
* 2 - way - set - assoc , 16 K I $ with 8 k MMU pg sz = > NO aliases
*
* - - - - - - - - - - - - - - - - - -
* MMU v1 / v2 ( Fixed Page Size 8 k )
* - - - - - - - - - - - - - - - - - -
* The solution was to provide CDU with these additonal vaddr bits . These
* would be bits [ x : 13 ] , x would depend on cache - geom .
* H / w folks chose [ 17 : 13 ] to be a future safe range , and moreso these 5 bits
* of vaddr could easily be " stuffed " in the paddr as bits [ 4 : 0 ] since the
* orig 5 bits of paddr were anyways ignored by CDU line ops , as they
* represent the offset within cache - line . The adv of using this " clumsy "
* interface for additional info was no new reg was needed in CDU .
*
* 17 : 13 represented the max num of bits passable , actual bits needed were
* fewer , based on the num - of - aliases possible .
* - for 2 alias possibility , only bit 13 needed ( 32 K cache )
* - for 4 alias possibility , bits 14 : 13 needed ( 64 K cache )
*
* Since vaddr was not available for all instances of I $ flush req by core
* kernel , the only safe way ( non - optimal though ) was to kill all possible
* lines which could represent an alias ( even if they didnt represent one
* in execution ) .
* e . g . for 64 K I $ , 4 aliases possible , so we did
* flush start
* flush start | 0x01
* flush start | 0x2
* flush start | 0x3
*
* The penalty was invoking the operation itself , since tag match is anyways
* paddr based , a line which didn ' t represent an alias would not match the
* paddr , hence wont be killed
*
* Note that aliasing concerns are independent of line - sz for a given cache
* geometry ( size + set_assoc ) because the extra bits required by line - sz are
* reduced from the set calc .
* e . g . 2 - way - set - assoc , 32 K I $ with 8 k MMU pg sz and using math above
* 32 b line - sz : 9 bits set - index - calc , 5 bits offset - in - line = > 1 extra bit
* 64 b line - sz : 8 bits set - index - calc , 6 bits offset - in - line = > 1 extra bit
*
* - - - - - - - - - - - - - - - - - -
* MMU v3
* - - - - - - - - - - - - - - - - - -
* This ver of MMU supports var page sizes ( 1 k - 16 k ) - Linux will support
* 8 k ( default ) , 16 k and 4 k .
* However from hardware perspective , smaller page sizes aggrevate aliasing
* meaning more vaddr bits needed to disambiguate the cache - line - op ;
* the existing scheme of piggybacking won ' t work for certain configurations .
* Two new registers IC_PTAG and DC_PTAG inttoduced .
* " tag " bits are provided in PTAG , index bits in existing IVIL / IVDL / FLDL regs
*/
/***********************************************************
* Machine specific helpers for per line I - Cache invalidate .
* 3 routines to accpunt for 1 , 2 , 4 aliases possible
*/
static void __ic_line_inv_no_alias ( unsigned long start , int num_lines )
{
while ( num_lines - - > 0 ) {
# if (CONFIG_ARC_MMU_VER > 2)
write_aux_reg ( ARC_REG_IC_PTAG , start ) ;
# endif
write_aux_reg ( ARC_REG_IC_IVIL , start ) ;
start + = ARC_ICACHE_LINE_LEN ;
}
}
static void __ic_line_inv_2_alias ( unsigned long start , int num_lines )
{
while ( num_lines - - > 0 ) {
# if (CONFIG_ARC_MMU_VER > 2)
/*
* MMU v3 , CDU prog model ( for line ops ) now uses a new IC_PTAG
* reg to pass the " tag " bits and existing IVIL reg only looks
* at bits relevant for " index " ( details above )
* Programming Notes :
* - when writing tag to PTAG reg , bit chopping can be avoided ,
* CDU ignores non - tag bits .
* - Ideally " index " must be computed from vaddr , but it is not
* avail in these rtns . So to be safe , we kill the lines in all
* possible indexes corresp to num of aliases possible for
* given cache config .
*/
write_aux_reg ( ARC_REG_IC_PTAG , start ) ;
write_aux_reg ( ARC_REG_IC_IVIL ,
start & ~ ( 0x1 < < PAGE_SHIFT ) ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | ( 0x1 < < PAGE_SHIFT ) ) ;
# else
write_aux_reg ( ARC_REG_IC_IVIL , start ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | 0x01 ) ;
# endif
start + = ARC_ICACHE_LINE_LEN ;
}
}
static void __ic_line_inv_4_alias ( unsigned long start , int num_lines )
{
while ( num_lines - - > 0 ) {
# if (CONFIG_ARC_MMU_VER > 2)
write_aux_reg ( ARC_REG_IC_PTAG , start ) ;
write_aux_reg ( ARC_REG_IC_IVIL ,
start & ~ ( 0x3 < < PAGE_SHIFT ) ) ;
write_aux_reg ( ARC_REG_IC_IVIL ,
start & ~ ( 0x2 < < PAGE_SHIFT ) ) ;
write_aux_reg ( ARC_REG_IC_IVIL ,
start & ~ ( 0x1 < < PAGE_SHIFT ) ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | ( 0x3 < < PAGE_SHIFT ) ) ;
# else
write_aux_reg ( ARC_REG_IC_IVIL , start ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | 0x01 ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | 0x02 ) ;
write_aux_reg ( ARC_REG_IC_IVIL , start | 0x03 ) ;
# endif
start + = ARC_ICACHE_LINE_LEN ;
}
}
static void __ic_line_inv ( unsigned long start , unsigned long sz )
{
unsigned long flags ;
int num_lines , slack ;
/*
* Ensure we properly floor / ceil the non - line aligned / sized requests
* and have @ start - aligned to cache line , and integral @ num_lines
* However page sized flushes can be compile time optimised .
* - @ start will be cache - line aligned already ( being page aligned )
* - @ sz will be integral multiple of line size ( being page sized ) .
*/
if ( ! ( __builtin_constant_p ( sz ) & & sz = = PAGE_SIZE ) ) {
slack = start & ~ ICACHE_LINE_MASK ;
sz + = slack ;
start - = slack ;
}
num_lines = DIV_ROUND_UP ( sz , ARC_ICACHE_LINE_LEN ) ;
local_irq_save ( flags ) ;
( * ___flush_icache_rtn ) ( start , num_lines ) ;
local_irq_restore ( flags ) ;
}
/* Unlike routines above, having vaddr for flush op (along with paddr),
* prevents the need to speculatively kill the lines in multiple sets
* based on ratio of way_sz : pg_sz
*/
static void __ic_line_inv_vaddr ( unsigned long phy_start ,
unsigned long vaddr , unsigned long sz )
{
unsigned long flags ;
int num_lines , slack ;
unsigned int addr ;
slack = phy_start & ~ ICACHE_LINE_MASK ;
sz + = slack ;
phy_start - = slack ;
num_lines = DIV_ROUND_UP ( sz , ARC_ICACHE_LINE_LEN ) ;
# if (CONFIG_ARC_MMU_VER > 2)
vaddr & = ~ ICACHE_LINE_MASK ;
addr = phy_start ;
# else
/* bits 17:13 of vaddr go as bits 4:0 of paddr */
addr = phy_start | ( ( vaddr > > 13 ) & 0x1F ) ;
# endif
local_irq_save ( flags ) ;
while ( num_lines - - > 0 ) {
# if (CONFIG_ARC_MMU_VER > 2)
/* tag comes from phy addr */
write_aux_reg ( ARC_REG_IC_PTAG , addr ) ;
/* index bits come from vaddr */
write_aux_reg ( ARC_REG_IC_IVIL , vaddr ) ;
vaddr + = ARC_ICACHE_LINE_LEN ;
# else
/* this paddr contains vaddrs bits as needed */
write_aux_reg ( ARC_REG_IC_IVIL , addr ) ;
# endif
addr + = ARC_ICACHE_LINE_LEN ;
}
local_irq_restore ( flags ) ;
}
# else
# define __ic_line_inv(start, sz)
# define __ic_line_inv_vaddr(pstart, vstart, sz)
# endif /* CONFIG_ARC_HAS_ICACHE */
/***********************************************************
* Exported APIs
*/
/* TBD: use pg_arch_1 to optimize this */
void flush_dcache_page ( struct page * page )
{
__dc_line_op ( ( unsigned long ) page_address ( page ) , PAGE_SIZE , OP_FLUSH ) ;
}
EXPORT_SYMBOL ( flush_dcache_page ) ;
void dma_cache_wback_inv ( unsigned long start , unsigned long sz )
{
__dc_line_op ( start , sz , OP_FLUSH_N_INV ) ;
}
EXPORT_SYMBOL ( dma_cache_wback_inv ) ;
void dma_cache_inv ( unsigned long start , unsigned long sz )
{
__dc_line_op ( start , sz , OP_INV ) ;
}
EXPORT_SYMBOL ( dma_cache_inv ) ;
void dma_cache_wback ( unsigned long start , unsigned long sz )
{
__dc_line_op ( start , sz , OP_FLUSH ) ;
}
EXPORT_SYMBOL ( dma_cache_wback ) ;
/*
2013-04-12 12:18:25 +05:30
* This is API for making I / D Caches consistent when modifying
* kernel code ( loadable modules , kprobes , kgdb . . . )
2013-01-18 15:12:19 +05:30
* This is called on insmod , with kernel virtual address for CODE of
* the module . ARC cache maintenance ops require PHY address thus we
* need to convert vmalloc addr to PHY addr
*/
void flush_icache_range ( unsigned long kstart , unsigned long kend )
{
unsigned int tot_sz , off , sz ;
unsigned long phy , pfn ;
unsigned long flags ;
/* printk("Kernel Cache Cohenercy: %lx to %lx\n",kstart, kend); */
/* This is not the right API for user virtual address */
if ( kstart < TASK_SIZE ) {
BUG_ON ( " Flush icache range for user virtual addr space " ) ;
return ;
}
/* Shortcut for bigger flush ranges.
* Here we don ' t care if this was kernel virtual or phy addr
*/
tot_sz = kend - kstart ;
if ( tot_sz > PAGE_SIZE ) {
flush_cache_all ( ) ;
return ;
}
/* Case: Kernel Phy addr (0x8000_0000 onwards) */
if ( likely ( kstart > PAGE_OFFSET ) ) {
2013-04-12 12:18:25 +05:30
/*
* The 2 nd arg despite being paddr will be used to index icache
* This is OK since no alternate virtual mappings will exist
* given the callers for this case : kprobe / kgdb in built - in
* kernel code only .
*/
__ic_line_inv_vaddr ( kstart , kstart , kend - kstart ) ;
2013-01-18 15:12:19 +05:30
__dc_line_op ( kstart , kend - kstart , OP_FLUSH ) ;
return ;
}
/*
* Case : Kernel Vaddr ( 0x7000 _0000 to 0x7fff _ffff )
* ( 1 ) ARC Cache Maintenance ops only take Phy addr , hence special
* handling of kernel vaddr .
*
* ( 2 ) Despite @ tot_sz being < PAGE_SIZE ( bigger cases handled already ) ,
* it still needs to handle a 2 page scenario , where the range
* straddles across 2 virtual pages and hence need for loop
*/
while ( tot_sz > 0 ) {
off = kstart % PAGE_SIZE ;
pfn = vmalloc_to_pfn ( ( void * ) kstart ) ;
phy = ( pfn < < PAGE_SHIFT ) + off ;
sz = min_t ( unsigned int , tot_sz , PAGE_SIZE - off ) ;
local_irq_save ( flags ) ;
__dc_line_op ( phy , sz , OP_FLUSH ) ;
2013-04-12 12:18:25 +05:30
__ic_line_inv_vaddr ( phy , kstart , sz ) ;
2013-01-18 15:12:19 +05:30
local_irq_restore ( flags ) ;
kstart + = sz ;
tot_sz - = sz ;
}
}
/*
* Optimised ver of flush_icache_range ( ) with spec callers : ptrace / signals
* where vaddr is also available . This allows passing both vaddr and paddr
* bits to CDU for cache flush , short - circuting the current pessimistic algo
* which kills all possible aliases .
* An added adv of knowing that vaddr is user - vaddr avoids various checks
* and handling for k - vaddr , k - paddr as done in orig ver above
*/
void flush_icache_range_vaddr ( unsigned long paddr , unsigned long u_vaddr ,
int len )
{
__ic_line_inv_vaddr ( paddr , u_vaddr , len ) ;
__dc_line_op ( paddr , len , OP_FLUSH ) ;
}
2013-04-11 18:36:35 +05:30
/* wrapper to compile time eliminate alignment checks in flush loop */
void __inv_icache_page ( unsigned long paddr , unsigned long vaddr )
2013-01-18 15:12:19 +05:30
{
2013-04-11 18:36:35 +05:30
__ic_line_inv_vaddr ( paddr , vaddr , PAGE_SIZE ) ;
2013-01-18 15:12:19 +05:30
}
void flush_icache_all ( void )
{
unsigned long flags ;
local_irq_save ( flags ) ;
write_aux_reg ( ARC_REG_IC_IVIC , 1 ) ;
/* lr will not complete till the icache inv operation is not over */
read_aux_reg ( ARC_REG_IC_CTRL ) ;
local_irq_restore ( flags ) ;
}
noinline void flush_cache_all ( void )
{
unsigned long flags ;
local_irq_save ( flags ) ;
flush_icache_all ( ) ;
__dc_entire_op ( OP_FLUSH_N_INV ) ;
local_irq_restore ( flags ) ;
}
/**********************************************************************
* Explicit Cache flush request from user space via syscall
* Needed for JITs which generate code on the fly
*/
SYSCALL_DEFINE3 ( cacheflush , uint32_t , start , uint32_t , sz , uint32_t , flags )
{
/* TBD: optimize this */
flush_cache_all ( ) ;
return 0 ;
}