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.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 /* Some systems (e.g. QEMU) need Open Firmware to stand down */ 155 ofw_quiesce(); 156 157 return (0); 158 } 159 160 void 161 chrp_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 167 static vm_offset_t 168 chrp_real_maxaddr(platform_t plat) 169 { 170 return (realmaxaddr); 171 } 172 173 static u_long 174 chrp_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 189 static int 190 chrp_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 238 static int 239 chrp_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 282 static int 283 chrp_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 313 static int 314 chrp_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 350 static struct cpu_group * 351 chrp_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 382 static void 383 chrp_reset(platform_t platform) 384 { 385 OF_reboot(); 386 } 387 388 #ifdef __powerpc64__ 389 static void 390 phyp_cpu_idle(sbintime_t sbt) 391 { 392 phyp_hcall(H_CEDE); 393 } 394 395 static void 396 chrp_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 407 static void 408 chrp_smp_ap_init(platform_t platform) 409 { 410 } 411 #endif 412 413