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