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/* 241 * dma_inv_range(start, end) 242 * 243 * Invalidate (discard) the specified virtual address range. 244 * May not write back any entries. If 'start' or 'end' 245 * are not cache line aligned, those lines must be written 246 * back. 247 * 248 * - start - virtual start address 249 * - end - virtual end address 250 * 251 * (same as v4wb) 252 */ 253 .align 5 254ENTRY(feroceon_dma_inv_range) 255 tst r0, #CACHE_DLINESIZE - 1 256 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry 257 tst r1, #CACHE_DLINESIZE - 1 258 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry 259 bic r0, r0, #CACHE_DLINESIZE - 1 2601: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry 261 add r0, r0, #CACHE_DLINESIZE 262 cmp r0, r1 263 blo 1b 264 mcr p15, 0, r0, c7, c10, 4 @ drain WB 265 mov pc, lr 266 267/* 268 * dma_clean_range(start, end) 269 * 270 * Clean the specified virtual address range. 271 * 272 * - start - virtual start address 273 * - end - virtual end address 274 * 275 * (same as v4wb) 276 */ 277 .align 5 278ENTRY(feroceon_dma_clean_range) 279 bic r0, r0, #CACHE_DLINESIZE - 1 2801: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 281 add r0, r0, #CACHE_DLINESIZE 282 cmp r0, r1 283 blo 1b 284 mcr p15, 0, r0, c7, c10, 4 @ drain WB 285 mov pc, lr 286 287/* 288 * dma_flush_range(start, end) 289 * 290 * Clean and invalidate the specified virtual address range. 291 * 292 * - start - virtual start address 293 * - end - virtual end address 294 */ 295 .align 5 296ENTRY(feroceon_dma_flush_range) 297 bic r0, r0, #CACHE_DLINESIZE - 1 2981: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry 299 add r0, r0, #CACHE_DLINESIZE 300 cmp r0, r1 301 blo 1b 302 mcr p15, 0, r0, c7, c10, 4 @ drain WB 303 mov pc, lr 304 305ENTRY(feroceon_cache_fns) 306 .long feroceon_flush_kern_cache_all 307 .long feroceon_flush_user_cache_all 308 .long feroceon_flush_user_cache_range 309 .long feroceon_coherent_kern_range 310 .long feroceon_coherent_user_range 311 .long feroceon_flush_kern_dcache_page 312 .long feroceon_dma_inv_range 313 .long feroceon_dma_clean_range 314 .long feroceon_dma_flush_range 315 316 .align 5 317ENTRY(cpu_feroceon_dcache_clean_area) 3181: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 319 add r0, r0, #CACHE_DLINESIZE 320 subs r1, r1, #CACHE_DLINESIZE 321 bhi 1b 322 mcr p15, 0, r0, c7, c10, 4 @ drain WB 323 mov pc, lr 324 325/* =============================== PageTable ============================== */ 326 327/* 328 * cpu_feroceon_switch_mm(pgd) 329 * 330 * Set the translation base pointer to be as described by pgd. 331 * 332 * pgd: new page tables 333 */ 334 .align 5 335ENTRY(cpu_feroceon_switch_mm) 336#ifdef CONFIG_MMU 337 /* 338 * Note: we wish to call __flush_whole_cache but we need to preserve 339 * lr to do so. The only way without touching main memory is to 340 * use r2 which is normally used to test the VM_EXEC flag, and 341 * compensate locally for the skipped ops if it is not set. 342 */ 343 mov r2, lr @ abuse r2 to preserve lr 344 bl __flush_whole_cache 345 @ if r2 contains the VM_EXEC bit then the next 2 ops are done already 346 tst r2, #VM_EXEC 347 mcreq p15, 0, ip, c7, c5, 0 @ invalidate I cache 348 mcreq p15, 0, ip, c7, c10, 4 @ drain WB 349 350 mcr p15, 0, r0, c2, c0, 0 @ load page table pointer 351 mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs 352 mov pc, r2 353#else 354 mov pc, lr 355#endif 356 357/* 358 * cpu_feroceon_set_pte_ext(ptep, pte, ext) 359 * 360 * Set a PTE and flush it out 361 */ 362 .align 5 363ENTRY(cpu_feroceon_set_pte_ext) 364#ifdef CONFIG_MMU 365 str r1, [r0], #-2048 @ linux version 366 367 eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY 368 369 bic r2, r1, #PTE_SMALL_AP_MASK 370 bic r2, r2, #PTE_TYPE_MASK 371 orr r2, r2, #PTE_TYPE_SMALL 372 373 tst r1, #L_PTE_USER @ User? 374 orrne r2, r2, #PTE_SMALL_AP_URO_SRW 375 376 tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? 377 orreq r2, r2, #PTE_SMALL_AP_UNO_SRW 378 379 tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? 380 movne r2, #0 381 382 str r2, [r0] @ hardware version 383 mov r0, r0 384 mcr p15, 0, r0, c7, c10, 1 @ clean D entry 385 mcr p15, 0, r0, c7, c10, 4 @ drain WB 386#endif 387 mov pc, lr 388 389 __INIT 390 391 .type __feroceon_setup, #function 392__feroceon_setup: 393 mov r0, #0 394 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 395 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 396#ifdef CONFIG_MMU 397 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 398#endif 399 400 adr r5, feroceon_crval 401 ldmia r5, {r5, r6} 402 mrc p15, 0, r0, c1, c0 @ get control register v4 403 bic r0, r0, r5 404 orr r0, r0, r6 405 mov pc, lr 406 .size __feroceon_setup, . - __feroceon_setup 407 408 /* 409 * R 410 * .RVI ZFRS BLDP WCAM 411 * .011 0001 ..11 0101 412 * 413 */ 414 .type feroceon_crval, #object 415feroceon_crval: 416 crval clear=0x00007f3f, mmuset=0x00003135, ucset=0x00001134 417 418 __INITDATA 419 420/* 421 * Purpose : Function pointers used to access above functions - all calls 422 * come through these 423 */ 424 .type feroceon_processor_functions, #object 425feroceon_processor_functions: 426 .word v5t_early_abort 427 .word pabort_noifar 428 .word cpu_feroceon_proc_init 429 .word cpu_feroceon_proc_fin 430 .word cpu_feroceon_reset 431 .word cpu_feroceon_do_idle 432 .word cpu_feroceon_dcache_clean_area 433 .word cpu_feroceon_switch_mm 434 .word cpu_feroceon_set_pte_ext 435 .size feroceon_processor_functions, . - feroceon_processor_functions 436 437 .section ".rodata" 438 439 .type cpu_arch_name, #object 440cpu_arch_name: 441 .asciz "armv5te" 442 .size cpu_arch_name, . - cpu_arch_name 443 444 .type cpu_elf_name, #object 445cpu_elf_name: 446 .asciz "v5" 447 .size cpu_elf_name, . - cpu_elf_name 448 449 .type cpu_feroceon_name, #object 450cpu_feroceon_name: 451 .asciz "Feroceon" 452 .size cpu_feroceon_name, . - cpu_feroceon_name 453 454 .type cpu_88fr531_name, #object 455cpu_88fr531_name: 456 .asciz "Feroceon 88FR531-vd" 457 .size cpu_88fr531_name, . - cpu_88fr531_name 458 459 .align 460 461 .section ".proc.info.init", #alloc, #execinstr 462 463#ifdef CONFIG_CPU_FEROCEON_OLD_ID 464 .type __feroceon_old_id_proc_info,#object 465__feroceon_old_id_proc_info: 466 .long 0x41009260 467 .long 0xff00fff0 468 .long PMD_TYPE_SECT | \ 469 PMD_SECT_BUFFERABLE | \ 470 PMD_SECT_CACHEABLE | \ 471 PMD_BIT4 | \ 472 PMD_SECT_AP_WRITE | \ 473 PMD_SECT_AP_READ 474 .long PMD_TYPE_SECT | \ 475 PMD_BIT4 | \ 476 PMD_SECT_AP_WRITE | \ 477 PMD_SECT_AP_READ 478 b __feroceon_setup 479 .long cpu_arch_name 480 .long cpu_elf_name 481 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 482 .long cpu_feroceon_name 483 .long feroceon_processor_functions 484 .long v4wbi_tlb_fns 485 .long feroceon_user_fns 486 .long feroceon_cache_fns 487 .size __feroceon_old_id_proc_info, . - __feroceon_old_id_proc_info 488#endif 489 490 .type __88fr531_proc_info,#object 491__88fr531_proc_info: 492 .long 0x56055310 493 .long 0xfffffff0 494 .long PMD_TYPE_SECT | \ 495 PMD_SECT_BUFFERABLE | \ 496 PMD_SECT_CACHEABLE | \ 497 PMD_BIT4 | \ 498 PMD_SECT_AP_WRITE | \ 499 PMD_SECT_AP_READ 500 .long PMD_TYPE_SECT | \ 501 PMD_BIT4 | \ 502 PMD_SECT_AP_WRITE | \ 503 PMD_SECT_AP_READ 504 b __feroceon_setup 505 .long cpu_arch_name 506 .long cpu_elf_name 507 .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP 508 .long cpu_88fr531_name 509 .long feroceon_processor_functions 510 .long v4wbi_tlb_fns 511 .long feroceon_user_fns 512 .long feroceon_cache_fns 513 .size __88fr531_proc_info, . - __88fr531_proc_info 514