1/* $NetBSD: cpu.c,v 1.27 2009/03/18 10:22:21 cegger Exp $ */
2
3/*-
4 * Copyright (c) 2000, 2001 Ben Harris
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * cpu.c - high-level CPU detection etc
31 */
32
33#include <sys/param.h>
34
35__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.27 2009/03/18 10:22:21 cegger Exp $");
36
37#include <sys/device.h>
38#include <sys/proc.h>
39#include <sys/systm.h>
40#include <sys/time.h>
41#include <uvm/uvm_extern.h>
42#include <arm/armreg.h>
43#include <arm/cpuconf.h>
44#include <arm/undefined.h>
45#include <machine/machdep.h>
46#include <machine/pcb.h>
47
48#include <arch/acorn26/acorn26/cpuvar.h>
49
50static int cpu_match(device_t, cfdata_t, void *);
51static void cpu_attach(device_t, device_t, void *);
52static int cpu_search(device_t, cfdata_t, const int *, void *);
53static register_t cpu_identify(void);
54#ifdef CPU_ARM2
55static int arm2_undef_handler(u_int, u_int, struct trapframe *, int);
56static int swp_handler(u_int, u_int, struct trapframe *, int);
57#endif
58#ifdef CPU_ARM3
59static void cpu_arm3_setup(device_t, int);
60#endif
61static void cpu_delay_calibrate(device_t);
62
63CFATTACH_DECL_NEW(cpu_root, 0, cpu_match, cpu_attach, NULL, NULL);
64
65/* cf_flags bits */
66#define CFF_NOCACHE	0x00000001
67
68static int
69cpu_match(device_t parent, cfdata_t cf, void *aux)
70{
71
72	if (curcpu()->ci_dev == NULL)
73		return 1;
74	return 0;
75}
76
77static void
78cpu_attach(device_t parent, device_t self, void *aux)
79{
80	int supported;
81
82	curcpu()->ci_dev = self;
83	aprint_normal(": ");
84	curcpu()->ci_arm_cpuid = cpu_identify();
85	cputype = curcpu()->ci_arm_cputype =
86	    curcpu()->ci_arm_cpuid & CPU_ID_CPU_MASK;
87	curcpu()->ci_arm_cpurev =
88	    curcpu()->ci_arm_cpuid & CPU_ID_REVISION_MASK;
89
90	supported = 0;
91	switch (curcpu()->ci_arm_cputype) {
92	case CPU_ID_ARM2:
93		aprint_normal("ARM2");
94#ifdef CPU_ARM2
95		supported = 1;
96		install_coproc_handler(CORE_UNKNOWN_HANDLER,
97		    arm2_undef_handler);
98#endif
99		break;
100	case CPU_ID_ARM250:
101		aprint_normal("ARM250");
102#ifdef CPU_ARM250
103		supported = 1;
104#endif
105		break;
106	case CPU_ID_ARM3:
107		aprint_normal("ARM3 (rev. %u)", curcpu()->ci_arm_cpurev);
108#ifdef CPU_ARM3
109		supported = 1;
110		cpu_arm3_setup(self, device_cfdata(self)->cf_flags);
111#endif
112		break;
113	default:
114		aprint_normal("Unknown type, ID=0x%08x",
115		    curcpu()->ci_arm_cputype);
116		break;
117	}
118	aprint_normal("\n");
119	set_cpufuncs();
120	if (!supported)
121		aprint_error_dev(self,
122		    "WARNING: CPU type not supported by kernel\n");
123	config_interrupts(self, cpu_delay_calibrate);
124	config_search_ia(cpu_search, self, "cpu", NULL);
125}
126
127static int
128cpu_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
129{
130
131	if (config_match(parent, cf, NULL) > 0)
132		config_attach(parent, cf, NULL, NULL);
133
134	return 0;
135}
136
137static label_t undef_jmp;
138
139static int
140cpu_undef_handler(u_int addr, u_int insn, struct trapframe *tf, int fault_code)
141{
142
143	longjmp(&undef_jmp);
144}
145
146static register_t
147cpu_identify(void)
148{
149	register_t dummy;
150	volatile register_t id;
151	void *cp_core, *cp15;
152
153	cp_core = install_coproc_handler(CORE_UNKNOWN_HANDLER,
154	    cpu_undef_handler);
155	cp15 = install_coproc_handler(SYSTEM_COPROC, cpu_undef_handler);
156	if (setjmp(&undef_jmp) == 0) {
157		id = CPU_ID_ARM2;
158		/* ARM250 and ARM3 support SWP. */
159		__asm volatile ("swp r0, r0, [%0]" : : "r" (&dummy) : "r0");
160		id = CPU_ID_ARM250;
161		/* ARM3 has an internal coprocessor 15 with an ID register. */
162		__asm volatile ("mrc 15, 0, %0, cr0, cr0" : "=r" (id));
163	}
164	remove_coproc_handler(cp_core);
165	remove_coproc_handler(cp15);
166	return id;
167}
168
169#ifdef CPU_ARM2
170static int
171arm2_undef_handler(u_int addr, u_int insn, struct trapframe *frame,
172    int fault_code)
173{
174
175	if ((insn & 0x0fb00ff0) == 0x01000090)
176		/* It's a SWP */
177		return swp_handler(addr, insn, frame, fault_code);
178	/*
179	 * Check if the aborted instruction was a SWI (ARM2 bug --
180	 * ARM3 data sheet p87) and call SWI handler if so.
181	 */
182	if ((insn & 0x0f000000) == 0x0f000000) {
183		swi_handler(frame);
184		return 0;
185	}
186	return 1;
187}
188
189/*
190 * In order for the following macro to work, any function using it
191 * must ensure that tf->r15 is copied into getreg(15).  This is safe
192 * with the current trapframe layout on acorn26, but be careful.
193 */
194#define getreg(r) (((register_t *)&tf->tf_r0)[r])
195
196static int
197swp_handler(u_int addr, u_int insn, struct trapframe *tf, int fault_code)
198{
199	struct proc *p = curlwp->l_proc;
200	int rd, rm, rn, byte;
201	register_t temp;
202	void *uaddr;
203	int err;
204
205	KASSERT(fault_code & FAULT_USER);
206	rd = (insn & 0x0000f000) >> 12;
207	rm = (insn & 0x0000000f);
208	rn = (insn & 0x000f0000) >> 16;
209	byte = insn & 0x00400000;
210
211	if (rd == 15 || rm == 15 || rn == 15)
212		/* UNPREDICTABLE.  Arbitrarily do nothing. */
213		return 0;
214	uaddr = (void *)getreg(rn);
215	/* We want the page wired so we won't sleep */
216	/* XXX only wire one byte due to weirdness with unaligned words */
217	err = uvm_vslock(p->p_vmspace, uaddr, 1, VM_PROT_READ | VM_PROT_WRITE);
218	if (err != 0) {
219		ksiginfo_t ksi;
220		KSI_INIT_TRAP(&ksi);
221		ksi.ksi_signo = SIGSEGV;
222		ksi.ksi_addr = uaddr;
223		ksi.ksi_code = SEGV_MAPERR;
224		trapsignal(curlwp, &ksi);
225		return 0;
226	}
227	/* I believe the uvm_vslock() guarantees the fetch/store won't fail. */
228	if (byte) {
229		temp = fubyte(uaddr);
230		subyte(uaddr, getreg(rm));
231		getreg(rd) = temp;
232	} else {
233		/*
234		 * XXX Unaligned addresses happen to be handled
235		 * appropriately by [fs]uword at present.
236		 */
237		temp = fuword(uaddr);
238		suword(uaddr, getreg(rm));
239		getreg(rd) = temp;
240	}
241	uvm_vsunlock(p->p_vmspace, uaddr, 1);
242	return 0;
243}
244#endif
245
246#ifdef CPU_ARM3
247
248#define ARM3_READ(reg, var) \
249	__asm ("mrc 15, 0, %0, cr" __STRING(reg) ", cr0" : "=r" (var))
250#define ARM3_WRITE(reg, val) \
251	__asm ("mcr 15, 0, %0, cr" __STRING(reg) ", cr0" : : "r" (val))
252
253static void
254cpu_arm3_setup(device_t self, int flags)
255{
256
257	/* Disable the cache while we set things up. */
258	ARM3_WRITE(ARM3_CP15_CONTROL, ARM3_CTL_SHARED);
259	if (flags & CFF_NOCACHE) {
260		aprint_normal(", cache disabled");
261		return;
262	}
263	/* All RAM and ROM is cacheable. */
264	ARM3_WRITE(ARM3_CP15_CACHEABLE, 0xfcffffff);
265	/* All RAM is updateable. */
266	ARM3_WRITE(ARM3_CP15_UPDATEABLE, 0x00ffffff);
267	/* Nothing is disruptive.  We'll do cache flushing manually. */
268	ARM3_WRITE(ARM3_CP15_DISRUPTIVE, 0x00000000);
269	/* Flush the cache and turn it on. */
270	ARM3_WRITE(ARM3_CP15_FLUSH, 0);
271	ARM3_WRITE(ARM3_CP15_CONTROL, ARM3_CTL_CACHE_ON | ARM3_CTL_SHARED);
272	aprint_normal(", cache enabled");
273	cpu_delay_factor = 8;
274}
275#endif
276
277/* XXX This should be inlined. */
278void
279cpu_cache_flush(void)
280{
281
282#ifdef CPU_ARM3
283#if defined(CPU_ARM2) || defined(CPU_ARM250)
284	if ((cputype & CPU_ID_CPU_MASK) == CPU_ID_ARM3)
285#endif
286		ARM3_WRITE(ARM3_CP15_FLUSH, 0);
287#endif
288}
289
290int cpu_delay_factor = 1;
291
292static void
293cpu_delay_calibrate(device_t self)
294{
295	struct timeval startt, end, diff;
296
297	microtime(&startt);
298	cpu_delayloop(10000);
299	microtime(&end);
300	timersub(&end, &startt, &diff);
301	cpu_delay_factor = 10000 / diff.tv_usec + 1;
302	aprint_normal_dev(self, "10000 loops in %d microseconds, "
303	    "delay factor = %d\n",
304	    diff.tv_usec, cpu_delay_factor);
305}
306