1 /* $OpenBSD: cpufunc.c,v 1.58 2025/01/20 20:13:29 kettenis Exp $ */ 2 /* $NetBSD: cpufunc.c,v 1.65 2003/11/05 12:53:15 scw Exp $ */ 3 4 /* 5 * arm7tdmi support code Copyright (c) 2001 John Fremlin 6 * arm8 support code Copyright (c) 1997 ARM Limited 7 * arm8 support code Copyright (c) 1997 Causality Limited 8 * arm9 support code Copyright (C) 2001 ARM Ltd 9 * Copyright (c) 1997 Mark Brinicombe. 10 * Copyright (c) 1997 Causality Limited 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by Causality Limited. 24 * 4. The name of Causality Limited may not be used to endorse or promote 25 * products derived from this software without specific prior written 26 * permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY CAUSALITY LIMITED ``AS IS'' AND ANY EXPRESS 29 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 * DISCLAIMED. IN NO EVENT SHALL CAUSALITY LIMITED BE LIABLE FOR ANY DIRECT, 32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * RiscBSD kernel project 41 * 42 * cpufuncs.c 43 * 44 * C functions for supporting CPU / MMU / TLB specific operations. 45 * 46 * Created : 30/01/97 47 */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 52 #include <uvm/uvm_extern.h> 53 54 #include <machine/pmap.h> 55 #include <arm/cpuconf.h> 56 57 #if defined(PERFCTRS) 58 struct arm_pmc_funcs *arm_pmc; 59 #endif 60 61 /* PRIMARY CACHE VARIABLES */ 62 int arm_picache_size; 63 int arm_picache_line_size; 64 int arm_picache_ways; 65 66 int arm_pdcache_size; /* and unified */ 67 int arm_pdcache_line_size; 68 int arm_pdcache_ways; 69 70 int arm_pcache_type; 71 int arm_pcache_unified; 72 73 int arm_dcache_align; 74 int arm_dcache_align_mask; 75 76 /* 1 == use cpu_sleep(), 0 == don't */ 77 int cpu_do_powersave; 78 79 struct cpu_functions armv7_cpufuncs = { 80 /* CPU functions */ 81 82 cpufunc_id, /* id */ 83 cpufunc_nullop, /* cpwait */ 84 85 /* MMU functions */ 86 87 cpufunc_control, /* control */ 88 cpufunc_auxcontrol, /* aux control */ 89 cpufunc_domains, /* Domain */ 90 armv7_setttb, /* Setttb */ 91 cpufunc_dfsr, /* dfsr */ 92 cpufunc_dfar, /* dfar */ 93 cpufunc_ifsr, /* ifsr */ 94 cpufunc_ifar, /* ifar */ 95 96 /* TLB functions */ 97 98 armv7_tlb_flushID, /* tlb_flushID */ 99 armv7_tlb_flushID_SE, /* tlb_flushID_SE */ 100 armv7_tlb_flushID, /* tlb_flushI */ 101 armv7_tlb_flushID_SE, /* tlb_flushI_SE */ 102 armv7_tlb_flushD, /* tlb_flushD */ 103 armv7_tlb_flushD_SE, /* tlb_flushD_SE */ 104 105 /* Cache operations */ 106 107 armv7_icache_sync_all, /* icache_sync_all */ 108 armv7_icache_sync_range, /* icache_sync_range */ 109 110 armv7_dcache_wbinv_all, /* dcache_wbinv_all */ 111 armv7_dcache_wbinv_range, /* dcache_wbinv_range */ 112 armv7_dcache_inv_range, /* dcache_inv_range */ 113 armv7_dcache_wb_range, /* dcache_wb_range */ 114 115 armv7_idcache_wbinv_all, /* idcache_wbinv_all */ 116 armv7_idcache_wbinv_range, /* idcache_wbinv_range */ 117 118 cpufunc_nullop, /* sdcache_wbinv_all */ 119 (void *)cpufunc_nullop, /* sdcache_wbinv_range */ 120 (void *)cpufunc_nullop, /* sdcache_inv_range */ 121 (void *)cpufunc_nullop, /* sdcache_wb_range */ 122 (void *)cpufunc_nullop, /* sdcache_drain_writebuf */ 123 124 /* Other functions */ 125 126 cpufunc_nullop, /* flush_prefetchbuf */ 127 armv7_drain_writebuf, /* drain_writebuf */ 128 129 armv7_cpu_sleep, /* sleep (wait for interrupt) */ 130 131 /* Soft functions */ 132 armv7_context_switch, /* context_switch */ 133 armv7_setup /* cpu setup */ 134 }; 135 136 /* 137 * Global constants also used by locore.s 138 */ 139 140 struct cpu_functions cpufuncs; 141 u_int cputype; 142 143 int arm_icache_min_line_size = 32; 144 int arm_dcache_min_line_size = 32; 145 int arm_idcache_min_line_size = 32; 146 147 void arm_get_cachetype_cp15v7 (void); 148 int arm_dcache_l2_nsets; 149 int arm_dcache_l2_assoc; 150 int arm_dcache_l2_linesize; 151 152 /* 153 * Base 2 logarithm of an int. returns 0 for 0 (yeye, I know). 154 */ 155 static int 156 log2(unsigned int i) 157 { 158 int ret = 0; 159 160 while (i >>= 1) 161 ret++; 162 163 return (ret); 164 } 165 166 void 167 arm_get_cachetype_cp15v7(void) 168 { 169 uint32_t ctype; 170 uint32_t cachereg; 171 uint32_t cache_level_id; 172 uint32_t sets; 173 uint32_t sel, level; 174 175 /* CTR - Cache Type Register */ 176 __asm volatile("mrc p15, 0, %0, c0, c0, 1" 177 : "=r" (ctype)); 178 179 arm_dcache_min_line_size = 1 << (CPU_CT_DMINLINE(ctype) + 2); 180 arm_icache_min_line_size = 1 << (CPU_CT_IMINLINE(ctype) + 2); 181 arm_idcache_min_line_size = 182 min(arm_icache_min_line_size, arm_dcache_min_line_size); 183 184 /* CLIDR - Cache Level ID Register */ 185 __asm volatile("mrc p15, 1, %0, c0, c0, 1" 186 : "=r" (cache_level_id) :); 187 cpu_drain_writebuf(); 188 189 /* L1 Cache available. */ 190 level = 0; 191 if (cache_level_id & (0x7 << level)) { 192 /* Unified cache. */ 193 if (cache_level_id & (0x4 << level)) 194 arm_pcache_unified = 1; 195 196 /* Unified or data cache separate. */ 197 if (cache_level_id & (0x4 << level) || 198 cache_level_id & (0x2 << level)) { 199 sel = level << 1 | 0 << 0; /* L1 | unified/data cache */ 200 /* CSSELR - Cache Size Selection Register */ 201 __asm volatile("mcr p15, 2, %0, c0, c0, 0" 202 :: "r" (sel)); 203 cpu_drain_writebuf(); 204 /* CCSIDR - Cache Size Identification Register */ 205 __asm volatile("mrc p15, 1, %0, c0, c0, 0" 206 : "=r" (cachereg) :); 207 cpu_drain_writebuf(); 208 sets = ((cachereg >> 13) & 0x7fff) + 1; 209 arm_pdcache_line_size = 1 << ((cachereg & 0x7) + 4); 210 arm_pdcache_ways = ((cachereg >> 3) & 0x3ff) + 1; 211 arm_pdcache_size = arm_pdcache_line_size * arm_pdcache_ways * sets; 212 switch (cachereg & 0xc0000000) { 213 case 0x00000000: 214 arm_pcache_type = 0; 215 break; 216 case 0x40000000: 217 case 0xc0000000: 218 arm_pcache_type = CPU_CT_CTYPE_WB1; 219 break; 220 case 0x80000000: 221 arm_pcache_type = CPU_CT_CTYPE_WT; 222 break; 223 } 224 } 225 226 /* Instruction cache separate. */ 227 if (cache_level_id & (0x1 << level)) { 228 sel = level << 1 | 1 << 0; /* L1 | instruction cache */ 229 /* CSSELR - Cache Size Selection Register */ 230 __asm volatile("mcr p15, 2, %0, c0, c0, 0" 231 :: "r" (sel)); 232 cpu_drain_writebuf(); 233 /* CCSIDR - Cache Size Identification Register */ 234 __asm volatile("mrc p15, 1, %0, c0, c0, 0" 235 : "=r" (cachereg) :); 236 cpu_drain_writebuf(); 237 sets = ((cachereg >> 13) & 0x7fff) + 1; 238 arm_picache_line_size = 1 << ((cachereg & 0x7) + 4); 239 arm_picache_ways = ((cachereg >> 3) & 0x3ff) + 1; 240 arm_picache_size = arm_picache_line_size * arm_picache_ways * sets; 241 } 242 } 243 244 arm_dcache_align = arm_pdcache_line_size; 245 arm_dcache_align_mask = arm_dcache_align - 1; 246 247 arm_dcache_l2_nsets = arm_pdcache_size/arm_pdcache_ways/arm_pdcache_line_size; 248 arm_dcache_l2_assoc = log2(arm_pdcache_ways); 249 arm_dcache_l2_linesize = log2(arm_pdcache_line_size); 250 } 251 252 /* 253 */ 254 void 255 armv7_idcache_wbinv_all(void) 256 { 257 uint32_t arg; 258 arg = 0; 259 __asm volatile("mcr p15, 0, r0, c7, c5, 0" :: "r" (arg)); 260 armv7_dcache_wbinv_all(); 261 } 262 263 /* brute force cache flushing */ 264 void 265 armv7_dcache_wbinv_all(void) 266 { 267 int sets, ways, lvl; 268 int nsets, nways; 269 uint32_t wayincr, setincr; 270 uint32_t wayval, setval; 271 uint32_t word; 272 273 nsets = arm_dcache_l2_nsets; 274 nways = arm_pdcache_ways; 275 276 setincr = armv7_dcache_sets_inc; 277 wayincr = armv7_dcache_index_inc; 278 279 #if 0 280 printf("l1 nsets %d nways %d wayincr %x setincr %x\n", 281 nsets, nways, wayincr, setincr); 282 #endif 283 284 lvl = 0; /* L1 */ 285 setval = 0; 286 for (sets = 0; sets < nsets; sets++) { 287 wayval = 0; 288 for (ways = 0; ways < nways; ways++) { 289 word = wayval | setval | lvl; 290 291 /* Clean D cache SE with Set/Index */ 292 __asm volatile("mcr p15, 0, %0, c7, c10, 2" 293 : : "r" (word)); 294 wayval += wayincr; 295 } 296 setval += setincr; 297 } 298 /* drain the write buffer */ 299 cpu_drain_writebuf(); 300 301 /* L2 cache flushing removed. Our current L2 caches are separate. */ 302 } 303 304 305 /* 306 * Cannot panic here as we may not have a console yet ... 307 */ 308 309 int 310 set_cpufuncs(void) 311 { 312 cputype = cpufunc_id(); 313 cputype &= CPU_ID_CPU_MASK; 314 315 /* 316 * NOTE: cpu_do_powersave defaults to off. If we encounter a 317 * CPU type where we want to use it by default, then we set it. 318 */ 319 320 if ((cputype & CPU_ID_ARCH_MASK) == CPU_ID_ARCH_CPUID) { 321 uint32_t mmfr0; 322 323 __asm volatile("mrc p15, 0, %0, c0, c1, 4" 324 : "=r" (mmfr0)); 325 326 switch (mmfr0 & ID_MMFR0_VMSA_MASK) { 327 case VMSA_V7: 328 case VMSA_V7_PXN: 329 case VMSA_V7_LDT: 330 cpufuncs = armv7_cpufuncs; 331 /* V4 or higher */ 332 arm_get_cachetype_cp15v7(); 333 armv7_dcache_sets_inc = 1U << arm_dcache_l2_linesize; 334 armv7_dcache_sets_max = (1U << (arm_dcache_l2_linesize + 335 arm_dcache_l2_nsets)) - armv7_dcache_sets_inc; 336 armv7_dcache_index_inc = 1U << (32 - 337 arm_dcache_l2_assoc); 338 armv7_dcache_index_max = 0U - armv7_dcache_index_inc; 339 pmap_pte_init_armv7(); 340 341 /* Use powersave on this CPU. */ 342 cpu_do_powersave = 1; 343 return 0; 344 } 345 } 346 /* 347 * Bzzzz. And the answer was ... 348 */ 349 panic("No support for this CPU type (%08x) in kernel", cputype); 350 return(ARCHITECTURE_NOT_PRESENT); 351 } 352 353 /* 354 * CPU Setup code 355 */ 356 357 void 358 armv7_setup(void) 359 { 360 uint32_t auxctrl, auxctrlmask; 361 uint32_t cpuctrl, cpuctrlmask; 362 uint32_t id_pfr1; 363 364 auxctrl = auxctrlmask = 0; 365 366 switch (cputype & CPU_ID_CORTEX_MASK) { 367 case CPU_ID_CORTEX_A5: 368 case CPU_ID_CORTEX_A9: 369 /* Cache and TLB maintenance broadcast */ 370 #ifdef notyet 371 auxctrlmask |= CORTEXA9_AUXCTL_FW; 372 auxctrl |= CORTEXA9_AUXCTL_FW; 373 #endif 374 /* FALLTHROUGH */ 375 case CPU_ID_CORTEX_A7: 376 case CPU_ID_CORTEX_A12: 377 case CPU_ID_CORTEX_A15: 378 case CPU_ID_CORTEX_A17: 379 /* Set SMP to allow LDREX/STREX */ 380 auxctrlmask |= CORTEXA9_AUXCTL_SMP; 381 auxctrl |= CORTEXA9_AUXCTL_SMP; 382 break; 383 } 384 385 cpuctrlmask = CPU_CONTROL_MMU_ENABLE 386 | CPU_CONTROL_AFLT_ENABLE 387 | CPU_CONTROL_DC_ENABLE 388 | CPU_CONTROL_BPRD_ENABLE 389 | CPU_CONTROL_IC_ENABLE 390 | CPU_CONTROL_VECRELOC 391 | CPU_CONTROL_TRE 392 | CPU_CONTROL_AFE; 393 394 cpuctrl = CPU_CONTROL_MMU_ENABLE 395 | CPU_CONTROL_DC_ENABLE 396 | CPU_CONTROL_BPRD_ENABLE 397 | CPU_CONTROL_IC_ENABLE 398 | CPU_CONTROL_AFE; 399 400 if (vector_page == ARM_VECTORS_HIGH) 401 cpuctrl |= CPU_CONTROL_VECRELOC; 402 403 /* 404 * Check for the Virtualization Extensions and enable UWXN of 405 * those are included. 406 */ 407 __asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1)); 408 if ((id_pfr1 & 0x0000f000) == 0x00001000) { 409 cpuctrlmask |= CPU_CONTROL_UWXN; 410 cpuctrl |= CPU_CONTROL_UWXN; 411 } 412 413 /* Clear out the cache */ 414 cpu_idcache_wbinv_all(); 415 416 /* 417 * Set the auxiliary control register first, as the SMP bit 418 * needs to be set to 1 before the caches and the MMU are 419 * enabled. 420 */ 421 cpu_auxcontrol(auxctrlmask, auxctrl); 422 423 /* Set the control register */ 424 cpu_control(cpuctrlmask, cpuctrl); 425 426 /* And again. */ 427 cpu_idcache_wbinv_all(); 428 } 429