2005-04-16 15:20:36 -07:00
/*
* arch / sh / mm / cache - sh7705 . c
*
* Copyright ( C ) 1999 , 2000 Niibe Yutaka
* Copyright ( C ) 2004 Alex Song
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
2007-03-05 19:46:47 +09:00
*
2005-04-16 15:20:36 -07:00
*/
# include <linux/init.h>
# include <linux/mman.h>
# include <linux/mm.h>
2009-07-22 19:20:49 +09:00
# include <linux/fs.h>
2021-04-29 22:55:35 -07:00
# include <linux/pagemap.h>
2005-04-16 15:20:36 -07:00
# include <linux/threads.h>
# include <asm/addrspace.h>
# include <asm/page.h>
# include <asm/processor.h>
# include <asm/cache.h>
# include <asm/io.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
# include <asm/mmu_context.h>
# include <asm/cacheflush.h>
2006-09-27 17:03:56 +09:00
/*
* The 32 KB cache on the SH7705 suffers from the same synonym problem
* as SH4 CPUs
*/
2005-04-16 15:20:36 -07:00
static inline void cache_wback_all ( void )
{
unsigned long ways , waysize , addrstart ;
2006-12-25 10:19:56 +09:00
ways = current_cpu_data . dcache . ways ;
waysize = current_cpu_data . dcache . sets ;
waysize < < = current_cpu_data . dcache . entry_shift ;
2005-04-16 15:20:36 -07:00
addrstart = CACHE_OC_ADDRESS_ARRAY ;
do {
unsigned long addr ;
for ( addr = addrstart ;
addr < addrstart + waysize ;
2006-12-25 10:19:56 +09:00
addr + = current_cpu_data . dcache . linesz ) {
2005-04-16 15:20:36 -07:00
unsigned long data ;
int v = SH_CACHE_UPDATED | SH_CACHE_VALID ;
2010-01-26 12:58:40 +09:00
data = __raw_readl ( addr ) ;
2005-04-16 15:20:36 -07:00
if ( ( data & v ) = = v )
2010-01-26 12:58:40 +09:00
__raw_writel ( data & ~ v , addr ) ;
2007-03-05 19:46:47 +09:00
2005-04-16 15:20:36 -07:00
}
2006-12-25 10:19:56 +09:00
addrstart + = current_cpu_data . dcache . way_incr ;
2005-04-16 15:20:36 -07:00
} while ( - - ways ) ;
}
/*
* Write back the range of D - cache , and purge the I - cache .
*
* Called from kernel / module . c : sys_init_module and routine for a . out format .
*/
2009-08-21 17:23:14 +09:00
static void sh7705_flush_icache_range ( void * args )
2005-04-16 15:20:36 -07:00
{
2009-08-21 17:23:14 +09:00
struct flusher_data * data = args ;
unsigned long start , end ;
start = data - > addr1 ;
end = data - > addr2 ;
2005-04-16 15:20:36 -07:00
__flush_wback_region ( ( void * ) start , end - start ) ;
}
/*
* Writeback & Invalidate the D - cache of the page
*/
2010-01-21 16:05:25 +09:00
static void __flush_dcache_page ( unsigned long phys )
2005-04-16 15:20:36 -07:00
{
unsigned long ways , waysize , addrstart ;
2009-09-01 21:12:55 +09:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
phys | = SH_CACHE_VALID ;
/*
* Here , phys is the physical address of the page . We check all the
* tags in the cache for those with the same page number as this page
* ( by masking off the lowest 2 bits of the 19 - bit tag ; these bits are
* derived from the offset within in the 4 k page ) . Matching valid
* entries are invalidated .
*
* Since 2 bits of the cache index are derived from the virtual page
* number , knowing this would reduce the number of cache entries to be
* searched by a factor of 4. However this function exists to deal with
* potential cache aliasing , therefore the optimisation is probably not
* possible .
*/
2009-09-01 21:12:55 +09:00
local_irq_save ( flags ) ;
2007-11-30 17:06:36 +09:00
jump_to_uncached ( ) ;
2005-04-16 15:20:36 -07:00
2006-12-25 10:19:56 +09:00
ways = current_cpu_data . dcache . ways ;
waysize = current_cpu_data . dcache . sets ;
waysize < < = current_cpu_data . dcache . entry_shift ;
2005-04-16 15:20:36 -07:00
addrstart = CACHE_OC_ADDRESS_ARRAY ;
do {
unsigned long addr ;
for ( addr = addrstart ;
addr < addrstart + waysize ;
2006-12-25 10:19:56 +09:00
addr + = current_cpu_data . dcache . linesz ) {
2005-04-16 15:20:36 -07:00
unsigned long data ;
2010-01-26 12:58:40 +09:00
data = __raw_readl ( addr ) & ( 0x1ffffC00 | SH_CACHE_VALID ) ;
2005-04-16 15:20:36 -07:00
if ( data = = phys ) {
data & = ~ ( SH_CACHE_VALID | SH_CACHE_UPDATED ) ;
2010-01-26 12:58:40 +09:00
__raw_writel ( data , addr ) ;
2005-04-16 15:20:36 -07:00
}
}
2006-12-25 10:19:56 +09:00
addrstart + = current_cpu_data . dcache . way_incr ;
2005-04-16 15:20:36 -07:00
} while ( - - ways ) ;
2007-11-30 17:06:36 +09:00
back_to_cached ( ) ;
2009-09-01 21:12:55 +09:00
local_irq_restore ( flags ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Write back & invalidate the D - cache of the page .
* ( To avoid " alias " issues )
*/
2023-08-02 16:13:52 +01:00
static void sh7705_flush_dcache_folio ( void * arg )
2005-04-16 15:20:36 -07:00
{
2023-08-02 16:13:52 +01:00
struct folio * folio = arg ;
struct address_space * mapping = folio_flush_mapping ( folio ) ;
2009-07-22 19:20:49 +09:00
if ( mapping & & ! mapping_mapped ( mapping ) )
2023-08-02 16:13:52 +01:00
clear_bit ( PG_dcache_clean , & folio - > flags ) ;
else {
unsigned long pfn = folio_pfn ( folio ) ;
unsigned int i , nr = folio_nr_pages ( folio ) ;
for ( i = 0 ; i < nr ; i + + )
__flush_dcache_page ( ( pfn + i ) * PAGE_SIZE ) ;
}
2005-04-16 15:20:36 -07:00
}
2010-01-21 16:05:25 +09:00
static void sh7705_flush_cache_all ( void * args )
2005-04-16 15:20:36 -07:00
{
2009-09-01 21:12:55 +09:00
unsigned long flags ;
local_irq_save ( flags ) ;
2007-11-30 17:06:36 +09:00
jump_to_uncached ( ) ;
2009-09-01 21:12:55 +09:00
2005-04-16 15:20:36 -07:00
cache_wback_all ( ) ;
2007-11-30 17:06:36 +09:00
back_to_cached ( ) ;
2009-09-01 21:12:55 +09:00
local_irq_restore ( flags ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Write back and invalidate I / D - caches for the page .
*
* ADDRESS : Virtual Address ( U0 address )
*/
2009-08-21 17:23:14 +09:00
static void sh7705_flush_cache_page ( void * args )
2005-04-16 15:20:36 -07:00
{
2009-08-21 17:23:14 +09:00
struct flusher_data * data = args ;
unsigned long pfn = data - > addr2 ;
2005-04-16 15:20:36 -07:00
__flush_dcache_page ( pfn < < PAGE_SHIFT ) ;
}
/*
* This is called when a page - cache page is about to be mapped into a
* user process ' address space . It offers an opportunity for a
* port to ensure d - cache / i - cache coherency if necessary .
*
* Not entirely sure why this is necessary on SH3 with 32 K cache but
* without it we get occasional " Memory fault " when loading a program .
*/
2023-08-02 16:13:52 +01:00
static void sh7705_flush_icache_folio ( void * arg )
2005-04-16 15:20:36 -07:00
{
2023-08-02 16:13:52 +01:00
struct folio * folio = arg ;
__flush_purge_region ( folio_address ( folio ) , folio_size ( folio ) ) ;
2005-04-16 15:20:36 -07:00
}
2009-08-15 12:53:39 +09:00
void __init sh7705_cache_init ( void )
{
2009-08-21 17:23:14 +09:00
local_flush_icache_range = sh7705_flush_icache_range ;
2023-08-02 16:13:52 +01:00
local_flush_dcache_folio = sh7705_flush_dcache_folio ;
2009-08-21 17:23:14 +09:00
local_flush_cache_all = sh7705_flush_cache_all ;
local_flush_cache_mm = sh7705_flush_cache_all ;
local_flush_cache_dup_mm = sh7705_flush_cache_all ;
local_flush_cache_range = sh7705_flush_cache_all ;
local_flush_cache_page = sh7705_flush_cache_page ;
2023-08-02 16:13:52 +01:00
local_flush_icache_folio = sh7705_flush_icache_folio ;
2009-08-15 12:53:39 +09:00
}