1/* 2 * linux/arch/arm/mm/proc-feroceon.S: MMU functions for Feroceon 3 * 4 * Heavily based on proc-arm926.S 5 * Maintainer: Assaf Hoffman <hoffman@marvell.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#include <linux/linkage.h> 23#include <linux/init.h> 24#include <asm/assembler.h> 25#include <asm/elf.h> 26#include <asm/pgtable-hwdef.h> 27#include <asm/pgtable.h> 28#include <asm/page.h> 29#include <asm/ptrace.h> 30#include "proc-macros.S" 31 32/* 33 * This is the maximum size of an area which will be invalidated 34 * using the single invalidate entry instructions. Anything larger 35 * than this, and we go for the whole cache. 36 * 37 * This value should be chosen such that we choose the cheapest 38 * alternative. 39 */ 40#define CACHE_DLIMIT 16384 41 42/* 43 * the cache line size of the I and D cache 44 */ 45#define CACHE_DLINESIZE 32 46 47 .bss 48 .align 3 49__cache_params_loc: 50 .space 8 51 52 .text 53__cache_params: 54 .word __cache_params_loc 55 56/* 57 * cpu_feroceon_proc_init() 58 */ 59ENTRY(cpu_feroceon_proc_init) 60 mrc p15, 0, r0, c0, c0, 1 @ read cache type register 61 ldr r1, __cache_params 62 mov r2, #(16 << 5) 63 tst r0, #(1 << 16) @ get way 64 mov r0, r0, lsr #18 @ get cache size order 65 movne r3, #((4 - 1) << 30) @ 4-way 66 and r0, r0, #0xf 67 moveq r3, #0 @ 1-way 68 mov r2, r2, lsl r0 @ actual cache size 69 movne r2, r2, lsr #2 @ turned into # of sets 70 sub r2, r2, #(1 << 5) 71 stmia r1, {r2, r3} 72 mov pc, lr 73 74/* 75 * cpu_feroceon_proc_fin() 76 */ 77ENTRY(cpu_feroceon_proc_fin) 78 stmfd sp!, {lr} 79 mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE 80 msr cpsr_c, ip 81 bl feroceon_flush_kern_cache_all 82 83#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH) 84 mov r0, #0 85 mcr p15, 1, r0, c15, c9, 0 @ clean L2 86 mcr p15, 0, r0, c7, c10, 4 @ drain WB 87#endif 88 89 mrc p15, 0, r0, c1, c0, 0 @ ctrl register 90 bic r0, r0, #0x1000 @ ...i............ 91 bic r0, r0, #0x000e @ ............wca. 92 mcr p15, 0, r0, c1, c0, 0 @ disable caches 93 ldmfd sp!, {pc} 94 95/* 96 * cpu_feroceon_reset(loc) 97 * 98 * Perform a soft reset of the system. Put the CPU into the 99 * same state as it would be if it had been reset, and branch 100 * to what would be the reset vector. 101 * 102 * loc: location to jump to for soft reset 103 */ 104 .align 5 105ENTRY(cpu_feroceon_reset) 106 mov ip, #0 107 mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches 108 mcr p15, 0, ip, c7, c10, 4 @ drain WB 109#ifdef CONFIG_MMU 110 mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs 111#endif 112 mrc p15, 0, ip, c1, c0, 0 @ ctrl register 113 bic ip, ip, #0x000f @ ............wcam 114 bic ip, ip, #0x1100 @ ...i...s........ 115 mcr p15, 0, ip, c1, c0, 0 @ ctrl register 116 mov pc, r0 117 118/* 119 * cpu_feroceon_do_idle() 120 * 121 * Called with IRQs disabled 122 */ 123 .align 5 124ENTRY(cpu_feroceon_do_idle) 125 mov r0, #0 126 mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer 127 mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt 128 mov pc, lr 129 130/* 131 * flush_user_cache_all() 132 * 133 * Clean and invalidate all cache entries in a particular 134 * address space. 135 */ 136 .align 5 137ENTRY(feroceon_flush_user_cache_all) 138 /* FALLTHROUGH */ 139 140/* 141 * flush_kern_cache_all() 142 * 143 * Clean and invalidate the entire cache. 144 */ 145ENTRY(feroceon_flush_kern_cache_all) 146 mov r2, #VM_EXEC 147 148__flush_whole_cache: 149 ldr r1, __cache_params 150 ldmia r1, {r1, r3} 1511: orr ip, r1, r3 1522: mcr p15, 0, ip, c7, c14, 2 @ clean + invalidate D set/way 153 subs ip, ip, #(1 << 30) @ next way 154 bcs 2b 155 subs r1, r1, #(1 << 5) @ next set 156 bcs 1b 157 158 tst r2, #VM_EXEC 159 mov ip, #0 160 mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache 161 mcrne p15, 0, ip, c7, c10, 4 @ drain WB 162 mov pc, lr 163 164/* 165 * flush_user_cache_range(start, end, flags) 166 * 167 * Clean and invalidate a range of cache entries in the 168 * specified address range. 169 * 170 * - start - start address (inclusive) 171 * - end - end address (exclusive) 172 * - flags - vm_flags describing address space 173 */ 174 .align 5 175ENTRY(feroceon_flush_user_cache_range) 176 sub r3, r1, r0 @ calculate total size 177 cmp r3, #CACHE_DLIMIT 178 bgt __flush_whole_cache 1791: tst r2, #VM_EXEC 180 mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry 181 mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry 182 add r0, r0, #CACHE_DLINESIZE 183 mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry 184 mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry 185 add r0, r0, #CACHE_DLINESIZE 186 cmp r0, r1 187 blo 1b 188 tst r2, #VM_EXEC 189 mov ip, #0 190 mcrne p15, 0, ip, c7, c10, 4 @ drain WB 191 mov pc, lr 192 193/* 194 * coherent_kern_range(start, end) 195 * 196 * Ensure coherency between the Icache and the Dcache in the 197 * region described by start, end. If you have non-snooping 198 * Harvard caches, you need to implement this function. 199 * 200 * - start - virtual start address 201 * - end - virtual end address 202 */ 203 .align 5 204ENTRY(feroceon_coherent_kern_range) 205 /* FALLTHROUGH */ 206 207/* 208 * coherent_user_range(start, end) 209 * 210 * Ensure coherency between the Icache and the Dcache in the 211 * region described by start, end. If you have non-snooping 212 * Harvard caches, you need to implement this function. 213 * 214 * - start - virtual start address 215 * - end - virtual end address 216 */ 217ENTRY(feroceon_coherent_user_range) 218 bic r0, r0, #CACHE_DLINESIZE - 1 2191: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 220 mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry 221 add r0, r0, #CACHE_DLINESIZE 222 cmp r0, r1 223 blo 1b 224 mcr p15, 0, r0, c7, c10, 4 @ drain WB 225 mov pc, lr 226 227/* 228 * flush_kern_dcache_page(void *page) 229 * 230 * Ensure no D cache aliasing occurs, either with itself or 231 * the I cache 232 * 233 * - addr - page aligned address 234 */ 235 .align 5 236ENTRY(feroceon_flush_kern_dcache_page) 237 add r1, r0, #PAGE_SZ 2381: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry 239 add r0, r0, #CACHE_DLINESIZE 240 cmp r0, r1 241 blo 1b 242 mov r0, #0 243 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache 244 mcr p15, 0, r0, c7, c10, 4 @ drain WB 245 mov pc, lr 246 247 .align 5 248ENTRY(feroceon_range_flush_kern_dcache_page) 249 mrs r2, cpsr 250 add r1, r0, #PAGE_SZ - CACHE_DLINESIZE @ top addr is inclusive 251 orr r3, r2, #PSR_I_BIT 252 msr cpsr_c, r3 @ disable interrupts 253 mcr p15, 5, r0, c15, c15, 0 @ D clean/inv range start 254 mcr p15, 5, r1, c15, c15, 1 @ D clean/inv range top 255 msr cpsr_c, r2 @ restore interrupts 256 mov r0, #0 257 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache 258 mcr p15, 0, r0, c7, c10, 4 @ drain WB 259 mov pc, lr 260 261/* 262 * dma_inv_range(start, end) 263 * 264 * Invalidate (discard) the specified virtual address range. 265 * May not write back any entries. If 'start' or 'end' 266 * are not cache line aligned, those lines must be written 267 * back. 268 * 269 * - start - virtual start address 270 * - end - virtual end address 271 * 272 * (same as v4wb) 273 */ 274 .align 5 275ENTRY(feroceon_dma_inv_range) 276 tst r0, #CACHE_DLINESIZE - 1 277 bic r0, r0, #CACHE_DLINESIZE - 1 278 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry 279 tst r1, #CACHE_DLINESIZE - 1 280 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry 2811: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry 282 add r0, r0, #CACHE_DLINESIZE 283 cmp r0, r1 284 blo 1b 285 mcr p15, 0, r0, c7, c10, 4 @ drain WB 286 mov pc, lr 287 288 .align 5 289ENTRY(feroceon_range_dma_inv_range) 290 mrs r2, cpsr 291 tst r0, #CACHE_DLINESIZE - 1 292 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry 293 tst r1, #CACHE_DLINESIZE - 1 294 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry 295 cmp r1, r0 296 subne r1, r1, #1 @ top address is inclusive 297 orr r3, r2, #PSR_I_BIT 298 msr cpsr_c, r3 @ disable interrupts 299 mcr p15, 5, r0, c15, c14, 0 @ D inv range start 300 mcr p15, 5, r1, c15, c14, 1 @ D inv range top 301 msr cpsr_c, r2 @ restore interrupts 302 mov pc, lr 303 304/* 305 * dma_clean_range(start, end) 306 * 307 * Clean the specified virtual address range. 308 * 309 * - start - virtual start address 310 * - end - virtual end address 311 * 312 * (same as v4wb) 313 */ 314 .align 5 315ENTRY(feroceon_dma_clean_range) 316 bic r0, r0, #CACHE_DLINESIZE - 1 3171: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 318 add r0, r0, #CACHE_DLINESIZE 319 cmp r0, r1 320 blo 1b 321 mcr p15, 0, r0, c7, c10, 4 @ drain WB 322 mov pc, lr 323 324 .align 5 325ENTRY(feroceon_range_dma_clean_range) 326 mrs r2, cpsr 327 cmp r1, r0 328 subne r1, r1, #1 @ top address is inclusive 329 orr r3, r2, #PSR_I_BIT 330 msr cpsr_c, r3 @ disable interrupts 331 mcr p15, 5, r0, c15, c13, 0 @ D clean range start 332 mcr p15, 5, r1, c15, c13, 1 @ D clean range top 333 msr cpsr_c, r2 @ restore interrupts 334 mcr p15, 0, r0, c7, c10, 4 @ drain WB 335 mov pc, lr 336 337/* 338 * dma_flush_range(start, end) 339 * 340 * Clean and invalidate the specified virtual address range. 341 * 342 * - start - virtual start address 343 * - end - virtual end address 344 */ 345 .align 5 346ENTRY(feroceon_dma_flush_range) 347 bic r0, r0, #CACHE_DLINESIZE - 1 3481: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry 349 add r0, r0, #CACHE_DLINESIZE 350 cmp r0, r1 351 blo 1b 352 mcr p15, 0, r0, c7, c10, 4 @ drain WB 353 mov pc, lr 354 355 .align 5 356ENTRY(feroceon_range_dma_flush_range) 357 mrs r2, cpsr 358 cmp r1, r0 359 subne r1, r1, #1 @ top address is inclusive 360 orr r3, r2, #PSR_I_BIT 361 msr cpsr_c, r3 @ disable interrupts 362 mcr p15, 5, r0, c15, c15, 0 @ D clean/inv range start 363 mcr p15, 5, r1, c15, c15, 1 @ D clean/inv range top 364 msr cpsr_c, r2 @ restore interrupts 365 mcr p15, 0, r0, c7, c10, 4 @ drain WB 366 mov pc, lr 367 368ENTRY(feroceon_cache_fns) 369 .long feroceon_flush_kern_cache_all 370 .long feroceon_flush_user_cache_all 371 .long feroceon_flush_user_cache_range 372 .long feroceon_coherent_kern_range 373 .long feroceon_coherent_user_range 374 .long feroceon_flush_kern_dcache_page 375 .long feroceon_dma_inv_range 376 .long feroceon_dma_clean_range 377 .long feroceon_dma_flush_range 378 379ENTRY(feroceon_range_cache_fns) 380 .long feroceon_flush_kern_cache_all 381 .long feroceon_flush_user_cache_all 382 .long feroceon_flush_user_cache_range 383 .long feroceon_coherent_kern_range 384 .long feroceon_coherent_user_range 385 .long feroceon_range_flush_kern_dcache_page 386 .long feroceon_range_dma_inv_range 387 .long feroceon_range_dma_clean_range 388 .long feroceon_range_dma_flush_range 389 390 .align 5 391ENTRY(cpu_feroceon_dcache_clean_area) 392#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH) 393 mov r2, r0 394 mov r3, r1 395#endif 3961: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 397 add r0, r0, #CACHE_DLINESIZE 398 subs r1, r1, #CACHE_DLINESIZE 399 bhi 1b 400#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH) 4011: mcr p15, 1, r2, c15, c9, 1 @ clean L2 entry 402 add r2, r2, #CACHE_DLINESIZE 403 subs r3, r3, #CACHE_DLINESIZE 404 bhi 1b 405#endif 406 mcr p15, 0, r0, c7, c10, 4 @ drain WB 407 mov pc, lr 408 409/* =============================== PageTable ============================== */ 410 411/* 412 * cpu_feroceon_switch_mm(pgd) 413 * 414 * Set the translation base pointer to be as described by pgd. 415 * 416 * pgd: new page tables 417 */ 418 .align 5 419ENTRY(cpu_feroceon_switch_mm) 420#ifdef CONFIG_MMU 421 /* 422 * Note: we wish to call __flush_whole_cache but we need to preserve 423 * lr to do so. The only way without touching main memory is to 424 * use r2 which is normally used to test the VM_EXEC flag, and 425 * compensate locally for the skipped ops if it is not set. 426 */ 427 mov r2, lr @ abuse r2 to preserve lr 428 bl __flush_whole_cache 429 @ if r2 contains the VM_EXEC bit then the next 2 ops are done already 430 tst r2, #VM_EXEC 431 mcreq p15, 0, ip, c7, c5, 0 @ invalidate I cache 432 mcreq p15, 0, ip, c7, c10, 4 @ drain WB 433 434 mcr p15, 0, r0, c2, c0, 0 @ load page table pointer 435 mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs 436 mov pc, r2 437#else 438 mov pc, lr 439#endif 440 441/* 442 * cpu_feroceon_set_pte_ext(ptep, pte, ext) 443 * 444 * Set a PTE and flush it out 445 */ 446 .align 5 447ENTRY(cpu_feroceon_set_pte_ext) 448#ifdef CONFIG_MMU 449 str r1, [r0], #-2048 @ linux version 450 451 eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY 452 453 bic r2, r1, #PTE_SMALL_AP_MASK 454 bic r2, r2, #PTE_TYPE_MASK 455 orr r2, r2, #PTE_TYPE_SMALL 456 457 tst r1, #L_PTE_USER @ User? 458 orrne r2, r2, #PTE_SMALL_AP_URO_SRW 459 460 tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? 461 orreq r2, r2, #PTE_SMALL_AP_UNO_SRW 462 463 tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? 464 movne r2, #0 465 466 str r2, [r0] @ hardware version 467 mov r0, r0 468 mcr p15, 0, r0, c7, c10, 1 @ clean D entry 469#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH) 470 mcr p15, 1, r0, c15, c9, 1 @ clean L2 entry 471#endif 472 mcr p15, 0, r0, c7, c10, 4 @ drain WB 473#endif 474 mov pc, lr 475 476 __INIT 477 478 .type __feroceon_setup, #function 479__feroceon_setup: 480 mov r0, #0 481 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 482 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 483#ifdef CONFIG_MMU 484 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 485#endif 486 487 adr r5, feroceon_crval 488 ldmia r5, {r5, r6} 489 mrc p15, 0, r0, c1, c0 @ get control register v4 490 bic r0, r0, r5 491 orr r0, r0, r6 492 mov pc, lr 493 .size __feroceon_setup, . - __feroceon_setup 494 495 /* 496 * R 497 * .RVI ZFRS BLDP WCAM 498 * .011 0001 ..11 0101 499 * 500 */ 501 .type feroceon_crval, #object 502feroceon_crval: 503 crval clear=0x00007f3f, mmuset=0x00003135, ucset=0x00001134 504 505 __INITDATA 506 507/* 508 * Purpose : Function pointers used to access above functions - all calls 509 * come through these 510 */ 511 .type feroceon_processor_functions, #object 512feroceon_processor_functions: 513 .word v5t_early_abort 514 .word pabort_noifar 515 .word cpu_feroceon_proc_init 516 .word cpu_feroceon_proc_fin 517 .word cpu_feroceon_reset 518 .word cpu_feroceon_do_idle 519 .word cpu_feroceon_dcache_clean_area 520 .word cpu_feroceon_switch_mm 521 .word cpu_feroceon_set_pte_ext 522 .size feroceon_processor_functions, . - feroceon_processor_functions 523 524 .section ".rodata" 525 526 .type cpu_arch_name, #object 527cpu_arch_name: 528 .asciz "armv5te" 529 .size cpu_arch_name, . - cpu_arch_name 530 531 .type cpu_elf_name, #object 532cpu_elf_name: 533 .asciz "v5" 534 .size cpu_elf_name, . - cpu_elf_name 535 536 .type cpu_feroceon_name, #object 537cpu_feroceon_name: 538 .asciz "Feroceon" 539 .size cpu_feroceon_name, . - cpu_feroceon_name 540 541 .type cpu_88fr531_name, #object 542cpu_88fr531_name: 543 .asciz "Feroceon 88FR531-vd" 544 .size cpu_88fr531_name, . - cpu_88fr531_name 545 546 .align 547 548 .section ".proc.info.init", #alloc, #execinstr 549 550#ifdef CONFIG_CPU_FEROCEON_OLD_ID 551 .type __feroceon_old_id_proc_info,#object 552__feroceon_old_id_proc_info: 553 .long 0x41009260 554 .long 0xff00fff0 555 .long PMD_TYPE_SECT | \ 556 PMD_SECT_BUFFERABLE | \ 557 PMD_SECT_CACHEABLE | \ 558 PMD_BIT4 | \ 559 PMD_SECT_AP_WRITE | \ 560 PMD_SECT_AP_READ 561 .long PMD_TYPE_SECT | \ 562 PMD_BIT4 | \ 563 PMD_SECT_AP_WRITE | \ 564 PMD_SECT_AP_READ 565 b __feroceon_setup 566 .long cpu_arch_name 567 .long cpu_elf_name 568 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 569 .long cpu_feroceon_name 570 .long feroceon_processor_functions 571 .long v4wbi_tlb_fns 572 .long feroceon_user_fns 573 .long feroceon_cache_fns 574 .size __feroceon_old_id_proc_info, . - __feroceon_old_id_proc_info 575#endif 576 577 .type __88fr531_proc_info,#object 578__88fr531_proc_info: 579 .long 0x56055310 580 .long 0xfffffff0 581 .long PMD_TYPE_SECT | \ 582 PMD_SECT_BUFFERABLE | \ 583 PMD_SECT_CACHEABLE | \ 584 PMD_BIT4 | \ 585 PMD_SECT_AP_WRITE | \ 586 PMD_SECT_AP_READ 587 .long PMD_TYPE_SECT | \ 588 PMD_BIT4 | \ 589 PMD_SECT_AP_WRITE | \ 590 PMD_SECT_AP_READ 591 b __feroceon_setup 592 .long cpu_arch_name 593 .long cpu_elf_name 594 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 595 .long cpu_88fr531_name 596 .long feroceon_processor_functions 597 .long v4wbi_tlb_fns 598 .long feroceon_user_fns 599 .long feroceon_cache_fns 600 .size __88fr531_proc_info, . - __88fr531_proc_info 601