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 mrc p15, 0, r0, c1, c0, 0 @ ctrl register 83 bic r0, r0, #0x1000 @ ...i............ 84 bic r0, r0, #0x000e @ ............wca. 85 mcr p15, 0, r0, c1, c0, 0 @ disable caches 86 ldmfd sp!, {pc} 87 88/* 89 * cpu_feroceon_reset(loc) 90 * 91 * Perform a soft reset of the system. Put the CPU into the 92 * same state as it would be if it had been reset, and branch 93 * to what would be the reset vector. 94 * 95 * loc: location to jump to for soft reset 96 */ 97 .align 5 98ENTRY(cpu_feroceon_reset) 99 mov ip, #0 100 mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches 101 mcr p15, 0, ip, c7, c10, 4 @ drain WB 102#ifdef CONFIG_MMU 103 mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs 104#endif 105 mrc p15, 0, ip, c1, c0, 0 @ ctrl register 106 bic ip, ip, #0x000f @ ............wcam 107 bic ip, ip, #0x1100 @ ...i...s........ 108 mcr p15, 0, ip, c1, c0, 0 @ ctrl register 109 mov pc, r0 110 111/* 112 * cpu_feroceon_do_idle() 113 * 114 * Called with IRQs disabled 115 */ 116 .align 5 117ENTRY(cpu_feroceon_do_idle) 118 mov r0, #0 119 mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer 120 mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt 121 mov pc, lr 122 123/* 124 * flush_user_cache_all() 125 * 126 * Clean and invalidate all cache entries in a particular 127 * address space. 128 */ 129 .align 5 130ENTRY(feroceon_flush_user_cache_all) 131 /* FALLTHROUGH */ 132 133/* 134 * flush_kern_cache_all() 135 * 136 * Clean and invalidate the entire cache. 137 */ 138ENTRY(feroceon_flush_kern_cache_all) 139 mov r2, #VM_EXEC 140 141__flush_whole_cache: 142 ldr r1, __cache_params 143 ldmia r1, {r1, r3} 1441: orr ip, r1, r3 1452: mcr p15, 0, ip, c7, c14, 2 @ clean + invalidate D set/way 146 subs ip, ip, #(1 << 30) @ next way 147 bcs 2b 148 subs r1, r1, #(1 << 5) @ next set 149 bcs 1b 150 151 tst r2, #VM_EXEC 152 mov ip, #0 153 mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache 154 mcrne p15, 0, ip, c7, c10, 4 @ drain WB 155 mov pc, lr 156 157/* 158 * flush_user_cache_range(start, end, flags) 159 * 160 * Clean and invalidate a range of cache entries in the 161 * specified address range. 162 * 163 * - start - start address (inclusive) 164 * - end - end address (exclusive) 165 * - flags - vm_flags describing address space 166 */ 167 .align 5 168ENTRY(feroceon_flush_user_cache_range) 169 sub r3, r1, r0 @ calculate total size 170 cmp r3, #CACHE_DLIMIT 171 bgt __flush_whole_cache 1721: tst r2, #VM_EXEC 173 mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry 174 mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry 175 add r0, r0, #CACHE_DLINESIZE 176 mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry 177 mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry 178 add r0, r0, #CACHE_DLINESIZE 179 cmp r0, r1 180 blo 1b 181 tst r2, #VM_EXEC 182 mov ip, #0 183 mcrne p15, 0, ip, c7, c10, 4 @ drain WB 184 mov pc, lr 185 186/* 187 * coherent_kern_range(start, end) 188 * 189 * Ensure coherency between the Icache and the Dcache in the 190 * region described by start, end. If you have non-snooping 191 * Harvard caches, you need to implement this function. 192 * 193 * - start - virtual start address 194 * - end - virtual end address 195 */ 196 .align 5 197ENTRY(feroceon_coherent_kern_range) 198 /* FALLTHROUGH */ 199 200/* 201 * coherent_user_range(start, end) 202 * 203 * Ensure coherency between the Icache and the Dcache in the 204 * region described by start, end. If you have non-snooping 205 * Harvard caches, you need to implement this function. 206 * 207 * - start - virtual start address 208 * - end - virtual end address 209 */ 210ENTRY(feroceon_coherent_user_range) 211 bic r0, r0, #CACHE_DLINESIZE - 1 2121: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 213 mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry 214 add r0, r0, #CACHE_DLINESIZE 215 cmp r0, r1 216 blo 1b 217 mcr p15, 0, r0, c7, c10, 4 @ drain WB 218 mov pc, lr 219 220/* 221 * flush_kern_dcache_page(void *page) 222 * 223 * Ensure no D cache aliasing occurs, either with itself or 224 * the I cache 225 * 226 * - addr - page aligned address 227 */ 228 .align 5 229ENTRY(feroceon_flush_kern_dcache_page) 230 add r1, r0, #PAGE_SZ 2311: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry 232 add r0, r0, #CACHE_DLINESIZE 233 cmp r0, r1 234 blo 1b 235 mov r0, #0 236 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache 237 mcr p15, 0, r0, c7, c10, 4 @ drain WB 238 mov pc, lr 239 240 .align 5 241ENTRY(feroceon_range_flush_kern_dcache_page) 242 mrs r2, cpsr 243 add r1, r0, #PAGE_SZ - CACHE_DLINESIZE @ top addr is inclusive 244 orr r3, r2, #PSR_I_BIT 245 msr cpsr_c, r3 @ disable interrupts 246 mcr p15, 5, r0, c15, c15, 0 @ D clean/inv range start 247 mcr p15, 5, r1, c15, c15, 1 @ D clean/inv range top 248 msr cpsr_c, r2 @ restore interrupts 249 mov r0, #0 250 mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache 251 mcr p15, 0, r0, c7, c10, 4 @ drain WB 252 mov pc, lr 253 254/* 255 * dma_inv_range(start, end) 256 * 257 * Invalidate (discard) the specified virtual address range. 258 * May not write back any entries. If 'start' or 'end' 259 * are not cache line aligned, those lines must be written 260 * back. 261 * 262 * - start - virtual start address 263 * - end - virtual end address 264 * 265 * (same as v4wb) 266 */ 267 .align 5 268ENTRY(feroceon_dma_inv_range) 269 tst r0, #CACHE_DLINESIZE - 1 270 bic r0, r0, #CACHE_DLINESIZE - 1 271 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry 272 tst r1, #CACHE_DLINESIZE - 1 273 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry 2741: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry 275 add r0, r0, #CACHE_DLINESIZE 276 cmp r0, r1 277 blo 1b 278 mcr p15, 0, r0, c7, c10, 4 @ drain WB 279 mov pc, lr 280 281 .align 5 282ENTRY(feroceon_range_dma_inv_range) 283 mrs r2, cpsr 284 tst r0, #CACHE_DLINESIZE - 1 285 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry 286 tst r1, #CACHE_DLINESIZE - 1 287 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry 288 cmp r1, r0 289 subne r1, r1, #1 @ top address is inclusive 290 orr r3, r2, #PSR_I_BIT 291 msr cpsr_c, r3 @ disable interrupts 292 mcr p15, 5, r0, c15, c14, 0 @ D inv range start 293 mcr p15, 5, r1, c15, c14, 1 @ D inv range top 294 msr cpsr_c, r2 @ restore interrupts 295 mov pc, lr 296 297/* 298 * dma_clean_range(start, end) 299 * 300 * Clean the specified virtual address range. 301 * 302 * - start - virtual start address 303 * - end - virtual end address 304 * 305 * (same as v4wb) 306 */ 307 .align 5 308ENTRY(feroceon_dma_clean_range) 309 bic r0, r0, #CACHE_DLINESIZE - 1 3101: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 311 add r0, r0, #CACHE_DLINESIZE 312 cmp r0, r1 313 blo 1b 314 mcr p15, 0, r0, c7, c10, 4 @ drain WB 315 mov pc, lr 316 317 .align 5 318ENTRY(feroceon_range_dma_clean_range) 319 mrs r2, cpsr 320 cmp r1, r0 321 subne r1, r1, #1 @ top address is inclusive 322 orr r3, r2, #PSR_I_BIT 323 msr cpsr_c, r3 @ disable interrupts 324 mcr p15, 5, r0, c15, c13, 0 @ D clean range start 325 mcr p15, 5, r1, c15, c13, 1 @ D clean range top 326 msr cpsr_c, r2 @ restore interrupts 327 mcr p15, 0, r0, c7, c10, 4 @ drain WB 328 mov pc, lr 329 330/* 331 * dma_flush_range(start, end) 332 * 333 * Clean and invalidate the specified virtual address range. 334 * 335 * - start - virtual start address 336 * - end - virtual end address 337 */ 338 .align 5 339ENTRY(feroceon_dma_flush_range) 340 bic r0, r0, #CACHE_DLINESIZE - 1 3411: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry 342 add r0, r0, #CACHE_DLINESIZE 343 cmp r0, r1 344 blo 1b 345 mcr p15, 0, r0, c7, c10, 4 @ drain WB 346 mov pc, lr 347 348 .align 5 349ENTRY(feroceon_range_dma_flush_range) 350 mrs r2, cpsr 351 cmp r1, r0 352 subne r1, r1, #1 @ top address is inclusive 353 orr r3, r2, #PSR_I_BIT 354 msr cpsr_c, r3 @ disable interrupts 355 mcr p15, 5, r0, c15, c15, 0 @ D clean/inv range start 356 mcr p15, 5, r1, c15, c15, 1 @ D clean/inv range top 357 msr cpsr_c, r2 @ restore interrupts 358 mcr p15, 0, r0, c7, c10, 4 @ drain WB 359 mov pc, lr 360 361ENTRY(feroceon_cache_fns) 362 .long feroceon_flush_kern_cache_all 363 .long feroceon_flush_user_cache_all 364 .long feroceon_flush_user_cache_range 365 .long feroceon_coherent_kern_range 366 .long feroceon_coherent_user_range 367 .long feroceon_flush_kern_dcache_page 368 .long feroceon_dma_inv_range 369 .long feroceon_dma_clean_range 370 .long feroceon_dma_flush_range 371 372ENTRY(feroceon_range_cache_fns) 373 .long feroceon_flush_kern_cache_all 374 .long feroceon_flush_user_cache_all 375 .long feroceon_flush_user_cache_range 376 .long feroceon_coherent_kern_range 377 .long feroceon_coherent_user_range 378 .long feroceon_range_flush_kern_dcache_page 379 .long feroceon_range_dma_inv_range 380 .long feroceon_range_dma_clean_range 381 .long feroceon_range_dma_flush_range 382 383 .align 5 384ENTRY(cpu_feroceon_dcache_clean_area) 3851: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 386 add r0, r0, #CACHE_DLINESIZE 387 subs r1, r1, #CACHE_DLINESIZE 388 bhi 1b 389 mcr p15, 0, r0, c7, c10, 4 @ drain WB 390 mov pc, lr 391 392/* =============================== PageTable ============================== */ 393 394/* 395 * cpu_feroceon_switch_mm(pgd) 396 * 397 * Set the translation base pointer to be as described by pgd. 398 * 399 * pgd: new page tables 400 */ 401 .align 5 402ENTRY(cpu_feroceon_switch_mm) 403#ifdef CONFIG_MMU 404 /* 405 * Note: we wish to call __flush_whole_cache but we need to preserve 406 * lr to do so. The only way without touching main memory is to 407 * use r2 which is normally used to test the VM_EXEC flag, and 408 * compensate locally for the skipped ops if it is not set. 409 */ 410 mov r2, lr @ abuse r2 to preserve lr 411 bl __flush_whole_cache 412 @ if r2 contains the VM_EXEC bit then the next 2 ops are done already 413 tst r2, #VM_EXEC 414 mcreq p15, 0, ip, c7, c5, 0 @ invalidate I cache 415 mcreq p15, 0, ip, c7, c10, 4 @ drain WB 416 417 mcr p15, 0, r0, c2, c0, 0 @ load page table pointer 418 mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs 419 mov pc, r2 420#else 421 mov pc, lr 422#endif 423 424/* 425 * cpu_feroceon_set_pte_ext(ptep, pte, ext) 426 * 427 * Set a PTE and flush it out 428 */ 429 .align 5 430ENTRY(cpu_feroceon_set_pte_ext) 431#ifdef CONFIG_MMU 432 str r1, [r0], #-2048 @ linux version 433 434 eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY 435 436 bic r2, r1, #PTE_SMALL_AP_MASK 437 bic r2, r2, #PTE_TYPE_MASK 438 orr r2, r2, #PTE_TYPE_SMALL 439 440 tst r1, #L_PTE_USER @ User? 441 orrne r2, r2, #PTE_SMALL_AP_URO_SRW 442 443 tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? 444 orreq r2, r2, #PTE_SMALL_AP_UNO_SRW 445 446 tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? 447 movne r2, #0 448 449 str r2, [r0] @ hardware version 450 mov r0, r0 451 mcr p15, 0, r0, c7, c10, 1 @ clean D entry 452 mcr p15, 0, r0, c7, c10, 4 @ drain WB 453#endif 454 mov pc, lr 455 456 __INIT 457 458 .type __feroceon_setup, #function 459__feroceon_setup: 460 mov r0, #0 461 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 462 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 463#ifdef CONFIG_MMU 464 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 465#endif 466 467 adr r5, feroceon_crval 468 ldmia r5, {r5, r6} 469 mrc p15, 0, r0, c1, c0 @ get control register v4 470 bic r0, r0, r5 471 orr r0, r0, r6 472 mov pc, lr 473 .size __feroceon_setup, . - __feroceon_setup 474 475 /* 476 * R 477 * .RVI ZFRS BLDP WCAM 478 * .011 0001 ..11 0101 479 * 480 */ 481 .type feroceon_crval, #object 482feroceon_crval: 483 crval clear=0x00007f3f, mmuset=0x00003135, ucset=0x00001134 484 485 __INITDATA 486 487/* 488 * Purpose : Function pointers used to access above functions - all calls 489 * come through these 490 */ 491 .type feroceon_processor_functions, #object 492feroceon_processor_functions: 493 .word v5t_early_abort 494 .word pabort_noifar 495 .word cpu_feroceon_proc_init 496 .word cpu_feroceon_proc_fin 497 .word cpu_feroceon_reset 498 .word cpu_feroceon_do_idle 499 .word cpu_feroceon_dcache_clean_area 500 .word cpu_feroceon_switch_mm 501 .word cpu_feroceon_set_pte_ext 502 .size feroceon_processor_functions, . - feroceon_processor_functions 503 504 .section ".rodata" 505 506 .type cpu_arch_name, #object 507cpu_arch_name: 508 .asciz "armv5te" 509 .size cpu_arch_name, . - cpu_arch_name 510 511 .type cpu_elf_name, #object 512cpu_elf_name: 513 .asciz "v5" 514 .size cpu_elf_name, . - cpu_elf_name 515 516 .type cpu_feroceon_name, #object 517cpu_feroceon_name: 518 .asciz "Feroceon" 519 .size cpu_feroceon_name, . - cpu_feroceon_name 520 521 .type cpu_88fr531_name, #object 522cpu_88fr531_name: 523 .asciz "Feroceon 88FR531-vd" 524 .size cpu_88fr531_name, . - cpu_88fr531_name 525 526 .align 527 528 .section ".proc.info.init", #alloc, #execinstr 529 530#ifdef CONFIG_CPU_FEROCEON_OLD_ID 531 .type __feroceon_old_id_proc_info,#object 532__feroceon_old_id_proc_info: 533 .long 0x41009260 534 .long 0xff00fff0 535 .long PMD_TYPE_SECT | \ 536 PMD_SECT_BUFFERABLE | \ 537 PMD_SECT_CACHEABLE | \ 538 PMD_BIT4 | \ 539 PMD_SECT_AP_WRITE | \ 540 PMD_SECT_AP_READ 541 .long PMD_TYPE_SECT | \ 542 PMD_BIT4 | \ 543 PMD_SECT_AP_WRITE | \ 544 PMD_SECT_AP_READ 545 b __feroceon_setup 546 .long cpu_arch_name 547 .long cpu_elf_name 548 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 549 .long cpu_feroceon_name 550 .long feroceon_processor_functions 551 .long v4wbi_tlb_fns 552 .long feroceon_user_fns 553 .long feroceon_cache_fns 554 .size __feroceon_old_id_proc_info, . - __feroceon_old_id_proc_info 555#endif 556 557 .type __88fr531_proc_info,#object 558__88fr531_proc_info: 559 .long 0x56055310 560 .long 0xfffffff0 561 .long PMD_TYPE_SECT | \ 562 PMD_SECT_BUFFERABLE | \ 563 PMD_SECT_CACHEABLE | \ 564 PMD_BIT4 | \ 565 PMD_SECT_AP_WRITE | \ 566 PMD_SECT_AP_READ 567 .long PMD_TYPE_SECT | \ 568 PMD_BIT4 | \ 569 PMD_SECT_AP_WRITE | \ 570 PMD_SECT_AP_READ 571 b __feroceon_setup 572 .long cpu_arch_name 573 .long cpu_elf_name 574 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 575 .long cpu_88fr531_name 576 .long feroceon_processor_functions 577 .long v4wbi_tlb_fns 578 .long feroceon_user_fns 579 .long feroceon_cache_fns 580 .size __88fr531_proc_info, . - __88fr531_proc_info 581