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