cpu.c revision 1.24
1/*	$NetBSD: cpu.c,v 1.24 2002/03/10 11:32:00 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.24 2002/03/10 11:32:00 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
67cpu_t cpus[MAX_CPUS];
68
69char cpu_model[256];
70volatile int undefined_test;	/* Used for FPA test */
71
72/* Prototypes */
73void identify_master_cpu(struct device *dv, int cpu_number);
74void identify_arm_cpu(struct device *dv, int cpu_number, struct cpu_info *);
75void identify_arm_fpu(struct device *dv, int cpu_number);
76int fpa_test(u_int, u_int, trapframe_t *, int);
77int 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
85void
86cpu_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
99int
100fpa_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
114int
115fpa_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
133void
134identify_master_cpu(struct device *dv, int cpu_number)
135{
136	u_int fpsr;
137	void *uh;
138
139	curcpu()->ci_dev = dv;
140
141	evcnt_attach_dynamic(&curcpu()->ci_arm700bugcount, EVCNT_TYPE_MISC,
142	    NULL, dv->dv_xname, "arm700swibug");
143
144	/* Get the cpu ID from coprocessor 15 */
145
146	curcpu()->ci_cpuid = cpu_id();
147
148	identify_arm_cpu(dv, cpu_number, curcpu());
149
150	if ((curcpu()->ci_cpuid & CPU_ID_CPU_MASK) == CPU_ID_SA110
151	    && (curcpu()->ci_cpuid & CPU_ID_REVISION_MASK) < 3) {
152		printf("%s: SA-110 with bugged STM^ instruction\n",
153		       dv->dv_xname);
154	}
155
156#ifdef CPU_ARM8
157	if ((curcpu()->ci_cpuid & CPU_ID_CPU_MASK) == CPU_ID_ARM810) {
158		int clock = arm8_clock_config(0, 0);
159		char *fclk;
160		printf("%s: ARM810 cp15=%02x", dv->dv_xname, clock);
161		printf(" clock:%s", (clock & 1) ? " dynamic" : "");
162		printf("%s", (clock & 2) ? " sync" : "");
163		switch ((clock >> 2) & 3) {
164		case 0:
165			fclk = "bus clock";
166			break;
167		case 1:
168			fclk = "ref clock";
169			break;
170		case 3:
171			fclk = "pll";
172			break;
173		default:
174			fclk = "illegal";
175			break;
176		}
177		printf(" fclk source=%s\n", fclk);
178 	}
179#endif
180
181	/*
182	 * Ok now we test for an FPA
183	 * At this point no floating point emulator has been installed.
184	 * This means any FP instruction will cause undefined exception.
185	 * We install a temporay coproc 1 handler which will modify
186	 * undefined_test if it is called.
187	 * We then try to read the FP status register. If undefined_test
188	 * has been decremented then the instruction was not handled by
189	 * an FPA so we know the FPA is missing. If undefined_test is
190	 * still 1 then we know the instruction was handled by an FPA.
191	 * We then remove our test handler and look at the
192	 * FP status register for identification.
193	 */
194
195	uh = install_coproc_handler(FP_COPROC, fpa_test);
196
197	undefined_test = 0;
198
199	__asm __volatile("rfs %0" : "=r" (fpsr));
200
201	remove_coproc_handler(uh);
202
203	if (undefined_test == 0) {
204		cpus[cpu_number].fpu_type = (fpsr >> 24);
205	        switch (fpsr >> 24) {
206		case 0x81:
207			cpus[cpu_number].fpu_class = FPU_CLASS_FPA;
208			break;
209
210		default:
211			cpus[cpu_number].fpu_class = FPU_CLASS_FPU;
212			break;
213		}
214		install_coproc_handler(FP_COPROC, fpa_handler);
215	} else {
216		cpus[cpu_number].fpu_class = FPU_CLASS_NONE;
217
218		/*
219		 * Ok if ARMFPE is defined and the boot options request the
220		 * ARM FPE then it will be installed as the FPE.
221		 * This is just while I work on integrating the new FPE.
222		 * It means the new FPE gets installed if compiled int (ARMFPE
223		 * defined) and also gives me a on/off option when I boot in
224		 * case the new FPE is causing panics.
225		 */
226
227#ifdef ARMFPE
228		if (boot_args) {
229			int usearmfpe = 1;
230
231			get_bootconf_option(boot_args, "armfpe",
232			    BOOTOPT_TYPE_BOOLEAN, &usearmfpe);
233			if (usearmfpe) {
234				if (initialise_arm_fpe(&cpus[cpu_number]) != 0)
235					identify_arm_fpu(dv, cpu_number);
236			}
237		}
238
239#endif
240	}
241
242	identify_arm_fpu(dv, cpu_number);
243}
244
245enum cpu_class {
246	CPU_CLASS_NONE,
247	CPU_CLASS_ARM2,
248	CPU_CLASS_ARM2AS,
249	CPU_CLASS_ARM3,
250	CPU_CLASS_ARM6,
251	CPU_CLASS_ARM7,
252	CPU_CLASS_ARM7TDMI,
253	CPU_CLASS_ARM8,
254	CPU_CLASS_ARM9TDMI,
255	CPU_CLASS_ARM9ES,
256	CPU_CLASS_SA1,
257	CPU_CLASS_XSCALE,
258};
259
260static const char *generic_steppings[16] = {
261	"rev 0",	"rev 1",	"rev 2",	"rev 3",
262	"rev 4",	"rev 5",	"rev 6",	"rev 7",
263	"rev 8",	"rev 9",	"rev 10",	"rev 11",
264	"rev 12",	"rev 13",	"rev 14",	"rev 15",
265};
266
267static const char *sa110_steppings[16] = {
268	"rev 0",	"step J",	"step K",	"step S",
269	"step T",	"rev 5",	"rev 6",	"rev 7",
270	"rev 8",	"rev 9",	"rev 10",	"rev 11",
271	"rev 12",	"rev 13",	"rev 14",	"rev 15",
272};
273
274static const char *sa1100_steppings[16] = {
275	"rev 0",	"step B",	"step C",	"rev 3",
276	"rev 4",	"rev 5",	"rev 6",	"rev 7",
277	"step D",	"step E",	"rev 10"	"step G",
278	"rev 12",	"rev 13",	"rev 14",	"rev 15",
279};
280
281static const char *sa1110_steppings[16] = {
282	"step A-0",	"rev 1",	"rev 2",	"rev 3",
283	"step B-0",	"step B-1",	"step B-2",	"step B-3",
284	"step B-4",	"step B-5",	"rev 10",	"rev 11",
285	"rev 12",	"rev 13",	"rev 14",	"rev 15",
286};
287
288static const char *i80200_steppings[16] = {
289	"step A-0",	"step A-1",	"step B-0",	"step C-0",
290	"rev 4",	"rev 5",	"rev 6",	"rev 7",
291	"rev 8",	"rev 9",	"rev 10",	"rev 11",
292	"rev 12",	"rev 13",	"rev 14",	"rev 15",
293};
294
295struct cpuidtab {
296	u_int32_t	cpuid;
297	enum		cpu_class cpu_class;
298	const char	*cpu_name;
299	const char	**cpu_steppings;
300};
301
302const struct cpuidtab cpuids[] = {
303	{ CPU_ID_ARM2,		CPU_CLASS_ARM2,		"ARM2",
304	  generic_steppings },
305	{ CPU_ID_ARM250,	CPU_CLASS_ARM2AS,	"ARM250",
306	  generic_steppings },
307
308	{ CPU_ID_ARM3,		CPU_CLASS_ARM3,		"ARM3",
309	  generic_steppings },
310
311	{ CPU_ID_ARM600,	CPU_CLASS_ARM6,		"ARM600",
312	  generic_steppings },
313	{ CPU_ID_ARM610,	CPU_CLASS_ARM6,		"ARM610",
314	  generic_steppings },
315	{ CPU_ID_ARM620,	CPU_CLASS_ARM6,		"ARM620",
316	  generic_steppings },
317
318	{ CPU_ID_ARM700,	CPU_CLASS_ARM7,		"ARM700",
319	  generic_steppings },
320	{ CPU_ID_ARM710,	CPU_CLASS_ARM7,		"ARM710",
321	  generic_steppings },
322	{ CPU_ID_ARM7500,	CPU_CLASS_ARM7,		"ARM7500",
323	  generic_steppings },
324	{ CPU_ID_ARM710A,	CPU_CLASS_ARM7,		"ARM710a",
325	  generic_steppings },
326	{ CPU_ID_ARM7500FE,	CPU_CLASS_ARM7,		"ARM7500FE",
327	  generic_steppings },
328	{ CPU_ID_ARM710T,	CPU_CLASS_ARM7TDMI,	"ARM710T",
329	  generic_steppings },
330	{ CPU_ID_ARM720T,	CPU_CLASS_ARM7TDMI,	"ARM720T",
331	  generic_steppings },
332	{ CPU_ID_ARM740T8K,	CPU_CLASS_ARM7TDMI, "ARM740T (8 KB cache)",
333	  generic_steppings },
334	{ CPU_ID_ARM740T4K,	CPU_CLASS_ARM7TDMI, "ARM740T (4 KB cache)",
335	  generic_steppings },
336
337	{ CPU_ID_ARM810,	CPU_CLASS_ARM8,		"ARM810",
338	  generic_steppings },
339
340	{ CPU_ID_ARM920T,	CPU_CLASS_ARM9TDMI,	"ARM920T",
341	  generic_steppings },
342	{ CPU_ID_ARM922T,	CPU_CLASS_ARM9TDMI,	"ARM922T",
343	  generic_steppings },
344	{ CPU_ID_ARM940T,	CPU_CLASS_ARM9TDMI,	"ARM940T",
345	  generic_steppings },
346	{ CPU_ID_ARM946ES,	CPU_CLASS_ARM9ES,	"ARM946E-S",
347	  generic_steppings },
348	{ CPU_ID_ARM966ES,	CPU_CLASS_ARM9ES,	"ARM966E-S",
349	  generic_steppings },
350	{ CPU_ID_ARM966ESR1,	CPU_CLASS_ARM9ES,	"ARM966E-S",
351	  generic_steppings },
352
353	{ CPU_ID_SA110,		CPU_CLASS_SA1,		"SA-110",
354	  sa110_steppings },
355	{ CPU_ID_SA1100,	CPU_CLASS_SA1,		"SA-1100",
356	  sa1100_steppings },
357	{ CPU_ID_SA1110,	CPU_CLASS_SA1,		"SA-1110",
358	  sa1110_steppings },
359
360	{ CPU_ID_I80200,	CPU_CLASS_XSCALE,	"i80200",
361	  i80200_steppings },
362
363	{ 0, CPU_CLASS_NONE, NULL, NULL }
364};
365
366struct cpu_classtab {
367	const char	*class_name;
368	const char	*class_option;
369};
370
371const struct cpu_classtab cpu_classes[] = {
372	{ "unknown",	NULL },			/* CPU_CLASS_NONE */
373	{ "ARM2",	"CPU_ARM2" },		/* CPU_CLASS_ARM2 */
374	{ "ARM2as",	"CPU_ARM250" },		/* CPU_CLASS_ARM2AS */
375	{ "ARM3",	"CPU_ARM3" },		/* CPU_CLASS_ARM3 */
376	{ "ARM6",	"CPU_ARM6" },		/* CPU_CLASS_ARM6 */
377	{ "ARM7",	"CPU_ARM7" },		/* CPU_CLASS_ARM7 */
378	{ "ARM7TDMI",	"CPU_ARM7TDMI" },	/* CPU_CLASS_ARM7TDMI */
379	{ "ARM8",	"CPU_ARM8" },		/* CPU_CLASS_ARM8 */
380	{ "ARM9TDMI",	NULL },			/* CPU_CLASS_ARM9TDMI */
381	{ "ARM9E-S",	NULL },			/* CPU_CLASS_ARM9ES */
382	{ "SA-1",	"CPU_SA110" },		/* CPU_CLASS_SA1 */
383	{ "XScale",	"CPU_XSCALE" },		/* CPU_CLASS_XSCALE */
384};
385
386/*
387 * Report the type of the specifed arm processor. This uses the generic and
388 * arm specific information in the cpu structure to identify the processor.
389 * The remaining fields in the cpu structure are filled in appropriately.
390 */
391
392static const char *wtnames[] = {
393	"write-through",
394	"write-back",
395	"write-back",
396	"**unknown 3**",
397	"**unknown 4**",
398	"write-back-locking",		/* XXX XScale-specific? */
399	"write-back-locking-A",
400	"write-back-locking-B",
401	"**unknown 8**",
402	"**unknown 9**",
403	"**unknown 10**",
404	"**unknown 11**",
405	"**unknown 12**",
406	"**unknown 13**",
407	"**unknown 14**",
408	"**unknown 15**",
409};
410
411void
412identify_arm_cpu(struct device *dv, int cpu_number, struct cpu_info *ci)
413{
414	cpu_t *cpu;
415	u_int cpuid;
416	enum cpu_class cpu_class;
417	int i;
418
419	cpu = &cpus[cpu_number];
420	cpuid = ci->ci_cpuid;
421
422	if (cpuid == 0) {
423		printf("Processor failed probe - no CPU ID\n");
424		return;
425	}
426
427	for (i = 0; cpuids[i].cpuid != 0; i++)
428		if (cpuids[i].cpuid == (cpuid & CPU_ID_CPU_MASK)) {
429			cpu_class = cpuids[i].cpu_class;
430			sprintf(cpu_model, "%s %s (%s core)",
431			    cpuids[i].cpu_name,
432			    cpuids[i].cpu_steppings[cpuid &
433						    CPU_ID_REVISION_MASK],
434			    cpu_classes[cpu_class].class_name);
435			break;
436		}
437
438	if (cpuids[i].cpuid == 0)
439		sprintf(cpu_model, "unknown CPU (ID = 0x%x)", cpuid);
440
441	switch (cpu_class) {
442	case CPU_CLASS_ARM6:
443	case CPU_CLASS_ARM7:
444	case CPU_CLASS_ARM7TDMI:
445	case CPU_CLASS_ARM8:
446		if ((ci->ci_ctrl & CPU_CONTROL_IDC_ENABLE) == 0)
447			strcat(cpu_model, " IDC disabled");
448		else
449			strcat(cpu_model, " IDC enabled");
450		break;
451	case CPU_CLASS_ARM9TDMI:
452	case CPU_CLASS_SA1:
453	case CPU_CLASS_XSCALE:
454		if ((ci->ci_ctrl & CPU_CONTROL_DC_ENABLE) == 0)
455			strcat(cpu_model, " DC disabled");
456		else
457			strcat(cpu_model, " DC enabled");
458		if ((ci->ci_ctrl & CPU_CONTROL_IC_ENABLE) == 0)
459			strcat(cpu_model, " IC disabled");
460		else
461			strcat(cpu_model, " IC enabled");
462		break;
463	default:
464		break;
465	}
466	if ((ci->ci_ctrl & CPU_CONTROL_WBUF_ENABLE) == 0)
467		strcat(cpu_model, " WB disabled");
468	else
469		strcat(cpu_model, " WB enabled");
470
471	if (ci->ci_ctrl & CPU_CONTROL_LABT_ENABLE)
472		strcat(cpu_model, " LABT");
473	else
474		strcat(cpu_model, " EABT");
475
476	if (ci->ci_ctrl & CPU_CONTROL_BPRD_ENABLE)
477		strcat(cpu_model, " branch prediction enabled");
478
479	/* Print the info */
480	printf(": %s\n", cpu_model);
481
482	/* Print cache info. */
483	if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0)
484		goto skip_pcache;
485
486	if (arm_pcache_unified) {
487		printf("%s: %dKB/%dB %d-way %s unified cache\n",
488		    dv->dv_xname, arm_pdcache_size / 1024,
489		    arm_pdcache_line_size, arm_pdcache_ways,
490		    wtnames[arm_pcache_type]);
491	} else {
492		printf("%s: %dKB/%dB %d-way Instruction cache\n",
493		    dv->dv_xname, arm_picache_size / 1024,
494		    arm_picache_line_size, arm_picache_ways);
495		printf("%s: %dKB/%dB %d-way %s Data cache\n",
496		    dv->dv_xname, arm_pdcache_size / 1024,
497		    arm_pdcache_line_size, arm_pdcache_ways,
498		    wtnames[arm_pcache_type]);
499	}
500
501 skip_pcache:
502
503	switch (cpu_class) {
504#ifdef CPU_ARM2
505	case CPU_CLASS_ARM2:
506#endif
507#ifdef CPU_ARM250
508	case CPU_CLASS_ARM2AS:
509#endif
510#ifdef CPU_ARM3
511	case CPU_CLASS_ARM3:
512#endif
513#ifdef CPU_ARM6
514	case CPU_CLASS_ARM6:
515#endif
516#ifdef CPU_ARM7
517	case CPU_CLASS_ARM7:
518#endif
519#ifdef CPU_ARM7TDMI
520	case CPU_CLASS_ARM7TDMI:
521#endif
522#ifdef CPU_ARM8
523	case CPU_CLASS_ARM8:
524#endif
525#ifdef CPU_ARM9
526	case CPU_CLASS_ARM9TDMI:
527#endif
528#ifdef CPU_SA110
529	case CPU_CLASS_SA1:
530#endif
531#ifdef CPU_XSCALE
532	case CPU_CLASS_XSCALE:
533#endif
534		break;
535	default:
536		if (cpu_classes[cpu_class].class_option != NULL)
537			printf("%s: %s does not fully support this CPU."
538			       "\n", dv->dv_xname, ostype);
539		else {
540			printf("%s: This kernel does not fully support "
541			       "this CPU.\n", dv->dv_xname);
542			printf("%s: Recompile with \"options %s\" to "
543			       "correct this.\n", dv->dv_xname,
544			       cpu_classes[cpu_class].class_option);
545		}
546		break;
547	}
548
549}
550
551
552/*
553 * Report the type of the specifed arm fpu. This uses the generic and arm
554 * specific information in the cpu structure to identify the fpu. The
555 * remaining fields in the cpu structure are filled in appropriately.
556 */
557
558void
559identify_arm_fpu(struct device *dv, int cpu_number)
560{
561	cpu_t *cpu;
562
563	cpu = &cpus[cpu_number];
564
565	/* Now for the FP info */
566
567	switch (cpu->fpu_class) {
568	case FPU_CLASS_NONE :
569		break;
570	case FPU_CLASS_FPE :
571		printf("%s: no FP hardware found\n", dv->dv_xname);
572		break;
573	case FPU_CLASS_FPA :
574		if (cpu->fpu_type == FPU_TYPE_FPA11)
575			printf("%s: FPA11 found\n", dv->dv_xname);
576		else
577			printf("%s: FPA10 found\n", dv->dv_xname);
578		break;
579	case FPU_CLASS_FPU :
580		printf("%s: Unknown FPU (ID=%02x)\n",
581		    dv->dv_xname, cpu->fpu_type);
582		break;
583	}
584}
585
586/* End of cpu.c */
587