cpu.c revision 1.17 1 /* $NetBSD: cpu.c,v 1.17 2002/03/09 19:11:21 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(struct device *dv, int cpu_number);
72 void identify_arm_cpu(struct device *dv, int cpu_number);
73 void identify_arm_fpu(struct device *dv, int cpu_number);
74 int fpa_test(u_int, u_int, trapframe_t *, int);
75 int fpa_handler(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(struct device *dv)
85 {
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(u_int address, u_int instruction, trapframe_t *frame, int fault_code)
99 {
100
101 frame->tf_pc += INSN_SIZE;
102 ++undefined_test;
103 return(0);
104 }
105
106 /*
107 * If an FPA was found then this function is installed as the coproc1 handler
108 * on the undefined instruction vector. Currently we don't support FPA's
109 * so this just triggers an exception.
110 */
111
112 int
113 fpa_handler(u_int address, u_int instruction, trapframe_t *frame,
114 int fault_code)
115 {
116 u_int fpsr;
117
118 __asm __volatile("stmfd sp!, {r0};"
119 "rfs r0;"
120 "mov %0, r0;"
121 "ldmfd sp!, {r0}" : "=r" (fpsr));
122
123 printf("FPA exception: fpsr = %08x\n", fpsr);
124
125 return(1);
126 }
127
128
129 /*
130 * Identify the master (boot) CPU
131 * This also probes for an FPU and will install an FPE if necessary
132 */
133
134 void
135 identify_master_cpu(struct device *dv, int cpu_number)
136 {
137 u_int fpsr;
138 void *uh;
139
140 evcnt_attach_dynamic(&curcpu()->ci_arm700bugcount, EVCNT_TYPE_MISC,
141 NULL, dv->dv_xname, "arm700swibug");
142
143 cpus[cpu_number].cpu_ctrl = cpuctrl;
144
145 /* Get the cpu ID from coprocessor 15 */
146
147 cpus[cpu_number].cpu_id = cpu_id();
148
149 identify_arm_cpu(dv, cpu_number);
150 strcpy(cpu_model, cpus[cpu_number].cpu_model);
151
152 if (cpus[CPU_MASTER].cpu_class == CPU_CLASS_SA1
153 && (cpus[CPU_MASTER].cpu_id & CPU_ID_REVISION_MASK) < 3) {
154 printf("%s: SA-110 with bugged STM^ instruction\n",
155 dv->dv_xname);
156 }
157
158 #ifdef CPU_ARM8
159 if ((cpus[CPU_MASTER].cpu_id & CPU_ID_CPU_MASK) == CPU_ID_ARM810) {
160 int clock = arm8_clock_config(0, 0);
161 char *fclk;
162 printf("%s: ARM810 cp15=%02x", dv->dv_xname, clock);
163 printf(" clock:%s", (clock & 1) ? " dynamic" : "");
164 printf("%s", (clock & 2) ? " sync" : "");
165 switch ((clock >> 2) & 3) {
166 case 0:
167 fclk = "bus clock";
168 break;
169 case 1:
170 fclk = "ref clock";
171 break;
172 case 3:
173 fclk = "pll";
174 break;
175 default:
176 fclk = "illegal";
177 break;
178 }
179 printf(" fclk source=%s\n", fclk);
180 }
181 #endif
182
183 /*
184 * Ok now we test for an FPA
185 * At this point no floating point emulator has been installed.
186 * This means any FP instruction will cause undefined exception.
187 * We install a temporay coproc 1 handler which will modify
188 * undefined_test if it is called.
189 * We then try to read the FP status register. If undefined_test
190 * has been decremented then the instruction was not handled by
191 * an FPA so we know the FPA is missing. If undefined_test is
192 * still 1 then we know the instruction was handled by an FPA.
193 * We then remove our test handler and look at the
194 * FP status register for identification.
195 */
196
197 uh = install_coproc_handler(FP_COPROC, fpa_test);
198
199 undefined_test = 0;
200
201 __asm __volatile("stmfd sp!, {r0};"
202 "rfs r0;"
203 "mov %0, r0;"
204 "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(struct device *dv, int cpu_number)
405 {
406 cpu_t *cpu;
407 u_int cpuid;
408 int i;
409
410 cpu = &cpus[cpu_number];
411 cpuid = cpu->cpu_id;
412
413 if (cpuid == 0) {
414 printf("Processor failed probe - no CPU ID\n");
415 return;
416 }
417
418 for (i = 0; cpuids[i].cpuid != 0; i++)
419 if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) {
420 cpu->cpu_class = cpuids[i].cpu_class;
421 sprintf(cpu->cpu_model, "%s %s (%s core)",
422 cpuids[i].cpu_name,
423 cpuids[i].cpu_steppings[cpuid &
424 CPU_ID_REVISION_MASK],
425 cpu_classes[cpu->cpu_class].class_name);
426 break;
427 }
428
429 if (cpuids[i].cpuid == 0)
430 sprintf(cpu->cpu_model, "unknown CPU (ID = 0x%x)", cpuid);
431
432 switch (cpu->cpu_class) {
433 case CPU_CLASS_ARM6:
434 case CPU_CLASS_ARM7:
435 case CPU_CLASS_ARM7TDMI:
436 case CPU_CLASS_ARM8:
437 if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0)
438 strcat(cpu->cpu_model, " IDC disabled");
439 else
440 strcat(cpu->cpu_model, " IDC enabled");
441 break;
442 case CPU_CLASS_ARM9TDMI:
443 case CPU_CLASS_SA1:
444 case CPU_CLASS_XSCALE:
445 if ((cpu->cpu_ctrl & CPU_CONTROL_DC_ENABLE) == 0)
446 strcat(cpu->cpu_model, " DC disabled");
447 else
448 strcat(cpu->cpu_model, " DC enabled");
449 if ((cpu->cpu_ctrl & CPU_CONTROL_IC_ENABLE) == 0)
450 strcat(cpu->cpu_model, " IC disabled");
451 else
452 strcat(cpu->cpu_model, " IC enabled");
453 break;
454 }
455 if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0)
456 strcat(cpu->cpu_model, " WB disabled");
457 else
458 strcat(cpu->cpu_model, " WB enabled");
459
460 if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE)
461 strcat(cpu->cpu_model, " LABT");
462 else
463 strcat(cpu->cpu_model, " EABT");
464
465 if (cpu->cpu_ctrl & CPU_CONTROL_BPRD_ENABLE)
466 strcat(cpu->cpu_model, " branch prediction enabled");
467
468 /* Print the info */
469 printf(": %s\n", cpu->cpu_model);
470
471 /* Print cache info. */
472 if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0)
473 goto skip_pcache;
474
475 if (arm_pcache_unified) {
476 printf("%s: %dKB/%dB %d-way %s unified cache\n",
477 dv->dv_xname, arm_pdcache_size / 1024,
478 arm_pdcache_line_size, arm_pdcache_ways,
479 wtnames[arm_pcache_type]);
480 } else {
481 printf("%s: %dKB/%dB %d-way Instruction cache\n",
482 dv->dv_xname, arm_picache_size / 1024,
483 arm_picache_line_size, arm_picache_ways);
484 printf("%s: %dKB/%dB %d-way %s Data cache\n",
485 dv->dv_xname, arm_pdcache_size / 1024,
486 arm_pdcache_line_size, arm_pdcache_ways,
487 wtnames[arm_pcache_type]);
488 }
489
490 skip_pcache:
491
492 switch (cpu->cpu_class) {
493 #ifdef CPU_ARM2
494 case CPU_CLASS_ARM2:
495 #endif
496 #ifdef CPU_ARM250
497 case CPU_CLASS_ARM2AS:
498 #endif
499 #ifdef CPU_ARM3
500 case CPU_CLASS_ARM3:
501 #endif
502 #ifdef CPU_ARM6
503 case CPU_CLASS_ARM6:
504 #endif
505 #ifdef CPU_ARM7
506 case CPU_CLASS_ARM7:
507 #endif
508 #ifdef CPU_ARM7TDMI
509 case CPU_CLASS_ARM7TDMI:
510 #endif
511 #ifdef CPU_ARM8
512 case CPU_CLASS_ARM8:
513 #endif
514 #ifdef CPU_ARM9
515 case CPU_CLASS_ARM9TDMI:
516 #endif
517 #ifdef CPU_SA110
518 case CPU_CLASS_SA1:
519 #endif
520 #ifdef CPU_XSCALE
521 case CPU_CLASS_XSCALE:
522 #endif
523 break;
524 default:
525 if (cpu_classes[cpu->cpu_class].class_option != NULL)
526 printf("%s: %s does not fully support this CPU."
527 "\n", dv->dv_xname, ostype);
528 else {
529 printf("%s: This kernel does not fully support "
530 "this CPU.\n", dv->dv_xname);
531 printf("%s: Recompile with \"options %s\" to "
532 "correct this.\n", dv->dv_xname,
533 cpu_classes[cpu->cpu_class].class_option);
534 }
535 break;
536 }
537
538 }
539
540
541 /*
542 * Report the type of the specifed arm fpu. This uses the generic and arm
543 * specific information in the cpu structure to identify the fpu. The
544 * remaining fields in the cpu structure are filled in appropriately.
545 */
546
547 void
548 identify_arm_fpu(struct device *dv, int cpu_number)
549 {
550 cpu_t *cpu;
551
552 cpu = &cpus[cpu_number];
553
554 /* Now for the FP info */
555
556 switch (cpu->fpu_class) {
557 case FPU_CLASS_NONE :
558 strcpy(cpu->fpu_model, "None");
559 break;
560 case FPU_CLASS_FPE :
561 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
562 printf("%s: no FP hardware found\n", dv->dv_xname);
563 break;
564 case FPU_CLASS_FPA :
565 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
566 if (cpu->fpu_type == FPU_TYPE_FPA11) {
567 strcpy(cpu->fpu_model, "FPA11");
568 printf("%s: FPA11 found\n", dv->dv_xname);
569 } else {
570 strcpy(cpu->fpu_model, "FPA");
571 printf("%s: FPA10 found\n", dv->dv_xname);
572 }
573 if ((cpu->fpu_flags & 4) == 0)
574 strcat(cpu->fpu_model, "");
575 else
576 strcat(cpu->fpu_model, " clk/2");
577 break;
578 case FPU_CLASS_FPU :
579 sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n",
580 cpu->fpu_type);
581 printf("%s: %s\n", dv->dv_xname, cpu->fpu_model);
582 break;
583 }
584 }
585
586 /* End of cpu.c */
587