xref: /freebsd/sys/powerpc/powernv/platform_powernv.c (revision 60fde7ce5d7bf5d94290720ea53db5701ab406a8)
1 /*-
2  * Copyright (c) 2015 Nathan Whitehorn
3  * Copyright (c) 2017-2018 Semihalf
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/bus.h>
35 #include <sys/pcpu.h>
36 #include <sys/proc.h>
37 #include <sys/smp.h>
38 #include <vm/vm.h>
39 #include <vm/pmap.h>
40 
41 #include <machine/bus.h>
42 #include <machine/cpu.h>
43 #include <machine/hid.h>
44 #include <machine/platformvar.h>
45 #include <machine/pmap.h>
46 #include <machine/rtas.h>
47 #include <machine/smp.h>
48 #include <machine/spr.h>
49 #include <machine/trap.h>
50 
51 #include <dev/ofw/openfirm.h>
52 #include <machine/ofw_machdep.h>
53 #include <powerpc/aim/mmu_oea64.h>
54 
55 #include "platform_if.h"
56 #include "opal.h"
57 
58 #ifdef SMP
59 extern void *ap_pcpu;
60 #endif
61 
62 static int powernv_probe(platform_t);
63 static int powernv_attach(platform_t);
64 void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
65     struct mem_region *avail, int *availsz);
66 static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref);
67 static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref);
68 static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref);
69 static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref);
70 static void powernv_smp_ap_init(platform_t);
71 #ifdef SMP
72 static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu);
73 static struct cpu_group *powernv_smp_topo(platform_t plat);
74 #endif
75 static void powernv_reset(platform_t);
76 static void powernv_cpu_idle(sbintime_t sbt);
77 static int powernv_cpuref_init(void);
78 
79 static platform_method_t powernv_methods[] = {
80 	PLATFORMMETHOD(platform_probe, 		powernv_probe),
81 	PLATFORMMETHOD(platform_attach,		powernv_attach),
82 	PLATFORMMETHOD(platform_mem_regions,	powernv_mem_regions),
83 	PLATFORMMETHOD(platform_timebase_freq,	powernv_timebase_freq),
84 
85 	PLATFORMMETHOD(platform_smp_ap_init,	powernv_smp_ap_init),
86 	PLATFORMMETHOD(platform_smp_first_cpu,	powernv_smp_first_cpu),
87 	PLATFORMMETHOD(platform_smp_next_cpu,	powernv_smp_next_cpu),
88 	PLATFORMMETHOD(platform_smp_get_bsp,	powernv_smp_get_bsp),
89 #ifdef SMP
90 	PLATFORMMETHOD(platform_smp_start_cpu,	powernv_smp_start_cpu),
91 	PLATFORMMETHOD(platform_smp_topo,	powernv_smp_topo),
92 #endif
93 
94 	PLATFORMMETHOD(platform_reset,		powernv_reset),
95 
96 	{ 0, 0 }
97 };
98 
99 static platform_def_t powernv_platform = {
100 	"powernv",
101 	powernv_methods,
102 	0
103 };
104 
105 static struct cpuref platform_cpuref[MAXCPU];
106 static int platform_cpuref_cnt;
107 static int platform_cpuref_valid;
108 
109 PLATFORM_DEF(powernv_platform);
110 
111 static uint64_t powernv_boot_pir;
112 
113 static int
114 powernv_probe(platform_t plat)
115 {
116 	if (opal_check() == 0)
117 		return (BUS_PROBE_SPECIFIC);
118 
119 	return (ENXIO);
120 }
121 
122 static int
123 powernv_attach(platform_t plat)
124 {
125 	uint32_t nptlp, shift = 0, slb_encoding = 0;
126 	int32_t lp_size, lp_encoding;
127 	char buf[255];
128 	pcell_t prop;
129 	phandle_t cpu;
130 	int res, len, idx;
131 	register_t msr;
132 
133 	/* Ping OPAL again just to make sure */
134 	opal_check();
135 
136 #if BYTE_ORDER == LITTLE_ENDIAN
137 	opal_call(OPAL_REINIT_CPUS, 2 /* Little endian */);
138 #else
139 	opal_call(OPAL_REINIT_CPUS, 1 /* Big endian */);
140 #endif
141 
142        if (cpu_idle_hook == NULL)
143                 cpu_idle_hook = powernv_cpu_idle;
144 
145 	powernv_boot_pir = mfspr(SPR_PIR);
146 
147 	/* LPID must not be altered when PSL_DR or PSL_IR is set */
148 	msr = mfmsr();
149 	mtmsr(msr & ~(PSL_DR | PSL_IR));
150 
151 	/* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */
152 	mtspr(SPR_LPID, 0);
153 	isync();
154 
155 	mtspr(SPR_LPCR, LPCR_LPES);
156 	isync();
157 
158 	mtmsr(msr);
159 
160 	/* Init CPU bits */
161 	powernv_smp_ap_init(plat);
162 
163 	powernv_cpuref_init();
164 
165 	/* Set SLB count from device tree */
166 	cpu = OF_peer(0);
167 	cpu = OF_child(cpu);
168 	while (cpu != 0) {
169 		res = OF_getprop(cpu, "name", buf, sizeof(buf));
170 		if (res > 0 && strcmp(buf, "cpus") == 0)
171 			break;
172 		cpu = OF_peer(cpu);
173 	}
174 	if (cpu == 0)
175 		goto out;
176 
177 	cpu = OF_child(cpu);
178 	while (cpu != 0) {
179 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
180 		if (res > 0 && strcmp(buf, "cpu") == 0)
181 			break;
182 		cpu = OF_peer(cpu);
183 	}
184 	if (cpu == 0)
185 		goto out;
186 
187 	res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop));
188 	if (res > 0)
189 		n_slbs = prop;
190 
191 	/*
192 	 * Scan the large page size property for PAPR compatible machines.
193 	 * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties'
194 	 * for the encoding of the property.
195 	 */
196 
197 	len = OF_getproplen(cpu, "ibm,segment-page-sizes");
198 	if (len > 0) {
199 		/*
200 		 * We have to use a variable length array on the stack
201 		 * since we have very limited stack space.
202 		 */
203 		pcell_t arr[len/sizeof(cell_t)];
204 		res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr,
205 		    sizeof(arr));
206 		len /= 4;
207 		idx = 0;
208 		while (len > 0) {
209 			shift = arr[idx];
210 			slb_encoding = arr[idx + 1];
211 			nptlp = arr[idx + 2];
212 			idx += 3;
213 			len -= 3;
214 			while (len > 0 && nptlp) {
215 				lp_size = arr[idx];
216 				lp_encoding = arr[idx+1];
217 				if (slb_encoding == SLBV_L && lp_encoding == 0)
218 					break;
219 
220 				idx += 2;
221 				len -= 2;
222 				nptlp--;
223 			}
224 			if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0)
225 				break;
226 		}
227 
228 		if (len == 0)
229 			panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) "
230 			    "not supported by this system.");
231 
232 		moea64_large_page_shift = shift;
233 		moea64_large_page_size = 1ULL << lp_size;
234 	}
235 
236 out:
237 	return (0);
238 }
239 
240 
241 void
242 powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
243     struct mem_region *avail, int *availsz)
244 {
245 
246 	ofw_mem_regions(phys, physsz, avail, availsz);
247 }
248 
249 static u_long
250 powernv_timebase_freq(platform_t plat, struct cpuref *cpuref)
251 {
252 	char buf[8];
253 	phandle_t cpu, dev, root;
254 	int res;
255 	int32_t ticks = -1;
256 
257 	root = OF_peer(0);
258 	dev = OF_child(root);
259 	while (dev != 0) {
260 		res = OF_getprop(dev, "name", buf, sizeof(buf));
261 		if (res > 0 && strcmp(buf, "cpus") == 0)
262 			break;
263 		dev = OF_peer(dev);
264 	}
265 
266 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
267 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
268 		if (res > 0 && strcmp(buf, "cpu") == 0)
269 			break;
270 	}
271 	if (cpu == 0)
272 		return (512000000);
273 
274 	OF_getencprop(cpu, "timebase-frequency", &ticks, sizeof(ticks));
275 
276 	if (ticks <= 0)
277 		panic("Unable to determine timebase frequency!");
278 
279 	return (ticks);
280 
281 }
282 
283 static int
284 powernv_cpuref_init(void)
285 {
286 	phandle_t cpu, dev;
287 	char buf[32];
288 	int a, res, tmp_cpuref_cnt;
289 	static struct cpuref tmp_cpuref[MAXCPU];
290 	cell_t interrupt_servers[32];
291 	uint64_t bsp;
292 
293 	if (platform_cpuref_valid)
294 		return (0);
295 
296 	dev = OF_peer(0);
297 	dev = OF_child(dev);
298 	while (dev != 0) {
299 		res = OF_getprop(dev, "name", buf, sizeof(buf));
300 		if (res > 0 && strcmp(buf, "cpus") == 0)
301 			break;
302 		dev = OF_peer(dev);
303 	}
304 
305 	bsp = 0;
306 	tmp_cpuref_cnt = 0;
307 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
308 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
309 		if (res > 0 && strcmp(buf, "cpu") == 0) {
310 			res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
311 			if (res > 0) {
312 
313 
314 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
315 				    interrupt_servers, res);
316 
317 				for (a = 0; a < res/sizeof(cell_t); a++) {
318 					tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
319 					tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
320 
321 					if (interrupt_servers[a] == (uint32_t)powernv_boot_pir)
322 						bsp = tmp_cpuref_cnt;
323 
324 					tmp_cpuref_cnt++;
325 				}
326 			}
327 		}
328 	}
329 
330 	/* Map IDs, so BSP has CPUID 0 regardless of hwref */
331 	for (a = bsp; a < tmp_cpuref_cnt; a++) {
332 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
333 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
334 		platform_cpuref_cnt++;
335 	}
336 	for (a = 0; a < bsp; a++) {
337 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
338 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
339 		platform_cpuref_cnt++;
340 	}
341 
342 	platform_cpuref_valid = 1;
343 
344 	return (0);
345 }
346 
347 static int
348 powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
349 {
350 	if (platform_cpuref_valid == 0)
351 		return (EINVAL);
352 
353 	cpuref->cr_cpuid = 0;
354 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
355 
356 	return (0);
357 }
358 
359 static int
360 powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
361 {
362 	int id;
363 
364 	if (platform_cpuref_valid == 0)
365 		return (EINVAL);
366 
367 	id = cpuref->cr_cpuid + 1;
368 	if (id >= platform_cpuref_cnt)
369 		return (ENOENT);
370 
371 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
372 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
373 
374 	return (0);
375 }
376 
377 static int
378 powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
379 {
380 
381 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
382 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
383 	return (0);
384 }
385 
386 #ifdef SMP
387 static int
388 powernv_smp_start_cpu(platform_t plat, struct pcpu *pc)
389 {
390 	int result;
391 
392 	ap_pcpu = pc;
393 	powerpc_sync();
394 
395 	result = opal_call(OPAL_START_CPU, pc->pc_hwref, EXC_RST);
396 	if (result != OPAL_SUCCESS) {
397 		printf("OPAL error (%d): unable to start AP %d\n",
398 		    result, (int)pc->pc_hwref);
399 		return (ENXIO);
400 	}
401 
402 	return (0);
403 }
404 
405 static struct cpu_group *
406 powernv_smp_topo(platform_t plat)
407 {
408 	char buf[8];
409 	phandle_t cpu, dev, root;
410 	int res, nthreads;
411 
412 	root = OF_peer(0);
413 
414 	dev = OF_child(root);
415 	while (dev != 0) {
416 		res = OF_getprop(dev, "name", buf, sizeof(buf));
417 		if (res > 0 && strcmp(buf, "cpus") == 0)
418 			break;
419 		dev = OF_peer(dev);
420 	}
421 
422 	nthreads = 1;
423 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
424 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
425 		if (res <= 0 || strcmp(buf, "cpu") != 0)
426 			continue;
427 
428 		res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
429 
430 		if (res >= 0)
431 			nthreads = res / sizeof(cell_t);
432 		else
433 			nthreads = 1;
434 		break;
435 	}
436 
437 	if (mp_ncpus % nthreads != 0) {
438 		printf("WARNING: Irregular SMP topology. Performance may be "
439 		     "suboptimal (%d threads, %d on first core)\n",
440 		     mp_ncpus, nthreads);
441 		return (smp_topo_none());
442 	}
443 
444 	/* Don't do anything fancier for non-threaded SMP */
445 	if (nthreads == 1)
446 		return (smp_topo_none());
447 
448 	return (smp_topo_1level(CG_SHARE_L1, nthreads, CG_FLAG_SMT));
449 }
450 
451 #endif
452 
453 static void
454 powernv_reset(platform_t platform)
455 {
456 
457 	opal_call(OPAL_CEC_REBOOT);
458 }
459 
460 static void
461 powernv_smp_ap_init(platform_t platform)
462 {
463 }
464 
465 static void
466 powernv_cpu_idle(sbintime_t sbt)
467 {
468 }
469