xref: /freebsd/sys/powerpc/pseries/platform_chrp.c (revision a662559264ccc44a9b3580f32b23a92efa08e0f2)
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 
33*a6625592SBrandon Bergren #include <sys/endian.h>
347a8d25c0SNathan Whitehorn #include <sys/param.h>
357a8d25c0SNathan Whitehorn #include <sys/systm.h>
367a8d25c0SNathan Whitehorn #include <sys/kernel.h>
377a8d25c0SNathan Whitehorn #include <sys/bus.h>
387a8d25c0SNathan Whitehorn #include <sys/pcpu.h>
397a8d25c0SNathan Whitehorn #include <sys/proc.h>
40e21d69e9SNathan Whitehorn #include <sys/sched.h>
417a8d25c0SNathan Whitehorn #include <sys/smp.h>
427a8d25c0SNathan Whitehorn #include <vm/vm.h>
437a8d25c0SNathan Whitehorn #include <vm/pmap.h>
447a8d25c0SNathan Whitehorn 
457a8d25c0SNathan Whitehorn #include <machine/bus.h>
467a8d25c0SNathan Whitehorn #include <machine/cpu.h>
477a8d25c0SNathan Whitehorn #include <machine/hid.h>
487a8d25c0SNathan Whitehorn #include <machine/platformvar.h>
497a8d25c0SNathan Whitehorn #include <machine/rtas.h>
507a8d25c0SNathan Whitehorn #include <machine/smp.h>
517a8d25c0SNathan Whitehorn #include <machine/spr.h>
52258dbffeSNathan Whitehorn #include <machine/trap.h>
537a8d25c0SNathan Whitehorn 
547a8d25c0SNathan Whitehorn #include <dev/ofw/openfirm.h>
557a8d25c0SNathan Whitehorn #include <machine/ofw_machdep.h>
567a8d25c0SNathan Whitehorn 
577a8d25c0SNathan Whitehorn #include "platform_if.h"
587a8d25c0SNathan Whitehorn 
597a8d25c0SNathan Whitehorn #ifdef SMP
607a8d25c0SNathan Whitehorn extern void *ap_pcpu;
617a8d25c0SNathan Whitehorn #endif
627a8d25c0SNathan Whitehorn 
637a8d25c0SNathan Whitehorn #ifdef __powerpc64__
64f1e48417SNathan Whitehorn static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
657a8d25c0SNathan Whitehorn #endif
667a8d25c0SNathan Whitehorn 
677a8d25c0SNathan Whitehorn static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
687a8d25c0SNathan Whitehorn 
697a8d25c0SNathan Whitehorn static int chrp_probe(platform_t);
707a8d25c0SNathan Whitehorn static int chrp_attach(platform_t);
71c1cb22d7SNathan Whitehorn void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
72c1cb22d7SNathan Whitehorn     struct mem_region *avail, int *availsz);
737a8d25c0SNathan Whitehorn static vm_offset_t chrp_real_maxaddr(platform_t);
747a8d25c0SNathan Whitehorn static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
757a8d25c0SNathan Whitehorn static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
767a8d25c0SNathan Whitehorn static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
777a8d25c0SNathan Whitehorn static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
787a8d25c0SNathan Whitehorn static void chrp_smp_ap_init(platform_t);
79f0393bbfSWojciech Macek static int chrp_cpuref_init(void);
807a8d25c0SNathan Whitehorn #ifdef SMP
817a8d25c0SNathan Whitehorn static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
82bba9cbe3SConrad Meyer static void chrp_smp_probe_threads(platform_t plat);
837a8d25c0SNathan Whitehorn static struct cpu_group *chrp_smp_topo(platform_t plat);
847a8d25c0SNathan Whitehorn #endif
857a8d25c0SNathan Whitehorn static void chrp_reset(platform_t);
867a8d25c0SNathan Whitehorn #ifdef __powerpc64__
877a8d25c0SNathan Whitehorn #include "phyp-hvcall.h"
887a8d25c0SNathan Whitehorn static void phyp_cpu_idle(sbintime_t sbt);
897a8d25c0SNathan Whitehorn #endif
907a8d25c0SNathan Whitehorn 
91f0393bbfSWojciech Macek static struct cpuref platform_cpuref[MAXCPU];
92f0393bbfSWojciech Macek static int platform_cpuref_cnt;
93f0393bbfSWojciech Macek static int platform_cpuref_valid;
94f0393bbfSWojciech Macek 
957a8d25c0SNathan Whitehorn static platform_method_t chrp_methods[] = {
967a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_probe, 		chrp_probe),
977a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_attach,		chrp_attach),
987a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_mem_regions,	chrp_mem_regions),
997a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_real_maxaddr,	chrp_real_maxaddr),
1007a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_timebase_freq,	chrp_timebase_freq),
1017a8d25c0SNathan Whitehorn 
1027a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_ap_init,	chrp_smp_ap_init),
1037a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_first_cpu,	chrp_smp_first_cpu),
1047a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_next_cpu,	chrp_smp_next_cpu),
1057a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_get_bsp,	chrp_smp_get_bsp),
1067a8d25c0SNathan Whitehorn #ifdef SMP
1077a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_start_cpu,	chrp_smp_start_cpu),
108bba9cbe3SConrad Meyer 	PLATFORMMETHOD(platform_smp_probe_threads,	chrp_smp_probe_threads),
1097a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_smp_topo,	chrp_smp_topo),
1107a8d25c0SNathan Whitehorn #endif
1117a8d25c0SNathan Whitehorn 
1127a8d25c0SNathan Whitehorn 	PLATFORMMETHOD(platform_reset,		chrp_reset),
1137a8d25c0SNathan Whitehorn 	{ 0, 0 }
1147a8d25c0SNathan Whitehorn };
1157a8d25c0SNathan Whitehorn 
1167a8d25c0SNathan Whitehorn static platform_def_t chrp_platform = {
1177a8d25c0SNathan Whitehorn 	"chrp",
1187a8d25c0SNathan Whitehorn 	chrp_methods,
1197a8d25c0SNathan Whitehorn 	0
1207a8d25c0SNathan Whitehorn };
1217a8d25c0SNathan Whitehorn 
1227a8d25c0SNathan Whitehorn PLATFORM_DEF(chrp_platform);
1237a8d25c0SNathan Whitehorn 
1247a8d25c0SNathan Whitehorn static int
1257a8d25c0SNathan Whitehorn chrp_probe(platform_t plat)
1267a8d25c0SNathan Whitehorn {
1277a8d25c0SNathan Whitehorn 	if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
1287a8d25c0SNathan Whitehorn 		return (BUS_PROBE_GENERIC);
1297a8d25c0SNathan Whitehorn 
1307a8d25c0SNathan Whitehorn 	return (ENXIO);
1317a8d25c0SNathan Whitehorn }
1327a8d25c0SNathan Whitehorn 
1337a8d25c0SNathan Whitehorn static int
1347a8d25c0SNathan Whitehorn chrp_attach(platform_t plat)
1357a8d25c0SNathan Whitehorn {
13635f29427SLeandro Lupori 	int quiesce;
13766fe9464SBjoern A. Zeeb #ifdef __powerpc64__
138f1e48417SNathan Whitehorn 	int i;
139f1e48417SNathan Whitehorn 
1407a8d25c0SNathan Whitehorn 	/* XXX: check for /rtas/ibm,hypertas-functions? */
1417a8d25c0SNathan Whitehorn 	if (!(mfmsr() & PSL_HV)) {
1427a8d25c0SNathan Whitehorn 		struct mem_region *phys, *avail;
1437a8d25c0SNathan Whitehorn 		int nphys, navail;
1448b55f9f8SLeandro Lupori 		vm_offset_t off;
1458b55f9f8SLeandro Lupori 
1467a8d25c0SNathan Whitehorn 		mem_regions(&phys, &nphys, &avail, &navail);
1478b55f9f8SLeandro Lupori 
1488b55f9f8SLeandro Lupori 		realmaxaddr = 0;
1498b55f9f8SLeandro Lupori 		for (i = 0; i < nphys; i++) {
1508b55f9f8SLeandro Lupori 			off = phys[i].mr_start + phys[i].mr_size;
1518b55f9f8SLeandro Lupori 			realmaxaddr = MAX(off, realmaxaddr);
1528b55f9f8SLeandro Lupori 		}
1537a8d25c0SNathan Whitehorn 
1547a8d25c0SNathan Whitehorn 		pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
1557a8d25c0SNathan Whitehorn 		cpu_idle_hook = phyp_cpu_idle;
1567a8d25c0SNathan Whitehorn 
1577a8d25c0SNathan Whitehorn 		/* Set up important VPA fields */
158f1e48417SNathan Whitehorn 		for (i = 0; i < MAXCPU; i++) {
159f1e48417SNathan Whitehorn 			/* First two: VPA size */
160f1e48417SNathan Whitehorn 			splpar_vpa[i][4] =
161f1e48417SNathan Whitehorn 			    (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
162f1e48417SNathan Whitehorn 			splpar_vpa[i][5] =
163f1e48417SNathan Whitehorn 			    (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
164f1e48417SNathan Whitehorn 			splpar_vpa[i][0xba] = 1;	/* Maintain FPRs */
165f1e48417SNathan Whitehorn 			splpar_vpa[i][0xbb] = 1;	/* Maintain PMCs */
166f1e48417SNathan Whitehorn 			splpar_vpa[i][0xfc] = 0xff;	/* Maintain full SLB */
167f1e48417SNathan Whitehorn 			splpar_vpa[i][0xfd] = 0xff;
168f1e48417SNathan Whitehorn 			splpar_vpa[i][0xff] = 1;	/* Maintain Altivec */
169f1e48417SNathan Whitehorn 		}
1707a8d25c0SNathan Whitehorn 		mb();
1717a8d25c0SNathan Whitehorn 
1727a8d25c0SNathan Whitehorn 		/* Set up hypervisor CPU stuff */
1737a8d25c0SNathan Whitehorn 		chrp_smp_ap_init(plat);
1747a8d25c0SNathan Whitehorn 	}
1757a8d25c0SNathan Whitehorn #endif
176f0393bbfSWojciech Macek 	chrp_cpuref_init();
1777a8d25c0SNathan Whitehorn 
1789f706727SNathan Whitehorn 	/* Some systems (e.g. QEMU) need Open Firmware to stand down */
17935f29427SLeandro Lupori 	quiesce = 1;
18035f29427SLeandro Lupori 	TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
18135f29427SLeandro Lupori 	if (quiesce)
1829f706727SNathan Whitehorn 		ofw_quiesce();
1839f706727SNathan Whitehorn 
1847a8d25c0SNathan Whitehorn 	return (0);
1857a8d25c0SNathan Whitehorn }
1867a8d25c0SNathan Whitehorn 
187c1cb22d7SNathan Whitehorn static int
188f5dfbe2fSAndreas Tobler parse_drconf_memory(struct mem_region *ofmem, int *msz,
189f5dfbe2fSAndreas Tobler 		    struct mem_region *ofavail, int *asz)
1907a8d25c0SNathan Whitehorn {
191c1cb22d7SNathan Whitehorn 	phandle_t phandle;
192c1cb22d7SNathan Whitehorn 	vm_offset_t base;
193c1cb22d7SNathan Whitehorn 	int i, idx, len, lasz, lmsz, res;
194f5dfbe2fSAndreas Tobler 	uint32_t flags, lmb_size[2];
195509142e1SNathan Whitehorn 	uint32_t *dmem;
196c1cb22d7SNathan Whitehorn 
197c1cb22d7SNathan Whitehorn 	lmsz = *msz;
198c1cb22d7SNathan Whitehorn 	lasz = *asz;
199c1cb22d7SNathan Whitehorn 
200c1cb22d7SNathan Whitehorn 	phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
201c1cb22d7SNathan Whitehorn 	if (phandle == -1)
202c1cb22d7SNathan Whitehorn 		/* No drconf node, return. */
203c1cb22d7SNathan Whitehorn 		return (0);
204c1cb22d7SNathan Whitehorn 
205509142e1SNathan Whitehorn 	res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
206509142e1SNathan Whitehorn 	    sizeof(lmb_size));
207c1cb22d7SNathan Whitehorn 	if (res == -1)
208c1cb22d7SNathan Whitehorn 		return (0);
209c1cb22d7SNathan Whitehorn 	printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
210c1cb22d7SNathan Whitehorn 
211c1cb22d7SNathan Whitehorn 	/* Parse the /ibm,dynamic-memory.
212c1cb22d7SNathan Whitehorn 	   The first position gives the # of entries. The next two words
213c1cb22d7SNathan Whitehorn  	   reflect the address of the memory block. The next four words are
214c1cb22d7SNathan Whitehorn 	   the DRC index, reserved, list index and flags.
215c1cb22d7SNathan Whitehorn 	   (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
216c1cb22d7SNathan Whitehorn 
217c1cb22d7SNathan Whitehorn 	    #el  Addr   DRC-idx  res   list-idx  flags
218c1cb22d7SNathan Whitehorn 	   -------------------------------------------------
219c1cb22d7SNathan Whitehorn 	   | 4 |   8   |   4   |   4   |   4   |   4   |....
220c1cb22d7SNathan Whitehorn 	   -------------------------------------------------
221c1cb22d7SNathan Whitehorn 	*/
222c1cb22d7SNathan Whitehorn 
223c1cb22d7SNathan Whitehorn 	len = OF_getproplen(phandle, "ibm,dynamic-memory");
224c1cb22d7SNathan Whitehorn 	if (len > 0) {
225c1cb22d7SNathan Whitehorn 		/* We have to use a variable length array on the stack
226c1cb22d7SNathan Whitehorn 		   since we have very limited stack space.
227c1cb22d7SNathan Whitehorn 		*/
228c1cb22d7SNathan Whitehorn 		cell_t arr[len/sizeof(cell_t)];
229c1cb22d7SNathan Whitehorn 
230509142e1SNathan Whitehorn 		res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
231c1cb22d7SNathan Whitehorn 		    sizeof(arr));
232c1cb22d7SNathan Whitehorn 		if (res == -1)
233c1cb22d7SNathan Whitehorn 			return (0);
234c1cb22d7SNathan Whitehorn 
235c1cb22d7SNathan Whitehorn 		/* Number of elements */
236c1cb22d7SNathan Whitehorn 		idx = arr[0];
237c1cb22d7SNathan Whitehorn 
238f5dfbe2fSAndreas Tobler 		/* First address, in arr[1], arr[2]*/
239509142e1SNathan Whitehorn 		dmem = &arr[1];
240c1cb22d7SNathan Whitehorn 
241c1cb22d7SNathan Whitehorn 		for (i = 0; i < idx; i++) {
242509142e1SNathan Whitehorn 			base = ((uint64_t)dmem[0] << 32) + dmem[1];
243509142e1SNathan Whitehorn 			dmem += 4;
244509142e1SNathan Whitehorn 			flags = dmem[1];
245c1cb22d7SNathan Whitehorn 			/* Use region only if available and not reserved. */
246c1cb22d7SNathan Whitehorn 			if ((flags & 0x8) && !(flags & 0x80)) {
247c1cb22d7SNathan Whitehorn 				ofmem[lmsz].mr_start = base;
248c1cb22d7SNathan Whitehorn 				ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
249c1cb22d7SNathan Whitehorn 				ofavail[lasz].mr_start = base;
250c1cb22d7SNathan Whitehorn 				ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
251c1cb22d7SNathan Whitehorn 				lmsz++;
252c1cb22d7SNathan Whitehorn 				lasz++;
253c1cb22d7SNathan Whitehorn 			}
254509142e1SNathan Whitehorn 			dmem += 2;
255c1cb22d7SNathan Whitehorn 		}
256c1cb22d7SNathan Whitehorn 	}
257c1cb22d7SNathan Whitehorn 
258c1cb22d7SNathan Whitehorn 	*msz = lmsz;
259c1cb22d7SNathan Whitehorn 	*asz = lasz;
260c1cb22d7SNathan Whitehorn 
261c1cb22d7SNathan Whitehorn 	return (1);
262c1cb22d7SNathan Whitehorn }
263c1cb22d7SNathan Whitehorn 
264c1cb22d7SNathan Whitehorn void
265c1cb22d7SNathan Whitehorn chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
266c1cb22d7SNathan Whitehorn     struct mem_region *avail, int *availsz)
267c1cb22d7SNathan Whitehorn {
268c1cb22d7SNathan Whitehorn 	vm_offset_t maxphysaddr;
269c1cb22d7SNathan Whitehorn 	int i;
270c1cb22d7SNathan Whitehorn 
2717a8d25c0SNathan Whitehorn 	ofw_mem_regions(phys, physsz, avail, availsz);
272f5dfbe2fSAndreas Tobler 	parse_drconf_memory(phys, physsz, avail, availsz);
273c1cb22d7SNathan Whitehorn 
274c1cb22d7SNathan Whitehorn 	/*
275c1cb22d7SNathan Whitehorn 	 * On some firmwares (SLOF), some memory may be marked available that
276c1cb22d7SNathan Whitehorn 	 * doesn't actually exist. This manifests as an extension of the last
277c1cb22d7SNathan Whitehorn 	 * available segment past the end of physical memory, so truncate that
278c1cb22d7SNathan Whitehorn 	 * one.
279c1cb22d7SNathan Whitehorn 	 */
280c1cb22d7SNathan Whitehorn 	maxphysaddr = 0;
281c1cb22d7SNathan Whitehorn 	for (i = 0; i < *physsz; i++)
282c1cb22d7SNathan Whitehorn 		if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
283c1cb22d7SNathan Whitehorn 			maxphysaddr = phys[i].mr_start + phys[i].mr_size;
284c1cb22d7SNathan Whitehorn 
285c1cb22d7SNathan Whitehorn 	for (i = 0; i < *availsz; i++)
286c1cb22d7SNathan Whitehorn 		if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
287c1cb22d7SNathan Whitehorn 			avail[i].mr_size = maxphysaddr - avail[i].mr_start;
2887a8d25c0SNathan Whitehorn }
2897a8d25c0SNathan Whitehorn 
2907a8d25c0SNathan Whitehorn static vm_offset_t
2917a8d25c0SNathan Whitehorn chrp_real_maxaddr(platform_t plat)
2927a8d25c0SNathan Whitehorn {
2937a8d25c0SNathan Whitehorn 	return (realmaxaddr);
2947a8d25c0SNathan Whitehorn }
2957a8d25c0SNathan Whitehorn 
2967a8d25c0SNathan Whitehorn static u_long
2977a8d25c0SNathan Whitehorn chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
2987a8d25c0SNathan Whitehorn {
299a00ce4e8SJustin Hibbits 	phandle_t cpus, cpunode;
3007a8d25c0SNathan Whitehorn 	int32_t ticks = -1;
301a00ce4e8SJustin Hibbits 	int res;
302a00ce4e8SJustin Hibbits 	char buf[8];
3037a8d25c0SNathan Whitehorn 
304a00ce4e8SJustin Hibbits 	cpus = OF_finddevice("/cpus");
305108117ccSOleksandr Tymoshenko 	if (cpus == -1)
306a00ce4e8SJustin Hibbits 		panic("CPU tree not found on Open Firmware\n");
3077a8d25c0SNathan Whitehorn 
308a00ce4e8SJustin Hibbits 	for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
309a00ce4e8SJustin Hibbits 		res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
310a00ce4e8SJustin Hibbits 		if (res > 0 && strcmp(buf, "cpu") == 0)
311a00ce4e8SJustin Hibbits 			break;
312a00ce4e8SJustin Hibbits 	}
313a00ce4e8SJustin Hibbits 	if (cpunode <= 0)
314a00ce4e8SJustin Hibbits 		panic("CPU node not found on Open Firmware\n");
315a00ce4e8SJustin Hibbits 
316a00ce4e8SJustin Hibbits 	OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
3177a8d25c0SNathan Whitehorn 
3187a8d25c0SNathan Whitehorn 	if (ticks <= 0)
3197a8d25c0SNathan Whitehorn 		panic("Unable to determine timebase frequency!");
3207a8d25c0SNathan Whitehorn 
3217a8d25c0SNathan Whitehorn 	return (ticks);
3227a8d25c0SNathan Whitehorn }
3237a8d25c0SNathan Whitehorn 
3247a8d25c0SNathan Whitehorn static int
32509f07b00SNathan Whitehorn chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
3267a8d25c0SNathan Whitehorn {
3277a8d25c0SNathan Whitehorn 
328f0393bbfSWojciech Macek 	if (platform_cpuref_valid == 0)
329f0393bbfSWojciech Macek 		return (EINVAL);
3307a8d25c0SNathan Whitehorn 
331f0393bbfSWojciech Macek 	cpuref->cr_cpuid = 0;
332f0393bbfSWojciech Macek 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
3337a8d25c0SNathan Whitehorn 
3347a8d25c0SNathan Whitehorn 	return (0);
3357a8d25c0SNathan Whitehorn }
3367a8d25c0SNathan Whitehorn 
3377a8d25c0SNathan Whitehorn static int
3387a8d25c0SNathan Whitehorn chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
3397a8d25c0SNathan Whitehorn {
340f0393bbfSWojciech Macek 	int id;
3417a8d25c0SNathan Whitehorn 
342f0393bbfSWojciech Macek 	if (platform_cpuref_valid == 0)
343f0393bbfSWojciech Macek 		return (EINVAL);
3447a8d25c0SNathan Whitehorn 
345f0393bbfSWojciech Macek 	id = cpuref->cr_cpuid + 1;
346f0393bbfSWojciech Macek 	if (id >= platform_cpuref_cnt)
3477a8d25c0SNathan Whitehorn 		return (ENOENT);
34809f07b00SNathan Whitehorn 
349f0393bbfSWojciech Macek 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
350f0393bbfSWojciech Macek 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
35109f07b00SNathan Whitehorn 
35209f07b00SNathan Whitehorn 	return (0);
3537a8d25c0SNathan Whitehorn }
3547a8d25c0SNathan Whitehorn 
3557a8d25c0SNathan Whitehorn static int
3567a8d25c0SNathan Whitehorn chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
3577a8d25c0SNathan Whitehorn {
3587a8d25c0SNathan Whitehorn 
359f0393bbfSWojciech Macek 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
360f0393bbfSWojciech Macek 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
361f0393bbfSWojciech Macek 	return (0);
362f0393bbfSWojciech Macek }
3637a8d25c0SNathan Whitehorn 
364151c44e2SJustin Hibbits static void
365151c44e2SJustin Hibbits get_cpu_reg(phandle_t cpu, cell_t *reg)
366151c44e2SJustin Hibbits {
367151c44e2SJustin Hibbits 	int res;
368151c44e2SJustin Hibbits 
369151c44e2SJustin Hibbits 	res = OF_getproplen(cpu, "reg");
370151c44e2SJustin Hibbits 	if (res != sizeof(cell_t))
371151c44e2SJustin Hibbits 		panic("Unexpected length for CPU property reg on Open Firmware\n");
372151c44e2SJustin Hibbits 	OF_getencprop(cpu, "reg", reg, res);
373151c44e2SJustin Hibbits }
374151c44e2SJustin Hibbits 
375f0393bbfSWojciech Macek static int
376f0393bbfSWojciech Macek chrp_cpuref_init(void)
377f0393bbfSWojciech Macek {
378151c44e2SJustin Hibbits 	phandle_t cpu, dev, chosen, pbsp;
379151c44e2SJustin Hibbits 	ihandle_t ibsp;
380f0393bbfSWojciech Macek 	char buf[32];
381151c44e2SJustin Hibbits 	int a, bsp, res, res2, tmp_cpuref_cnt;
382151c44e2SJustin Hibbits 	static struct cpuref tmp_cpuref[MAXCPU];
383151c44e2SJustin Hibbits 	cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
3847a8d25c0SNathan Whitehorn 
385f0393bbfSWojciech Macek 	if (platform_cpuref_valid)
386f0393bbfSWojciech Macek 		return (0);
3877a8d25c0SNathan Whitehorn 
388f0393bbfSWojciech Macek 	dev = OF_peer(0);
389f0393bbfSWojciech Macek 	dev = OF_child(dev);
390f0393bbfSWojciech Macek 	while (dev != 0) {
391f0393bbfSWojciech Macek 		res = OF_getprop(dev, "name", buf, sizeof(buf));
392f0393bbfSWojciech Macek 		if (res > 0 && strcmp(buf, "cpus") == 0)
393f0393bbfSWojciech Macek 			break;
394f0393bbfSWojciech Macek 		dev = OF_peer(dev);
395f0393bbfSWojciech Macek 	}
396f0393bbfSWojciech Macek 
397151c44e2SJustin Hibbits 	/* Make sure that cpus reg property have 1 address cell and 0 size cells */
398151c44e2SJustin Hibbits 	res = OF_getproplen(dev, "#address-cells");
399151c44e2SJustin Hibbits 	res2 = OF_getproplen(dev, "#size-cells");
400151c44e2SJustin Hibbits 	if (res != res2 || res != sizeof(cell_t))
401151c44e2SJustin Hibbits 		panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
402151c44e2SJustin Hibbits 	OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
403151c44e2SJustin Hibbits 	OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
404151c44e2SJustin Hibbits 	if (addr_cells != 1 || size_cells != 0)
405151c44e2SJustin Hibbits 		panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
406151c44e2SJustin Hibbits 
407151c44e2SJustin Hibbits 	/* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
408151c44e2SJustin Hibbits 
409151c44e2SJustin Hibbits 	chosen = OF_finddevice("/chosen");
410151c44e2SJustin Hibbits 	if (chosen == -1)
411151c44e2SJustin Hibbits 		panic("Device /chosen not found on Open Firmware\n");
412151c44e2SJustin Hibbits 
413151c44e2SJustin Hibbits 	bsp_reg = -1;
414151c44e2SJustin Hibbits 
415151c44e2SJustin Hibbits 	/* /chosen/cpu */
416151c44e2SJustin Hibbits 	if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
417151c44e2SJustin Hibbits 		OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
418*a6625592SBrandon Bergren 		pbsp = OF_instance_to_package(be32toh(ibsp));
419151c44e2SJustin Hibbits 		if (pbsp != -1)
420151c44e2SJustin Hibbits 			get_cpu_reg(pbsp, &bsp_reg);
421151c44e2SJustin Hibbits 	}
422151c44e2SJustin Hibbits 
423151c44e2SJustin Hibbits 	/* /chosen/fdtbootcpu */
424151c44e2SJustin Hibbits 	if (bsp_reg == -1) {
425151c44e2SJustin Hibbits 		if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
426151c44e2SJustin Hibbits 			OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
427151c44e2SJustin Hibbits 	}
428151c44e2SJustin Hibbits 
429151c44e2SJustin Hibbits 	if (bsp_reg == -1)
430151c44e2SJustin Hibbits 		panic("Boot CPU not found on Open Firmware\n");
431151c44e2SJustin Hibbits 
432151c44e2SJustin Hibbits 	bsp = -1;
433151c44e2SJustin Hibbits 	tmp_cpuref_cnt = 0;
434f0393bbfSWojciech Macek 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
435f0393bbfSWojciech Macek 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
436f0393bbfSWojciech Macek 		if (res > 0 && strcmp(buf, "cpu") == 0) {
437f0393bbfSWojciech Macek 			res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
438f0393bbfSWojciech Macek 			if (res > 0) {
439f0393bbfSWojciech Macek 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
440f0393bbfSWojciech Macek 				    interrupt_servers, res);
441f0393bbfSWojciech Macek 
442151c44e2SJustin Hibbits 				get_cpu_reg(cpu, &reg);
443151c44e2SJustin Hibbits 				if (reg == bsp_reg)
444151c44e2SJustin Hibbits 					bsp = tmp_cpuref_cnt;
445f0393bbfSWojciech Macek 
446151c44e2SJustin Hibbits 				for (a = 0; a < res/sizeof(cell_t); a++) {
447151c44e2SJustin Hibbits 					tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
448151c44e2SJustin Hibbits 					tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
449151c44e2SJustin Hibbits 					tmp_cpuref_cnt++;
450151c44e2SJustin Hibbits 				}
451151c44e2SJustin Hibbits 			}
452151c44e2SJustin Hibbits 		}
453151c44e2SJustin Hibbits 	}
454151c44e2SJustin Hibbits 
455151c44e2SJustin Hibbits 	if (bsp == -1)
456151c44e2SJustin Hibbits 		panic("Boot CPU not found\n");
457151c44e2SJustin Hibbits 
458151c44e2SJustin Hibbits 	/* Map IDs, so BSP has CPUID 0 regardless of hwref */
459151c44e2SJustin Hibbits 	for (a = bsp; a < tmp_cpuref_cnt; a++) {
460151c44e2SJustin Hibbits 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
461151c44e2SJustin Hibbits 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
462f0393bbfSWojciech Macek 		platform_cpuref_cnt++;
463f0393bbfSWojciech Macek 	}
464151c44e2SJustin Hibbits 	for (a = 0; a < bsp; a++) {
465151c44e2SJustin Hibbits 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
466151c44e2SJustin Hibbits 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
467151c44e2SJustin Hibbits 		platform_cpuref_cnt++;
468f0393bbfSWojciech Macek 	}
469f0393bbfSWojciech Macek 
470f0393bbfSWojciech Macek 	platform_cpuref_valid = 1;
4717a8d25c0SNathan Whitehorn 
4727a8d25c0SNathan Whitehorn 	return (0);
4737a8d25c0SNathan Whitehorn }
4747a8d25c0SNathan Whitehorn 
4757a8d25c0SNathan Whitehorn #ifdef SMP
4767a8d25c0SNathan Whitehorn static int
4777a8d25c0SNathan Whitehorn chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
4787a8d25c0SNathan Whitehorn {
4797a8d25c0SNathan Whitehorn 	cell_t start_cpu;
4807a8d25c0SNathan Whitehorn 	int result, err, timeout;
4817a8d25c0SNathan Whitehorn 
4827a8d25c0SNathan Whitehorn 	if (!rtas_exists()) {
4837a8d25c0SNathan Whitehorn 		printf("RTAS uninitialized: unable to start AP %d\n",
4847a8d25c0SNathan Whitehorn 		    pc->pc_cpuid);
4857a8d25c0SNathan Whitehorn 		return (ENXIO);
4867a8d25c0SNathan Whitehorn 	}
4877a8d25c0SNathan Whitehorn 
4887a8d25c0SNathan Whitehorn 	start_cpu = rtas_token_lookup("start-cpu");
4897a8d25c0SNathan Whitehorn 	if (start_cpu == -1) {
4907a8d25c0SNathan Whitehorn 		printf("RTAS unknown method: unable to start AP %d\n",
4917a8d25c0SNathan Whitehorn 		    pc->pc_cpuid);
4927a8d25c0SNathan Whitehorn 		return (ENXIO);
4937a8d25c0SNathan Whitehorn 	}
4947a8d25c0SNathan Whitehorn 
4957a8d25c0SNathan Whitehorn 	ap_pcpu = pc;
4967a8d25c0SNathan Whitehorn 	powerpc_sync();
4977a8d25c0SNathan Whitehorn 
498f0393bbfSWojciech Macek 	result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
4997a8d25c0SNathan Whitehorn 	    &err);
5007a8d25c0SNathan Whitehorn 	if (result < 0 || err != 0) {
5017a8d25c0SNathan Whitehorn 		printf("RTAS error (%d/%d): unable to start AP %d\n",
5027a8d25c0SNathan Whitehorn 		    result, err, pc->pc_cpuid);
5037a8d25c0SNathan Whitehorn 		return (ENXIO);
5047a8d25c0SNathan Whitehorn 	}
5057a8d25c0SNathan Whitehorn 
5067a8d25c0SNathan Whitehorn 	timeout = 10000;
5077a8d25c0SNathan Whitehorn 	while (!pc->pc_awake && timeout--)
5087a8d25c0SNathan Whitehorn 		DELAY(100);
5097a8d25c0SNathan Whitehorn 
5107a8d25c0SNathan Whitehorn 	return ((pc->pc_awake) ? 0 : EBUSY);
5117a8d25c0SNathan Whitehorn }
5127a8d25c0SNathan Whitehorn 
513bba9cbe3SConrad Meyer static void
514bba9cbe3SConrad Meyer chrp_smp_probe_threads(platform_t plat)
5157a8d25c0SNathan Whitehorn {
51609f07b00SNathan Whitehorn 	struct pcpu *pc, *last_pc;
517bba9cbe3SConrad Meyer 	int i, ncores;
5187a8d25c0SNathan Whitehorn 
519bba9cbe3SConrad Meyer 	ncores = 0;
52009f07b00SNathan Whitehorn 	last_pc = NULL;
52109f07b00SNathan Whitehorn 	for (i = 0; i <= mp_maxid; i++) {
52209f07b00SNathan Whitehorn 		pc = pcpu_find(i);
52309f07b00SNathan Whitehorn 		if (pc == NULL)
524f9d6e0a5SNathan Whitehorn 			continue;
52509f07b00SNathan Whitehorn 		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
52609f07b00SNathan Whitehorn 			ncores++;
52709f07b00SNathan Whitehorn 		last_pc = pc;
528f9d6e0a5SNathan Whitehorn 	}
529f9d6e0a5SNathan Whitehorn 
5306b83069eSConrad Meyer 	mp_ncores = ncores;
531bba9cbe3SConrad Meyer 	if (mp_ncpus % ncores == 0)
532bba9cbe3SConrad Meyer 		smp_threads_per_core = mp_ncpus / ncores;
533bba9cbe3SConrad Meyer }
5346b83069eSConrad Meyer 
535bba9cbe3SConrad Meyer static struct cpu_group *
536bba9cbe3SConrad Meyer chrp_smp_topo(platform_t plat)
537bba9cbe3SConrad Meyer {
538bba9cbe3SConrad Meyer 
539bba9cbe3SConrad Meyer 	if (mp_ncpus % mp_ncores != 0) {
5407a8d25c0SNathan Whitehorn 		printf("WARNING: Irregular SMP topology. Performance may be "
541bba9cbe3SConrad Meyer 		     "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
5427a8d25c0SNathan Whitehorn 		return (smp_topo_none());
5437a8d25c0SNathan Whitehorn 	}
5447a8d25c0SNathan Whitehorn 
5457a8d25c0SNathan Whitehorn 	/* Don't do anything fancier for non-threaded SMP */
546bba9cbe3SConrad Meyer 	if (mp_ncpus == mp_ncores)
5477a8d25c0SNathan Whitehorn 		return (smp_topo_none());
5487a8d25c0SNathan Whitehorn 
549bba9cbe3SConrad Meyer 	return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
550bba9cbe3SConrad Meyer 	    CG_FLAG_SMT));
5517a8d25c0SNathan Whitehorn }
5527a8d25c0SNathan Whitehorn #endif
5537a8d25c0SNathan Whitehorn 
5547a8d25c0SNathan Whitehorn static void
5557a8d25c0SNathan Whitehorn chrp_reset(platform_t platform)
5567a8d25c0SNathan Whitehorn {
5577a8d25c0SNathan Whitehorn 	OF_reboot();
5587a8d25c0SNathan Whitehorn }
5597a8d25c0SNathan Whitehorn 
5607a8d25c0SNathan Whitehorn #ifdef __powerpc64__
5617a8d25c0SNathan Whitehorn static void
5627a8d25c0SNathan Whitehorn phyp_cpu_idle(sbintime_t sbt)
5637a8d25c0SNathan Whitehorn {
564e21d69e9SNathan Whitehorn 	register_t msr;
565e21d69e9SNathan Whitehorn 
566e21d69e9SNathan Whitehorn 	msr = mfmsr();
567e21d69e9SNathan Whitehorn 
568e21d69e9SNathan Whitehorn 	mtmsr(msr & ~PSL_EE);
569e21d69e9SNathan Whitehorn 	if (sched_runnable()) {
570e21d69e9SNathan Whitehorn 		mtmsr(msr);
571e21d69e9SNathan Whitehorn 		return;
572e21d69e9SNathan Whitehorn 	}
573e21d69e9SNathan Whitehorn 
574e21d69e9SNathan Whitehorn 	phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
575e21d69e9SNathan Whitehorn 	mtmsr(msr);
5767a8d25c0SNathan Whitehorn }
5777a8d25c0SNathan Whitehorn 
5787a8d25c0SNathan Whitehorn static void
5797a8d25c0SNathan Whitehorn chrp_smp_ap_init(platform_t platform)
5807a8d25c0SNathan Whitehorn {
5817a8d25c0SNathan Whitehorn 	if (!(mfmsr() & PSL_HV)) {
582f1e48417SNathan Whitehorn 		/* Register VPA */
583f0393bbfSWojciech Macek 		phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
584f0393bbfSWojciech Macek 		    splpar_vpa[PCPU_GET(hwref)]);
585f1e48417SNathan Whitehorn 
5867a8d25c0SNathan Whitehorn 		/* Set interrupt priority */
5877a8d25c0SNathan Whitehorn 		phyp_hcall(H_CPPR, 0xff);
5887a8d25c0SNathan Whitehorn 	}
5897a8d25c0SNathan Whitehorn }
5907a8d25c0SNathan Whitehorn #else
5917a8d25c0SNathan Whitehorn static void
5927a8d25c0SNathan Whitehorn chrp_smp_ap_init(platform_t platform)
5937a8d25c0SNathan Whitehorn {
5947a8d25c0SNathan Whitehorn }
5957a8d25c0SNathan Whitehorn #endif
596