cpu.c revision 1.23 1 /* $NetBSD: cpu.c,v 1.23 2002/03/10 11:06:01 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.23 2002/03/10 11:06:01 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("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
133 void
134 identify_master_cpu(struct device *dv, int cpu_number)
135 {
136 u_int fpsr;
137 void *uh;
138
139 curcpu()->ci_dev = dv;
140
141 evcnt_attach_dynamic(&curcpu()->ci_arm700bugcount, EVCNT_TYPE_MISC,
142 NULL, dv->dv_xname, "arm700swibug");
143
144 /* Get the cpu ID from coprocessor 15 */
145
146 curcpu()->ci_cpuid = cpu_id();
147
148 identify_arm_cpu(dv, cpu_number, curcpu());
149
150 if ((curcpu()->ci_cpuid & CPU_ID_CPU_MASK) == CPU_ID_SA110
151 && (curcpu()->ci_cpuid & CPU_ID_REVISION_MASK) < 3) {
152 printf("%s: SA-110 with bugged STM^ instruction\n",
153 dv->dv_xname);
154 }
155
156 #ifdef CPU_ARM8
157 if ((curcpu()->ci_cpuid & CPU_ID_CPU_MASK) == CPU_ID_ARM810) {
158 int clock = arm8_clock_config(0, 0);
159 char *fclk;
160 printf("%s: ARM810 cp15=%02x", dv->dv_xname, clock);
161 printf(" clock:%s", (clock & 1) ? " dynamic" : "");
162 printf("%s", (clock & 2) ? " sync" : "");
163 switch ((clock >> 2) & 3) {
164 case 0:
165 fclk = "bus clock";
166 break;
167 case 1:
168 fclk = "ref clock";
169 break;
170 case 3:
171 fclk = "pll";
172 break;
173 default:
174 fclk = "illegal";
175 break;
176 }
177 printf(" fclk source=%s\n", fclk);
178 }
179 #endif
180
181 /*
182 * Ok now we test for an FPA
183 * At this point no floating point emulator has been installed.
184 * This means any FP instruction will cause undefined exception.
185 * We install a temporay coproc 1 handler which will modify
186 * undefined_test if it is called.
187 * We then try to read the FP status register. If undefined_test
188 * has been decremented then the instruction was not handled by
189 * an FPA so we know the FPA is missing. If undefined_test is
190 * still 1 then we know the instruction was handled by an FPA.
191 * We then remove our test handler and look at the
192 * FP status register for identification.
193 */
194
195 uh = install_coproc_handler(FP_COPROC, fpa_test);
196
197 undefined_test = 0;
198
199 __asm __volatile("rfs %0" : "=r" (fpsr));
200
201 remove_coproc_handler(uh);
202
203 if (undefined_test == 0) {
204 cpus[cpu_number].fpu_type = (fpsr >> 24);
205 switch (fpsr >> 24) {
206 case 0x81:
207 cpus[cpu_number].fpu_class = FPU_CLASS_FPA;
208 break;
209
210 default:
211 cpus[cpu_number].fpu_class = FPU_CLASS_FPU;
212 break;
213 }
214 install_coproc_handler(FP_COPROC, fpa_handler);
215 } else {
216 cpus[cpu_number].fpu_class = FPU_CLASS_NONE;
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
245 enum 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
260 static 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
267 static 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
274 static 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
281 static 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
288 static 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
295 struct cpuidtab {
296 u_int32_t cpuid;
297 enum cpu_class cpu_class;
298 const char *cpu_name;
299 const char **cpu_steppings;
300 };
301
302 const 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
366 struct cpu_classtab {
367 const char *class_name;
368 const char *class_option;
369 };
370
371 const 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
392 static 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
411 void
412 identify_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
558 void
559 identify_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 case FPU_CLASS_FPU :
585 sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n",
586 cpu->fpu_type);
587 printf("%s: %s\n", dv->dv_xname, cpu->fpu_model);
588 break;
589 }
590 }
591
592 /* End of cpu.c */
593