1fb3855e0SWojciech Macek /*- 2fb3855e0SWojciech Macek * Copyright (c) 2015 Nathan Whitehorn 3f0393bbfSWojciech Macek * Copyright (c) 2017-2018 Semihalf 4fb3855e0SWojciech Macek * All rights reserved. 5fb3855e0SWojciech Macek * 6fb3855e0SWojciech Macek * Redistribution and use in source and binary forms, with or without 7fb3855e0SWojciech Macek * modification, are permitted provided that the following conditions 8fb3855e0SWojciech Macek * are met: 9fb3855e0SWojciech Macek * 10fb3855e0SWojciech Macek * 1. Redistributions of source code must retain the above copyright 11fb3855e0SWojciech Macek * notice, this list of conditions and the following disclaimer. 12fb3855e0SWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright 13fb3855e0SWojciech Macek * notice, this list of conditions and the following disclaimer in the 14fb3855e0SWojciech Macek * documentation and/or other materials provided with the distribution. 15fb3855e0SWojciech Macek * 16fb3855e0SWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17fb3855e0SWojciech Macek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18fb3855e0SWojciech Macek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19fb3855e0SWojciech Macek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20fb3855e0SWojciech Macek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21fb3855e0SWojciech Macek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22fb3855e0SWojciech Macek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23fb3855e0SWojciech Macek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24fb3855e0SWojciech Macek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25fb3855e0SWojciech Macek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26fb3855e0SWojciech Macek */ 27fb3855e0SWojciech Macek 28fb3855e0SWojciech Macek #include <sys/cdefs.h> 29fb3855e0SWojciech Macek __FBSDID("$FreeBSD$"); 30fb3855e0SWojciech Macek 31fb3855e0SWojciech Macek #include <sys/param.h> 32fb3855e0SWojciech Macek #include <sys/systm.h> 33fb3855e0SWojciech Macek #include <sys/kernel.h> 34fb3855e0SWojciech Macek #include <sys/bus.h> 35fb3855e0SWojciech Macek #include <sys/pcpu.h> 36fb3855e0SWojciech Macek #include <sys/proc.h> 37fb3855e0SWojciech Macek #include <sys/smp.h> 38fb3855e0SWojciech Macek #include <vm/vm.h> 39fb3855e0SWojciech Macek #include <vm/pmap.h> 40fb3855e0SWojciech Macek 41fb3855e0SWojciech Macek #include <machine/bus.h> 42fb3855e0SWojciech Macek #include <machine/cpu.h> 43fb3855e0SWojciech Macek #include <machine/hid.h> 44fb3855e0SWojciech Macek #include <machine/platformvar.h> 45fb3855e0SWojciech Macek #include <machine/pmap.h> 46fb3855e0SWojciech Macek #include <machine/rtas.h> 47fb3855e0SWojciech Macek #include <machine/smp.h> 48fb3855e0SWojciech Macek #include <machine/spr.h> 49fb3855e0SWojciech Macek #include <machine/trap.h> 50fb3855e0SWojciech Macek 51fb3855e0SWojciech Macek #include <dev/ofw/openfirm.h> 52e48f804fSJustin Hibbits #include <dev/ofw/ofw_bus.h> 53e48f804fSJustin Hibbits #include <dev/ofw/ofw_bus_subr.h> 54fb3855e0SWojciech Macek #include <machine/ofw_machdep.h> 55504d9b60SWojciech Macek #include <powerpc/aim/mmu_oea64.h> 56fb3855e0SWojciech Macek 57fb3855e0SWojciech Macek #include "platform_if.h" 58fb3855e0SWojciech Macek #include "opal.h" 59fb3855e0SWojciech Macek 60fb3855e0SWojciech Macek #ifdef SMP 61fb3855e0SWojciech Macek extern void *ap_pcpu; 62fb3855e0SWojciech Macek #endif 63fb3855e0SWojciech Macek 64d49fc192SJustin Hibbits void (*powernv_smp_ap_extra_init)(void); 65d49fc192SJustin Hibbits 66fb3855e0SWojciech Macek static int powernv_probe(platform_t); 67fb3855e0SWojciech Macek static int powernv_attach(platform_t); 68fb3855e0SWojciech Macek void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz, 69fb3855e0SWojciech Macek struct mem_region *avail, int *availsz); 7049d9a597SJustin Hibbits static void powernv_numa_mem_regions(platform_t plat, struct numa_mem_region *phys, int *physsz); 71fb3855e0SWojciech Macek static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref); 72fb3855e0SWojciech Macek static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref); 73fb3855e0SWojciech Macek static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref); 74fb3855e0SWojciech Macek static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref); 75fb3855e0SWojciech Macek static void powernv_smp_ap_init(platform_t); 76fb3855e0SWojciech Macek #ifdef SMP 77fb3855e0SWojciech Macek static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu); 78bba9cbe3SConrad Meyer static void powernv_smp_probe_threads(platform_t); 79fb3855e0SWojciech Macek static struct cpu_group *powernv_smp_topo(platform_t plat); 80fb3855e0SWojciech Macek #endif 81fb3855e0SWojciech Macek static void powernv_reset(platform_t); 8201d7bda7SWojciech Macek static void powernv_cpu_idle(sbintime_t sbt); 83f0393bbfSWojciech Macek static int powernv_cpuref_init(void); 84490ebb8fSJustin Hibbits static int powernv_node_numa_domain(platform_t platform, phandle_t node); 85fb3855e0SWojciech Macek 86fb3855e0SWojciech Macek static platform_method_t powernv_methods[] = { 87fb3855e0SWojciech Macek PLATFORMMETHOD(platform_probe, powernv_probe), 88fb3855e0SWojciech Macek PLATFORMMETHOD(platform_attach, powernv_attach), 89fb3855e0SWojciech Macek PLATFORMMETHOD(platform_mem_regions, powernv_mem_regions), 9049d9a597SJustin Hibbits PLATFORMMETHOD(platform_numa_mem_regions, powernv_numa_mem_regions), 91fb3855e0SWojciech Macek PLATFORMMETHOD(platform_timebase_freq, powernv_timebase_freq), 92fb3855e0SWojciech Macek 93fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_ap_init, powernv_smp_ap_init), 94fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_first_cpu, powernv_smp_first_cpu), 95fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_next_cpu, powernv_smp_next_cpu), 96fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_get_bsp, powernv_smp_get_bsp), 97fb3855e0SWojciech Macek #ifdef SMP 98fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_start_cpu, powernv_smp_start_cpu), 99bba9cbe3SConrad Meyer PLATFORMMETHOD(platform_smp_probe_threads, powernv_smp_probe_threads), 100fb3855e0SWojciech Macek PLATFORMMETHOD(platform_smp_topo, powernv_smp_topo), 101fb3855e0SWojciech Macek #endif 102490ebb8fSJustin Hibbits PLATFORMMETHOD(platform_node_numa_domain, powernv_node_numa_domain), 103fb3855e0SWojciech Macek 104fb3855e0SWojciech Macek PLATFORMMETHOD(platform_reset, powernv_reset), 105fb3855e0SWojciech Macek { 0, 0 } 106fb3855e0SWojciech Macek }; 107fb3855e0SWojciech Macek 108fb3855e0SWojciech Macek static platform_def_t powernv_platform = { 109fb3855e0SWojciech Macek "powernv", 110fb3855e0SWojciech Macek powernv_methods, 111fb3855e0SWojciech Macek 0 112fb3855e0SWojciech Macek }; 113fb3855e0SWojciech Macek 114f0393bbfSWojciech Macek static struct cpuref platform_cpuref[MAXCPU]; 115f0393bbfSWojciech Macek static int platform_cpuref_cnt; 116f0393bbfSWojciech Macek static int platform_cpuref_valid; 117490ebb8fSJustin Hibbits static int platform_associativity; 118f0393bbfSWojciech Macek 119fb3855e0SWojciech Macek PLATFORM_DEF(powernv_platform); 120fb3855e0SWojciech Macek 121f0393bbfSWojciech Macek static uint64_t powernv_boot_pir; 122504d9b60SWojciech Macek 123fb3855e0SWojciech Macek static int 124fb3855e0SWojciech Macek powernv_probe(platform_t plat) 125fb3855e0SWojciech Macek { 126fb3855e0SWojciech Macek if (opal_check() == 0) 127fb3855e0SWojciech Macek return (BUS_PROBE_SPECIFIC); 128fb3855e0SWojciech Macek 129fb3855e0SWojciech Macek return (ENXIO); 130fb3855e0SWojciech Macek } 131fb3855e0SWojciech Macek 132fb3855e0SWojciech Macek static int 133fb3855e0SWojciech Macek powernv_attach(platform_t plat) 134fb3855e0SWojciech Macek { 135504d9b60SWojciech Macek uint32_t nptlp, shift = 0, slb_encoding = 0; 136504d9b60SWojciech Macek int32_t lp_size, lp_encoding; 137504d9b60SWojciech Macek char buf[255]; 138490ebb8fSJustin Hibbits pcell_t refpoints[3]; 139504d9b60SWojciech Macek pcell_t prop; 140504d9b60SWojciech Macek phandle_t cpu; 141490ebb8fSJustin Hibbits phandle_t opal; 14272820025SNathan Whitehorn int res, len, idx; 14370bb600aSWojciech Macek register_t msr; 144*e2d6c417SLeandro Lupori bool has_lp; 145504d9b60SWojciech Macek 146fb3855e0SWojciech Macek /* Ping OPAL again just to make sure */ 147fb3855e0SWojciech Macek opal_check(); 148fb3855e0SWojciech Macek 149f0393bbfSWojciech Macek #if BYTE_ORDER == LITTLE_ENDIAN 150f0393bbfSWojciech Macek opal_call(OPAL_REINIT_CPUS, 2 /* Little endian */); 151f0393bbfSWojciech Macek #else 152f0393bbfSWojciech Macek opal_call(OPAL_REINIT_CPUS, 1 /* Big endian */); 153f0393bbfSWojciech Macek #endif 154490ebb8fSJustin Hibbits opal = OF_finddevice("/ibm,opal"); 155490ebb8fSJustin Hibbits 156490ebb8fSJustin Hibbits platform_associativity = 4; /* Skiboot default. */ 157490ebb8fSJustin Hibbits if (OF_getencprop(opal, "ibm,associativity-reference-points", refpoints, 158490ebb8fSJustin Hibbits sizeof(refpoints)) > 0) { 159490ebb8fSJustin Hibbits platform_associativity = refpoints[0]; 160490ebb8fSJustin Hibbits } 161f0393bbfSWojciech Macek 1626d13fd63SWojciech Macek if (cpu_idle_hook == NULL) 16301d7bda7SWojciech Macek cpu_idle_hook = powernv_cpu_idle; 1646d13fd63SWojciech Macek 165504d9b60SWojciech Macek powernv_boot_pir = mfspr(SPR_PIR); 16601d7bda7SWojciech Macek 16770bb600aSWojciech Macek /* LPID must not be altered when PSL_DR or PSL_IR is set */ 16870bb600aSWojciech Macek msr = mfmsr(); 16970bb600aSWojciech Macek mtmsr(msr & ~(PSL_DR | PSL_IR)); 17070bb600aSWojciech Macek 17170bb600aSWojciech Macek /* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */ 17270bb600aSWojciech Macek mtspr(SPR_LPID, 0); 17370bb600aSWojciech Macek isync(); 17470bb600aSWojciech Macek 175ef6da5e5SJustin Hibbits if (cpu_features2 & PPC_FEATURE2_ARCH_3_00) 176ef6da5e5SJustin Hibbits lpcr |= LPCR_HVICE; 177ef6da5e5SJustin Hibbits 178c16359cfSBrandon Bergren #if BYTE_ORDER == LITTLE_ENDIAN 179c16359cfSBrandon Bergren lpcr |= LPCR_ILE; 180c16359cfSBrandon Bergren #endif 181c16359cfSBrandon Bergren 182ef6da5e5SJustin Hibbits mtspr(SPR_LPCR, lpcr); 18370bb600aSWojciech Macek isync(); 18470bb600aSWojciech Macek 1856d13fd63SWojciech Macek mtmsr(msr); 1866d13fd63SWojciech Macek 187f0393bbfSWojciech Macek powernv_cpuref_init(); 188f0393bbfSWojciech Macek 189504d9b60SWojciech Macek /* Set SLB count from device tree */ 190504d9b60SWojciech Macek cpu = OF_peer(0); 191504d9b60SWojciech Macek cpu = OF_child(cpu); 192504d9b60SWojciech Macek while (cpu != 0) { 193504d9b60SWojciech Macek res = OF_getprop(cpu, "name", buf, sizeof(buf)); 194504d9b60SWojciech Macek if (res > 0 && strcmp(buf, "cpus") == 0) 195504d9b60SWojciech Macek break; 196504d9b60SWojciech Macek cpu = OF_peer(cpu); 197504d9b60SWojciech Macek } 198504d9b60SWojciech Macek if (cpu == 0) 199504d9b60SWojciech Macek goto out; 200504d9b60SWojciech Macek 201504d9b60SWojciech Macek cpu = OF_child(cpu); 202504d9b60SWojciech Macek while (cpu != 0) { 203504d9b60SWojciech Macek res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 204504d9b60SWojciech Macek if (res > 0 && strcmp(buf, "cpu") == 0) 205504d9b60SWojciech Macek break; 206504d9b60SWojciech Macek cpu = OF_peer(cpu); 207504d9b60SWojciech Macek } 208504d9b60SWojciech Macek if (cpu == 0) 209504d9b60SWojciech Macek goto out; 210504d9b60SWojciech Macek 211504d9b60SWojciech Macek res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop)); 212504d9b60SWojciech Macek if (res > 0) 213504d9b60SWojciech Macek n_slbs = prop; 214504d9b60SWojciech Macek 215504d9b60SWojciech Macek /* 216504d9b60SWojciech Macek * Scan the large page size property for PAPR compatible machines. 217504d9b60SWojciech Macek * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' 218504d9b60SWojciech Macek * for the encoding of the property. 219504d9b60SWojciech Macek */ 220504d9b60SWojciech Macek 22172820025SNathan Whitehorn len = OF_getproplen(cpu, "ibm,segment-page-sizes"); 222504d9b60SWojciech Macek if (len > 0) { 223504d9b60SWojciech Macek /* 224504d9b60SWojciech Macek * We have to use a variable length array on the stack 225504d9b60SWojciech Macek * since we have very limited stack space. 226504d9b60SWojciech Macek */ 227504d9b60SWojciech Macek pcell_t arr[len/sizeof(cell_t)]; 228504d9b60SWojciech Macek res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr, 229504d9b60SWojciech Macek sizeof(arr)); 230504d9b60SWojciech Macek len /= 4; 231504d9b60SWojciech Macek idx = 0; 232*e2d6c417SLeandro Lupori has_lp = false; 233504d9b60SWojciech Macek while (len > 0) { 234504d9b60SWojciech Macek shift = arr[idx]; 235504d9b60SWojciech Macek slb_encoding = arr[idx + 1]; 236504d9b60SWojciech Macek nptlp = arr[idx + 2]; 237504d9b60SWojciech Macek idx += 3; 238504d9b60SWojciech Macek len -= 3; 239504d9b60SWojciech Macek while (len > 0 && nptlp) { 240504d9b60SWojciech Macek lp_size = arr[idx]; 241504d9b60SWojciech Macek lp_encoding = arr[idx+1]; 242504d9b60SWojciech Macek if (slb_encoding == SLBV_L && lp_encoding == 0) 243*e2d6c417SLeandro Lupori has_lp = true; 244*e2d6c417SLeandro Lupori 245*e2d6c417SLeandro Lupori if (slb_encoding == SLB_PGSZ_4K_4K && 246*e2d6c417SLeandro Lupori lp_encoding == LP_4K_16M) 247*e2d6c417SLeandro Lupori moea64_has_lp_4k_16m = true; 248504d9b60SWojciech Macek 249504d9b60SWojciech Macek idx += 2; 250504d9b60SWojciech Macek len -= 2; 251504d9b60SWojciech Macek nptlp--; 252504d9b60SWojciech Macek } 253*e2d6c417SLeandro Lupori if (has_lp && moea64_has_lp_4k_16m) 254504d9b60SWojciech Macek break; 255504d9b60SWojciech Macek } 256504d9b60SWojciech Macek 257*e2d6c417SLeandro Lupori if (!has_lp) 258504d9b60SWojciech Macek panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) " 259504d9b60SWojciech Macek "not supported by this system."); 260504d9b60SWojciech Macek 261504d9b60SWojciech Macek moea64_large_page_shift = shift; 262504d9b60SWojciech Macek moea64_large_page_size = 1ULL << lp_size; 263504d9b60SWojciech Macek } 264504d9b60SWojciech Macek 265504d9b60SWojciech Macek out: 266fb3855e0SWojciech Macek return (0); 267fb3855e0SWojciech Macek } 268fb3855e0SWojciech Macek 269fb3855e0SWojciech Macek void 270fb3855e0SWojciech Macek powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, 271fb3855e0SWojciech Macek struct mem_region *avail, int *availsz) 272fb3855e0SWojciech Macek { 273fb3855e0SWojciech Macek 274fb3855e0SWojciech Macek ofw_mem_regions(phys, physsz, avail, availsz); 275fb3855e0SWojciech Macek } 276fb3855e0SWojciech Macek 27749d9a597SJustin Hibbits static void 27849d9a597SJustin Hibbits powernv_numa_mem_regions(platform_t plat, struct numa_mem_region *phys, int *physsz) 27949d9a597SJustin Hibbits { 28049d9a597SJustin Hibbits 28149d9a597SJustin Hibbits ofw_numa_mem_regions(phys, physsz); 28249d9a597SJustin Hibbits } 28349d9a597SJustin Hibbits 284fb3855e0SWojciech Macek static u_long 285fb3855e0SWojciech Macek powernv_timebase_freq(platform_t plat, struct cpuref *cpuref) 286fb3855e0SWojciech Macek { 287fb3855e0SWojciech Macek char buf[8]; 288fb3855e0SWojciech Macek phandle_t cpu, dev, root; 289f0393bbfSWojciech Macek int res; 290f0393bbfSWojciech Macek int32_t ticks = -1; 291fb3855e0SWojciech Macek 292fb3855e0SWojciech Macek root = OF_peer(0); 293fb3855e0SWojciech Macek dev = OF_child(root); 294fb3855e0SWojciech Macek while (dev != 0) { 295fb3855e0SWojciech Macek res = OF_getprop(dev, "name", buf, sizeof(buf)); 296fb3855e0SWojciech Macek if (res > 0 && strcmp(buf, "cpus") == 0) 297fb3855e0SWojciech Macek break; 298fb3855e0SWojciech Macek dev = OF_peer(dev); 299fb3855e0SWojciech Macek } 300fb3855e0SWojciech Macek 301f0393bbfSWojciech Macek for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 302fb3855e0SWojciech Macek res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 303fb3855e0SWojciech Macek if (res > 0 && strcmp(buf, "cpu") == 0) 304fb3855e0SWojciech Macek break; 305fb3855e0SWojciech Macek } 306fb3855e0SWojciech Macek if (cpu == 0) 307f0393bbfSWojciech Macek return (512000000); 308fb3855e0SWojciech Macek 309f0393bbfSWojciech Macek OF_getencprop(cpu, "timebase-frequency", &ticks, sizeof(ticks)); 310f0393bbfSWojciech Macek 311f0393bbfSWojciech Macek if (ticks <= 0) 312f0393bbfSWojciech Macek panic("Unable to determine timebase frequency!"); 313f0393bbfSWojciech Macek 314f0393bbfSWojciech Macek return (ticks); 315f0393bbfSWojciech Macek 316f0393bbfSWojciech Macek } 317f0393bbfSWojciech Macek 318f0393bbfSWojciech Macek static int 319f0393bbfSWojciech Macek powernv_cpuref_init(void) 320f0393bbfSWojciech Macek { 321f0393bbfSWojciech Macek phandle_t cpu, dev; 322f0393bbfSWojciech Macek char buf[32]; 323f0393bbfSWojciech Macek int a, res, tmp_cpuref_cnt; 324f0393bbfSWojciech Macek static struct cpuref tmp_cpuref[MAXCPU]; 325f0393bbfSWojciech Macek cell_t interrupt_servers[32]; 326f0393bbfSWojciech Macek uint64_t bsp; 327f0393bbfSWojciech Macek 328f0393bbfSWojciech Macek if (platform_cpuref_valid) 329f0393bbfSWojciech Macek return (0); 330f0393bbfSWojciech Macek 331f0393bbfSWojciech Macek dev = OF_peer(0); 332f0393bbfSWojciech Macek dev = OF_child(dev); 333f0393bbfSWojciech Macek while (dev != 0) { 334f0393bbfSWojciech Macek res = OF_getprop(dev, "name", buf, sizeof(buf)); 335f0393bbfSWojciech Macek if (res > 0 && strcmp(buf, "cpus") == 0) 336f0393bbfSWojciech Macek break; 337f0393bbfSWojciech Macek dev = OF_peer(dev); 338f0393bbfSWojciech Macek } 339f0393bbfSWojciech Macek 340f0393bbfSWojciech Macek bsp = 0; 341f0393bbfSWojciech Macek tmp_cpuref_cnt = 0; 342f0393bbfSWojciech Macek for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 343f0393bbfSWojciech Macek res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 344f0393bbfSWojciech Macek if (res > 0 && strcmp(buf, "cpu") == 0) { 345e48f804fSJustin Hibbits if (!ofw_bus_node_status_okay(cpu)) 346e48f804fSJustin Hibbits continue; 347f0393bbfSWojciech Macek res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); 348f0393bbfSWojciech Macek if (res > 0) { 349f0393bbfSWojciech Macek OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", 350f0393bbfSWojciech Macek interrupt_servers, res); 351f0393bbfSWojciech Macek 352f0393bbfSWojciech Macek for (a = 0; a < res/sizeof(cell_t); a++) { 353f0393bbfSWojciech Macek tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a]; 354f0393bbfSWojciech Macek tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt; 355490ebb8fSJustin Hibbits tmp_cpuref[tmp_cpuref_cnt].cr_domain = 356490ebb8fSJustin Hibbits powernv_node_numa_domain(NULL, cpu); 357f0393bbfSWojciech Macek if (interrupt_servers[a] == (uint32_t)powernv_boot_pir) 358f0393bbfSWojciech Macek bsp = tmp_cpuref_cnt; 359f0393bbfSWojciech Macek 360f0393bbfSWojciech Macek tmp_cpuref_cnt++; 361f0393bbfSWojciech Macek } 362f0393bbfSWojciech Macek } 363f0393bbfSWojciech Macek } 364f0393bbfSWojciech Macek } 365f0393bbfSWojciech Macek 366f0393bbfSWojciech Macek /* Map IDs, so BSP has CPUID 0 regardless of hwref */ 367f0393bbfSWojciech Macek for (a = bsp; a < tmp_cpuref_cnt; a++) { 368f0393bbfSWojciech Macek platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 369f0393bbfSWojciech Macek platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 37049d9a597SJustin Hibbits platform_cpuref[platform_cpuref_cnt].cr_domain = tmp_cpuref[a].cr_domain; 371f0393bbfSWojciech Macek platform_cpuref_cnt++; 372f0393bbfSWojciech Macek } 373f0393bbfSWojciech Macek for (a = 0; a < bsp; a++) { 374f0393bbfSWojciech Macek platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; 375f0393bbfSWojciech Macek platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; 37649d9a597SJustin Hibbits platform_cpuref[platform_cpuref_cnt].cr_domain = tmp_cpuref[a].cr_domain; 377f0393bbfSWojciech Macek platform_cpuref_cnt++; 378f0393bbfSWojciech Macek } 379f0393bbfSWojciech Macek 380f0393bbfSWojciech Macek platform_cpuref_valid = 1; 381f0393bbfSWojciech Macek 382f0393bbfSWojciech Macek return (0); 383f0393bbfSWojciech Macek } 384f0393bbfSWojciech Macek 385f0393bbfSWojciech Macek static int 386f0393bbfSWojciech Macek powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref) 387f0393bbfSWojciech Macek { 388f0393bbfSWojciech Macek if (platform_cpuref_valid == 0) 389f0393bbfSWojciech Macek return (EINVAL); 390f0393bbfSWojciech Macek 391f0393bbfSWojciech Macek cpuref->cr_cpuid = 0; 392f0393bbfSWojciech Macek cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 39349d9a597SJustin Hibbits cpuref->cr_domain = platform_cpuref[0].cr_domain; 394fb3855e0SWojciech Macek 395fb3855e0SWojciech Macek return (0); 396fb3855e0SWojciech Macek } 397fb3855e0SWojciech Macek 398fb3855e0SWojciech Macek static int 399fb3855e0SWojciech Macek powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref) 400fb3855e0SWojciech Macek { 401f0393bbfSWojciech Macek int id; 402fb3855e0SWojciech Macek 403f0393bbfSWojciech Macek if (platform_cpuref_valid == 0) 404f0393bbfSWojciech Macek return (EINVAL); 405fb3855e0SWojciech Macek 406f0393bbfSWojciech Macek id = cpuref->cr_cpuid + 1; 407f0393bbfSWojciech Macek if (id >= platform_cpuref_cnt) 408fb3855e0SWojciech Macek return (ENOENT); 409fb3855e0SWojciech Macek 410f0393bbfSWojciech Macek cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid; 411f0393bbfSWojciech Macek cpuref->cr_hwref = platform_cpuref[id].cr_hwref; 41249d9a597SJustin Hibbits cpuref->cr_domain = platform_cpuref[id].cr_domain; 413fb3855e0SWojciech Macek 414fb3855e0SWojciech Macek return (0); 415fb3855e0SWojciech Macek } 416fb3855e0SWojciech Macek 417fb3855e0SWojciech Macek static int 418fb3855e0SWojciech Macek powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref) 419fb3855e0SWojciech Macek { 420fb3855e0SWojciech Macek 421f0393bbfSWojciech Macek cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid; 422f0393bbfSWojciech Macek cpuref->cr_hwref = platform_cpuref[0].cr_hwref; 42349d9a597SJustin Hibbits cpuref->cr_domain = platform_cpuref[0].cr_domain; 424fb3855e0SWojciech Macek return (0); 425fb3855e0SWojciech Macek } 426fb3855e0SWojciech Macek 427fb3855e0SWojciech Macek #ifdef SMP 428fb3855e0SWojciech Macek static int 429fb3855e0SWojciech Macek powernv_smp_start_cpu(platform_t plat, struct pcpu *pc) 430fb3855e0SWojciech Macek { 43101d7bda7SWojciech Macek int result; 432fb3855e0SWojciech Macek 433fb3855e0SWojciech Macek ap_pcpu = pc; 434fb3855e0SWojciech Macek powerpc_sync(); 435fb3855e0SWojciech Macek 436f0393bbfSWojciech Macek result = opal_call(OPAL_START_CPU, pc->pc_hwref, EXC_RST); 43701d7bda7SWojciech Macek if (result != OPAL_SUCCESS) { 43801d7bda7SWojciech Macek printf("OPAL error (%d): unable to start AP %d\n", 439f0393bbfSWojciech Macek result, (int)pc->pc_hwref); 440fb3855e0SWojciech Macek return (ENXIO); 441fb3855e0SWojciech Macek } 442fb3855e0SWojciech Macek 44301d7bda7SWojciech Macek return (0); 444fb3855e0SWojciech Macek } 445fb3855e0SWojciech Macek 446bba9cbe3SConrad Meyer static void 447bba9cbe3SConrad Meyer powernv_smp_probe_threads(platform_t plat) 448fb3855e0SWojciech Macek { 449f0393bbfSWojciech Macek char buf[8]; 450f0393bbfSWojciech Macek phandle_t cpu, dev, root; 451f0393bbfSWojciech Macek int res, nthreads; 452fb3855e0SWojciech Macek 453f0393bbfSWojciech Macek root = OF_peer(0); 454f0393bbfSWojciech Macek 455f0393bbfSWojciech Macek dev = OF_child(root); 456f0393bbfSWojciech Macek while (dev != 0) { 457f0393bbfSWojciech Macek res = OF_getprop(dev, "name", buf, sizeof(buf)); 458f0393bbfSWojciech Macek if (res > 0 && strcmp(buf, "cpus") == 0) 459f0393bbfSWojciech Macek break; 460f0393bbfSWojciech Macek dev = OF_peer(dev); 461fb3855e0SWojciech Macek } 462fb3855e0SWojciech Macek 463f0393bbfSWojciech Macek nthreads = 1; 464f0393bbfSWojciech Macek for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { 465f0393bbfSWojciech Macek res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); 466f0393bbfSWojciech Macek if (res <= 0 || strcmp(buf, "cpu") != 0) 467f0393bbfSWojciech Macek continue; 468f0393bbfSWojciech Macek 469f0393bbfSWojciech Macek res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); 470f0393bbfSWojciech Macek 471f0393bbfSWojciech Macek if (res >= 0) 472f0393bbfSWojciech Macek nthreads = res / sizeof(cell_t); 473f0393bbfSWojciech Macek else 474f0393bbfSWojciech Macek nthreads = 1; 475f0393bbfSWojciech Macek break; 476f0393bbfSWojciech Macek } 477f0393bbfSWojciech Macek 4786b83069eSConrad Meyer smp_threads_per_core = nthreads; 479bba9cbe3SConrad Meyer if (mp_ncpus % nthreads == 0) 480bba9cbe3SConrad Meyer mp_ncores = mp_ncpus / nthreads; 481bba9cbe3SConrad Meyer } 4826b83069eSConrad Meyer 483bba9cbe3SConrad Meyer static struct cpu_group * 484bba9cbe3SConrad Meyer powernv_smp_topo(platform_t plat) 485bba9cbe3SConrad Meyer { 486bba9cbe3SConrad Meyer if (mp_ncpus % smp_threads_per_core != 0) { 487fb3855e0SWojciech Macek printf("WARNING: Irregular SMP topology. Performance may be " 488f0393bbfSWojciech Macek "suboptimal (%d threads, %d on first core)\n", 489bba9cbe3SConrad Meyer mp_ncpus, smp_threads_per_core); 490fb3855e0SWojciech Macek return (smp_topo_none()); 491fb3855e0SWojciech Macek } 492fb3855e0SWojciech Macek 493fb3855e0SWojciech Macek /* Don't do anything fancier for non-threaded SMP */ 494bba9cbe3SConrad Meyer if (smp_threads_per_core == 1) 495fb3855e0SWojciech Macek return (smp_topo_none()); 496fb3855e0SWojciech Macek 497bba9cbe3SConrad Meyer return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core, 498bba9cbe3SConrad Meyer CG_FLAG_SMT)); 499fb3855e0SWojciech Macek } 500f0393bbfSWojciech Macek 501fb3855e0SWojciech Macek #endif 502fb3855e0SWojciech Macek 503fb3855e0SWojciech Macek static void 504fb3855e0SWojciech Macek powernv_reset(platform_t platform) 505fb3855e0SWojciech Macek { 50632d1354aSWojciech Macek 50732d1354aSWojciech Macek opal_call(OPAL_CEC_REBOOT); 508fb3855e0SWojciech Macek } 509fb3855e0SWojciech Macek 510fb3855e0SWojciech Macek static void 511fb3855e0SWojciech Macek powernv_smp_ap_init(platform_t platform) 512fb3855e0SWojciech Macek { 513ef6da5e5SJustin Hibbits 514d49fc192SJustin Hibbits if (powernv_smp_ap_extra_init != NULL) 515d49fc192SJustin Hibbits powernv_smp_ap_extra_init(); 516fb3855e0SWojciech Macek } 517fb3855e0SWojciech Macek 51801d7bda7SWojciech Macek static void 51901d7bda7SWojciech Macek powernv_cpu_idle(sbintime_t sbt) 52001d7bda7SWojciech Macek { 52101d7bda7SWojciech Macek } 5221223b40eSJustin Hibbits 523490ebb8fSJustin Hibbits static int 524490ebb8fSJustin Hibbits powernv_node_numa_domain(platform_t platform, phandle_t node) 525490ebb8fSJustin Hibbits { 526490ebb8fSJustin Hibbits /* XXX: Is locking necessary in here? */ 527490ebb8fSJustin Hibbits static int numa_domains[MAXMEMDOM]; 528490ebb8fSJustin Hibbits static int numa_max_domain; 529490ebb8fSJustin Hibbits cell_t associativity[5]; 530490ebb8fSJustin Hibbits int i, res; 531490ebb8fSJustin Hibbits 5326df6aae9SJustin Hibbits #ifndef NUMA 5336df6aae9SJustin Hibbits return (0); 5346df6aae9SJustin Hibbits #endif 5356df6aae9SJustin Hibbits if (vm_ndomains == 1) 5366df6aae9SJustin Hibbits return (0); 5376df6aae9SJustin Hibbits 538a9d8f71fSLeandro Lupori res = OF_getencprop(node, "ibm,associativity", 539a9d8f71fSLeandro Lupori associativity, sizeof(associativity)); 540490ebb8fSJustin Hibbits 541a9d8f71fSLeandro Lupori /* 542a9d8f71fSLeandro Lupori * If this node doesn't have associativity, or if there are not 543a9d8f71fSLeandro Lupori * enough elements in it, check its parent. 544a9d8f71fSLeandro Lupori */ 545a9d8f71fSLeandro Lupori if (res < (int)(sizeof(cell_t) * (platform_associativity + 1))) { 546a9d8f71fSLeandro Lupori node = OF_parent(node); 547490ebb8fSJustin Hibbits /* If already at the root, use default domain. */ 548a9d8f71fSLeandro Lupori if (node == 0) 549490ebb8fSJustin Hibbits return (0); 550a9d8f71fSLeandro Lupori return (powernv_node_numa_domain(platform, node)); 551a9d8f71fSLeandro Lupori } 552490ebb8fSJustin Hibbits 553490ebb8fSJustin Hibbits for (i = 0; i < numa_max_domain; i++) { 554490ebb8fSJustin Hibbits if (numa_domains[i] == associativity[platform_associativity]) 555490ebb8fSJustin Hibbits return (i); 556490ebb8fSJustin Hibbits } 557490ebb8fSJustin Hibbits if (i < MAXMEMDOM) 558490ebb8fSJustin Hibbits numa_domains[numa_max_domain++] = 559490ebb8fSJustin Hibbits associativity[platform_associativity]; 560490ebb8fSJustin Hibbits else 561490ebb8fSJustin Hibbits i = 0; 562490ebb8fSJustin Hibbits 563490ebb8fSJustin Hibbits return (i); 564490ebb8fSJustin Hibbits } 565490ebb8fSJustin Hibbits 5661223b40eSJustin Hibbits /* Set up the Nest MMU on POWER9 relatively early, but after pmap is setup. */ 5671223b40eSJustin Hibbits static void 5681223b40eSJustin Hibbits powernv_setup_nmmu(void *unused) 5691223b40eSJustin Hibbits { 5701223b40eSJustin Hibbits if (opal_check() != 0) 5711223b40eSJustin Hibbits return; 5721223b40eSJustin Hibbits opal_call(OPAL_NMMU_SET_PTCR, -1, mfspr(SPR_PTCR)); 5731223b40eSJustin Hibbits } 5741223b40eSJustin Hibbits 5751223b40eSJustin Hibbits SYSINIT(powernv_setup_nmmu, SI_SUB_CPU, SI_ORDER_ANY, powernv_setup_nmmu, NULL); 576