cache.c (16973B)
1/* 2 * Cache control for MicroBlaze cache memories 3 * 4 * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> 5 * Copyright (C) 2007-2009 PetaLogix 6 * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com> 7 * 8 * This file is subject to the terms and conditions of the GNU General 9 * Public License. See the file COPYING in the main directory of this 10 * archive for more details. 11 */ 12 13#include <asm/cacheflush.h> 14#include <linux/cache.h> 15#include <asm/cpuinfo.h> 16#include <asm/pvr.h> 17 18static inline void __enable_icache_msr(void) 19{ 20 __asm__ __volatile__ (" msrset r0, %0;" \ 21 "nop;" \ 22 : : "i" (MSR_ICE) : "memory"); 23} 24 25static inline void __disable_icache_msr(void) 26{ 27 __asm__ __volatile__ (" msrclr r0, %0;" \ 28 "nop;" \ 29 : : "i" (MSR_ICE) : "memory"); 30} 31 32static inline void __enable_dcache_msr(void) 33{ 34 __asm__ __volatile__ (" msrset r0, %0;" \ 35 "nop;" \ 36 : : "i" (MSR_DCE) : "memory"); 37} 38 39static inline void __disable_dcache_msr(void) 40{ 41 __asm__ __volatile__ (" msrclr r0, %0;" \ 42 "nop; " \ 43 : : "i" (MSR_DCE) : "memory"); 44} 45 46static inline void __enable_icache_nomsr(void) 47{ 48 __asm__ __volatile__ (" mfs r12, rmsr;" \ 49 "nop;" \ 50 "ori r12, r12, %0;" \ 51 "mts rmsr, r12;" \ 52 "nop;" \ 53 : : "i" (MSR_ICE) : "memory", "r12"); 54} 55 56static inline void __disable_icache_nomsr(void) 57{ 58 __asm__ __volatile__ (" mfs r12, rmsr;" \ 59 "nop;" \ 60 "andi r12, r12, ~%0;" \ 61 "mts rmsr, r12;" \ 62 "nop;" \ 63 : : "i" (MSR_ICE) : "memory", "r12"); 64} 65 66static inline void __enable_dcache_nomsr(void) 67{ 68 __asm__ __volatile__ (" mfs r12, rmsr;" \ 69 "nop;" \ 70 "ori r12, r12, %0;" \ 71 "mts rmsr, r12;" \ 72 "nop;" \ 73 : : "i" (MSR_DCE) : "memory", "r12"); 74} 75 76static inline void __disable_dcache_nomsr(void) 77{ 78 __asm__ __volatile__ (" mfs r12, rmsr;" \ 79 "nop;" \ 80 "andi r12, r12, ~%0;" \ 81 "mts rmsr, r12;" \ 82 "nop;" \ 83 : : "i" (MSR_DCE) : "memory", "r12"); 84} 85 86 87/* Helper macro for computing the limits of cache range loops 88 * 89 * End address can be unaligned which is OK for C implementation. 90 * ASM implementation align it in ASM macros 91 */ 92#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \ 93do { \ 94 int align = ~(cache_line_length - 1); \ 95 if (start < UINT_MAX - cache_size) \ 96 end = min(start + cache_size, end); \ 97 start &= align; \ 98} while (0) 99 100/* 101 * Helper macro to loop over the specified cache_size/line_length and 102 * execute 'op' on that cacheline 103 */ 104#define CACHE_ALL_LOOP(cache_size, line_length, op) \ 105do { \ 106 unsigned int len = cache_size - line_length; \ 107 int step = -line_length; \ 108 WARN_ON(step >= 0); \ 109 \ 110 __asm__ __volatile__ (" 1: " #op " %0, r0;" \ 111 "bgtid %0, 1b;" \ 112 "addk %0, %0, %1;" \ 113 : : "r" (len), "r" (step) \ 114 : "memory"); \ 115} while (0) 116 117/* Used for wdc.flush/clear which can use rB for offset which is not possible 118 * to use for simple wdc or wic. 119 * 120 * start address is cache aligned 121 * end address is not aligned, if end is aligned then I have to subtract 122 * cacheline length because I can't flush/invalidate the next cacheline. 123 * If is not, I align it because I will flush/invalidate whole line. 124 */ 125#define CACHE_RANGE_LOOP_2(start, end, line_length, op) \ 126do { \ 127 int step = -line_length; \ 128 int align = ~(line_length - 1); \ 129 int count; \ 130 end = ((end & align) == end) ? end - line_length : end & align; \ 131 count = end - start; \ 132 WARN_ON(count < 0); \ 133 \ 134 __asm__ __volatile__ (" 1: " #op " %0, %1;" \ 135 "bgtid %1, 1b;" \ 136 "addk %1, %1, %2;" \ 137 : : "r" (start), "r" (count), \ 138 "r" (step) : "memory"); \ 139} while (0) 140 141/* It is used only first parameter for OP - for wic, wdc */ 142#define CACHE_RANGE_LOOP_1(start, end, line_length, op) \ 143do { \ 144 unsigned int volatile temp = 0; \ 145 unsigned int align = ~(line_length - 1); \ 146 end = ((end & align) == end) ? end - line_length : end & align; \ 147 WARN_ON(end < start); \ 148 \ 149 __asm__ __volatile__ (" 1: " #op " %1, r0;" \ 150 "cmpu %0, %1, %2;" \ 151 "bgtid %0, 1b;" \ 152 "addk %1, %1, %3;" \ 153 : : "r" (temp), "r" (start), "r" (end), \ 154 "r" (line_length) : "memory"); \ 155} while (0) 156 157#define ASM_LOOP 158 159static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end) 160{ 161 unsigned long flags; 162#ifndef ASM_LOOP 163 int i; 164#endif 165 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 166 (unsigned int)start, (unsigned int) end); 167 168 CACHE_LOOP_LIMITS(start, end, 169 cpuinfo.icache_line_length, cpuinfo.icache_size); 170 171 local_irq_save(flags); 172 __disable_icache_msr(); 173 174#ifdef ASM_LOOP 175 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); 176#else 177 for (i = start; i < end; i += cpuinfo.icache_line_length) 178 __asm__ __volatile__ ("wic %0, r0;" \ 179 : : "r" (i)); 180#endif 181 __enable_icache_msr(); 182 local_irq_restore(flags); 183} 184 185static void __flush_icache_range_nomsr_irq(unsigned long start, 186 unsigned long end) 187{ 188 unsigned long flags; 189#ifndef ASM_LOOP 190 int i; 191#endif 192 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 193 (unsigned int)start, (unsigned int) end); 194 195 CACHE_LOOP_LIMITS(start, end, 196 cpuinfo.icache_line_length, cpuinfo.icache_size); 197 198 local_irq_save(flags); 199 __disable_icache_nomsr(); 200 201#ifdef ASM_LOOP 202 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); 203#else 204 for (i = start; i < end; i += cpuinfo.icache_line_length) 205 __asm__ __volatile__ ("wic %0, r0;" \ 206 : : "r" (i)); 207#endif 208 209 __enable_icache_nomsr(); 210 local_irq_restore(flags); 211} 212 213static void __flush_icache_range_noirq(unsigned long start, 214 unsigned long end) 215{ 216#ifndef ASM_LOOP 217 int i; 218#endif 219 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 220 (unsigned int)start, (unsigned int) end); 221 222 CACHE_LOOP_LIMITS(start, end, 223 cpuinfo.icache_line_length, cpuinfo.icache_size); 224#ifdef ASM_LOOP 225 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); 226#else 227 for (i = start; i < end; i += cpuinfo.icache_line_length) 228 __asm__ __volatile__ ("wic %0, r0;" \ 229 : : "r" (i)); 230#endif 231} 232 233static void __flush_icache_all_msr_irq(void) 234{ 235 unsigned long flags; 236#ifndef ASM_LOOP 237 int i; 238#endif 239 pr_debug("%s\n", __func__); 240 241 local_irq_save(flags); 242 __disable_icache_msr(); 243#ifdef ASM_LOOP 244 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); 245#else 246 for (i = 0; i < cpuinfo.icache_size; 247 i += cpuinfo.icache_line_length) 248 __asm__ __volatile__ ("wic %0, r0;" \ 249 : : "r" (i)); 250#endif 251 __enable_icache_msr(); 252 local_irq_restore(flags); 253} 254 255static void __flush_icache_all_nomsr_irq(void) 256{ 257 unsigned long flags; 258#ifndef ASM_LOOP 259 int i; 260#endif 261 pr_debug("%s\n", __func__); 262 263 local_irq_save(flags); 264 __disable_icache_nomsr(); 265#ifdef ASM_LOOP 266 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); 267#else 268 for (i = 0; i < cpuinfo.icache_size; 269 i += cpuinfo.icache_line_length) 270 __asm__ __volatile__ ("wic %0, r0;" \ 271 : : "r" (i)); 272#endif 273 __enable_icache_nomsr(); 274 local_irq_restore(flags); 275} 276 277static void __flush_icache_all_noirq(void) 278{ 279#ifndef ASM_LOOP 280 int i; 281#endif 282 pr_debug("%s\n", __func__); 283#ifdef ASM_LOOP 284 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); 285#else 286 for (i = 0; i < cpuinfo.icache_size; 287 i += cpuinfo.icache_line_length) 288 __asm__ __volatile__ ("wic %0, r0;" \ 289 : : "r" (i)); 290#endif 291} 292 293static void __invalidate_dcache_all_msr_irq(void) 294{ 295 unsigned long flags; 296#ifndef ASM_LOOP 297 int i; 298#endif 299 pr_debug("%s\n", __func__); 300 301 local_irq_save(flags); 302 __disable_dcache_msr(); 303#ifdef ASM_LOOP 304 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); 305#else 306 for (i = 0; i < cpuinfo.dcache_size; 307 i += cpuinfo.dcache_line_length) 308 __asm__ __volatile__ ("wdc %0, r0;" \ 309 : : "r" (i)); 310#endif 311 __enable_dcache_msr(); 312 local_irq_restore(flags); 313} 314 315static void __invalidate_dcache_all_nomsr_irq(void) 316{ 317 unsigned long flags; 318#ifndef ASM_LOOP 319 int i; 320#endif 321 pr_debug("%s\n", __func__); 322 323 local_irq_save(flags); 324 __disable_dcache_nomsr(); 325#ifdef ASM_LOOP 326 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); 327#else 328 for (i = 0; i < cpuinfo.dcache_size; 329 i += cpuinfo.dcache_line_length) 330 __asm__ __volatile__ ("wdc %0, r0;" \ 331 : : "r" (i)); 332#endif 333 __enable_dcache_nomsr(); 334 local_irq_restore(flags); 335} 336 337static void __invalidate_dcache_all_noirq_wt(void) 338{ 339#ifndef ASM_LOOP 340 int i; 341#endif 342 pr_debug("%s\n", __func__); 343#ifdef ASM_LOOP 344 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); 345#else 346 for (i = 0; i < cpuinfo.dcache_size; 347 i += cpuinfo.dcache_line_length) 348 __asm__ __volatile__ ("wdc %0, r0;" \ 349 : : "r" (i)); 350#endif 351} 352 353/* 354 * FIXME It is blindly invalidation as is expected 355 * but can't be called on noMMU in microblaze_cache_init below 356 * 357 * MS: noMMU kernel won't boot if simple wdc is used 358 * The reason should be that there are discared data which kernel needs 359 */ 360static void __invalidate_dcache_all_wb(void) 361{ 362#ifndef ASM_LOOP 363 int i; 364#endif 365 pr_debug("%s\n", __func__); 366#ifdef ASM_LOOP 367 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, 368 wdc); 369#else 370 for (i = 0; i < cpuinfo.dcache_size; 371 i += cpuinfo.dcache_line_length) 372 __asm__ __volatile__ ("wdc %0, r0;" \ 373 : : "r" (i)); 374#endif 375} 376 377static void __invalidate_dcache_range_wb(unsigned long start, 378 unsigned long end) 379{ 380#ifndef ASM_LOOP 381 int i; 382#endif 383 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 384 (unsigned int)start, (unsigned int) end); 385 386 CACHE_LOOP_LIMITS(start, end, 387 cpuinfo.dcache_line_length, cpuinfo.dcache_size); 388#ifdef ASM_LOOP 389 CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear); 390#else 391 for (i = start; i < end; i += cpuinfo.dcache_line_length) 392 __asm__ __volatile__ ("wdc.clear %0, r0;" \ 393 : : "r" (i)); 394#endif 395} 396 397static void __invalidate_dcache_range_nomsr_wt(unsigned long start, 398 unsigned long end) 399{ 400#ifndef ASM_LOOP 401 int i; 402#endif 403 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 404 (unsigned int)start, (unsigned int) end); 405 CACHE_LOOP_LIMITS(start, end, 406 cpuinfo.dcache_line_length, cpuinfo.dcache_size); 407 408#ifdef ASM_LOOP 409 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); 410#else 411 for (i = start; i < end; i += cpuinfo.dcache_line_length) 412 __asm__ __volatile__ ("wdc %0, r0;" \ 413 : : "r" (i)); 414#endif 415} 416 417static void __invalidate_dcache_range_msr_irq_wt(unsigned long start, 418 unsigned long end) 419{ 420 unsigned long flags; 421#ifndef ASM_LOOP 422 int i; 423#endif 424 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 425 (unsigned int)start, (unsigned int) end); 426 CACHE_LOOP_LIMITS(start, end, 427 cpuinfo.dcache_line_length, cpuinfo.dcache_size); 428 429 local_irq_save(flags); 430 __disable_dcache_msr(); 431 432#ifdef ASM_LOOP 433 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); 434#else 435 for (i = start; i < end; i += cpuinfo.dcache_line_length) 436 __asm__ __volatile__ ("wdc %0, r0;" \ 437 : : "r" (i)); 438#endif 439 440 __enable_dcache_msr(); 441 local_irq_restore(flags); 442} 443 444static void __invalidate_dcache_range_nomsr_irq(unsigned long start, 445 unsigned long end) 446{ 447 unsigned long flags; 448#ifndef ASM_LOOP 449 int i; 450#endif 451 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 452 (unsigned int)start, (unsigned int) end); 453 454 CACHE_LOOP_LIMITS(start, end, 455 cpuinfo.dcache_line_length, cpuinfo.dcache_size); 456 457 local_irq_save(flags); 458 __disable_dcache_nomsr(); 459 460#ifdef ASM_LOOP 461 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); 462#else 463 for (i = start; i < end; i += cpuinfo.dcache_line_length) 464 __asm__ __volatile__ ("wdc %0, r0;" \ 465 : : "r" (i)); 466#endif 467 468 __enable_dcache_nomsr(); 469 local_irq_restore(flags); 470} 471 472static void __flush_dcache_all_wb(void) 473{ 474#ifndef ASM_LOOP 475 int i; 476#endif 477 pr_debug("%s\n", __func__); 478#ifdef ASM_LOOP 479 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, 480 wdc.flush); 481#else 482 for (i = 0; i < cpuinfo.dcache_size; 483 i += cpuinfo.dcache_line_length) 484 __asm__ __volatile__ ("wdc.flush %0, r0;" \ 485 : : "r" (i)); 486#endif 487} 488 489static void __flush_dcache_range_wb(unsigned long start, unsigned long end) 490{ 491#ifndef ASM_LOOP 492 int i; 493#endif 494 pr_debug("%s: start 0x%x, end 0x%x\n", __func__, 495 (unsigned int)start, (unsigned int) end); 496 497 CACHE_LOOP_LIMITS(start, end, 498 cpuinfo.dcache_line_length, cpuinfo.dcache_size); 499#ifdef ASM_LOOP 500 CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush); 501#else 502 for (i = start; i < end; i += cpuinfo.dcache_line_length) 503 __asm__ __volatile__ ("wdc.flush %0, r0;" \ 504 : : "r" (i)); 505#endif 506} 507 508/* struct for wb caches and for wt caches */ 509struct scache *mbc; 510 511/* new wb cache model */ 512static const struct scache wb_msr = { 513 .ie = __enable_icache_msr, 514 .id = __disable_icache_msr, 515 .ifl = __flush_icache_all_noirq, 516 .iflr = __flush_icache_range_noirq, 517 .iin = __flush_icache_all_noirq, 518 .iinr = __flush_icache_range_noirq, 519 .de = __enable_dcache_msr, 520 .dd = __disable_dcache_msr, 521 .dfl = __flush_dcache_all_wb, 522 .dflr = __flush_dcache_range_wb, 523 .din = __invalidate_dcache_all_wb, 524 .dinr = __invalidate_dcache_range_wb, 525}; 526 527/* There is only difference in ie, id, de, dd functions */ 528static const struct scache wb_nomsr = { 529 .ie = __enable_icache_nomsr, 530 .id = __disable_icache_nomsr, 531 .ifl = __flush_icache_all_noirq, 532 .iflr = __flush_icache_range_noirq, 533 .iin = __flush_icache_all_noirq, 534 .iinr = __flush_icache_range_noirq, 535 .de = __enable_dcache_nomsr, 536 .dd = __disable_dcache_nomsr, 537 .dfl = __flush_dcache_all_wb, 538 .dflr = __flush_dcache_range_wb, 539 .din = __invalidate_dcache_all_wb, 540 .dinr = __invalidate_dcache_range_wb, 541}; 542 543/* Old wt cache model with disabling irq and turn off cache */ 544static const struct scache wt_msr = { 545 .ie = __enable_icache_msr, 546 .id = __disable_icache_msr, 547 .ifl = __flush_icache_all_msr_irq, 548 .iflr = __flush_icache_range_msr_irq, 549 .iin = __flush_icache_all_msr_irq, 550 .iinr = __flush_icache_range_msr_irq, 551 .de = __enable_dcache_msr, 552 .dd = __disable_dcache_msr, 553 .dfl = __invalidate_dcache_all_msr_irq, 554 .dflr = __invalidate_dcache_range_msr_irq_wt, 555 .din = __invalidate_dcache_all_msr_irq, 556 .dinr = __invalidate_dcache_range_msr_irq_wt, 557}; 558 559static const struct scache wt_nomsr = { 560 .ie = __enable_icache_nomsr, 561 .id = __disable_icache_nomsr, 562 .ifl = __flush_icache_all_nomsr_irq, 563 .iflr = __flush_icache_range_nomsr_irq, 564 .iin = __flush_icache_all_nomsr_irq, 565 .iinr = __flush_icache_range_nomsr_irq, 566 .de = __enable_dcache_nomsr, 567 .dd = __disable_dcache_nomsr, 568 .dfl = __invalidate_dcache_all_nomsr_irq, 569 .dflr = __invalidate_dcache_range_nomsr_irq, 570 .din = __invalidate_dcache_all_nomsr_irq, 571 .dinr = __invalidate_dcache_range_nomsr_irq, 572}; 573 574/* New wt cache model for newer Microblaze versions */ 575static const struct scache wt_msr_noirq = { 576 .ie = __enable_icache_msr, 577 .id = __disable_icache_msr, 578 .ifl = __flush_icache_all_noirq, 579 .iflr = __flush_icache_range_noirq, 580 .iin = __flush_icache_all_noirq, 581 .iinr = __flush_icache_range_noirq, 582 .de = __enable_dcache_msr, 583 .dd = __disable_dcache_msr, 584 .dfl = __invalidate_dcache_all_noirq_wt, 585 .dflr = __invalidate_dcache_range_nomsr_wt, 586 .din = __invalidate_dcache_all_noirq_wt, 587 .dinr = __invalidate_dcache_range_nomsr_wt, 588}; 589 590static const struct scache wt_nomsr_noirq = { 591 .ie = __enable_icache_nomsr, 592 .id = __disable_icache_nomsr, 593 .ifl = __flush_icache_all_noirq, 594 .iflr = __flush_icache_range_noirq, 595 .iin = __flush_icache_all_noirq, 596 .iinr = __flush_icache_range_noirq, 597 .de = __enable_dcache_nomsr, 598 .dd = __disable_dcache_nomsr, 599 .dfl = __invalidate_dcache_all_noirq_wt, 600 .dflr = __invalidate_dcache_range_nomsr_wt, 601 .din = __invalidate_dcache_all_noirq_wt, 602 .dinr = __invalidate_dcache_range_nomsr_wt, 603}; 604 605/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */ 606#define CPUVER_7_20_A 0x0c 607#define CPUVER_7_20_D 0x0f 608 609void microblaze_cache_init(void) 610{ 611 if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) { 612 if (cpuinfo.dcache_wb) { 613 pr_info("wb_msr\n"); 614 mbc = (struct scache *)&wb_msr; 615 if (cpuinfo.ver_code <= CPUVER_7_20_D) { 616 /* MS: problem with signal handling - hw bug */ 617 pr_info("WB won't work properly\n"); 618 } 619 } else { 620 if (cpuinfo.ver_code >= CPUVER_7_20_A) { 621 pr_info("wt_msr_noirq\n"); 622 mbc = (struct scache *)&wt_msr_noirq; 623 } else { 624 pr_info("wt_msr\n"); 625 mbc = (struct scache *)&wt_msr; 626 } 627 } 628 } else { 629 if (cpuinfo.dcache_wb) { 630 pr_info("wb_nomsr\n"); 631 mbc = (struct scache *)&wb_nomsr; 632 if (cpuinfo.ver_code <= CPUVER_7_20_D) { 633 /* MS: problem with signal handling - hw bug */ 634 pr_info("WB won't work properly\n"); 635 } 636 } else { 637 if (cpuinfo.ver_code >= CPUVER_7_20_A) { 638 pr_info("wt_nomsr_noirq\n"); 639 mbc = (struct scache *)&wt_nomsr_noirq; 640 } else { 641 pr_info("wt_nomsr\n"); 642 mbc = (struct scache *)&wt_nomsr; 643 } 644 } 645 } 646 /* 647 * FIXME Invalidation is done in U-BOOT 648 * WT cache: Data is already written to main memory 649 * WB cache: Discard data on noMMU which caused that kernel doesn't boot 650 */ 651 /* invalidate_dcache(); */ 652 enable_dcache(); 653 654 invalidate_icache(); 655 enable_icache(); 656}