2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2008 Marcel Moolenaar
5 * Copyright (c) 2009 Nathan Whitehorn
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
39 #include <sys/sched.h>
44 #include <machine/bus.h>
45 #include <machine/cpu.h>
46 #include <machine/hid.h>
47 #include <machine/platformvar.h>
48 #include <machine/rtas.h>
49 #include <machine/smp.h>
50 #include <machine/spr.h>
51 #include <machine/trap.h>
53 #include <dev/ofw/openfirm.h>
54 #include <machine/ofw_machdep.h>
56 #include "platform_if.h"
63 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
66 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
68 static int chrp_probe(platform_t);
69 static int chrp_attach(platform_t);
70 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
71 struct mem_region *avail, int *availsz);
72 static vm_offset_t chrp_real_maxaddr(platform_t);
73 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
74 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
75 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
76 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
77 static void chrp_smp_ap_init(platform_t);
78 static int chrp_cpuref_init(void);
80 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
81 static void chrp_smp_probe_threads(platform_t plat);
82 static struct cpu_group *chrp_smp_topo(platform_t plat);
84 static void chrp_reset(platform_t);
86 #include "phyp-hvcall.h"
87 static void phyp_cpu_idle(sbintime_t sbt);
90 static struct cpuref platform_cpuref[MAXCPU];
91 static int platform_cpuref_cnt;
92 static int platform_cpuref_valid;
94 static platform_method_t chrp_methods[] = {
95 PLATFORMMETHOD(platform_probe, chrp_probe),
96 PLATFORMMETHOD(platform_attach, chrp_attach),
97 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
98 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
99 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
101 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
102 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
103 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
104 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
106 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
107 PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads),
108 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
111 PLATFORMMETHOD(platform_reset, chrp_reset),
116 static platform_def_t chrp_platform = {
122 PLATFORM_DEF(chrp_platform);
125 chrp_probe(platform_t plat)
127 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
128 return (BUS_PROBE_GENERIC);
134 chrp_attach(platform_t plat)
140 /* XXX: check for /rtas/ibm,hypertas-functions? */
141 if (!(mfmsr() & PSL_HV)) {
142 struct mem_region *phys, *avail;
146 mem_regions(&phys, &nphys, &avail, &navail);
149 for (i = 0; i < nphys; i++) {
150 off = phys[i].mr_start + phys[i].mr_size;
151 realmaxaddr = MAX(off, realmaxaddr);
154 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
155 cpu_idle_hook = phyp_cpu_idle;
157 /* Set up important VPA fields */
158 for (i = 0; i < MAXCPU; i++) {
159 /* First two: VPA size */
161 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
163 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
164 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
165 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
166 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
167 splpar_vpa[i][0xfd] = 0xff;
168 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
172 /* Set up hypervisor CPU stuff */
173 chrp_smp_ap_init(plat);
178 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
180 TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
188 parse_drconf_memory(struct mem_region *ofmem, int *msz,
189 struct mem_region *ofavail, int *asz)
193 int i, idx, len, lasz, lmsz, res;
194 uint32_t flags, lmb_size[2];
200 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
202 /* No drconf node, return. */
205 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
209 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
211 /* Parse the /ibm,dynamic-memory.
212 The first position gives the # of entries. The next two words
213 reflect the address of the memory block. The next four words are
214 the DRC index, reserved, list index and flags.
215 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
217 #el Addr DRC-idx res list-idx flags
218 -------------------------------------------------
219 | 4 | 8 | 4 | 4 | 4 | 4 |....
220 -------------------------------------------------
223 len = OF_getproplen(phandle, "ibm,dynamic-memory");
226 /* We have to use a variable length array on the stack
227 since we have very limited stack space.
229 cell_t arr[len/sizeof(cell_t)];
231 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
236 /* Number of elements */
239 /* First address, in arr[1], arr[2]*/
242 for (i = 0; i < idx; i++) {
243 base = ((uint64_t)dmem[0] << 32) + dmem[1];
246 /* Use region only if available and not reserved. */
247 if ((flags & 0x8) && !(flags & 0x80)) {
248 ofmem[lmsz].mr_start = base;
249 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
250 ofavail[lasz].mr_start = base;
251 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
266 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
267 struct mem_region *avail, int *availsz)
269 vm_offset_t maxphysaddr;
272 ofw_mem_regions(phys, physsz, avail, availsz);
273 parse_drconf_memory(phys, physsz, avail, availsz);
276 * On some firmwares (SLOF), some memory may be marked available that
277 * doesn't actually exist. This manifests as an extension of the last
278 * available segment past the end of physical memory, so truncate that
282 for (i = 0; i < *physsz; i++)
283 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
284 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
286 for (i = 0; i < *availsz; i++)
287 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
288 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
292 chrp_real_maxaddr(platform_t plat)
294 return (realmaxaddr);
298 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
300 phandle_t cpus, cpunode;
305 cpus = OF_finddevice("/cpus");
307 panic("CPU tree not found on Open Firmware\n");
309 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
310 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
311 if (res > 0 && strcmp(buf, "cpu") == 0)
315 panic("CPU node not found on Open Firmware\n");
317 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
320 panic("Unable to determine timebase frequency!");
326 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
329 if (platform_cpuref_valid == 0)
332 cpuref->cr_cpuid = 0;
333 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
339 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
343 if (platform_cpuref_valid == 0)
346 id = cpuref->cr_cpuid + 1;
347 if (id >= platform_cpuref_cnt)
350 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
351 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
357 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
360 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
361 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
366 get_cpu_reg(phandle_t cpu, cell_t *reg)
370 res = OF_getproplen(cpu, "reg");
371 if (res != sizeof(cell_t))
372 panic("Unexpected length for CPU property reg on Open Firmware\n");
373 OF_getencprop(cpu, "reg", reg, res);
377 chrp_cpuref_init(void)
379 phandle_t cpu, dev, chosen, pbsp;
382 int a, bsp, res, res2, tmp_cpuref_cnt;
383 static struct cpuref tmp_cpuref[MAXCPU];
384 cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
386 if (platform_cpuref_valid)
392 res = OF_getprop(dev, "name", buf, sizeof(buf));
393 if (res > 0 && strcmp(buf, "cpus") == 0)
398 /* Make sure that cpus reg property have 1 address cell and 0 size cells */
399 res = OF_getproplen(dev, "#address-cells");
400 res2 = OF_getproplen(dev, "#size-cells");
401 if (res != res2 || res != sizeof(cell_t))
402 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
403 OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
404 OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
405 if (addr_cells != 1 || size_cells != 0)
406 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
408 /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
410 chosen = OF_finddevice("/chosen");
412 panic("Device /chosen not found on Open Firmware\n");
417 if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
418 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
419 pbsp = OF_instance_to_package(ibsp);
421 get_cpu_reg(pbsp, &bsp_reg);
424 /* /chosen/fdtbootcpu */
426 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
427 OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
431 panic("Boot CPU not found on Open Firmware\n");
435 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
436 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
437 if (res > 0 && strcmp(buf, "cpu") == 0) {
438 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
440 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
441 interrupt_servers, res);
443 get_cpu_reg(cpu, ®);
445 bsp = tmp_cpuref_cnt;
447 for (a = 0; a < res/sizeof(cell_t); a++) {
448 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
449 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
457 panic("Boot CPU not found\n");
459 /* Map IDs, so BSP has CPUID 0 regardless of hwref */
460 for (a = bsp; a < tmp_cpuref_cnt; a++) {
461 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
462 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
463 platform_cpuref_cnt++;
465 for (a = 0; a < bsp; a++) {
466 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
467 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
468 platform_cpuref_cnt++;
471 platform_cpuref_valid = 1;
479 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
482 int result, err, timeout;
484 if (!rtas_exists()) {
485 printf("RTAS uninitialized: unable to start AP %d\n",
490 start_cpu = rtas_token_lookup("start-cpu");
491 if (start_cpu == -1) {
492 printf("RTAS unknown method: unable to start AP %d\n",
500 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
502 if (result < 0 || err != 0) {
503 printf("RTAS error (%d/%d): unable to start AP %d\n",
504 result, err, pc->pc_cpuid);
509 while (!pc->pc_awake && timeout--)
512 return ((pc->pc_awake) ? 0 : EBUSY);
516 chrp_smp_probe_threads(platform_t plat)
518 struct pcpu *pc, *last_pc;
523 for (i = 0; i <= mp_maxid; i++) {
527 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
533 if (mp_ncpus % ncores == 0)
534 smp_threads_per_core = mp_ncpus / ncores;
537 static struct cpu_group *
538 chrp_smp_topo(platform_t plat)
541 if (mp_ncpus % mp_ncores != 0) {
542 printf("WARNING: Irregular SMP topology. Performance may be "
543 "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
544 return (smp_topo_none());
547 /* Don't do anything fancier for non-threaded SMP */
548 if (mp_ncpus == mp_ncores)
549 return (smp_topo_none());
551 return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
557 chrp_reset(platform_t platform)
564 phyp_cpu_idle(sbintime_t sbt)
570 mtmsr(msr & ~PSL_EE);
571 if (sched_runnable()) {
576 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
581 chrp_smp_ap_init(platform_t platform)
583 if (!(mfmsr() & PSL_HV)) {
585 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
586 splpar_vpa[PCPU_GET(hwref)]);
588 /* Set interrupt priority */
589 phyp_hcall(H_CPPR, 0xff);
594 chrp_smp_ap_init(platform_t platform)