xref: /freebsd/sys/powerpc/powernv/platform_powernv.c (revision 01d7bda7b7fd540b73a9e7c1a3f2914649d4b518)
1fb3855e0SWojciech Macek /*-
2fb3855e0SWojciech Macek  * Copyright (c) 2015 Nathan Whitehorn
3fb3855e0SWojciech Macek  * All rights reserved.
4fb3855e0SWojciech Macek  *
5fb3855e0SWojciech Macek  * Redistribution and use in source and binary forms, with or without
6fb3855e0SWojciech Macek  * modification, are permitted provided that the following conditions
7fb3855e0SWojciech Macek  * are met:
8fb3855e0SWojciech Macek  *
9fb3855e0SWojciech Macek  * 1. Redistributions of source code must retain the above copyright
10fb3855e0SWojciech Macek  *    notice, this list of conditions and the following disclaimer.
11fb3855e0SWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
12fb3855e0SWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
13fb3855e0SWojciech Macek  *    documentation and/or other materials provided with the distribution.
14fb3855e0SWojciech Macek  *
15fb3855e0SWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16fb3855e0SWojciech Macek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17fb3855e0SWojciech Macek  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18fb3855e0SWojciech Macek  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19fb3855e0SWojciech Macek  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20fb3855e0SWojciech Macek  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21fb3855e0SWojciech Macek  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22fb3855e0SWojciech Macek  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23fb3855e0SWojciech Macek  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24fb3855e0SWojciech Macek  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25fb3855e0SWojciech Macek  */
26fb3855e0SWojciech Macek 
27fb3855e0SWojciech Macek #include <sys/cdefs.h>
28fb3855e0SWojciech Macek __FBSDID("$FreeBSD$");
29fb3855e0SWojciech Macek 
30fb3855e0SWojciech Macek #include <sys/param.h>
31fb3855e0SWojciech Macek #include <sys/systm.h>
32fb3855e0SWojciech Macek #include <sys/kernel.h>
33fb3855e0SWojciech Macek #include <sys/bus.h>
34fb3855e0SWojciech Macek #include <sys/pcpu.h>
35fb3855e0SWojciech Macek #include <sys/proc.h>
36fb3855e0SWojciech Macek #include <sys/smp.h>
37fb3855e0SWojciech Macek #include <vm/vm.h>
38fb3855e0SWojciech Macek #include <vm/pmap.h>
39fb3855e0SWojciech Macek 
40fb3855e0SWojciech Macek #include <machine/bus.h>
41fb3855e0SWojciech Macek #include <machine/cpu.h>
42fb3855e0SWojciech Macek #include <machine/hid.h>
43fb3855e0SWojciech Macek #include <machine/platformvar.h>
44fb3855e0SWojciech Macek #include <machine/pmap.h>
45fb3855e0SWojciech Macek #include <machine/rtas.h>
46fb3855e0SWojciech Macek #include <machine/smp.h>
47fb3855e0SWojciech Macek #include <machine/spr.h>
48fb3855e0SWojciech Macek #include <machine/trap.h>
49fb3855e0SWojciech Macek 
50fb3855e0SWojciech Macek #include <dev/ofw/openfirm.h>
51fb3855e0SWojciech Macek #include <machine/ofw_machdep.h>
52fb3855e0SWojciech Macek 
53fb3855e0SWojciech Macek #include "platform_if.h"
54fb3855e0SWojciech Macek #include "opal.h"
55fb3855e0SWojciech Macek 
56fb3855e0SWojciech Macek #ifdef SMP
57fb3855e0SWojciech Macek extern void *ap_pcpu;
58fb3855e0SWojciech Macek #endif
59fb3855e0SWojciech Macek 
60fb3855e0SWojciech Macek static int powernv_probe(platform_t);
61fb3855e0SWojciech Macek static int powernv_attach(platform_t);
62fb3855e0SWojciech Macek void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
63fb3855e0SWojciech Macek     struct mem_region *avail, int *availsz);
64fb3855e0SWojciech Macek static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref);
65fb3855e0SWojciech Macek static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref);
66fb3855e0SWojciech Macek static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref);
67fb3855e0SWojciech Macek static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref);
68fb3855e0SWojciech Macek static void powernv_smp_ap_init(platform_t);
69fb3855e0SWojciech Macek #ifdef SMP
70fb3855e0SWojciech Macek static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu);
71fb3855e0SWojciech Macek static struct cpu_group *powernv_smp_topo(platform_t plat);
72fb3855e0SWojciech Macek #endif
73fb3855e0SWojciech Macek static void powernv_reset(platform_t);
74*01d7bda7SWojciech Macek static void powernv_cpu_idle(sbintime_t sbt);
75fb3855e0SWojciech Macek 
76fb3855e0SWojciech Macek static platform_method_t powernv_methods[] = {
77fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_probe, 		powernv_probe),
78fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_attach,		powernv_attach),
79fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_mem_regions,	powernv_mem_regions),
80fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_timebase_freq,	powernv_timebase_freq),
81fb3855e0SWojciech Macek 
82fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_ap_init,	powernv_smp_ap_init),
83fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_first_cpu,	powernv_smp_first_cpu),
84fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_next_cpu,	powernv_smp_next_cpu),
85fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_get_bsp,	powernv_smp_get_bsp),
86fb3855e0SWojciech Macek #ifdef SMP
87fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_start_cpu,	powernv_smp_start_cpu),
88fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_smp_topo,	powernv_smp_topo),
89fb3855e0SWojciech Macek #endif
90fb3855e0SWojciech Macek 
91fb3855e0SWojciech Macek 	PLATFORMMETHOD(platform_reset,		powernv_reset),
92fb3855e0SWojciech Macek 
93fb3855e0SWojciech Macek 	{ 0, 0 }
94fb3855e0SWojciech Macek };
95fb3855e0SWojciech Macek 
96fb3855e0SWojciech Macek static platform_def_t powernv_platform = {
97fb3855e0SWojciech Macek 	"powernv",
98fb3855e0SWojciech Macek 	powernv_methods,
99fb3855e0SWojciech Macek 	0
100fb3855e0SWojciech Macek };
101fb3855e0SWojciech Macek 
102fb3855e0SWojciech Macek PLATFORM_DEF(powernv_platform);
103fb3855e0SWojciech Macek 
104fb3855e0SWojciech Macek static int
105fb3855e0SWojciech Macek powernv_probe(platform_t plat)
106fb3855e0SWojciech Macek {
107fb3855e0SWojciech Macek 	if (opal_check() == 0)
108fb3855e0SWojciech Macek 		return (BUS_PROBE_SPECIFIC);
109fb3855e0SWojciech Macek 
110fb3855e0SWojciech Macek 	return (ENXIO);
111fb3855e0SWojciech Macek }
112fb3855e0SWojciech Macek 
113fb3855e0SWojciech Macek static int
114fb3855e0SWojciech Macek powernv_attach(platform_t plat)
115fb3855e0SWojciech Macek {
116fb3855e0SWojciech Macek 	/* Ping OPAL again just to make sure */
117fb3855e0SWojciech Macek 	opal_check();
118fb3855e0SWojciech Macek 
119*01d7bda7SWojciech Macek 	cpu_idle_hook = powernv_cpu_idle;
120*01d7bda7SWojciech Macek 
121fb3855e0SWojciech Macek 	return (0);
122fb3855e0SWojciech Macek }
123fb3855e0SWojciech Macek 
124fb3855e0SWojciech Macek void
125fb3855e0SWojciech Macek powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
126fb3855e0SWojciech Macek     struct mem_region *avail, int *availsz)
127fb3855e0SWojciech Macek {
128fb3855e0SWojciech Macek 
129fb3855e0SWojciech Macek 	ofw_mem_regions(phys, physsz, avail, availsz);
130fb3855e0SWojciech Macek }
131fb3855e0SWojciech Macek 
132fb3855e0SWojciech Macek static u_long
133fb3855e0SWojciech Macek powernv_timebase_freq(platform_t plat, struct cpuref *cpuref)
134fb3855e0SWojciech Macek {
135fb3855e0SWojciech Macek 	phandle_t phandle;
136fb3855e0SWojciech Macek 	int32_t ticks = -1;
137fb3855e0SWojciech Macek 
138fb3855e0SWojciech Macek 	phandle = cpuref->cr_hwref;
139fb3855e0SWojciech Macek 
140fb3855e0SWojciech Macek 	OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
141fb3855e0SWojciech Macek 
142fb3855e0SWojciech Macek 	if (ticks <= 0)
143fb3855e0SWojciech Macek 		panic("Unable to determine timebase frequency!");
144fb3855e0SWojciech Macek 
145fb3855e0SWojciech Macek 	return (ticks);
146fb3855e0SWojciech Macek }
147fb3855e0SWojciech Macek 
148fb3855e0SWojciech Macek static int
149fb3855e0SWojciech Macek powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
150fb3855e0SWojciech Macek {
151fb3855e0SWojciech Macek 	char buf[8];
152fb3855e0SWojciech Macek 	phandle_t cpu, dev, root;
153fb3855e0SWojciech Macek 	int res, cpuid;
154fb3855e0SWojciech Macek 
155fb3855e0SWojciech Macek 	root = OF_peer(0);
156fb3855e0SWojciech Macek 
157fb3855e0SWojciech Macek 	dev = OF_child(root);
158fb3855e0SWojciech Macek 	while (dev != 0) {
159fb3855e0SWojciech Macek 		res = OF_getprop(dev, "name", buf, sizeof(buf));
160fb3855e0SWojciech Macek 		if (res > 0 && strcmp(buf, "cpus") == 0)
161fb3855e0SWojciech Macek 			break;
162fb3855e0SWojciech Macek 		dev = OF_peer(dev);
163fb3855e0SWojciech Macek 	}
164fb3855e0SWojciech Macek 	if (dev == 0) {
165fb3855e0SWojciech Macek 		/*
166fb3855e0SWojciech Macek 		 * psim doesn't have a name property on the /cpus node,
167fb3855e0SWojciech Macek 		 * but it can be found directly
168fb3855e0SWojciech Macek 		 */
169fb3855e0SWojciech Macek 		dev = OF_finddevice("/cpus");
170fb3855e0SWojciech Macek 		if (dev == 0)
171fb3855e0SWojciech Macek 			return (ENOENT);
172fb3855e0SWojciech Macek 	}
173fb3855e0SWojciech Macek 
174fb3855e0SWojciech Macek 	cpu = OF_child(dev);
175fb3855e0SWojciech Macek 
176fb3855e0SWojciech Macek 	while (cpu != 0) {
177fb3855e0SWojciech Macek 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
178fb3855e0SWojciech Macek 		if (res > 0 && strcmp(buf, "cpu") == 0)
179fb3855e0SWojciech Macek 			break;
180fb3855e0SWojciech Macek 		cpu = OF_peer(cpu);
181fb3855e0SWojciech Macek 	}
182fb3855e0SWojciech Macek 	if (cpu == 0)
183fb3855e0SWojciech Macek 		return (ENOENT);
184fb3855e0SWojciech Macek 
185fb3855e0SWojciech Macek 	cpuref->cr_hwref = cpu;
186fb3855e0SWojciech Macek 	res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
187fb3855e0SWojciech Macek 	    sizeof(cpuid));
188fb3855e0SWojciech Macek 	if (res <= 0)
189fb3855e0SWojciech Macek 		res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
190fb3855e0SWojciech Macek 	if (res <= 0)
191fb3855e0SWojciech Macek 		cpuid = 0;
192fb3855e0SWojciech Macek 	cpuref->cr_cpuid = cpuid;
193fb3855e0SWojciech Macek 
194fb3855e0SWojciech Macek 	return (0);
195fb3855e0SWojciech Macek }
196fb3855e0SWojciech Macek 
197fb3855e0SWojciech Macek static int
198fb3855e0SWojciech Macek powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
199fb3855e0SWojciech Macek {
200fb3855e0SWojciech Macek 	char buf[8];
201fb3855e0SWojciech Macek 	phandle_t cpu;
202fb3855e0SWojciech Macek 	int i, res, cpuid;
203fb3855e0SWojciech Macek 
204fb3855e0SWojciech Macek 	/* Check for whether it should be the next thread */
205fb3855e0SWojciech Macek 	res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
206fb3855e0SWojciech Macek 	if (res > 0) {
207fb3855e0SWojciech Macek 		cell_t interrupt_servers[res/sizeof(cell_t)];
208fb3855e0SWojciech Macek 		OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
209fb3855e0SWojciech Macek 		    interrupt_servers, res);
210fb3855e0SWojciech Macek 		for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
211fb3855e0SWojciech Macek 			if (interrupt_servers[i] == cpuref->cr_cpuid) {
212fb3855e0SWojciech Macek 				cpuref->cr_cpuid = interrupt_servers[i+1];
213fb3855e0SWojciech Macek 				return (0);
214fb3855e0SWojciech Macek 			}
215fb3855e0SWojciech Macek 		}
216fb3855e0SWojciech Macek 	}
217fb3855e0SWojciech Macek 
218fb3855e0SWojciech Macek 	/* Next CPU core/package */
219fb3855e0SWojciech Macek 	cpu = OF_peer(cpuref->cr_hwref);
220fb3855e0SWojciech Macek 	while (cpu != 0) {
221fb3855e0SWojciech Macek 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
222fb3855e0SWojciech Macek 		if (res > 0 && strcmp(buf, "cpu") == 0)
223fb3855e0SWojciech Macek 			break;
224fb3855e0SWojciech Macek 		cpu = OF_peer(cpu);
225fb3855e0SWojciech Macek 	}
226fb3855e0SWojciech Macek 	if (cpu == 0)
227fb3855e0SWojciech Macek 		return (ENOENT);
228fb3855e0SWojciech Macek 
229fb3855e0SWojciech Macek 	cpuref->cr_hwref = cpu;
230fb3855e0SWojciech Macek 	res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
231fb3855e0SWojciech Macek 	    sizeof(cpuid));
232fb3855e0SWojciech Macek 	if (res <= 0)
233fb3855e0SWojciech Macek 		res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
234fb3855e0SWojciech Macek 	if (res <= 0)
235fb3855e0SWojciech Macek 		cpuid = 0;
236fb3855e0SWojciech Macek 	cpuref->cr_cpuid = cpuid;
237fb3855e0SWojciech Macek 
238fb3855e0SWojciech Macek 	return (0);
239fb3855e0SWojciech Macek }
240fb3855e0SWojciech Macek 
241fb3855e0SWojciech Macek static int
242fb3855e0SWojciech Macek powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
243fb3855e0SWojciech Macek {
244fb3855e0SWojciech Macek 	phandle_t chosen;
245fb3855e0SWojciech Macek 	int cpuid, res;
246fb3855e0SWojciech Macek 	struct cpuref i;
247fb3855e0SWojciech Macek 
248fb3855e0SWojciech Macek 	chosen = OF_finddevice("/chosen");
249fb3855e0SWojciech Macek 	if (chosen == 0)
250fb3855e0SWojciech Macek 		return (ENOENT);
251fb3855e0SWojciech Macek 
252fb3855e0SWojciech Macek 	res = OF_getencprop(chosen, "fdtbootcpu", &cpuid, sizeof(cpuid));
253fb3855e0SWojciech Macek 	if (res < 0)
254fb3855e0SWojciech Macek 		return (ENOENT);
255fb3855e0SWojciech Macek 
256fb3855e0SWojciech Macek 	cpuref->cr_cpuid = cpuid;
257fb3855e0SWojciech Macek 
258fb3855e0SWojciech Macek 	if (powernv_smp_first_cpu(plat, &i) != 0)
259fb3855e0SWojciech Macek 		return (ENOENT);
260fb3855e0SWojciech Macek 	cpuref->cr_hwref = i.cr_hwref;
261fb3855e0SWojciech Macek 
262fb3855e0SWojciech Macek 	do {
263fb3855e0SWojciech Macek 		if (i.cr_cpuid == cpuid) {
264fb3855e0SWojciech Macek 			cpuref->cr_hwref = i.cr_hwref;
265fb3855e0SWojciech Macek 			break;
266fb3855e0SWojciech Macek 		}
267fb3855e0SWojciech Macek 	} while (powernv_smp_next_cpu(plat, &i) == 0);
268fb3855e0SWojciech Macek 
269fb3855e0SWojciech Macek 	return (0);
270fb3855e0SWojciech Macek }
271fb3855e0SWojciech Macek 
272fb3855e0SWojciech Macek #ifdef SMP
273fb3855e0SWojciech Macek static int
274fb3855e0SWojciech Macek powernv_smp_start_cpu(platform_t plat, struct pcpu *pc)
275fb3855e0SWojciech Macek {
276*01d7bda7SWojciech Macek 	int result;
277fb3855e0SWojciech Macek 
278fb3855e0SWojciech Macek 	ap_pcpu = pc;
279fb3855e0SWojciech Macek 	powerpc_sync();
280fb3855e0SWojciech Macek 
281fb3855e0SWojciech Macek 	result = opal_call(OPAL_START_CPU, pc->pc_cpuid, EXC_RST);
282*01d7bda7SWojciech Macek 	if (result != OPAL_SUCCESS) {
283*01d7bda7SWojciech Macek 		printf("OPAL error (%d): unable to start AP %d\n",
284*01d7bda7SWojciech Macek 		    result, pc->pc_cpuid);
285fb3855e0SWojciech Macek 		return (ENXIO);
286fb3855e0SWojciech Macek 	}
287fb3855e0SWojciech Macek 
288*01d7bda7SWojciech Macek 	return (0);
289fb3855e0SWojciech Macek }
290fb3855e0SWojciech Macek 
291fb3855e0SWojciech Macek static struct cpu_group *
292fb3855e0SWojciech Macek powernv_smp_topo(platform_t plat)
293fb3855e0SWojciech Macek {
294fb3855e0SWojciech Macek 	struct pcpu *pc, *last_pc;
295fb3855e0SWojciech Macek 	int i, ncores, ncpus;
296fb3855e0SWojciech Macek 
297fb3855e0SWojciech Macek 	ncores = ncpus = 0;
298fb3855e0SWojciech Macek 	last_pc = NULL;
299fb3855e0SWojciech Macek 	for (i = 0; i <= mp_maxid; i++) {
300fb3855e0SWojciech Macek 		pc = pcpu_find(i);
301fb3855e0SWojciech Macek 		if (pc == NULL)
302fb3855e0SWojciech Macek 			continue;
303fb3855e0SWojciech Macek 		if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
304fb3855e0SWojciech Macek 			ncores++;
305fb3855e0SWojciech Macek 		last_pc = pc;
306fb3855e0SWojciech Macek 		ncpus++;
307fb3855e0SWojciech Macek 	}
308fb3855e0SWojciech Macek 
309fb3855e0SWojciech Macek 	if (ncpus % ncores != 0) {
310fb3855e0SWojciech Macek 		printf("WARNING: Irregular SMP topology. Performance may be "
311fb3855e0SWojciech Macek 		     "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
312fb3855e0SWojciech Macek 		return (smp_topo_none());
313fb3855e0SWojciech Macek 	}
314fb3855e0SWojciech Macek 
315fb3855e0SWojciech Macek 	/* Don't do anything fancier for non-threaded SMP */
316fb3855e0SWojciech Macek 	if (ncpus == ncores)
317fb3855e0SWojciech Macek 		return (smp_topo_none());
318fb3855e0SWojciech Macek 
319fb3855e0SWojciech Macek 	return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
320fb3855e0SWojciech Macek }
321fb3855e0SWojciech Macek #endif
322fb3855e0SWojciech Macek 
323fb3855e0SWojciech Macek static void
324fb3855e0SWojciech Macek powernv_reset(platform_t platform)
325fb3855e0SWojciech Macek {
32632d1354aSWojciech Macek 
32732d1354aSWojciech Macek 	opal_call(OPAL_CEC_REBOOT);
328fb3855e0SWojciech Macek }
329fb3855e0SWojciech Macek 
330fb3855e0SWojciech Macek static void
331fb3855e0SWojciech Macek powernv_smp_ap_init(platform_t platform)
332fb3855e0SWojciech Macek {
333fb3855e0SWojciech Macek }
334fb3855e0SWojciech Macek 
335*01d7bda7SWojciech Macek static void
336*01d7bda7SWojciech Macek powernv_cpu_idle(sbintime_t sbt)
337*01d7bda7SWojciech Macek {
338*01d7bda7SWojciech Macek }
339