1*4882a593Smuzhiyun/* 2*4882a593Smuzhiyun * (C) Copyright 2013 3*4882a593Smuzhiyun * David Feng <fenghua@phytium.com.cn> 4*4882a593Smuzhiyun * 5*4882a593Smuzhiyun * This file is based on sample code from ARMv8 ARM. 6*4882a593Smuzhiyun * 7*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+ 8*4882a593Smuzhiyun */ 9*4882a593Smuzhiyun 10*4882a593Smuzhiyun#include <asm-offsets.h> 11*4882a593Smuzhiyun#include <config.h> 12*4882a593Smuzhiyun#include <asm/macro.h> 13*4882a593Smuzhiyun#include <asm/system.h> 14*4882a593Smuzhiyun#include <linux/linkage.h> 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun/* 17*4882a593Smuzhiyun * void __asm_dcache_level(level) 18*4882a593Smuzhiyun * 19*4882a593Smuzhiyun * flush or invalidate one level cache. 20*4882a593Smuzhiyun * 21*4882a593Smuzhiyun * x0: cache level 22*4882a593Smuzhiyun * x1: 0 clean & invalidate, 1 invalidate only 23*4882a593Smuzhiyun * x2~x9: clobbered 24*4882a593Smuzhiyun */ 25*4882a593Smuzhiyun.pushsection .text.__asm_dcache_level, "ax" 26*4882a593SmuzhiyunENTRY(__asm_dcache_level) 27*4882a593Smuzhiyun lsl x12, x0, #1 28*4882a593Smuzhiyun msr csselr_el1, x12 /* select cache level */ 29*4882a593Smuzhiyun isb /* sync change of cssidr_el1 */ 30*4882a593Smuzhiyun mrs x6, ccsidr_el1 /* read the new cssidr_el1 */ 31*4882a593Smuzhiyun and x2, x6, #7 /* x2 <- log2(cache line size)-4 */ 32*4882a593Smuzhiyun add x2, x2, #4 /* x2 <- log2(cache line size) */ 33*4882a593Smuzhiyun mov x3, #0x3ff 34*4882a593Smuzhiyun and x3, x3, x6, lsr #3 /* x3 <- max number of #ways */ 35*4882a593Smuzhiyun clz w5, w3 /* bit position of #ways */ 36*4882a593Smuzhiyun mov x4, #0x7fff 37*4882a593Smuzhiyun and x4, x4, x6, lsr #13 /* x4 <- max number of #sets */ 38*4882a593Smuzhiyun /* x12 <- cache level << 1 */ 39*4882a593Smuzhiyun /* x2 <- line length offset */ 40*4882a593Smuzhiyun /* x3 <- number of cache ways - 1 */ 41*4882a593Smuzhiyun /* x4 <- number of cache sets - 1 */ 42*4882a593Smuzhiyun /* x5 <- bit position of #ways */ 43*4882a593Smuzhiyun 44*4882a593Smuzhiyunloop_set: 45*4882a593Smuzhiyun mov x6, x3 /* x6 <- working copy of #ways */ 46*4882a593Smuzhiyunloop_way: 47*4882a593Smuzhiyun lsl x7, x6, x5 48*4882a593Smuzhiyun orr x9, x12, x7 /* map way and level to cisw value */ 49*4882a593Smuzhiyun lsl x7, x4, x2 50*4882a593Smuzhiyun orr x9, x9, x7 /* map set number to cisw value */ 51*4882a593Smuzhiyun tbz w1, #0, 1f 52*4882a593Smuzhiyun dc isw, x9 53*4882a593Smuzhiyun b 2f 54*4882a593Smuzhiyun1: dc cisw, x9 /* clean & invalidate by set/way */ 55*4882a593Smuzhiyun2: subs x6, x6, #1 /* decrement the way */ 56*4882a593Smuzhiyun b.ge loop_way 57*4882a593Smuzhiyun subs x4, x4, #1 /* decrement the set */ 58*4882a593Smuzhiyun b.ge loop_set 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun ret 61*4882a593SmuzhiyunENDPROC(__asm_dcache_level) 62*4882a593Smuzhiyun.popsection 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun/* 65*4882a593Smuzhiyun * void __asm_flush_dcache_all(int invalidate_only) 66*4882a593Smuzhiyun * 67*4882a593Smuzhiyun * x0: 0 clean & invalidate, 1 invalidate only 68*4882a593Smuzhiyun * 69*4882a593Smuzhiyun * flush or invalidate all data cache by SET/WAY. 70*4882a593Smuzhiyun */ 71*4882a593Smuzhiyun.pushsection .text.__asm_dcache_all, "ax" 72*4882a593SmuzhiyunENTRY(__asm_dcache_all) 73*4882a593Smuzhiyun mov x1, x0 74*4882a593Smuzhiyun dsb sy 75*4882a593Smuzhiyun mrs x10, clidr_el1 /* read clidr_el1 */ 76*4882a593Smuzhiyun lsr x11, x10, #24 77*4882a593Smuzhiyun and x11, x11, #0x7 /* x11 <- loc */ 78*4882a593Smuzhiyun cbz x11, finished /* if loc is 0, exit */ 79*4882a593Smuzhiyun mov x15, lr 80*4882a593Smuzhiyun mov x0, #0 /* start flush at cache level 0 */ 81*4882a593Smuzhiyun /* x0 <- cache level */ 82*4882a593Smuzhiyun /* x10 <- clidr_el1 */ 83*4882a593Smuzhiyun /* x11 <- loc */ 84*4882a593Smuzhiyun /* x15 <- return address */ 85*4882a593Smuzhiyun 86*4882a593Smuzhiyunloop_level: 87*4882a593Smuzhiyun lsl x12, x0, #1 88*4882a593Smuzhiyun add x12, x12, x0 /* x0 <- tripled cache level */ 89*4882a593Smuzhiyun lsr x12, x10, x12 90*4882a593Smuzhiyun and x12, x12, #7 /* x12 <- cache type */ 91*4882a593Smuzhiyun cmp x12, #2 92*4882a593Smuzhiyun b.lt skip /* skip if no cache or icache */ 93*4882a593Smuzhiyun bl __asm_dcache_level /* x1 = 0 flush, 1 invalidate */ 94*4882a593Smuzhiyunskip: 95*4882a593Smuzhiyun add x0, x0, #1 /* increment cache level */ 96*4882a593Smuzhiyun cmp x11, x0 97*4882a593Smuzhiyun b.gt loop_level 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun mov x0, #0 100*4882a593Smuzhiyun msr csselr_el1, x0 /* restore csselr_el1 */ 101*4882a593Smuzhiyun dsb sy 102*4882a593Smuzhiyun isb 103*4882a593Smuzhiyun mov lr, x15 104*4882a593Smuzhiyun 105*4882a593Smuzhiyunfinished: 106*4882a593Smuzhiyun ret 107*4882a593SmuzhiyunENDPROC(__asm_dcache_all) 108*4882a593Smuzhiyun.popsection 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun.pushsection .text.__asm_flush_dcache_all, "ax" 111*4882a593SmuzhiyunENTRY(__asm_flush_dcache_all) 112*4882a593Smuzhiyun mov x0, #0 113*4882a593Smuzhiyun b __asm_dcache_all 114*4882a593SmuzhiyunENDPROC(__asm_flush_dcache_all) 115*4882a593Smuzhiyun.popsection 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun.pushsection .text.__asm_invalidate_dcache_all, "ax" 118*4882a593SmuzhiyunENTRY(__asm_invalidate_dcache_all) 119*4882a593Smuzhiyun mov x0, #0x1 120*4882a593Smuzhiyun b __asm_dcache_all 121*4882a593SmuzhiyunENDPROC(__asm_invalidate_dcache_all) 122*4882a593Smuzhiyun.popsection 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun/* 125*4882a593Smuzhiyun * void __asm_flush_dcache_range(start, end) 126*4882a593Smuzhiyun * 127*4882a593Smuzhiyun * clean & invalidate data cache in the range 128*4882a593Smuzhiyun * 129*4882a593Smuzhiyun * x0: start address 130*4882a593Smuzhiyun * x1: end address 131*4882a593Smuzhiyun */ 132*4882a593Smuzhiyun.pushsection .text.__asm_flush_dcache_range, "ax" 133*4882a593SmuzhiyunENTRY(__asm_flush_dcache_range) 134*4882a593Smuzhiyun isb 135*4882a593Smuzhiyun mrs x3, ctr_el0 136*4882a593Smuzhiyun lsr x3, x3, #16 137*4882a593Smuzhiyun and x3, x3, #0xf 138*4882a593Smuzhiyun mov x2, #4 139*4882a593Smuzhiyun lsl x2, x2, x3 /* cache line size */ 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun /* x2 <- minimal cache line size in cache system */ 142*4882a593Smuzhiyun sub x3, x2, #1 143*4882a593Smuzhiyun bic x0, x0, x3 144*4882a593Smuzhiyun1: dc civac, x0 /* clean & invalidate data or unified cache */ 145*4882a593Smuzhiyun add x0, x0, x2 146*4882a593Smuzhiyun cmp x0, x1 147*4882a593Smuzhiyun b.lo 1b 148*4882a593Smuzhiyun dsb sy 149*4882a593Smuzhiyun isb 150*4882a593Smuzhiyun ret 151*4882a593SmuzhiyunENDPROC(__asm_flush_dcache_range) 152*4882a593Smuzhiyun.popsection 153*4882a593Smuzhiyun/* 154*4882a593Smuzhiyun * void __asm_invalidate_dcache_range(start, end) 155*4882a593Smuzhiyun * 156*4882a593Smuzhiyun * invalidate data cache in the range 157*4882a593Smuzhiyun * 158*4882a593Smuzhiyun * x0: start address 159*4882a593Smuzhiyun * x1: end address 160*4882a593Smuzhiyun */ 161*4882a593Smuzhiyun.pushsection .text.__asm_invalidate_dcache_range, "ax" 162*4882a593SmuzhiyunENTRY(__asm_invalidate_dcache_range) 163*4882a593Smuzhiyun mrs x3, ctr_el0 164*4882a593Smuzhiyun ubfm x3, x3, #16, #19 165*4882a593Smuzhiyun mov x2, #4 166*4882a593Smuzhiyun lsl x2, x2, x3 /* cache line size */ 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun /* x2 <- minimal cache line size in cache system */ 169*4882a593Smuzhiyun sub x3, x2, #1 170*4882a593Smuzhiyun bic x0, x0, x3 171*4882a593Smuzhiyun1: dc ivac, x0 /* invalidate data or unified cache */ 172*4882a593Smuzhiyun add x0, x0, x2 173*4882a593Smuzhiyun cmp x0, x1 174*4882a593Smuzhiyun b.lo 1b 175*4882a593Smuzhiyun dsb sy 176*4882a593Smuzhiyun isb 177*4882a593Smuzhiyun ret 178*4882a593SmuzhiyunENDPROC(__asm_invalidate_dcache_range) 179*4882a593Smuzhiyun.popsection 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun/* 182*4882a593Smuzhiyun * void __asm_invalidate_icache_all(void) 183*4882a593Smuzhiyun * 184*4882a593Smuzhiyun * invalidate all tlb entries. 185*4882a593Smuzhiyun */ 186*4882a593Smuzhiyun.pushsection .text.__asm_invalidate_icache_all, "ax" 187*4882a593SmuzhiyunENTRY(__asm_invalidate_icache_all) 188*4882a593Smuzhiyun ic ialluis 189*4882a593Smuzhiyun isb sy 190*4882a593Smuzhiyun ret 191*4882a593SmuzhiyunENDPROC(__asm_invalidate_icache_all) 192*4882a593Smuzhiyun.popsection 193*4882a593Smuzhiyun 194*4882a593Smuzhiyun.pushsection .text.__asm_invalidate_l3_dcache, "ax" 195*4882a593SmuzhiyunENTRY(__asm_invalidate_l3_dcache) 196*4882a593Smuzhiyun mov x0, #0 /* return status as success */ 197*4882a593Smuzhiyun ret 198*4882a593SmuzhiyunENDPROC(__asm_invalidate_l3_dcache) 199*4882a593Smuzhiyun .weak __asm_invalidate_l3_dcache 200*4882a593Smuzhiyun.popsection 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun.pushsection .text.__asm_flush_l3_dcache, "ax" 203*4882a593SmuzhiyunENTRY(__asm_flush_l3_dcache) 204*4882a593Smuzhiyun mov x0, #0 /* return status as success */ 205*4882a593Smuzhiyun ret 206*4882a593SmuzhiyunENDPROC(__asm_flush_l3_dcache) 207*4882a593Smuzhiyun .weak __asm_flush_l3_dcache 208*4882a593Smuzhiyun.popsection 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun.pushsection .text.__asm_invalidate_l3_icache, "ax" 211*4882a593SmuzhiyunENTRY(__asm_invalidate_l3_icache) 212*4882a593Smuzhiyun mov x0, #0 /* return status as success */ 213*4882a593Smuzhiyun ret 214*4882a593SmuzhiyunENDPROC(__asm_invalidate_l3_icache) 215*4882a593Smuzhiyun .weak __asm_invalidate_l3_icache 216*4882a593Smuzhiyun.popsection 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun/* 219*4882a593Smuzhiyun * void __asm_switch_ttbr(ulong new_ttbr) 220*4882a593Smuzhiyun * 221*4882a593Smuzhiyun * Safely switches to a new page table. 222*4882a593Smuzhiyun */ 223*4882a593Smuzhiyun.pushsection .text.__asm_switch_ttbr, "ax" 224*4882a593SmuzhiyunENTRY(__asm_switch_ttbr) 225*4882a593Smuzhiyun /* x2 = SCTLR (alive throghout the function) */ 226*4882a593Smuzhiyun switch_el x4, 3f, 2f, 1f 227*4882a593Smuzhiyun3: mrs x2, sctlr_el3 228*4882a593Smuzhiyun b 0f 229*4882a593Smuzhiyun2: mrs x2, sctlr_el2 230*4882a593Smuzhiyun b 0f 231*4882a593Smuzhiyun1: mrs x2, sctlr_el1 232*4882a593Smuzhiyun0: 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun /* Unset CR_M | CR_C | CR_I from SCTLR to disable all caches */ 235*4882a593Smuzhiyun movn x1, #(CR_M | CR_C | CR_I) 236*4882a593Smuzhiyun and x1, x2, x1 237*4882a593Smuzhiyun switch_el x4, 3f, 2f, 1f 238*4882a593Smuzhiyun3: msr sctlr_el3, x1 239*4882a593Smuzhiyun b 0f 240*4882a593Smuzhiyun2: msr sctlr_el2, x1 241*4882a593Smuzhiyun b 0f 242*4882a593Smuzhiyun1: msr sctlr_el1, x1 243*4882a593Smuzhiyun0: isb 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun /* This call only clobbers x30 (lr) and x9 (unused) */ 246*4882a593Smuzhiyun mov x3, x30 247*4882a593Smuzhiyun bl __asm_invalidate_tlb_all 248*4882a593Smuzhiyun 249*4882a593Smuzhiyun /* From here on we're running safely with caches disabled */ 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun /* Set TTBR to our first argument */ 252*4882a593Smuzhiyun switch_el x4, 3f, 2f, 1f 253*4882a593Smuzhiyun3: msr ttbr0_el3, x0 254*4882a593Smuzhiyun b 0f 255*4882a593Smuzhiyun2: msr ttbr0_el2, x0 256*4882a593Smuzhiyun b 0f 257*4882a593Smuzhiyun1: msr ttbr0_el1, x0 258*4882a593Smuzhiyun0: isb 259*4882a593Smuzhiyun 260*4882a593Smuzhiyun /* Restore original SCTLR and thus enable caches again */ 261*4882a593Smuzhiyun switch_el x4, 3f, 2f, 1f 262*4882a593Smuzhiyun3: msr sctlr_el3, x2 263*4882a593Smuzhiyun b 0f 264*4882a593Smuzhiyun2: msr sctlr_el2, x2 265*4882a593Smuzhiyun b 0f 266*4882a593Smuzhiyun1: msr sctlr_el1, x2 267*4882a593Smuzhiyun0: isb 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun ret x3 270*4882a593SmuzhiyunENDPROC(__asm_switch_ttbr) 271*4882a593Smuzhiyun.popsection 272