162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sc-rm7k.c: RM7000 cache management functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1997, 2001, 2003, 2004 Ralf Baechle (ralf@linux-mips.org) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#undef DEBUG 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/bitops.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/addrspace.h> 1562306a36Sopenharmony_ci#include <asm/bcache.h> 1662306a36Sopenharmony_ci#include <asm/cacheops.h> 1762306a36Sopenharmony_ci#include <asm/mipsregs.h> 1862306a36Sopenharmony_ci#include <asm/processor.h> 1962306a36Sopenharmony_ci#include <asm/sections.h> 2062306a36Sopenharmony_ci#include <asm/cacheflush.h> /* for run_uncached() */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Primary cache parameters. */ 2362306a36Sopenharmony_ci#define sc_lsize 32 2462306a36Sopenharmony_ci#define tc_pagesize (32*128) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Secondary cache parameters. */ 2762306a36Sopenharmony_ci#define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Tertiary cache parameters */ 3062306a36Sopenharmony_ci#define tc_lsize 32 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciextern unsigned long icache_way_size, dcache_way_size; 3362306a36Sopenharmony_cistatic unsigned long tcache_size; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <asm/r4kcache.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int rm7k_tcache_init; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Writeback and invalidate the primary cache dcache before DMA. 4162306a36Sopenharmony_ci * (XXX These need to be fixed ...) 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic void rm7k_sc_wback_inv(unsigned long addr, unsigned long size) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci unsigned long end, a; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci pr_debug("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Catch bad driver code */ 5062306a36Sopenharmony_ci BUG_ON(size == 0); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci blast_scache_range(addr, addr + size); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!rm7k_tcache_init) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci a = addr & ~(tc_pagesize - 1); 5862306a36Sopenharmony_ci end = (addr + size - 1) & ~(tc_pagesize - 1); 5962306a36Sopenharmony_ci while(1) { 6062306a36Sopenharmony_ci invalidate_tcache_page(a); /* Page_Invalidate_T */ 6162306a36Sopenharmony_ci if (a == end) 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci a += tc_pagesize; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void rm7k_sc_inv(unsigned long addr, unsigned long size) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci unsigned long end, a; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci pr_debug("rm7k_sc_inv[%08lx,%08lx]", addr, size); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Catch bad driver code */ 7462306a36Sopenharmony_ci BUG_ON(size == 0); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci blast_inv_scache_range(addr, addr + size); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!rm7k_tcache_init) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci a = addr & ~(tc_pagesize - 1); 8262306a36Sopenharmony_ci end = (addr + size - 1) & ~(tc_pagesize - 1); 8362306a36Sopenharmony_ci while(1) { 8462306a36Sopenharmony_ci invalidate_tcache_page(a); /* Page_Invalidate_T */ 8562306a36Sopenharmony_ci if (a == end) 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci a += tc_pagesize; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void blast_rm7k_tcache(void) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned long start = CKSEG0ADDR(0); 9462306a36Sopenharmony_ci unsigned long end = start + tcache_size; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci write_c0_taglo(0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci while (start < end) { 9962306a36Sopenharmony_ci cache_op(Page_Invalidate_T, start); 10062306a36Sopenharmony_ci start += tc_pagesize; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * This function is executed in uncached address space. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic void __rm7k_tc_enable(void) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int i; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci set_c0_config(RM7K_CONF_TE); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci write_c0_taglo(0); 11462306a36Sopenharmony_ci write_c0_taghi(0); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (i = 0; i < tcache_size; i += tc_lsize) 11762306a36Sopenharmony_ci cache_op(Index_Store_Tag_T, CKSEG0ADDR(i)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void rm7k_tc_enable(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci if (read_c0_config() & RM7K_CONF_TE) 12362306a36Sopenharmony_ci return; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci BUG_ON(tcache_size == 0); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci run_uncached(__rm7k_tc_enable); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * This function is executed in uncached address space. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic void __rm7k_sc_enable(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci set_c0_config(RM7K_CONF_SE); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci write_c0_taglo(0); 14062306a36Sopenharmony_ci write_c0_taghi(0); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (i = 0; i < scache_size; i += sc_lsize) 14362306a36Sopenharmony_ci cache_op(Index_Store_Tag_SD, CKSEG0ADDR(i)); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void rm7k_sc_enable(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (read_c0_config() & RM7K_CONF_SE) 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci pr_info("Enabling secondary cache...\n"); 15262306a36Sopenharmony_ci run_uncached(__rm7k_sc_enable); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (rm7k_tcache_init) 15562306a36Sopenharmony_ci rm7k_tc_enable(); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void rm7k_tc_disable(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci unsigned long flags; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci local_irq_save(flags); 16362306a36Sopenharmony_ci blast_rm7k_tcache(); 16462306a36Sopenharmony_ci clear_c0_config(RM7K_CONF_TE); 16562306a36Sopenharmony_ci local_irq_restore(flags); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void rm7k_sc_disable(void) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci clear_c0_config(RM7K_CONF_SE); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (rm7k_tcache_init) 17362306a36Sopenharmony_ci rm7k_tc_disable(); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct bcache_ops rm7k_sc_ops = { 17762306a36Sopenharmony_ci .bc_enable = rm7k_sc_enable, 17862306a36Sopenharmony_ci .bc_disable = rm7k_sc_disable, 17962306a36Sopenharmony_ci .bc_wback_inv = rm7k_sc_wback_inv, 18062306a36Sopenharmony_ci .bc_inv = rm7k_sc_inv 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * This is a probing function like the one found in c-r4k.c, we look for the 18562306a36Sopenharmony_ci * wrap around point with different addresses. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic void __probe_tcache(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned long flags, addr, begin, end, pow2; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci begin = (unsigned long) &_stext; 19262306a36Sopenharmony_ci begin &= ~((8 * 1024 * 1024) - 1); 19362306a36Sopenharmony_ci end = begin + (8 * 1024 * 1024); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci local_irq_save(flags); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci set_c0_config(RM7K_CONF_TE); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Fill size-multiple lines with a valid tag */ 20062306a36Sopenharmony_ci pow2 = (256 * 1024); 20162306a36Sopenharmony_ci for (addr = begin; addr <= end; addr = (begin + pow2)) { 20262306a36Sopenharmony_ci unsigned long *p = (unsigned long *) addr; 20362306a36Sopenharmony_ci __asm__ __volatile__("nop" : : "r" (*p)); 20462306a36Sopenharmony_ci pow2 <<= 1; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Load first line with a 0 tag, to check after */ 20862306a36Sopenharmony_ci write_c0_taglo(0); 20962306a36Sopenharmony_ci write_c0_taghi(0); 21062306a36Sopenharmony_ci cache_op(Index_Store_Tag_T, begin); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Look for the wrap-around */ 21362306a36Sopenharmony_ci pow2 = (512 * 1024); 21462306a36Sopenharmony_ci for (addr = begin + (512 * 1024); addr <= end; addr = begin + pow2) { 21562306a36Sopenharmony_ci cache_op(Index_Load_Tag_T, addr); 21662306a36Sopenharmony_ci if (!read_c0_taglo()) 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci pow2 <<= 1; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci addr -= begin; 22262306a36Sopenharmony_ci tcache_size = addr; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci clear_c0_config(RM7K_CONF_TE); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci local_irq_restore(flags); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid rm7k_sc_init(void) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct cpuinfo_mips *c = ¤t_cpu_data; 23262306a36Sopenharmony_ci unsigned int config = read_c0_config(); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if ((config & RM7K_CONF_SC)) 23562306a36Sopenharmony_ci return; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci c->scache.linesz = sc_lsize; 23862306a36Sopenharmony_ci c->scache.ways = 4; 23962306a36Sopenharmony_ci c->scache.waybit= __ffs(scache_size / c->scache.ways); 24062306a36Sopenharmony_ci c->scache.waysize = scache_size / c->scache.ways; 24162306a36Sopenharmony_ci c->scache.sets = scache_size / (c->scache.linesz * c->scache.ways); 24262306a36Sopenharmony_ci printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n", 24362306a36Sopenharmony_ci (scache_size >> 10), sc_lsize); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!(config & RM7K_CONF_SE)) 24662306a36Sopenharmony_ci rm7k_sc_enable(); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci bcops = &rm7k_sc_ops; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * While we're at it let's deal with the tertiary cache. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci rm7k_tcache_init = 0; 25562306a36Sopenharmony_ci tcache_size = 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (config & RM7K_CONF_TC) 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * No efficient way to ask the hardware for the size of the tcache, 26262306a36Sopenharmony_ci * so must probe for it. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci run_uncached(__probe_tcache); 26562306a36Sopenharmony_ci rm7k_tc_enable(); 26662306a36Sopenharmony_ci rm7k_tcache_init = 1; 26762306a36Sopenharmony_ci c->tcache.linesz = tc_lsize; 26862306a36Sopenharmony_ci c->tcache.ways = 1; 26962306a36Sopenharmony_ci pr_info("Tertiary cache size %ldK.\n", (tcache_size >> 10)); 27062306a36Sopenharmony_ci} 271