xref: /netbsd-src/sys/arch/arm/arm32/cpu.c (revision 9fbd88883c38d0c0fbfcbe66d76fe6b0fab3f9de)
1 /*	$NetBSD: cpu.c,v 1.14 2002/01/27 14:43:47 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 __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 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(dv, cpu_number)
405 	struct device *dv;
406 	int cpu_number;
407 {
408 	cpu_t *cpu;
409 	u_int cpuid;
410 	int i;
411 
412 	cpu = &cpus[cpu_number];
413 	cpuid = cpu->cpu_id;
414 
415 	if (cpuid == 0) {
416 		printf("Processor failed probe - no CPU ID\n");
417 		return;
418 	}
419 
420 	for (i = 0; cpuids[i].cpuid != 0; i++)
421 		if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) {
422 			cpu->cpu_class = cpuids[i].cpu_class;
423 			sprintf(cpu->cpu_model, "%s %s (%s core)",
424 			    cpuids[i].cpu_name,
425 			    cpuids[i].cpu_steppings[cpuid &
426 						    CPU_ID_REVISION_MASK],
427 			    cpu_classes[cpu->cpu_class].class_name);
428 			break;
429 		}
430 
431 	if (cpuids[i].cpuid == 0)
432 		sprintf(cpu->cpu_model, "unknown CPU (ID = 0x%x)", cpuid);
433 
434 	switch (cpu->cpu_class) {
435 	case CPU_CLASS_ARM6:
436 	case CPU_CLASS_ARM7:
437 	case CPU_CLASS_ARM7TDMI:
438 	case CPU_CLASS_ARM8:
439 		if ((cpu->cpu_ctrl & CPU_CONTROL_IDC_ENABLE) == 0)
440 			strcat(cpu->cpu_model, " IDC disabled");
441 		else
442 			strcat(cpu->cpu_model, " IDC enabled");
443 		break;
444 	case CPU_CLASS_ARM9TDMI:
445 	case CPU_CLASS_SA1:
446 	case CPU_CLASS_XSCALE:
447 		if ((cpu->cpu_ctrl & CPU_CONTROL_DC_ENABLE) == 0)
448 			strcat(cpu->cpu_model, " DC disabled");
449 		else
450 			strcat(cpu->cpu_model, " DC enabled");
451 		if ((cpu->cpu_ctrl & CPU_CONTROL_IC_ENABLE) == 0)
452 			strcat(cpu->cpu_model, " IC disabled");
453 		else
454 			strcat(cpu->cpu_model, " IC enabled");
455 		break;
456 	}
457 	if ((cpu->cpu_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0)
458 		strcat(cpu->cpu_model, " WB disabled");
459 	else
460 		strcat(cpu->cpu_model, " WB enabled");
461 
462 	if (cpu->cpu_ctrl & CPU_CONTROL_LABT_ENABLE)
463 		strcat(cpu->cpu_model, " LABT");
464 	else
465 		strcat(cpu->cpu_model, " EABT");
466 
467 	if (cpu->cpu_ctrl & CPU_CONTROL_BPRD_ENABLE)
468 		strcat(cpu->cpu_model, " branch prediction enabled");
469 
470 	/* Print the info */
471 	printf(": %s\n", cpu->cpu_model);
472 
473 	/* Print cache info. */
474 	if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0)
475 		goto skip_pcache;
476 
477 	if (arm_pcache_unified) {
478 		printf("%s: %dKB/%dB %d-way %s unified cache\n",
479 		    dv->dv_xname, arm_pdcache_size / 1024,
480 		    arm_pdcache_line_size, arm_pdcache_ways,
481 		    wtnames[arm_pcache_type]);
482 	} else {
483 		printf("%s: %dKB/%dB %d-way Instruction cache\n",
484 		    dv->dv_xname, arm_picache_size / 1024,
485 		    arm_picache_line_size, arm_picache_ways);
486 		printf("%s: %dKB/%dB %d-way %s Data cache\n",
487 		    dv->dv_xname, arm_pdcache_size / 1024,
488 		    arm_pdcache_line_size, arm_pdcache_ways,
489 		    wtnames[arm_pcache_type]);
490 	}
491 
492  skip_pcache:
493 
494 	switch (cpu->cpu_class) {
495 #ifdef CPU_ARM2
496 	case CPU_CLASS_ARM2:
497 #endif
498 #ifdef CPU_ARM250
499 	case CPU_CLASS_ARM2AS:
500 #endif
501 #ifdef CPU_ARM3
502 	case CPU_CLASS_ARM3:
503 #endif
504 #ifdef CPU_ARM6
505 	case CPU_CLASS_ARM6:
506 #endif
507 #ifdef CPU_ARM7
508 	case CPU_CLASS_ARM7:
509 #endif
510 #ifdef CPU_ARM7TDMI
511 	case CPU_CLASS_ARM7TDMI:
512 #endif
513 #ifdef CPU_ARM8
514 	case CPU_CLASS_ARM8:
515 #endif
516 #ifdef CPU_ARM9
517 	case CPU_CLASS_ARM9TDMI:
518 #endif
519 #ifdef CPU_SA110
520 	case CPU_CLASS_SA1:
521 #endif
522 #ifdef CPU_XSCALE
523 	case CPU_CLASS_XSCALE:
524 #endif
525 		break;
526 	default:
527 		if (cpu_classes[cpu->cpu_class].class_option != NULL)
528 			printf("%s: %s does not fully support this CPU."
529 			       "\n", dv->dv_xname, ostype);
530 		else {
531 			printf("%s: This kernel does not fully support "
532 			       "this CPU.\n", dv->dv_xname);
533 			printf("%s: Recompile with \"options %s\" to "
534 			       "correct this.\n", dv->dv_xname,
535 			       cpu_classes[cpu->cpu_class].class_option);
536 		}
537 		break;
538 	}
539 
540 }
541 
542 
543 /*
544  * Report the type of the specifed arm fpu. This uses the generic and arm
545  * specific information in the cpu structure to identify the fpu. The
546  * remaining fields in the cpu structure are filled in appropriately.
547  */
548 
549 void
550 identify_arm_fpu(dv, cpu_number)
551 	struct device *dv;
552 	int cpu_number;
553 {
554 	cpu_t *cpu;
555 
556 	cpu = &cpus[cpu_number];
557 
558 	/* Now for the FP info */
559 
560 	switch (cpu->fpu_class) {
561 	case FPU_CLASS_NONE :
562 		strcpy(cpu->fpu_model, "None");
563 		break;
564 	case FPU_CLASS_FPE :
565 		printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
566 		printf("%s: no FP hardware found\n", dv->dv_xname);
567 		break;
568 	case FPU_CLASS_FPA :
569 		printf("%s: FPE: %s\n", dv->dv_xname, cpu->fpu_model);
570 		if (cpu->fpu_type == FPU_TYPE_FPA11) {
571 			strcpy(cpu->fpu_model, "FPA11");
572 			printf("%s: FPA11 found\n", dv->dv_xname);
573 		} else {
574 			strcpy(cpu->fpu_model, "FPA");
575 			printf("%s: FPA10 found\n", dv->dv_xname);
576 		}
577 		if ((cpu->fpu_flags & 4) == 0)
578 			strcat(cpu->fpu_model, "");
579 		else
580 			strcat(cpu->fpu_model, " clk/2");
581 		break;
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