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 struct cpu_group *chrp_smp_topo(platform_t plat);
83 static void chrp_reset(platform_t);
85 #include "phyp-hvcall.h"
86 static void phyp_cpu_idle(sbintime_t sbt);
89 static struct cpuref platform_cpuref[MAXCPU];
90 static int platform_cpuref_cnt;
91 static int platform_cpuref_valid;
93 static platform_method_t chrp_methods[] = {
94 PLATFORMMETHOD(platform_probe, chrp_probe),
95 PLATFORMMETHOD(platform_attach, chrp_attach),
96 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
97 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
98 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
100 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
101 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
102 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
103 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
105 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
106 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
109 PLATFORMMETHOD(platform_reset, chrp_reset),
114 static platform_def_t chrp_platform = {
120 PLATFORM_DEF(chrp_platform);
123 chrp_probe(platform_t plat)
125 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
126 return (BUS_PROBE_GENERIC);
132 chrp_attach(platform_t plat)
137 /* XXX: check for /rtas/ibm,hypertas-functions? */
138 if (!(mfmsr() & PSL_HV)) {
139 struct mem_region *phys, *avail;
141 mem_regions(&phys, &nphys, &avail, &navail);
142 realmaxaddr = phys[0].mr_size;
144 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
145 cpu_idle_hook = phyp_cpu_idle;
147 /* Set up important VPA fields */
148 for (i = 0; i < MAXCPU; i++) {
149 bzero(splpar_vpa[i], sizeof(splpar_vpa));
150 /* First two: VPA size */
152 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
154 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
155 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
156 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
157 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
158 splpar_vpa[i][0xfd] = 0xff;
159 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
163 /* Set up hypervisor CPU stuff */
164 chrp_smp_ap_init(plat);
169 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
176 parse_drconf_memory(struct mem_region *ofmem, int *msz,
177 struct mem_region *ofavail, int *asz)
181 int i, idx, len, lasz, lmsz, res;
182 uint32_t flags, lmb_size[2];
188 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
190 /* No drconf node, return. */
193 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
197 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
199 /* Parse the /ibm,dynamic-memory.
200 The first position gives the # of entries. The next two words
201 reflect the address of the memory block. The next four words are
202 the DRC index, reserved, list index and flags.
203 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
205 #el Addr DRC-idx res list-idx flags
206 -------------------------------------------------
207 | 4 | 8 | 4 | 4 | 4 | 4 |....
208 -------------------------------------------------
211 len = OF_getproplen(phandle, "ibm,dynamic-memory");
214 /* We have to use a variable length array on the stack
215 since we have very limited stack space.
217 cell_t arr[len/sizeof(cell_t)];
219 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
224 /* Number of elements */
227 /* First address, in arr[1], arr[2]*/
230 for (i = 0; i < idx; i++) {
231 base = ((uint64_t)dmem[0] << 32) + dmem[1];
234 /* Use region only if available and not reserved. */
235 if ((flags & 0x8) && !(flags & 0x80)) {
236 ofmem[lmsz].mr_start = base;
237 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
238 ofavail[lasz].mr_start = base;
239 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
254 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
255 struct mem_region *avail, int *availsz)
257 vm_offset_t maxphysaddr;
260 ofw_mem_regions(phys, physsz, avail, availsz);
261 parse_drconf_memory(phys, physsz, avail, availsz);
264 * On some firmwares (SLOF), some memory may be marked available that
265 * doesn't actually exist. This manifests as an extension of the last
266 * available segment past the end of physical memory, so truncate that
270 for (i = 0; i < *physsz; i++)
271 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
272 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
274 for (i = 0; i < *availsz; i++)
275 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
276 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
280 chrp_real_maxaddr(platform_t plat)
282 return (realmaxaddr);
286 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
288 phandle_t cpus, cpunode;
293 cpus = OF_finddevice("/cpus");
295 panic("CPU tree not found on Open Firmware\n");
297 for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
298 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
299 if (res > 0 && strcmp(buf, "cpu") == 0)
303 panic("CPU node not found on Open Firmware\n");
305 OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
308 panic("Unable to determine timebase frequency!");
314 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
317 if (platform_cpuref_valid == 0)
320 cpuref->cr_cpuid = 0;
321 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
327 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
331 if (platform_cpuref_valid == 0)
334 id = cpuref->cr_cpuid + 1;
335 if (id >= platform_cpuref_cnt)
338 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
339 cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
345 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
348 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
349 cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
354 chrp_cpuref_init(void)
359 cell_t interrupt_servers[32];
362 if (platform_cpuref_valid)
368 res = OF_getprop(dev, "name", buf, sizeof(buf));
369 if (res > 0 && strcmp(buf, "cpus") == 0)
375 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
376 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
377 if (res > 0 && strcmp(buf, "cpu") == 0) {
378 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
382 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
383 interrupt_servers, res);
385 for (a = 0; a < res/sizeof(cell_t); a++) {
386 platform_cpuref[platform_cpuref_cnt].cr_hwref = interrupt_servers[a];
387 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
389 platform_cpuref_cnt++;
395 platform_cpuref_valid = 1;
403 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
406 int result, err, timeout;
408 if (!rtas_exists()) {
409 printf("RTAS uninitialized: unable to start AP %d\n",
414 start_cpu = rtas_token_lookup("start-cpu");
415 if (start_cpu == -1) {
416 printf("RTAS unknown method: unable to start AP %d\n",
424 result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
426 if (result < 0 || err != 0) {
427 printf("RTAS error (%d/%d): unable to start AP %d\n",
428 result, err, pc->pc_cpuid);
433 while (!pc->pc_awake && timeout--)
436 return ((pc->pc_awake) ? 0 : EBUSY);
439 static struct cpu_group *
440 chrp_smp_topo(platform_t plat)
442 struct pcpu *pc, *last_pc;
443 int i, ncores, ncpus;
447 for (i = 0; i <= mp_maxid; i++) {
451 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
457 if (ncpus % ncores != 0) {
458 printf("WARNING: Irregular SMP topology. Performance may be "
459 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
460 return (smp_topo_none());
463 /* Don't do anything fancier for non-threaded SMP */
465 return (smp_topo_none());
467 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
472 chrp_reset(platform_t platform)
479 phyp_cpu_idle(sbintime_t sbt)
485 mtmsr(msr & ~PSL_EE);
486 if (sched_runnable()) {
491 phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
496 chrp_smp_ap_init(platform_t platform)
498 if (!(mfmsr() & PSL_HV)) {
500 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
501 splpar_vpa[PCPU_GET(hwref)]);
503 /* Set interrupt priority */
504 phyp_hcall(H_CPPR, 0xff);
509 chrp_smp_ap_init(platform_t platform)