| /* |
| * Copyright (c) 2008 Travis Geiselbrecht |
| * Copyright (c) 2013, The Linux Foundation. All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <asm.h> |
| #include <arch/ops.h> |
| #include <arch/defines.h> |
| |
| .text |
| |
| #if ARM_WITH_CACHE |
| |
| /* low level cache routines for various cpu families */ |
| |
| #if ARM_CPU_ARM1136 || ARM_CPU_ARM926 |
| |
| /* void arch_disable_cache(uint flags) */ |
| FUNCTION(arch_disable_cache) |
| mov r12, #0 // zero register |
| mrs r3, cpsr // save the old interrupt state |
| #if ARM_ISA_ARMv6 |
| .word 0xf10c01c0 /* cpsid iaf */ // interrupts disabled |
| #else |
| orr r3, r3, #(1<<7) |
| msr cpsr, r3 |
| #endif |
| |
| .Ldcache_disable: |
| tst r0, #DCACHE |
| beq .Licache_disable |
| mrc p15, 0, r1, c1, c0, 0 // cr1 |
| tst r1, #(1<<2) // is the dcache already disabled? |
| beq .Licache_disable |
| |
| bic r1, #(1<<2) |
| mcr p15, 0, r1, c1, c0, 0 // disable dcache |
| |
| #if ARM_CPU_ARM1136 |
| mcr p15, 0, r12, c7, c14, 0 // clean & invalidate dcache |
| #elif ARM_CPU_ARM926 |
| 0: |
| mrc p15, 0, r15, c7, c14, 3 // clean & invalidate dcache |
| bne 0b |
| #else |
| #error whut? |
| #endif |
| mcr p15, 0, r0, c7, c10, 4 // data sync barrier (formerly drain write buffer) |
| |
| .Licache_disable: |
| tst r0, #ICACHE |
| beq .Ldone_disable |
| |
| mrc p15, 0, r1, c1, c0, 0 // cr1 |
| bic r1, #(1<<12) |
| mcr p15, 0, r1, c1, c0, 0 // disable icache |
| |
| mcr p15, 0, r12, c7, c5, 0 // invalidate icache |
| |
| .Ldone_disable: |
| msr cpsr, r3 |
| bx lr |
| |
| /* void arch_enable_cache(uint flags) */ |
| FUNCTION(arch_enable_cache) |
| mov r12, #0 // zero register |
| mrs r3, cpsr // save the old interrupt state |
| #if ARM_ISA_ARMv6 |
| .word 0xf10c01c0 /* cpsid iaf */ // interrupts disabled |
| #else |
| orr r3, r3, #(1<<7) |
| msr cpsr, r3 |
| #endif |
| |
| .Ldcache_enable: |
| tst r0, #DCACHE |
| beq .Licache_enable |
| mrc p15, 0, r1, c1, c0, 0 // cr1 |
| tst r1, #(1<<2) // is the dcache already enabled? |
| bne .Licache_enable |
| |
| mcr p15, 0, r12, c7, c6, 0 // invalidate dcache |
| |
| orr r1, #(1<<2) |
| mcr p15, 0, r1, c1, c0, 0 // enable dcache |
| |
| .Licache_enable: |
| tst r0, #ICACHE |
| beq .Ldone_enable |
| |
| mcr p15, 0, r12, c7, c5, 0 // invalidate icache |
| |
| mrc p15, 0, r1, c1, c0, 0 // cr1 |
| orr r1, #(1<<12) |
| mcr p15, 0, r1, c1, c0, 0 // enable icache |
| |
| .Ldone_enable: |
| msr cpsr, r3 |
| bx lr |
| |
| #elif ARM_CPU_CORTEX_A8 |
| |
| /* void arch_disable_cache(uint flags) */ |
| FUNCTION(arch_disable_cache) |
| stmfd sp!, {r4-r11, lr} |
| |
| mov r7, r0 // save flags |
| |
| mrs r12, cpsr // save the old interrupt state |
| .word 0xf10c01c0 /* cpsid iaf */ // interrupts disabled |
| |
| .Ldcache_disable: |
| tst r7, #DCACHE |
| beq .Licache_disable |
| mrc p15, 0, r0, c1, c0, 0 // cr1 |
| tst r0, #(1<<2) // is the dcache already disabled? |
| beq .Ldcache_already_disabled |
| |
| bic r0, #(1<<2) |
| // make sure all data operations are completed |
| dsb |
| mcr p15, 0, r0, c1, c0, 0 // disable dcache |
| // make sure previous instruction finishes before we clean and flush |
| isb |
| |
| // flush and invalidate the dcache |
| // NOTE: trashes a bunch of registers, can't be spilling stuff to the stack |
| bl flush_invalidate_cache_v7 |
| |
| b .Ldcache_disable_L2 |
| |
| .Ldcache_already_disabled: |
| // make sure all of the caches are invalidated |
| // NOTE: trashes a bunch of registers, can't be spilling stuff to the stack |
| bl invalidate_cache_v7 |
| |
| .Ldcache_disable_L2: |
| |
| #if ARM_WITH_L2 |
| // disable the L2, if present |
| mrc p15, 0, r0, c1, c0, 1 // aux cr1 |
| bic r0, #(1<<1) |
| mcr p15, 0, r0, c1, c0, 1 // disable L2 dcache |
| #endif |
| |
| .Licache_disable: |
| tst r7, #ICACHE |
| beq .Ldone_disable |
| |
| mrc p15, 0, r0, c1, c0, 0 // cr1 |
| bic r0, #(1<<12) |
| mcr p15, 0, r0, c1, c0, 0 // disable icache |
| // make sure previous instruction finishes |
| isb |
| |
| .Ldone_disable: |
| // make sure the icache is always invalidated |
| mov r0, #0 |
| mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU |
| // make sure that data is in sync |
| dsb |
| |
| msr cpsr, r12 |
| ldmfd sp!, {r4-r11, pc} |
| |
| /* void arch_enable_cache(uint flags) */ |
| FUNCTION(arch_enable_cache) |
| stmfd sp!, {r4-r11, lr} |
| |
| mov r7, r0 // save flags |
| |
| mrs r12, cpsr // save the old interrupt state |
| .word 0xf10c01c0 /* cpsid iaf */ // interrupts disabled |
| |
| .Ldcache_enable: |
| tst r7, #DCACHE |
| beq .Licache_enable |
| mrc p15, 0, r0, c1, c0, 0 // cr1 |
| tst r0, #(1<<2) // is the dcache already enabled? |
| bne .Licache_enable |
| |
| // invalidate L1 and L2 |
| // NOTE: trashes a bunch of registers, can't be spilling stuff to the stack |
| bl invalidate_cache_v7 |
| |
| #if ARM_WITH_L2 |
| // enable the L2, if present |
| mrc p15, 0, r0, c1, c0, 1 // aux cr1 |
| orr r0, #(1<<1) |
| mcr p15, 0, r0, c1, c0, 1 // enable L2 dcache |
| #endif |
| |
| mrc p15, 0, r0, c1, c0, 0 // cr1 |
| orr r0, #(1<<2) |
| mcr p15, 0, r0, c1, c0, 0 // enable dcache |
| |
| .Licache_enable: |
| tst r7, #ICACHE |
| beq .Ldone_enable |
| |
| mov r0, #0 |
| mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU |
| |
| mrc p15, 0, r0, c1, c0, 0 // cr1 |
| orr r0, #(1<<12) |
| mcr p15, 0, r0, c1, c0, 0 // enable icache |
| |
| .Ldone_enable: |
| msr cpsr, r12 |
| ldmfd sp!, {r4-r11, pc} |
| |
| // flush & invalidate cache routine, trashes r0-r6, r9-r11 |
| flush_invalidate_cache_v7: |
| DMB |
| /* from ARMv7 manual, B2-17 */ |
| MRC p15, 1, R0, c0, c0, 1 // Read CLIDR |
| ANDS R3, R0, #0x7000000 |
| MOV R3, R3, LSR #23 // Cache level value (naturally aligned) |
| BEQ .Lfinished |
| MOV R10, #0 |
| .Loop1: |
| ADD R2, R10, R10, LSR #1 // Work out 3xcachelevel |
| MOV R1, R0, LSR R2 // bottom 3 bits are the Cache type for this level |
| AND R1, R1, #7 // get those 3 bits alone |
| CMP R1, #2 |
| BLT .Lskip // no cache or only instruction cache at this level |
| MCR p15, 2, R10, c0, c0, 0 // write the Cache Size selection register |
| .word 0xf57ff06f // ISB // ISB to sync the change to the CacheSizeID reg |
| MRC p15, 1, R1, c0, c0, 0 // reads current Cache Size ID register |
| AND R2, R1, #0x7 // extract the line length field |
| ADD R2, R2, #4 // add 4 for the line length offset (log2 16 bytes) |
| LDR R4, =0x3FF |
| ANDS R4, R4, R1, LSR #3 // R4 is the max number on the way size (right aligned) |
| CLZ R5, R4 // R5 is the bit position of the way size increment |
| LDR R6, =0x00007FFF |
| ANDS R6, R6, R1, LSR #13 // R6 is the max number of the index size (right aligned) |
| .Loop2: |
| MOV R9, R4 // R9 working copy of the max way size (right aligned) |
| .Loop3: |
| ORR R11, R10, R9, LSL R5 // factor in the way number and cache number into R11 |
| ORR R11, R11, R6, LSL R2 // factor in the index number |
| MCR p15, 0, R11, c7, c14, 2 // clean & invalidate by set/way |
| SUBS R9, R9, #1 // decrement the way number |
| BGE .Loop3 |
| SUBS R6, R6, #1 // decrement the index |
| BGE .Loop2 |
| .Lskip: |
| ADD R10, R10, #2 // increment the cache number |
| CMP R3, R10 |
| BGT .Loop1 |
| |
| .Lfinished: |
| mov r10, #0 |
| mcr p15, 2, r10, c0, c0, 0 // select cache level 0 |
| dsb |
| .word 0xf57ff06f // isb |
| |
| bx lr |
| |
| // invalidate cache routine, trashes r0-r6, r9-r11 |
| invalidate_cache_v7: |
| /* from ARMv7 manual, B2-17 */ |
| MRC p15, 1, R0, c0, c0, 1 // Read CLIDR |
| ANDS R3, R0, #0x7000000 |
| MOV R3, R3, LSR #23 // Cache level value (naturally aligned) |
| BEQ .Lfinished_invalidate |
| MOV R10, #0 |
| .Loop1_invalidate: |
| ADD R2, R10, R10, LSR #1 // Work out 3xcachelevel |
| MOV R1, R0, LSR R2 // bottom 3 bits are the Cache type for this level |
| AND R1, R1, #7 // get those 3 bits alone |
| CMP R1, #2 |
| BLT .Lskip_invalidate // no cache or only instruction cache at this level |
| MCR p15, 2, R10, c0, c0, 0 // write the Cache Size selection register |
| .word 0xf57ff06f // ISB // ISB to sync the change to the CacheSizeID reg |
| MRC p15, 1, R1, c0, c0, 0 // reads current Cache Size ID register |
| AND R2, R1, #0x7 // extract the line length field |
| ADD R2, R2, #4 // add 4 for the line length offset (log2 16 bytes) |
| LDR R4, =0x3FF |
| ANDS R4, R4, R1, LSR #3 // R4 is the max number on the way size (right aligned) |
| CLZ R5, R4 // R5 is the bit position of the way size increment |
| LDR R6, =0x00007FFF |
| ANDS R6, R6, R1, LSR #13 // R6 is the max number of the index size (right aligned) |
| .Loop2_invalidate: |
| MOV R9, R4 // R9 working copy of the max way size (right aligned) |
| .Loop3_invalidate: |
| ORR R11, R10, R9, LSL R5 // factor in the way number and cache number into R11 |
| ORR R11, R11, R6, LSL R2 // factor in the index number |
| MCR p15, 0, R11, c7, c6, 2 // invalidate by set/way |
| SUBS R9, R9, #1 // decrement the way number |
| BGE .Loop3_invalidate |
| SUBS R6, R6, #1 // decrement the index |
| BGE .Loop2_invalidate |
| .Lskip_invalidate: |
| ADD R10, R10, #2 // increment the cache number |
| CMP R3, R10 |
| BGT .Loop1_invalidate |
| |
| .Lfinished_invalidate: |
| mov r10, #0 |
| mcr p15, 2, r10, c0, c0, 0 // select cache level 0 |
| dsb |
| .word 0xf57ff06f // isb |
| |
| bx lr |
| |
| #else |
| #error unhandled cpu |
| #endif |
| |
| #if ARM_CPU_ARM926 || ARM_CPU_ARM1136 || ARM_CPU_CORTEX_A8 |
| /* shared cache flush routines */ |
| |
| /* void arch_flush_cache_range(addr_t start, size_t len); */ |
| FUNCTION(arch_clean_cache_range) |
| 0: |
| mcr p15, 0, r0, c7, c10, 1 // clean cache to PoC by MVA |
| add r0, r0, #CACHE_LINE |
| subs r1, r1, #CACHE_LINE |
| bhs 0b |
| |
| mov r0, #0 |
| mcr p15, 0, r0, c7, c10, 4 // data sync barrier (formerly drain write buffer) |
| |
| bx lr |
| |
| /* void arch_flush_invalidate_cache_range(addr_t start, size_t len); */ |
| FUNCTION(arch_clean_invalidate_cache_range) |
| 0: |
| mcr p15, 0, r0, c7, c14, 1 // clean & invalidate cache to PoC by MVA |
| add r0, r0, #CACHE_LINE |
| subs r1, r1, #CACHE_LINE |
| bhs 0b |
| |
| mov r0, #0 |
| mcr p15, 0, r0, c7, c10, 4 // data sync barrier (formerly drain write buffer) |
| |
| bx lr |
| |
| /* void arch_invalidate_cache_range(addr_t start, size_t len); */ |
| FUNCTION(arch_invalidate_cache_range) |
| 0: |
| /* invalidate cache line */ |
| mcr p15, 0, r0, c7, c6, 1 |
| add r0, r0, #CACHE_LINE |
| subs r1, r1, #CACHE_LINE |
| bhs 0b |
| mov r0, #0 |
| /* data sync barrier (formerly drain write buffer*/ |
| mcr p15, 0, r0, c7, c10, 4 |
| bx lr |
| |
| /* void arch_sync_cache_range(addr_t start, size_t len); */ |
| FUNCTION(arch_sync_cache_range) |
| push { r14 } |
| bl arch_clean_cache_range |
| |
| mov r0, #0 |
| mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU |
| |
| pop { pc } |
| |
| #else |
| #error unhandled cpu |
| #endif |
| |
| #else |
| |
| /* no cache */ |
| |
| FUNCTION(arch_disable_cache) |
| bx lr |
| |
| FUNCTION(arch_enable_cache) |
| bx lr |
| |
| FUNCTION(arch_clean_cache_range) |
| bx lr |
| |
| FUNCTION(arch_clean_invalidate_cache_range) |
| bx lr |
| |
| FUNCTION(arch_sync_cache_range) |
| bx lr |
| |
| #endif // ARM_WITH_CACHE |
| |