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