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.28 433 lines 11 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 void tx39__flush_cache_vmap(void) 126{ 127 tx39_blast_dcache(); 128} 129 130static void tx39__flush_cache_vunmap(void) 131{ 132 tx39_blast_dcache(); 133} 134 135static inline void tx39_flush_cache_all(void) 136{ 137 if (!cpu_has_dc_aliases) 138 return; 139 140 tx39_blast_dcache(); 141} 142 143static inline void tx39___flush_cache_all(void) 144{ 145 tx39_blast_dcache(); 146 tx39_blast_icache(); 147} 148 149static void tx39_flush_cache_mm(struct mm_struct *mm) 150{ 151 if (!cpu_has_dc_aliases) 152 return; 153 154 if (cpu_context(smp_processor_id(), mm) != 0) 155 tx39_blast_dcache(); 156} 157 158static void tx39_flush_cache_range(struct vm_area_struct *vma, 159 unsigned long start, unsigned long end) 160{ 161 if (!cpu_has_dc_aliases) 162 return; 163 if (!(cpu_context(smp_processor_id(), vma->vm_mm))) 164 return; 165 166 tx39_blast_dcache(); 167} 168 169static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) 170{ 171 int exec = vma->vm_flags & VM_EXEC; 172 struct mm_struct *mm = vma->vm_mm; 173 pgd_t *pgdp; 174 pud_t *pudp; 175 pmd_t *pmdp; 176 pte_t *ptep; 177 178 /* 179 * If ownes no valid ASID yet, cannot possibly have gotten 180 * this page into the cache. 181 */ 182 if (cpu_context(smp_processor_id(), mm) == 0) 183 return; 184 185 page &= PAGE_MASK; 186 pgdp = pgd_offset(mm, page); 187 pudp = pud_offset(pgdp, page); 188 pmdp = pmd_offset(pudp, page); 189 ptep = pte_offset(pmdp, page); 190 191 /* 192 * If the page isn't marked valid, the page cannot possibly be 193 * in the cache. 194 */ 195 if (!(pte_val(*ptep) & _PAGE_PRESENT)) 196 return; 197 198 /* 199 * Doing flushes for another ASID than the current one is 200 * too difficult since stupid R4k caches do a TLB translation 201 * for every cache flush operation. So we do indexed flushes 202 * in that case, which doesn't overly flush the cache too much. 203 */ 204 if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { 205 if (cpu_has_dc_aliases || exec) 206 tx39_blast_dcache_page(page); 207 if (exec) 208 tx39_blast_icache_page(page); 209 210 return; 211 } 212 213 /* 214 * Do indexed flush, too much work to get the (possible) TLB refills 215 * to work correctly. 216 */ 217 if (cpu_has_dc_aliases || exec) 218 tx39_blast_dcache_page_indexed(page); 219 if (exec) 220 tx39_blast_icache_page_indexed(page); 221} 222 223static void local_tx39_flush_data_cache_page(void * addr) 224{ 225 tx39_blast_dcache_page((unsigned long)addr); 226} 227 228static void tx39_flush_data_cache_page(unsigned long addr) 229{ 230 tx39_blast_dcache_page(addr); 231} 232 233static void tx39_flush_icache_range(unsigned long start, unsigned long end) 234{ 235 if (end - start > dcache_size) 236 tx39_blast_dcache(); 237 else 238 protected_blast_dcache_range(start, end); 239 240 if (end - start > icache_size) 241 tx39_blast_icache(); 242 else { 243 unsigned long flags, config; 244 /* disable icache (set ICE#) */ 245 local_irq_save(flags); 246 config = read_c0_conf(); 247 write_c0_conf(config & ~TX39_CONF_ICE); 248 TX39_STOP_STREAMING(); 249 protected_blast_icache_range(start, end); 250 write_c0_conf(config); 251 local_irq_restore(flags); 252 } 253} 254 255static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size) 256{ 257 unsigned long end; 258 259 if (((size | addr) & (PAGE_SIZE - 1)) == 0) { 260 end = addr + size; 261 do { 262 tx39_blast_dcache_page(addr); 263 addr += PAGE_SIZE; 264 } while(addr != end); 265 } else if (size > dcache_size) { 266 tx39_blast_dcache(); 267 } else { 268 blast_dcache_range(addr, addr + size); 269 } 270} 271 272static void tx39_dma_cache_inv(unsigned long addr, unsigned long size) 273{ 274 unsigned long end; 275 276 if (((size | addr) & (PAGE_SIZE - 1)) == 0) { 277 end = addr + size; 278 do { 279 tx39_blast_dcache_page(addr); 280 addr += PAGE_SIZE; 281 } while(addr != end); 282 } else if (size > dcache_size) { 283 tx39_blast_dcache(); 284 } else { 285 blast_inv_dcache_range(addr, addr + size); 286 } 287} 288 289static void tx39_flush_cache_sigtramp(unsigned long addr) 290{ 291 unsigned long ic_lsize = current_cpu_data.icache.linesz; 292 unsigned long dc_lsize = current_cpu_data.dcache.linesz; 293 unsigned long config; 294 unsigned long flags; 295 296 protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); 297 298 /* disable icache (set ICE#) */ 299 local_irq_save(flags); 300 config = read_c0_conf(); 301 write_c0_conf(config & ~TX39_CONF_ICE); 302 TX39_STOP_STREAMING(); 303 protected_flush_icache_line(addr & ~(ic_lsize - 1)); 304 write_c0_conf(config); 305 local_irq_restore(flags); 306} 307 308static __init void tx39_probe_cache(void) 309{ 310 unsigned long config; 311 312 config = read_c0_conf(); 313 314 icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >> 315 TX39_CONF_ICS_SHIFT)); 316 dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >> 317 TX39_CONF_DCS_SHIFT)); 318 319 current_cpu_data.icache.linesz = 16; 320 switch (current_cpu_type()) { 321 case CPU_TX3912: 322 current_cpu_data.icache.ways = 1; 323 current_cpu_data.dcache.ways = 1; 324 current_cpu_data.dcache.linesz = 4; 325 break; 326 327 case CPU_TX3927: 328 current_cpu_data.icache.ways = 2; 329 current_cpu_data.dcache.ways = 2; 330 current_cpu_data.dcache.linesz = 16; 331 break; 332 333 case CPU_TX3922: 334 default: 335 current_cpu_data.icache.ways = 1; 336 current_cpu_data.dcache.ways = 1; 337 current_cpu_data.dcache.linesz = 16; 338 break; 339 } 340} 341 342void __cpuinit tx39_cache_init(void) 343{ 344 extern void build_clear_page(void); 345 extern void build_copy_page(void); 346 unsigned long config; 347 348 config = read_c0_conf(); 349 config &= ~TX39_CONF_WBON; 350 write_c0_conf(config); 351 352 tx39_probe_cache(); 353 354 switch (current_cpu_type()) { 355 case CPU_TX3912: 356 /* TX39/H core (writethru direct-map cache) */ 357 __flush_cache_vmap = tx39__flush_cache_vmap; 358 __flush_cache_vunmap = tx39__flush_cache_vunmap; 359 flush_cache_all = tx39h_flush_icache_all; 360 __flush_cache_all = tx39h_flush_icache_all; 361 flush_cache_mm = (void *) tx39h_flush_icache_all; 362 flush_cache_range = (void *) tx39h_flush_icache_all; 363 flush_cache_page = (void *) tx39h_flush_icache_all; 364 flush_icache_range = (void *) tx39h_flush_icache_all; 365 local_flush_icache_range = (void *) tx39h_flush_icache_all; 366 367 flush_cache_sigtramp = (void *) tx39h_flush_icache_all; 368 local_flush_data_cache_page = (void *) tx39h_flush_icache_all; 369 flush_data_cache_page = (void *) tx39h_flush_icache_all; 370 371 _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; 372 373 shm_align_mask = PAGE_SIZE - 1; 374 375 break; 376 377 case CPU_TX3922: 378 case CPU_TX3927: 379 default: 380 /* TX39/H2,H3 core (writeback 2way-set-associative cache) */ 381 r3k_have_wired_reg = 1; 382 write_c0_wired(0); /* set 8 on reset... */ 383 /* board-dependent init code may set WBON */ 384 385 __flush_cache_vmap = tx39__flush_cache_vmap; 386 __flush_cache_vunmap = tx39__flush_cache_vunmap; 387 388 flush_cache_all = tx39_flush_cache_all; 389 __flush_cache_all = tx39___flush_cache_all; 390 flush_cache_mm = tx39_flush_cache_mm; 391 flush_cache_range = tx39_flush_cache_range; 392 flush_cache_page = tx39_flush_cache_page; 393 flush_icache_range = tx39_flush_icache_range; 394 local_flush_icache_range = tx39_flush_icache_range; 395 396 flush_cache_sigtramp = tx39_flush_cache_sigtramp; 397 local_flush_data_cache_page = local_tx39_flush_data_cache_page; 398 flush_data_cache_page = tx39_flush_data_cache_page; 399 400 _dma_cache_wback_inv = tx39_dma_cache_wback_inv; 401 _dma_cache_wback = tx39_dma_cache_wback_inv; 402 _dma_cache_inv = tx39_dma_cache_inv; 403 404 shm_align_mask = max_t(unsigned long, 405 (dcache_size / current_cpu_data.dcache.ways) - 1, 406 PAGE_SIZE - 1); 407 408 break; 409 } 410 411 current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways; 412 current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways; 413 414 current_cpu_data.icache.sets = 415 current_cpu_data.icache.waysize / current_cpu_data.icache.linesz; 416 current_cpu_data.dcache.sets = 417 current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz; 418 419 if (current_cpu_data.dcache.waysize > PAGE_SIZE) 420 current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; 421 422 current_cpu_data.icache.waybit = 0; 423 current_cpu_data.dcache.waybit = 0; 424 425 printk("Primary instruction cache %ldkB, linesize %d bytes\n", 426 icache_size >> 10, current_cpu_data.icache.linesz); 427 printk("Primary data cache %ldkB, linesize %d bytes\n", 428 dcache_size >> 10, current_cpu_data.dcache.linesz); 429 430 build_clear_page(); 431 build_copy_page(); 432 tx39h_flush_icache_all(); 433}