1 /* $NetBSD: cpu.c,v 1.14 2002/01/27 14:43:47 bjh21 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 <arm/undefined.h> 56 57 #include <arm/cpus.h> 58 59 #ifdef ARMFPE 60 #include <machine/bootconfig.h> /* For boot args */ 61 #include <arm/fpe-arm/armfpe.h> 62 #endif 63 64 cpu_t cpus[MAX_CPUS]; 65 66 char cpu_model[64]; 67 volatile int undefined_test; /* Used for FPA test */ 68 extern int cpuctrl; /* cpu control register value */ 69 70 /* Prototypes */ 71 void identify_master_cpu __P((struct device *dv, int cpu_number)); 72 void identify_arm_cpu __P((struct device *dv, int cpu_number)); 73 void identify_arm_fpu __P((struct device *dv, int cpu_number)); 74 int fpa_test __P((u_int, u_int, trapframe_t *, int)); 75 int fpa_handler __P((u_int, u_int, trapframe_t *, int)); 76 77 /* 78 * void cpusattach(struct device *parent, struct device *dev, void *aux) 79 * 80 * Attach the main cpu 81 */ 82 83 void 84 cpu_attach(dv) 85 struct device *dv; 86 { 87 identify_master_cpu(dv, CPU_MASTER); 88 } 89 90 /* 91 * Used to test for an FPA. The following function is installed as a coproc1 92 * handler on the undefined instruction vector and then we issue a FPA 93 * instruction. If undefined_test is non zero then the FPA did not handle 94 * the instruction so must be absent. 95 */ 96 97 int 98 fpa_test(address, instruction, frame, fault_code) 99 u_int address; 100 u_int instruction; 101 trapframe_t *frame; 102 int fault_code; 103 { 104 105 frame->tf_pc += INSN_SIZE; 106 ++undefined_test; 107 return(0); 108 } 109 110 /* 111 * If an FPA was found then this function is installed as the coproc1 handler 112 * on the undefined instruction vector. Currently we don't support FPA's 113 * so this just triggers an exception. 114 */ 115 116 int 117 fpa_handler(address, instruction, frame, fault_code) 118 u_int address; 119 u_int instruction; 120 trapframe_t *frame; 121 int fault_code; 122 { 123 u_int fpsr; 124 125 __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); 126 127 printf("FPA exception: fpsr = %08x\n", fpsr); 128 129 return(1); 130 } 131 132 133 /* 134 * Identify the master (boot) CPU 135 * This also probes for an FPU and will install an FPE if necessary 136 */ 137 138 void 139 identify_master_cpu(dv, cpu_number) 140 struct device *dv; 141 int cpu_number; 142 { 143 u_int fpsr; 144 void *uh; 145 146 cpus[cpu_number].cpu_ctrl = cpuctrl; 147 148 /* Get the cpu ID from coprocessor 15 */ 149 150 cpus[cpu_number].cpu_id = cpu_id(); 151 152 identify_arm_cpu(dv, cpu_number); 153 strcpy(cpu_model, cpus[cpu_number].cpu_model); 154 155 if (cpus[CPU_MASTER].cpu_class == CPU_CLASS_SA1 156 && (cpus[CPU_MASTER].cpu_id & CPU_ID_REVISION_MASK) < 3) { 157 printf("%s: SA-110 with bugged STM^ instruction\n", 158 dv->dv_xname); 159 } 160 161 #ifdef CPU_ARM8 162 if ((cpus[CPU_MASTER].cpu_id & CPU_ID_CPU_MASK) == CPU_ID_ARM810) { 163 int clock = arm8_clock_config(0, 0); 164 char *fclk; 165 printf("%s: ARM810 cp15=%02x", dv->dv_xname, clock); 166 printf(" clock:%s", (clock & 1) ? " dynamic" : ""); 167 printf("%s", (clock & 2) ? " sync" : ""); 168 switch ((clock >> 2) & 3) { 169 case 0 : 170 fclk = "bus clock"; 171 break; 172 case 1 : 173 fclk = "ref clock"; 174 break; 175 case 3 : 176 fclk = "pll"; 177 break; 178 default : 179 fclk = "illegal"; 180 break; 181 } 182 printf(" fclk source=%s\n", fclk); 183 } 184 #endif 185 186 /* 187 * Ok now we test for an FPA 188 * At this point no floating point emulator has been installed. 189 * This means any FP instruction will cause undefined exception. 190 * We install a temporay coproc 1 handler which will modify 191 * undefined_test if it is called. 192 * We then try to read the FP status register. If undefined_test 193 * has been decremented then the instruction was not handled by 194 * an FPA so we know the FPA is missing. If undefined_test is 195 * still 1 then we know the instruction was handled by an FPA. 196 * We then remove our test handler and look at the 197 * FP status register for identification. 198 */ 199 200 uh = install_coproc_handler(FP_COPROC, fpa_test); 201 202 undefined_test = 0; 203 204 __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr)); 205 206 remove_coproc_handler(uh); 207 208 if (undefined_test == 0) { 209 cpus[cpu_number].fpu_type = (fpsr >> 24); 210 switch (fpsr >> 24) { 211 case 0x81 : 212 cpus[cpu_number].fpu_class = FPU_CLASS_FPA; 213 break; 214 215 default : 216 cpus[cpu_number].fpu_class = FPU_CLASS_FPU; 217 break; 218 } 219 cpus[cpu_number].fpu_flags = 0; 220 install_coproc_handler(FP_COPROC, fpa_handler); 221 } else { 222 cpus[cpu_number].fpu_class = FPU_CLASS_NONE; 223 cpus[cpu_number].fpu_flags = 0; 224 225 /* 226 * Ok if ARMFPE is defined and the boot options request the 227 * ARM FPE then it will be installed as the FPE. 228 * This is just while I work on integrating the new FPE. 229 * It means the new FPE gets installed if compiled int (ARMFPE 230 * defined) and also gives me a on/off option when I boot in 231 * case the new FPE is causing panics. 232 */ 233 234 #ifdef ARMFPE 235 if (boot_args) { 236 int usearmfpe = 1; 237 238 get_bootconf_option(boot_args, "armfpe", 239 BOOTOPT_TYPE_BOOLEAN, &usearmfpe); 240 if (usearmfpe) { 241 if (initialise_arm_fpe(&cpus[cpu_number]) != 0) 242 identify_arm_fpu(dv, cpu_number); 243 } 244 } 245 246 #endif 247 } 248 249 identify_arm_fpu(dv, cpu_number); 250 } 251 252 static const char *generic_steppings[16] = { 253 "rev 0", "rev 1", "rev 2", "rev 3", 254 "rev 4", "rev 5", "rev 6", "rev 7", 255 "rev 8", "rev 9", "rev 10", "rev 11", 256 "rev 12", "rev 13", "rev 14", "rev 15", 257 }; 258 259 static const char *sa110_steppings[16] = { 260 "rev 0", "step J", "step K", "step S", 261 "step T", "rev 5", "rev 6", "rev 7", 262 "rev 8", "rev 9", "rev 10", "rev 11", 263 "rev 12", "rev 13", "rev 14", "rev 15", 264 }; 265 266 static const char *sa1100_steppings[16] = { 267 "rev 0", "step B", "step C", "rev 3", 268 "rev 4", "rev 5", "rev 6", "rev 7", 269 "step D", "step E", "rev 10" "step G", 270 "rev 12", "rev 13", "rev 14", "rev 15", 271 }; 272 273 static const char *sa1110_steppings[16] = { 274 "step A-0", "rev 1", "rev 2", "rev 3", 275 "step B-0", "step B-1", "step B-2", "step B-3", 276 "step B-4", "step B-5", "rev 10", "rev 11", 277 "rev 12", "rev 13", "rev 14", "rev 15", 278 }; 279 280 static const char *i80200_steppings[16] = { 281 "step A-0", "step A-1", "step B-0", "step C-0", 282 "rev 4", "rev 5", "rev 6", "rev 7", 283 "rev 8", "rev 9", "rev 10", "rev 11", 284 "rev 12", "rev 13", "rev 14", "rev 15", 285 }; 286 287 struct cpuidtab { 288 u_int32_t cpuid; 289 enum cpu_class cpu_class; 290 const char *cpu_name; 291 const char **cpu_steppings; 292 }; 293 294 const struct cpuidtab cpuids[] = { 295 { CPU_ID_ARM2, CPU_CLASS_ARM2, "ARM2", 296 generic_steppings }, 297 { CPU_ID_ARM250, CPU_CLASS_ARM2AS, "ARM250", 298 generic_steppings }, 299 300 { CPU_ID_ARM3, CPU_CLASS_ARM3, "ARM3", 301 generic_steppings }, 302 303 { CPU_ID_ARM600, CPU_CLASS_ARM6, "ARM600", 304 generic_steppings }, 305 { CPU_ID_ARM610, CPU_CLASS_ARM6, "ARM610", 306 generic_steppings }, 307 { CPU_ID_ARM620, CPU_CLASS_ARM6, "ARM620", 308 generic_steppings }, 309 310 { CPU_ID_ARM700, CPU_CLASS_ARM7, "ARM700", 311 generic_steppings }, 312 { CPU_ID_ARM710, CPU_CLASS_ARM7, "ARM710", 313 generic_steppings }, 314 { CPU_ID_ARM7500, CPU_CLASS_ARM7, "ARM7500", 315 generic_steppings }, 316 { CPU_ID_ARM710A, CPU_CLASS_ARM7, "ARM710a", 317 generic_steppings }, 318 { CPU_ID_ARM7500FE, CPU_CLASS_ARM7, "ARM7500FE", 319 generic_steppings }, 320 { CPU_ID_ARM710T, CPU_CLASS_ARM7TDMI, "ARM710T", 321 generic_steppings }, 322 { CPU_ID_ARM720T, CPU_CLASS_ARM7TDMI, "ARM720T", 323 generic_steppings }, 324 { CPU_ID_ARM740T8K, CPU_CLASS_ARM7TDMI, "ARM740T (8 KB cache)", 325 generic_steppings }, 326 { CPU_ID_ARM740T4K, CPU_CLASS_ARM7TDMI, "ARM740T (4 KB cache)", 327 generic_steppings }, 328 329 { CPU_ID_ARM810, CPU_CLASS_ARM8, "ARM810", 330 generic_steppings }, 331 332 { CPU_ID_ARM920T, CPU_CLASS_ARM9TDMI, "ARM920T", 333 generic_steppings }, 334 { CPU_ID_ARM922T, CPU_CLASS_ARM9TDMI, "ARM922T", 335 generic_steppings }, 336 { CPU_ID_ARM940T, CPU_CLASS_ARM9TDMI, "ARM940T", 337 generic_steppings }, 338 { CPU_ID_ARM946ES, CPU_CLASS_ARM9ES, "ARM946E-S", 339 generic_steppings }, 340 { CPU_ID_ARM966ES, CPU_CLASS_ARM9ES, "ARM966E-S", 341 generic_steppings }, 342 { CPU_ID_ARM966ESR1, CPU_CLASS_ARM9ES, "ARM966E-S", 343 generic_steppings }, 344 345 { CPU_ID_SA110, CPU_CLASS_SA1, "SA-110", 346 sa110_steppings }, 347 { CPU_ID_SA1100, CPU_CLASS_SA1, "SA-1100", 348 sa1100_steppings }, 349 { CPU_ID_SA1110, CPU_CLASS_SA1, "SA-1110", 350 sa1110_steppings }, 351 352 { CPU_ID_I80200, CPU_CLASS_XSCALE, "i80200", 353 i80200_steppings }, 354 355 { 0, CPU_CLASS_NONE, NULL, NULL } 356 }; 357 358 struct cpu_classtab { 359 const char *class_name; 360 const char *class_option; 361 }; 362 363 const struct cpu_classtab cpu_classes[] = { 364 { "unknown", NULL }, /* CPU_CLASS_NONE */ 365 { "ARM2", "CPU_ARM2" }, /* CPU_CLASS_ARM2 */ 366 { "ARM2as", "CPU_ARM250" }, /* CPU_CLASS_ARM2AS */ 367 { "ARM3", "CPU_ARM3" }, /* CPU_CLASS_ARM3 */ 368 { "ARM6", "CPU_ARM6" }, /* CPU_CLASS_ARM6 */ 369 { "ARM7", "CPU_ARM7" }, /* CPU_CLASS_ARM7 */ 370 { "ARM7TDMI", "CPU_ARM7TDMI" }, /* CPU_CLASS_ARM7TDMI */ 371 { "ARM8", "CPU_ARM8" }, /* CPU_CLASS_ARM8 */ 372 { "ARM9TDMI", NULL }, /* CPU_CLASS_ARM9TDMI */ 373 { "ARM9E-S", NULL }, /* CPU_CLASS_ARM9ES */ 374 { "SA-1", "CPU_SA110" }, /* CPU_CLASS_SA1 */ 375 { "XScale", "CPU_XSCALE" }, /* CPU_CLASS_XSCALE */ 376 }; 377 378 /* 379 * Report the type of the specifed arm processor. This uses the generic and 380 * arm specific information in the cpu structure to identify the processor. 381 * The remaining fields in the cpu structure are filled in appropriately. 382 */ 383 384 static const char *wtnames[] = { 385 "write-through", 386 "write-back", 387 "write-back", 388 "**unknown 3**", 389 "**unknown 4**", 390 "write-back-locking", /* XXX XScale-specific? */ 391 "write-back-locking-A", 392 "write-back-locking-B", 393 "**unknown 8**", 394 "**unknown 9**", 395 "**unknown 10**", 396 "**unknown 11**", 397 "**unknown 12**", 398 "**unknown 13**", 399 "**unknown 14**", 400 "**unknown 15**", 401 }; 402 403 void 404 identify_arm_cpu(dv, cpu_number) 405 struct device *dv; 406 int cpu_number; 407 { 408 cpu_t *cpu; 409 u_int cpuid; 410 int i; 411 412 cpu = &cpus[cpu_number]; 413 cpuid = cpu->cpu_id; 414 415 if (cpuid == 0) { 416 printf("Processor failed probe - no CPU ID\n"); 417 return; 418 } 419 420 for (i = 0; cpuids[i].cpuid != 0; i++) 421 if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) { 422 cpu->cpu_class = cpuids[i].cpu_class; 423 sprintf(cpu->cpu_model, "%s %s (%s core)", 424 cpuids[i].cpu_name, 425 cpuids[i].cpu_steppings[cpuid & 426 CPU_ID_REVISION_MASK], 427 cpu_classes[cpu->cpu_class].class_name); 428 break; 429 } 430 431 if (cpuids[i].cpuid == 0) 432 sprintf(cpu->cpu_model, "unknown CPU (ID = 0x%x)", cpuid); 433 434 switch (cpu->cpu_class) { 435 case CPU_CLASS_ARM6: 436 case CPU_CLASS_ARM7: 437 case CPU_CLASS_ARM7TDMI: 438 case CPU_CLASS_ARM8: 439 if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0) 440 strcat(cpu->cpu_model, " IDC disabled"); 441 else 442 strcat(cpu->cpu_model, " IDC enabled"); 443 break; 444 case CPU_CLASS_ARM9TDMI: 445 case CPU_CLASS_SA1: 446 case CPU_CLASS_XSCALE: 447 if ((cpu->cpu_ctrl & CPU_CONTROL_DC_ENABLE) == 0) 448 strcat(cpu->cpu_model, " DC disabled"); 449 else 450 strcat(cpu->cpu_model, " DC enabled"); 451 if ((cpu->cpu_ctrl & CPU_CONTROL_IC_ENABLE) == 0) 452 strcat(cpu->cpu_model, " IC disabled"); 453 else 454 strcat(cpu->cpu_model, " IC enabled"); 455 break; 456 } 457 if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0) 458 strcat(cpu->cpu_model, " WB disabled"); 459 else 460 strcat(cpu->cpu_model, " WB enabled"); 461 462 if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE) 463 strcat(cpu->cpu_model, " LABT"); 464 else 465 strcat(cpu->cpu_model, " EABT"); 466 467 if (cpu->cpu_ctrl & CPU_CONTROL_BPRD_ENABLE) 468 strcat(cpu->cpu_model, " branch prediction enabled"); 469 470 /* Print the info */ 471 printf(": %s\n", cpu->cpu_model); 472 473 /* Print cache info. */ 474 if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0) 475 goto skip_pcache; 476 477 if (arm_pcache_unified) { 478 printf("%s: %dKB/%dB %d-way %s unified cache\n", 479 dv->dv_xname, arm_pdcache_size / 1024, 480 arm_pdcache_line_size, arm_pdcache_ways, 481 wtnames[arm_pcache_type]); 482 } else { 483 printf("%s: %dKB/%dB %d-way Instruction cache\n", 484 dv->dv_xname, arm_picache_size / 1024, 485 arm_picache_line_size, arm_picache_ways); 486 printf("%s: %dKB/%dB %d-way %s Data cache\n", 487 dv->dv_xname, arm_pdcache_size / 1024, 488 arm_pdcache_line_size, arm_pdcache_ways, 489 wtnames[arm_pcache_type]); 490 } 491 492 skip_pcache: 493 494 switch (cpu->cpu_class) { 495 #ifdef CPU_ARM2 496 case CPU_CLASS_ARM2: 497 #endif 498 #ifdef CPU_ARM250 499 case CPU_CLASS_ARM2AS: 500 #endif 501 #ifdef CPU_ARM3 502 case CPU_CLASS_ARM3: 503 #endif 504 #ifdef CPU_ARM6 505 case CPU_CLASS_ARM6: 506 #endif 507 #ifdef CPU_ARM7 508 case CPU_CLASS_ARM7: 509 #endif 510 #ifdef CPU_ARM7TDMI 511 case CPU_CLASS_ARM7TDMI: 512 #endif 513 #ifdef CPU_ARM8 514 case CPU_CLASS_ARM8: 515 #endif 516 #ifdef CPU_ARM9 517 case CPU_CLASS_ARM9TDMI: 518 #endif 519 #ifdef CPU_SA110 520 case CPU_CLASS_SA1: 521 #endif 522 #ifdef CPU_XSCALE 523 case CPU_CLASS_XSCALE: 524 #endif 525 break; 526 default: 527 if (cpu_classes[cpu->cpu_class].class_option != NULL) 528 printf("%s: %s does not fully support this CPU." 529 "\n", dv->dv_xname, ostype); 530 else { 531 printf("%s: This kernel does not fully support " 532 "this CPU.\n", dv->dv_xname); 533 printf("%s: Recompile with \"options %s\" to " 534 "correct this.\n", dv->dv_xname, 535 cpu_classes[cpu->cpu_class].class_option); 536 } 537 break; 538 } 539 540 } 541 542 543 /* 544 * Report the type of the specifed arm fpu. This uses the generic and arm 545 * specific information in the cpu structure to identify the fpu. The 546 * remaining fields in the cpu structure are filled in appropriately. 547 */ 548 549 void 550 identify_arm_fpu(dv, cpu_number) 551 struct device *dv; 552 int cpu_number; 553 { 554 cpu_t *cpu; 555 556 cpu = &cpus[cpu_number]; 557 558 /* Now for the FP info */ 559 560 switch (cpu->fpu_class) { 561 case FPU_CLASS_NONE : 562 strcpy(cpu->fpu_model, "None"); 563 break; 564 case FPU_CLASS_FPE : 565 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model); 566 printf("%s: no FP hardware found\n", dv->dv_xname); 567 break; 568 case FPU_CLASS_FPA : 569 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model); 570 if (cpu->fpu_type == FPU_TYPE_FPA11) { 571 strcpy(cpu->fpu_model, "FPA11"); 572 printf("%s: FPA11 found\n", dv->dv_xname); 573 } else { 574 strcpy(cpu->fpu_model, "FPA"); 575 printf("%s: FPA10 found\n", dv->dv_xname); 576 } 577 if ((cpu->fpu_flags & 4) == 0) 578 strcat(cpu->fpu_model, ""); 579 else 580 strcat(cpu->fpu_model, " clk/2"); 581 break; 582 case FPU_CLASS_FPU : 583 sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n", 584 cpu->fpu_type); 585 printf("%s: %s\n", dv->dv_xname, cpu->fpu_model); 586 break; 587 } 588 } 589 590 /* End of cpu.c */ 591