Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * linux/arch/arm26/mm/proc-arm2,3.S |
| 3 | * |
| 4 | * Copyright (C) 1997-1999 Russell King |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2 as |
| 8 | * published by the Free Software Foundation. |
| 9 | * |
| 10 | * MMU functions for ARM2,3 |
| 11 | * |
| 12 | * These are the low level assembler for performing cache |
| 13 | * and memory functions on ARM2, ARM250 and ARM3 processors. |
| 14 | */ |
| 15 | #include <linux/linkage.h> |
| 16 | #include <asm/assembler.h> |
| 17 | #include <asm/asm_offsets.h> |
| 18 | #include <asm/procinfo.h> |
| 19 | #include <asm/ptrace.h> |
| 20 | |
| 21 | /* |
| 22 | * MEMC workhorse code. It's both a horse which things it's a pig. |
| 23 | */ |
| 24 | /* |
| 25 | * Function: cpu_memc_update_entry(pgd_t *pgd, unsigned long phys_pte, unsigned long addr) |
| 26 | * Params : pgd Page tables/MEMC mapping |
| 27 | * : phys_pte physical address, or PTE |
| 28 | * : addr virtual address |
| 29 | */ |
| 30 | ENTRY(cpu_memc_update_entry) |
| 31 | tst r1, #PAGE_PRESENT @ is the page present |
| 32 | orreq r1, r1, #PAGE_OLD | PAGE_CLEAN |
| 33 | moveq r2, #0x01f00000 |
| 34 | mov r3, r1, lsr #13 @ convert to physical page nr |
| 35 | and r3, r3, #0x3fc |
| 36 | adr ip, memc_phys_table_32 |
| 37 | ldr r3, [ip, r3] |
| 38 | tst r1, #PAGE_OLD | PAGE_NOT_USER |
| 39 | biceq r3, r3, #0x200 |
| 40 | tsteq r1, #PAGE_READONLY | PAGE_CLEAN |
| 41 | biceq r3, r3, #0x300 |
| 42 | mov r2, r2, lsr #15 @ virtual -> nr |
| 43 | orr r3, r3, r2, lsl #15 |
| 44 | and r2, r2, #0x300 |
| 45 | orr r3, r3, r2, lsl #2 |
| 46 | and r2, r3, #255 |
| 47 | sub r0, r0, #256 * 4 |
| 48 | str r3, [r0, r2, lsl #2] |
| 49 | strb r3, [r3] |
| 50 | movs pc, lr |
| 51 | /* |
| 52 | * Params : r0 = preserved |
| 53 | * : r1 = memc table base (preserved) |
| 54 | * : r2 = page table entry |
| 55 | * : r3 = preserved |
| 56 | * : r4 = unused |
| 57 | * : r5 = memc physical address translation table |
| 58 | * : ip = virtual address (preserved) |
| 59 | */ |
| 60 | update_pte: |
| 61 | mov r4, r2, lsr #13 |
| 62 | and r4, r4, #0x3fc |
| 63 | ldr r4, [r5, r4] @ covert to MEMC page |
| 64 | |
| 65 | tst r2, #PAGE_OLD | PAGE_NOT_USER @ check for MEMC read |
| 66 | biceq r4, r4, #0x200 |
| 67 | tsteq r2, #PAGE_READONLY | PAGE_CLEAN @ check for MEMC write |
| 68 | biceq r4, r4, #0x300 |
| 69 | |
| 70 | orr r4, r4, ip |
| 71 | and r2, ip, #0x01800000 |
| 72 | orr r4, r4, r2, lsr #13 |
| 73 | |
| 74 | and r2, r4, #255 |
| 75 | str r4, [r1, r2, lsl #2] |
| 76 | movs pc, lr |
| 77 | |
| 78 | /* |
| 79 | * Params : r0 = preserved |
| 80 | * : r1 = memc table base (preserved) |
| 81 | * : r2 = page table base |
| 82 | * : r3 = preserved |
| 83 | * : r4 = unused |
| 84 | * : r5 = memc physical address translation table |
| 85 | * : ip = virtual address (updated) |
| 86 | */ |
| 87 | update_pte_table: |
| 88 | stmfd sp!, {r0, lr} |
| 89 | bic r0, r2, #3 |
| 90 | 1: ldr r2, [r0], #4 @ get entry |
| 91 | tst r2, #PAGE_PRESENT @ page present |
| 92 | blne update_pte @ process pte |
| 93 | add ip, ip, #32768 @ increment virt addr |
| 94 | ldr r2, [r0], #4 @ get entry |
| 95 | tst r2, #PAGE_PRESENT @ page present |
| 96 | blne update_pte @ process pte |
| 97 | add ip, ip, #32768 @ increment virt addr |
| 98 | ldr r2, [r0], #4 @ get entry |
| 99 | tst r2, #PAGE_PRESENT @ page present |
| 100 | blne update_pte @ process pte |
| 101 | add ip, ip, #32768 @ increment virt addr |
| 102 | ldr r2, [r0], #4 @ get entry |
| 103 | tst r2, #PAGE_PRESENT @ page present |
| 104 | blne update_pte @ process pte |
| 105 | add ip, ip, #32768 @ increment virt addr |
| 106 | tst ip, #32768 * 31 @ finished? |
| 107 | bne 1b |
| 108 | ldmfd sp!, {r0, pc}^ |
| 109 | |
| 110 | /* |
| 111 | * Function: cpu_memc_update_all(pgd_t *pgd) |
| 112 | * Params : pgd Page tables/MEMC mapping |
| 113 | * Notes : this is optimised for 32k pages |
| 114 | */ |
| 115 | ENTRY(cpu_memc_update_all) |
| 116 | stmfd sp!, {r4, r5, lr} |
| 117 | bl clear_tables |
| 118 | sub r1, r0, #256 * 4 @ start of MEMC tables |
| 119 | adr r5, memc_phys_table_32 @ Convert to logical page number |
| 120 | mov ip, #0 @ virtual address |
| 121 | 1: ldmia r0!, {r2, r3} @ load two pgd entries |
| 122 | tst r2, #PAGE_PRESENT @ is pgd entry present? |
| 123 | addeq ip, ip, #1048576 @FIXME - PAGE_PRESENT is for PTEs technically... |
| 124 | blne update_pte_table |
| 125 | mov r2, r3 |
| 126 | tst r2, #PAGE_PRESENT @ is pgd entry present? |
| 127 | addeq ip, ip, #1048576 |
| 128 | blne update_pte_table |
| 129 | teq ip, #32 * 1048576 |
| 130 | bne 1b |
| 131 | ldmfd sp!, {r4, r5, pc}^ |
| 132 | |
| 133 | /* |
| 134 | * Build the table to map from physical page number to memc page number |
| 135 | */ |
| 136 | .type memc_phys_table_32, #object |
| 137 | memc_phys_table_32: |
| 138 | .irp b7, 0x00, 0x80 |
| 139 | .irp b6, 0x00, 0x02 |
| 140 | .irp b5, 0x00, 0x04 |
| 141 | .irp b4, 0x00, 0x01 |
| 142 | |
| 143 | .irp b3, 0x00, 0x40 |
| 144 | .irp b2, 0x00, 0x20 |
| 145 | .irp b1, 0x00, 0x10 |
| 146 | .irp b0, 0x00, 0x08 |
| 147 | .long 0x03800300 + \b7 + \b6 + \b5 + \b4 + \b3 + \b2 + \b1 + \b0 |
| 148 | .endr |
| 149 | .endr |
| 150 | .endr |
| 151 | .endr |
| 152 | |
| 153 | .endr |
| 154 | .endr |
| 155 | .endr |
| 156 | .endr |
| 157 | .size memc_phys_table_32, . - memc_phys_table_32 |
| 158 | |
| 159 | /* |
| 160 | * helper for cpu_memc_update_all, this clears out all |
| 161 | * mappings, setting them close to the top of memory, |
| 162 | * and inaccessible (0x01f00000). |
| 163 | * Params : r0 = page table pointer |
| 164 | */ |
| 165 | clear_tables: ldr r1, _arm3_set_pgd - 4 |
| 166 | ldr r2, [r1] |
| 167 | sub r1, r0, #256 * 4 @ start of MEMC tables |
| 168 | add r2, r1, r2, lsl #2 @ end of tables |
| 169 | mov r3, #0x03f00000 @ Default mapping (null mapping) |
| 170 | orr r3, r3, #0x00000f00 |
| 171 | orr r4, r3, #1 |
| 172 | orr r5, r3, #2 |
| 173 | orr ip, r3, #3 |
| 174 | 1: stmia r1!, {r3, r4, r5, ip} |
| 175 | add r3, r3, #4 |
| 176 | add r4, r4, #4 |
| 177 | add r5, r5, #4 |
| 178 | add ip, ip, #4 |
| 179 | stmia r1!, {r3, r4, r5, ip} |
| 180 | add r3, r3, #4 |
| 181 | add r4, r4, #4 |
| 182 | add r5, r5, #4 |
| 183 | add ip, ip, #4 |
| 184 | teq r1, r2 |
| 185 | bne 1b |
| 186 | mov pc, lr |
| 187 | |
| 188 | /* |
| 189 | * Function: *_set_pgd(pgd_t *pgd) |
| 190 | * Params : pgd New page tables/MEMC mapping |
| 191 | * Purpose : update MEMC hardware with new mapping |
| 192 | */ |
| 193 | .word page_nr @ extern - declared in mm-memc.c |
| 194 | _arm3_set_pgd: mcr p15, 0, r1, c1, c0, 0 @ flush cache |
| 195 | _arm2_set_pgd: stmfd sp!, {lr} |
| 196 | ldr r1, _arm3_set_pgd - 4 |
| 197 | ldr r2, [r1] |
| 198 | sub r0, r0, #256 * 4 @ start of MEMC tables |
| 199 | add r1, r0, r2, lsl #2 @ end of tables |
| 200 | 1: ldmia r0!, {r2, r3, ip, lr} |
| 201 | strb r2, [r2] |
| 202 | strb r3, [r3] |
| 203 | strb ip, [ip] |
| 204 | strb lr, [lr] |
| 205 | ldmia r0!, {r2, r3, ip, lr} |
| 206 | strb r2, [r2] |
| 207 | strb r3, [r3] |
| 208 | strb ip, [ip] |
| 209 | strb lr, [lr] |
| 210 | teq r0, r1 |
| 211 | bne 1b |
| 212 | ldmfd sp!, {pc}^ |
| 213 | |
| 214 | /* |
| 215 | * Function: *_proc_init (void) |
| 216 | * Purpose : Initialise the cache control registers |
| 217 | */ |
| 218 | _arm3_proc_init: |
| 219 | mov r0, #0x001f0000 |
| 220 | orr r0, r0, #0x0000ff00 |
| 221 | orr r0, r0, #0x000000ff |
| 222 | mcr p15, 0, r0, c3, c0 @ ARM3 Cacheable |
| 223 | mcr p15, 0, r0, c4, c0 @ ARM3 Updateable |
| 224 | mov r0, #0 |
| 225 | mcr p15, 0, r0, c5, c0 @ ARM3 Disruptive |
| 226 | mcr p15, 0, r0, c1, c0 @ ARM3 Flush |
| 227 | mov r0, #3 |
| 228 | mcr p15, 0, r0, c2, c0 @ ARM3 Control |
| 229 | _arm2_proc_init: |
| 230 | movs pc, lr |
| 231 | |
| 232 | /* |
| 233 | * Function: *_proc_fin (void) |
| 234 | * Purpose : Finalise processor (disable caches) |
| 235 | */ |
| 236 | _arm3_proc_fin: mov r0, #2 |
| 237 | mcr p15, 0, r0, c2, c0 |
| 238 | _arm2_proc_fin: orrs pc, lr, #PSR_I_BIT|PSR_F_BIT |
| 239 | |
| 240 | /* |
| 241 | * Function: *_xchg_1 (int new, volatile void *ptr) |
| 242 | * Params : new New value to store at... |
| 243 | * : ptr pointer to byte-wide location |
| 244 | * Purpose : Performs an exchange operation |
| 245 | * Returns : Original byte data at 'ptr' |
| 246 | */ |
| 247 | _arm2_xchg_1: mov r2, pc |
| 248 | orr r2, r2, #PSR_I_BIT |
| 249 | teqp r2, #0 |
| 250 | ldrb r2, [r1] |
| 251 | strb r0, [r1] |
| 252 | mov r0, r2 |
| 253 | movs pc, lr |
| 254 | |
| 255 | _arm3_xchg_1: swpb r0, r0, [r1] |
| 256 | movs pc, lr |
| 257 | |
| 258 | /* |
| 259 | * Function: *_xchg_4 (int new, volatile void *ptr) |
| 260 | * Params : new New value to store at... |
| 261 | * : ptr pointer to word-wide location |
| 262 | * Purpose : Performs an exchange operation |
| 263 | * Returns : Original word data at 'ptr' |
| 264 | */ |
| 265 | _arm2_xchg_4: mov r2, pc |
| 266 | orr r2, r2, #PSR_I_BIT |
| 267 | teqp r2, #0 |
| 268 | ldr r2, [r1] |
| 269 | str r0, [r1] |
| 270 | mov r0, r2 |
| 271 | movs pc, lr |
| 272 | |
| 273 | _arm3_xchg_4: swp r0, r0, [r1] |
| 274 | movs pc, lr |
| 275 | |
| 276 | _arm2_3_check_bugs: |
| 277 | bics pc, lr, #PSR_F_BIT @ Clear FIQ disable bit |
| 278 | |
| 279 | armvlsi_name: .asciz "ARM/VLSI" |
| 280 | _arm2_name: .asciz "ARM 2" |
| 281 | _arm250_name: .asciz "ARM 250" |
| 282 | _arm3_name: .asciz "ARM 3" |
| 283 | |
| 284 | .section ".init.text", #alloc, #execinstr |
| 285 | /* |
| 286 | * Purpose : Function pointers used to access above functions - all calls |
| 287 | * come through these |
| 288 | */ |
| 289 | .globl arm2_processor_functions |
| 290 | arm2_processor_functions: |
| 291 | .word _arm2_3_check_bugs |
| 292 | .word _arm2_proc_init |
| 293 | .word _arm2_proc_fin |
| 294 | .word _arm2_set_pgd |
| 295 | .word _arm2_xchg_1 |
| 296 | .word _arm2_xchg_4 |
| 297 | |
| 298 | cpu_arm2_info: |
| 299 | .long armvlsi_name |
| 300 | .long _arm2_name |
| 301 | |
| 302 | .globl arm250_processor_functions |
| 303 | arm250_processor_functions: |
| 304 | .word _arm2_3_check_bugs |
| 305 | .word _arm2_proc_init |
| 306 | .word _arm2_proc_fin |
| 307 | .word _arm2_set_pgd |
| 308 | .word _arm3_xchg_1 |
| 309 | .word _arm3_xchg_4 |
| 310 | |
| 311 | cpu_arm250_info: |
| 312 | .long armvlsi_name |
| 313 | .long _arm250_name |
| 314 | |
| 315 | .globl arm3_processor_functions |
| 316 | arm3_processor_functions: |
| 317 | .word _arm2_3_check_bugs |
| 318 | .word _arm3_proc_init |
| 319 | .word _arm3_proc_fin |
| 320 | .word _arm3_set_pgd |
| 321 | .word _arm3_xchg_1 |
| 322 | .word _arm3_xchg_4 |
| 323 | |
| 324 | cpu_arm3_info: |
| 325 | .long armvlsi_name |
| 326 | .long _arm3_name |
| 327 | |
| 328 | arm2_arch_name: .asciz "armv1" |
| 329 | arm3_arch_name: .asciz "armv2" |
| 330 | arm2_elf_name: .asciz "v1" |
| 331 | arm3_elf_name: .asciz "v2" |
| 332 | .align |
| 333 | |
| 334 | .section ".proc.info", #alloc, #execinstr |
| 335 | |
| 336 | .long 0x41560200 |
| 337 | .long 0xfffffff0 |
| 338 | .long arm2_arch_name |
| 339 | .long arm2_elf_name |
| 340 | .long 0 |
| 341 | .long cpu_arm2_info |
| 342 | .long arm2_processor_functions |
| 343 | |
| 344 | .long 0x41560250 |
| 345 | .long 0xfffffff0 |
| 346 | .long arm3_arch_name |
| 347 | .long arm3_elf_name |
| 348 | .long 0 |
| 349 | .long cpu_arm250_info |
| 350 | .long arm250_processor_functions |
| 351 | |
| 352 | .long 0x41560300 |
| 353 | .long 0xfffffff0 |
| 354 | .long arm3_arch_name |
| 355 | .long arm3_elf_name |
| 356 | .long 0 |
| 357 | .long cpu_arm3_info |
| 358 | .long arm3_processor_functions |
| 359 | |