11708Sstevel /* 21708Sstevel * CDDL HEADER START 31708Sstevel * 41708Sstevel * The contents of this file are subject to the terms of the 51772Sjl139090 * Common Development and Distribution License (the "License"). 61772Sjl139090 * You may not use this file except in compliance with the License. 71708Sstevel * 81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91708Sstevel * or http://www.opensolaris.org/os/licensing. 101708Sstevel * See the License for the specific language governing permissions 111708Sstevel * and limitations under the License. 121708Sstevel * 131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each 141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151708Sstevel * If applicable, add the following below this CDDL HEADER, with the 161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 181708Sstevel * 191708Sstevel * CDDL HEADER END 201708Sstevel */ 211708Sstevel /* 22*5834Spt157919 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231708Sstevel * Use is subject to license terms. 241708Sstevel */ 251708Sstevel 261708Sstevel #pragma ident "%Z%%M% %I% %E% SMI" 271708Sstevel 281708Sstevel #include <sys/param.h> 291708Sstevel #include <sys/systm.h> 301708Sstevel #include <sys/sysmacros.h> 311708Sstevel #include <sys/sunddi.h> 321708Sstevel #include <sys/esunddi.h> 331708Sstevel #include <sys/sunndi.h> 341708Sstevel #include <sys/modctl.h> 351708Sstevel #include <sys/promif.h> 361708Sstevel #include <sys/machparam.h> 371708Sstevel #include <sys/kobj.h> 381708Sstevel #include <sys/cpuvar.h> 391708Sstevel #include <sys/mem_cage.h> 401708Sstevel #include <sys/promif.h> 411708Sstevel #include <sys/promimpl.h> 421708Sstevel #include <sys/platform_module.h> 431708Sstevel #include <sys/errno.h> 441708Sstevel #include <sys/cpu_sgnblk_defs.h> 451708Sstevel #include <sys/iosramio.h> 461708Sstevel #include <sys/domaind.h> 471708Sstevel #include <sys/starcat.h> 481708Sstevel #include <sys/machsystm.h> 491708Sstevel #include <sys/bootconf.h> 501708Sstevel #include <sys/memnode.h> 511708Sstevel #include <vm/vm_dep.h> 521708Sstevel #include <vm/page.h> 531708Sstevel #include <sys/cheetahregs.h> 541708Sstevel #include <sys/plat_ecc_unum.h> 551708Sstevel #include <sys/plat_ecc_dimm.h> 561708Sstevel #include <sys/lgrp.h> 571708Sstevel #include <sys/dr.h> 581708Sstevel #include <sys/post/scat_dcd.h> 591708Sstevel #include <sys/kdi_impl.h> 601708Sstevel #include <sys/iosramreg.h> 611708Sstevel #include <sys/iosramvar.h> 621708Sstevel #include <sys/mc-us3.h> 631708Sstevel 641708Sstevel /* Preallocation of spare tsb's for DR */ 651708Sstevel int starcat_tsb_spares = STARCAT_SPARE_TSB_MAX; 661708Sstevel 671708Sstevel /* Set the maximum number of slot0 + slot1 boards. .. for DR */ 681708Sstevel int starcat_boards = STARCAT_BDSET_MAX * STARCAT_BDSET_SLOT_MAX; 691708Sstevel 701708Sstevel /* Maximum number of cpus per board... for DR */ 711708Sstevel int starcat_cpu_per_board = MAX(STARCAT_SLOT0_CPU_MAX, STARCAT_SLOT1_CPU_MAX); 721708Sstevel 731708Sstevel /* Maximum number of mem-units per board... for DR */ 741708Sstevel int starcat_mem_per_board = MAX(STARCAT_SLOT0_MEM_MAX, STARCAT_SLOT1_MEM_MAX); 751708Sstevel 761708Sstevel /* Maximum number of io-units (buses) per board... for DR */ 771708Sstevel int starcat_io_per_board = 2 * MAX(STARCAT_SLOT0_IO_MAX, STARCAT_SLOT1_IO_MAX); 781708Sstevel 791708Sstevel /* Preferred minimum cage size (expressed in pages)... for DR */ 801708Sstevel pgcnt_t starcat_startup_cage_size = 0; 811708Sstevel 821708Sstevel /* Platform specific function to get unum information */ 831708Sstevel int (*p2get_mem_unum)(int, uint64_t, char *, int, int *); 841708Sstevel 851708Sstevel /* Memory for fcode claims. 16k times # maximum possible schizos */ 861708Sstevel #define EFCODE_SIZE (STARCAT_BDSET_MAX * 4 * 0x4000) 871708Sstevel int efcode_size = EFCODE_SIZE; 881708Sstevel 891708Sstevel void sgn_update_all_cpus(ushort_t, uchar_t, uchar_t); 901708Sstevel 911708Sstevel /* 921708Sstevel * The IOSRAM driver is loaded in load_platform_drivers() any cpu signature 931708Sstevel * usage prior to that time will have not have a function to call. 941708Sstevel */ 951708Sstevel static int (*iosram_rdp)(uint32_t key, uint32_t off, uint32_t len, 961708Sstevel caddr_t dptr) = prom_starcat_iosram_read; 971708Sstevel static int (*iosram_wrp)(uint32_t key, uint32_t off, uint32_t len, 981708Sstevel caddr_t dptr) = prom_starcat_iosram_write; 991708Sstevel 1001708Sstevel plat_dimm_sid_board_t domain_dimm_sids[STARCAT_BDSET_MAX]; 1011708Sstevel 1021708Sstevel /* 1031708Sstevel * set_platform_max_ncpus should return the maximum number of CPUs that the 1041708Sstevel * platform supports. This function is called from check_cpus() to set the 1051708Sstevel * value of max_ncpus [see PSARC 1997/165 CPU Dynamic Reconfiguration]. 1061708Sstevel * Data elements which are allocated based upon max_ncpus are all accessed 1071708Sstevel * via cpu_seqid and not physical IDs. Previously, the value of max_ncpus 1081708Sstevel * was being set to the largest physical ID, which led to boot problems on 1091708Sstevel * systems with less than 1.25GB of memory. 1101708Sstevel */ 1111708Sstevel 1121708Sstevel int 1131708Sstevel set_platform_max_ncpus(void) 1141708Sstevel { 1151708Sstevel int n; 1161708Sstevel 1171708Sstevel /* 1181708Sstevel * Convert number of slot0 + slot1 boards to number of expander brds 1191708Sstevel * and constrain the value to an architecturally plausible range 1201708Sstevel */ 1211708Sstevel n = MAX(starcat_boards, STARCAT_BDSET_MIN * STARCAT_BDSET_SLOT_MAX); 1221708Sstevel n = MIN(n, STARCAT_BDSET_MAX * STARCAT_BDSET_SLOT_MAX); 1231708Sstevel n = (n + STARCAT_BDSET_SLOT_MAX - 1) / STARCAT_BDSET_SLOT_MAX; 1241708Sstevel 1251708Sstevel /* return maximum number of cpus possible on N expander boards */ 1261708Sstevel return (n * STARCAT_BDSET_CPU_MAX - STARCAT_SLOT1_CPU_MAX); 1271708Sstevel } 1281708Sstevel 1291708Sstevel int 1301708Sstevel set_platform_tsb_spares() 1311708Sstevel { 1321708Sstevel return (MIN(starcat_tsb_spares, MAX_UPA)); 1331708Sstevel } 1341708Sstevel 1351708Sstevel #pragma weak mmu_init_large_pages 1361708Sstevel 1371708Sstevel void 1381708Sstevel set_platform_defaults(void) 1391708Sstevel { 1401708Sstevel extern char *tod_module_name; 1411708Sstevel extern int ts_dispatch_extended; 1421708Sstevel extern void cpu_sgn_update(ushort_t, uchar_t, uchar_t, int); 1431708Sstevel extern int tsb_lgrp_affinity; 1441708Sstevel extern int segkmem_reloc; 1451708Sstevel extern void mmu_init_large_pages(size_t); 1461708Sstevel extern int ncpunode; /* number of CPUs detected by OBP */ 1471708Sstevel 1481708Sstevel #ifdef DEBUG 1491708Sstevel ce_verbose_memory = 2; 1501708Sstevel ce_verbose_other = 2; 1511708Sstevel #endif 1521708Sstevel 1531708Sstevel /* Set the CPU signature function pointer */ 1541708Sstevel cpu_sgn_func = cpu_sgn_update; 1551708Sstevel 1561708Sstevel /* Set appropriate tod module for starcat */ 1571708Sstevel ASSERT(tod_module_name == NULL); 1581708Sstevel tod_module_name = "todstarcat"; 1591708Sstevel 1601708Sstevel /* 1611708Sstevel * Use the alternate TS dispatch table, which is better 1621708Sstevel * tuned for large servers. 1631708Sstevel */ 1641708Sstevel if (ts_dispatch_extended == -1) 1651708Sstevel ts_dispatch_extended = 1; 1661708Sstevel 1671708Sstevel /* 1681708Sstevel * Use lgroup-aware TSB allocations on this platform, 1691708Sstevel * since they are a considerable performance win. 1701708Sstevel */ 1711708Sstevel tsb_lgrp_affinity = 1; 1721708Sstevel 1731708Sstevel if ((mmu_page_sizes == max_mmu_page_sizes) && 1742659Ssusans (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) { 1751708Sstevel if (&mmu_init_large_pages) 1761708Sstevel mmu_init_large_pages(mmu_ism_pagesize); 1771708Sstevel } 1781708Sstevel 1791708Sstevel /* 1801708Sstevel * KPR (kernel page relocation) is supported on this platform. 1811708Sstevel */ 1821708Sstevel if (hat_kpr_enabled && kernel_cage_enable && ncpunode >= 32) { 1831708Sstevel segkmem_reloc = 1; 1841708Sstevel cmn_err(CE_NOTE, "!Kernel Page Relocation is ENABLED"); 1851708Sstevel } else { 1861708Sstevel cmn_err(CE_NOTE, "!Kernel Page Relocation is DISABLED"); 1871708Sstevel } 1881708Sstevel } 1891708Sstevel 1901708Sstevel #ifdef DEBUG 1911708Sstevel pgcnt_t starcat_cage_size_limit; 1921708Sstevel #endif 1931708Sstevel 1941708Sstevel void 1951708Sstevel set_platform_cage_params(void) 1961708Sstevel { 1971708Sstevel extern pgcnt_t total_pages; 1981708Sstevel extern struct memlist *phys_avail; 1991708Sstevel 2001708Sstevel if (kernel_cage_enable) { 2011708Sstevel pgcnt_t preferred_cage_size; 2021708Sstevel 2031708Sstevel preferred_cage_size = 2045048Smb91622 MAX(starcat_startup_cage_size, total_pages / 256); 2051708Sstevel 2061708Sstevel #ifdef DEBUG 2071708Sstevel if (starcat_cage_size_limit) 2081708Sstevel preferred_cage_size = starcat_cage_size_limit; 2091708Sstevel #endif 2101708Sstevel /* 2111708Sstevel * Note: we are assuming that post has load the 2121708Sstevel * whole show in to the high end of memory. Having 2131708Sstevel * taken this leap, we copy the whole of phys_avail 2141708Sstevel * the glist and arrange for the cage to grow 2151708Sstevel * downward (descending pfns). 2161708Sstevel */ 2174266Sdp78419 kcage_range_init(phys_avail, KCAGE_DOWN, preferred_cage_size); 2181708Sstevel } 2191708Sstevel 2201708Sstevel if (kcage_on) 2211708Sstevel cmn_err(CE_NOTE, "!DR Kernel Cage is ENABLED"); 2221708Sstevel else 2231708Sstevel cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED"); 2241708Sstevel } 2251708Sstevel 2261708Sstevel void 2271708Sstevel load_platform_modules(void) 2281708Sstevel { 2291708Sstevel if (modload("misc", "pcihp") < 0) { 2301708Sstevel cmn_err(CE_NOTE, "pcihp driver failed to load"); 2311708Sstevel } 2321708Sstevel } 2331708Sstevel 2341708Sstevel /* 2351708Sstevel * Starcat does not support power control of CPUs from the OS. 2361708Sstevel */ 2371708Sstevel /*ARGSUSED*/ 2381708Sstevel int 2391708Sstevel plat_cpu_poweron(struct cpu *cp) 2401708Sstevel { 2411708Sstevel int (*starcat_cpu_poweron)(struct cpu *) = NULL; 2421708Sstevel 2431708Sstevel starcat_cpu_poweron = 2445048Smb91622 (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweron", 0); 2451708Sstevel 2461708Sstevel if (starcat_cpu_poweron == NULL) 2471708Sstevel return (ENOTSUP); 2481708Sstevel else 2491708Sstevel return ((starcat_cpu_poweron)(cp)); 2501708Sstevel } 2511708Sstevel 2521708Sstevel /*ARGSUSED*/ 2531708Sstevel int 2541708Sstevel plat_cpu_poweroff(struct cpu *cp) 2551708Sstevel { 2561708Sstevel int (*starcat_cpu_poweroff)(struct cpu *) = NULL; 2571708Sstevel 2581708Sstevel starcat_cpu_poweroff = 2595048Smb91622 (int (*)(struct cpu *))kobj_getsymvalue("drmach_cpu_poweroff", 0); 2601708Sstevel 2611708Sstevel if (starcat_cpu_poweroff == NULL) 2621708Sstevel return (ENOTSUP); 2631708Sstevel else 2641708Sstevel return ((starcat_cpu_poweroff)(cp)); 2651708Sstevel } 2661708Sstevel 2671708Sstevel /* 2681708Sstevel * The following are currently private to Starcat DR 2691708Sstevel */ 2701708Sstevel int 2711708Sstevel plat_max_boards() 2721708Sstevel { 2731708Sstevel return (starcat_boards); 2741708Sstevel } 2751708Sstevel 2761708Sstevel int 2771708Sstevel plat_max_cpu_units_per_board() 2781708Sstevel { 2791708Sstevel return (starcat_cpu_per_board); 2801708Sstevel } 2811708Sstevel 2821708Sstevel int 2831708Sstevel plat_max_mc_units_per_board() 2841708Sstevel { 2851708Sstevel return (starcat_mem_per_board); /* each CPU has a memory controller */ 2861708Sstevel } 2871708Sstevel 2881708Sstevel int 2891708Sstevel plat_max_mem_units_per_board() 2901708Sstevel { 2911708Sstevel return (starcat_mem_per_board); 2921708Sstevel } 2931708Sstevel 2941708Sstevel int 2951708Sstevel plat_max_io_units_per_board() 2961708Sstevel { 2971708Sstevel return (starcat_io_per_board); 2981708Sstevel } 2991708Sstevel 3001708Sstevel int 3011708Sstevel plat_max_cpumem_boards(void) 3021708Sstevel { 3031708Sstevel return (STARCAT_BDSET_MAX); 3041708Sstevel } 3051708Sstevel 3061708Sstevel int 3071708Sstevel plat_pfn_to_mem_node(pfn_t pfn) 3081708Sstevel { 3091708Sstevel return (pfn >> mem_node_pfn_shift); 3101708Sstevel } 3111708Sstevel 3121708Sstevel #define STARCAT_MC_MEMBOARD_SHIFT 37 /* Boards on 128BG boundary */ 3131708Sstevel 3141708Sstevel /* ARGSUSED */ 3151708Sstevel void 3165648Ssetje plat_build_mem_nodes(prom_memlist_t *list, size_t nelems) 3171708Sstevel { 3181708Sstevel size_t elem; 3191708Sstevel pfn_t basepfn; 3201708Sstevel pgcnt_t npgs; 3211708Sstevel 3221708Sstevel /* 3231708Sstevel * Starcat mem slices are always aligned on a 128GB boundary, 3241708Sstevel * fixed, and limited to one slice per expander due to design 3251708Sstevel * of the centerplane ASICs. 3261708Sstevel */ 3271708Sstevel mem_node_pfn_shift = STARCAT_MC_MEMBOARD_SHIFT - MMU_PAGESHIFT; 3281708Sstevel mem_node_physalign = 0; 3291708Sstevel 3301708Sstevel /* 3311708Sstevel * Boot install lists are arranged <addr, len>, <addr, len>, ... 3321708Sstevel */ 3335648Ssetje for (elem = 0; elem < nelems; list++, elem++) { 3345648Ssetje basepfn = btop(list->addr); 3355648Ssetje npgs = btop(list->size); 3361708Sstevel mem_node_add_slice(basepfn, basepfn + npgs - 1); 3371708Sstevel } 3381708Sstevel } 3391708Sstevel 3401708Sstevel /* 3411708Sstevel * Find the CPU associated with a slice at boot-time. 3421708Sstevel */ 3431708Sstevel void 3441708Sstevel plat_fill_mc(pnode_t nodeid) 3451708Sstevel { 3461708Sstevel int len; 3471708Sstevel uint64_t mc_addr, mask; 3481708Sstevel uint64_t mc_decode[MAX_BANKS_PER_MC]; 3491708Sstevel uint32_t regs[4]; 3501708Sstevel int local_mc; 3511708Sstevel int portid; 3521708Sstevel int expnum; 3531708Sstevel int i; 3541708Sstevel 3551708Sstevel /* 3561708Sstevel * Memory address decoding registers 3571708Sstevel * (see Chap 9 of SPARCV9 JSP-1 US-III implementation) 3581708Sstevel */ 3591708Sstevel const uint64_t mc_decode_addr[MAX_BANKS_PER_MC] = { 3601708Sstevel 0x400028, 0x400010, 0x400018, 0x400020 3611708Sstevel }; 3621708Sstevel 3631708Sstevel /* 3641708Sstevel * Starcat memory controller portid == global CPU id 3651708Sstevel */ 3661708Sstevel if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) || 3675048Smb91622 (portid == -1)) 3681708Sstevel return; 3691708Sstevel 3701708Sstevel expnum = STARCAT_CPUID_TO_EXPANDER(portid); 3711708Sstevel 3721708Sstevel /* 3731708Sstevel * The "reg" property returns 4 32-bit values. The first two are 3741708Sstevel * combined to form a 64-bit address. The second two are for a 3751708Sstevel * 64-bit size, but we don't actually need to look at that value. 3761708Sstevel */ 3771708Sstevel len = prom_getproplen(nodeid, "reg"); 3781708Sstevel if (len != (sizeof (uint32_t) * 4)) { 3791708Sstevel prom_printf("Warning: malformed 'reg' property\n"); 3801708Sstevel return; 3811708Sstevel } 3821708Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0) 3831708Sstevel return; 3841708Sstevel mc_addr = ((uint64_t)regs[0]) << 32; 3851708Sstevel mc_addr |= (uint64_t)regs[1]; 3861708Sstevel 3871708Sstevel /* 3881708Sstevel * Figure out whether the memory controller we are examining 3891708Sstevel * belongs to this CPU/CMP or a different one. 3901708Sstevel */ 3911708Sstevel if (portid == cpunodes[CPU->cpu_id].portid) 3921708Sstevel local_mc = 1; 3931708Sstevel else 3941708Sstevel local_mc = 0; 3951708Sstevel 3961708Sstevel for (i = 0; i < MAX_BANKS_PER_MC; i++) { 3971708Sstevel 3981708Sstevel mask = mc_decode_addr[i]; 3991708Sstevel 4001708Sstevel /* 4011708Sstevel * If the memory controller is local to this CPU, we use 4021708Sstevel * the special ASI to read the decode registers. 4031708Sstevel * Otherwise, we load the values from a magic address in 4041708Sstevel * I/O space. 4051708Sstevel */ 4061708Sstevel if (local_mc) 4071708Sstevel mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK); 4081708Sstevel else 4091708Sstevel mc_decode[i] = lddphysio((mc_addr | mask)); 4101708Sstevel 4111708Sstevel if (mc_decode[i] >> MC_VALID_SHIFT) { 4121708Sstevel uint64_t base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT; 4131708Sstevel int sliceid = (base >> STARCAT_MC_MEMBOARD_SHIFT); 4141708Sstevel 4151708Sstevel if (sliceid < max_mem_nodes) { 4161708Sstevel /* 4171708Sstevel * Establish start-of-day mappings of 4181708Sstevel * lgroup platform handles to memnodes. 4191708Sstevel * Handle == Expander Number 4201708Sstevel * Memnode == Fixed 128GB Slice 4211708Sstevel */ 4221708Sstevel plat_assign_lgrphand_to_mem_node(expnum, 4231708Sstevel sliceid); 4241708Sstevel } 4251708Sstevel } 4261708Sstevel } 4271708Sstevel } 4281708Sstevel 4291708Sstevel /* 4301708Sstevel * Starcat support for lgroups. 4311708Sstevel * 4321708Sstevel * On Starcat, an lgroup platform handle == expander number. 4331708Sstevel * For split-slot configurations (e.g. slot 0 and slot 1 boards 4341708Sstevel * in different domains) an MCPU board has only remote memory. 4351708Sstevel * 4361708Sstevel * The centerplane logic provides fixed 128GB memory slices 4371708Sstevel * each of which map to a memnode. The initial mapping of 4381708Sstevel * memnodes to lgroup handles is determined at boot time. 4391708Sstevel * A DR addition of memory adds a new mapping. A DR copy-rename 4401708Sstevel * swaps mappings. 4411708Sstevel */ 4421708Sstevel 4431708Sstevel /* 4441708Sstevel * Convert board number to expander number. 4451708Sstevel */ 4461708Sstevel #define BOARDNUM_2_EXPANDER(b) (b >> 1) 4471708Sstevel 4481708Sstevel /* 4491708Sstevel * Return the number of boards configured with NULL LPA. 4501708Sstevel */ 4511708Sstevel static int 4521708Sstevel check_for_null_lpa(void) 4531708Sstevel { 4541708Sstevel gdcd_t *gdcd; 4551708Sstevel uint_t exp, nlpa; 4561708Sstevel 4571708Sstevel /* 4581708Sstevel * Read GDCD from IOSRAM. 4591708Sstevel * If this fails indicate a NULL LPA condition. 4601708Sstevel */ 4611708Sstevel if ((gdcd = kmem_zalloc(sizeof (gdcd_t), KM_NOSLEEP)) == NULL) 4621708Sstevel return (EXP_COUNT+1); 4631708Sstevel 4641708Sstevel if ((*iosram_rdp)(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd) || 4651708Sstevel (gdcd->h.dcd_magic != GDCD_MAGIC) || 4661708Sstevel (gdcd->h.dcd_version != DCD_VERSION)) { 4671708Sstevel kmem_free(gdcd, sizeof (gdcd_t)); 4681708Sstevel cmn_err(CE_WARN, "check_for_null_lpa: failed to access GDCD\n"); 4691708Sstevel return (EXP_COUNT+2); 4701708Sstevel } 4711708Sstevel 4721708Sstevel /* 4731708Sstevel * Check for NULL LPAs on all slot 0 boards in domain 4741708Sstevel * (i.e. in all expanders marked good for this domain). 4751708Sstevel */ 4761708Sstevel nlpa = 0; 4771708Sstevel for (exp = 0; exp < EXP_COUNT; exp++) { 4781708Sstevel if (RSV_GOOD(gdcd->dcd_slot[exp][0].l1ss_rsv) && 4791708Sstevel (gdcd->dcd_slot[exp][0].l1ss_flags & 4801708Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA)) 4811708Sstevel nlpa++; 4821708Sstevel } 4831708Sstevel 4841708Sstevel kmem_free(gdcd, sizeof (gdcd_t)); 4851708Sstevel return (nlpa); 4861708Sstevel } 4871708Sstevel 4881708Sstevel /* 4891708Sstevel * Return the platform handle for the lgroup containing the given CPU 4901708Sstevel * 4911708Sstevel * For Starcat, lgroup platform handle == expander. 4921708Sstevel */ 4931708Sstevel 4941708Sstevel extern int mpo_disabled; 4951708Sstevel extern lgrp_handle_t lgrp_default_handle; 4961708Sstevel int null_lpa_boards = -1; 4971708Sstevel 4981708Sstevel lgrp_handle_t 4991708Sstevel plat_lgrp_cpu_to_hand(processorid_t id) 5001708Sstevel { 5011708Sstevel lgrp_handle_t plathand; 5021708Sstevel 5031708Sstevel plathand = STARCAT_CPUID_TO_EXPANDER(id); 5041708Sstevel 5051708Sstevel /* 5061708Sstevel * Return the real platform handle for the CPU until 5071708Sstevel * such time as we know that MPO should be disabled. 5081708Sstevel * At that point, we set the "mpo_disabled" flag to true, 5091708Sstevel * and from that point on, return the default handle. 5101708Sstevel * 5111708Sstevel * By the time we know that MPO should be disabled, the 5121708Sstevel * first CPU will have already been added to a leaf 5131708Sstevel * lgroup, but that's ok. The common lgroup code will 5141708Sstevel * double check that the boot CPU is in the correct place, 5151708Sstevel * and in the case where mpo should be disabled, will move 5161708Sstevel * it to the root if necessary. 5171708Sstevel */ 5181708Sstevel if (mpo_disabled) { 5191708Sstevel /* If MPO is disabled, return the default (UMA) handle */ 5201708Sstevel plathand = lgrp_default_handle; 5211708Sstevel } else { 5221708Sstevel if (null_lpa_boards > 0) { 5231708Sstevel /* Determine if MPO should be disabled */ 5241708Sstevel mpo_disabled = 1; 5251708Sstevel plathand = lgrp_default_handle; 5261708Sstevel } 5271708Sstevel } 5281708Sstevel return (plathand); 5291708Sstevel } 5301708Sstevel 5311708Sstevel /* 5321708Sstevel * Platform specific lgroup initialization 5331708Sstevel */ 5341708Sstevel void 5351708Sstevel plat_lgrp_init(void) 5361708Sstevel { 5371708Sstevel extern uint32_t lgrp_expand_proc_thresh; 5381708Sstevel extern uint32_t lgrp_expand_proc_diff; 5391708Sstevel 5401708Sstevel /* 5411708Sstevel * Set tuneables for Starcat architecture 5421708Sstevel * 5431708Sstevel * lgrp_expand_proc_thresh is the minimum load on the lgroups 5441708Sstevel * this process is currently running on before considering 5451708Sstevel * expanding threads to another lgroup. 5461708Sstevel * 5471708Sstevel * lgrp_expand_proc_diff determines how much less the remote lgroup 5481708Sstevel * must be loaded before expanding to it. 5491708Sstevel * 5501708Sstevel * Since remote latencies can be costly, attempt to keep 3 threads 5511708Sstevel * within the same lgroup before expanding to the next lgroup. 5521708Sstevel */ 5531708Sstevel lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX * 3; 5541708Sstevel lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX; 5551708Sstevel } 5561708Sstevel 5571708Sstevel /* 5581708Sstevel * Platform notification of lgroup (re)configuration changes 5591708Sstevel */ 5601708Sstevel /*ARGSUSED*/ 5611708Sstevel void 5621708Sstevel plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg) 5631708Sstevel { 5641708Sstevel update_membounds_t *umb; 5651708Sstevel lgrp_config_mem_rename_t lmr; 5661708Sstevel int sbd, tbd; 5671708Sstevel lgrp_handle_t hand, shand, thand; 5681708Sstevel int mnode, snode, tnode; 5691708Sstevel 5701708Sstevel if (mpo_disabled) 5711708Sstevel return; 5721708Sstevel 5731708Sstevel switch (evt) { 5741708Sstevel 5751708Sstevel case LGRP_CONFIG_MEM_ADD: 5761708Sstevel /* 5771708Sstevel * Establish the lgroup handle to memnode translation. 5781708Sstevel */ 5791708Sstevel umb = (update_membounds_t *)arg; 5801708Sstevel 5811708Sstevel hand = BOARDNUM_2_EXPANDER(umb->u_board); 5821708Sstevel mnode = plat_pfn_to_mem_node(umb->u_base >> MMU_PAGESHIFT); 5831708Sstevel plat_assign_lgrphand_to_mem_node(hand, mnode); 5841708Sstevel 5851708Sstevel break; 5861708Sstevel 5871708Sstevel case LGRP_CONFIG_MEM_DEL: 5881708Sstevel /* We don't have to do anything */ 5891708Sstevel 5901708Sstevel break; 5911708Sstevel 5921708Sstevel case LGRP_CONFIG_MEM_RENAME: 5931708Sstevel /* 5941708Sstevel * During a DR copy-rename operation, all of the memory 5951708Sstevel * on one board is moved to another board -- but the 5961708Sstevel * addresses/pfns and memnodes don't change. This means 5971708Sstevel * the memory has changed locations without changing identity. 5981708Sstevel * 5991708Sstevel * Source is where we are copying from and target is where we 6001708Sstevel * are copying to. After source memnode is copied to target 6011708Sstevel * memnode, the physical addresses of the target memnode are 6021708Sstevel * renamed to match what the source memnode had. Then target 6031708Sstevel * memnode can be removed and source memnode can take its 6041708Sstevel * place. 6051708Sstevel * 6061708Sstevel * To do this, swap the lgroup handle to memnode mappings for 6071708Sstevel * the boards, so target lgroup will have source memnode and 6081708Sstevel * source lgroup will have empty target memnode which is where 6091708Sstevel * its memory will go (if any is added to it later). 6101708Sstevel * 6111708Sstevel * Then source memnode needs to be removed from its lgroup 6121708Sstevel * and added to the target lgroup where the memory was living 6131708Sstevel * but under a different name/memnode. The memory was in the 6141708Sstevel * target memnode and now lives in the source memnode with 6151708Sstevel * different physical addresses even though it is the same 6161708Sstevel * memory. 6171708Sstevel */ 6181708Sstevel sbd = arg & 0xffff; 6191708Sstevel tbd = (arg & 0xffff0000) >> 16; 6201708Sstevel shand = BOARDNUM_2_EXPANDER(sbd); 6211708Sstevel thand = BOARDNUM_2_EXPANDER(tbd); 6221708Sstevel snode = plat_lgrphand_to_mem_node(shand); 6231708Sstevel tnode = plat_lgrphand_to_mem_node(thand); 6241708Sstevel 6251708Sstevel plat_assign_lgrphand_to_mem_node(thand, snode); 6261708Sstevel plat_assign_lgrphand_to_mem_node(shand, tnode); 6271708Sstevel 6281708Sstevel lmr.lmem_rename_from = shand; 6291708Sstevel lmr.lmem_rename_to = thand; 6301708Sstevel 6311708Sstevel /* 6321708Sstevel * Remove source memnode of copy rename from its lgroup 6331708Sstevel * and add it to its new target lgroup 6341708Sstevel */ 6351708Sstevel lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode, 6361708Sstevel (uintptr_t)&lmr); 6371708Sstevel 6381708Sstevel break; 6391708Sstevel 6401708Sstevel default: 6411708Sstevel break; 6421708Sstevel } 6431708Sstevel } 6441708Sstevel 6451708Sstevel /* 6461708Sstevel * Return latency between "from" and "to" lgroups 6471708Sstevel * 6481708Sstevel * This latency number can only be used for relative comparison 6491708Sstevel * between lgroups on the running system, cannot be used across platforms, 6501708Sstevel * and may not reflect the actual latency. It is platform and implementation 6511708Sstevel * specific, so platform gets to decide its value. It would be nice if the 6521708Sstevel * number was at least proportional to make comparisons more meaningful though. 6531708Sstevel * NOTE: The numbers below are supposed to be load latencies for uncached 6541708Sstevel * memory divided by 10. 6551708Sstevel */ 6561708Sstevel int 6571708Sstevel plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to) 6581708Sstevel { 6591708Sstevel /* 6601708Sstevel * Return min remote latency when there are more than two lgroups 6611708Sstevel * (root and child) and getting latency between two different lgroups 6621708Sstevel * or root is involved 6631708Sstevel */ 6641708Sstevel if (lgrp_optimizations() && (from != to || 6651708Sstevel from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE)) 6661708Sstevel return (48); 6671708Sstevel else 6681708Sstevel return (28); 6691708Sstevel } 6701708Sstevel 6711708Sstevel /* 6721708Sstevel * Return platform handle for root lgroup 6731708Sstevel */ 6741708Sstevel lgrp_handle_t 6751708Sstevel plat_lgrp_root_hand(void) 6761708Sstevel { 6771708Sstevel if (mpo_disabled) 6781708Sstevel return (lgrp_default_handle); 6791708Sstevel 6801708Sstevel return (LGRP_DEFAULT_HANDLE); 6811708Sstevel } 6821708Sstevel 6831708Sstevel /* ARGSUSED */ 6841708Sstevel void 6851708Sstevel plat_freelist_process(int mnode) 6861708Sstevel { 6871708Sstevel } 6881708Sstevel 6891708Sstevel void 6901708Sstevel load_platform_drivers(void) 6911708Sstevel { 6921708Sstevel uint_t tunnel; 6931708Sstevel pnode_t nodeid; 6941708Sstevel dev_info_t *chosen_devi; 6951708Sstevel char chosen_iosram[MAXNAMELEN]; 6961708Sstevel 6971708Sstevel /* 6981708Sstevel * Get /chosen node - that's where the tunnel property is 6991708Sstevel */ 7001708Sstevel nodeid = prom_chosennode(); 7011708Sstevel 7021708Sstevel /* 7031708Sstevel * Get the iosram property from the chosen node. 7041708Sstevel */ 7051708Sstevel if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) { 7061708Sstevel prom_printf("Unable to get iosram property\n"); 7071708Sstevel cmn_err(CE_PANIC, "Unable to get iosram property\n"); 7081708Sstevel } 7091708Sstevel 7101708Sstevel if (prom_phandle_to_path((phandle_t)tunnel, chosen_iosram, 7115048Smb91622 sizeof (chosen_iosram)) < 0) { 7121708Sstevel (void) prom_printf("prom_phandle_to_path(0x%x) failed\n", 7135048Smb91622 tunnel); 7141708Sstevel cmn_err(CE_PANIC, "prom_phandle_to_path(0x%x) failed\n", 7155048Smb91622 tunnel); 7161708Sstevel } 7171708Sstevel 7181708Sstevel /* 7191708Sstevel * Attach all driver instances along the iosram's device path 7201708Sstevel */ 7211708Sstevel if (i_ddi_attach_hw_nodes("iosram") != DDI_SUCCESS) { 7221708Sstevel cmn_err(CE_WARN, "IOSRAM failed to load\n"); 7231708Sstevel } 7241708Sstevel 7251708Sstevel if ((chosen_devi = e_ddi_hold_devi_by_path(chosen_iosram, 0)) == NULL) { 7261708Sstevel (void) prom_printf("e_ddi_hold_devi_by_path(%s) failed\n", 7275048Smb91622 chosen_iosram); 7281708Sstevel cmn_err(CE_PANIC, "e_ddi_hold_devi_by_path(%s) failed\n", 7295048Smb91622 chosen_iosram); 7301708Sstevel } 7311708Sstevel ndi_rele_devi(chosen_devi); 7321708Sstevel 7331708Sstevel /* 7341708Sstevel * iosram driver is now loaded so we need to set our read and 7351708Sstevel * write pointers. 7361708Sstevel */ 7371708Sstevel iosram_rdp = (int (*)(uint32_t, uint32_t, uint32_t, caddr_t)) 7385048Smb91622 modgetsymvalue("iosram_rd", 0); 7391708Sstevel iosram_wrp = (int (*)(uint32_t, uint32_t, uint32_t, caddr_t)) 7405048Smb91622 modgetsymvalue("iosram_wr", 0); 7411708Sstevel 7421708Sstevel /* 7431708Sstevel * Need to check for null proc LPA after IOSRAM driver is loaded 7441708Sstevel * and before multiple lgroups created (when start_other_cpus() called) 7451708Sstevel */ 7461708Sstevel null_lpa_boards = check_for_null_lpa(); 7471708Sstevel 7481708Sstevel /* load and attach the axq driver */ 7491708Sstevel if (i_ddi_attach_hw_nodes("axq") != DDI_SUCCESS) { 7501708Sstevel cmn_err(CE_WARN, "AXQ failed to load\n"); 7511708Sstevel } 7521708Sstevel 7531708Sstevel /* load Starcat Solaris Mailbox Client driver */ 7541708Sstevel if (modload("misc", "scosmb") < 0) { 7551708Sstevel cmn_err(CE_WARN, "SCOSMB failed to load\n"); 7561708Sstevel } 7571708Sstevel 7581708Sstevel /* load the DR driver */ 7591708Sstevel if (i_ddi_attach_hw_nodes("dr") != DDI_SUCCESS) { 7601708Sstevel cmn_err(CE_WARN, "dr failed to load"); 7611708Sstevel } 7621708Sstevel 7631708Sstevel /* 7641708Sstevel * Load the mc-us3 memory driver. 7651708Sstevel */ 7661708Sstevel if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS) 7671708Sstevel cmn_err(CE_WARN, "mc-us3 failed to load"); 7681708Sstevel else 7691708Sstevel (void) ddi_hold_driver(ddi_name_to_major("mc-us3")); 7701708Sstevel 7711708Sstevel /* Load the schizo pci bus nexus driver. */ 7721708Sstevel if (i_ddi_attach_hw_nodes("pcisch") != DDI_SUCCESS) 7731708Sstevel cmn_err(CE_WARN, "pcisch failed to load"); 7741708Sstevel 7751708Sstevel plat_ecc_init(); 7761708Sstevel } 7771708Sstevel 7781708Sstevel 7791708Sstevel /* 7801708Sstevel * No platform drivers on this platform 7811708Sstevel */ 7821708Sstevel char *platform_module_list[] = { 7831708Sstevel (char *)0 7841708Sstevel }; 7851708Sstevel 7861708Sstevel 7871708Sstevel /*ARGSUSED*/ 7881708Sstevel void 7891708Sstevel plat_tod_fault(enum tod_fault_type tod_bad) 7901708Sstevel { 7911708Sstevel } 7921708Sstevel 7931708Sstevel /* 7941708Sstevel * Update the signature(s) in the IOSRAM's domain data section. 7951708Sstevel */ 7961708Sstevel void 7971708Sstevel cpu_sgn_update(ushort_t sgn, uchar_t state, uchar_t sub_state, int cpuid) 7981708Sstevel { 7991708Sstevel sig_state_t new_sgn; 8001708Sstevel sig_state_t current_sgn; 8011708Sstevel 8021708Sstevel /* 8031708Sstevel * If the substate is REBOOT, then check for panic flow 8041708Sstevel */ 8051708Sstevel if (sub_state == SIGSUBST_REBOOT) { 8061708Sstevel (*iosram_rdp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET, 8071708Sstevel sizeof (sig_state_t), (caddr_t)¤t_sgn); 8081708Sstevel if (current_sgn.state_t.state == SIGST_EXIT) 8091708Sstevel sub_state = SIGSUBST_PANIC_REBOOT; 8101708Sstevel } 8111708Sstevel 8121708Sstevel /* 8131708Sstevel * cpuid == -1 indicates that the operation applies to all cpus. 8141708Sstevel */ 8151708Sstevel if (cpuid < 0) { 8161708Sstevel sgn_update_all_cpus(sgn, state, sub_state); 8171708Sstevel return; 8181708Sstevel } 8191708Sstevel 8201708Sstevel new_sgn.signature = CPU_SIG_BLD(sgn, state, sub_state); 8211708Sstevel (*iosram_wrp)(DOMD_MAGIC, 8221708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t), 8231708Sstevel sizeof (sig_state_t), (caddr_t)&new_sgn); 8241708Sstevel 8251708Sstevel /* 8261708Sstevel * Under certain conditions we don't update the signature 8271708Sstevel * of the domain_state. 8281708Sstevel */ 8291708Sstevel if ((sgn == OS_SIG) && 8301708Sstevel ((state == SIGST_OFFLINE) || (state == SIGST_DETACHED))) 8311708Sstevel return; 8321708Sstevel (*iosram_wrp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET, sizeof (sig_state_t), 8331708Sstevel (caddr_t)&new_sgn); 8341708Sstevel } 8351708Sstevel 8361708Sstevel /* 8371708Sstevel * Update the signature(s) in the IOSRAM's domain data section for all CPUs. 8381708Sstevel */ 8391708Sstevel void 8401708Sstevel sgn_update_all_cpus(ushort_t sgn, uchar_t state, uchar_t sub_state) 8411708Sstevel { 8421708Sstevel sig_state_t new_sgn; 8431708Sstevel int i = 0; 8441708Sstevel 8451708Sstevel new_sgn.signature = CPU_SIG_BLD(sgn, state, sub_state); 8461708Sstevel 8471708Sstevel /* 8481708Sstevel * First update the domain_state signature 8491708Sstevel */ 8501708Sstevel (*iosram_wrp)(DOMD_MAGIC, DOMD_DSTATE_OFFSET, sizeof (sig_state_t), 8511708Sstevel (caddr_t)&new_sgn); 8521708Sstevel 8531708Sstevel for (i = 0; i < NCPU; i++) { 8541708Sstevel if (cpu[i] != NULL && (cpu[i]->cpu_flags & 8551708Sstevel (CPU_EXISTS|CPU_QUIESCED))) { 8561708Sstevel (*iosram_wrp)(DOMD_MAGIC, 8571708Sstevel DOMD_CPUSIGS_OFFSET + i * sizeof (sig_state_t), 8581708Sstevel sizeof (sig_state_t), (caddr_t)&new_sgn); 8591708Sstevel } 8601708Sstevel } 8611708Sstevel } 8621708Sstevel 8631708Sstevel ushort_t 8641708Sstevel get_cpu_sgn(int cpuid) 8651708Sstevel { 8661708Sstevel sig_state_t cpu_sgn; 8671708Sstevel 8681708Sstevel (*iosram_rdp)(DOMD_MAGIC, 8691708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t), 8701708Sstevel sizeof (sig_state_t), (caddr_t)&cpu_sgn); 8711708Sstevel 8721708Sstevel return (cpu_sgn.state_t.sig); 8731708Sstevel } 8741708Sstevel 8751708Sstevel uchar_t 8761708Sstevel get_cpu_sgn_state(int cpuid) 8771708Sstevel { 8781708Sstevel sig_state_t cpu_sgn; 8791708Sstevel 8801708Sstevel (*iosram_rdp)(DOMD_MAGIC, 8811708Sstevel DOMD_CPUSIGS_OFFSET + cpuid * sizeof (sig_state_t), 8821708Sstevel sizeof (sig_state_t), (caddr_t)&cpu_sgn); 8831708Sstevel 8841708Sstevel return (cpu_sgn.state_t.state); 8851708Sstevel } 8861708Sstevel 8871708Sstevel 8881708Sstevel /* 8891708Sstevel * Type of argument passed into plat_get_ecache_cpu via ddi_walk_devs 8901708Sstevel * for matching on specific CPU node in device tree 8911708Sstevel */ 8921708Sstevel 8931708Sstevel typedef struct { 8941708Sstevel char *jnum; /* output, kmem_alloc'd if successful */ 8951708Sstevel int cpuid; /* input, to match cpuid/portid/upa-portid */ 8961708Sstevel uint_t dimm; /* input, index into ecache-dimm-label */ 8971708Sstevel } plat_ecache_cpu_arg_t; 8981708Sstevel 8991708Sstevel 9001708Sstevel /* 9011708Sstevel * plat_get_ecache_cpu is called repeatedly by ddi_walk_devs with pointers 9021708Sstevel * to device tree nodes (dip) and to a plat_ecache_cpu_arg_t structure (arg). 9031708Sstevel * Returning DDI_WALK_CONTINUE tells ddi_walk_devs to keep going, returning 9041708Sstevel * DDI_WALK_TERMINATE ends the walk. When the node for the specific CPU 9051708Sstevel * being searched for is found, the walk is done. But before returning to 9061708Sstevel * ddi_walk_devs and plat_get_ecacheunum, we grab this CPU's ecache-dimm-label 9071708Sstevel * property and set the jnum member of the plat_ecache_cpu_arg_t structure to 9081708Sstevel * point to the label corresponding to this specific ecache DIMM. It is up 9091708Sstevel * to plat_get_ecacheunum to kmem_free this string. 9101708Sstevel */ 9111708Sstevel 9121708Sstevel static int 9131708Sstevel plat_get_ecache_cpu(dev_info_t *dip, void *arg) 9141708Sstevel { 9151708Sstevel char *devtype; 9161708Sstevel plat_ecache_cpu_arg_t *cpuarg; 9171708Sstevel char **dimm_labels; 9181708Sstevel uint_t numlabels; 9191708Sstevel int portid; 9201708Sstevel 9211708Sstevel /* 9221708Sstevel * Check device_type, must be "cpu" 9231708Sstevel */ 9241708Sstevel 9251708Sstevel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 9265048Smb91622 "device_type", &devtype) != DDI_PROP_SUCCESS) 9271708Sstevel return (DDI_WALK_CONTINUE); 9281708Sstevel 9291708Sstevel if (strcmp(devtype, "cpu")) { 9301708Sstevel ddi_prop_free((void *)devtype); 9311708Sstevel return (DDI_WALK_CONTINUE); 9321708Sstevel } 9331708Sstevel 9341708Sstevel ddi_prop_free((void *)devtype); 9351708Sstevel 9361708Sstevel /* 9371708Sstevel * Check cpuid, portid, upa-portid (in that order), must 9381708Sstevel * match the cpuid being sought 9391708Sstevel */ 9401708Sstevel 9411708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 9425048Smb91622 DDI_PROP_DONTPASS, "cpuid", -1); 9431708Sstevel 9441708Sstevel if (portid == -1) 9451708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 9465048Smb91622 DDI_PROP_DONTPASS, "portid", -1); 9471708Sstevel 9481708Sstevel if (portid == -1) 9491708Sstevel portid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 9505048Smb91622 DDI_PROP_DONTPASS, "upa-portid", -1); 9511708Sstevel 9521708Sstevel cpuarg = (plat_ecache_cpu_arg_t *)arg; 9531708Sstevel 9541708Sstevel if (portid != cpuarg->cpuid) 9551708Sstevel return (DDI_WALK_CONTINUE); 9561708Sstevel 9571708Sstevel /* 9581708Sstevel * Found the right CPU, fetch ecache-dimm-label property 9591708Sstevel */ 9601708Sstevel 9611708Sstevel if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 9625048Smb91622 "ecache-dimm-label", &dimm_labels, &numlabels) 9635048Smb91622 != DDI_PROP_SUCCESS) { 9641708Sstevel #ifdef DEBUG 9651708Sstevel cmn_err(CE_NOTE, "cpuid=%d missing ecache-dimm-label property", 9665048Smb91622 portid); 9671708Sstevel #endif /* DEBUG */ 9681708Sstevel return (DDI_WALK_TERMINATE); 9691708Sstevel } 9701708Sstevel 9711708Sstevel if (cpuarg->dimm < numlabels) { 9725048Smb91622 cpuarg->jnum = kmem_alloc(strlen(dimm_labels[cpuarg->dimm]) + 1, 9735048Smb91622 KM_SLEEP); 9741708Sstevel if (cpuarg->jnum != (char *)NULL) 9751708Sstevel (void) strcpy(cpuarg->jnum, dimm_labels[cpuarg->dimm]); 9761708Sstevel #ifdef DEBUG 9771708Sstevel else 9781708Sstevel cmn_err(CE_WARN, 9795048Smb91622 "cannot kmem_alloc for ecache dimm label"); 9801708Sstevel #endif /* DEBUG */ 9811708Sstevel } 9821708Sstevel 9831708Sstevel ddi_prop_free((void *)dimm_labels); 9841708Sstevel return (DDI_WALK_TERMINATE); 9851708Sstevel } 9861708Sstevel 9871708Sstevel 9881708Sstevel /* 9891708Sstevel * Bit 4 of physical address indicates ecache 0 or 1 9901708Sstevel */ 9911708Sstevel 9921708Sstevel #define ECACHE_DIMM_MASK 0x10 9931708Sstevel 9941708Sstevel /* 9951708Sstevel * plat_get_ecacheunum is called to generate the unum for an ecache error. 9961708Sstevel * After some initialization, nearly all of the work is done by ddi_walk_devs 9971708Sstevel * and plat_get_ecache_cpu. 9981708Sstevel */ 9991708Sstevel 10001708Sstevel int 10011708Sstevel plat_get_ecacheunum(int cpuid, unsigned long long physaddr, char *buf, 10021708Sstevel int buflen, int *ustrlen) 10031708Sstevel { 10041708Sstevel plat_ecache_cpu_arg_t findcpu; 10051708Sstevel uint_t expander, slot, proc; 10061708Sstevel 10071708Sstevel findcpu.jnum = (char *)NULL; 10081708Sstevel findcpu.cpuid = cpuid; 10095048Smb91622 10105048Smb91622 /* 10115048Smb91622 * Bit 4 of physaddr equal 0 maps to E0 and 1 maps to E1 10125048Smb91622 * except for Panther and Jaguar where it indicates the reverse 10135048Smb91622 */ 10145048Smb91622 if (IS_PANTHER(cpunodes[CPU->cpu_id].implementation) || 10155048Smb91622 IS_JAGUAR(cpunodes[CPU->cpu_id].implementation)) 10165048Smb91622 findcpu.dimm = (physaddr & ECACHE_DIMM_MASK) ? 0 : 1; 10175048Smb91622 else 10185048Smb91622 findcpu.dimm = (physaddr & ECACHE_DIMM_MASK) ? 1 : 0; 10191708Sstevel 10201708Sstevel /* 10211708Sstevel * Walk the device tree, find this specific CPU, and get the label 10221708Sstevel * for this ecache, returned here in findcpu.jnum 10231708Sstevel */ 10241708Sstevel 10251708Sstevel ddi_walk_devs(ddi_root_node(), plat_get_ecache_cpu, (void *)&findcpu); 10261708Sstevel 10271708Sstevel if (findcpu.jnum == (char *)NULL) 10281708Sstevel return (-1); 10291708Sstevel 10301708Sstevel expander = STARCAT_CPUID_TO_EXPANDER(cpuid); 10311708Sstevel slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid); 10321708Sstevel 10331708Sstevel /* 10341708Sstevel * STARCAT_CPUID_TO_PORTID clears the CoreID bit so that 10351708Sstevel * STARCAT_CPUID_TO_AGENT will return a physical proc (0 - 3). 10361708Sstevel */ 10371708Sstevel proc = STARCAT_CPUID_TO_AGENT(STARCAT_CPUID_TO_PORTID(cpuid)); 10381708Sstevel 10391708Sstevel /* 10401708Sstevel * NOTE: Any modifications to the snprintf() call below will require 10411708Sstevel * changing plat_log_fruid_error() as well! 10421708Sstevel */ 10431708Sstevel (void) snprintf(buf, buflen, "%s%u/P%u/E%u J%s", (slot ? "IO" : "SB"), 10445048Smb91622 expander, proc, findcpu.dimm, findcpu.jnum); 10451708Sstevel 10461708Sstevel *ustrlen = strlen(buf); 10471708Sstevel 10481708Sstevel kmem_free(findcpu.jnum, strlen(findcpu.jnum) + 1); 10491708Sstevel 10501708Sstevel return (0); 10511708Sstevel } 10521708Sstevel 10531708Sstevel /*ARGSUSED*/ 10541708Sstevel int 10551708Sstevel plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id, 10561708Sstevel int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp) 10571708Sstevel { 10581708Sstevel int ret; 10591708Sstevel 10601708Sstevel /* 10611708Sstevel * check if it's a Memory or an Ecache error. 10621708Sstevel */ 10631708Sstevel if (flt_in_memory) { 10641708Sstevel if (p2get_mem_unum != NULL) { 10651708Sstevel return (p2get_mem_unum(synd_code, P2ALIGN(flt_addr, 8), 10665048Smb91622 buf, buflen, lenp)); 10671708Sstevel } else { 10681708Sstevel return (ENOTSUP); 10691708Sstevel } 10701708Sstevel } else if (flt_status & ECC_ECACHE) { 10711708Sstevel if ((ret = plat_get_ecacheunum(flt_bus_id, 10721708Sstevel P2ALIGN(flt_addr, 8), buf, buflen, lenp)) != 0) 10731708Sstevel return (EIO); 10741708Sstevel } else { 10751708Sstevel return (ENOTSUP); 10761708Sstevel } 10771708Sstevel 10781708Sstevel return (ret); 10791708Sstevel } 10801708Sstevel 10811708Sstevel static int (*ecc_mailbox_msg_func)(plat_ecc_message_type_t, void *) = NULL; 10821708Sstevel 10831708Sstevel /* 10841708Sstevel * To keep OS mailbox handling localized, all we do is forward the call to the 10851708Sstevel * scosmb module (if it is available). 10861708Sstevel */ 10871708Sstevel int 10881708Sstevel plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap) 10891708Sstevel { 10901708Sstevel /* 10911708Sstevel * find the symbol for the mailbox sender routine in the scosmb module 10921708Sstevel */ 10931708Sstevel if (ecc_mailbox_msg_func == NULL) 10941708Sstevel ecc_mailbox_msg_func = (int (*)(plat_ecc_message_type_t, 10951708Sstevel void *))modgetsymvalue("scosmb_log_ecc_error", 0); 10961708Sstevel 10971708Sstevel /* 10981708Sstevel * If the symbol was found, call it. Otherwise, there is not much 10991708Sstevel * else we can do and console messages will have to suffice. 11001708Sstevel */ 11011708Sstevel if (ecc_mailbox_msg_func) 11021708Sstevel return ((*ecc_mailbox_msg_func)(msg_type, datap)); 11031708Sstevel else 11041708Sstevel return (ENODEV); 11051708Sstevel } 11061708Sstevel 11071708Sstevel int 11081708Sstevel plat_make_fru_cpuid(int sb, int m, int proc) 11091708Sstevel { 11101708Sstevel return (MAKE_CPUID(sb, m, proc)); 11111708Sstevel } 11121708Sstevel 11131708Sstevel /* 11141708Sstevel * board number for a given proc 11151708Sstevel */ 11161708Sstevel int 11171708Sstevel plat_make_fru_boardnum(int proc) 11181708Sstevel { 11191708Sstevel return (STARCAT_CPUID_TO_EXPANDER(proc)); 11201708Sstevel } 11211708Sstevel 11221708Sstevel /* 11231708Sstevel * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3 11241708Sstevel * driver giving each platform the opportunity to add platform 11251708Sstevel * specific label information to the unum for ECC error logging purposes. 11261708Sstevel */ 11271708Sstevel void 11281708Sstevel plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm) 11291708Sstevel { 11301708Sstevel char new_unum[UNUM_NAMLEN]; 11311708Sstevel uint_t expander = STARCAT_CPUID_TO_EXPANDER(mcid); 11321708Sstevel uint_t slot = STARCAT_CPUID_TO_BOARDSLOT(mcid); 11331708Sstevel 11341708Sstevel /* 11351708Sstevel * STARCAT_CPUID_TO_PORTID clears the CoreID bit so that 11361708Sstevel * STARCAT_CPUID_TO_AGENT will return a physical proc (0 - 3). 11371708Sstevel */ 11381708Sstevel uint_t proc = STARCAT_CPUID_TO_AGENT(STARCAT_CPUID_TO_PORTID(mcid)); 11391708Sstevel 11401708Sstevel /* 11411708Sstevel * NOTE: Any modifications to the two sprintf() calls below will 11421708Sstevel * require changing plat_log_fruid_error() as well! 11431708Sstevel */ 11441708Sstevel if (dimm == -1) 11451708Sstevel (void) snprintf(new_unum, UNUM_NAMLEN, "%s%u/P%u/B%d %s", 11465048Smb91622 (slot ? "IO" : "SB"), expander, proc, (bank & 0x1), unum); 11471708Sstevel else 11481708Sstevel (void) snprintf(new_unum, UNUM_NAMLEN, "%s%u/P%u/B%d/D%d %s", 11495048Smb91622 (slot ? "IO" : "SB"), expander, 11505048Smb91622 proc, (bank & 0x1), (dimm & 0x3), unum); 11511708Sstevel 11521708Sstevel (void) strcpy(unum, new_unum); 11531708Sstevel } 11541708Sstevel 11551708Sstevel int 11561708Sstevel plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp) 11571708Sstevel { 11581708Sstevel int expander = STARCAT_CPUID_TO_EXPANDER(cpuid); 11591708Sstevel int slot = STARCAT_CPUID_TO_BOARDSLOT(cpuid); 11601708Sstevel 11611708Sstevel if (snprintf(buf, buflen, "%s%d", (slot ? "IO" : "SB"), expander) 11621708Sstevel >= buflen) { 11631708Sstevel return (ENOSPC); 11641708Sstevel } else { 11651708Sstevel *lenp = strlen(buf); 11661708Sstevel return (0); 11671708Sstevel } 11681708Sstevel } 11691708Sstevel 11701708Sstevel /* 11711708Sstevel * This routine is used by the data bearing mondo (DMV) initialization 11721708Sstevel * routine to determine the number of hardware and software DMV interrupts 11731708Sstevel * that a platform supports. 11741708Sstevel */ 11751708Sstevel void 11761708Sstevel plat_dmv_params(uint_t *hwint, uint_t *swint) 11771708Sstevel { 11781708Sstevel *hwint = STARCAT_DMV_HWINT; 11791708Sstevel *swint = 0; 11801708Sstevel } 11811708Sstevel 11821708Sstevel /* 11831708Sstevel * If provided, this function will be called whenever the nodename is updated. 11841708Sstevel * To keep OS mailbox handling localized, all we do is forward the call to the 11851708Sstevel * scosmb module (if it is available). 11861708Sstevel */ 11871708Sstevel void 11881708Sstevel plat_nodename_set(void) 11891708Sstevel { 11901708Sstevel void (*nodename_update_func)(uint64_t) = NULL; 11911708Sstevel 11921708Sstevel /* 11931708Sstevel * find the symbol for the nodename update routine in the scosmb module 11941708Sstevel */ 11951708Sstevel nodename_update_func = (void (*)(uint64_t)) 11961708Sstevel modgetsymvalue("scosmb_update_nodename", 0); 11971708Sstevel 11981708Sstevel /* 11991708Sstevel * If the symbol was found, call it. Otherwise, log a note (but not to 12001708Sstevel * the console). 12011708Sstevel */ 12021708Sstevel if (nodename_update_func != NULL) { 12031708Sstevel nodename_update_func(0); 12041708Sstevel } else { 12051708Sstevel cmn_err(CE_NOTE, 12061708Sstevel "!plat_nodename_set: scosmb_update_nodename not found\n"); 12071708Sstevel } 12081708Sstevel } 12091708Sstevel 12101708Sstevel caddr_t efcode_vaddr = NULL; 12111708Sstevel caddr_t efcode_paddr = NULL; 12121708Sstevel /* 12131708Sstevel * Preallocate enough memory for fcode claims. 12141708Sstevel */ 12151708Sstevel 12161708Sstevel caddr_t 12171708Sstevel efcode_alloc(caddr_t alloc_base) 12181708Sstevel { 12191708Sstevel caddr_t efcode_alloc_base = (caddr_t)roundup((uintptr_t)alloc_base, 12201708Sstevel MMU_PAGESIZE); 12211708Sstevel caddr_t vaddr; 12221708Sstevel 12231708Sstevel /* 12241708Sstevel * allocate the physical memory schizo fcode. 12251708Sstevel */ 12261708Sstevel if ((vaddr = (caddr_t)BOP_ALLOC(bootops, efcode_alloc_base, 12271708Sstevel efcode_size, MMU_PAGESIZE)) == NULL) 12281708Sstevel cmn_err(CE_PANIC, "Cannot allocate Efcode Memory"); 12291708Sstevel 12301708Sstevel efcode_vaddr = vaddr; 12311708Sstevel 12321708Sstevel return (efcode_alloc_base + efcode_size); 12331708Sstevel } 12341708Sstevel 12351708Sstevel caddr_t 12361772Sjl139090 plat_startup_memlist(caddr_t alloc_base) 12371708Sstevel { 12381708Sstevel caddr_t tmp_alloc_base; 12391708Sstevel 12401708Sstevel tmp_alloc_base = efcode_alloc(alloc_base); 12411708Sstevel tmp_alloc_base = (caddr_t)roundup((uintptr_t)tmp_alloc_base, 12425048Smb91622 ecache_alignsize); 12431708Sstevel return (tmp_alloc_base); 12441708Sstevel } 12451708Sstevel 12461708Sstevel /* 12471708Sstevel * This is a helper function to determine if a given 12481708Sstevel * node should be considered for a dr operation according 12491708Sstevel * to predefined dr names. This is accomplished using 12501708Sstevel * a function defined in drmach module. The drmach module 12511708Sstevel * owns the definition of dr allowable names. 12521708Sstevel * Formal Parameter: The name of a device node. 12531708Sstevel * Expected Return Value: -1, device node name does not map to a valid dr name. 12541708Sstevel * A value greater or equal to 0, name is valid. 12551708Sstevel */ 12561708Sstevel int 12571708Sstevel starcat_dr_name(char *name) 12581708Sstevel { 12591708Sstevel int (*drmach_name2type)(char *) = NULL; 12601708Sstevel 12611708Sstevel /* Get a pointer to helper function in the dramch module. */ 12621708Sstevel drmach_name2type = 12631708Sstevel (int (*)(char *))kobj_getsymvalue("drmach_name2type_idx", 0); 12641708Sstevel 12651708Sstevel if (drmach_name2type == NULL) 12661708Sstevel return (-1); 12671708Sstevel 12681708Sstevel return ((*drmach_name2type)(name)); 12691708Sstevel } 12701708Sstevel 12711708Sstevel void 12721708Sstevel startup_platform(void) 12731708Sstevel { 1274*5834Spt157919 /* set per platform constants for mutex backoff */ 1275*5834Spt157919 mutex_backoff_base = 2; 1276*5834Spt157919 mutex_cap_factor = 64; 12771708Sstevel } 12781708Sstevel 12791708Sstevel /* 12801708Sstevel * KDI functions - used by the in-situ kernel debugger (kmdb) to perform 12811708Sstevel * platform-specific operations. These functions execute when the world is 12821708Sstevel * stopped, and as such cannot make any blocking calls, hold locks, etc. 12831708Sstevel * promif functions are a special case, and may be used. 12841708Sstevel */ 12851708Sstevel 12861708Sstevel static void 12871708Sstevel starcat_system_claim(void) 12881708Sstevel { 12891708Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0); 12901708Sstevel } 12911708Sstevel 12921708Sstevel static void 12931708Sstevel starcat_system_release(void) 12941708Sstevel { 12951708Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0); 12961708Sstevel } 12971708Sstevel 12981708Sstevel void 12991708Sstevel plat_kdi_init(kdi_t *kdi) 13001708Sstevel { 13011708Sstevel kdi->pkdi_system_claim = starcat_system_claim; 13021708Sstevel kdi->pkdi_system_release = starcat_system_release; 13031708Sstevel } 13041708Sstevel 13051708Sstevel /* 13061708Sstevel * This function returns 1 if large pages for kernel heap are supported 13071708Sstevel * and 0 otherwise. 13081708Sstevel * 13091708Sstevel * Currently we disable lp kmem support if kpr is going to be enabled 13101708Sstevel * because in the case of large pages hat_add_callback()/hat_delete_callback() 13111708Sstevel * cause network performance degradation 13121708Sstevel */ 13131708Sstevel int 13141708Sstevel plat_lpkmem_is_supported(void) 13151708Sstevel { 13161708Sstevel extern int segkmem_reloc; 13171708Sstevel 13181708Sstevel if (hat_kpr_enabled && kernel_cage_enable && 13191708Sstevel (ncpunode >= 32 || segkmem_reloc == 1)) 13201708Sstevel return (0); 13211708Sstevel 13221708Sstevel return (1); 13231708Sstevel } 1324