cpu.c revision 1.22 1 /* $NetBSD: cpu.c,v 1.22 2002/03/10 00:44:09 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.22 2002/03/10 00:44:09 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 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 install_coproc_handler(FP_COPROC, fpa_handler);
213 } else {
214 cpus[cpu_number].fpu_class = FPU_CLASS_NONE;
215
216 /*
217 * Ok if ARMFPE is defined and the boot options request the
218 * ARM FPE then it will be installed as the FPE.
219 * This is just while I work on integrating the new FPE.
220 * It means the new FPE gets installed if compiled int (ARMFPE
221 * defined) and also gives me a on/off option when I boot in
222 * case the new FPE is causing panics.
223 */
224
225 #ifdef ARMFPE
226 if (boot_args) {
227 int usearmfpe = 1;
228
229 get_bootconf_option(boot_args, "armfpe",
230 BOOTOPT_TYPE_BOOLEAN, &usearmfpe);
231 if (usearmfpe) {
232 if (initialise_arm_fpe(&cpus[cpu_number]) != 0)
233 identify_arm_fpu(dv, cpu_number);
234 }
235 }
236
237 #endif
238 }
239
240 identify_arm_fpu(dv, cpu_number);
241 }
242
243 enum cpu_class {
244 CPU_CLASS_NONE,
245 CPU_CLASS_ARM2,
246 CPU_CLASS_ARM2AS,
247 CPU_CLASS_ARM3,
248 CPU_CLASS_ARM6,
249 CPU_CLASS_ARM7,
250 CPU_CLASS_ARM7TDMI,
251 CPU_CLASS_ARM8,
252 CPU_CLASS_ARM9TDMI,
253 CPU_CLASS_ARM9ES,
254 CPU_CLASS_SA1,
255 CPU_CLASS_XSCALE,
256 };
257
258 static const char *generic_steppings[16] = {
259 "rev 0", "rev 1", "rev 2", "rev 3",
260 "rev 4", "rev 5", "rev 6", "rev 7",
261 "rev 8", "rev 9", "rev 10", "rev 11",
262 "rev 12", "rev 13", "rev 14", "rev 15",
263 };
264
265 static const char *sa110_steppings[16] = {
266 "rev 0", "step J", "step K", "step S",
267 "step T", "rev 5", "rev 6", "rev 7",
268 "rev 8", "rev 9", "rev 10", "rev 11",
269 "rev 12", "rev 13", "rev 14", "rev 15",
270 };
271
272 static const char *sa1100_steppings[16] = {
273 "rev 0", "step B", "step C", "rev 3",
274 "rev 4", "rev 5", "rev 6", "rev 7",
275 "step D", "step E", "rev 10" "step G",
276 "rev 12", "rev 13", "rev 14", "rev 15",
277 };
278
279 static const char *sa1110_steppings[16] = {
280 "step A-0", "rev 1", "rev 2", "rev 3",
281 "step B-0", "step B-1", "step B-2", "step B-3",
282 "step B-4", "step B-5", "rev 10", "rev 11",
283 "rev 12", "rev 13", "rev 14", "rev 15",
284 };
285
286 static const char *i80200_steppings[16] = {
287 "step A-0", "step A-1", "step B-0", "step C-0",
288 "rev 4", "rev 5", "rev 6", "rev 7",
289 "rev 8", "rev 9", "rev 10", "rev 11",
290 "rev 12", "rev 13", "rev 14", "rev 15",
291 };
292
293 struct cpuidtab {
294 u_int32_t cpuid;
295 enum cpu_class cpu_class;
296 const char *cpu_name;
297 const char **cpu_steppings;
298 };
299
300 const struct cpuidtab cpuids[] = {
301 { CPU_ID_ARM2, CPU_CLASS_ARM2, "ARM2",
302 generic_steppings },
303 { CPU_ID_ARM250, CPU_CLASS_ARM2AS, "ARM250",
304 generic_steppings },
305
306 { CPU_ID_ARM3, CPU_CLASS_ARM3, "ARM3",
307 generic_steppings },
308
309 { CPU_ID_ARM600, CPU_CLASS_ARM6, "ARM600",
310 generic_steppings },
311 { CPU_ID_ARM610, CPU_CLASS_ARM6, "ARM610",
312 generic_steppings },
313 { CPU_ID_ARM620, CPU_CLASS_ARM6, "ARM620",
314 generic_steppings },
315
316 { CPU_ID_ARM700, CPU_CLASS_ARM7, "ARM700",
317 generic_steppings },
318 { CPU_ID_ARM710, CPU_CLASS_ARM7, "ARM710",
319 generic_steppings },
320 { CPU_ID_ARM7500, CPU_CLASS_ARM7, "ARM7500",
321 generic_steppings },
322 { CPU_ID_ARM710A, CPU_CLASS_ARM7, "ARM710a",
323 generic_steppings },
324 { CPU_ID_ARM7500FE, CPU_CLASS_ARM7, "ARM7500FE",
325 generic_steppings },
326 { CPU_ID_ARM710T, CPU_CLASS_ARM7TDMI, "ARM710T",
327 generic_steppings },
328 { CPU_ID_ARM720T, CPU_CLASS_ARM7TDMI, "ARM720T",
329 generic_steppings },
330 { CPU_ID_ARM740T8K, CPU_CLASS_ARM7TDMI, "ARM740T (8 KB cache)",
331 generic_steppings },
332 { CPU_ID_ARM740T4K, CPU_CLASS_ARM7TDMI, "ARM740T (4 KB cache)",
333 generic_steppings },
334
335 { CPU_ID_ARM810, CPU_CLASS_ARM8, "ARM810",
336 generic_steppings },
337
338 { CPU_ID_ARM920T, CPU_CLASS_ARM9TDMI, "ARM920T",
339 generic_steppings },
340 { CPU_ID_ARM922T, CPU_CLASS_ARM9TDMI, "ARM922T",
341 generic_steppings },
342 { CPU_ID_ARM940T, CPU_CLASS_ARM9TDMI, "ARM940T",
343 generic_steppings },
344 { CPU_ID_ARM946ES, CPU_CLASS_ARM9ES, "ARM946E-S",
345 generic_steppings },
346 { CPU_ID_ARM966ES, CPU_CLASS_ARM9ES, "ARM966E-S",
347 generic_steppings },
348 { CPU_ID_ARM966ESR1, CPU_CLASS_ARM9ES, "ARM966E-S",
349 generic_steppings },
350
351 { CPU_ID_SA110, CPU_CLASS_SA1, "SA-110",
352 sa110_steppings },
353 { CPU_ID_SA1100, CPU_CLASS_SA1, "SA-1100",
354 sa1100_steppings },
355 { CPU_ID_SA1110, CPU_CLASS_SA1, "SA-1110",
356 sa1110_steppings },
357
358 { CPU_ID_I80200, CPU_CLASS_XSCALE, "i80200",
359 i80200_steppings },
360
361 { 0, CPU_CLASS_NONE, NULL, NULL }
362 };
363
364 struct cpu_classtab {
365 const char *class_name;
366 const char *class_option;
367 };
368
369 const struct cpu_classtab cpu_classes[] = {
370 { "unknown", NULL }, /* CPU_CLASS_NONE */
371 { "ARM2", "CPU_ARM2" }, /* CPU_CLASS_ARM2 */
372 { "ARM2as", "CPU_ARM250" }, /* CPU_CLASS_ARM2AS */
373 { "ARM3", "CPU_ARM3" }, /* CPU_CLASS_ARM3 */
374 { "ARM6", "CPU_ARM6" }, /* CPU_CLASS_ARM6 */
375 { "ARM7", "CPU_ARM7" }, /* CPU_CLASS_ARM7 */
376 { "ARM7TDMI", "CPU_ARM7TDMI" }, /* CPU_CLASS_ARM7TDMI */
377 { "ARM8", "CPU_ARM8" }, /* CPU_CLASS_ARM8 */
378 { "ARM9TDMI", NULL }, /* CPU_CLASS_ARM9TDMI */
379 { "ARM9E-S", NULL }, /* CPU_CLASS_ARM9ES */
380 { "SA-1", "CPU_SA110" }, /* CPU_CLASS_SA1 */
381 { "XScale", "CPU_XSCALE" }, /* CPU_CLASS_XSCALE */
382 };
383
384 /*
385 * Report the type of the specifed arm processor. This uses the generic and
386 * arm specific information in the cpu structure to identify the processor.
387 * The remaining fields in the cpu structure are filled in appropriately.
388 */
389
390 static const char *wtnames[] = {
391 "write-through",
392 "write-back",
393 "write-back",
394 "**unknown 3**",
395 "**unknown 4**",
396 "write-back-locking", /* XXX XScale-specific? */
397 "write-back-locking-A",
398 "write-back-locking-B",
399 "**unknown 8**",
400 "**unknown 9**",
401 "**unknown 10**",
402 "**unknown 11**",
403 "**unknown 12**",
404 "**unknown 13**",
405 "**unknown 14**",
406 "**unknown 15**",
407 };
408
409 void
410 identify_arm_cpu(struct device *dv, int cpu_number, struct cpu_info *ci)
411 {
412 cpu_t *cpu;
413 u_int cpuid;
414 enum cpu_class cpu_class;
415 int i;
416
417 cpu = &cpus[cpu_number];
418 cpuid = ci->ci_cpuid;
419
420 if (cpuid == 0) {
421 printf("Processor failed probe - no CPU ID\n");
422 return;
423 }
424
425 for (i = 0; cpuids[i].cpuid != 0; i++)
426 if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) {
427 cpu_class = cpuids[i].cpu_class;
428 sprintf(cpu_model, "%s %s (%s core)",
429 cpuids[i].cpu_name,
430 cpuids[i].cpu_steppings[cpuid &
431 CPU_ID_REVISION_MASK],
432 cpu_classes[cpu_class].class_name);
433 break;
434 }
435
436 if (cpuids[i].cpuid == 0)
437 sprintf(cpu_model, "unknown CPU (ID = 0x%x)", cpuid);
438
439 switch (cpu_class) {
440 case CPU_CLASS_ARM6:
441 case CPU_CLASS_ARM7:
442 case CPU_CLASS_ARM7TDMI:
443 case CPU_CLASS_ARM8:
444 if ((ci->ci_ctrl & CPU_CONTROL_IDC_ENABLE) == 0)
445 strcat(cpu_model, " IDC disabled");
446 else
447 strcat(cpu_model, " IDC enabled");
448 break;
449 case CPU_CLASS_ARM9TDMI:
450 case CPU_CLASS_SA1:
451 case CPU_CLASS_XSCALE:
452 if ((ci->ci_ctrl & CPU_CONTROL_DC_ENABLE) == 0)
453 strcat(cpu_model, " DC disabled");
454 else
455 strcat(cpu_model, " DC enabled");
456 if ((ci->ci_ctrl & CPU_CONTROL_IC_ENABLE) == 0)
457 strcat(cpu_model, " IC disabled");
458 else
459 strcat(cpu_model, " IC enabled");
460 break;
461 default:
462 break;
463 }
464 if ((ci->ci_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0)
465 strcat(cpu_model, " WB disabled");
466 else
467 strcat(cpu_model, " WB enabled");
468
469 if (ci->ci_ctrl & CPU_CONTROL_LABT_ENABLE)
470 strcat(cpu_model, " LABT");
471 else
472 strcat(cpu_model, " EABT");
473
474 if (ci->ci_ctrl & CPU_CONTROL_BPRD_ENABLE)
475 strcat(cpu_model, " branch prediction enabled");
476
477 /* Print the info */
478 printf(": %s\n", cpu_model);
479
480 /* Print cache info. */
481 if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0)
482 goto skip_pcache;
483
484 if (arm_pcache_unified) {
485 printf("%s: %dKB/%dB %d-way %s unified cache\n",
486 dv->dv_xname, arm_pdcache_size / 1024,
487 arm_pdcache_line_size, arm_pdcache_ways,
488 wtnames[arm_pcache_type]);
489 } else {
490 printf("%s: %dKB/%dB %d-way Instruction cache\n",
491 dv->dv_xname, arm_picache_size / 1024,
492 arm_picache_line_size, arm_picache_ways);
493 printf("%s: %dKB/%dB %d-way %s Data cache\n",
494 dv->dv_xname, arm_pdcache_size / 1024,
495 arm_pdcache_line_size, arm_pdcache_ways,
496 wtnames[arm_pcache_type]);
497 }
498
499 skip_pcache:
500
501 switch (cpu_class) {
502 #ifdef CPU_ARM2
503 case CPU_CLASS_ARM2:
504 #endif
505 #ifdef CPU_ARM250
506 case CPU_CLASS_ARM2AS:
507 #endif
508 #ifdef CPU_ARM3
509 case CPU_CLASS_ARM3:
510 #endif
511 #ifdef CPU_ARM6
512 case CPU_CLASS_ARM6:
513 #endif
514 #ifdef CPU_ARM7
515 case CPU_CLASS_ARM7:
516 #endif
517 #ifdef CPU_ARM7TDMI
518 case CPU_CLASS_ARM7TDMI:
519 #endif
520 #ifdef CPU_ARM8
521 case CPU_CLASS_ARM8:
522 #endif
523 #ifdef CPU_ARM9
524 case CPU_CLASS_ARM9TDMI:
525 #endif
526 #ifdef CPU_SA110
527 case CPU_CLASS_SA1:
528 #endif
529 #ifdef CPU_XSCALE
530 case CPU_CLASS_XSCALE:
531 #endif
532 break;
533 default:
534 if (cpu_classes[cpu_class].class_option != NULL)
535 printf("%s: %s does not fully support this CPU."
536 "\n", dv->dv_xname, ostype);
537 else {
538 printf("%s: This kernel does not fully support "
539 "this CPU.\n", dv->dv_xname);
540 printf("%s: Recompile with \"options %s\" to "
541 "correct this.\n", dv->dv_xname,
542 cpu_classes[cpu_class].class_option);
543 }
544 break;
545 }
546
547 }
548
549
550 /*
551 * Report the type of the specifed arm fpu. This uses the generic and arm
552 * specific information in the cpu structure to identify the fpu. The
553 * remaining fields in the cpu structure are filled in appropriately.
554 */
555
556 void
557 identify_arm_fpu(struct device *dv, int cpu_number)
558 {
559 cpu_t *cpu;
560
561 cpu = &cpus[cpu_number];
562
563 /* Now for the FP info */
564
565 switch (cpu->fpu_class) {
566 case FPU_CLASS_NONE :
567 strcpy(cpu->fpu_model, "None");
568 break;
569 case FPU_CLASS_FPE :
570 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
571 printf("%s: no FP hardware found\n", dv->dv_xname);
572 break;
573 case FPU_CLASS_FPA :
574 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
575 if (cpu->fpu_type == FPU_TYPE_FPA11) {
576 strcpy(cpu->fpu_model, "FPA11");
577 printf("%s: FPA11 found\n", dv->dv_xname);
578 } else {
579 strcpy(cpu->fpu_model, "FPA");
580 printf("%s: FPA10 found\n", dv->dv_xname);
581 }
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