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$"); 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_aim.h> 50 51 #include <dev/ofw/openfirm.h> 52 #include <machine/ofw_machdep.h> 53 54 #include "platform_if.h" 55 56 #ifdef SMP 57 extern void *ap_pcpu; 58 #endif 59 60 #ifdef __powerpc64__ 61 static uint8_t splpar_vpa[640] __aligned(64); 62 #endif 63 64 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; 65 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); 76 #ifdef SMP 77 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); 78 static struct cpu_group *chrp_smp_topo(platform_t plat); 79 #endif 80 static void chrp_reset(platform_t); 81 #ifdef __powerpc64__ 82 #include "phyp-hvcall.h" 83 static void phyp_cpu_idle(sbintime_t sbt); 84 #endif 85 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), 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 107 static platform_def_t chrp_platform = { 108 "chrp", 109 chrp_methods, 110 0 111 }; 112 113 PLATFORM_DEF(chrp_platform); 114 115 static int 116 chrp_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 124 static int 125 chrp_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 return (0); 155 } 156 157 void 158 chrp_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, 159 struct mem_region **avail, int *availsz) 160 { 161 ofw_mem_regions(phys,physsz,avail,availsz); 162 } 163 164 static vm_offset_t 165 chrp_real_maxaddr(platform_t plat) 166 { 167 return (realmaxaddr); 168 } 169 170 static u_long 171 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) 172 { 173 phandle_t phandle; 174 int32_t ticks = -1; 175 176 phandle = cpuref->cr_hwref; 177 178 OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); 179 180 if (ticks <= 0) 181 panic("Unable to determine timebase frequency!"); 182 183 return (ticks); 184 } 185 186 static int 187 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 188 { 189 char buf[8]; 190 phandle_t cpu, dev, root; 191 int res, cpuid; 192 193 root = OF_peer(0); 194 195 dev = OF_child(root); 196 while (dev != 0) { 197 res = OF_getprop(dev, "name", buf, sizeof(buf)); 198 if (res > 0 && strcmp(buf, "cpus") == 0) 199 break; 200 dev = OF_peer(dev); 201 } 202 if (dev == 0) { 203 /* 204 * psim doesn't have a name property on the /cpus node, 205 * but it can be found directly 206 */ 207 dev = OF_finddevice("/cpus"); 208 if (dev == 0) 209 return (ENOENT); 210 } 211 212 cpu = OF_child(dev); 213 214 while (cpu != 0) { 215 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 216 if (res > 0 && strcmp(buf, "cpu") == 0) 217 break; 218 cpu = OF_peer(cpu); 219 } 220 if (cpu == 0) 221 return (ENOENT); 222 223 cpuref->cr_hwref = cpu; 224 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 225 sizeof(cpuid)); 226 if (res <= 0) 227 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 228 if (res <= 0) 229 cpuid = 0; 230 cpuref->cr_cpuid = cpuid; 231 232 return (0); 233 } 234 235 static int 236 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 237 { 238 char buf[8]; 239 phandle_t cpu; 240 int i, res, cpuid; 241 242 /* Check for whether it should be the next thread */ 243 res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); 244 if (res > 0) { 245 cell_t interrupt_servers[res/sizeof(cell_t)]; 246 OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", 247 interrupt_servers, res); 248 for (i = 0; i < res/sizeof(cell_t) - 1; i++) { 249 if (interrupt_servers[i] == cpuref->cr_cpuid) { 250 cpuref->cr_cpuid = interrupt_servers[i+1]; 251 return (0); 252 } 253 } 254 } 255 256 /* Next CPU core/package */ 257 cpu = OF_peer(cpuref->cr_hwref); 258 while (cpu != 0) { 259 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 260 if (res > 0 && strcmp(buf, "cpu") == 0) 261 break; 262 cpu = OF_peer(cpu); 263 } 264 if (cpu == 0) 265 return (ENOENT); 266 267 cpuref->cr_hwref = cpu; 268 res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, 269 sizeof(cpuid)); 270 if (res <= 0) 271 res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); 272 if (res <= 0) 273 cpuid = 0; 274 cpuref->cr_cpuid = cpuid; 275 276 return (0); 277 } 278 279 static int 280 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 281 { 282 ihandle_t inst; 283 phandle_t bsp, chosen; 284 int res, cpuid; 285 286 chosen = OF_finddevice("/chosen"); 287 if (chosen == 0) 288 return (ENXIO); 289 290 res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); 291 if (res < 0) 292 return (ENXIO); 293 294 bsp = OF_instance_to_package(inst); 295 296 /* Pick the primary thread. Can it be any other? */ 297 cpuref->cr_hwref = bsp; 298 res = OF_getprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid, 299 sizeof(cpuid)); 300 if (res <= 0) 301 res = OF_getprop(bsp, "reg", &cpuid, sizeof(cpuid)); 302 if (res <= 0) 303 cpuid = 0; 304 cpuref->cr_cpuid = cpuid; 305 306 return (0); 307 } 308 309 #ifdef SMP 310 static int 311 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) 312 { 313 cell_t start_cpu; 314 int result, err, timeout; 315 316 if (!rtas_exists()) { 317 printf("RTAS uninitialized: unable to start AP %d\n", 318 pc->pc_cpuid); 319 return (ENXIO); 320 } 321 322 start_cpu = rtas_token_lookup("start-cpu"); 323 if (start_cpu == -1) { 324 printf("RTAS unknown method: unable to start AP %d\n", 325 pc->pc_cpuid); 326 return (ENXIO); 327 } 328 329 ap_pcpu = pc; 330 powerpc_sync(); 331 332 result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc, 333 &err); 334 if (result < 0 || err != 0) { 335 printf("RTAS error (%d/%d): unable to start AP %d\n", 336 result, err, pc->pc_cpuid); 337 return (ENXIO); 338 } 339 340 timeout = 10000; 341 while (!pc->pc_awake && timeout--) 342 DELAY(100); 343 344 return ((pc->pc_awake) ? 0 : EBUSY); 345 } 346 347 static struct cpu_group * 348 chrp_smp_topo(platform_t plat) 349 { 350 struct pcpu *pc, *last_pc; 351 int i, ncores, ncpus; 352 353 ncores = ncpus = 0; 354 last_pc = NULL; 355 for (i = 0; i <= mp_maxid; i++) { 356 pc = pcpu_find(i); 357 if (pc == NULL) 358 continue; 359 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) 360 ncores++; 361 last_pc = pc; 362 ncpus++; 363 } 364 365 if (ncpus % ncores != 0) { 366 printf("WARNING: Irregular SMP topology. Performance may be " 367 "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); 368 return (smp_topo_none()); 369 } 370 371 /* Don't do anything fancier for non-threaded SMP */ 372 if (ncpus == ncores) 373 return (smp_topo_none()); 374 375 return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); 376 } 377 #endif 378 379 static void 380 chrp_reset(platform_t platform) 381 { 382 OF_reboot(); 383 } 384 385 #ifdef __powerpc64__ 386 static void 387 phyp_cpu_idle(sbintime_t sbt) 388 { 389 #ifdef NOTYET /* Causes hangs on QEMU */ 390 phyp_hcall(H_CEDE); 391 #endif 392 } 393 394 static void 395 chrp_smp_ap_init(platform_t platform) 396 { 397 if (!(mfmsr() & PSL_HV)) { 398 /* Set interrupt priority */ 399 phyp_hcall(H_CPPR, 0xff); 400 401 /* Register VPA */ 402 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid), splpar_vpa); 403 } 404 } 405 #else 406 static void 407 chrp_smp_ap_init(platform_t platform) 408 { 409 } 410 #endif 411 412