2005-04-17 02:20:36 +04:00
/*
2009-08-21 12:23:14 +04:00
* arch / sh / mm / cache . c
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1999 , 2000 , 2002 Niibe Yutaka
2009-07-27 15:53:22 +04:00
* Copyright ( C ) 2002 - 2009 Paul Mundt
2005-04-17 02:20:36 +04:00
*
* Released under the terms of the GNU GPL v2 .0 .
*/
# include <linux/mm.h>
2008-11-10 14:00:45 +03:00
# include <linux/init.h>
2006-11-21 05:09:41 +03:00
# include <linux/mutex.h>
2007-07-31 08:01:43 +04:00
# include <linux/fs.h>
2009-08-21 12:23:14 +04:00
# include <linux/smp.h>
2007-11-05 10:12:32 +03:00
# include <linux/highmem.h>
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <asm/mmu_context.h>
# include <asm/cacheflush.h>
2009-08-21 12:23:14 +04:00
void ( * local_flush_cache_all ) ( void * args ) = cache_noop ;
void ( * local_flush_cache_mm ) ( void * args ) = cache_noop ;
void ( * local_flush_cache_dup_mm ) ( void * args ) = cache_noop ;
void ( * local_flush_cache_page ) ( void * args ) = cache_noop ;
void ( * local_flush_cache_range ) ( void * args ) = cache_noop ;
void ( * local_flush_dcache_page ) ( void * args ) = cache_noop ;
void ( * local_flush_icache_range ) ( void * args ) = cache_noop ;
void ( * local_flush_icache_page ) ( void * args ) = cache_noop ;
void ( * local_flush_cache_sigtramp ) ( void * args ) = cache_noop ;
2009-08-15 07:29:49 +04:00
void ( * __flush_wback_region ) ( void * start , int size ) ;
void ( * __flush_purge_region ) ( void * start , int size ) ;
void ( * __flush_invalidate_region ) ( void * start , int size ) ;
static inline void noop__flush_region ( void * start , int size )
{
}
2007-11-05 10:18:16 +03:00
void copy_to_user_page ( struct vm_area_struct * vma , struct page * page ,
unsigned long vaddr , void * dst , const void * src ,
unsigned long len )
2005-04-17 02:20:36 +04:00
{
2009-07-27 16:30:17 +04:00
if ( boot_cpu_data . dcache . n_aliases & & page_mapped ( page ) & &
! test_bit ( PG_dcache_dirty , & page - > flags ) ) {
2009-07-22 14:20:49 +04:00
void * vto = kmap_coherent ( page , vaddr ) + ( vaddr & ~ PAGE_MASK ) ;
memcpy ( vto , src , len ) ;
2009-08-04 11:00:36 +04:00
kunmap_coherent ( ) ;
2009-07-22 14:20:49 +04:00
} else {
memcpy ( dst , src , len ) ;
2009-07-27 16:30:17 +04:00
if ( boot_cpu_data . dcache . n_aliases )
set_bit ( PG_dcache_dirty , & page - > flags ) ;
2009-07-22 14:20:49 +04:00
}
2007-11-05 10:18:16 +03:00
if ( vma - > vm_flags & VM_EXEC )
flush_cache_page ( vma , vaddr , page_to_pfn ( page ) ) ;
}
void copy_from_user_page ( struct vm_area_struct * vma , struct page * page ,
unsigned long vaddr , void * dst , const void * src ,
unsigned long len )
{
2009-07-27 16:30:17 +04:00
if ( boot_cpu_data . dcache . n_aliases & & page_mapped ( page ) & &
! test_bit ( PG_dcache_dirty , & page - > flags ) ) {
2009-07-22 14:20:49 +04:00
void * vfrom = kmap_coherent ( page , vaddr ) + ( vaddr & ~ PAGE_MASK ) ;
memcpy ( dst , vfrom , len ) ;
2009-08-04 11:00:36 +04:00
kunmap_coherent ( ) ;
2009-07-22 14:20:49 +04:00
} else {
memcpy ( dst , src , len ) ;
2009-07-27 16:30:17 +04:00
if ( boot_cpu_data . dcache . n_aliases )
set_bit ( PG_dcache_dirty , & page - > flags ) ;
2009-07-22 14:20:49 +04:00
}
2005-04-17 02:20:36 +04:00
}
2007-03-05 13:46:47 +03:00
2007-11-05 10:12:32 +03:00
void copy_user_highpage ( struct page * to , struct page * from ,
unsigned long vaddr , struct vm_area_struct * vma )
{
void * vfrom , * vto ;
vto = kmap_atomic ( to , KM_USER1 ) ;
2009-07-27 16:30:17 +04:00
if ( boot_cpu_data . dcache . n_aliases & & page_mapped ( from ) & &
! test_bit ( PG_dcache_dirty , & from - > flags ) ) {
2009-07-22 14:20:49 +04:00
vfrom = kmap_coherent ( from , vaddr ) ;
copy_page ( vto , vfrom ) ;
2009-08-04 11:00:36 +04:00
kunmap_coherent ( ) ;
2009-07-22 14:20:49 +04:00
} else {
vfrom = kmap_atomic ( from , KM_USER0 ) ;
copy_page ( vto , vfrom ) ;
kunmap_atomic ( vfrom , KM_USER0 ) ;
}
if ( pages_do_alias ( ( unsigned long ) vto , vaddr & PAGE_MASK ) )
2007-11-05 10:12:32 +03:00
__flush_wback_region ( vto , PAGE_SIZE ) ;
kunmap_atomic ( vto , KM_USER1 ) ;
/* Make sure this page is cleared on other CPU's too before using it */
smp_wmb ( ) ;
}
EXPORT_SYMBOL ( copy_user_highpage ) ;
2009-07-27 15:53:22 +04:00
void clear_user_highpage ( struct page * page , unsigned long vaddr )
{
void * kaddr = kmap_atomic ( page , KM_USER0 ) ;
clear_page ( kaddr ) ;
if ( pages_do_alias ( ( unsigned long ) kaddr , vaddr & PAGE_MASK ) )
__flush_wback_region ( kaddr , PAGE_SIZE ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
}
EXPORT_SYMBOL ( clear_user_highpage ) ;
2009-07-28 19:12:17 +04:00
void __update_cache ( struct vm_area_struct * vma ,
unsigned long address , pte_t pte )
{
struct page * page ;
unsigned long pfn = pte_pfn ( pte ) ;
if ( ! boot_cpu_data . dcache . n_aliases )
return ;
page = pfn_to_page ( pfn ) ;
if ( pfn_valid ( pfn ) & & page_mapping ( page ) ) {
int dirty = test_and_clear_bit ( PG_dcache_dirty , & page - > flags ) ;
if ( dirty ) {
unsigned long addr = ( unsigned long ) page_address ( page ) ;
if ( pages_do_alias ( addr , address & PAGE_MASK ) )
__flush_wback_region ( ( void * ) addr , PAGE_SIZE ) ;
}
}
}
2009-08-04 11:02:43 +04:00
void __flush_anon_page ( struct page * page , unsigned long vmaddr )
{
unsigned long addr = ( unsigned long ) page_address ( page ) ;
if ( pages_do_alias ( addr , vmaddr ) ) {
if ( boot_cpu_data . dcache . n_aliases & & page_mapped ( page ) & &
! test_bit ( PG_dcache_dirty , & page - > flags ) ) {
void * kaddr ;
kaddr = kmap_coherent ( page , vmaddr ) ;
__flush_wback_region ( ( void * ) kaddr , PAGE_SIZE ) ;
kunmap_coherent ( ) ;
} else
__flush_wback_region ( ( void * ) addr , PAGE_SIZE ) ;
}
}
2009-08-15 06:05:42 +04:00
2009-08-21 12:23:14 +04:00
void flush_cache_all ( void )
{
on_each_cpu ( local_flush_cache_all , NULL , 1 ) ;
}
void flush_cache_mm ( struct mm_struct * mm )
{
on_each_cpu ( local_flush_cache_mm , mm , 1 ) ;
}
void flush_cache_dup_mm ( struct mm_struct * mm )
{
on_each_cpu ( local_flush_cache_dup_mm , mm , 1 ) ;
}
void flush_cache_page ( struct vm_area_struct * vma , unsigned long addr ,
unsigned long pfn )
{
struct flusher_data data ;
data . vma = vma ;
data . addr1 = addr ;
data . addr2 = pfn ;
on_each_cpu ( local_flush_cache_page , ( void * ) & data , 1 ) ;
}
void flush_cache_range ( struct vm_area_struct * vma , unsigned long start ,
unsigned long end )
{
struct flusher_data data ;
data . vma = vma ;
data . addr1 = start ;
data . addr2 = end ;
on_each_cpu ( local_flush_cache_range , ( void * ) & data , 1 ) ;
}
void flush_dcache_page ( struct page * page )
{
on_each_cpu ( local_flush_dcache_page , page , 1 ) ;
}
void flush_icache_range ( unsigned long start , unsigned long end )
{
struct flusher_data data ;
data . vma = NULL ;
data . addr1 = start ;
data . addr2 = end ;
on_each_cpu ( local_flush_icache_range , ( void * ) & data , 1 ) ;
}
void flush_icache_page ( struct vm_area_struct * vma , struct page * page )
{
/* Nothing uses the VMA, so just pass the struct page along */
on_each_cpu ( local_flush_icache_page , page , 1 ) ;
}
void flush_cache_sigtramp ( unsigned long address )
{
on_each_cpu ( local_flush_cache_sigtramp , ( void * ) address , 1 ) ;
}
2009-08-15 06:11:16 +04:00
static void compute_alias ( struct cache_info * c )
{
c - > alias_mask = ( ( c - > sets - 1 ) < < c - > entry_shift ) & ~ ( PAGE_SIZE - 1 ) ;
c - > n_aliases = c - > alias_mask ? ( c - > alias_mask > > PAGE_SHIFT ) + 1 : 0 ;
}
static void __init emit_cache_params ( void )
{
printk ( KERN_NOTICE " I-cache : n_ways=%d n_sets=%d way_incr=%d \n " ,
boot_cpu_data . icache . ways ,
boot_cpu_data . icache . sets ,
boot_cpu_data . icache . way_incr ) ;
printk ( KERN_NOTICE " I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d \n " ,
boot_cpu_data . icache . entry_mask ,
boot_cpu_data . icache . alias_mask ,
boot_cpu_data . icache . n_aliases ) ;
printk ( KERN_NOTICE " D-cache : n_ways=%d n_sets=%d way_incr=%d \n " ,
boot_cpu_data . dcache . ways ,
boot_cpu_data . dcache . sets ,
boot_cpu_data . dcache . way_incr ) ;
printk ( KERN_NOTICE " D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d \n " ,
boot_cpu_data . dcache . entry_mask ,
boot_cpu_data . dcache . alias_mask ,
boot_cpu_data . dcache . n_aliases ) ;
/*
* Emit Secondary Cache parameters if the CPU has a probed L2 .
*/
if ( boot_cpu_data . flags & CPU_HAS_L2_CACHE ) {
printk ( KERN_NOTICE " S-cache : n_ways=%d n_sets=%d way_incr=%d \n " ,
boot_cpu_data . scache . ways ,
boot_cpu_data . scache . sets ,
boot_cpu_data . scache . way_incr ) ;
printk ( KERN_NOTICE " S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d \n " ,
boot_cpu_data . scache . entry_mask ,
boot_cpu_data . scache . alias_mask ,
boot_cpu_data . scache . n_aliases ) ;
}
}
2009-08-15 06:05:42 +04:00
void __init cpu_cache_init ( void )
{
2009-08-15 06:11:16 +04:00
compute_alias ( & boot_cpu_data . icache ) ;
compute_alias ( & boot_cpu_data . dcache ) ;
compute_alias ( & boot_cpu_data . scache ) ;
2009-08-15 07:29:49 +04:00
__flush_wback_region = noop__flush_region ;
__flush_purge_region = noop__flush_region ;
__flush_invalidate_region = noop__flush_region ;
2009-08-15 07:35:15 +04:00
if ( boot_cpu_data . family = = CPU_FAMILY_SH2 ) {
extern void __weak sh2_cache_init ( void ) ;
sh2_cache_init ( ) ;
}
2009-08-15 07:38:29 +04:00
if ( boot_cpu_data . family = = CPU_FAMILY_SH2A ) {
extern void __weak sh2a_cache_init ( void ) ;
sh2a_cache_init ( ) ;
}
2009-08-15 07:42:55 +04:00
if ( boot_cpu_data . family = = CPU_FAMILY_SH3 ) {
extern void __weak sh3_cache_init ( void ) ;
sh3_cache_init ( ) ;
2009-08-15 07:53:39 +04:00
if ( ( boot_cpu_data . type = = CPU_SH7705 ) & &
( boot_cpu_data . dcache . sets = = 512 ) ) {
extern void __weak sh7705_cache_init ( void ) ;
sh7705_cache_init ( ) ;
}
2009-08-15 07:42:55 +04:00
}
2009-08-15 06:05:42 +04:00
if ( ( boot_cpu_data . family = = CPU_FAMILY_SH4 ) | |
( boot_cpu_data . family = = CPU_FAMILY_SH4A ) | |
( boot_cpu_data . family = = CPU_FAMILY_SH4AL_DSP ) ) {
extern void __weak sh4_cache_init ( void ) ;
sh4_cache_init ( ) ;
}
2009-08-15 06:11:16 +04:00
2009-08-15 21:16:44 +04:00
if ( boot_cpu_data . family = = CPU_FAMILY_SH5 ) {
extern void __weak sh5_cache_init ( void ) ;
sh5_cache_init ( ) ;
}
2009-08-15 06:11:16 +04:00
emit_cache_params ( ) ;
2009-08-15 06:05:42 +04:00
}