125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 2125cf1a30Sjl /* 2268ac2337Sjl * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2325cf1a30Sjl * Use is subject to license terms. 2425cf1a30Sjl */ 2525cf1a30Sjl 2625cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 2725cf1a30Sjl 2825cf1a30Sjl #include <sys/cpuvar.h> 2925cf1a30Sjl #include <sys/systm.h> 3025cf1a30Sjl #include <sys/sysmacros.h> 3125cf1a30Sjl #include <sys/promif.h> 3225cf1a30Sjl #include <sys/platform_module.h> 3325cf1a30Sjl #include <sys/cmn_err.h> 3425cf1a30Sjl #include <sys/errno.h> 3525cf1a30Sjl #include <sys/machsystm.h> 3625cf1a30Sjl #include <sys/bootconf.h> 3725cf1a30Sjl #include <sys/nvpair.h> 3825cf1a30Sjl #include <sys/kobj.h> 3925cf1a30Sjl #include <sys/mem_cage.h> 4025cf1a30Sjl #include <sys/opl.h> 4125cf1a30Sjl #include <sys/scfd/scfostoescf.h> 4225cf1a30Sjl #include <sys/cpu_sgnblk_defs.h> 4325cf1a30Sjl #include <sys/utsname.h> 4425cf1a30Sjl #include <sys/ddi.h> 4525cf1a30Sjl #include <sys/sunndi.h> 4625cf1a30Sjl #include <sys/lgrp.h> 4725cf1a30Sjl #include <sys/memnode.h> 4825cf1a30Sjl #include <sys/sysmacros.h> 49e603b7d4Spm #include <sys/time.h> 50e603b7d4Spm #include <sys/cpu.h> 5125cf1a30Sjl #include <vm/vm_dep.h> 5225cf1a30Sjl 5325cf1a30Sjl int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 540cc8ae86Sav int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp); 550cc8ae86Sav int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp); 560cc8ae86Sav int (*opl_get_mem_addr)(char *unum, char *sid, 570cc8ae86Sav uint64_t offset, uint64_t *paddr); 5825cf1a30Sjl 5925cf1a30Sjl /* Memory for fcode claims. 16k times # maximum possible IO units */ 6025cf1a30Sjl #define EFCODE_SIZE (OPL_MAX_BOARDS * OPL_MAX_IO_UNITS_PER_BOARD * 0x4000) 6125cf1a30Sjl int efcode_size = EFCODE_SIZE; 6225cf1a30Sjl 6325cf1a30Sjl #define OPL_MC_MEMBOARD_SHIFT 38 /* Boards on 256BG boundary */ 6425cf1a30Sjl 6525cf1a30Sjl /* Set the maximum number of boards for DR */ 6625cf1a30Sjl int opl_boards = OPL_MAX_BOARDS; 6725cf1a30Sjl 6825cf1a30Sjl void sgn_update_all_cpus(ushort_t, uchar_t, uchar_t); 6925cf1a30Sjl 7025cf1a30Sjl extern int tsb_lgrp_affinity; 7125cf1a30Sjl 7225cf1a30Sjl int opl_tsb_spares = (OPL_MAX_BOARDS) * (OPL_MAX_PCICH_UNITS_PER_BOARD) * 7325cf1a30Sjl (OPL_MAX_TSBS_PER_PCICH); 7425cf1a30Sjl 7525cf1a30Sjl pgcnt_t opl_startup_cage_size = 0; 7625cf1a30Sjl 773f1fa9a7Sjfrank /* 783f1fa9a7Sjfrank * The length of the delay in seconds in communication with XSCF after 793f1fa9a7Sjfrank * which the warning message will be logged. 803f1fa9a7Sjfrank */ 813f1fa9a7Sjfrank uint_t xscf_connect_delay = 60 * 15; 823f1fa9a7Sjfrank 831e2e7a75Shuah static opl_model_info_t opl_models[] = { 84195196c6Ssubhan { "FF1", OPL_MAX_BOARDS_FF1, FF1, STD_DISPATCH_TABLE }, 85195196c6Ssubhan { "FF2", OPL_MAX_BOARDS_FF2, FF2, STD_DISPATCH_TABLE }, 86195196c6Ssubhan { "DC1", OPL_MAX_BOARDS_DC1, DC1, STD_DISPATCH_TABLE }, 87195196c6Ssubhan { "DC2", OPL_MAX_BOARDS_DC2, DC2, EXT_DISPATCH_TABLE }, 88195196c6Ssubhan { "DC3", OPL_MAX_BOARDS_DC3, DC3, EXT_DISPATCH_TABLE }, 891e2e7a75Shuah }; 901e2e7a75Shuah static int opl_num_models = sizeof (opl_models)/sizeof (opl_model_info_t); 911e2e7a75Shuah 92195196c6Ssubhan /* 9372b9fce9Ssubhan * opl_cur_model 94195196c6Ssubhan */ 9572b9fce9Ssubhan static opl_model_info_t *opl_cur_model = NULL; 961e2e7a75Shuah 9725cf1a30Sjl static struct memlist *opl_memlist_per_board(struct memlist *ml); 983f1fa9a7Sjfrank static void post_xscf_msg(char *, int); 993f1fa9a7Sjfrank static void pass2xscf_thread(); 10025cf1a30Sjl 101e603b7d4Spm /* 102e603b7d4Spm * Note FF/DC out-of-order instruction engine takes only a 103e603b7d4Spm * single cycle to execute each spin loop 104e603b7d4Spm * for comparison, Panther takes 6 cycles for same loop 105e603b7d4Spm * 1500 approx nsec for OPL sleep instruction 106e603b7d4Spm * if spin count = OPL_BOFF_SLEEP*OPL_BOFF_SPIN then 107e603b7d4Spm * spin time should be equal to OPL_BOFF_TM nsecs 108e603b7d4Spm * Listed values tuned for 2.15GHz to 2.4GHz systems 109e603b7d4Spm * Value may change for future systems 110e603b7d4Spm */ 111e603b7d4Spm #define OPL_BOFF_SPIN 720 112e603b7d4Spm #define OPL_BOFF_BASE 1 113e603b7d4Spm #define OPL_BOFF_SLEEP 5 114e603b7d4Spm #define OPL_BOFF_CAP1 20 115e603b7d4Spm #define OPL_BOFF_CAP2 60 116e603b7d4Spm #define OPL_BOFF_MAX (40 * OPL_BOFF_SLEEP) 117e603b7d4Spm #define OPL_BOFF_TM 1500 118e603b7d4Spm 11925cf1a30Sjl int 12025cf1a30Sjl set_platform_max_ncpus(void) 12125cf1a30Sjl { 12225cf1a30Sjl return (OPL_MAX_CPU_PER_BOARD * OPL_MAX_BOARDS); 12325cf1a30Sjl } 12425cf1a30Sjl 12525cf1a30Sjl int 12625cf1a30Sjl set_platform_tsb_spares(void) 12725cf1a30Sjl { 12825cf1a30Sjl return (MIN(opl_tsb_spares, MAX_UPA)); 12925cf1a30Sjl } 13025cf1a30Sjl 1311e2e7a75Shuah static void 1321e2e7a75Shuah set_model_info() 1331e2e7a75Shuah { 134195196c6Ssubhan extern int ts_dispatch_extended; 1351e2e7a75Shuah char name[MAXSYSNAME]; 1361e2e7a75Shuah int i; 1371e2e7a75Shuah 1381e2e7a75Shuah /* 1391e2e7a75Shuah * Get model name from the root node. 1401e2e7a75Shuah * 1411e2e7a75Shuah * We are using the prom device tree since, at this point, 1421e2e7a75Shuah * the Solaris device tree is not yet setup. 1431e2e7a75Shuah */ 1441e2e7a75Shuah (void) prom_getprop(prom_rootnode(), "model", (caddr_t)name); 1451e2e7a75Shuah 1461e2e7a75Shuah for (i = 0; i < opl_num_models; i++) { 1471e2e7a75Shuah if (strncmp(name, opl_models[i].model_name, MAXSYSNAME) == 0) { 1481e2e7a75Shuah opl_cur_model = &opl_models[i]; 1491e2e7a75Shuah break; 1501e2e7a75Shuah } 1511e2e7a75Shuah } 152195196c6Ssubhan 153*9b71d8e9Swh /* 154*9b71d8e9Swh * If model not matched, it's an unknown model. 155*9b71d8e9Swh * just return. 156*9b71d8e9Swh */ 1571e2e7a75Shuah if (i == opl_num_models) 158*9b71d8e9Swh return; 159195196c6Ssubhan 160195196c6Ssubhan if ((opl_cur_model->model_cmds & EXT_DISPATCH_TABLE) && 161e98fafb9Sjl (ts_dispatch_extended == -1)) { 162195196c6Ssubhan /* 163195196c6Ssubhan * Based on a platform model, select a dispatch table. 164195196c6Ssubhan * Only DC2 and DC3 systems uses the alternate/extended 165195196c6Ssubhan * TS dispatch table. 166195196c6Ssubhan * FF1, FF2 and DC1 systems used standard dispatch tables. 167195196c6Ssubhan */ 168195196c6Ssubhan ts_dispatch_extended = 1; 169195196c6Ssubhan } 170195196c6Ssubhan 1711e2e7a75Shuah } 1721e2e7a75Shuah 1731e2e7a75Shuah static void 1741e2e7a75Shuah set_max_mmu_ctxdoms() 1751e2e7a75Shuah { 1761e2e7a75Shuah extern uint_t max_mmu_ctxdoms; 1771e2e7a75Shuah int max_boards; 1781e2e7a75Shuah 1791e2e7a75Shuah /* 1801e2e7a75Shuah * From the model, get the maximum number of boards 1811e2e7a75Shuah * supported and set the value accordingly. If the model 1821e2e7a75Shuah * could not be determined or recognized, we assume the max value. 1831e2e7a75Shuah */ 1841e2e7a75Shuah if (opl_cur_model == NULL) 1851e2e7a75Shuah max_boards = OPL_MAX_BOARDS; 1861e2e7a75Shuah else 1871e2e7a75Shuah max_boards = opl_cur_model->model_max_boards; 1881e2e7a75Shuah 1891e2e7a75Shuah /* 1901e2e7a75Shuah * On OPL, cores and MMUs are one-to-one. 1911e2e7a75Shuah */ 1921e2e7a75Shuah max_mmu_ctxdoms = OPL_MAX_CORE_UNITS_PER_BOARD * max_boards; 1931e2e7a75Shuah } 1941e2e7a75Shuah 19525cf1a30Sjl #pragma weak mmu_init_large_pages 19625cf1a30Sjl 19725cf1a30Sjl void 19825cf1a30Sjl set_platform_defaults(void) 19925cf1a30Sjl { 20025cf1a30Sjl extern char *tod_module_name; 20125cf1a30Sjl extern void cpu_sgn_update(ushort_t, uchar_t, uchar_t, int); 20225cf1a30Sjl extern void mmu_init_large_pages(size_t); 20325cf1a30Sjl 20425cf1a30Sjl /* Set the CPU signature function pointer */ 20525cf1a30Sjl cpu_sgn_func = cpu_sgn_update; 20625cf1a30Sjl 20725cf1a30Sjl /* Set appropriate tod module for OPL platform */ 20825cf1a30Sjl ASSERT(tod_module_name == NULL); 20925cf1a30Sjl tod_module_name = "todopl"; 21025cf1a30Sjl 21125cf1a30Sjl if ((mmu_page_sizes == max_mmu_page_sizes) && 212e12a8a13Ssusans (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) { 21325cf1a30Sjl if (&mmu_init_large_pages) 21425cf1a30Sjl mmu_init_large_pages(mmu_ism_pagesize); 21525cf1a30Sjl } 21625cf1a30Sjl 21725cf1a30Sjl tsb_lgrp_affinity = 1; 2181e2e7a75Shuah 2191e2e7a75Shuah set_max_mmu_ctxdoms(); 22025cf1a30Sjl } 22125cf1a30Sjl 22225cf1a30Sjl /* 22325cf1a30Sjl * Convert logical a board number to a physical one. 22425cf1a30Sjl */ 22525cf1a30Sjl 22625cf1a30Sjl #define LSBPROP "board#" 22725cf1a30Sjl #define PSBPROP "physical-board#" 22825cf1a30Sjl 22925cf1a30Sjl int 23025cf1a30Sjl opl_get_physical_board(int id) 23125cf1a30Sjl { 23225cf1a30Sjl dev_info_t *root_dip, *dip = NULL; 23325cf1a30Sjl char *dname = NULL; 23425cf1a30Sjl int circ; 23525cf1a30Sjl 23625cf1a30Sjl pnode_t pnode; 23725cf1a30Sjl char pname[MAXSYSNAME] = {0}; 23825cf1a30Sjl 23925cf1a30Sjl int lsb_id; /* Logical System Board ID */ 24025cf1a30Sjl int psb_id; /* Physical System Board ID */ 24125cf1a30Sjl 24225cf1a30Sjl 24325cf1a30Sjl /* 24425cf1a30Sjl * This function is called on early stage of bootup when the 24525cf1a30Sjl * kernel device tree is not initialized yet, and also 24625cf1a30Sjl * later on when the device tree is up. We want to try 24725cf1a30Sjl * the fast track first. 24825cf1a30Sjl */ 24925cf1a30Sjl root_dip = ddi_root_node(); 25025cf1a30Sjl if (root_dip) { 25125cf1a30Sjl /* Get from devinfo node */ 25225cf1a30Sjl ndi_devi_enter(root_dip, &circ); 25325cf1a30Sjl for (dip = ddi_get_child(root_dip); dip; 25425cf1a30Sjl dip = ddi_get_next_sibling(dip)) { 25525cf1a30Sjl 25625cf1a30Sjl dname = ddi_node_name(dip); 25725cf1a30Sjl if (strncmp(dname, "pseudo-mc", 9) != 0) 25825cf1a30Sjl continue; 25925cf1a30Sjl 26025cf1a30Sjl if ((lsb_id = (int)ddi_getprop(DDI_DEV_T_ANY, dip, 26125cf1a30Sjl DDI_PROP_DONTPASS, LSBPROP, -1)) == -1) 26225cf1a30Sjl continue; 26325cf1a30Sjl 26425cf1a30Sjl if (id == lsb_id) { 26525cf1a30Sjl if ((psb_id = (int)ddi_getprop(DDI_DEV_T_ANY, 26625cf1a30Sjl dip, DDI_PROP_DONTPASS, PSBPROP, -1)) 26725cf1a30Sjl == -1) { 26825cf1a30Sjl ndi_devi_exit(root_dip, circ); 26925cf1a30Sjl return (-1); 27025cf1a30Sjl } else { 27125cf1a30Sjl ndi_devi_exit(root_dip, circ); 27225cf1a30Sjl return (psb_id); 27325cf1a30Sjl } 27425cf1a30Sjl } 27525cf1a30Sjl } 27625cf1a30Sjl ndi_devi_exit(root_dip, circ); 27725cf1a30Sjl } 27825cf1a30Sjl 27925cf1a30Sjl /* 28025cf1a30Sjl * We do not have the kernel device tree, or we did not 28125cf1a30Sjl * find the node for some reason (let's say the kernel 28225cf1a30Sjl * device tree was modified), let's try the OBP tree. 28325cf1a30Sjl */ 28425cf1a30Sjl pnode = prom_rootnode(); 28525cf1a30Sjl for (pnode = prom_childnode(pnode); pnode; 28625cf1a30Sjl pnode = prom_nextnode(pnode)) { 28725cf1a30Sjl 28825cf1a30Sjl if ((prom_getprop(pnode, "name", (caddr_t)pname) == -1) || 28925cf1a30Sjl (strncmp(pname, "pseudo-mc", 9) != 0)) 29025cf1a30Sjl continue; 29125cf1a30Sjl 29225cf1a30Sjl if (prom_getprop(pnode, LSBPROP, (caddr_t)&lsb_id) == -1) 29325cf1a30Sjl continue; 29425cf1a30Sjl 29525cf1a30Sjl if (id == lsb_id) { 29625cf1a30Sjl if (prom_getprop(pnode, PSBPROP, 29725cf1a30Sjl (caddr_t)&psb_id) == -1) { 29825cf1a30Sjl return (-1); 29925cf1a30Sjl } else { 30025cf1a30Sjl return (psb_id); 30125cf1a30Sjl } 30225cf1a30Sjl } 30325cf1a30Sjl } 30425cf1a30Sjl 30525cf1a30Sjl return (-1); 30625cf1a30Sjl } 30725cf1a30Sjl 30825cf1a30Sjl /* 30925cf1a30Sjl * For OPL it's possible that memory from two or more successive boards 31025cf1a30Sjl * will be contiguous across the boards, and therefore represented as a 31125cf1a30Sjl * single chunk. 31225cf1a30Sjl * This function splits such chunks down the board boundaries. 31325cf1a30Sjl */ 31425cf1a30Sjl static struct memlist * 31525cf1a30Sjl opl_memlist_per_board(struct memlist *ml) 31625cf1a30Sjl { 31725cf1a30Sjl uint64_t ssize, low, high, boundary; 31825cf1a30Sjl struct memlist *head, *tail, *new; 31925cf1a30Sjl 32025cf1a30Sjl ssize = (1ull << OPL_MC_MEMBOARD_SHIFT); 32125cf1a30Sjl 32225cf1a30Sjl head = tail = NULL; 32325cf1a30Sjl 32425cf1a30Sjl for (; ml; ml = ml->next) { 32525cf1a30Sjl low = (uint64_t)ml->address; 32625cf1a30Sjl high = low+(uint64_t)(ml->size); 32725cf1a30Sjl while (low < high) { 32825cf1a30Sjl boundary = roundup(low+1, ssize); 32925cf1a30Sjl boundary = MIN(high, boundary); 33025cf1a30Sjl new = kmem_zalloc(sizeof (struct memlist), KM_SLEEP); 33125cf1a30Sjl new->address = low; 33225cf1a30Sjl new->size = boundary - low; 33325cf1a30Sjl if (head == NULL) 33425cf1a30Sjl head = new; 33525cf1a30Sjl if (tail) { 33625cf1a30Sjl tail->next = new; 33725cf1a30Sjl new->prev = tail; 33825cf1a30Sjl } 33925cf1a30Sjl tail = new; 34025cf1a30Sjl low = boundary; 34125cf1a30Sjl } 34225cf1a30Sjl } 34325cf1a30Sjl return (head); 34425cf1a30Sjl } 34525cf1a30Sjl 34625cf1a30Sjl void 34725cf1a30Sjl set_platform_cage_params(void) 34825cf1a30Sjl { 34925cf1a30Sjl extern pgcnt_t total_pages; 35025cf1a30Sjl extern struct memlist *phys_avail; 35125cf1a30Sjl struct memlist *ml, *tml; 35225cf1a30Sjl 35325cf1a30Sjl if (kernel_cage_enable) { 35425cf1a30Sjl pgcnt_t preferred_cage_size; 35525cf1a30Sjl 356e98fafb9Sjl preferred_cage_size = MAX(opl_startup_cage_size, 357e98fafb9Sjl total_pages / 256); 35825cf1a30Sjl 35925cf1a30Sjl ml = opl_memlist_per_board(phys_avail); 36025cf1a30Sjl 36125cf1a30Sjl /* 36225cf1a30Sjl * Note: we are assuming that post has load the 36325cf1a30Sjl * whole show in to the high end of memory. Having 36425cf1a30Sjl * taken this leap, we copy the whole of phys_avail 36525cf1a30Sjl * the glist and arrange for the cage to grow 36625cf1a30Sjl * downward (descending pfns). 36725cf1a30Sjl */ 36885f58038Sdp kcage_range_init(ml, KCAGE_DOWN, preferred_cage_size); 36925cf1a30Sjl 37025cf1a30Sjl /* free the memlist */ 37125cf1a30Sjl do { 37225cf1a30Sjl tml = ml->next; 37325cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 37425cf1a30Sjl ml = tml; 37525cf1a30Sjl } while (ml != NULL); 37625cf1a30Sjl } 37725cf1a30Sjl 37825cf1a30Sjl if (kcage_on) 37925cf1a30Sjl cmn_err(CE_NOTE, "!DR Kernel Cage is ENABLED"); 38025cf1a30Sjl else 38125cf1a30Sjl cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED"); 38225cf1a30Sjl } 38325cf1a30Sjl 38425cf1a30Sjl /*ARGSUSED*/ 38525cf1a30Sjl int 38625cf1a30Sjl plat_cpu_poweron(struct cpu *cp) 38725cf1a30Sjl { 38825cf1a30Sjl int (*opl_cpu_poweron)(struct cpu *) = NULL; 38925cf1a30Sjl 39025cf1a30Sjl opl_cpu_poweron = 39125cf1a30Sjl (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweron", 0); 39225cf1a30Sjl 39325cf1a30Sjl if (opl_cpu_poweron == NULL) 39425cf1a30Sjl return (ENOTSUP); 39525cf1a30Sjl else 39625cf1a30Sjl return ((opl_cpu_poweron)(cp)); 39725cf1a30Sjl 39825cf1a30Sjl } 39925cf1a30Sjl 40025cf1a30Sjl /*ARGSUSED*/ 40125cf1a30Sjl int 40225cf1a30Sjl plat_cpu_poweroff(struct cpu *cp) 40325cf1a30Sjl { 40425cf1a30Sjl int (*opl_cpu_poweroff)(struct cpu *) = NULL; 40525cf1a30Sjl 40625cf1a30Sjl opl_cpu_poweroff = 40725cf1a30Sjl (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweroff", 0); 40825cf1a30Sjl 40925cf1a30Sjl if (opl_cpu_poweroff == NULL) 41025cf1a30Sjl return (ENOTSUP); 41125cf1a30Sjl else 41225cf1a30Sjl return ((opl_cpu_poweroff)(cp)); 41325cf1a30Sjl 41425cf1a30Sjl } 41525cf1a30Sjl 41625cf1a30Sjl int 41725cf1a30Sjl plat_max_boards(void) 41825cf1a30Sjl { 41925cf1a30Sjl return (OPL_MAX_BOARDS); 42025cf1a30Sjl } 42125cf1a30Sjl 42225cf1a30Sjl int 42325cf1a30Sjl plat_max_cpu_units_per_board(void) 42425cf1a30Sjl { 42525cf1a30Sjl return (OPL_MAX_CPU_PER_BOARD); 42625cf1a30Sjl } 42725cf1a30Sjl 42825cf1a30Sjl int 42925cf1a30Sjl plat_max_mem_units_per_board(void) 43025cf1a30Sjl { 43125cf1a30Sjl return (OPL_MAX_MEM_UNITS_PER_BOARD); 43225cf1a30Sjl } 43325cf1a30Sjl 43425cf1a30Sjl int 43525cf1a30Sjl plat_max_io_units_per_board(void) 43625cf1a30Sjl { 43725cf1a30Sjl return (OPL_MAX_IO_UNITS_PER_BOARD); 43825cf1a30Sjl } 43925cf1a30Sjl 44025cf1a30Sjl int 44125cf1a30Sjl plat_max_cmp_units_per_board(void) 44225cf1a30Sjl { 44325cf1a30Sjl return (OPL_MAX_CMP_UNITS_PER_BOARD); 44425cf1a30Sjl } 44525cf1a30Sjl 44625cf1a30Sjl int 44725cf1a30Sjl plat_max_core_units_per_board(void) 44825cf1a30Sjl { 44925cf1a30Sjl return (OPL_MAX_CORE_UNITS_PER_BOARD); 45025cf1a30Sjl } 45125cf1a30Sjl 45225cf1a30Sjl int 45325cf1a30Sjl plat_pfn_to_mem_node(pfn_t pfn) 45425cf1a30Sjl { 45525cf1a30Sjl return (pfn >> mem_node_pfn_shift); 45625cf1a30Sjl } 45725cf1a30Sjl 45825cf1a30Sjl /* ARGSUSED */ 45925cf1a30Sjl void 46025cf1a30Sjl plat_build_mem_nodes(u_longlong_t *list, size_t nelems) 46125cf1a30Sjl { 46225cf1a30Sjl size_t elem; 46325cf1a30Sjl pfn_t basepfn; 46425cf1a30Sjl pgcnt_t npgs; 46525cf1a30Sjl uint64_t boundary, ssize; 46625cf1a30Sjl uint64_t low, high; 46725cf1a30Sjl 46825cf1a30Sjl /* 46925cf1a30Sjl * OPL mem slices are always aligned on a 256GB boundary. 47025cf1a30Sjl */ 47125cf1a30Sjl mem_node_pfn_shift = OPL_MC_MEMBOARD_SHIFT - MMU_PAGESHIFT; 47225cf1a30Sjl mem_node_physalign = 0; 47325cf1a30Sjl 47425cf1a30Sjl /* 47525cf1a30Sjl * Boot install lists are arranged <addr, len>, <addr, len>, ... 47625cf1a30Sjl */ 47725cf1a30Sjl ssize = (1ull << OPL_MC_MEMBOARD_SHIFT); 47825cf1a30Sjl for (elem = 0; elem < nelems; elem += 2) { 47925cf1a30Sjl low = (uint64_t)list[elem]; 48025cf1a30Sjl high = low+(uint64_t)(list[elem+1]); 48125cf1a30Sjl while (low < high) { 48225cf1a30Sjl boundary = roundup(low+1, ssize); 48325cf1a30Sjl boundary = MIN(high, boundary); 48425cf1a30Sjl basepfn = btop(low); 48525cf1a30Sjl npgs = btop(boundary - low); 48625cf1a30Sjl mem_node_add_slice(basepfn, basepfn + npgs - 1); 48725cf1a30Sjl low = boundary; 48825cf1a30Sjl } 48925cf1a30Sjl } 49025cf1a30Sjl } 49125cf1a30Sjl 49225cf1a30Sjl /* 49325cf1a30Sjl * Find the CPU associated with a slice at boot-time. 49425cf1a30Sjl */ 49525cf1a30Sjl void 49625cf1a30Sjl plat_fill_mc(pnode_t nodeid) 49725cf1a30Sjl { 49825cf1a30Sjl int board; 49925cf1a30Sjl int memnode; 50025cf1a30Sjl struct { 50125cf1a30Sjl uint64_t addr; 50225cf1a30Sjl uint64_t size; 50325cf1a30Sjl } mem_range; 50425cf1a30Sjl 50525cf1a30Sjl if (prom_getprop(nodeid, "board#", (caddr_t)&board) < 0) { 50625cf1a30Sjl panic("Can not find board# property in mc node %x", nodeid); 50725cf1a30Sjl } 50825cf1a30Sjl if (prom_getprop(nodeid, "sb-mem-ranges", (caddr_t)&mem_range) < 0) { 50925cf1a30Sjl panic("Can not find sb-mem-ranges property in mc node %x", 510e98fafb9Sjl nodeid); 51125cf1a30Sjl } 51225cf1a30Sjl memnode = mem_range.addr >> OPL_MC_MEMBOARD_SHIFT; 51325cf1a30Sjl plat_assign_lgrphand_to_mem_node(board, memnode); 51425cf1a30Sjl } 51525cf1a30Sjl 51625cf1a30Sjl /* 51725cf1a30Sjl * Return the platform handle for the lgroup containing the given CPU 51825cf1a30Sjl * 51925cf1a30Sjl * For OPL, lgroup platform handle == board #. 52025cf1a30Sjl */ 52125cf1a30Sjl 52225cf1a30Sjl extern int mpo_disabled; 52325cf1a30Sjl extern lgrp_handle_t lgrp_default_handle; 52425cf1a30Sjl 52525cf1a30Sjl lgrp_handle_t 52625cf1a30Sjl plat_lgrp_cpu_to_hand(processorid_t id) 52725cf1a30Sjl { 52825cf1a30Sjl lgrp_handle_t plathand; 52925cf1a30Sjl 53025cf1a30Sjl /* 53125cf1a30Sjl * Return the real platform handle for the CPU until 53225cf1a30Sjl * such time as we know that MPO should be disabled. 53325cf1a30Sjl * At that point, we set the "mpo_disabled" flag to true, 53425cf1a30Sjl * and from that point on, return the default handle. 53525cf1a30Sjl * 53625cf1a30Sjl * By the time we know that MPO should be disabled, the 53725cf1a30Sjl * first CPU will have already been added to a leaf 53825cf1a30Sjl * lgroup, but that's ok. The common lgroup code will 53925cf1a30Sjl * double check that the boot CPU is in the correct place, 54025cf1a30Sjl * and in the case where mpo should be disabled, will move 54125cf1a30Sjl * it to the root if necessary. 54225cf1a30Sjl */ 54325cf1a30Sjl if (mpo_disabled) { 54425cf1a30Sjl /* If MPO is disabled, return the default (UMA) handle */ 54525cf1a30Sjl plathand = lgrp_default_handle; 54625cf1a30Sjl } else 54725cf1a30Sjl plathand = (lgrp_handle_t)LSB_ID(id); 54825cf1a30Sjl return (plathand); 54925cf1a30Sjl } 55025cf1a30Sjl 55125cf1a30Sjl /* 55225cf1a30Sjl * Platform specific lgroup initialization 55325cf1a30Sjl */ 55425cf1a30Sjl void 55525cf1a30Sjl plat_lgrp_init(void) 55625cf1a30Sjl { 55725cf1a30Sjl extern uint32_t lgrp_expand_proc_thresh; 55825cf1a30Sjl extern uint32_t lgrp_expand_proc_diff; 55925cf1a30Sjl 56025cf1a30Sjl /* 56125cf1a30Sjl * Set tuneables for the OPL architecture 56225cf1a30Sjl * 56325cf1a30Sjl * lgrp_expand_proc_thresh is the minimum load on the lgroups 56425cf1a30Sjl * this process is currently running on before considering 56525cf1a30Sjl * expanding threads to another lgroup. 56625cf1a30Sjl * 56725cf1a30Sjl * lgrp_expand_proc_diff determines how much less the remote lgroup 56825cf1a30Sjl * must be loaded before expanding to it. 56925cf1a30Sjl * 57025cf1a30Sjl * Since remote latencies can be costly, attempt to keep 3 threads 57125cf1a30Sjl * within the same lgroup before expanding to the next lgroup. 57225cf1a30Sjl */ 57325cf1a30Sjl lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX * 3; 57425cf1a30Sjl lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX; 57525cf1a30Sjl } 57625cf1a30Sjl 57725cf1a30Sjl /* 57825cf1a30Sjl * Platform notification of lgroup (re)configuration changes 57925cf1a30Sjl */ 58025cf1a30Sjl /*ARGSUSED*/ 58125cf1a30Sjl void 58225cf1a30Sjl plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg) 58325cf1a30Sjl { 58425cf1a30Sjl update_membounds_t *umb; 58525cf1a30Sjl lgrp_config_mem_rename_t lmr; 58625cf1a30Sjl int sbd, tbd; 58725cf1a30Sjl lgrp_handle_t hand, shand, thand; 58825cf1a30Sjl int mnode, snode, tnode; 58925cf1a30Sjl pfn_t start, end; 59025cf1a30Sjl 59125cf1a30Sjl if (mpo_disabled) 59225cf1a30Sjl return; 59325cf1a30Sjl 59425cf1a30Sjl switch (evt) { 59525cf1a30Sjl 59625cf1a30Sjl case LGRP_CONFIG_MEM_ADD: 59725cf1a30Sjl /* 59825cf1a30Sjl * Establish the lgroup handle to memnode translation. 59925cf1a30Sjl */ 60025cf1a30Sjl umb = (update_membounds_t *)arg; 60125cf1a30Sjl 60225cf1a30Sjl hand = umb->u_board; 60325cf1a30Sjl mnode = plat_pfn_to_mem_node(umb->u_base >> MMU_PAGESHIFT); 60425cf1a30Sjl plat_assign_lgrphand_to_mem_node(hand, mnode); 60525cf1a30Sjl 60625cf1a30Sjl break; 60725cf1a30Sjl 60825cf1a30Sjl case LGRP_CONFIG_MEM_DEL: 60925cf1a30Sjl /* 61025cf1a30Sjl * Special handling for possible memory holes. 61125cf1a30Sjl */ 61225cf1a30Sjl umb = (update_membounds_t *)arg; 61325cf1a30Sjl hand = umb->u_board; 61425cf1a30Sjl if ((mnode = plat_lgrphand_to_mem_node(hand)) != -1) { 61525cf1a30Sjl if (mem_node_config[mnode].exists) { 61625cf1a30Sjl start = mem_node_config[mnode].physbase; 61725cf1a30Sjl end = mem_node_config[mnode].physmax; 61825cf1a30Sjl mem_node_pre_del_slice(start, end); 61925cf1a30Sjl mem_node_post_del_slice(start, end, 0); 62025cf1a30Sjl } 62125cf1a30Sjl } 62225cf1a30Sjl 62325cf1a30Sjl break; 62425cf1a30Sjl 62525cf1a30Sjl case LGRP_CONFIG_MEM_RENAME: 62625cf1a30Sjl /* 62725cf1a30Sjl * During a DR copy-rename operation, all of the memory 62825cf1a30Sjl * on one board is moved to another board -- but the 62925cf1a30Sjl * addresses/pfns and memnodes don't change. This means 63025cf1a30Sjl * the memory has changed locations without changing identity. 63125cf1a30Sjl * 63225cf1a30Sjl * Source is where we are copying from and target is where we 63325cf1a30Sjl * are copying to. After source memnode is copied to target 63425cf1a30Sjl * memnode, the physical addresses of the target memnode are 63525cf1a30Sjl * renamed to match what the source memnode had. Then target 63625cf1a30Sjl * memnode can be removed and source memnode can take its 63725cf1a30Sjl * place. 63825cf1a30Sjl * 63925cf1a30Sjl * To do this, swap the lgroup handle to memnode mappings for 64025cf1a30Sjl * the boards, so target lgroup will have source memnode and 64125cf1a30Sjl * source lgroup will have empty target memnode which is where 64225cf1a30Sjl * its memory will go (if any is added to it later). 64325cf1a30Sjl * 64425cf1a30Sjl * Then source memnode needs to be removed from its lgroup 64525cf1a30Sjl * and added to the target lgroup where the memory was living 64625cf1a30Sjl * but under a different name/memnode. The memory was in the 64725cf1a30Sjl * target memnode and now lives in the source memnode with 64825cf1a30Sjl * different physical addresses even though it is the same 64925cf1a30Sjl * memory. 65025cf1a30Sjl */ 65125cf1a30Sjl sbd = arg & 0xffff; 65225cf1a30Sjl tbd = (arg & 0xffff0000) >> 16; 65325cf1a30Sjl shand = sbd; 65425cf1a30Sjl thand = tbd; 65525cf1a30Sjl snode = plat_lgrphand_to_mem_node(shand); 65625cf1a30Sjl tnode = plat_lgrphand_to_mem_node(thand); 65725cf1a30Sjl 65825cf1a30Sjl /* 65925cf1a30Sjl * Special handling for possible memory holes. 66025cf1a30Sjl */ 66125cf1a30Sjl if (tnode != -1 && mem_node_config[tnode].exists) { 66268ac2337Sjl start = mem_node_config[tnode].physbase; 66368ac2337Sjl end = mem_node_config[tnode].physmax; 66425cf1a30Sjl mem_node_pre_del_slice(start, end); 66525cf1a30Sjl mem_node_post_del_slice(start, end, 0); 66625cf1a30Sjl } 66725cf1a30Sjl 66825cf1a30Sjl plat_assign_lgrphand_to_mem_node(thand, snode); 66925cf1a30Sjl plat_assign_lgrphand_to_mem_node(shand, tnode); 67025cf1a30Sjl 67125cf1a30Sjl lmr.lmem_rename_from = shand; 67225cf1a30Sjl lmr.lmem_rename_to = thand; 67325cf1a30Sjl 67425cf1a30Sjl /* 67525cf1a30Sjl * Remove source memnode of copy rename from its lgroup 67625cf1a30Sjl * and add it to its new target lgroup 67725cf1a30Sjl */ 67825cf1a30Sjl lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode, 67925cf1a30Sjl (uintptr_t)&lmr); 68025cf1a30Sjl 68125cf1a30Sjl break; 68225cf1a30Sjl 68325cf1a30Sjl default: 68425cf1a30Sjl break; 68525cf1a30Sjl } 68625cf1a30Sjl } 68725cf1a30Sjl 68825cf1a30Sjl /* 68925cf1a30Sjl * Return latency between "from" and "to" lgroups 69025cf1a30Sjl * 69125cf1a30Sjl * This latency number can only be used for relative comparison 69225cf1a30Sjl * between lgroups on the running system, cannot be used across platforms, 69325cf1a30Sjl * and may not reflect the actual latency. It is platform and implementation 69425cf1a30Sjl * specific, so platform gets to decide its value. It would be nice if the 69525cf1a30Sjl * number was at least proportional to make comparisons more meaningful though. 69625cf1a30Sjl * NOTE: The numbers below are supposed to be load latencies for uncached 69725cf1a30Sjl * memory divided by 10. 69825cf1a30Sjl * 69925cf1a30Sjl */ 70025cf1a30Sjl int 70125cf1a30Sjl plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to) 70225cf1a30Sjl { 70325cf1a30Sjl /* 70425cf1a30Sjl * Return min remote latency when there are more than two lgroups 70525cf1a30Sjl * (root and child) and getting latency between two different lgroups 70625cf1a30Sjl * or root is involved 70725cf1a30Sjl */ 70825cf1a30Sjl if (lgrp_optimizations() && (from != to || 70925cf1a30Sjl from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE)) 71071b3c2ffShyw return (42); 71125cf1a30Sjl else 71271b3c2ffShyw return (35); 71325cf1a30Sjl } 71425cf1a30Sjl 71525cf1a30Sjl /* 71625cf1a30Sjl * Return platform handle for root lgroup 71725cf1a30Sjl */ 71825cf1a30Sjl lgrp_handle_t 71925cf1a30Sjl plat_lgrp_root_hand(void) 72025cf1a30Sjl { 72125cf1a30Sjl if (mpo_disabled) 72225cf1a30Sjl return (lgrp_default_handle); 72325cf1a30Sjl 72425cf1a30Sjl return (LGRP_DEFAULT_HANDLE); 72525cf1a30Sjl } 72625cf1a30Sjl 72725cf1a30Sjl /*ARGSUSED*/ 72825cf1a30Sjl void 72925cf1a30Sjl plat_freelist_process(int mnode) 73025cf1a30Sjl { 73125cf1a30Sjl } 73225cf1a30Sjl 73325cf1a30Sjl void 73425cf1a30Sjl load_platform_drivers(void) 73525cf1a30Sjl { 73625cf1a30Sjl (void) i_ddi_attach_pseudo_node("dr"); 73725cf1a30Sjl } 73825cf1a30Sjl 73925cf1a30Sjl /* 74025cf1a30Sjl * No platform drivers on this platform 74125cf1a30Sjl */ 74225cf1a30Sjl char *platform_module_list[] = { 74325cf1a30Sjl (char *)0 74425cf1a30Sjl }; 74525cf1a30Sjl 74625cf1a30Sjl /*ARGSUSED*/ 74725cf1a30Sjl void 74825cf1a30Sjl plat_tod_fault(enum tod_fault_type tod_bad) 74925cf1a30Sjl { 75025cf1a30Sjl } 75125cf1a30Sjl 75225cf1a30Sjl /*ARGSUSED*/ 75325cf1a30Sjl void 75425cf1a30Sjl cpu_sgn_update(ushort_t sgn, uchar_t state, uchar_t sub_state, int cpuid) 75525cf1a30Sjl { 75625cf1a30Sjl static void (*scf_panic_callback)(int); 75725cf1a30Sjl static void (*scf_shutdown_callback)(int); 75825cf1a30Sjl 75925cf1a30Sjl /* 76025cf1a30Sjl * This is for notifing system panic/shutdown to SCF. 76125cf1a30Sjl * In case of shutdown and panic, SCF call back 76225cf1a30Sjl * function should be called. 76325cf1a30Sjl * <SCF call back functions> 76425cf1a30Sjl * scf_panic_callb() : panicsys()->panic_quiesce_hw() 76525cf1a30Sjl * scf_shutdown_callb(): halt() or power_down() or reboot_machine() 76625cf1a30Sjl * cpuid should be -1 and state should be SIGST_EXIT. 76725cf1a30Sjl */ 76825cf1a30Sjl if (state == SIGST_EXIT && cpuid == -1) { 76925cf1a30Sjl 77025cf1a30Sjl /* 77125cf1a30Sjl * find the symbol for the SCF panic callback routine in driver 77225cf1a30Sjl */ 77325cf1a30Sjl if (scf_panic_callback == NULL) 77425cf1a30Sjl scf_panic_callback = (void (*)(int)) 775e98fafb9Sjl modgetsymvalue("scf_panic_callb", 0); 77625cf1a30Sjl if (scf_shutdown_callback == NULL) 77725cf1a30Sjl scf_shutdown_callback = (void (*)(int)) 778e98fafb9Sjl modgetsymvalue("scf_shutdown_callb", 0); 77925cf1a30Sjl 78025cf1a30Sjl switch (sub_state) { 78125cf1a30Sjl case SIGSUBST_PANIC: 78225cf1a30Sjl if (scf_panic_callback == NULL) { 78325cf1a30Sjl cmn_err(CE_NOTE, "!cpu_sgn_update: " 78425cf1a30Sjl "scf_panic_callb not found\n"); 78525cf1a30Sjl return; 78625cf1a30Sjl } 78725cf1a30Sjl scf_panic_callback(SIGSUBST_PANIC); 78825cf1a30Sjl break; 78925cf1a30Sjl 79025cf1a30Sjl case SIGSUBST_HALT: 79125cf1a30Sjl if (scf_shutdown_callback == NULL) { 79225cf1a30Sjl cmn_err(CE_NOTE, "!cpu_sgn_update: " 79325cf1a30Sjl "scf_shutdown_callb not found\n"); 79425cf1a30Sjl return; 79525cf1a30Sjl } 79625cf1a30Sjl scf_shutdown_callback(SIGSUBST_HALT); 79725cf1a30Sjl break; 79825cf1a30Sjl 79925cf1a30Sjl case SIGSUBST_ENVIRON: 80025cf1a30Sjl if (scf_shutdown_callback == NULL) { 80125cf1a30Sjl cmn_err(CE_NOTE, "!cpu_sgn_update: " 80225cf1a30Sjl "scf_shutdown_callb not found\n"); 80325cf1a30Sjl return; 80425cf1a30Sjl } 80525cf1a30Sjl scf_shutdown_callback(SIGSUBST_ENVIRON); 80625cf1a30Sjl break; 80725cf1a30Sjl 80825cf1a30Sjl case SIGSUBST_REBOOT: 80925cf1a30Sjl if (scf_shutdown_callback == NULL) { 81025cf1a30Sjl cmn_err(CE_NOTE, "!cpu_sgn_update: " 81125cf1a30Sjl "scf_shutdown_callb not found\n"); 81225cf1a30Sjl return; 81325cf1a30Sjl } 81425cf1a30Sjl scf_shutdown_callback(SIGSUBST_REBOOT); 81525cf1a30Sjl break; 81625cf1a30Sjl } 81725cf1a30Sjl } 81825cf1a30Sjl } 81925cf1a30Sjl 82025cf1a30Sjl /*ARGSUSED*/ 82125cf1a30Sjl int 82225cf1a30Sjl plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id, 82325cf1a30Sjl int flt_in_memory, ushort_t flt_status, 82425cf1a30Sjl char *buf, int buflen, int *lenp) 82525cf1a30Sjl { 82625cf1a30Sjl /* 82725cf1a30Sjl * check if it's a Memory error. 82825cf1a30Sjl */ 82925cf1a30Sjl if (flt_in_memory) { 83025cf1a30Sjl if (opl_get_mem_unum != NULL) { 831e98fafb9Sjl return (opl_get_mem_unum(synd_code, flt_addr, buf, 832e98fafb9Sjl buflen, lenp)); 83325cf1a30Sjl } else { 83425cf1a30Sjl return (ENOTSUP); 83525cf1a30Sjl } 83625cf1a30Sjl } else { 83725cf1a30Sjl return (ENOTSUP); 83825cf1a30Sjl } 83925cf1a30Sjl } 84025cf1a30Sjl 84125cf1a30Sjl /*ARGSUSED*/ 84225cf1a30Sjl int 84325cf1a30Sjl plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp) 84425cf1a30Sjl { 8450cc8ae86Sav int ret = 0; 8463f1fa9a7Sjfrank int sb; 847195196c6Ssubhan int plen; 84825cf1a30Sjl 84925cf1a30Sjl sb = opl_get_physical_board(LSB_ID(cpuid)); 85025cf1a30Sjl if (sb == -1) { 85125cf1a30Sjl return (ENXIO); 85225cf1a30Sjl } 85325cf1a30Sjl 85472b9fce9Ssubhan /* 85572b9fce9Ssubhan * opl_cur_model is assigned here 85672b9fce9Ssubhan */ 85772b9fce9Ssubhan if (opl_cur_model == NULL) { 85872b9fce9Ssubhan set_model_info(); 859*9b71d8e9Swh 860*9b71d8e9Swh /* 861*9b71d8e9Swh * if not matched, return 862*9b71d8e9Swh */ 863*9b71d8e9Swh if (opl_cur_model == NULL) 864*9b71d8e9Swh return (ENODEV); 86572b9fce9Ssubhan } 86672b9fce9Ssubhan 867195196c6Ssubhan ASSERT((opl_cur_model - opl_models) == (opl_cur_model->model_type)); 868195196c6Ssubhan 869195196c6Ssubhan switch (opl_cur_model->model_type) { 870195196c6Ssubhan case FF1: 8710cc8ae86Sav plen = snprintf(buf, buflen, "/%s/CPUM%d", "MBU_A", 8720cc8ae86Sav CHIP_ID(cpuid) / 2); 8730cc8ae86Sav break; 8740cc8ae86Sav 875195196c6Ssubhan case FF2: 8760cc8ae86Sav plen = snprintf(buf, buflen, "/%s/CPUM%d", "MBU_B", 87711114147Sav (CHIP_ID(cpuid) / 2) + (sb * 2)); 8780cc8ae86Sav break; 8790cc8ae86Sav 880195196c6Ssubhan case DC1: 881195196c6Ssubhan case DC2: 882195196c6Ssubhan case DC3: 8830cc8ae86Sav plen = snprintf(buf, buflen, "/%s%02d/CPUM%d", "CMU", sb, 8840cc8ae86Sav CHIP_ID(cpuid)); 8850cc8ae86Sav break; 8860cc8ae86Sav 8870cc8ae86Sav default: 8880cc8ae86Sav /* This should never happen */ 8890cc8ae86Sav return (ENODEV); 8900cc8ae86Sav } 8910cc8ae86Sav 8920cc8ae86Sav if (plen >= buflen) { 8930cc8ae86Sav ret = ENOSPC; 89425cf1a30Sjl } else { 89525cf1a30Sjl if (lenp) 89625cf1a30Sjl *lenp = strlen(buf); 89725cf1a30Sjl } 8980cc8ae86Sav return (ret); 89925cf1a30Sjl } 90025cf1a30Sjl 90125cf1a30Sjl void 90225cf1a30Sjl plat_nodename_set(void) 90325cf1a30Sjl { 9043f1fa9a7Sjfrank post_xscf_msg((char *)&utsname, sizeof (struct utsname)); 90525cf1a30Sjl } 90625cf1a30Sjl 90725cf1a30Sjl caddr_t efcode_vaddr = NULL; 90825cf1a30Sjl 90925cf1a30Sjl /* 91025cf1a30Sjl * Preallocate enough memory for fcode claims. 91125cf1a30Sjl */ 91225cf1a30Sjl 91325cf1a30Sjl caddr_t 91425cf1a30Sjl efcode_alloc(caddr_t alloc_base) 91525cf1a30Sjl { 91625cf1a30Sjl caddr_t efcode_alloc_base = (caddr_t)roundup((uintptr_t)alloc_base, 91725cf1a30Sjl MMU_PAGESIZE); 91825cf1a30Sjl caddr_t vaddr; 91925cf1a30Sjl 92025cf1a30Sjl /* 92125cf1a30Sjl * allocate the physical memory for the Oberon fcode. 92225cf1a30Sjl */ 92325cf1a30Sjl if ((vaddr = (caddr_t)BOP_ALLOC(bootops, efcode_alloc_base, 92425cf1a30Sjl efcode_size, MMU_PAGESIZE)) == NULL) 92525cf1a30Sjl cmn_err(CE_PANIC, "Cannot allocate Efcode Memory"); 92625cf1a30Sjl 92725cf1a30Sjl efcode_vaddr = vaddr; 92825cf1a30Sjl 92925cf1a30Sjl return (efcode_alloc_base + efcode_size); 93025cf1a30Sjl } 93125cf1a30Sjl 93225cf1a30Sjl caddr_t 93325cf1a30Sjl plat_startup_memlist(caddr_t alloc_base) 93425cf1a30Sjl { 93525cf1a30Sjl caddr_t tmp_alloc_base; 93625cf1a30Sjl 93725cf1a30Sjl tmp_alloc_base = efcode_alloc(alloc_base); 93825cf1a30Sjl tmp_alloc_base = 93925cf1a30Sjl (caddr_t)roundup((uintptr_t)tmp_alloc_base, ecache_alignsize); 94025cf1a30Sjl return (tmp_alloc_base); 94125cf1a30Sjl } 94225cf1a30Sjl 94325cf1a30Sjl void 94425cf1a30Sjl startup_platform(void) 94525cf1a30Sjl { 94625cf1a30Sjl } 9470cc8ae86Sav 9481e2e7a75Shuah void 9491e2e7a75Shuah plat_cpuid_to_mmu_ctx_info(processorid_t cpuid, mmu_ctx_info_t *info) 9501e2e7a75Shuah { 9511e2e7a75Shuah int impl; 9521e2e7a75Shuah 9531e2e7a75Shuah impl = cpunodes[cpuid].implementation; 954e98fafb9Sjl if (IS_OLYMPUS_C(impl) || IS_JUPITER(impl)) { 95531f6f5eeSmv info->mmu_idx = MMU_ID(cpuid); 9561e2e7a75Shuah info->mmu_nctxs = 8192; 9571e2e7a75Shuah } else { 9581e2e7a75Shuah cmn_err(CE_PANIC, "Unknown processor %d", impl); 9591e2e7a75Shuah } 9601e2e7a75Shuah } 9611e2e7a75Shuah 9620cc8ae86Sav int 9630cc8ae86Sav plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 9640cc8ae86Sav { 9650cc8ae86Sav if (opl_get_mem_sid == NULL) { 9660cc8ae86Sav return (ENOTSUP); 9670cc8ae86Sav } 9680cc8ae86Sav return (opl_get_mem_sid(unum, buf, buflen, lenp)); 9690cc8ae86Sav } 9700cc8ae86Sav 9710cc8ae86Sav int 9720cc8ae86Sav plat_get_mem_offset(uint64_t paddr, uint64_t *offp) 9730cc8ae86Sav { 9740cc8ae86Sav if (opl_get_mem_offset == NULL) { 9750cc8ae86Sav return (ENOTSUP); 9760cc8ae86Sav } 9770cc8ae86Sav return (opl_get_mem_offset(paddr, offp)); 9780cc8ae86Sav } 9790cc8ae86Sav 9800cc8ae86Sav int 9810cc8ae86Sav plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp) 9820cc8ae86Sav { 9830cc8ae86Sav if (opl_get_mem_addr == NULL) { 9840cc8ae86Sav return (ENOTSUP); 9850cc8ae86Sav } 9860cc8ae86Sav return (opl_get_mem_addr(unum, sid, offset, addrp)); 9870cc8ae86Sav } 988e603b7d4Spm 989e603b7d4Spm void 990e603b7d4Spm plat_lock_delay(int *backoff) 991e603b7d4Spm { 992e603b7d4Spm int i; 993e603b7d4Spm int cnt; 994e603b7d4Spm int flag; 995e603b7d4Spm int ctr; 996e603b7d4Spm hrtime_t delay_start; 997e603b7d4Spm /* 998e603b7d4Spm * Platform specific lock delay code for OPL 999e603b7d4Spm * 1000e603b7d4Spm * Using staged linear increases in the delay. 1001e603b7d4Spm * The sleep instruction is the preferred method of delay, 1002e603b7d4Spm * but is too large of granularity for the initial backoff. 1003e603b7d4Spm */ 1004e603b7d4Spm 1005e603b7d4Spm if (*backoff == 0) *backoff = OPL_BOFF_BASE; 1006e603b7d4Spm 1007e603b7d4Spm flag = !*backoff; 1008e603b7d4Spm 1009e603b7d4Spm if (*backoff < OPL_BOFF_CAP1) { 1010e603b7d4Spm /* 1011e603b7d4Spm * If desired backoff is long enough, 1012e603b7d4Spm * use sleep for most of it 1013e603b7d4Spm */ 1014e98fafb9Sjl for (cnt = *backoff; cnt >= OPL_BOFF_SLEEP; 1015e98fafb9Sjl cnt -= OPL_BOFF_SLEEP) { 1016e603b7d4Spm cpu_smt_pause(); 1017e603b7d4Spm } 1018e603b7d4Spm /* 1019e603b7d4Spm * spin for small remainder of backoff 1020e603b7d4Spm * 1021e603b7d4Spm * fake call to nulldev included to prevent 1022e603b7d4Spm * compiler from optimizing out the spin loop 1023e603b7d4Spm */ 1024e603b7d4Spm for (ctr = cnt * OPL_BOFF_SPIN; ctr; ctr--) { 1025e603b7d4Spm if (flag) (void) nulldev(); 1026e603b7d4Spm } 1027e603b7d4Spm } else { 1028e603b7d4Spm /* backoff is very large. Fill it by sleeping */ 1029e603b7d4Spm delay_start = gethrtime(); 1030e603b7d4Spm cnt = *backoff/OPL_BOFF_SLEEP; 1031e603b7d4Spm /* 1032e603b7d4Spm * use sleep instructions for delay 1033e603b7d4Spm */ 1034e603b7d4Spm for (i = 0; i < cnt; i++) { 1035e603b7d4Spm cpu_smt_pause(); 1036e603b7d4Spm } 1037e603b7d4Spm 1038e603b7d4Spm /* 1039e603b7d4Spm * Note: if the other strand executes a sleep instruction, 1040e603b7d4Spm * then the sleep ends immediately with a minimum time of 1041e603b7d4Spm * 42 clocks. We check gethrtime to insure we have 1042e603b7d4Spm * waited long enough. And we include both a short 1043e603b7d4Spm * spin loop and a sleep for any final delay time. 1044e603b7d4Spm */ 1045e603b7d4Spm 1046e603b7d4Spm while ((gethrtime() - delay_start) < cnt * OPL_BOFF_TM) { 1047e603b7d4Spm cpu_smt_pause(); 1048e603b7d4Spm for (ctr = OPL_BOFF_SPIN; ctr; ctr--) { 1049e603b7d4Spm if (flag) (void) nulldev(); 1050e603b7d4Spm } 1051e603b7d4Spm } 1052e603b7d4Spm } 1053e603b7d4Spm 1054e603b7d4Spm /* 1055e603b7d4Spm * We adjust the backoff in three linear stages 1056e603b7d4Spm * The initial stage has small increases as this phase is 1057e603b7d4Spm * usually handle locks with light contention. We don't want 1058e603b7d4Spm * to have a long backoff on a lock that is available. 1059e603b7d4Spm * 1060e603b7d4Spm * In the second stage, we are in transition, unsure whether 1061e603b7d4Spm * the lock is under heavy contention. As the failures to 1062e603b7d4Spm * obtain the lock increase, we back off further. 1063e603b7d4Spm * 1064e603b7d4Spm * For the final stage, we are in a heavily contended or 1065e603b7d4Spm * long held long so we want to reduce the number of tries. 1066e603b7d4Spm */ 1067e603b7d4Spm if (*backoff < OPL_BOFF_CAP1) { 1068e603b7d4Spm *backoff += 1; 1069e603b7d4Spm } else { 1070e603b7d4Spm if (*backoff < OPL_BOFF_CAP2) { 1071e603b7d4Spm *backoff += OPL_BOFF_SLEEP; 1072e603b7d4Spm } else { 1073e603b7d4Spm *backoff += 2 * OPL_BOFF_SLEEP; 1074e603b7d4Spm } 1075e603b7d4Spm if (*backoff > OPL_BOFF_MAX) { 1076e603b7d4Spm *backoff = OPL_BOFF_MAX; 1077e603b7d4Spm } 1078e603b7d4Spm } 1079e603b7d4Spm } 10803f1fa9a7Sjfrank 10813f1fa9a7Sjfrank /* 10823f1fa9a7Sjfrank * The following code implements asynchronous call to XSCF to setup the 10833f1fa9a7Sjfrank * domain node name. 10843f1fa9a7Sjfrank */ 10853f1fa9a7Sjfrank 10863f1fa9a7Sjfrank #define FREE_MSG(m) kmem_free((m), NM_LEN((m)->len)) 10873f1fa9a7Sjfrank 10883f1fa9a7Sjfrank /* 10893f1fa9a7Sjfrank * The following three macros define the all operations on the request 10903f1fa9a7Sjfrank * list we are using here, and hide the details of the list 10913f1fa9a7Sjfrank * implementation from the code. 10923f1fa9a7Sjfrank */ 10933f1fa9a7Sjfrank #define PUSH(m) \ 10943f1fa9a7Sjfrank { \ 10953f1fa9a7Sjfrank (m)->next = ctl_msg.head; \ 10963f1fa9a7Sjfrank (m)->prev = NULL; \ 10973f1fa9a7Sjfrank if ((m)->next != NULL) \ 10983f1fa9a7Sjfrank (m)->next->prev = (m); \ 10993f1fa9a7Sjfrank ctl_msg.head = (m); \ 11003f1fa9a7Sjfrank } 11013f1fa9a7Sjfrank 11023f1fa9a7Sjfrank #define REMOVE(m) \ 11033f1fa9a7Sjfrank { \ 11043f1fa9a7Sjfrank if ((m)->prev != NULL) \ 11053f1fa9a7Sjfrank (m)->prev->next = (m)->next; \ 11063f1fa9a7Sjfrank else \ 11073f1fa9a7Sjfrank ctl_msg.head = (m)->next; \ 11083f1fa9a7Sjfrank if ((m)->next != NULL) \ 11093f1fa9a7Sjfrank (m)->next->prev = (m)->prev; \ 11103f1fa9a7Sjfrank } 11113f1fa9a7Sjfrank 11123f1fa9a7Sjfrank #define FREE_THE_TAIL(head) \ 11133f1fa9a7Sjfrank { \ 11143f1fa9a7Sjfrank nm_msg_t *n_msg, *m; \ 11153f1fa9a7Sjfrank m = (head)->next; \ 11163f1fa9a7Sjfrank (head)->next = NULL; \ 11173f1fa9a7Sjfrank while (m != NULL) { \ 11183f1fa9a7Sjfrank n_msg = m->next; \ 11193f1fa9a7Sjfrank FREE_MSG(m); \ 11203f1fa9a7Sjfrank m = n_msg; \ 11213f1fa9a7Sjfrank } \ 11223f1fa9a7Sjfrank } 11233f1fa9a7Sjfrank 11243f1fa9a7Sjfrank #define SCF_PUTINFO(f, s, p) \ 11253f1fa9a7Sjfrank f(KEY_ESCF, 0x01, 0, s, p) 11263f1fa9a7Sjfrank 11273f1fa9a7Sjfrank #define PASS2XSCF(m, r) ((r = SCF_PUTINFO(ctl_msg.scf_service_function, \ 11283f1fa9a7Sjfrank (m)->len, (m)->data)) == 0) 11293f1fa9a7Sjfrank 11303f1fa9a7Sjfrank /* 11313f1fa9a7Sjfrank * The value of the following macro loosely depends on the 11323f1fa9a7Sjfrank * value of the "device busy" timeout used in the SCF driver. 11333f1fa9a7Sjfrank * (See pass2xscf_thread()). 11343f1fa9a7Sjfrank */ 11353f1fa9a7Sjfrank #define SCF_DEVBUSY_DELAY 10 11363f1fa9a7Sjfrank 11373f1fa9a7Sjfrank /* 11383f1fa9a7Sjfrank * The default number of attempts to contact the scf driver 11393f1fa9a7Sjfrank * if we cannot fetch any information about the timeout value 11403f1fa9a7Sjfrank * it uses. 11413f1fa9a7Sjfrank */ 11423f1fa9a7Sjfrank 11433f1fa9a7Sjfrank #define REPEATS 4 11443f1fa9a7Sjfrank 11453f1fa9a7Sjfrank typedef struct nm_msg { 11463f1fa9a7Sjfrank struct nm_msg *next; 11473f1fa9a7Sjfrank struct nm_msg *prev; 11483f1fa9a7Sjfrank int len; 11493f1fa9a7Sjfrank char data[1]; 11503f1fa9a7Sjfrank } nm_msg_t; 11513f1fa9a7Sjfrank 11523f1fa9a7Sjfrank #define NM_LEN(len) (sizeof (nm_msg_t) + (len) - 1) 11533f1fa9a7Sjfrank 11543f1fa9a7Sjfrank static struct ctlmsg { 11553f1fa9a7Sjfrank nm_msg_t *head; 11563f1fa9a7Sjfrank nm_msg_t *now_serving; 11573f1fa9a7Sjfrank kmutex_t nm_lock; 11583f1fa9a7Sjfrank kthread_t *nmt; 11593f1fa9a7Sjfrank int cnt; 11603f1fa9a7Sjfrank int (*scf_service_function)(uint32_t, uint8_t, 11613f1fa9a7Sjfrank uint32_t, uint32_t, void *); 11623f1fa9a7Sjfrank } ctl_msg; 11633f1fa9a7Sjfrank 11643f1fa9a7Sjfrank static void 11653f1fa9a7Sjfrank post_xscf_msg(char *dp, int len) 11663f1fa9a7Sjfrank { 11673f1fa9a7Sjfrank nm_msg_t *msg; 11683f1fa9a7Sjfrank 11693f1fa9a7Sjfrank msg = (nm_msg_t *)kmem_zalloc(NM_LEN(len), KM_SLEEP); 11703f1fa9a7Sjfrank 11713f1fa9a7Sjfrank bcopy(dp, msg->data, len); 11723f1fa9a7Sjfrank msg->len = len; 11733f1fa9a7Sjfrank 11743f1fa9a7Sjfrank mutex_enter(&ctl_msg.nm_lock); 11753f1fa9a7Sjfrank if (ctl_msg.nmt == NULL) { 11763f1fa9a7Sjfrank ctl_msg.nmt = thread_create(NULL, 0, pass2xscf_thread, 11773f1fa9a7Sjfrank NULL, 0, &p0, TS_RUN, minclsyspri); 11783f1fa9a7Sjfrank } 11793f1fa9a7Sjfrank 11803f1fa9a7Sjfrank PUSH(msg); 11813f1fa9a7Sjfrank ctl_msg.cnt++; 11823f1fa9a7Sjfrank mutex_exit(&ctl_msg.nm_lock); 11833f1fa9a7Sjfrank } 11843f1fa9a7Sjfrank 11853f1fa9a7Sjfrank static void 11863f1fa9a7Sjfrank pass2xscf_thread() 11873f1fa9a7Sjfrank { 11883f1fa9a7Sjfrank nm_msg_t *msg; 11893f1fa9a7Sjfrank int ret; 11903f1fa9a7Sjfrank uint_t i, msg_sent, xscf_driver_delay; 11913f1fa9a7Sjfrank static uint_t repeat_cnt; 11923f1fa9a7Sjfrank uint_t *scf_wait_cnt; 11933f1fa9a7Sjfrank 11943f1fa9a7Sjfrank mutex_enter(&ctl_msg.nm_lock); 11953f1fa9a7Sjfrank 11963f1fa9a7Sjfrank /* 11973f1fa9a7Sjfrank * Find the address of the SCF put routine if it's not done yet. 11983f1fa9a7Sjfrank */ 11993f1fa9a7Sjfrank if (ctl_msg.scf_service_function == NULL) { 12003f1fa9a7Sjfrank if ((ctl_msg.scf_service_function = 12013f1fa9a7Sjfrank (int (*)(uint32_t, uint8_t, uint32_t, uint32_t, void *)) 12023f1fa9a7Sjfrank modgetsymvalue("scf_service_putinfo", 0)) == NULL) { 12033f1fa9a7Sjfrank cmn_err(CE_NOTE, "pass2xscf_thread: " 12043f1fa9a7Sjfrank "scf_service_putinfo not found\n"); 12053f1fa9a7Sjfrank ctl_msg.nmt = NULL; 12063f1fa9a7Sjfrank mutex_exit(&ctl_msg.nm_lock); 12073f1fa9a7Sjfrank return; 12083f1fa9a7Sjfrank } 12093f1fa9a7Sjfrank } 12103f1fa9a7Sjfrank 12113f1fa9a7Sjfrank /* 12123f1fa9a7Sjfrank * Calculate the number of attempts to connect XSCF based on the 12133f1fa9a7Sjfrank * scf driver delay (which is 12143f1fa9a7Sjfrank * SCF_DEVBUSY_DELAY*scf_online_wait_rcnt seconds) and the value 12153f1fa9a7Sjfrank * of xscf_connect_delay (the total number of seconds to wait 12163f1fa9a7Sjfrank * till xscf get ready.) 12173f1fa9a7Sjfrank */ 12183f1fa9a7Sjfrank if (repeat_cnt == 0) { 12193f1fa9a7Sjfrank if ((scf_wait_cnt = 12203f1fa9a7Sjfrank (uint_t *) 12213f1fa9a7Sjfrank modgetsymvalue("scf_online_wait_rcnt", 0)) == NULL) { 12223f1fa9a7Sjfrank repeat_cnt = REPEATS; 12233f1fa9a7Sjfrank } else { 12243f1fa9a7Sjfrank 12253f1fa9a7Sjfrank xscf_driver_delay = *scf_wait_cnt * 12263f1fa9a7Sjfrank SCF_DEVBUSY_DELAY; 12273f1fa9a7Sjfrank repeat_cnt = (xscf_connect_delay/xscf_driver_delay) + 1; 12283f1fa9a7Sjfrank } 12293f1fa9a7Sjfrank } 12303f1fa9a7Sjfrank 12313f1fa9a7Sjfrank while (ctl_msg.cnt != 0) { 12323f1fa9a7Sjfrank 12333f1fa9a7Sjfrank /* 12343f1fa9a7Sjfrank * Take the very last request from the queue, 12353f1fa9a7Sjfrank */ 12363f1fa9a7Sjfrank ctl_msg.now_serving = ctl_msg.head; 12373f1fa9a7Sjfrank ASSERT(ctl_msg.now_serving != NULL); 12383f1fa9a7Sjfrank 12393f1fa9a7Sjfrank /* 12403f1fa9a7Sjfrank * and discard all the others if any. 12413f1fa9a7Sjfrank */ 12423f1fa9a7Sjfrank FREE_THE_TAIL(ctl_msg.now_serving); 12433f1fa9a7Sjfrank ctl_msg.cnt = 1; 12443f1fa9a7Sjfrank mutex_exit(&ctl_msg.nm_lock); 12453f1fa9a7Sjfrank 12463f1fa9a7Sjfrank /* 12473f1fa9a7Sjfrank * Pass the name to XSCF. Note please, we do not hold the 12483f1fa9a7Sjfrank * mutex while we are doing this. 12493f1fa9a7Sjfrank */ 12503f1fa9a7Sjfrank msg_sent = 0; 12513f1fa9a7Sjfrank for (i = 0; i < repeat_cnt; i++) { 12523f1fa9a7Sjfrank if (PASS2XSCF(ctl_msg.now_serving, ret)) { 12533f1fa9a7Sjfrank msg_sent = 1; 12543f1fa9a7Sjfrank break; 12553f1fa9a7Sjfrank } else { 12563f1fa9a7Sjfrank if (ret != EBUSY) { 12573f1fa9a7Sjfrank cmn_err(CE_NOTE, "pass2xscf_thread:" 12583f1fa9a7Sjfrank " unexpected return code" 12593f1fa9a7Sjfrank " from scf_service_putinfo():" 12603f1fa9a7Sjfrank " %d\n", ret); 12613f1fa9a7Sjfrank } 12623f1fa9a7Sjfrank } 12633f1fa9a7Sjfrank } 12643f1fa9a7Sjfrank 12653f1fa9a7Sjfrank if (msg_sent) { 12663f1fa9a7Sjfrank 12673f1fa9a7Sjfrank /* 12683f1fa9a7Sjfrank * Remove the request from the list 12693f1fa9a7Sjfrank */ 12703f1fa9a7Sjfrank mutex_enter(&ctl_msg.nm_lock); 12713f1fa9a7Sjfrank msg = ctl_msg.now_serving; 12723f1fa9a7Sjfrank ctl_msg.now_serving = NULL; 12733f1fa9a7Sjfrank REMOVE(msg); 12743f1fa9a7Sjfrank ctl_msg.cnt--; 12753f1fa9a7Sjfrank mutex_exit(&ctl_msg.nm_lock); 12763f1fa9a7Sjfrank FREE_MSG(msg); 12773f1fa9a7Sjfrank } else { 12783f1fa9a7Sjfrank 12793f1fa9a7Sjfrank /* 12803f1fa9a7Sjfrank * If while we have tried to communicate with 12813f1fa9a7Sjfrank * XSCF there were any other requests we are 12823f1fa9a7Sjfrank * going to drop this one and take the latest 12833f1fa9a7Sjfrank * one. Otherwise we will try to pass this one 12843f1fa9a7Sjfrank * again. 12853f1fa9a7Sjfrank */ 12863f1fa9a7Sjfrank cmn_err(CE_NOTE, 12873f1fa9a7Sjfrank "pass2xscf_thread: " 12883f1fa9a7Sjfrank "scf_service_putinfo " 12893f1fa9a7Sjfrank "not responding\n"); 12903f1fa9a7Sjfrank } 12913f1fa9a7Sjfrank mutex_enter(&ctl_msg.nm_lock); 12923f1fa9a7Sjfrank } 12933f1fa9a7Sjfrank 12943f1fa9a7Sjfrank /* 12953f1fa9a7Sjfrank * The request queue is empty, exit. 12963f1fa9a7Sjfrank */ 12973f1fa9a7Sjfrank ctl_msg.nmt = NULL; 12983f1fa9a7Sjfrank mutex_exit(&ctl_msg.nm_lock); 12993f1fa9a7Sjfrank } 1300