1/* 2 * sc-rm7k.c: RM7000 cache management functions. 3 * 4 * Copyright (C) 1997, 2001, 2003, 2004 Ralf Baechle (ralf@linux-mips.org) 5 */ 6 7#undef DEBUG 8 9#include <linux/init.h> 10#include <linux/kernel.h> 11#include <linux/mm.h> 12#include <linux/bitops.h> 13 14#include <asm/addrspace.h> 15#include <asm/bcache.h> 16#include <asm/cacheops.h> 17#include <asm/mipsregs.h> 18#include <asm/processor.h> 19#include <asm/sections.h> 20#include <asm/cacheflush.h> /* for run_uncached() */ 21 22/* Primary cache parameters. */ 23#define sc_lsize 32 24#define tc_pagesize (32*128) 25 26/* Secondary cache parameters. */ 27#define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ 28 29/* Tertiary cache parameters */ 30#define tc_lsize 32 31 32extern unsigned long icache_way_size, dcache_way_size; 33static unsigned long tcache_size; 34 35#include <asm/r4kcache.h> 36 37static int rm7k_tcache_init; 38 39static void rm7k_sc_wback_inv(unsigned long addr, unsigned long size) 40{ 41 unsigned long end, a; 42 43 pr_debug("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size); 44 45 /* Catch bad driver code */ 46 BUG_ON(size == 0); 47 48 blast_scache_range(addr, addr + size); 49 50 if (!rm7k_tcache_init) 51 return; 52 53 a = addr & ~(tc_pagesize - 1); 54 end = (addr + size - 1) & ~(tc_pagesize - 1); 55 while(1) { 56 invalidate_tcache_page(a); /* Page_Invalidate_T */ 57 if (a == end) 58 break; 59 a += tc_pagesize; 60 } 61} 62 63static void rm7k_sc_inv(unsigned long addr, unsigned long size) 64{ 65 unsigned long end, a; 66 67 pr_debug("rm7k_sc_inv[%08lx,%08lx]", addr, size); 68 69 /* Catch bad driver code */ 70 BUG_ON(size == 0); 71 72 blast_inv_scache_range(addr, addr + size); 73 74 if (!rm7k_tcache_init) 75 return; 76 77 a = addr & ~(tc_pagesize - 1); 78 end = (addr + size - 1) & ~(tc_pagesize - 1); 79 while(1) { 80 invalidate_tcache_page(a); /* Page_Invalidate_T */ 81 if (a == end) 82 break; 83 a += tc_pagesize; 84 } 85} 86 87static void blast_rm7k_tcache(void) 88{ 89 unsigned long start = CKSEG0ADDR(0); 90 unsigned long end = start + tcache_size; 91 92 write_c0_taglo(0); 93 94 while (start < end) { 95 cache_op(Page_Invalidate_T, start); 96 start += tc_pagesize; 97 } 98} 99 100/* 101 * This function is executed in uncached address space. 102 */ 103static __cpuinit void __rm7k_tc_enable(void) 104{ 105 int i; 106 107 set_c0_config(RM7K_CONF_TE); 108 109 write_c0_taglo(0); 110 write_c0_taghi(0); 111 112 for (i = 0; i < tcache_size; i += tc_lsize) 113 cache_op(Index_Store_Tag_T, CKSEG0ADDR(i)); 114} 115 116static __cpuinit void rm7k_tc_enable(void) 117{ 118 if (read_c0_config() & RM7K_CONF_TE) 119 return; 120 121 BUG_ON(tcache_size == 0); 122 123 run_uncached(__rm7k_tc_enable); 124} 125 126/* 127 * This function is executed in uncached address space. 128 */ 129static __cpuinit void __rm7k_sc_enable(void) 130{ 131 int i; 132 133 set_c0_config(RM7K_CONF_SE); 134 135 write_c0_taglo(0); 136 write_c0_taghi(0); 137 138 for (i = 0; i < scache_size; i += sc_lsize) 139 cache_op(Index_Store_Tag_SD, CKSEG0ADDR(i)); 140} 141 142static __cpuinit void rm7k_sc_enable(void) 143{ 144 if (read_c0_config() & RM7K_CONF_SE) 145 return; 146 147 pr_info("Enabling secondary cache...\n"); 148 run_uncached(__rm7k_sc_enable); 149 150 if (rm7k_tcache_init) 151 rm7k_tc_enable(); 152} 153 154static void rm7k_tc_disable(void) 155{ 156 unsigned long flags; 157 158 local_irq_save(flags); 159 blast_rm7k_tcache(); 160 clear_c0_config(RM7K_CONF_TE); 161 local_irq_save(flags); 162} 163 164static void rm7k_sc_disable(void) 165{ 166 clear_c0_config(RM7K_CONF_SE); 167 168 if (rm7k_tcache_init) 169 rm7k_tc_disable(); 170} 171 172static struct bcache_ops rm7k_sc_ops = { 173 .bc_enable = rm7k_sc_enable, 174 .bc_disable = rm7k_sc_disable, 175 .bc_wback_inv = rm7k_sc_wback_inv, 176 .bc_inv = rm7k_sc_inv 177}; 178 179/* 180 * This is a probing function like the one found in c-r4k.c, we look for the 181 * wrap around point with different addresses. 182 */ 183static __cpuinit void __probe_tcache(void) 184{ 185 unsigned long flags, addr, begin, end, pow2; 186 187 begin = (unsigned long) &_stext; 188 begin &= ~((8 * 1024 * 1024) - 1); 189 end = begin + (8 * 1024 * 1024); 190 191 local_irq_save(flags); 192 193 set_c0_config(RM7K_CONF_TE); 194 195 /* Fill size-multiple lines with a valid tag */ 196 pow2 = (256 * 1024); 197 for (addr = begin; addr <= end; addr = (begin + pow2)) { 198 unsigned long *p = (unsigned long *) addr; 199 __asm__ __volatile__("nop" : : "r" (*p)); 200 pow2 <<= 1; 201 } 202 203 /* Load first line with a 0 tag, to check after */ 204 write_c0_taglo(0); 205 write_c0_taghi(0); 206 cache_op(Index_Store_Tag_T, begin); 207 208 /* Look for the wrap-around */ 209 pow2 = (512 * 1024); 210 for (addr = begin + (512 * 1024); addr <= end; addr = begin + pow2) { 211 cache_op(Index_Load_Tag_T, addr); 212 if (!read_c0_taglo()) 213 break; 214 pow2 <<= 1; 215 } 216 217 addr -= begin; 218 tcache_size = addr; 219 220 clear_c0_config(RM7K_CONF_TE); 221 222 local_irq_restore(flags); 223} 224 225void __cpuinit rm7k_sc_init(void) 226{ 227 struct cpuinfo_mips *c = ¤t_cpu_data; 228 unsigned int config = read_c0_config(); 229 230 if ((config & RM7K_CONF_SC)) 231 return; 232 233 c->scache.linesz = sc_lsize; 234 c->scache.ways = 4; 235 c->scache.waybit= __ffs(scache_size / c->scache.ways); 236 c->scache.waysize = scache_size / c->scache.ways; 237 c->scache.sets = scache_size / (c->scache.linesz * c->scache.ways); 238 printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n", 239 (scache_size >> 10), sc_lsize); 240 241 if (!(config & RM7K_CONF_SE)) 242 rm7k_sc_enable(); 243 244 bcops = &rm7k_sc_ops; 245 246 /* 247 * While we're at it let's deal with the tertiary cache. 248 */ 249 250 rm7k_tcache_init = 0; 251 tcache_size = 0; 252 253 if (config & RM7K_CONF_TC) 254 return; 255 256 /* 257 * No efficient way to ask the hardware for the size of the tcache, 258 * so must probe for it. 259 */ 260 run_uncached(__probe_tcache); 261 rm7k_tc_enable(); 262 rm7k_tcache_init = 1; 263 c->tcache.linesz = tc_lsize; 264 c->tcache.ways = 1; 265 pr_info("Tertiary cache size %ldK.\n", (tcache_size >> 10)); 266} 267