xref: /freebsd/sys/powerpc/pseries/platform_chrp.c (revision f9d6e0a5d0aabf774ddc15d96e17f90b47e1a9b5)
17a8d25c0SNathan Whitehorn /*-
271e3c308SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
371e3c308SPedro F. Giffuni  *
47a8d25c0SNathan Whitehorn  * Copyright (c) 2008 Marcel Moolenaar
57a8d25c0SNathan Whitehorn  * Copyright (c) 2009 Nathan Whitehorn
67a8d25c0SNathan Whitehorn  * All rights reserved.
77a8d25c0SNathan Whitehorn  *
87a8d25c0SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
97a8d25c0SNathan Whitehorn  * modification, are permitted provided that the following conditions
107a8d25c0SNathan Whitehorn  * are met:
117a8d25c0SNathan Whitehorn  *
127a8d25c0SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
137a8d25c0SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
147a8d25c0SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
157a8d25c0SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
167a8d25c0SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
177a8d25c0SNathan Whitehorn  *
187a8d25c0SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
197a8d25c0SNathan Whitehorn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
207a8d25c0SNathan Whitehorn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
217a8d25c0SNathan Whitehorn  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
227a8d25c0SNathan Whitehorn  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
237a8d25c0SNathan Whitehorn  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247a8d25c0SNathan Whitehorn  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
257a8d25c0SNathan Whitehorn  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
267a8d25c0SNathan Whitehorn  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
277a8d25c0SNathan Whitehorn  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
287a8d25c0SNathan Whitehorn  */
297a8d25c0SNathan Whitehorn 
307a8d25c0SNathan Whitehorn #include <sys/cdefs.h>
317a8d25c0SNathan Whitehorn __FBSDID("$FreeBSD$");
327a8d25c0SNathan Whitehorn 
337a8d25c0SNathan Whitehorn #include <sys/param.h>
347a8d25c0SNathan Whitehorn #include <sys/systm.h>
357a8d25c0SNathan Whitehorn #include <sys/kernel.h>
367a8d25c0SNathan Whitehorn #include <sys/bus.h>
377a8d25c0SNathan Whitehorn #include <sys/pcpu.h>
387a8d25c0SNathan Whitehorn #include <sys/proc.h>
39e21d69e9SNathan Whitehorn #include <sys/sched.h>
407a8d25c0SNathan Whitehorn #include <sys/smp.h>
417a8d25c0SNathan Whitehorn #include <vm/vm.h>
427a8d25c0SNathan Whitehorn #include <vm/pmap.h>
437a8d25c0SNathan Whitehorn 
447a8d25c0SNathan Whitehorn #include <machine/bus.h>
457a8d25c0SNathan Whitehorn #include <machine/cpu.h>
467a8d25c0SNathan Whitehorn #include <machine/hid.h>
477a8d25c0SNathan Whitehorn #include <machine/platformvar.h>
487a8d25c0SNathan Whitehorn #include <machine/rtas.h>
497a8d25c0SNathan Whitehorn #include <machine/smp.h>
507a8d25c0SNathan Whitehorn #include <machine/spr.h>
51258dbffeSNathan Whitehorn #include <machine/trap.h>
527a8d25c0SNathan Whitehorn 
537a8d25c0SNathan Whitehorn #include <dev/ofw/openfirm.h>
547a8d25c0SNathan Whitehorn #include <machine/ofw_machdep.h>
557a8d25c0SNathan Whitehorn 
567a8d25c0SNathan Whitehorn #include "platform_if.h"
577a8d25c0SNathan Whitehorn 
587a8d25c0SNathan Whitehorn #ifdef SMP
597a8d25c0SNathan Whitehorn extern void *ap_pcpu;
607a8d25c0SNathan Whitehorn #endif
617a8d25c0SNathan Whitehorn 
627a8d25c0SNathan Whitehorn #ifdef __powerpc64__
63f1e48417SNathan Whitehorn static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
647a8d25c0SNathan Whitehorn #endif
657a8d25c0SNathan Whitehorn 
667a8d25c0SNathan Whitehorn static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
677a8d25c0SNathan Whitehorn 
687a8d25c0SNathan Whitehorn static int chrp_probe(platform_t);
697a8d25c0SNathan Whitehorn static int chrp_attach(platform_t);
70c1cb22d7SNathan Whitehorn void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
71c1cb22d7SNathan Whitehorn     struct mem_region *avail, int *availsz);
727a8d25c0SNathan Whitehorn static vm_offset_t chrp_real_maxaddr(platform_t);
737a8d25c0SNathan Whitehorn static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
747a8d25c0SNathan Whitehorn static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
757a8d25c0SNathan Whitehorn static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
767a8d25c0SNathan Whitehorn static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
777a8d25c0SNathan Whitehorn static void chrp_smp_ap_init(platform_t);
787a8d25c0SNathan Whitehorn #ifdef SMP
797a8d25c0SNathan Whitehorn static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
807a8d25c0SNathan Whitehorn static struct cpu_group *chrp_smp_topo(platform_t plat);
817a8d25c0SNathan Whitehorn #endif
827a8d25c0SNathan Whitehorn static void chrp_reset(platform_t);
837a8d25c0SNathan Whitehorn #ifdef __powerpc64__
847a8d25c0SNathan Whitehorn #include "phyp-hvcall.h"
857a8d25c0SNathan Whitehorn static void phyp_cpu_idle(sbintime_t sbt);
867a8d25c0SNathan Whitehorn #endif
877a8d25c0SNathan Whitehorn 
887a8d25c0SNathan Whitehorn static platform_method_t chrp_methods[] = {
897a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_probe, 		chrp_probe),
907a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_attach,		chrp_attach),
917a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_mem_regions,	chrp_mem_regions),
927a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_real_maxaddr,	chrp_real_maxaddr),
937a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_timebase_freq,	chrp_timebase_freq),
947a8d25c0SNathan Whitehorn 
957a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_ap_init,	chrp_smp_ap_init),
967a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_first_cpu,	chrp_smp_first_cpu),
977a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_next_cpu,	chrp_smp_next_cpu),
987a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_get_bsp,	chrp_smp_get_bsp),
997a8d25c0SNathan Whitehorn #ifdef SMP
1007a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_start_cpu,	chrp_smp_start_cpu),
1017a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_topo,	chrp_smp_topo),
1027a8d25c0SNathan Whitehorn #endif
1037a8d25c0SNathan Whitehorn 
1047a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_reset,		chrp_reset),
1057a8d25c0SNathan Whitehorn 
1067a8d25c0SNathan Whitehorn 	{ 0, 0 }
1077a8d25c0SNathan Whitehorn };
1087a8d25c0SNathan Whitehorn 
1097a8d25c0SNathan Whitehorn static platform_def_t chrp_platform = {
1107a8d25c0SNathan Whitehorn 	"chrp",
1117a8d25c0SNathan Whitehorn 	chrp_methods,
1127a8d25c0SNathan Whitehorn 	0
1137a8d25c0SNathan Whitehorn };
1147a8d25c0SNathan Whitehorn 
1157a8d25c0SNathan Whitehorn PLATFORM_DEF(chrp_platform);
1167a8d25c0SNathan Whitehorn 
117*f9d6e0a5SNathan Whitehorn #define BSP_MUST_BE_CPU_ZERO
118*f9d6e0a5SNathan Whitehorn 
1197a8d25c0SNathan Whitehorn static int
1207a8d25c0SNathan Whitehorn chrp_probe(platform_t plat)
1217a8d25c0SNathan Whitehorn {
1227a8d25c0SNathan Whitehorn 	if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
1237a8d25c0SNathan Whitehorn 		return (BUS_PROBE_GENERIC);
1247a8d25c0SNathan Whitehorn 
1257a8d25c0SNathan Whitehorn 	return (ENXIO);
1267a8d25c0SNathan Whitehorn }
1277a8d25c0SNathan Whitehorn 
1287a8d25c0SNathan Whitehorn static int
1297a8d25c0SNathan Whitehorn chrp_attach(platform_t plat)
1307a8d25c0SNathan Whitehorn {
13166fe9464SBjoern A. Zeeb #ifdef __powerpc64__
132f1e48417SNathan Whitehorn 	int i;
133f1e48417SNathan Whitehorn 
1347a8d25c0SNathan Whitehorn 	/* XXX: check for /rtas/ibm,hypertas-functions? */
1357a8d25c0SNathan Whitehorn 	if (!(mfmsr() & PSL_HV)) {
1367a8d25c0SNathan Whitehorn 		struct mem_region *phys, *avail;
1377a8d25c0SNathan Whitehorn 		int nphys, navail;
1387a8d25c0SNathan Whitehorn 		mem_regions(&phys, &nphys, &avail, &navail);
1397a8d25c0SNathan Whitehorn 		realmaxaddr = phys[0].mr_size;
1407a8d25c0SNathan Whitehorn 
1417a8d25c0SNathan Whitehorn 		pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
1427a8d25c0SNathan Whitehorn 		cpu_idle_hook = phyp_cpu_idle;
1437a8d25c0SNathan Whitehorn 
1447a8d25c0SNathan Whitehorn 		/* Set up important VPA fields */
145f1e48417SNathan Whitehorn 		for (i = 0; i < MAXCPU; i++) {
146f1e48417SNathan Whitehorn 			bzero(splpar_vpa[i], sizeof(splpar_vpa));
147f1e48417SNathan Whitehorn 			/* First two: VPA size */
148f1e48417SNathan Whitehorn 			splpar_vpa[i][4] =
149f1e48417SNathan Whitehorn 			    (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
150f1e48417SNathan Whitehorn 			splpar_vpa[i][5] =
151f1e48417SNathan Whitehorn 			    (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
152f1e48417SNathan Whitehorn 			splpar_vpa[i][0xba] = 1;	/* Maintain FPRs */
153f1e48417SNathan Whitehorn 			splpar_vpa[i][0xbb] = 1;	/* Maintain PMCs */
154f1e48417SNathan Whitehorn 			splpar_vpa[i][0xfc] = 0xff;	/* Maintain full SLB */
155f1e48417SNathan Whitehorn 			splpar_vpa[i][0xfd] = 0xff;
156f1e48417SNathan Whitehorn 			splpar_vpa[i][0xff] = 1;	/* Maintain Altivec */
157f1e48417SNathan Whitehorn 		}
1587a8d25c0SNathan Whitehorn 		mb();
1597a8d25c0SNathan Whitehorn 
1607a8d25c0SNathan Whitehorn 		/* Set up hypervisor CPU stuff */
1617a8d25c0SNathan Whitehorn 		chrp_smp_ap_init(plat);
1627a8d25c0SNathan Whitehorn 	}
1637a8d25c0SNathan Whitehorn #endif
1647a8d25c0SNathan Whitehorn 
1659f706727SNathan Whitehorn 	/* Some systems (e.g. QEMU) need Open Firmware to stand down */
1669f706727SNathan Whitehorn 	ofw_quiesce();
1679f706727SNathan Whitehorn 
1687a8d25c0SNathan Whitehorn 	return (0);
1697a8d25c0SNathan Whitehorn }
1707a8d25c0SNathan Whitehorn 
171c1cb22d7SNathan Whitehorn static int
172f5dfbe2fSAndreas Tobler parse_drconf_memory(struct mem_region *ofmem, int *msz,
173f5dfbe2fSAndreas Tobler 		    struct mem_region *ofavail, int *asz)
1747a8d25c0SNathan Whitehorn {
175c1cb22d7SNathan Whitehorn 	phandle_t phandle;
176c1cb22d7SNathan Whitehorn 	vm_offset_t base;
177c1cb22d7SNathan Whitehorn 	int i, idx, len, lasz, lmsz, res;
178f5dfbe2fSAndreas Tobler 	uint32_t flags, lmb_size[2];
179509142e1SNathan Whitehorn 	uint32_t *dmem;
180c1cb22d7SNathan Whitehorn 
181c1cb22d7SNathan Whitehorn 	lmsz = *msz;
182c1cb22d7SNathan Whitehorn 	lasz = *asz;
183c1cb22d7SNathan Whitehorn 
184c1cb22d7SNathan Whitehorn 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
185c1cb22d7SNathan Whitehorn 	if (phandle == -1)
186c1cb22d7SNathan Whitehorn 		/* No drconf node, return. */
187c1cb22d7SNathan Whitehorn 		return (0);
188c1cb22d7SNathan Whitehorn 
189509142e1SNathan Whitehorn 	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
190509142e1SNathan Whitehorn 	    sizeof(lmb_size));
191c1cb22d7SNathan Whitehorn 	if (res == -1)
192c1cb22d7SNathan Whitehorn 		return (0);
193c1cb22d7SNathan Whitehorn 	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
194c1cb22d7SNathan Whitehorn 
195c1cb22d7SNathan Whitehorn 	/* Parse the /ibm,dynamic-memory.
196c1cb22d7SNathan Whitehorn 	   The first position gives the # of entries. The next two words
197c1cb22d7SNathan Whitehorn  	   reflect the address of the memory block. The next four words are
198c1cb22d7SNathan Whitehorn 	   the DRC index, reserved, list index and flags.
199c1cb22d7SNathan Whitehorn 	   (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
200c1cb22d7SNathan Whitehorn 
201c1cb22d7SNathan Whitehorn 	    #el  Addr   DRC-idx  res   list-idx  flags
202c1cb22d7SNathan Whitehorn 	   -------------------------------------------------
203c1cb22d7SNathan Whitehorn 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
204c1cb22d7SNathan Whitehorn 	   -------------------------------------------------
205c1cb22d7SNathan Whitehorn 	*/
206c1cb22d7SNathan Whitehorn 
207c1cb22d7SNathan Whitehorn 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
208c1cb22d7SNathan Whitehorn 	if (len > 0) {
209c1cb22d7SNathan Whitehorn 
210c1cb22d7SNathan Whitehorn 		/* We have to use a variable length array on the stack
211c1cb22d7SNathan Whitehorn 		   since we have very limited stack space.
212c1cb22d7SNathan Whitehorn 		*/
213c1cb22d7SNathan Whitehorn 		cell_t arr[len/sizeof(cell_t)];
214c1cb22d7SNathan Whitehorn 
215509142e1SNathan Whitehorn 		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
216c1cb22d7SNathan Whitehorn 		    sizeof(arr));
217c1cb22d7SNathan Whitehorn 		if (res == -1)
218c1cb22d7SNathan Whitehorn 			return (0);
219c1cb22d7SNathan Whitehorn 
220c1cb22d7SNathan Whitehorn 		/* Number of elements */
221c1cb22d7SNathan Whitehorn 		idx = arr[0];
222c1cb22d7SNathan Whitehorn 
223f5dfbe2fSAndreas Tobler 		/* First address, in arr[1], arr[2]*/
224509142e1SNathan Whitehorn 		dmem = &arr[1];
225c1cb22d7SNathan Whitehorn 
226c1cb22d7SNathan Whitehorn 		for (i = 0; i < idx; i++) {
227509142e1SNathan Whitehorn 			base = ((uint64_t)dmem[0] << 32) + dmem[1];
228509142e1SNathan Whitehorn 			dmem += 4;
229509142e1SNathan Whitehorn 			flags = dmem[1];
230c1cb22d7SNathan Whitehorn 			/* Use region only if available and not reserved. */
231c1cb22d7SNathan Whitehorn 			if ((flags & 0x8) && !(flags & 0x80)) {
232c1cb22d7SNathan Whitehorn 				ofmem[lmsz].mr_start = base;
233c1cb22d7SNathan Whitehorn 				ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
234c1cb22d7SNathan Whitehorn 				ofavail[lasz].mr_start = base;
235c1cb22d7SNathan Whitehorn 				ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
236c1cb22d7SNathan Whitehorn 				lmsz++;
237c1cb22d7SNathan Whitehorn 				lasz++;
238c1cb22d7SNathan Whitehorn 			}
239509142e1SNathan Whitehorn 			dmem += 2;
240c1cb22d7SNathan Whitehorn 		}
241c1cb22d7SNathan Whitehorn 	}
242c1cb22d7SNathan Whitehorn 
243c1cb22d7SNathan Whitehorn 	*msz = lmsz;
244c1cb22d7SNathan Whitehorn 	*asz = lasz;
245c1cb22d7SNathan Whitehorn 
246c1cb22d7SNathan Whitehorn 	return (1);
247c1cb22d7SNathan Whitehorn }
248c1cb22d7SNathan Whitehorn 
249c1cb22d7SNathan Whitehorn void
250c1cb22d7SNathan Whitehorn chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
251c1cb22d7SNathan Whitehorn     struct mem_region *avail, int *availsz)
252c1cb22d7SNathan Whitehorn {
253c1cb22d7SNathan Whitehorn 	vm_offset_t maxphysaddr;
254c1cb22d7SNathan Whitehorn 	int i;
255c1cb22d7SNathan Whitehorn 
2567a8d25c0SNathan Whitehorn 	ofw_mem_regions(phys, physsz, avail, availsz);
257f5dfbe2fSAndreas Tobler 	parse_drconf_memory(phys, physsz, avail, availsz);
258c1cb22d7SNathan Whitehorn 
259c1cb22d7SNathan Whitehorn 	/*
260c1cb22d7SNathan Whitehorn 	 * On some firmwares (SLOF), some memory may be marked available that
261c1cb22d7SNathan Whitehorn 	 * doesn't actually exist. This manifests as an extension of the last
262c1cb22d7SNathan Whitehorn 	 * available segment past the end of physical memory, so truncate that
263c1cb22d7SNathan Whitehorn 	 * one.
264c1cb22d7SNathan Whitehorn 	 */
265c1cb22d7SNathan Whitehorn 	maxphysaddr = 0;
266c1cb22d7SNathan Whitehorn 	for (i = 0; i < *physsz; i++)
267c1cb22d7SNathan Whitehorn 		if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
268c1cb22d7SNathan Whitehorn 			maxphysaddr = phys[i].mr_start + phys[i].mr_size;
269c1cb22d7SNathan Whitehorn 
270c1cb22d7SNathan Whitehorn 	for (i = 0; i < *availsz; i++)
271c1cb22d7SNathan Whitehorn 		if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
272c1cb22d7SNathan Whitehorn 			avail[i].mr_size = maxphysaddr - avail[i].mr_start;
2737a8d25c0SNathan Whitehorn }
2747a8d25c0SNathan Whitehorn 
2757a8d25c0SNathan Whitehorn static vm_offset_t
2767a8d25c0SNathan Whitehorn chrp_real_maxaddr(platform_t plat)
2777a8d25c0SNathan Whitehorn {
2787a8d25c0SNathan Whitehorn 	return (realmaxaddr);
2797a8d25c0SNathan Whitehorn }
2807a8d25c0SNathan Whitehorn 
2817a8d25c0SNathan Whitehorn static u_long
2827a8d25c0SNathan Whitehorn chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
2837a8d25c0SNathan Whitehorn {
284*f9d6e0a5SNathan Whitehorn 	char buf[8];
285*f9d6e0a5SNathan Whitehorn 	phandle_t cpu, dev, root;
286*f9d6e0a5SNathan Whitehorn 	int res;
2877a8d25c0SNathan Whitehorn 	int32_t ticks = -1;
2887a8d25c0SNathan Whitehorn 
289*f9d6e0a5SNathan Whitehorn 	root = OF_peer(0);
2907a8d25c0SNathan Whitehorn 
291*f9d6e0a5SNathan Whitehorn 	dev = OF_child(root);
292*f9d6e0a5SNathan Whitehorn 	while (dev != 0) {
293*f9d6e0a5SNathan Whitehorn 		res = OF_getprop(dev, "name", buf, sizeof(buf));
294*f9d6e0a5SNathan Whitehorn 		if (res > 0 && strcmp(buf, "cpus") == 0)
295*f9d6e0a5SNathan Whitehorn 			break;
296*f9d6e0a5SNathan Whitehorn 		dev = OF_peer(dev);
297*f9d6e0a5SNathan Whitehorn 	}
298*f9d6e0a5SNathan Whitehorn 
299*f9d6e0a5SNathan Whitehorn 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
300*f9d6e0a5SNathan Whitehorn 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
301*f9d6e0a5SNathan Whitehorn 		if (res > 0 && strcmp(buf, "cpu") == 0)
302*f9d6e0a5SNathan Whitehorn 			break;
303*f9d6e0a5SNathan Whitehorn 	}
304*f9d6e0a5SNathan Whitehorn 	if (cpu == 0)
305*f9d6e0a5SNathan Whitehorn 		return (512000000);
306*f9d6e0a5SNathan Whitehorn 
307*f9d6e0a5SNathan Whitehorn 	OF_getencprop(cpu, "timebase-frequency", &ticks, sizeof(ticks));
3087a8d25c0SNathan Whitehorn 
3097a8d25c0SNathan Whitehorn 	if (ticks <= 0)
3107a8d25c0SNathan Whitehorn 		panic("Unable to determine timebase frequency!");
3117a8d25c0SNathan Whitehorn 
3127a8d25c0SNathan Whitehorn 	return (ticks);
3137a8d25c0SNathan Whitehorn }
3147a8d25c0SNathan Whitehorn 
3157a8d25c0SNathan Whitehorn static int
316*f9d6e0a5SNathan Whitehorn chrp_cpuref_for_server(struct cpuref *cpuref, int cpu_n, int server)
3177a8d25c0SNathan Whitehorn {
3187a8d25c0SNathan Whitehorn 	char buf[8];
3197a8d25c0SNathan Whitehorn 	phandle_t cpu, dev, root;
320*f9d6e0a5SNathan Whitehorn 	int res, cpuid, i, j;
3217a8d25c0SNathan Whitehorn 
3227a8d25c0SNathan Whitehorn 	root = OF_peer(0);
3237a8d25c0SNathan Whitehorn 
3247a8d25c0SNathan Whitehorn 	dev = OF_child(root);
3257a8d25c0SNathan Whitehorn 	while (dev != 0) {
3267a8d25c0SNathan Whitehorn 		res = OF_getprop(dev, "name", buf, sizeof(buf));
3277a8d25c0SNathan Whitehorn 		if (res > 0 && strcmp(buf, "cpus") == 0)
3287a8d25c0SNathan Whitehorn 			break;
3297a8d25c0SNathan Whitehorn 		dev = OF_peer(dev);
3307a8d25c0SNathan Whitehorn 	}
3317a8d25c0SNathan Whitehorn 	if (dev == 0) {
3327a8d25c0SNathan Whitehorn 		/*
3337a8d25c0SNathan Whitehorn 		 * psim doesn't have a name property on the /cpus node,
3347a8d25c0SNathan Whitehorn 		 * but it can be found directly
3357a8d25c0SNathan Whitehorn 		 */
3367a8d25c0SNathan Whitehorn 		dev = OF_finddevice("/cpus");
3377a8d25c0SNathan Whitehorn 		if (dev == 0)
3387a8d25c0SNathan Whitehorn 			return (ENOENT);
3397a8d25c0SNathan Whitehorn 	}
3407a8d25c0SNathan Whitehorn 
341*f9d6e0a5SNathan Whitehorn 	i = 0;
342*f9d6e0a5SNathan Whitehorn 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
3437a8d25c0SNathan Whitehorn 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
344*f9d6e0a5SNathan Whitehorn 		if (res <= 0 || strcmp(buf, "cpu") != 0)
345*f9d6e0a5SNathan Whitehorn 			continue;
3467a8d25c0SNathan Whitehorn 
347*f9d6e0a5SNathan Whitehorn 		res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
348*f9d6e0a5SNathan Whitehorn 		if (res > 0) {
349*f9d6e0a5SNathan Whitehorn 			cell_t interrupt_servers[res/sizeof(cell_t)];
350*f9d6e0a5SNathan Whitehorn 			OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
351*f9d6e0a5SNathan Whitehorn 			    interrupt_servers, res);
352*f9d6e0a5SNathan Whitehorn 			for (j = 0; j < res/sizeof(cell_t); j++) {
353*f9d6e0a5SNathan Whitehorn 				cpuid = interrupt_servers[j];
354*f9d6e0a5SNathan Whitehorn 				if (server != -1 && cpuid == server)
355*f9d6e0a5SNathan Whitehorn 					break;
356*f9d6e0a5SNathan Whitehorn 				if (cpu_n != -1 && cpu_n == i)
357*f9d6e0a5SNathan Whitehorn 					break;
358*f9d6e0a5SNathan Whitehorn 				i++;
359*f9d6e0a5SNathan Whitehorn 			}
360*f9d6e0a5SNathan Whitehorn 
361*f9d6e0a5SNathan Whitehorn 			if (j != res/sizeof(cell_t))
362*f9d6e0a5SNathan Whitehorn 				break;
363*f9d6e0a5SNathan Whitehorn 		} else {
364509142e1SNathan Whitehorn 			res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
3657a8d25c0SNathan Whitehorn 			if (res <= 0)
3667a8d25c0SNathan Whitehorn 				cpuid = 0;
367*f9d6e0a5SNathan Whitehorn 			if (server != -1 && cpuid == server)
368*f9d6e0a5SNathan Whitehorn 				break;
369*f9d6e0a5SNathan Whitehorn 			if (cpu_n != -1 && cpu_n == i)
370*f9d6e0a5SNathan Whitehorn 				break;
371*f9d6e0a5SNathan Whitehorn 			i++;
372*f9d6e0a5SNathan Whitehorn 		}
373*f9d6e0a5SNathan Whitehorn 	}
374*f9d6e0a5SNathan Whitehorn 
375*f9d6e0a5SNathan Whitehorn 	if (cpu == 0)
376*f9d6e0a5SNathan Whitehorn 		return (ENOENT);
377*f9d6e0a5SNathan Whitehorn 
378*f9d6e0a5SNathan Whitehorn 	cpuref->cr_hwref = cpuid;
379*f9d6e0a5SNathan Whitehorn 	cpuref->cr_cpuid = i;
3807a8d25c0SNathan Whitehorn 
3817a8d25c0SNathan Whitehorn 	return (0);
3827a8d25c0SNathan Whitehorn }
3837a8d25c0SNathan Whitehorn 
3847a8d25c0SNathan Whitehorn static int
385*f9d6e0a5SNathan Whitehorn chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
386*f9d6e0a5SNathan Whitehorn {
387*f9d6e0a5SNathan Whitehorn #ifdef BSP_MUST_BE_CPU_ZERO
388*f9d6e0a5SNathan Whitehorn 	return (chrp_smp_get_bsp(plat, cpuref));
389*f9d6e0a5SNathan Whitehorn #else
390*f9d6e0a5SNathan Whitehorn 	return (chrp_cpuref_for_server(cpuref, 0, -1));
391*f9d6e0a5SNathan Whitehorn #endif
392*f9d6e0a5SNathan Whitehorn }
393*f9d6e0a5SNathan Whitehorn 
394*f9d6e0a5SNathan Whitehorn static int
3957a8d25c0SNathan Whitehorn chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
3967a8d25c0SNathan Whitehorn {
397*f9d6e0a5SNathan Whitehorn #ifdef BSP_MUST_BE_CPU_ZERO
398*f9d6e0a5SNathan Whitehorn 	int bsp, ncpus, err;
399*f9d6e0a5SNathan Whitehorn 	struct cpuref scratch;
4007a8d25c0SNathan Whitehorn 
401*f9d6e0a5SNathan Whitehorn 	chrp_smp_get_bsp(plat, &scratch);
402*f9d6e0a5SNathan Whitehorn 	chrp_cpuref_for_server(&scratch, -1, scratch.cr_hwref);
403*f9d6e0a5SNathan Whitehorn 	bsp = scratch.cr_cpuid;
4047a8d25c0SNathan Whitehorn 
405*f9d6e0a5SNathan Whitehorn 	for (ncpus = bsp; chrp_cpuref_for_server(&scratch, ncpus, -1) !=
406*f9d6e0a5SNathan Whitehorn 	    ENOENT; ncpus++) {}
407*f9d6e0a5SNathan Whitehorn 	if (cpuref->cr_cpuid + 1 == ncpus)
4087a8d25c0SNathan Whitehorn 		return (ENOENT);
409*f9d6e0a5SNathan Whitehorn 	err = chrp_cpuref_for_server(cpuref,
410*f9d6e0a5SNathan Whitehorn 	    (cpuref->cr_cpuid + bsp + 1) % ncpus, -1);
411*f9d6e0a5SNathan Whitehorn 	if (cpuref->cr_cpuid >= bsp)
412*f9d6e0a5SNathan Whitehorn 		cpuref->cr_cpuid -= bsp;
413*f9d6e0a5SNathan Whitehorn 	else
414*f9d6e0a5SNathan Whitehorn 		cpuref->cr_cpuid = ncpus - (bsp - cpuref->cr_cpuid);
415*f9d6e0a5SNathan Whitehorn 	return (err);
416*f9d6e0a5SNathan Whitehorn #else
417*f9d6e0a5SNathan Whitehorn 	return (chrp_cpuref_for_server(cpuref, cpuref->cr_cpuid+1, -1));
418*f9d6e0a5SNathan Whitehorn #endif
4197a8d25c0SNathan Whitehorn }
4207a8d25c0SNathan Whitehorn 
4217a8d25c0SNathan Whitehorn static int
4227a8d25c0SNathan Whitehorn chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
4237a8d25c0SNathan Whitehorn {
4247a8d25c0SNathan Whitehorn 	ihandle_t inst;
4257a8d25c0SNathan Whitehorn 	phandle_t bsp, chosen;
4267a8d25c0SNathan Whitehorn 	int res, cpuid;
4277a8d25c0SNathan Whitehorn 
4287a8d25c0SNathan Whitehorn 	chosen = OF_finddevice("/chosen");
4297a8d25c0SNathan Whitehorn 	if (chosen == 0)
4307a8d25c0SNathan Whitehorn 		return (ENXIO);
4317a8d25c0SNathan Whitehorn 
432509142e1SNathan Whitehorn 	res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst));
4337a8d25c0SNathan Whitehorn 	if (res < 0)
4347a8d25c0SNathan Whitehorn 		return (ENXIO);
4357a8d25c0SNathan Whitehorn 
4367a8d25c0SNathan Whitehorn 	bsp = OF_instance_to_package(inst);
4377a8d25c0SNathan Whitehorn 
4387a8d25c0SNathan Whitehorn 	/* Pick the primary thread. Can it be any other? */
439509142e1SNathan Whitehorn 	res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
4407a8d25c0SNathan Whitehorn 	    sizeof(cpuid));
4417a8d25c0SNathan Whitehorn 	if (res <= 0)
442509142e1SNathan Whitehorn 		res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid));
4437a8d25c0SNathan Whitehorn 	if (res <= 0)
4447a8d25c0SNathan Whitehorn 		cpuid = 0;
445*f9d6e0a5SNathan Whitehorn 
446*f9d6e0a5SNathan Whitehorn 	chrp_cpuref_for_server(cpuref, -1, cpuid);
447*f9d6e0a5SNathan Whitehorn #ifdef BSP_MUST_BE_CPU_ZERO
4487a8d25c0SNathan Whitehorn 	cpuref->cr_cpuid = cpuid;
449*f9d6e0a5SNathan Whitehorn #endif
4507a8d25c0SNathan Whitehorn 
4517a8d25c0SNathan Whitehorn 	return (0);
4527a8d25c0SNathan Whitehorn }
4537a8d25c0SNathan Whitehorn 
4547a8d25c0SNathan Whitehorn #ifdef SMP
4557a8d25c0SNathan Whitehorn static int
4567a8d25c0SNathan Whitehorn chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
4577a8d25c0SNathan Whitehorn {
4587a8d25c0SNathan Whitehorn 	cell_t start_cpu;
4597a8d25c0SNathan Whitehorn 	int result, err, timeout;
4607a8d25c0SNathan Whitehorn 
4617a8d25c0SNathan Whitehorn 	if (!rtas_exists()) {
4627a8d25c0SNathan Whitehorn 		printf("RTAS uninitialized: unable to start AP %d\n",
4637a8d25c0SNathan Whitehorn 		    pc->pc_cpuid);
4647a8d25c0SNathan Whitehorn 		return (ENXIO);
4657a8d25c0SNathan Whitehorn 	}
4667a8d25c0SNathan Whitehorn 
4677a8d25c0SNathan Whitehorn 	start_cpu = rtas_token_lookup("start-cpu");
4687a8d25c0SNathan Whitehorn 	if (start_cpu == -1) {
4697a8d25c0SNathan Whitehorn 		printf("RTAS unknown method: unable to start AP %d\n",
4707a8d25c0SNathan Whitehorn 		    pc->pc_cpuid);
4717a8d25c0SNathan Whitehorn 		return (ENXIO);
4727a8d25c0SNathan Whitehorn 	}
4737a8d25c0SNathan Whitehorn 
4747a8d25c0SNathan Whitehorn 	ap_pcpu = pc;
4757a8d25c0SNathan Whitehorn 	powerpc_sync();
4767a8d25c0SNathan Whitehorn 
477*f9d6e0a5SNathan Whitehorn 	result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
4787a8d25c0SNathan Whitehorn 	    &err);
4797a8d25c0SNathan Whitehorn 	if (result < 0 || err != 0) {
4807a8d25c0SNathan Whitehorn 		printf("RTAS error (%d/%d): unable to start AP %d\n",
4817a8d25c0SNathan Whitehorn 		    result, err, pc->pc_cpuid);
4827a8d25c0SNathan Whitehorn 		return (ENXIO);
4837a8d25c0SNathan Whitehorn 	}
4847a8d25c0SNathan Whitehorn 
4857a8d25c0SNathan Whitehorn 	timeout = 10000;
4867a8d25c0SNathan Whitehorn 	while (!pc->pc_awake && timeout--)
4877a8d25c0SNathan Whitehorn 		DELAY(100);
4887a8d25c0SNathan Whitehorn 
4897a8d25c0SNathan Whitehorn 	return ((pc->pc_awake) ? 0 : EBUSY);
4907a8d25c0SNathan Whitehorn }
4917a8d25c0SNathan Whitehorn 
4927a8d25c0SNathan Whitehorn static struct cpu_group *
4937a8d25c0SNathan Whitehorn chrp_smp_topo(platform_t plat)
4947a8d25c0SNathan Whitehorn {
495*f9d6e0a5SNathan Whitehorn 	char buf[8];
496*f9d6e0a5SNathan Whitehorn 	phandle_t cpu, dev, root;
497*f9d6e0a5SNathan Whitehorn 	int res, nthreads;
4987a8d25c0SNathan Whitehorn 
499*f9d6e0a5SNathan Whitehorn 	root = OF_peer(0);
500*f9d6e0a5SNathan Whitehorn 
501*f9d6e0a5SNathan Whitehorn 	dev = OF_child(root);
502*f9d6e0a5SNathan Whitehorn 	while (dev != 0) {
503*f9d6e0a5SNathan Whitehorn 		res = OF_getprop(dev, "name", buf, sizeof(buf));
504*f9d6e0a5SNathan Whitehorn 		if (res > 0 && strcmp(buf, "cpus") == 0)
505*f9d6e0a5SNathan Whitehorn 			break;
506*f9d6e0a5SNathan Whitehorn 		dev = OF_peer(dev);
5077a8d25c0SNathan Whitehorn 	}
5087a8d25c0SNathan Whitehorn 
509*f9d6e0a5SNathan Whitehorn 	nthreads = 1;
510*f9d6e0a5SNathan Whitehorn 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
511*f9d6e0a5SNathan Whitehorn 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
512*f9d6e0a5SNathan Whitehorn 		if (res <= 0 || strcmp(buf, "cpu") != 0)
513*f9d6e0a5SNathan Whitehorn 			continue;
514*f9d6e0a5SNathan Whitehorn 
515*f9d6e0a5SNathan Whitehorn 		res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
516*f9d6e0a5SNathan Whitehorn 
517*f9d6e0a5SNathan Whitehorn 		if (res >= 0)
518*f9d6e0a5SNathan Whitehorn 			nthreads = res / sizeof(cell_t);
519*f9d6e0a5SNathan Whitehorn 		else
520*f9d6e0a5SNathan Whitehorn 			nthreads = 1;
521*f9d6e0a5SNathan Whitehorn 		break;
522*f9d6e0a5SNathan Whitehorn 	}
523*f9d6e0a5SNathan Whitehorn 
524*f9d6e0a5SNathan Whitehorn 	if (mp_ncpus % nthreads != 0) {
5257a8d25c0SNathan Whitehorn 		printf("WARNING: Irregular SMP topology. Performance may be "
526*f9d6e0a5SNathan Whitehorn 		     "suboptimal (%d threads, %d on first core)\n",
527*f9d6e0a5SNathan Whitehorn 		     mp_ncpus, nthreads);
5287a8d25c0SNathan Whitehorn 		return (smp_topo_none());
5297a8d25c0SNathan Whitehorn 	}
5307a8d25c0SNathan Whitehorn 
5317a8d25c0SNathan Whitehorn 	/* Don't do anything fancier for non-threaded SMP */
532*f9d6e0a5SNathan Whitehorn 	if (nthreads == 1)
5337a8d25c0SNathan Whitehorn 		return (smp_topo_none());
5347a8d25c0SNathan Whitehorn 
535*f9d6e0a5SNathan Whitehorn 	return (smp_topo_1level(CG_SHARE_L1, nthreads, CG_FLAG_SMT));
5367a8d25c0SNathan Whitehorn }
5377a8d25c0SNathan Whitehorn #endif
5387a8d25c0SNathan Whitehorn 
5397a8d25c0SNathan Whitehorn static void
5407a8d25c0SNathan Whitehorn chrp_reset(platform_t platform)
5417a8d25c0SNathan Whitehorn {
542*f9d6e0a5SNathan Whitehorn 	cell_t token, status;
543*f9d6e0a5SNathan Whitehorn 
544*f9d6e0a5SNathan Whitehorn 	if (rtas_exists()) {
545*f9d6e0a5SNathan Whitehorn 		token = rtas_token_lookup("system-reboot");
546*f9d6e0a5SNathan Whitehorn 		if (token != -1)
547*f9d6e0a5SNathan Whitehorn 			rtas_call_method(token, 0, 1, &status);
548*f9d6e0a5SNathan Whitehorn 	} else {
5497a8d25c0SNathan Whitehorn 		OF_reboot();
5507a8d25c0SNathan Whitehorn 	}
551*f9d6e0a5SNathan Whitehorn }
5527a8d25c0SNathan Whitehorn 
5537a8d25c0SNathan Whitehorn #ifdef __powerpc64__
5547a8d25c0SNathan Whitehorn static void
5557a8d25c0SNathan Whitehorn phyp_cpu_idle(sbintime_t sbt)
5567a8d25c0SNathan Whitehorn {
557e21d69e9SNathan Whitehorn 	register_t msr;
558e21d69e9SNathan Whitehorn 
559e21d69e9SNathan Whitehorn 	msr = mfmsr();
560e21d69e9SNathan Whitehorn 
561e21d69e9SNathan Whitehorn 	mtmsr(msr & ~PSL_EE);
562e21d69e9SNathan Whitehorn 	if (sched_runnable()) {
563e21d69e9SNathan Whitehorn 		mtmsr(msr);
564e21d69e9SNathan Whitehorn 		return;
565e21d69e9SNathan Whitehorn 	}
566e21d69e9SNathan Whitehorn 
567e21d69e9SNathan Whitehorn 	phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
568e21d69e9SNathan Whitehorn 	mtmsr(msr);
5697a8d25c0SNathan Whitehorn }
5707a8d25c0SNathan Whitehorn 
5717a8d25c0SNathan Whitehorn static void
5727a8d25c0SNathan Whitehorn chrp_smp_ap_init(platform_t platform)
5737a8d25c0SNathan Whitehorn {
5747a8d25c0SNathan Whitehorn 	if (!(mfmsr() & PSL_HV)) {
575f1e48417SNathan Whitehorn 		/* Register VPA */
576f1e48417SNathan Whitehorn 		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid),
577f1e48417SNathan Whitehorn 		    splpar_vpa[PCPU_GET(cpuid)]);
578f1e48417SNathan Whitehorn 
5797a8d25c0SNathan Whitehorn 		/* Set interrupt priority */
5807a8d25c0SNathan Whitehorn 		phyp_hcall(H_CPPR, 0xff);
5817a8d25c0SNathan Whitehorn 	}
5827a8d25c0SNathan Whitehorn }
5837a8d25c0SNathan Whitehorn #else
5847a8d25c0SNathan Whitehorn static void
5857a8d25c0SNathan Whitehorn chrp_smp_ap_init(platform_t platform)
5867a8d25c0SNathan Whitehorn {
5877a8d25c0SNathan Whitehorn }
5887a8d25c0SNathan Whitehorn #endif
5897a8d25c0SNathan Whitehorn 
590