xref: /freebsd/sys/powerpc/powernv/platform_powernv.c (revision 3c5ba95ad12285ad37c182a4bfc1b240ec6d18a7)
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 extern void xicp_smp_cpu_startup(void);
63 static int powernv_probe(platform_t);
64 static int powernv_attach(platform_t);
65 void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
66     struct mem_region *avail, int *availsz);
67 static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref);
68 static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref);
69 static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref);
70 static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref);
71 static void powernv_smp_ap_init(platform_t);
72 #ifdef SMP
73 static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu);
74 static struct cpu_group *powernv_smp_topo(platform_t plat);
75 #endif
76 static void powernv_reset(platform_t);
77 static void powernv_cpu_idle(sbintime_t sbt);
78 static int powernv_cpuref_init(void);
79 
80 static platform_method_t powernv_methods[] = {
81 	PLATFORMMETHOD(platform_probe, 		powernv_probe),
82 	PLATFORMMETHOD(platform_attach,		powernv_attach),
83 	PLATFORMMETHOD(platform_mem_regions,	powernv_mem_regions),
84 	PLATFORMMETHOD(platform_timebase_freq,	powernv_timebase_freq),
85 
86 	PLATFORMMETHOD(platform_smp_ap_init,	powernv_smp_ap_init),
87 	PLATFORMMETHOD(platform_smp_first_cpu,	powernv_smp_first_cpu),
88 	PLATFORMMETHOD(platform_smp_next_cpu,	powernv_smp_next_cpu),
89 	PLATFORMMETHOD(platform_smp_get_bsp,	powernv_smp_get_bsp),
90 #ifdef SMP
91 	PLATFORMMETHOD(platform_smp_start_cpu,	powernv_smp_start_cpu),
92 	PLATFORMMETHOD(platform_smp_topo,	powernv_smp_topo),
93 #endif
94 
95 	PLATFORMMETHOD(platform_reset,		powernv_reset),
96 
97 	{ 0, 0 }
98 };
99 
100 static platform_def_t powernv_platform = {
101 	"powernv",
102 	powernv_methods,
103 	0
104 };
105 
106 static struct cpuref platform_cpuref[MAXCPU];
107 static int platform_cpuref_cnt;
108 static int platform_cpuref_valid;
109 
110 PLATFORM_DEF(powernv_platform);
111 
112 static uint64_t powernv_boot_pir;
113 
114 static int
115 powernv_probe(platform_t plat)
116 {
117 	if (opal_check() == 0)
118 		return (BUS_PROBE_SPECIFIC);
119 
120 	return (ENXIO);
121 }
122 
123 static int
124 powernv_attach(platform_t plat)
125 {
126 	uint32_t nptlp, shift = 0, slb_encoding = 0;
127 	int32_t lp_size, lp_encoding;
128 	char buf[255];
129 	pcell_t prop;
130 	phandle_t cpu;
131 	int res, len, idx;
132 	register_t msr;
133 
134 	/* Ping OPAL again just to make sure */
135 	opal_check();
136 
137 #if BYTE_ORDER == LITTLE_ENDIAN
138 	opal_call(OPAL_REINIT_CPUS, 2 /* Little endian */);
139 #else
140 	opal_call(OPAL_REINIT_CPUS, 1 /* Big endian */);
141 #endif
142 
143        if (cpu_idle_hook == NULL)
144                 cpu_idle_hook = powernv_cpu_idle;
145 
146 	powernv_boot_pir = mfspr(SPR_PIR);
147 
148 	/* LPID must not be altered when PSL_DR or PSL_IR is set */
149 	msr = mfmsr();
150 	mtmsr(msr & ~(PSL_DR | PSL_IR));
151 
152 	/* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */
153 	mtspr(SPR_LPID, 0);
154 	isync();
155 
156 	if (cpu_features2 & PPC_FEATURE2_ARCH_3_00)
157 		lpcr |= LPCR_HVICE;
158 
159 	mtspr(SPR_LPCR, lpcr);
160 	isync();
161 
162 	mtmsr(msr);
163 
164 	powernv_cpuref_init();
165 
166 	/* Set SLB count from device tree */
167 	cpu = OF_peer(0);
168 	cpu = OF_child(cpu);
169 	while (cpu != 0) {
170 		res = OF_getprop(cpu, "name", buf, sizeof(buf));
171 		if (res > 0 && strcmp(buf, "cpus") == 0)
172 			break;
173 		cpu = OF_peer(cpu);
174 	}
175 	if (cpu == 0)
176 		goto out;
177 
178 	cpu = OF_child(cpu);
179 	while (cpu != 0) {
180 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
181 		if (res > 0 && strcmp(buf, "cpu") == 0)
182 			break;
183 		cpu = OF_peer(cpu);
184 	}
185 	if (cpu == 0)
186 		goto out;
187 
188 	res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop));
189 	if (res > 0)
190 		n_slbs = prop;
191 
192 	/*
193 	 * Scan the large page size property for PAPR compatible machines.
194 	 * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties'
195 	 * for the encoding of the property.
196 	 */
197 
198 	len = OF_getproplen(cpu, "ibm,segment-page-sizes");
199 	if (len > 0) {
200 		/*
201 		 * We have to use a variable length array on the stack
202 		 * since we have very limited stack space.
203 		 */
204 		pcell_t arr[len/sizeof(cell_t)];
205 		res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr,
206 		    sizeof(arr));
207 		len /= 4;
208 		idx = 0;
209 		while (len > 0) {
210 			shift = arr[idx];
211 			slb_encoding = arr[idx + 1];
212 			nptlp = arr[idx + 2];
213 			idx += 3;
214 			len -= 3;
215 			while (len > 0 && nptlp) {
216 				lp_size = arr[idx];
217 				lp_encoding = arr[idx+1];
218 				if (slb_encoding == SLBV_L && lp_encoding == 0)
219 					break;
220 
221 				idx += 2;
222 				len -= 2;
223 				nptlp--;
224 			}
225 			if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0)
226 				break;
227 		}
228 
229 		if (len == 0)
230 			panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) "
231 			    "not supported by this system.");
232 
233 		moea64_large_page_shift = shift;
234 		moea64_large_page_size = 1ULL << lp_size;
235 	}
236 
237 out:
238 	return (0);
239 }
240 
241 
242 void
243 powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
244     struct mem_region *avail, int *availsz)
245 {
246 
247 	ofw_mem_regions(phys, physsz, avail, availsz);
248 }
249 
250 static u_long
251 powernv_timebase_freq(platform_t plat, struct cpuref *cpuref)
252 {
253 	char buf[8];
254 	phandle_t cpu, dev, root;
255 	int res;
256 	int32_t ticks = -1;
257 
258 	root = OF_peer(0);
259 	dev = OF_child(root);
260 	while (dev != 0) {
261 		res = OF_getprop(dev, "name", buf, sizeof(buf));
262 		if (res > 0 && strcmp(buf, "cpus") == 0)
263 			break;
264 		dev = OF_peer(dev);
265 	}
266 
267 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
268 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
269 		if (res > 0 && strcmp(buf, "cpu") == 0)
270 			break;
271 	}
272 	if (cpu == 0)
273 		return (512000000);
274 
275 	OF_getencprop(cpu, "timebase-frequency", &ticks, sizeof(ticks));
276 
277 	if (ticks <= 0)
278 		panic("Unable to determine timebase frequency!");
279 
280 	return (ticks);
281 
282 }
283 
284 static int
285 powernv_cpuref_init(void)
286 {
287 	phandle_t cpu, dev;
288 	char buf[32];
289 	int a, res, tmp_cpuref_cnt;
290 	static struct cpuref tmp_cpuref[MAXCPU];
291 	cell_t interrupt_servers[32];
292 	uint64_t bsp;
293 
294 	if (platform_cpuref_valid)
295 		return (0);
296 
297 	dev = OF_peer(0);
298 	dev = OF_child(dev);
299 	while (dev != 0) {
300 		res = OF_getprop(dev, "name", buf, sizeof(buf));
301 		if (res > 0 && strcmp(buf, "cpus") == 0)
302 			break;
303 		dev = OF_peer(dev);
304 	}
305 
306 	bsp = 0;
307 	tmp_cpuref_cnt = 0;
308 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
309 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
310 		if (res > 0 && strcmp(buf, "cpu") == 0) {
311 			res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
312 			if (res > 0) {
313 
314 
315 				OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
316 				    interrupt_servers, res);
317 
318 				for (a = 0; a < res/sizeof(cell_t); a++) {
319 					tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
320 					tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
321 
322 					if (interrupt_servers[a] == (uint32_t)powernv_boot_pir)
323 						bsp = tmp_cpuref_cnt;
324 
325 					tmp_cpuref_cnt++;
326 				}
327 			}
328 		}
329 	}
330 
331 	/* Map IDs, so BSP has CPUID 0 regardless of hwref */
332 	for (a = bsp; a < tmp_cpuref_cnt; a++) {
333 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
334 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
335 		platform_cpuref_cnt++;
336 	}
337 	for (a = 0; a < bsp; a++) {
338 		platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
339 		platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
340 		platform_cpuref_cnt++;
341 	}
342 
343 	platform_cpuref_valid = 1;
344 
345 	return (0);
346 }
347 
348 static int
349 powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
350 {
351 	if (platform_cpuref_valid == 0)
352 		return (EINVAL);
353 
354 	cpuref->cr_cpuid = 0;
355 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
356 
357 	return (0);
358 }
359 
360 static int
361 powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
362 {
363 	int id;
364 
365 	if (platform_cpuref_valid == 0)
366 		return (EINVAL);
367 
368 	id = cpuref->cr_cpuid + 1;
369 	if (id >= platform_cpuref_cnt)
370 		return (ENOENT);
371 
372 	cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
373 	cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
374 
375 	return (0);
376 }
377 
378 static int
379 powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
380 {
381 
382 	cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
383 	cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
384 	return (0);
385 }
386 
387 #ifdef SMP
388 static int
389 powernv_smp_start_cpu(platform_t plat, struct pcpu *pc)
390 {
391 	int result;
392 
393 	ap_pcpu = pc;
394 	powerpc_sync();
395 
396 	result = opal_call(OPAL_START_CPU, pc->pc_hwref, EXC_RST);
397 	if (result != OPAL_SUCCESS) {
398 		printf("OPAL error (%d): unable to start AP %d\n",
399 		    result, (int)pc->pc_hwref);
400 		return (ENXIO);
401 	}
402 
403 	return (0);
404 }
405 
406 static struct cpu_group *
407 powernv_smp_topo(platform_t plat)
408 {
409 	char buf[8];
410 	phandle_t cpu, dev, root;
411 	int res, nthreads;
412 
413 	root = OF_peer(0);
414 
415 	dev = OF_child(root);
416 	while (dev != 0) {
417 		res = OF_getprop(dev, "name", buf, sizeof(buf));
418 		if (res > 0 && strcmp(buf, "cpus") == 0)
419 			break;
420 		dev = OF_peer(dev);
421 	}
422 
423 	nthreads = 1;
424 	for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
425 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
426 		if (res <= 0 || strcmp(buf, "cpu") != 0)
427 			continue;
428 
429 		res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
430 
431 		if (res >= 0)
432 			nthreads = res / sizeof(cell_t);
433 		else
434 			nthreads = 1;
435 		break;
436 	}
437 
438 	if (mp_ncpus % nthreads != 0) {
439 		printf("WARNING: Irregular SMP topology. Performance may be "
440 		     "suboptimal (%d threads, %d on first core)\n",
441 		     mp_ncpus, nthreads);
442 		return (smp_topo_none());
443 	}
444 
445 	/* Don't do anything fancier for non-threaded SMP */
446 	if (nthreads == 1)
447 		return (smp_topo_none());
448 
449 	return (smp_topo_1level(CG_SHARE_L1, nthreads, CG_FLAG_SMT));
450 }
451 
452 #endif
453 
454 static void
455 powernv_reset(platform_t platform)
456 {
457 
458 	opal_call(OPAL_CEC_REBOOT);
459 }
460 
461 static void
462 powernv_smp_ap_init(platform_t platform)
463 {
464 
465 	xicp_smp_cpu_startup();
466 }
467 
468 static void
469 powernv_cpu_idle(sbintime_t sbt)
470 {
471 }
472