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