1 /*- 2 * Copyright (c) 2015 Nathan Whitehorn 3 * Copyright (c) 2017-2018 Semihalf 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 #include <powerpc/aim/mmu_oea64.h> 54 55 #include "platform_if.h" 56 #include "opal.h" 57 58 #ifdef SMP 59 extern void *ap_pcpu; 60 #endif 61 62 static int powernv_probe(platform_t); 63 static int powernv_attach(platform_t); 64 void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz, 65 struct mem_region *avail, int *availsz); 66 static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref); 67 static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref); 68 static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref); 69 static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref); 70 static void powernv_smp_ap_init(platform_t); 71 #ifdef SMP 72 static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu); 73 static struct cpu_group *powernv_smp_topo(platform_t plat); 74 #endif 75 static void powernv_reset(platform_t); 76 static void powernv_cpu_idle(sbintime_t sbt); 77 static int powernv_cpuref_init(void); 78 79 static platform_method_t powernv_methods[] = { 80 PLATFORMMETHOD(platform_probe, powernv_probe), 81 PLATFORMMETHOD(platform_attach, powernv_attach), 82 PLATFORMMETHOD(platform_mem_regions, powernv_mem_regions), 83 PLATFORMMETHOD(platform_timebase_freq, powernv_timebase_freq), 84 85 PLATFORMMETHOD(platform_smp_ap_init, powernv_smp_ap_init), 86 PLATFORMMETHOD(platform_smp_first_cpu, powernv_smp_first_cpu), 87 PLATFORMMETHOD(platform_smp_next_cpu, powernv_smp_next_cpu), 88 PLATFORMMETHOD(platform_smp_get_bsp, powernv_smp_get_bsp), 89 #ifdef SMP 90 PLATFORMMETHOD(platform_smp_start_cpu, powernv_smp_start_cpu), 91 PLATFORMMETHOD(platform_smp_topo, powernv_smp_topo), 92 #endif 93 94 PLATFORMMETHOD(platform_reset, powernv_reset), 95 96 { 0, 0 } 97 }; 98 99 static platform_def_t powernv_platform = { 100 "powernv", 101 powernv_methods, 102 0 103 }; 104 105 static struct cpuref platform_cpuref[MAXCPU]; 106 static int platform_cpuref_cnt; 107 static int platform_cpuref_valid; 108 109 PLATFORM_DEF(powernv_platform); 110 111 static uint64_t powernv_boot_pir; 112 113 static int 114 powernv_probe(platform_t plat) 115 { 116 if (opal_check() == 0) 117 return (BUS_PROBE_SPECIFIC); 118 119 return (ENXIO); 120 } 121 122 static int 123 powernv_attach(platform_t plat) 124 { 125 uint32_t nptlp, shift = 0, slb_encoding = 0; 126 int32_t lp_size, lp_encoding; 127 char buf[255]; 128 pcell_t prop; 129 phandle_t cpu; 130 int res, len, node, idx; 131 register_t msr; 132 133 /* Ping OPAL again just to make sure */ 134 opal_check(); 135 136 #if BYTE_ORDER == LITTLE_ENDIAN 137 opal_call(OPAL_REINIT_CPUS, 2 /* Little endian */); 138 #else 139 opal_call(OPAL_REINIT_CPUS, 1 /* Big endian */); 140 #endif 141 142 cpu_idle_hook = powernv_cpu_idle; 143 powernv_boot_pir = mfspr(SPR_PIR); 144 145 /* LPID must not be altered when PSL_DR or PSL_IR is set */ 146 msr = mfmsr(); 147 mtmsr(msr & ~(PSL_DR | PSL_IR)); 148 149 /* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */ 150 mtspr(SPR_LPID, 0); 151 isync(); 152 153 mtmsr(msr); 154 155 mtspr(SPR_LPCR, LPCR_LPES); 156 isync(); 157 158 /* Init CPU bits */ 159 powernv_smp_ap_init(plat); 160 161 powernv_cpuref_init(); 162 163 /* Set SLB count from device tree */ 164 cpu = OF_peer(0); 165 cpu = OF_child(cpu); 166 while (cpu != 0) { 167 res = OF_getprop(cpu, "name", buf, sizeof(buf)); 168 if (res > 0 && strcmp(buf, "cpus") == 0) 169 break; 170 cpu = OF_peer(cpu); 171 } 172 if (cpu == 0) 173 goto out; 174 175 cpu = OF_child(cpu); 176 while (cpu != 0) { 177 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 178 if (res > 0 && strcmp(buf, "cpu") == 0) 179 break; 180 cpu = OF_peer(cpu); 181 } 182 if (cpu == 0) 183 goto out; 184 185 res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop)); 186 if (res > 0) 187 n_slbs = prop; 188 189 /* 190 * Scan the large page size property for PAPR compatible machines. 191 * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' 192 * for the encoding of the property. 193 */ 194 195 len = OF_getproplen(node, "ibm,segment-page-sizes"); 196 if (len > 0) { 197 /* 198 * We have to use a variable length array on the stack 199 * since we have very limited stack space. 200 */ 201 pcell_t arr[len/sizeof(cell_t)]; 202 res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr, 203 sizeof(arr)); 204 len /= 4; 205 idx = 0; 206 while (len > 0) { 207 shift = arr[idx]; 208 slb_encoding = arr[idx + 1]; 209 nptlp = arr[idx + 2]; 210 idx += 3; 211 len -= 3; 212 while (len > 0 && nptlp) { 213 lp_size = arr[idx]; 214 lp_encoding = arr[idx+1]; 215 if (slb_encoding == SLBV_L && lp_encoding == 0) 216 break; 217 218 idx += 2; 219 len -= 2; 220 nptlp--; 221 } 222 if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0) 223 break; 224 } 225 226 if (len == 0) 227 panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) " 228 "not supported by this system."); 229 230 moea64_large_page_shift = shift; 231 moea64_large_page_size = 1ULL << lp_size; 232 } 233 234 out: 235 return (0); 236 } 237 238 239 void 240 powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, 241 struct mem_region *avail, int *availsz) 242 { 243 244 ofw_mem_regions(phys, physsz, avail, availsz); 245 } 246 247 static u_long 248 powernv_timebase_freq(platform_t plat, struct cpuref *cpuref) 249 { 250 char buf[8]; 251 phandle_t cpu, dev, root; 252 int res; 253 int32_t ticks = -1; 254 255 root = OF_peer(0); 256 dev = OF_child(root); 257 while (dev != 0) { 258 res = OF_getprop(dev, "name", buf, sizeof(buf)); 259 if (res > 0 && strcmp(buf, "cpus") == 0) 260 break; 261 dev = OF_peer(dev); 262 } 263 264 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 265 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 266 if (res > 0 && strcmp(buf, "cpu") == 0) 267 break; 268 } 269 if (cpu == 0) 270 return (512000000); 271 272 OF_getencprop(cpu, "timebase-frequency", &ticks, sizeof(ticks)); 273 274 if (ticks <= 0) 275 panic("Unable to determine timebase frequency!"); 276 277 return (ticks); 278 279 } 280 281 static int 282 powernv_cpuref_init(void) 283 { 284 phandle_t cpu, dev; 285 char buf[32]; 286 int a, res, tmp_cpuref_cnt; 287 static struct cpuref tmp_cpuref[MAXCPU]; 288 cell_t interrupt_servers[32]; 289 uint64_t bsp; 290 291 if (platform_cpuref_valid) 292 return (0); 293 294 dev = OF_peer(0); 295 dev = OF_child(dev); 296 while (dev != 0) { 297 res = OF_getprop(dev, "name", buf, sizeof(buf)); 298 if (res > 0 && strcmp(buf, "cpus") == 0) 299 break; 300 dev = OF_peer(dev); 301 } 302 303 bsp = 0; 304 tmp_cpuref_cnt = 0; 305 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 306 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 307 if (res > 0 && strcmp(buf, "cpu") == 0) { 308 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); 309 if (res > 0) { 310 311 312 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", 313 interrupt_servers, res); 314 315 for (a = 0; a < res/sizeof(cell_t); a++) { 316 tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a]; 317 tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt; 318 319 if (interrupt_servers[a] == (uint32_t)powernv_boot_pir) 320 bsp = tmp_cpuref_cnt; 321 322 tmp_cpuref_cnt++; 323 } 324 } 325 } 326 } 327 328 /* Map IDs, so BSP has CPUID 0 regardless of hwref */ 329 for (a = bsp; a < tmp_cpuref_cnt; a++) { 330 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 331 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 332 platform_cpuref_cnt++; 333 } 334 for (a = 0; a < bsp; a++) { 335 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 336 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 337 platform_cpuref_cnt++; 338 } 339 340 platform_cpuref_valid = 1; 341 342 return (0); 343 } 344 345 static int 346 powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 347 { 348 if (platform_cpuref_valid == 0) 349 return (EINVAL); 350 351 cpuref->cr_cpuid = 0; 352 cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 353 354 return (0); 355 } 356 357 static int 358 powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 359 { 360 int id; 361 362 if (platform_cpuref_valid == 0) 363 return (EINVAL); 364 365 id = cpuref->cr_cpuid + 1; 366 if (id >= platform_cpuref_cnt) 367 return (ENOENT); 368 369 cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid; 370 cpuref->cr_hwref = platform_cpuref[id].cr_hwref; 371 372 return (0); 373 } 374 375 static int 376 powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 377 { 378 379 cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid; 380 cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 381 return (0); 382 } 383 384 #ifdef SMP 385 static int 386 powernv_smp_start_cpu(platform_t plat, struct pcpu *pc) 387 { 388 int result; 389 390 ap_pcpu = pc; 391 powerpc_sync(); 392 393 result = opal_call(OPAL_START_CPU, pc->pc_hwref, EXC_RST); 394 if (result != OPAL_SUCCESS) { 395 printf("OPAL error (%d): unable to start AP %d\n", 396 result, (int)pc->pc_hwref); 397 return (ENXIO); 398 } 399 400 return (0); 401 } 402 403 static struct cpu_group * 404 powernv_smp_topo(platform_t plat) 405 { 406 char buf[8]; 407 phandle_t cpu, dev, root; 408 int res, nthreads; 409 410 root = OF_peer(0); 411 412 dev = OF_child(root); 413 while (dev != 0) { 414 res = OF_getprop(dev, "name", buf, sizeof(buf)); 415 if (res > 0 && strcmp(buf, "cpus") == 0) 416 break; 417 dev = OF_peer(dev); 418 } 419 420 nthreads = 1; 421 for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 422 res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 423 if (res <= 0 || strcmp(buf, "cpu") != 0) 424 continue; 425 426 res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); 427 428 if (res >= 0) 429 nthreads = res / sizeof(cell_t); 430 else 431 nthreads = 1; 432 break; 433 } 434 435 if (mp_ncpus % nthreads != 0) { 436 printf("WARNING: Irregular SMP topology. Performance may be " 437 "suboptimal (%d threads, %d on first core)\n", 438 mp_ncpus, nthreads); 439 return (smp_topo_none()); 440 } 441 442 /* Don't do anything fancier for non-threaded SMP */ 443 if (nthreads == 1) 444 return (smp_topo_none()); 445 446 return (smp_topo_1level(CG_SHARE_L1, nthreads, CG_FLAG_SMT)); 447 } 448 449 #endif 450 451 static void 452 powernv_reset(platform_t platform) 453 { 454 455 opal_call(OPAL_CEC_REBOOT); 456 } 457 458 static void 459 powernv_smp_ap_init(platform_t platform) 460 { 461 } 462 463 static void 464 powernv_cpu_idle(sbintime_t sbt) 465 { 466 } 467