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);
79 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
80 static struct cpu_group *chrp_smp_topo(platform_t plat);
82 static void chrp_reset(platform_t);
84 #include "phyp-hvcall.h"
85 static void phyp_cpu_idle(sbintime_t sbt);
88 static platform_method_t chrp_methods[] = {
89 PLATFORMMETHOD(platform_probe, chrp_probe),
90 PLATFORMMETHOD(platform_attach, chrp_attach),
91 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
92 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
93 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
95 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
96 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
97 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
98 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
100 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
101 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
104 PLATFORMMETHOD(platform_reset, chrp_reset),
109 static platform_def_t chrp_platform = {
115 PLATFORM_DEF(chrp_platform);
118 chrp_probe(platform_t plat)
120 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
121 return (BUS_PROBE_GENERIC);
127 chrp_attach(platform_t plat)
132 /* XXX: check for /rtas/ibm,hypertas-functions? */
133 if (!(mfmsr() & PSL_HV)) {
134 struct mem_region *phys, *avail;
136 mem_regions(&phys, &nphys, &avail, &navail);
137 realmaxaddr = phys[0].mr_size;
139 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
140 cpu_idle_hook = phyp_cpu_idle;
142 /* Set up important VPA fields */
143 for (i = 0; i < MAXCPU; i++) {
144 bzero(splpar_vpa[i], sizeof(splpar_vpa));
145 /* First two: VPA size */
147 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
149 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
150 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
151 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
152 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
153 splpar_vpa[i][0xfd] = 0xff;
154 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
158 /* Set up hypervisor CPU stuff */
159 chrp_smp_ap_init(plat);
163 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
170 parse_drconf_memory(struct mem_region *ofmem, int *msz,
171 struct mem_region *ofavail, int *asz)
175 int i, idx, len, lasz, lmsz, res;
176 uint32_t flags, lmb_size[2];
182 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
184 /* No drconf node, return. */
187 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
191 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
193 /* Parse the /ibm,dynamic-memory.
194 The first position gives the # of entries. The next two words
195 reflect the address of the memory block. The next four words are
196 the DRC index, reserved, list index and flags.
197 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
199 #el Addr DRC-idx res list-idx flags
200 -------------------------------------------------
201 | 4 | 8 | 4 | 4 | 4 | 4 |....
202 -------------------------------------------------
205 len = OF_getproplen(phandle, "ibm,dynamic-memory");
208 /* We have to use a variable length array on the stack
209 since we have very limited stack space.
211 cell_t arr[len/sizeof(cell_t)];
213 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
218 /* Number of elements */
221 /* First address, in arr[1], arr[2]*/
224 for (i = 0; i < idx; i++) {
225 base = ((uint64_t)dmem[0] << 32) + dmem[1];
228 /* Use region only if available and not reserved. */
229 if ((flags & 0x8) && !(flags & 0x80)) {
230 ofmem[lmsz].mr_start = base;
231 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
232 ofavail[lasz].mr_start = base;
233 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
248 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
249 struct mem_region *avail, int *availsz)
251 vm_offset_t maxphysaddr;
254 ofw_mem_regions(phys, physsz, avail, availsz);
255 parse_drconf_memory(phys, physsz, avail, availsz);
258 * On some firmwares (SLOF), some memory may be marked available that
259 * doesn't actually exist. This manifests as an extension of the last
260 * available segment past the end of physical memory, so truncate that
264 for (i = 0; i < *physsz; i++)
265 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
266 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
268 for (i = 0; i < *availsz; i++)
269 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
270 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
274 chrp_real_maxaddr(platform_t plat)
276 return (realmaxaddr);
280 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
285 phandle = cpuref->cr_hwref;
287 OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
290 panic("Unable to determine timebase frequency!");
296 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
299 phandle_t cpu, dev, root;
304 dev = OF_child(root);
306 res = OF_getprop(dev, "name", buf, sizeof(buf));
307 if (res > 0 && strcmp(buf, "cpus") == 0)
313 * psim doesn't have a name property on the /cpus node,
314 * but it can be found directly
316 dev = OF_finddevice("/cpus");
324 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
325 if (res > 0 && strcmp(buf, "cpu") == 0)
332 cpuref->cr_hwref = cpu;
333 res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
336 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
339 cpuref->cr_cpuid = cpuid;
345 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
351 /* Check for whether it should be the next thread */
352 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
354 cell_t interrupt_servers[res/sizeof(cell_t)];
355 OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
356 interrupt_servers, res);
357 for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
358 if (interrupt_servers[i] == cpuref->cr_cpuid) {
359 cpuref->cr_cpuid = interrupt_servers[i+1];
365 /* Next CPU core/package */
366 cpu = OF_peer(cpuref->cr_hwref);
368 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
369 if (res > 0 && strcmp(buf, "cpu") == 0)
376 cpuref->cr_hwref = cpu;
377 res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
380 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
383 cpuref->cr_cpuid = cpuid;
389 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
392 phandle_t bsp, chosen;
395 chosen = OF_finddevice("/chosen");
399 res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst));
403 bsp = OF_instance_to_package(inst);
405 /* Pick the primary thread. Can it be any other? */
406 cpuref->cr_hwref = bsp;
407 res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
410 res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid));
413 cpuref->cr_cpuid = cpuid;
420 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
423 int result, err, timeout;
425 if (!rtas_exists()) {
426 printf("RTAS uninitialized: unable to start AP %d\n",
431 start_cpu = rtas_token_lookup("start-cpu");
432 if (start_cpu == -1) {
433 printf("RTAS unknown method: unable to start AP %d\n",
441 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
443 if (result < 0 || err != 0) {
444 printf("RTAS error (%d/%d): unable to start AP %d\n",
445 result, err, pc->pc_cpuid);
450 while (!pc->pc_awake && timeout--)
453 return ((pc->pc_awake) ? 0 : EBUSY);
456 static struct cpu_group *
457 chrp_smp_topo(platform_t plat)
459 struct pcpu *pc, *last_pc;
460 int i, ncores, ncpus;
464 for (i = 0; i <= mp_maxid; i++) {
468 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
474 if (ncpus % ncores != 0) {
475 printf("WARNING: Irregular SMP topology. Performance may be "
476 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
477 return (smp_topo_none());
480 /* Don't do anything fancier for non-threaded SMP */
482 return (smp_topo_none());
484 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
489 chrp_reset(platform_t platform)
496 phyp_cpu_idle(sbintime_t sbt)
502 mtmsr(msr & ~PSL_EE);
503 if (sched_runnable()) {
508 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
513 chrp_smp_ap_init(platform_t platform)
515 if (!(mfmsr() & PSL_HV)) {
517 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid),
518 splpar_vpa[PCPU_GET(cpuid)]);
520 /* Set interrupt priority */
521 phyp_hcall(H_CPPR, 0xff);
526 chrp_smp_ap_init(platform_t platform)