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