2 * Copyright (c) 2008 Marcel Moolenaar
3 * Copyright (c) 2009 Nathan Whitehorn
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
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>
51 #include <dev/ofw/openfirm.h>
52 #include <machine/ofw_machdep.h>
54 #include "platform_if.h"
61 static uint8_t splpar_vpa[640] __aligned(64);
64 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
66 static int chrp_probe(platform_t);
67 static int chrp_attach(platform_t);
68 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
69 struct mem_region *avail, int *availsz);
70 static vm_offset_t chrp_real_maxaddr(platform_t);
71 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
72 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
73 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
74 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
75 static void chrp_smp_ap_init(platform_t);
77 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
78 static struct cpu_group *chrp_smp_topo(platform_t plat);
80 static void chrp_reset(platform_t);
82 #include "phyp-hvcall.h"
83 static void phyp_cpu_idle(sbintime_t sbt);
86 static platform_method_t chrp_methods[] = {
87 PLATFORMMETHOD(platform_probe, chrp_probe),
88 PLATFORMMETHOD(platform_attach, chrp_attach),
89 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
90 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
91 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
93 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
94 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
95 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
96 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
98 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
99 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
102 PLATFORMMETHOD(platform_reset, chrp_reset),
107 static platform_def_t chrp_platform = {
113 PLATFORM_DEF(chrp_platform);
116 chrp_probe(platform_t plat)
118 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
119 return (BUS_PROBE_GENERIC);
125 chrp_attach(platform_t plat)
128 /* XXX: check for /rtas/ibm,hypertas-functions? */
129 if (!(mfmsr() & PSL_HV)) {
130 struct mem_region *phys, *avail;
132 mem_regions(&phys, &nphys, &avail, &navail);
133 realmaxaddr = phys[0].mr_size;
135 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
136 cpu_idle_hook = phyp_cpu_idle;
138 /* Set up important VPA fields */
139 bzero(splpar_vpa, sizeof(splpar_vpa));
140 splpar_vpa[4] = (uint8_t)((sizeof(splpar_vpa) >> 8) & 0xff);
141 splpar_vpa[5] = (uint8_t)(sizeof(splpar_vpa) & 0xff);
142 splpar_vpa[0xba] = 1; /* Maintain FPRs */
143 splpar_vpa[0xbb] = 1; /* Maintain PMCs */
144 splpar_vpa[0xfc] = 0xff; /* Maintain full SLB */
145 splpar_vpa[0xfd] = 0xff;
146 splpar_vpa[0xff] = 1; /* Maintain Altivec */
149 /* Set up hypervisor CPU stuff */
150 chrp_smp_ap_init(plat);
154 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
161 parse_drconf_memory(int *msz, int *asz, struct mem_region *ofmem,
162 struct mem_region *ofavail)
166 int i, idx, len, lasz, lmsz, res;
167 uint32_t lmb_size[2];
168 unsigned long *dmem, flags;
173 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
175 /* No drconf node, return. */
178 res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size));
181 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
183 /* Parse the /ibm,dynamic-memory.
184 The first position gives the # of entries. The next two words
185 reflect the address of the memory block. The next four words are
186 the DRC index, reserved, list index and flags.
187 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
189 #el Addr DRC-idx res list-idx flags
190 -------------------------------------------------
191 | 4 | 8 | 4 | 4 | 4 | 4 |....
192 -------------------------------------------------
195 len = OF_getproplen(phandle, "ibm,dynamic-memory");
198 /* We have to use a variable length array on the stack
199 since we have very limited stack space.
201 cell_t arr[len/sizeof(cell_t)];
203 res = OF_getprop(phandle, "ibm,dynamic-memory", &arr,
208 /* Number of elements */
212 dmem = (void*)&arr[1];
214 for (i = 0; i < idx; i++) {
218 /* Use region only if available and not reserved. */
219 if ((flags & 0x8) && !(flags & 0x80)) {
220 ofmem[lmsz].mr_start = base;
221 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
222 ofavail[lasz].mr_start = base;
223 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
238 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
239 struct mem_region *avail, int *availsz)
241 vm_offset_t maxphysaddr;
244 ofw_mem_regions(phys, physsz, avail, availsz);
245 parse_drconf_memory(physsz, availsz, phys, avail);
248 * On some firmwares (SLOF), some memory may be marked available that
249 * doesn't actually exist. This manifests as an extension of the last
250 * available segment past the end of physical memory, so truncate that
254 for (i = 0; i < *physsz; i++)
255 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
256 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
258 for (i = 0; i < *availsz; i++)
259 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
260 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
264 chrp_real_maxaddr(platform_t plat)
266 return (realmaxaddr);
270 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
275 phandle = cpuref->cr_hwref;
277 OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
280 panic("Unable to determine timebase frequency!");
286 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
289 phandle_t cpu, dev, root;
294 dev = OF_child(root);
296 res = OF_getprop(dev, "name", buf, sizeof(buf));
297 if (res > 0 && strcmp(buf, "cpus") == 0)
303 * psim doesn't have a name property on the /cpus node,
304 * but it can be found directly
306 dev = OF_finddevice("/cpus");
314 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
315 if (res > 0 && strcmp(buf, "cpu") == 0)
322 cpuref->cr_hwref = cpu;
323 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
326 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
329 cpuref->cr_cpuid = cpuid;
335 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
341 /* Check for whether it should be the next thread */
342 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
344 cell_t interrupt_servers[res/sizeof(cell_t)];
345 OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
346 interrupt_servers, res);
347 for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
348 if (interrupt_servers[i] == cpuref->cr_cpuid) {
349 cpuref->cr_cpuid = interrupt_servers[i+1];
355 /* Next CPU core/package */
356 cpu = OF_peer(cpuref->cr_hwref);
358 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
359 if (res > 0 && strcmp(buf, "cpu") == 0)
366 cpuref->cr_hwref = cpu;
367 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
370 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
373 cpuref->cr_cpuid = cpuid;
379 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
382 phandle_t bsp, chosen;
385 chosen = OF_finddevice("/chosen");
389 res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
393 bsp = OF_instance_to_package(inst);
395 /* Pick the primary thread. Can it be any other? */
396 cpuref->cr_hwref = bsp;
397 res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
400 res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid));
403 cpuref->cr_cpuid = cpuid;
410 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
413 int result, err, timeout;
415 if (!rtas_exists()) {
416 printf("RTAS uninitialized: unable to start AP %d\n",
421 start_cpu = rtas_token_lookup("start-cpu");
422 if (start_cpu == -1) {
423 printf("RTAS unknown method: unable to start AP %d\n",
431 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
433 if (result < 0 || err != 0) {
434 printf("RTAS error (%d/%d): unable to start AP %d\n",
435 result, err, pc->pc_cpuid);
440 while (!pc->pc_awake && timeout--)
443 return ((pc->pc_awake) ? 0 : EBUSY);
446 static struct cpu_group *
447 chrp_smp_topo(platform_t plat)
449 struct pcpu *pc, *last_pc;
450 int i, ncores, ncpus;
454 for (i = 0; i <= mp_maxid; i++) {
458 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
464 if (ncpus % ncores != 0) {
465 printf("WARNING: Irregular SMP topology. Performance may be "
466 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
467 return (smp_topo_none());
470 /* Don't do anything fancier for non-threaded SMP */
472 return (smp_topo_none());
474 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
479 chrp_reset(platform_t platform)
486 phyp_cpu_idle(sbintime_t sbt)
492 chrp_smp_ap_init(platform_t platform)
494 if (!(mfmsr() & PSL_HV)) {
495 /* Set interrupt priority */
496 phyp_hcall(H_CPPR, 0xff);
499 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa);
504 chrp_smp_ap_init(platform_t platform)