xref: /netbsd-src/sys/arch/sparc64/sparc64/cpu.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: cpu.c,v 1.64 2007/10/17 19:57:30 garbled Exp $ */
2 
3 /*
4  * Copyright (c) 1996
5  *	The President and Fellows of Harvard College. All rights reserved.
6  * Copyright (c) 1992, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * This software was developed by the Computer Systems Engineering group
10  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11  * contributed to Berkeley.
12  *
13  * All advertising materials mentioning features or use of this software
14  * must display the following acknowledgement:
15  *	This product includes software developed by Harvard University.
16  *	This product includes software developed by the University of
17  *	California, Lawrence Berkeley Laboratory.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  *
23  * 1. Redistributions of source code must retain the above copyright
24  *    notice, this list of conditions and the following disclaimer.
25  * 2. Redistributions in binary form must reproduce the above copyright
26  *    notice, this list of conditions and the following disclaimer in the
27  *    documentation and/or other materials provided with the distribution.
28  * 3. All advertising materials mentioning features or use of this software
29  *    must display the following acknowledgement:
30  *	This product includes software developed by Aaron Brown and
31  *	Harvard University.
32  *	This product includes software developed by the University of
33  *	California, Berkeley and its contributors.
34  * 4. Neither the name of the University nor the names of its contributors
35  *    may be used to endorse or promote products derived from this software
36  *    without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  *
50  *	@(#)cpu.c	8.5 (Berkeley) 11/23/93
51  *
52  */
53 
54 #include <sys/cdefs.h>
55 __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.64 2007/10/17 19:57:30 garbled Exp $");
56 
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/device.h>
60 #include <sys/kernel.h>
61 
62 #include <uvm/uvm_extern.h>
63 
64 #include <machine/autoconf.h>
65 #include <machine/cpu.h>
66 #include <machine/reg.h>
67 #include <machine/trap.h>
68 #include <machine/pmap.h>
69 #include <machine/sparc64.h>
70 #include <machine/openfirm.h>
71 
72 #include <sparc64/sparc64/cache.h>
73 
74 int ecache_min_line_size;
75 
76 /* Linked list of all CPUs in system. */
77 int sparc_ncpus = 0;
78 struct cpu_info *cpus = NULL;
79 
80 volatile cpuset_t cpus_active;/* set of active cpus */
81 struct cpu_bootargs *cpu_args;	/* allocated very early in pmap_bootstrap. */
82 
83 static struct cpu_info *alloc_cpuinfo(u_int);
84 
85 /* The following are used externally (sysctl_hw). */
86 char	machine[] = MACHINE;		/* from <machine/param.h> */
87 char	machine_arch[] = MACHINE_ARCH;	/* from <machine/param.h> */
88 char	cpu_model[100];			/* machine model (primary CPU) */
89 extern char machine_model[];
90 
91 static void cpu_reset_fpustate(void);
92 
93 /* The CPU configuration driver. */
94 void cpu_attach(struct device *, struct device *, void *);
95 int cpu_match(struct device *, struct cfdata *, void *);
96 
97 CFATTACH_DECL(cpu, sizeof(struct device),
98     cpu_match, cpu_attach, NULL, NULL);
99 
100 struct cpu_info *
101 alloc_cpuinfo(u_int cpu_node)
102 {
103 	paddr_t pa0, pa;
104 	vaddr_t va, va0;
105 	vsize_t sz = 8 * PAGE_SIZE;
106 	int portid;
107 	struct cpu_info *cpi, *ci;
108 	extern paddr_t cpu0paddr;
109 
110 	/*
111 	 * Check for UPAID in the cpus list.
112 	 */
113 	if (OF_getprop(cpu_node, "upa-portid", &portid, sizeof(portid)) <= 0)
114 		panic("alloc_cpuinfo: upa-portid");
115 
116 	for (cpi = cpus; cpi != NULL; cpi = cpi->ci_next)
117 		if (cpi->ci_cpuid == portid)
118 			return cpi;
119 
120 	/* Allocate the aligned VA and determine the size. */
121 	va = uvm_km_alloc(kernel_map, sz, 8 * PAGE_SIZE, UVM_KMF_VAONLY);
122 	if (!va)
123 		panic("alloc_cpuinfo: no virtual space");
124 	va0 = va;
125 
126 	pa0 = cpu0paddr;
127 	cpu0paddr += sz;
128 
129 	for (pa = pa0; pa < cpu0paddr; pa += PAGE_SIZE, va += PAGE_SIZE)
130 		pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE);
131 
132 	pmap_update(pmap_kernel());
133 
134 	cpi = (struct cpu_info *)(va0 + CPUINFO_VA - INTSTACK);
135 
136 	memset((void *)va0, 0, sz);
137 
138 	/*
139 	 * Initialize cpuinfo structure.
140 	 *
141 	 * Arrange pcb, idle stack and interrupt stack in the same
142 	 * way as is done for the boot CPU in pmap.c.
143 	 */
144 	cpi->ci_next = NULL;
145 	cpi->ci_curlwp = NULL;
146 	cpi->ci_cpuid = portid;
147 	cpi->ci_fplwp = NULL;
148 	cpi->ci_spinup = NULL;
149 	cpi->ci_paddr = pa0;
150 	cpi->ci_self = cpi;
151 	cpi->ci_node = cpu_node;
152 
153 	/*
154 	 * Finally, add itself to the list of active cpus.
155 	 */
156 	for (ci = cpus; ci->ci_next != NULL; ci = ci->ci_next)
157 		;
158 	ci->ci_next = cpi;
159 	return (cpi);
160 }
161 
162 int
163 cpu_match(struct device *parent, struct cfdata *cf, void *aux)
164 {
165 	struct mainbus_attach_args *ma = aux;
166 
167 	return (strcmp(cf->cf_name, ma->ma_name) == 0);
168 }
169 
170 static void
171 cpu_reset_fpustate(void)
172 {
173 	struct fpstate64 *fpstate;
174 	struct fpstate64 fps[2];
175 
176 	/* This needs to be 64-bit aligned */
177 	fpstate = ALIGNFPSTATE(&fps[1]);
178 
179 	/*
180 	 * Get the FSR and clear any exceptions.  If we do not unload
181 	 * the queue here and it is left over from a previous crash, we
182 	 * will panic in the first loadfpstate(), due to a sequence error,
183 	 * so we need to dump the whole state anyway.
184 	 */
185 	fpstate->fs_fsr = 7 << FSR_VER_SHIFT;	/* 7 is reserved for "none" */
186 	savefpstate(fpstate);
187 }
188 
189 /*
190  * Attach the CPU.
191  * Discover interesting goop about the virtual address cache
192  * (slightly funny place to do it, but this is where it is to be found).
193  */
194 void
195 cpu_attach(struct device *parent, struct device *dev, void *aux)
196 {
197 	int node;
198 	long clk;
199 	struct mainbus_attach_args *ma = aux;
200 	struct cpu_info *ci;
201 	const char *sep;
202 	register int i, l;
203 	int bigcache, cachesize;
204 	char buf[100];
205 	int 	totalsize = 0;
206 	int 	linesize;
207 	static bool passed = false;
208 
209 	/* tell them what we have */
210 	node = ma->ma_node;
211 
212 	/*
213 	 * Allocate cpu_info structure if needed.
214 	 */
215 	ci = alloc_cpuinfo((u_int)node);
216 
217 	/*
218 	 * Only do this on the boot cpu.  Other cpu's call
219 	 * cpu_reset_fpustate() from cpu_hatch() before they
220 	 * call into the idle loop.
221 	 * For other cpus, we need to call mi_cpu_attach()
222 	 * and complete setting up cpcb.
223 	 */
224 	if (!passed) {
225 		passed = true;
226 		cpu_reset_fpustate();
227 	}
228 #ifdef MULTIPROCESSOR
229 	else {
230 		mi_cpu_attach(ci);
231 		ci->ci_cpcb = (struct pcb *)ci->ci_data.cpu_idlelwp->l_addr;
232 	}
233 #endif
234 
235 	clk = prom_getpropint(node, "clock-frequency", 0);
236 	if (clk == 0) {
237 		/*
238 		 * Try to find it in the OpenPROM root...
239 		 */
240 		clk = prom_getpropint(findroot(), "clock-frequency", 0);
241 	}
242 	if (clk) {
243 		/* Tell OS what frequency we run on */
244 		ci->ci_cpu_clockrate[0] = clk;
245 		ci->ci_cpu_clockrate[1] = clk / 1000000;
246 	}
247 
248 	snprintf(buf, sizeof buf, "%s @ %s MHz",
249 		prom_getpropstring(node, "name"), clockfreq(clk));
250 	snprintf(cpu_model, sizeof cpu_model, "%s (%s)", machine_model, buf);
251 
252 	printf(": %s, UPA id %d\n", buf, ci->ci_cpuid);
253 	printf("%s:", dev->dv_xname);
254 
255 	bigcache = 0;
256 
257 	linesize = l =
258 		prom_getpropint(node, "icache-line-size", 0);
259 	for (i = 0; (1 << i) < l && l; i++)
260 		/* void */;
261 	if ((1 << i) != l && l)
262 		panic("bad icache line size %d", l);
263 	totalsize =
264 		prom_getpropint(node, "icache-size", 0) *
265 		prom_getpropint(node, "icache-associativity", 1);
266 	if (totalsize == 0)
267 		totalsize = l *
268 			prom_getpropint(node, "icache-nlines", 64) *
269 			prom_getpropint(node, "icache-associativity", 1);
270 
271 	cachesize = totalsize /
272 	    prom_getpropint(node, "icache-associativity", 1);
273 	bigcache = cachesize;
274 
275 	sep = " ";
276 	if (totalsize > 0) {
277 		printf("%s%ldK instruction (%ld b/l)", sep,
278 		       (long)totalsize/1024,
279 		       (long)linesize);
280 		sep = ", ";
281 	}
282 
283 	linesize = l =
284 		prom_getpropint(node, "dcache-line-size",0);
285 	for (i = 0; (1 << i) < l && l; i++)
286 		/* void */;
287 	if ((1 << i) != l && l)
288 		panic("bad dcache line size %d", l);
289 	totalsize =
290 		prom_getpropint(node, "dcache-size", 0) *
291 		prom_getpropint(node, "dcache-associativity", 1);
292 	if (totalsize == 0)
293 		totalsize = l *
294 			prom_getpropint(node, "dcache-nlines", 128) *
295 			prom_getpropint(node, "dcache-associativity", 1);
296 
297 	cachesize = totalsize /
298 	    prom_getpropint(node, "dcache-associativity", 1);
299 	if (cachesize > bigcache)
300 		bigcache = cachesize;
301 
302 	if (totalsize > 0) {
303 		printf("%s%ldK data (%ld b/l)", sep,
304 		       (long)totalsize/1024,
305 		       (long)linesize);
306 		sep = ", ";
307 	}
308 
309 	linesize = l =
310 		prom_getpropint(node, "ecache-line-size", 0);
311 	for (i = 0; (1 << i) < l && l; i++)
312 		/* void */;
313 	if ((1 << i) != l && l)
314 		panic("bad ecache line size %d", l);
315 	totalsize =
316 		prom_getpropint(node, "ecache-size", 0) *
317 		prom_getpropint(node, "ecache-associativity", 1);
318 	if (totalsize == 0)
319 		totalsize = l *
320 			prom_getpropint(node, "ecache-nlines", 32768) *
321 			prom_getpropint(node, "ecache-associativity", 1);
322 
323 	cachesize = totalsize /
324 	     prom_getpropint(node, "ecache-associativity", 1);
325 	if (cachesize > bigcache)
326 		bigcache = cachesize;
327 
328 	if (totalsize > 0) {
329 		printf("%s%ldK external (%ld b/l)", sep,
330 		       (long)totalsize/1024,
331 		       (long)linesize);
332 	}
333 	printf("\n");
334 
335 	if (ecache_min_line_size == 0 ||
336 	    linesize < ecache_min_line_size)
337 		ecache_min_line_size = linesize;
338 
339 	/*
340 	 * Now that we know the size of the largest cache on this CPU,
341 	 * re-color our pages.
342 	 */
343 	uvm_page_recolor(atop(bigcache)); /* XXX */
344 
345 }
346 
347 #if defined(MULTIPROCESSOR)
348 vaddr_t cpu_spinup_trampoline;
349 
350 /*
351  * Start secondary processors in motion.
352  */
353 void
354 cpu_boot_secondary_processors()
355 {
356 	int i, pstate;
357 	struct cpu_info *ci;
358 
359 	sparc64_ipi_init();
360 
361 	for (ci = cpus; ci != NULL; ci = ci->ci_next) {
362 		if (ci->ci_cpuid == CPU_UPAID)
363 			continue;
364 
365 		cpu_args->cb_node = ci->ci_node;
366 		cpu_args->cb_cpuinfo = ci->ci_paddr;
367 		membar_sync();
368 
369 		/* Disable interrupts and start another CPU. */
370 		pstate = getpstate();
371 		setpstate(PSTATE_KERN);
372 
373 		prom_startcpu(ci->ci_node, (void *)cpu_spinup_trampoline, 0);
374 
375 		for (i = 0; i < 2000; i++) {
376 			membar_sync();
377 			if (CPUSET_HAS(cpus_active, ci->ci_index))
378 				break;
379 			delay(10000);
380 		}
381 		setpstate(pstate);
382 
383 		if (!CPUSET_HAS(cpus_active, ci->ci_index))
384 			printf("cpu%d: startup failed\n", ci->ci_cpuid);
385 	}
386 }
387 
388 void
389 cpu_hatch()
390 {
391 	extern void tickintr_establish(void);
392 	char *v = (char*)CPUINFO_VA;
393 	int i;
394 
395 	for (i = 0; i < 4*PAGE_SIZE; i += sizeof(long))
396 		flush(v + i);
397 
398 	CPUSET_ADD(cpus_active, cpu_number());
399 	cpu_reset_fpustate();
400 	curlwp = curcpu()->ci_data.cpu_idlelwp;
401 	membar_sync();
402 	spl0();
403 
404 #if 0
405 	tickintr_establish();
406 #endif
407 }
408 #endif /* MULTIPROCESSOR */
409