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/rtas.h>
46 #include <machine/smp.h>
47 #include <machine/spr.h>
48 #include <machine/trap.h>
50 #include <dev/ofw/openfirm.h>
51 #include <machine/ofw_machdep.h>
53 #include "platform_if.h"
60 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
63 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
65 static int chrp_probe(platform_t);
66 static int chrp_attach(platform_t);
67 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
68 struct mem_region *avail, int *availsz);
69 static vm_offset_t chrp_real_maxaddr(platform_t);
70 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
71 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
72 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
73 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
74 static void chrp_smp_ap_init(platform_t);
76 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
77 static struct cpu_group *chrp_smp_topo(platform_t plat);
79 static void chrp_reset(platform_t);
81 #include "phyp-hvcall.h"
82 static void phyp_cpu_idle(sbintime_t sbt);
85 static platform_method_t chrp_methods[] = {
86 PLATFORMMETHOD(platform_probe, chrp_probe),
87 PLATFORMMETHOD(platform_attach, chrp_attach),
88 PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),
89 PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),
90 PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),
92 PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),
93 PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),
94 PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),
95 PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),
97 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),
98 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),
101 PLATFORMMETHOD(platform_reset, chrp_reset),
106 static platform_def_t chrp_platform = {
112 PLATFORM_DEF(chrp_platform);
115 chrp_probe(platform_t plat)
117 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
118 return (BUS_PROBE_GENERIC);
124 chrp_attach(platform_t plat)
129 /* XXX: check for /rtas/ibm,hypertas-functions? */
130 if (!(mfmsr() & PSL_HV)) {
131 struct mem_region *phys, *avail;
133 mem_regions(&phys, &nphys, &avail, &navail);
134 realmaxaddr = phys[0].mr_size;
136 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
137 cpu_idle_hook = phyp_cpu_idle;
139 /* Set up important VPA fields */
140 for (i = 0; i < MAXCPU; i++) {
141 bzero(splpar_vpa[i], sizeof(splpar_vpa));
142 /* First two: VPA size */
144 (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
146 (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
147 splpar_vpa[i][0xba] = 1; /* Maintain FPRs */
148 splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */
149 splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */
150 splpar_vpa[i][0xfd] = 0xff;
151 splpar_vpa[i][0xff] = 1; /* Maintain Altivec */
155 /* Set up hypervisor CPU stuff */
156 chrp_smp_ap_init(plat);
160 /* Some systems (e.g. QEMU) need Open Firmware to stand down */
167 parse_drconf_memory(struct mem_region *ofmem, int *msz,
168 struct mem_region *ofavail, int *asz)
172 int i, idx, len, lasz, lmsz, res;
173 uint32_t flags, lmb_size[2];
179 phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
181 /* No drconf node, return. */
184 res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
188 printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
190 /* Parse the /ibm,dynamic-memory.
191 The first position gives the # of entries. The next two words
192 reflect the address of the memory block. The next four words are
193 the DRC index, reserved, list index and flags.
194 (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
196 #el Addr DRC-idx res list-idx flags
197 -------------------------------------------------
198 | 4 | 8 | 4 | 4 | 4 | 4 |....
199 -------------------------------------------------
202 len = OF_getproplen(phandle, "ibm,dynamic-memory");
205 /* We have to use a variable length array on the stack
206 since we have very limited stack space.
208 cell_t arr[len/sizeof(cell_t)];
210 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
215 /* Number of elements */
218 /* First address, in arr[1], arr[2]*/
221 for (i = 0; i < idx; i++) {
222 base = ((uint64_t)dmem[0] << 32) + dmem[1];
225 /* Use region only if available and not reserved. */
226 if ((flags & 0x8) && !(flags & 0x80)) {
227 ofmem[lmsz].mr_start = base;
228 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
229 ofavail[lasz].mr_start = base;
230 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
245 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
246 struct mem_region *avail, int *availsz)
248 vm_offset_t maxphysaddr;
251 ofw_mem_regions(phys, physsz, avail, availsz);
252 parse_drconf_memory(phys, physsz, avail, availsz);
255 * On some firmwares (SLOF), some memory may be marked available that
256 * doesn't actually exist. This manifests as an extension of the last
257 * available segment past the end of physical memory, so truncate that
261 for (i = 0; i < *physsz; i++)
262 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
263 maxphysaddr = phys[i].mr_start + phys[i].mr_size;
265 for (i = 0; i < *availsz; i++)
266 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
267 avail[i].mr_size = maxphysaddr - avail[i].mr_start;
271 chrp_real_maxaddr(platform_t plat)
273 return (realmaxaddr);
277 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
282 phandle = cpuref->cr_hwref;
284 OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
287 panic("Unable to determine timebase frequency!");
293 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
296 phandle_t cpu, dev, root;
301 dev = OF_child(root);
303 res = OF_getprop(dev, "name", buf, sizeof(buf));
304 if (res > 0 && strcmp(buf, "cpus") == 0)
310 * psim doesn't have a name property on the /cpus node,
311 * but it can be found directly
313 dev = OF_finddevice("/cpus");
321 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
322 if (res > 0 && strcmp(buf, "cpu") == 0)
329 cpuref->cr_hwref = cpu;
330 res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
333 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
336 cpuref->cr_cpuid = cpuid;
342 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
348 /* Check for whether it should be the next thread */
349 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
351 cell_t interrupt_servers[res/sizeof(cell_t)];
352 OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
353 interrupt_servers, res);
354 for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
355 if (interrupt_servers[i] == cpuref->cr_cpuid) {
356 cpuref->cr_cpuid = interrupt_servers[i+1];
362 /* Next CPU core/package */
363 cpu = OF_peer(cpuref->cr_hwref);
365 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
366 if (res > 0 && strcmp(buf, "cpu") == 0)
373 cpuref->cr_hwref = cpu;
374 res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
377 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
380 cpuref->cr_cpuid = cpuid;
386 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
389 phandle_t bsp, chosen;
392 chosen = OF_finddevice("/chosen");
396 res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst));
400 bsp = OF_instance_to_package(inst);
402 /* Pick the primary thread. Can it be any other? */
403 cpuref->cr_hwref = bsp;
404 res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
407 res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid));
410 cpuref->cr_cpuid = cpuid;
417 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
420 int result, err, timeout;
422 if (!rtas_exists()) {
423 printf("RTAS uninitialized: unable to start AP %d\n",
428 start_cpu = rtas_token_lookup("start-cpu");
429 if (start_cpu == -1) {
430 printf("RTAS unknown method: unable to start AP %d\n",
438 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
440 if (result < 0 || err != 0) {
441 printf("RTAS error (%d/%d): unable to start AP %d\n",
442 result, err, pc->pc_cpuid);
447 while (!pc->pc_awake && timeout--)
450 return ((pc->pc_awake) ? 0 : EBUSY);
453 static struct cpu_group *
454 chrp_smp_topo(platform_t plat)
456 struct pcpu *pc, *last_pc;
457 int i, ncores, ncpus;
461 for (i = 0; i <= mp_maxid; i++) {
465 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
471 if (ncpus % ncores != 0) {
472 printf("WARNING: Irregular SMP topology. Performance may be "
473 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
474 return (smp_topo_none());
477 /* Don't do anything fancier for non-threaded SMP */
479 return (smp_topo_none());
481 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
486 chrp_reset(platform_t platform)
493 phyp_cpu_idle(sbintime_t sbt)
499 chrp_smp_ap_init(platform_t platform)
501 if (!(mfmsr() & PSL_HV)) {
503 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid),
504 splpar_vpa[PCPU_GET(cpuid)]);
506 /* Set interrupt priority */
507 phyp_hcall(H_CPPR, 0xff);
512 chrp_smp_ap_init(platform_t platform)