platform_chrp.c revision 265974
1/*- 2 * Copyright (c) 2008 Marcel Moolenaar 3 * Copyright (c) 2009 Nathan Whitehorn 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 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. 15 * 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. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/10/sys/powerpc/pseries/platform_chrp.c 265974 2014-05-13 19:12:53Z ian $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/kernel.h> 34#include <sys/bus.h> 35#include <sys/pcpu.h> 36#include <sys/proc.h> 37#include <sys/smp.h> 38#include <vm/vm.h> 39#include <vm/pmap.h> 40 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> 50 51#include <dev/ofw/openfirm.h> 52#include <machine/ofw_machdep.h> 53 54#include "platform_if.h" 55 56#ifdef SMP 57extern void *ap_pcpu; 58#endif 59 60#ifdef __powerpc64__ 61static uint8_t splpar_vpa[640] __aligned(64); 62#endif 63 64static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; 65 66static int chrp_probe(platform_t); 67static int chrp_attach(platform_t); 68void chrp_mem_regions(platform_t, struct mem_region **phys, int *physsz, 69 struct mem_region **avail, int *availsz); 70static vm_offset_t chrp_real_maxaddr(platform_t); 71static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); 72static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); 73static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); 74static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); 75static void chrp_smp_ap_init(platform_t); 76#ifdef SMP 77static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); 78static struct cpu_group *chrp_smp_topo(platform_t plat); 79#endif 80static void chrp_reset(platform_t); 81#ifdef __powerpc64__ 82#include "phyp-hvcall.h" 83static void phyp_cpu_idle(sbintime_t sbt); 84#endif 85 86static 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), 92 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), 97#ifdef SMP 98 PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), 99 PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), 100#endif 101 102 PLATFORMMETHOD(platform_reset, chrp_reset), 103 104 { 0, 0 } 105}; 106 107static platform_def_t chrp_platform = { 108 "chrp", 109 chrp_methods, 110 0 111}; 112 113PLATFORM_DEF(chrp_platform); 114 115static int 116chrp_probe(platform_t plat) 117{ 118 if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) 119 return (BUS_PROBE_GENERIC); 120 121 return (ENXIO); 122} 123 124static int 125chrp_attach(platform_t plat) 126{ 127#ifdef __powerpc64__ 128 /* XXX: check for /rtas/ibm,hypertas-functions? */ 129 if (!(mfmsr() & PSL_HV)) { 130 struct mem_region *phys, *avail; 131 int nphys, navail; 132 mem_regions(&phys, &nphys, &avail, &navail); 133 realmaxaddr = phys[0].mr_size; 134 135 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); 136 cpu_idle_hook = phyp_cpu_idle; 137 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 */ 147 mb(); 148 149 /* Set up hypervisor CPU stuff */ 150 chrp_smp_ap_init(plat); 151 } 152#endif 153 154 /* Some systems (e.g. QEMU) need Open Firmware to stand down */ 155 ofw_quiesce(); 156 157 return (0); 158} 159 160void 161chrp_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, 162 struct mem_region **avail, int *availsz) 163{ 164 ofw_mem_regions(phys,physsz,avail,availsz); 165} 166 167static vm_offset_t 168chrp_real_maxaddr(platform_t plat) 169{ 170 return (realmaxaddr); 171} 172 173static u_long 174chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) 175{ 176 phandle_t phandle; 177 int32_t ticks = -1; 178 179 phandle = cpuref->cr_hwref; 180 181 OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); 182 183 if (ticks <= 0) 184 panic("Unable to determine timebase frequency!"); 185 186 return (ticks); 187} 188 189static int 190chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 191{ 192 char buf[8]; 193 phandle_t cpu, dev, root; 194 int res, cpuid; 195 196 root = OF_peer(0); 197 198 dev = OF_child(root); 199 while (dev != 0) { 200 res = OF_getprop(dev, "name", buf, sizeof(buf)); 201 if (res > 0 && strcmp(buf, "cpus") == 0) 202 break; 203 dev = OF_peer(dev); 204 } 205 if (dev == 0) { 206 /* 207 * psim doesn't have a name property on the /cpus node, 208 * but it can be found directly 209 */ 210 dev = OF_finddevice("/cpus"); 211 if (dev == 0) 212 return (ENOENT); 213 } 214 215 cpu = OF_child(dev); 216 217 while (cpu != 0) { 218 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 219 if (res > 0 && strcmp(buf, "cpu") == 0) 220 break; 221 cpu = OF_peer(cpu); 222 } 223 if (cpu == 0) 224 return (ENOENT); 225 226 cpuref->cr_hwref = cpu; 227 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 228 sizeof(cpuid)); 229 if (res <= 0) 230 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 231 if (res <= 0) 232 cpuid = 0; 233 cpuref->cr_cpuid = cpuid; 234 235 return (0); 236} 237 238static int 239chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 240{ 241 char buf[8]; 242 phandle_t cpu; 243 int i, res, cpuid; 244 245 /* Check for whether it should be the next thread */ 246 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); 247 if (res > 0) { 248 cell_t interrupt_servers[res/sizeof(cell_t)]; 249 OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", 250 interrupt_servers, res); 251 for (i = 0; i < res/sizeof(cell_t) - 1; i++) { 252 if (interrupt_servers[i] == cpuref->cr_cpuid) { 253 cpuref->cr_cpuid = interrupt_servers[i+1]; 254 return (0); 255 } 256 } 257 } 258 259 /* Next CPU core/package */ 260 cpu = OF_peer(cpuref->cr_hwref); 261 while (cpu != 0) { 262 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 263 if (res > 0 && strcmp(buf, "cpu") == 0) 264 break; 265 cpu = OF_peer(cpu); 266 } 267 if (cpu == 0) 268 return (ENOENT); 269 270 cpuref->cr_hwref = cpu; 271 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 272 sizeof(cpuid)); 273 if (res <= 0) 274 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 275 if (res <= 0) 276 cpuid = 0; 277 cpuref->cr_cpuid = cpuid; 278 279 return (0); 280} 281 282static int 283chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 284{ 285 ihandle_t inst; 286 phandle_t bsp, chosen; 287 int res, cpuid; 288 289 chosen = OF_finddevice("/chosen"); 290 if (chosen == 0) 291 return (ENXIO); 292 293 res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); 294 if (res < 0) 295 return (ENXIO); 296 297 bsp = OF_instance_to_package(inst); 298 299 /* Pick the primary thread. Can it be any other? */ 300 cpuref->cr_hwref = bsp; 301 res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, 302 sizeof(cpuid)); 303 if (res <= 0) 304 res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid)); 305 if (res <= 0) 306 cpuid = 0; 307 cpuref->cr_cpuid = cpuid; 308 309 return (0); 310} 311 312#ifdef SMP 313static int 314chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) 315{ 316 cell_t start_cpu; 317 int result, err, timeout; 318 319 if (!rtas_exists()) { 320 printf("RTAS uninitialized: unable to start AP %d\n", 321 pc->pc_cpuid); 322 return (ENXIO); 323 } 324 325 start_cpu = rtas_token_lookup("start-cpu"); 326 if (start_cpu == -1) { 327 printf("RTAS unknown method: unable to start AP %d\n", 328 pc->pc_cpuid); 329 return (ENXIO); 330 } 331 332 ap_pcpu = pc; 333 powerpc_sync(); 334 335 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, 336 &err); 337 if (result < 0 || err != 0) { 338 printf("RTAS error (%d/%d): unable to start AP %d\n", 339 result, err, pc->pc_cpuid); 340 return (ENXIO); 341 } 342 343 timeout = 10000; 344 while (!pc->pc_awake && timeout--) 345 DELAY(100); 346 347 return ((pc->pc_awake) ? 0 : EBUSY); 348} 349 350static struct cpu_group * 351chrp_smp_topo(platform_t plat) 352{ 353 struct pcpu *pc, *last_pc; 354 int i, ncores, ncpus; 355 356 ncores = ncpus = 0; 357 last_pc = NULL; 358 for (i = 0; i <= mp_maxid; i++) { 359 pc = pcpu_find(i); 360 if (pc == NULL) 361 continue; 362 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) 363 ncores++; 364 last_pc = pc; 365 ncpus++; 366 } 367 368 if (ncpus % ncores != 0) { 369 printf("WARNING: Irregular SMP topology. Performance may be " 370 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); 371 return (smp_topo_none()); 372 } 373 374 /* Don't do anything fancier for non-threaded SMP */ 375 if (ncpus == ncores) 376 return (smp_topo_none()); 377 378 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); 379} 380#endif 381 382static void 383chrp_reset(platform_t platform) 384{ 385 OF_reboot(); 386} 387 388#ifdef __powerpc64__ 389static void 390phyp_cpu_idle(sbintime_t sbt) 391{ 392 phyp_hcall(H_CEDE); 393} 394 395static void 396chrp_smp_ap_init(platform_t platform) 397{ 398 if (!(mfmsr() & PSL_HV)) { 399 /* Set interrupt priority */ 400 phyp_hcall(H_CPPR, 0xff); 401 402 /* Register VPA */ 403 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa); 404 } 405} 406#else 407static void 408chrp_smp_ap_init(platform_t platform) 409{ 410} 411#endif 412 413