1 /* $NetBSD: cpu.c,v 1.9 2001/11/06 20:39:13 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Mark Brinicombe. 5 * Copyright (c) 1995 Brini. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Brini. 19 * 4. The name of the company nor the name of the author may be used to 20 * endorse or promote products derived from this software without specific 21 * prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * RiscBSD kernel project 36 * 37 * cpu.c 38 * 39 * Probing and configuration for the master cpu 40 * 41 * Created : 10/10/95 42 */ 43 44 #include "opt_armfpe.h" 45 #include "opt_cputypes.h" 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/malloc.h> 50 #include <sys/device.h> 51 #include <sys/proc.h> 52 #include <uvm/uvm_extern.h> 53 #include <machine/conf.h> 54 #include <machine/cpu.h> 55 #include <machine/cpus.h> 56 #include <machine/undefined.h> 57 58 #ifdef ARMFPE 59 #include <machine/bootconfig.h> /* For boot args */ 60 #include <arm32/fpe-arm/armfpe.h> 61 #endif /* ARMFPE */ 62 63 cpu_t cpus[MAX_CPUS]; 64 65 char cpu_model[64]; 66 volatile int undefined_test; /* Used for FPA test */ 67 extern int cpuctrl; /* cpu control register value */ 68 69 /* Prototypes */ 70 void identify_master_cpu __P((struct device *dv, int cpu_number)); 71 void identify_arm_cpu __P((struct device *dv, int cpu_number)); 72 void identify_arm_fpu __P((struct device *dv, int cpu_number)); 73 int fpa_test __P((u_int, u_int, trapframe_t *, int)); 74 int fpa_handler __P((u_int, u_int, trapframe_t *, int)); 75 76 /* 77 * void cpusattach(struct device *parent, struct device *dev, void *aux) 78 * 79 * Attach the main cpu 80 */ 81 82 void 83 cpu_attach(dv) 84 struct device *dv; 85 { 86 identify_master_cpu(dv, CPU_MASTER); 87 } 88 89 /* 90 * Used to test for an FPA. The following function is installed as a coproc1 91 * handler on the undefined instruction vector and then we issue a FPA 92 * instruction. If undefined_test is non zero then the FPA did not handle 93 * the instruction so must be absent. 94 */ 95 96 int 97 fpa_test(address, instruction, frame, fault_code) 98 u_int address; 99 u_int instruction; 100 trapframe_t *frame; 101 int fault_code; 102 { 103 104 frame->tf_pc += INSN_SIZE; 105 ++undefined_test; 106 return(0); 107 } 108 109 /* 110 * If an FPA was found then this function is installed as the coproc1 handler 111 * on the undefined instruction vector. Currently we don't support FPA's 112 * so this just triggers an exception. 113 */ 114 115 int 116 fpa_handler(address, instruction, frame, fault_code) 117 u_int address; 118 u_int instruction; 119 trapframe_t *frame; 120 int fault_code; 121 { 122 u_int fpsr; 123 124 __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); 125 126 printf("FPA exception: fpsr = %08x\n", fpsr); 127 128 return(1); 129 } 130 131 132 /* 133 * Identify the master (boot) CPU 134 * This also probes for an FPU and will install an FPE if necessary 135 */ 136 137 void 138 identify_master_cpu(dv, cpu_number) 139 struct device *dv; 140 int cpu_number; 141 { 142 u_int fpsr; 143 void *uh; 144 145 cpus[cpu_number].cpu_ctrl = cpuctrl; 146 147 /* Get the cpu ID from coprocessor 15 */ 148 149 cpus[cpu_number].cpu_id = cpu_id(); 150 151 identify_arm_cpu(dv, cpu_number); 152 strcpy(cpu_model, cpus[cpu_number].cpu_model); 153 154 if (cpus[CPU_MASTER].cpu_class == CPU_CLASS_SA1 155 && (cpus[CPU_MASTER].cpu_id & CPU_ID_REVISION_MASK) < 3) { 156 printf("%s: SA-110 with bugged STM^ instruction\n", 157 dv->dv_xname); 158 } 159 160 #ifdef CPU_ARM8 161 if ((cpus[CPU_MASTER].cpu_id & CPU_ID_CPU_MASK) == CPU_ID_ARM810) { 162 int clock = arm8_clock_config(0, 0); 163 char *fclk; 164 printf("%s: ARM810 cp15=%02x", dv->dv_xname, clock); 165 printf(" clock:%s", (clock & 1) ? " dynamic" : ""); 166 printf("%s", (clock & 2) ? " sync" : ""); 167 switch ((clock >> 2) & 3) { 168 case 0 : 169 fclk = "bus clock"; 170 break; 171 case 1 : 172 fclk = "ref clock"; 173 break; 174 case 3 : 175 fclk = "pll"; 176 break; 177 default : 178 fclk = "illegal"; 179 break; 180 } 181 printf(" fclk source=%s\n", fclk); 182 } 183 #endif 184 185 /* 186 * Ok now we test for an FPA 187 * At this point no floating point emulator has been installed. 188 * This means any FP instruction will cause undefined exception. 189 * We install a temporay coproc 1 handler which will modify 190 * undefined_test if it is called. 191 * We then try to read the FP status register. If undefined_test 192 * has been decremented then the instruction was not handled by 193 * an FPA so we know the FPA is missing. If undefined_test is 194 * still 1 then we know the instruction was handled by an FPA. 195 * We then remove our test handler and look at the 196 * FP status register for identification. 197 */ 198 199 uh = install_coproc_handler(FP_COPROC, fpa_test); 200 201 undefined_test = 0; 202 203 __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); 204 205 remove_coproc_handler(uh); 206 207 if (undefined_test == 0) { 208 cpus[cpu_number].fpu_type = (fpsr >> 24); 209 switch (fpsr >> 24) { 210 case 0x81 : 211 cpus[cpu_number].fpu_class = FPU_CLASS_FPA; 212 break; 213 214 default : 215 cpus[cpu_number].fpu_class = FPU_CLASS_FPU; 216 break; 217 } 218 cpus[cpu_number].fpu_flags = 0; 219 install_coproc_handler(FP_COPROC, fpa_handler); 220 } else { 221 cpus[cpu_number].fpu_class = FPU_CLASS_NONE; 222 cpus[cpu_number].fpu_flags = 0; 223 224 /* 225 * Ok if ARMFPE is defined and the boot options request the 226 * ARM FPE then it will be installed as the FPE. 227 * This is just while I work on integrating the new FPE. 228 * It means the new FPE gets installed if compiled int (ARMFPE 229 * defined) and also gives me a on/off option when I boot in 230 * case the new FPE is causing panics. 231 */ 232 233 #ifdef ARMFPE 234 if (boot_args) { 235 int usearmfpe = 1; 236 237 get_bootconf_option(boot_args, "armfpe", 238 BOOTOPT_TYPE_BOOLEAN, &usearmfpe); 239 if (usearmfpe) { 240 if (initialise_arm_fpe(&cpus[cpu_number]) != 0) 241 identify_arm_fpu(dv, cpu_number); 242 } 243 } 244 245 #endif 246 } 247 248 identify_arm_fpu(dv, cpu_number); 249 } 250 251 struct cpuidtab { 252 u_int32_t cpuid; 253 enum cpu_class cpu_class; 254 const char *cpu_name; 255 }; 256 257 const struct cpuidtab cpuids[] = { 258 { CPU_ID_ARM2, CPU_CLASS_ARM2, "ARM2" }, 259 { CPU_ID_ARM250, CPU_CLASS_ARM2AS, "ARM250" }, 260 { CPU_ID_ARM3, CPU_CLASS_ARM3, "ARM3" }, 261 { CPU_ID_ARM600, CPU_CLASS_ARM6, "ARM600" }, 262 { CPU_ID_ARM610, CPU_CLASS_ARM6, "ARM610" }, 263 { CPU_ID_ARM620, CPU_CLASS_ARM6, "ARM620" }, 264 { CPU_ID_ARM700, CPU_CLASS_ARM7, "ARM700" }, 265 { CPU_ID_ARM710, CPU_CLASS_ARM7, "ARM710" }, 266 { CPU_ID_ARM7500, CPU_CLASS_ARM7, "ARM7500" }, 267 { CPU_ID_ARM710A, CPU_CLASS_ARM7, "ARM710a" }, 268 { CPU_ID_ARM7500FE, CPU_CLASS_ARM7, "ARM7500FE" }, 269 { CPU_ID_ARM710T, CPU_CLASS_ARM7TDMI, "ARM710T" }, 270 { CPU_ID_ARM720T, CPU_CLASS_ARM7TDMI, "ARM720T" }, 271 { CPU_ID_ARM740T8K, CPU_CLASS_ARM7TDMI, "ARM740T (8 KB cache)" }, 272 { CPU_ID_ARM740T4K, CPU_CLASS_ARM7TDMI, "ARM740T (4 KB cache)" }, 273 { CPU_ID_ARM810, CPU_CLASS_ARM8, "ARM810" }, 274 { CPU_ID_ARM920T, CPU_CLASS_ARM9TDMI, "ARM920T" }, 275 { CPU_ID_ARM922T, CPU_CLASS_ARM9TDMI, "ARM922T" }, 276 { CPU_ID_ARM940T, CPU_CLASS_ARM9TDMI, "ARM940T" }, 277 { CPU_ID_ARM946ES, CPU_CLASS_ARM9ES, "ARM946E-S" }, 278 { CPU_ID_ARM966ES, CPU_CLASS_ARM9ES, "ARM966E-S" }, 279 { CPU_ID_ARM966ESR1, CPU_CLASS_ARM9ES, "ARM966E-S" }, 280 { CPU_ID_SA110, CPU_CLASS_SA1, "SA-110" }, 281 { CPU_ID_SA1100, CPU_CLASS_SA1, "SA-1100" }, 282 { CPU_ID_SA1110, CPU_CLASS_SA1, "SA-1110" }, 283 { CPU_ID_I80200, CPU_CLASS_XSCALE, "i80200" }, 284 { 0, CPU_CLASS_NONE, NULL } 285 }; 286 287 struct cpu_classtab { 288 const char *class_name; 289 const char *class_option; 290 }; 291 292 const struct cpu_classtab cpu_classes[] = { 293 { "unknown", NULL }, /* CPU_CLASS_NONE */ 294 { "ARM2", "CPU_ARM2" }, /* CPU_CLASS_ARM2 */ 295 { "ARM2as", "CPU_ARM250" }, /* CPU_CLASS_ARM2AS */ 296 { "ARM3", "CPU_ARM3" }, /* CPU_CLASS_ARM3 */ 297 { "ARM6", "CPU_ARM6" }, /* CPU_CLASS_ARM6 */ 298 { "ARM7", "CPU_ARM7" }, /* CPU_CLASS_ARM7 */ 299 { "ARM7TDMI", "CPU_ARM7TDMI" }, /* CPU_CLASS_ARM7TDMI */ 300 { "ARM8", "CPU_ARM8" }, /* CPU_CLASS_ARM8 */ 301 { "ARM9TDMI", NULL }, /* CPU_CLASS_ARM9TDMI */ 302 { "ARM9E-S", NULL }, /* CPU_CLASS_ARM9ES */ 303 { "SA-1", "CPU_SA110" }, /* CPU_CLASS_SA1 */ 304 { "XScale", "CPU_XSCALE" }, /* CPU_CLASS_XSCALE */ 305 }; 306 307 /* 308 * Report the type of the specifed arm processor. This uses the generic and 309 * arm specific information in the cpu structure to identify the processor. 310 * The remaining fields in the cpu structure are filled in appropriately. 311 */ 312 313 void 314 identify_arm_cpu(dv, cpu_number) 315 struct device *dv; 316 int cpu_number; 317 { 318 cpu_t *cpu; 319 u_int cpuid; 320 int i; 321 322 cpu = &cpus[cpu_number]; 323 cpuid = cpu->cpu_id; 324 325 if (cpuid == 0) { 326 printf("Processor failed probe - no CPU ID\n"); 327 return; 328 } 329 330 for (i = 0; cpuids[i].cpuid != 0; i++) 331 if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) { 332 cpu->cpu_class = cpuids[i].cpu_class; 333 sprintf(cpu->cpu_model, "%s rev %d (%s core)", 334 cpuids[i].cpu_name, cpuid & CPU_ID_REVISION_MASK, 335 cpu_classes[cpu->cpu_class].class_name); 336 break; 337 } 338 339 if (cpuids[i].cpuid == 0) 340 sprintf(cpu->cpu_model, "unknown CPU (ID = 0x%x)", cpuid); 341 342 switch (cpu->cpu_class) { 343 case CPU_CLASS_ARM6: 344 case CPU_CLASS_ARM7: 345 case CPU_CLASS_ARM7TDMI: 346 case CPU_CLASS_ARM8: 347 if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0) 348 strcat(cpu->cpu_model, " IDC disabled"); 349 else 350 strcat(cpu->cpu_model, " IDC enabled"); 351 break; 352 case CPU_CLASS_ARM9TDMI: 353 case CPU_CLASS_SA1: 354 case CPU_CLASS_XSCALE: 355 if ((cpu->cpu_ctrl & CPU_CONTROL_DC_ENABLE) == 0) 356 strcat(cpu->cpu_model, " DC disabled"); 357 else 358 strcat(cpu->cpu_model, " DC enabled"); 359 if ((cpu->cpu_ctrl & CPU_CONTROL_IC_ENABLE) == 0) 360 strcat(cpu->cpu_model, " IC disabled"); 361 else 362 strcat(cpu->cpu_model, " IC enabled"); 363 break; 364 } 365 if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0) 366 strcat(cpu->cpu_model, " WB disabled"); 367 else 368 strcat(cpu->cpu_model, " WB enabled"); 369 370 if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE) 371 strcat(cpu->cpu_model, " LABT"); 372 else 373 strcat(cpu->cpu_model, " EABT"); 374 375 if (cpu->cpu_ctrl & CPU_CONTROL_BPRD_ENABLE) 376 strcat(cpu->cpu_model, " branch prediction enabled"); 377 378 /* Print the info */ 379 380 printf(": %s\n", cpu->cpu_model); 381 382 switch (cpu->cpu_class) { 383 #ifdef CPU_ARM2 384 case CPU_CLASS_ARM2: 385 #endif 386 #ifdef CPU_ARM250 387 case CPU_CLASS_ARM2AS: 388 #endif 389 #ifdef CPU_ARM3 390 case CPU_CLASS_ARM3: 391 #endif 392 #ifdef CPU_ARM6 393 case CPU_CLASS_ARM6: 394 #endif 395 #ifdef CPU_ARM7 396 case CPU_CLASS_ARM7: 397 #endif 398 #ifdef CPU_ARM7TDMI 399 case CPU_CLASS_ARM7TDMI: 400 #endif 401 #ifdef CPU_ARM8 402 case CPU_CLASS_ARM8: 403 #endif 404 #ifdef CPU_ARM9 405 case CPU_CLASS_ARM9TDMI: 406 #endif 407 #ifdef CPU_SA110 408 case CPU_CLASS_SA1: 409 #endif 410 #ifdef CPU_XSCALE 411 case CPU_CLASS_XSCALE: 412 #endif 413 break; 414 default: 415 if (cpu_classes[cpu->cpu_class].class_option != NULL) 416 printf("%s: %s does not fully support this CPU." 417 "\n", dv->dv_xname, ostype); 418 else { 419 printf("%s: This kernel does not fully support " 420 "this CPU.\n", dv->dv_xname); 421 printf("%s: Recompile with \"options %s\" to " 422 "correct this.\n", dv->dv_xname, 423 cpu_classes[cpu->cpu_class].class_option); 424 } 425 break; 426 } 427 428 } 429 430 431 /* 432 * Report the type of the specifed arm fpu. This uses the generic and arm 433 * specific information in the cpu structure to identify the fpu. The 434 * remaining fields in the cpu structure are filled in appropriately. 435 */ 436 437 void 438 identify_arm_fpu(dv, cpu_number) 439 struct device *dv; 440 int cpu_number; 441 { 442 cpu_t *cpu; 443 444 cpu = &cpus[cpu_number]; 445 446 /* Now for the FP info */ 447 448 switch (cpu->fpu_class) { 449 case FPU_CLASS_NONE : 450 strcpy(cpu->fpu_model, "None"); 451 break; 452 case FPU_CLASS_FPE : 453 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model); 454 printf("%s: no FP hardware found\n", dv->dv_xname); 455 break; 456 case FPU_CLASS_FPA : 457 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model); 458 if (cpu->fpu_type == FPU_TYPE_FPA11) { 459 strcpy(cpu->fpu_model, "FPA11"); 460 printf("%s: FPA11 found\n", dv->dv_xname); 461 } else { 462 strcpy(cpu->fpu_model, "FPA"); 463 printf("%s: FPA10 found\n", dv->dv_xname); 464 } 465 if ((cpu->fpu_flags & 4) == 0) 466 strcat(cpu->fpu_model, ""); 467 else 468 strcat(cpu->fpu_model, " clk/2"); 469 break; 470 case FPU_CLASS_FPU : 471 sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n", 472 cpu->fpu_type); 473 printf("%s: %s\n", dv->dv_xname, cpu->fpu_model); 474 break; 475 } 476 } 477 478 /* End of cpu.c */ 479