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