cpu.c revision 1.1 1 /* $NetBSD: cpu.c,v 1.1 2001/04/20 18:08:48 matt 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 <machine/cpus.h>
56 #include <machine/undefined.h>
57
58 #ifdef ARMFPE
59 #include <machine/bootconfig.h> /* For boot args */
60 #include <arm32/fpe-arm/armfpe.h>
61 #endif /* ARMFPE */
62
63 cpu_t cpus[MAX_CPUS];
64
65 char cpu_model[64];
66 volatile int undefined_test; /* Used for FPA test */
67 extern int cpuctrl; /* cpu control register value */
68
69 /* Prototypes */
70 void identify_master_cpu __P((struct device *dv, int cpu_number));
71 void identify_arm_cpu __P((struct device *dv, int cpu_number));
72 void identify_arm_fpu __P((struct device *dv, int cpu_number));
73
74
75 /*
76 * void cpusattach(struct device *parent, struct device *dev, void *aux)
77 *
78 * Attach the main cpu
79 */
80
81 void
82 cpu_attach(dv)
83 struct device *dv;
84 {
85 identify_master_cpu(dv, CPU_MASTER);
86 }
87
88 /*
89 * Used to test for an FPA. The following function is installed as a coproc1
90 * handler on the undefined instruction vector and then we issue a FPA
91 * instruction. If undefined_test is non zero then the FPA did not handle
92 * the instruction so must be absent.
93 */
94
95 int
96 fpa_test(address, instruction, frame)
97 u_int address;
98 u_int instruction;
99 trapframe_t *frame;
100 {
101
102 frame->tf_pc += INSN_SIZE;
103 ++undefined_test;
104 return(0);
105 }
106
107 /*
108 * If an FPA was found then this function is installed as the coproc1 handler
109 * on the undefined instruction vector. Currently we don't support FPA's
110 * so this just triggers an exception.
111 */
112
113 int
114 fpa_handler(address, instruction, frame, fault_code)
115 u_int address;
116 u_int instruction;
117 trapframe_t *frame;
118 int fault_code;
119 {
120 u_int fpsr;
121
122 __asm __volatile("stmfd sp!, {r0}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr));
123
124 printf("FPA exception: fpsr = %08x\n", fpsr);
125
126 return(1);
127 }
128
129
130 /*
131 * Identify the master (boot) CPU
132 * This also probes for an FPU and will install an FPE if necessary
133 */
134
135 void
136 identify_master_cpu(dv, cpu_number)
137 struct device *dv;
138 int cpu_number;
139 {
140 u_int fpsr;
141 void *uh;
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}; .word 0xee300110; mov %0, r0; ldmfd sp!, {r0}" : "=r" (fpsr));
202
203 remove_coproc_handler(uh);
204
205 if (undefined_test == 0) {
206 cpus[cpu_number].fpu_type = (fpsr >> 24);
207 switch (fpsr >> 24) {
208 case 0x81 :
209 cpus[cpu_number].fpu_class = FPU_CLASS_FPA;
210 break;
211
212 default :
213 cpus[cpu_number].fpu_class = FPU_CLASS_FPU;
214 break;
215 }
216 cpus[cpu_number].fpu_flags = 0;
217 install_coproc_handler(FP_COPROC, fpa_handler);
218 } else {
219 cpus[cpu_number].fpu_class = FPU_CLASS_NONE;
220 cpus[cpu_number].fpu_flags = 0;
221
222 /*
223 * Ok if ARMFPE is defined and the boot options request the
224 * ARM FPE then it will be installed as the FPE.
225 * This is just while I work on integrating the new FPE.
226 * It means the new FPE gets installed if compiled int (ARMFPE
227 * defined) and also gives me a on/off option when I boot in
228 * case the new FPE is causing panics.
229 */
230
231 #ifdef ARMFPE
232 if (boot_args) {
233 char *ptr;
234
235 ptr = strstr(boot_args, "noarmfpe");
236 if (!ptr) {
237 if (initialise_arm_fpe(&cpus[cpu_number]) != 0)
238 identify_arm_fpu(dv, cpu_number);
239 }
240 }
241
242 #endif
243 }
244
245 identify_arm_fpu(dv, cpu_number);
246 }
247
248 struct cpuidtab {
249 u_int32_t cpuid;
250 enum cpu_class cpu_class;
251 char * cpu_name;
252 };
253
254 const struct cpuidtab cpuids[] = {
255 { CPU_ID_ARM2, CPU_CLASS_ARM2, "ARM2" },
256 { CPU_ID_ARM250, CPU_CLASS_ARM2AS, "ARM250" },
257 { CPU_ID_ARM3, CPU_CLASS_ARM3, "ARM3" },
258 { CPU_ID_ARM600, CPU_CLASS_ARM6, "ARM600" },
259 { CPU_ID_ARM610, CPU_CLASS_ARM6, "ARM610" },
260 { CPU_ID_ARM620, CPU_CLASS_ARM6, "ARM620" },
261 { CPU_ID_ARM700, CPU_CLASS_ARM7, "ARM700" },
262 { CPU_ID_ARM710, CPU_CLASS_ARM7, "ARM710" },
263 { CPU_ID_ARM7500, CPU_CLASS_ARM7, "ARM7500" },
264 { CPU_ID_ARM710A, CPU_CLASS_ARM7, "ARM710a" },
265 { CPU_ID_ARM7500FE, CPU_CLASS_ARM7, "ARM7500FE" },
266 { CPU_ID_ARM710T, CPU_CLASS_ARM7TDMI, "ARM710T" },
267 { CPU_ID_ARM720T, CPU_CLASS_ARM7TDMI, "ARM720T" },
268 { CPU_ID_ARM740T8K, CPU_CLASS_ARM7TDMI, "ARM740T (8 KB cache)" },
269 { CPU_ID_ARM740T4K, CPU_CLASS_ARM7TDMI, "ARM740T (4 KB cache)" },
270 { CPU_ID_ARM810, CPU_CLASS_ARM8, "ARM810" },
271 { CPU_ID_ARM920T, CPU_CLASS_ARM9TDMI, "ARM920T" },
272 { CPU_ID_ARM922T, CPU_CLASS_ARM9TDMI, "ARM922T" },
273 { CPU_ID_ARM940T, CPU_CLASS_ARM9TDMI, "ARM940T" },
274 { CPU_ID_ARM946ES, CPU_CLASS_ARM9ES, "ARM946E-S" },
275 { CPU_ID_ARM966ES, CPU_CLASS_ARM9ES, "ARM966E-S" },
276 { CPU_ID_ARM966ESR1, CPU_CLASS_ARM9ES, "ARM966E-S (Rev 1)" },
277 { CPU_ID_SA110, CPU_CLASS_SA1, "SA-110" },
278 { CPU_ID_SA1100, CPU_CLASS_SA1, "SA-1100" },
279 { CPU_ID_SA1110, CPU_CLASS_SA1, "SA-1110" },
280 { CPU_ID_I80200, CPU_CLASS_XSCALE, "80200" },
281 { 0, CPU_CLASS_NONE, NULL }
282 };
283
284 struct cpu_classtab {
285 char *class_name;
286 char *class_option;
287 };
288
289 const struct cpu_classtab cpu_classes[] = {
290 { "unknown", NULL }, /* CPU_CLASS_NONE */
291 { "ARM2", "CPU_ARM2" }, /* CPU_CLASS_ARM2 */
292 { "ARM2as", "CPU_ARM250" }, /* CPU_CLASS_ARM2AS */
293 { "ARM3", "CPU_ARM3" }, /* CPU_CLASS_ARM3 */
294 { "ARM6", "CPU_ARM6" }, /* CPU_CLASS_ARM6 */
295 { "ARM7", "CPU_ARM7" }, /* CPU_CLASS_ARM7 */
296 { "ARM7TDMI", NULL }, /* CPU_CLASS_ARM7TDMI */
297 { "ARM8", "CPU_ARM8" }, /* CPU_CLASS_ARM8 */
298 { "ARM9TDMI", NULL }, /* CPU_CLASS_ARM9TDMI */
299 { "ARM9E-S", NULL }, /* CPU_CLASS_ARM9ES */
300 { "SA-1", "CPU_SA110" }, /* CPU_CLASS_SA1 */
301 { "Xscale", NULL }, /* CPU_CLASS_XSCALE */
302 };
303
304 /*
305 * Report the type of the specifed arm processor. This uses the generic and
306 * arm specific information in the cpu structure to identify the processor.
307 * The remaining fields in the cpu structure are filled in appropriately.
308 */
309
310 void
311 identify_arm_cpu(dv, cpu_number)
312 struct device *dv;
313 int cpu_number;
314 {
315 cpu_t *cpu;
316 u_int cpuid;
317 int i;
318
319 cpu = &cpus[cpu_number];
320 cpuid = cpu->cpu_id;
321
322 if (cpuid == 0) {
323 printf("Processor failed probe - no CPU ID\n");
324 return;
325 }
326
327 for (i = 0; cpuids[i].cpuid != 0; i++)
328 if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) {
329 cpu->cpu_class = cpuids[i].cpu_class;
330 sprintf(cpu->cpu_model, "%s rev %d (%s core)",
331 cpuids[i].cpu_name, cpuid & CPU_ID_REVISION_MASK,
332 cpu_classes[cpu->cpu_class].class_name);
333 break;
334 }
335
336 if (cpuids[i].cpuid == 0)
337 sprintf(cpu->cpu_model, "unknown CPU (ID = 0x%x)", cpuid);
338
339 switch (cpu->cpu_class) {
340 case CPU_CLASS_ARM6:
341 case CPU_CLASS_ARM7:
342 case CPU_CLASS_ARM8:
343 if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0)
344 strcat(cpu->cpu_model, " IDC disabled");
345 else
346 strcat(cpu->cpu_model, " IDC enabled");
347 break;
348 case CPU_CLASS_SA1:
349 if ((cpu->cpu_ctrl & CPU_CONTROL_DC_ENABLE) == 0)
350 strcat(cpu->cpu_model, " DC disabled");
351 else
352 strcat(cpu->cpu_model, " DC enabled");
353 if ((cpu->cpu_ctrl & CPU_CONTROL_IC_ENABLE) == 0)
354 strcat(cpu->cpu_model, " IC disabled");
355 else
356 strcat(cpu->cpu_model, " IC enabled");
357 break;
358 }
359 if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0)
360 strcat(cpu->cpu_model, " WB disabled");
361 else
362 strcat(cpu->cpu_model, " WB enabled");
363
364 if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE)
365 strcat(cpu->cpu_model, " LABT");
366 else
367 strcat(cpu->cpu_model, " EABT");
368
369 if (cpu->cpu_ctrl & CPU_CONTROL_BPRD_ENABLE)
370 strcat(cpu->cpu_model, " branch prediction enabled");
371
372 /* Print the info */
373
374 printf(": %s\n", cpu->cpu_model);
375
376 switch (cpu->cpu_class) {
377 #ifdef CPU_ARM2
378 case CPU_CLASS_ARM2:
379 #endif
380 #ifdef CPU_ARM250
381 case CPU_CLASS_ARM2AS:
382 #endif
383 #ifdef CPU_ARM3
384 case CPU_CLASS_ARM3:
385 #endif
386 #ifdef CPU_ARM6
387 case CPU_CLASS_ARM6:
388 #endif
389 #ifdef CPU_ARM7
390 case CPU_CLASS_ARM7:
391 #endif
392 #ifdef CPU_ARM8
393 case CPU_CLASS_ARM8:
394 #endif
395 #ifdef CPU_SA110
396 case CPU_CLASS_SA1:
397 #endif
398 break;
399 default:
400 if (cpu_classes[cpu->cpu_class].class_option != NULL)
401 printf("%s: %s does not fully support this CPU."
402 "\n", dv->dv_xname, ostype);
403 else {
404 printf("%s: This kernel does not fully support "
405 "this CPU.\n", dv->dv_xname);
406 printf("%s: Recompile with \"options %s\" to "
407 "correct this.\n", dv->dv_xname,
408 cpu_classes[cpu->cpu_class].class_option);
409 }
410 break;
411 }
412
413 }
414
415
416 /*
417 * Report the type of the specifed arm fpu. This uses the generic and arm
418 * specific information in the cpu structure to identify the fpu. The
419 * remaining fields in the cpu structure are filled in appropriately.
420 */
421
422 void
423 identify_arm_fpu(dv, cpu_number)
424 struct device *dv;
425 int cpu_number;
426 {
427 cpu_t *cpu;
428
429 cpu = &cpus[cpu_number];
430
431 /* Now for the FP info */
432
433 switch (cpu->fpu_class) {
434 case FPU_CLASS_NONE :
435 strcpy(cpu->fpu_model, "None");
436 break;
437 case FPU_CLASS_FPE :
438 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
439 printf("%s: no FP hardware found\n", dv->dv_xname);
440 break;
441 case FPU_CLASS_FPA :
442 printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
443 if (cpu->fpu_type == FPU_TYPE_FPA11) {
444 strcpy(cpu->fpu_model, "FPA11");
445 printf("%s: FPA11 found\n", dv->dv_xname);
446 } else {
447 strcpy(cpu->fpu_model, "FPA");
448 printf("%s: FPA10 found\n", dv->dv_xname);
449 }
450 if ((cpu->fpu_flags & 4) == 0)
451 strcat(cpu->fpu_model, "");
452 else
453 strcat(cpu->fpu_model, " clk/2");
454 break;
455 case FPU_CLASS_FPU :
456 sprintf(cpu->fpu_model, "Unknown FPU (ID=%02x)\n",
457 cpu->fpu_type);
458 printf("%s: %s\n", dv->dv_xname, cpu->fpu_model);
459 break;
460 }
461 }
462
463 /* End of cpu.c */
464