Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.24 416 lines 10 kB view raw
1/* 2 * r2300.c: R2000 and R3000 specific mmu/cache code. 3 * 4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) 5 * 6 * with a lot of changes to make this thing work for R3000s 7 * Tx39XX R4k style caches added. HK 8 * Copyright (C) 1998, 1999, 2000 Harald Koerfgen 9 * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov 10 */ 11#include <linux/init.h> 12#include <linux/kernel.h> 13#include <linux/sched.h> 14#include <linux/mm.h> 15 16#include <asm/cacheops.h> 17#include <asm/page.h> 18#include <asm/pgtable.h> 19#include <asm/mmu_context.h> 20#include <asm/system.h> 21#include <asm/isadep.h> 22#include <asm/io.h> 23#include <asm/bootinfo.h> 24#include <asm/cpu.h> 25 26/* For R3000 cores with R4000 style caches */ 27static unsigned long icache_size, dcache_size; /* Size in bytes */ 28 29#include <asm/r4kcache.h> 30 31extern int r3k_have_wired_reg; /* in r3k-tlb.c */ 32 33/* This sequence is required to ensure icache is disabled immediately */ 34#define TX39_STOP_STREAMING() \ 35__asm__ __volatile__( \ 36 ".set push\n\t" \ 37 ".set noreorder\n\t" \ 38 "b 1f\n\t" \ 39 "nop\n\t" \ 40 "1:\n\t" \ 41 ".set pop" \ 42 ) 43 44/* TX39H-style cache flush routines. */ 45static void tx39h_flush_icache_all(void) 46{ 47 unsigned long flags, config; 48 49 /* disable icache (set ICE#) */ 50 local_irq_save(flags); 51 config = read_c0_conf(); 52 write_c0_conf(config & ~TX39_CONF_ICE); 53 TX39_STOP_STREAMING(); 54 blast_icache16(); 55 write_c0_conf(config); 56 local_irq_restore(flags); 57} 58 59static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size) 60{ 61 /* Catch bad driver code */ 62 BUG_ON(size == 0); 63 64 iob(); 65 blast_inv_dcache_range(addr, addr + size); 66} 67 68 69/* TX39H2,TX39H3 */ 70static inline void tx39_blast_dcache_page(unsigned long addr) 71{ 72 if (current_cpu_type() != CPU_TX3912) 73 blast_dcache16_page(addr); 74} 75 76static inline void tx39_blast_dcache_page_indexed(unsigned long addr) 77{ 78 blast_dcache16_page_indexed(addr); 79} 80 81static inline void tx39_blast_dcache(void) 82{ 83 blast_dcache16(); 84} 85 86static inline void tx39_blast_icache_page(unsigned long addr) 87{ 88 unsigned long flags, config; 89 /* disable icache (set ICE#) */ 90 local_irq_save(flags); 91 config = read_c0_conf(); 92 write_c0_conf(config & ~TX39_CONF_ICE); 93 TX39_STOP_STREAMING(); 94 blast_icache16_page(addr); 95 write_c0_conf(config); 96 local_irq_restore(flags); 97} 98 99static inline void tx39_blast_icache_page_indexed(unsigned long addr) 100{ 101 unsigned long flags, config; 102 /* disable icache (set ICE#) */ 103 local_irq_save(flags); 104 config = read_c0_conf(); 105 write_c0_conf(config & ~TX39_CONF_ICE); 106 TX39_STOP_STREAMING(); 107 blast_icache16_page_indexed(addr); 108 write_c0_conf(config); 109 local_irq_restore(flags); 110} 111 112static inline void tx39_blast_icache(void) 113{ 114 unsigned long flags, config; 115 /* disable icache (set ICE#) */ 116 local_irq_save(flags); 117 config = read_c0_conf(); 118 write_c0_conf(config & ~TX39_CONF_ICE); 119 TX39_STOP_STREAMING(); 120 blast_icache16(); 121 write_c0_conf(config); 122 local_irq_restore(flags); 123} 124 125static inline void tx39_flush_cache_all(void) 126{ 127 if (!cpu_has_dc_aliases) 128 return; 129 130 tx39_blast_dcache(); 131} 132 133static inline void tx39___flush_cache_all(void) 134{ 135 tx39_blast_dcache(); 136 tx39_blast_icache(); 137} 138 139static void tx39_flush_cache_mm(struct mm_struct *mm) 140{ 141 if (!cpu_has_dc_aliases) 142 return; 143 144 if (cpu_context(smp_processor_id(), mm) != 0) 145 tx39_blast_dcache(); 146} 147 148static void tx39_flush_cache_range(struct vm_area_struct *vma, 149 unsigned long start, unsigned long end) 150{ 151 if (!cpu_has_dc_aliases) 152 return; 153 if (!(cpu_context(smp_processor_id(), vma->vm_mm))) 154 return; 155 156 tx39_blast_dcache(); 157} 158 159static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) 160{ 161 int exec = vma->vm_flags & VM_EXEC; 162 struct mm_struct *mm = vma->vm_mm; 163 pgd_t *pgdp; 164 pud_t *pudp; 165 pmd_t *pmdp; 166 pte_t *ptep; 167 168 /* 169 * If ownes no valid ASID yet, cannot possibly have gotten 170 * this page into the cache. 171 */ 172 if (cpu_context(smp_processor_id(), mm) == 0) 173 return; 174 175 page &= PAGE_MASK; 176 pgdp = pgd_offset(mm, page); 177 pudp = pud_offset(pgdp, page); 178 pmdp = pmd_offset(pudp, page); 179 ptep = pte_offset(pmdp, page); 180 181 /* 182 * If the page isn't marked valid, the page cannot possibly be 183 * in the cache. 184 */ 185 if (!(pte_val(*ptep) & _PAGE_PRESENT)) 186 return; 187 188 /* 189 * Doing flushes for another ASID than the current one is 190 * too difficult since stupid R4k caches do a TLB translation 191 * for every cache flush operation. So we do indexed flushes 192 * in that case, which doesn't overly flush the cache too much. 193 */ 194 if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { 195 if (cpu_has_dc_aliases || exec) 196 tx39_blast_dcache_page(page); 197 if (exec) 198 tx39_blast_icache_page(page); 199 200 return; 201 } 202 203 /* 204 * Do indexed flush, too much work to get the (possible) TLB refills 205 * to work correctly. 206 */ 207 if (cpu_has_dc_aliases || exec) 208 tx39_blast_dcache_page_indexed(page); 209 if (exec) 210 tx39_blast_icache_page_indexed(page); 211} 212 213static void local_tx39_flush_data_cache_page(void * addr) 214{ 215 tx39_blast_dcache_page((unsigned long)addr); 216} 217 218static void tx39_flush_data_cache_page(unsigned long addr) 219{ 220 tx39_blast_dcache_page(addr); 221} 222 223static void tx39_flush_icache_range(unsigned long start, unsigned long end) 224{ 225 if (end - start > dcache_size) 226 tx39_blast_dcache(); 227 else 228 protected_blast_dcache_range(start, end); 229 230 if (end - start > icache_size) 231 tx39_blast_icache(); 232 else { 233 unsigned long flags, config; 234 /* disable icache (set ICE#) */ 235 local_irq_save(flags); 236 config = read_c0_conf(); 237 write_c0_conf(config & ~TX39_CONF_ICE); 238 TX39_STOP_STREAMING(); 239 protected_blast_icache_range(start, end); 240 write_c0_conf(config); 241 local_irq_restore(flags); 242 } 243} 244 245static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size) 246{ 247 unsigned long end; 248 249 if (((size | addr) & (PAGE_SIZE - 1)) == 0) { 250 end = addr + size; 251 do { 252 tx39_blast_dcache_page(addr); 253 addr += PAGE_SIZE; 254 } while(addr != end); 255 } else if (size > dcache_size) { 256 tx39_blast_dcache(); 257 } else { 258 blast_dcache_range(addr, addr + size); 259 } 260} 261 262static void tx39_dma_cache_inv(unsigned long addr, unsigned long size) 263{ 264 unsigned long end; 265 266 if (((size | addr) & (PAGE_SIZE - 1)) == 0) { 267 end = addr + size; 268 do { 269 tx39_blast_dcache_page(addr); 270 addr += PAGE_SIZE; 271 } while(addr != end); 272 } else if (size > dcache_size) { 273 tx39_blast_dcache(); 274 } else { 275 blast_inv_dcache_range(addr, addr + size); 276 } 277} 278 279static void tx39_flush_cache_sigtramp(unsigned long addr) 280{ 281 unsigned long ic_lsize = current_cpu_data.icache.linesz; 282 unsigned long dc_lsize = current_cpu_data.dcache.linesz; 283 unsigned long config; 284 unsigned long flags; 285 286 protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); 287 288 /* disable icache (set ICE#) */ 289 local_irq_save(flags); 290 config = read_c0_conf(); 291 write_c0_conf(config & ~TX39_CONF_ICE); 292 TX39_STOP_STREAMING(); 293 protected_flush_icache_line(addr & ~(ic_lsize - 1)); 294 write_c0_conf(config); 295 local_irq_restore(flags); 296} 297 298static __init void tx39_probe_cache(void) 299{ 300 unsigned long config; 301 302 config = read_c0_conf(); 303 304 icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >> 305 TX39_CONF_ICS_SHIFT)); 306 dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >> 307 TX39_CONF_DCS_SHIFT)); 308 309 current_cpu_data.icache.linesz = 16; 310 switch (current_cpu_type()) { 311 case CPU_TX3912: 312 current_cpu_data.icache.ways = 1; 313 current_cpu_data.dcache.ways = 1; 314 current_cpu_data.dcache.linesz = 4; 315 break; 316 317 case CPU_TX3927: 318 current_cpu_data.icache.ways = 2; 319 current_cpu_data.dcache.ways = 2; 320 current_cpu_data.dcache.linesz = 16; 321 break; 322 323 case CPU_TX3922: 324 default: 325 current_cpu_data.icache.ways = 1; 326 current_cpu_data.dcache.ways = 1; 327 current_cpu_data.dcache.linesz = 16; 328 break; 329 } 330} 331 332void __init tx39_cache_init(void) 333{ 334 extern void build_clear_page(void); 335 extern void build_copy_page(void); 336 unsigned long config; 337 338 config = read_c0_conf(); 339 config &= ~TX39_CONF_WBON; 340 write_c0_conf(config); 341 342 tx39_probe_cache(); 343 344 switch (current_cpu_type()) { 345 case CPU_TX3912: 346 /* TX39/H core (writethru direct-map cache) */ 347 flush_cache_all = tx39h_flush_icache_all; 348 __flush_cache_all = tx39h_flush_icache_all; 349 flush_cache_mm = (void *) tx39h_flush_icache_all; 350 flush_cache_range = (void *) tx39h_flush_icache_all; 351 flush_cache_page = (void *) tx39h_flush_icache_all; 352 flush_icache_range = (void *) tx39h_flush_icache_all; 353 354 flush_cache_sigtramp = (void *) tx39h_flush_icache_all; 355 local_flush_data_cache_page = (void *) tx39h_flush_icache_all; 356 flush_data_cache_page = (void *) tx39h_flush_icache_all; 357 358 _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; 359 360 shm_align_mask = PAGE_SIZE - 1; 361 362 break; 363 364 case CPU_TX3922: 365 case CPU_TX3927: 366 default: 367 /* TX39/H2,H3 core (writeback 2way-set-associative cache) */ 368 r3k_have_wired_reg = 1; 369 write_c0_wired(0); /* set 8 on reset... */ 370 /* board-dependent init code may set WBON */ 371 372 flush_cache_all = tx39_flush_cache_all; 373 __flush_cache_all = tx39___flush_cache_all; 374 flush_cache_mm = tx39_flush_cache_mm; 375 flush_cache_range = tx39_flush_cache_range; 376 flush_cache_page = tx39_flush_cache_page; 377 flush_icache_range = tx39_flush_icache_range; 378 379 flush_cache_sigtramp = tx39_flush_cache_sigtramp; 380 local_flush_data_cache_page = local_tx39_flush_data_cache_page; 381 flush_data_cache_page = tx39_flush_data_cache_page; 382 383 _dma_cache_wback_inv = tx39_dma_cache_wback_inv; 384 _dma_cache_wback = tx39_dma_cache_wback_inv; 385 _dma_cache_inv = tx39_dma_cache_inv; 386 387 shm_align_mask = max_t(unsigned long, 388 (dcache_size / current_cpu_data.dcache.ways) - 1, 389 PAGE_SIZE - 1); 390 391 break; 392 } 393 394 current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways; 395 current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways; 396 397 current_cpu_data.icache.sets = 398 current_cpu_data.icache.waysize / current_cpu_data.icache.linesz; 399 current_cpu_data.dcache.sets = 400 current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz; 401 402 if (current_cpu_data.dcache.waysize > PAGE_SIZE) 403 current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; 404 405 current_cpu_data.icache.waybit = 0; 406 current_cpu_data.dcache.waybit = 0; 407 408 printk("Primary instruction cache %ldkB, linesize %d bytes\n", 409 icache_size >> 10, current_cpu_data.icache.linesz); 410 printk("Primary data cache %ldkB, linesize %d bytes\n", 411 dcache_size >> 10, current_cpu_data.dcache.linesz); 412 413 build_clear_page(); 414 build_copy_page(); 415 tx39h_flush_icache_all(); 416}