103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 5c39996a7Sstevel * Common Development and Distribution License (the "License"). 6c39996a7Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 21c39996a7Sstevel 2203831d35Sstevel /* 23d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel #include <sys/time.h> 2803831d35Sstevel #include <sys/cpuvar.h> 2903831d35Sstevel #include <sys/dditypes.h> 3003831d35Sstevel #include <sys/ddipropdefs.h> 3103831d35Sstevel #include <sys/ddi_impldefs.h> 3203831d35Sstevel #include <sys/sunddi.h> 3303831d35Sstevel #include <sys/esunddi.h> 3403831d35Sstevel #include <sys/sunndi.h> 3503831d35Sstevel #include <sys/platform_module.h> 3603831d35Sstevel #include <sys/errno.h> 3703831d35Sstevel #include <sys/conf.h> 3803831d35Sstevel #include <sys/modctl.h> 3903831d35Sstevel #include <sys/promif.h> 4003831d35Sstevel #include <sys/promimpl.h> 4103831d35Sstevel #include <sys/prom_plat.h> 4203831d35Sstevel #include <sys/cmn_err.h> 4303831d35Sstevel #include <sys/sysmacros.h> 4403831d35Sstevel #include <sys/mem_cage.h> 4503831d35Sstevel #include <sys/kobj.h> 4603831d35Sstevel #include <sys/utsname.h> 4703831d35Sstevel #include <sys/cpu_sgnblk_defs.h> 4803831d35Sstevel #include <sys/atomic.h> 4903831d35Sstevel #include <sys/kdi_impl.h> 5003831d35Sstevel 5103831d35Sstevel #include <sys/sgsbbc.h> 5203831d35Sstevel #include <sys/sgsbbc_iosram.h> 5303831d35Sstevel #include <sys/sgsbbc_iosram_priv.h> 5403831d35Sstevel #include <sys/sgsbbc_mailbox.h> 5503831d35Sstevel #include <sys/sgsgn.h> 5603831d35Sstevel #include <sys/sgcn.h> 5703831d35Sstevel #include <sys/serengeti.h> 5803831d35Sstevel #include <sys/sgfrutypes.h> 5903831d35Sstevel #include <sys/machsystm.h> 6003831d35Sstevel #include <sys/sbd_ioctl.h> 6103831d35Sstevel #include <sys/sbd.h> 6203831d35Sstevel #include <sys/sbdp_mem.h> 6303831d35Sstevel 6403831d35Sstevel #include <sys/memnode.h> 6503831d35Sstevel #include <vm/vm_dep.h> 6603831d35Sstevel #include <vm/page.h> 6703831d35Sstevel 6803831d35Sstevel #include <sys/cheetahregs.h> 6903831d35Sstevel #include <sys/plat_ecc_unum.h> 7003831d35Sstevel #include <sys/plat_ecc_dimm.h> 7103831d35Sstevel 7203831d35Sstevel #include <sys/lgrp.h> 73d3d50737SRafael Vanoni #include <sys/clock_impl.h> 7403831d35Sstevel 7503831d35Sstevel static int sg_debug = 0; 7603831d35Sstevel 7703831d35Sstevel #ifdef DEBUG 7803831d35Sstevel #define DCMNERR if (sg_debug) cmn_err 7903831d35Sstevel #else 8003831d35Sstevel #define DCMNERR 8103831d35Sstevel #endif 8203831d35Sstevel 8303831d35Sstevel int (*p2get_mem_unum)(int, uint64_t, char *, int, int *); 8403831d35Sstevel 8503831d35Sstevel /* local functions */ 8603831d35Sstevel static void cpu_sgn_update(ushort_t sgn, uchar_t state, 8703831d35Sstevel uchar_t sub_state, int cpuid); 8803831d35Sstevel 8903831d35Sstevel 9003831d35Sstevel /* 9103831d35Sstevel * Local data. 9203831d35Sstevel * 9303831d35Sstevel * iosram_write_ptr is a pointer to iosram_write(). Because of 9403831d35Sstevel * kernel dynamic linking, we can't get to the function by name, 9503831d35Sstevel * but we can look up its address, and store it in this variable 9603831d35Sstevel * instead. 9703831d35Sstevel * 9803831d35Sstevel * We include the extern for iosram_write() here not because we call 9903831d35Sstevel * it, but to force compilation errors if its prototype doesn't 10003831d35Sstevel * match the prototype of iosram_write_ptr. 10103831d35Sstevel * 10203831d35Sstevel * The same issues apply to iosram_read() and iosram_read_ptr. 10303831d35Sstevel */ 10403831d35Sstevel /*CSTYLED*/ 10503831d35Sstevel extern int iosram_write (int, uint32_t, caddr_t, uint32_t); 10603831d35Sstevel static int (*iosram_write_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL; 10703831d35Sstevel /*CSTYLED*/ 10803831d35Sstevel extern int iosram_read (int, uint32_t, caddr_t, uint32_t); 10903831d35Sstevel static int (*iosram_read_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL; 11003831d35Sstevel 11103831d35Sstevel 11203831d35Sstevel /* 11303831d35Sstevel * Variable to indicate if the date should be obtained from the SC or not. 11403831d35Sstevel */ 11503831d35Sstevel int todsg_use_sc = FALSE; /* set the false at the beginning */ 11603831d35Sstevel 11703831d35Sstevel /* 11803831d35Sstevel * Preallocation of spare tsb's for DR 11903831d35Sstevel * 12003831d35Sstevel * We don't allocate spares for Wildcat since TSBs should come 12103831d35Sstevel * out of memory local to the node. 12203831d35Sstevel */ 12303831d35Sstevel #define IOMMU_PER_SCHIZO 2 12403831d35Sstevel int serengeti_tsb_spares = (SG_MAX_IO_BDS * SG_SCHIZO_PER_IO_BD * 12503831d35Sstevel IOMMU_PER_SCHIZO); 12603831d35Sstevel 12703831d35Sstevel /* 1281e2e7a75Shuah * sg_max_ncpus is the maximum number of CPUs supported on Serengeti. 1291e2e7a75Shuah * sg_max_ncpus is set to be smaller than NCPU to reduce the amount of 1301e2e7a75Shuah * memory the logs take up until we have a dynamic log memory allocation 1311e2e7a75Shuah * solution. 13203831d35Sstevel */ 1331e2e7a75Shuah int sg_max_ncpus = (24 * 2); /* (max # of processors * # of cores/proc) */ 13403831d35Sstevel 13503831d35Sstevel /* 13603831d35Sstevel * variables to control mailbox message timeouts. 13703831d35Sstevel * These can be patched via /etc/system or mdb. 13803831d35Sstevel */ 13903831d35Sstevel int sbbc_mbox_default_timeout = MBOX_DEFAULT_TIMEOUT; 14003831d35Sstevel int sbbc_mbox_min_timeout = MBOX_MIN_TIMEOUT; 14103831d35Sstevel 14203831d35Sstevel /* cached 'chosen' node_id */ 14303831d35Sstevel pnode_t chosen_nodeid = (pnode_t)0; 14403831d35Sstevel 14503831d35Sstevel static void (*sg_ecc_taskq_func)(sbbc_ecc_mbox_t *) = NULL; 14603831d35Sstevel static int (*sg_ecc_mbox_func)(sbbc_ecc_mbox_t *) = NULL; 14703831d35Sstevel 14803831d35Sstevel /* 14903831d35Sstevel * Table that maps memory slices to a specific memnode. 15003831d35Sstevel */ 15103831d35Sstevel int slice_to_memnode[SG_MAX_SLICE]; 15203831d35Sstevel 15303831d35Sstevel plat_dimm_sid_board_t domain_dimm_sids[SG_MAX_CPU_BDS]; 15403831d35Sstevel 15503831d35Sstevel 15603831d35Sstevel int 15703831d35Sstevel set_platform_tsb_spares() 15803831d35Sstevel { 15903831d35Sstevel return (MIN(serengeti_tsb_spares, MAX_UPA)); 16003831d35Sstevel } 16103831d35Sstevel 16203831d35Sstevel #pragma weak mmu_init_large_pages 16303831d35Sstevel 16403831d35Sstevel void 16503831d35Sstevel set_platform_defaults(void) 16603831d35Sstevel { 16703831d35Sstevel extern int watchdog_enable; 16803831d35Sstevel extern uint64_t xc_tick_limit_scale; 16903831d35Sstevel extern void mmu_init_large_pages(size_t); 17003831d35Sstevel 17103831d35Sstevel #ifdef DEBUG 17203831d35Sstevel char *todsg_name = "todsg"; 17303831d35Sstevel ce_verbose_memory = 2; 17403831d35Sstevel ce_verbose_other = 2; 17503831d35Sstevel #endif /* DEBUG */ 17603831d35Sstevel 17703831d35Sstevel watchdog_enable = TRUE; 17803831d35Sstevel watchdog_available = TRUE; 17903831d35Sstevel 18003831d35Sstevel cpu_sgn_func = cpu_sgn_update; 18103831d35Sstevel 18203831d35Sstevel #ifdef DEBUG 18303831d35Sstevel /* tod_module_name should be set to "todsg" from OBP property */ 18403831d35Sstevel if (tod_module_name && (strcmp(tod_module_name, todsg_name) == 0)) 18503831d35Sstevel prom_printf("Using todsg driver\n"); 18603831d35Sstevel else { 18703831d35Sstevel prom_printf("Force using todsg driver\n"); 18803831d35Sstevel tod_module_name = todsg_name; 18903831d35Sstevel } 19003831d35Sstevel #endif /* DEBUG */ 19103831d35Sstevel 19203831d35Sstevel /* Serengeti does not support forthdebug */ 19303831d35Sstevel forthdebug_supported = 0; 19403831d35Sstevel 19503831d35Sstevel 19603831d35Sstevel /* 19703831d35Sstevel * Some DR operations require the system to be sync paused. 19803831d35Sstevel * Sync pause on Serengeti could potentially take up to 4 19903831d35Sstevel * seconds to complete depending on the load on the SC. To 20003831d35Sstevel * avoid send_mond panics during such operations, we need to 20103831d35Sstevel * increase xc_tick_limit to a larger value on Serengeti by 20203831d35Sstevel * setting xc_tick_limit_scale to 5. 20303831d35Sstevel */ 20403831d35Sstevel xc_tick_limit_scale = 5; 20503831d35Sstevel 20603831d35Sstevel if ((mmu_page_sizes == max_mmu_page_sizes) && 207e12a8a13Ssusans (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) { 20803831d35Sstevel if (&mmu_init_large_pages) 20903831d35Sstevel mmu_init_large_pages(mmu_ism_pagesize); 21003831d35Sstevel } 21103831d35Sstevel } 21203831d35Sstevel 21303831d35Sstevel void 21403831d35Sstevel load_platform_modules(void) 21503831d35Sstevel { 21603831d35Sstevel if (modload("misc", "pcihp") < 0) { 21703831d35Sstevel cmn_err(CE_NOTE, "pcihp driver failed to load"); 21803831d35Sstevel } 21903831d35Sstevel } 22003831d35Sstevel 22103831d35Sstevel /*ARGSUSED*/ 22203831d35Sstevel int 22303831d35Sstevel plat_cpu_poweron(struct cpu *cp) 22403831d35Sstevel { 22503831d35Sstevel int (*serengeti_cpu_poweron)(struct cpu *) = NULL; 22603831d35Sstevel 22703831d35Sstevel serengeti_cpu_poweron = 22803831d35Sstevel (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweron", 0); 22903831d35Sstevel 23003831d35Sstevel if (serengeti_cpu_poweron == NULL) 23103831d35Sstevel return (ENOTSUP); 23203831d35Sstevel else 23303831d35Sstevel return ((serengeti_cpu_poweron)(cp)); 23403831d35Sstevel } 23503831d35Sstevel 23603831d35Sstevel /*ARGSUSED*/ 23703831d35Sstevel int 23803831d35Sstevel plat_cpu_poweroff(struct cpu *cp) 23903831d35Sstevel { 24003831d35Sstevel int (*serengeti_cpu_poweroff)(struct cpu *) = NULL; 24103831d35Sstevel 24203831d35Sstevel serengeti_cpu_poweroff = 24303831d35Sstevel (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweroff", 0); 24403831d35Sstevel 24503831d35Sstevel if (serengeti_cpu_poweroff == NULL) 24603831d35Sstevel return (ENOTSUP); 24703831d35Sstevel else 24803831d35Sstevel return ((serengeti_cpu_poweroff)(cp)); 24903831d35Sstevel } 25003831d35Sstevel 25103831d35Sstevel #ifdef DEBUG 25203831d35Sstevel pgcnt_t serengeti_cage_size_limit; 25303831d35Sstevel #endif 25403831d35Sstevel 25503831d35Sstevel /* Preferred minimum cage size (expressed in pages)... for DR */ 25603831d35Sstevel pgcnt_t serengeti_minimum_cage_size = 0; 25703831d35Sstevel 25803831d35Sstevel void 25903831d35Sstevel set_platform_cage_params(void) 26003831d35Sstevel { 26103831d35Sstevel extern pgcnt_t total_pages; 26203831d35Sstevel extern struct memlist *phys_avail; 26303831d35Sstevel 26403831d35Sstevel if (kernel_cage_enable) { 26503831d35Sstevel pgcnt_t preferred_cage_size; 26603831d35Sstevel 26703831d35Sstevel preferred_cage_size = 26803831d35Sstevel MAX(serengeti_minimum_cage_size, total_pages / 256); 26903831d35Sstevel #ifdef DEBUG 27003831d35Sstevel if (serengeti_cage_size_limit) 27103831d35Sstevel preferred_cage_size = serengeti_cage_size_limit; 27203831d35Sstevel #endif 27303831d35Sstevel /* 27403831d35Sstevel * Post copies obp into the lowest slice. This requires the 27503831d35Sstevel * cage to grow upwards 27603831d35Sstevel */ 27785f58038Sdp78419 kcage_range_init(phys_avail, KCAGE_UP, preferred_cage_size); 27803831d35Sstevel } 27903831d35Sstevel 2805832075cSsetje kcage_startup_dir = KCAGE_UP; 2815832075cSsetje 28203831d35Sstevel /* Only note when the cage is off since it should always be on. */ 28303831d35Sstevel if (!kcage_on) 28403831d35Sstevel cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED"); 28503831d35Sstevel } 28603831d35Sstevel 28703831d35Sstevel #define ALIGN(x, a) ((a) == 0 ? (uint64_t)(x) : \ 28803831d35Sstevel (((uint64_t)(x) + (uint64_t)(a) - 1l) & ~((uint64_t)(a) - 1l))) 28903831d35Sstevel 29003831d35Sstevel void 29103831d35Sstevel update_mem_bounds(int brd, uint64_t base, uint64_t sz) 29203831d35Sstevel { 29303831d35Sstevel uint64_t end; 29403831d35Sstevel int mnode; 29503831d35Sstevel 29603831d35Sstevel end = base + sz - 1; 29703831d35Sstevel 29803831d35Sstevel /* 29903831d35Sstevel * First see if this board already has a memnode associated 30003831d35Sstevel * with it. If not, see if this slice has a memnode. This 30103831d35Sstevel * covers the cases where a single slice covers multiple 30203831d35Sstevel * boards (cross-board interleaving) and where a single 30303831d35Sstevel * board has multiple slices (1+GB DIMMs). 30403831d35Sstevel */ 30503831d35Sstevel if ((mnode = plat_lgrphand_to_mem_node(brd)) == -1) { 30603831d35Sstevel if ((mnode = slice_to_memnode[PA_2_SLICE(base)]) == -1) 30703831d35Sstevel mnode = mem_node_alloc(); 30803831d35Sstevel plat_assign_lgrphand_to_mem_node(brd, mnode); 30903831d35Sstevel } 31003831d35Sstevel 31103831d35Sstevel /* 31203831d35Sstevel * Align base at 16GB boundary 31303831d35Sstevel */ 31403831d35Sstevel base = ALIGN(base, (1ul << PA_SLICE_SHIFT)); 31503831d35Sstevel 31603831d35Sstevel while (base < end) { 31703831d35Sstevel slice_to_memnode[PA_2_SLICE(base)] = mnode; 31803831d35Sstevel base += (1ul << PA_SLICE_SHIFT); 31903831d35Sstevel } 32003831d35Sstevel } 32103831d35Sstevel 32203831d35Sstevel /* 32303831d35Sstevel * Dynamically detect memory slices in the system by decoding 32403831d35Sstevel * the cpu memory decoder registers at boot time. 32503831d35Sstevel */ 32603831d35Sstevel void 32703831d35Sstevel plat_fill_mc(pnode_t nodeid) 32803831d35Sstevel { 32903831d35Sstevel uint64_t mc_addr, mask; 33003831d35Sstevel uint64_t mc_decode[SG_MAX_BANKS_PER_MC]; 33103831d35Sstevel uint64_t base, size; 33203831d35Sstevel uint32_t regs[4]; 33303831d35Sstevel int len; 33403831d35Sstevel int local_mc; 33503831d35Sstevel int portid; 33603831d35Sstevel int boardid; 33703831d35Sstevel int i; 33803831d35Sstevel 33903831d35Sstevel if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) || 34003831d35Sstevel (portid == -1)) 34103831d35Sstevel return; 34203831d35Sstevel 34303831d35Sstevel /* 34403831d35Sstevel * Decode the board number from the MC portid 34503831d35Sstevel */ 34603831d35Sstevel boardid = SG_PORTID_TO_BOARD_NUM(portid); 34703831d35Sstevel 34803831d35Sstevel /* 34903831d35Sstevel * The "reg" property returns 4 32-bit values. The first two are 35003831d35Sstevel * combined to form a 64-bit address. The second two are for a 35103831d35Sstevel * 64-bit size, but we don't actually need to look at that value. 35203831d35Sstevel */ 35303831d35Sstevel len = prom_getproplen(nodeid, "reg"); 35403831d35Sstevel if (len != (sizeof (uint32_t) * 4)) { 35503831d35Sstevel prom_printf("Warning: malformed 'reg' property\n"); 35603831d35Sstevel return; 35703831d35Sstevel } 35803831d35Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0) 35903831d35Sstevel return; 36003831d35Sstevel mc_addr = ((uint64_t)regs[0]) << 32; 36103831d35Sstevel mc_addr |= (uint64_t)regs[1]; 36203831d35Sstevel 36303831d35Sstevel /* 36403831d35Sstevel * Figure out whether the memory controller we are examining 36503831d35Sstevel * belongs to this CPU or a different one. 36603831d35Sstevel */ 36703831d35Sstevel if (portid == cpunodes[CPU->cpu_id].portid) 36803831d35Sstevel local_mc = 1; 36903831d35Sstevel else 37003831d35Sstevel local_mc = 0; 37103831d35Sstevel 37203831d35Sstevel for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) { 37303831d35Sstevel mask = SG_REG_2_OFFSET(i); 37403831d35Sstevel 37503831d35Sstevel /* 37603831d35Sstevel * If the memory controller is local to this CPU, we use 37703831d35Sstevel * the special ASI to read the decode registers. 37803831d35Sstevel * Otherwise, we load the values from a magic address in 37903831d35Sstevel * I/O space. 38003831d35Sstevel */ 38103831d35Sstevel if (local_mc) 38203831d35Sstevel mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK); 38303831d35Sstevel else 38403831d35Sstevel mc_decode[i] = lddphysio((mc_addr | mask)); 38503831d35Sstevel 38603831d35Sstevel if (mc_decode[i] >> MC_VALID_SHIFT) { 38703831d35Sstevel /* 38803831d35Sstevel * The memory decode register is a bitmask field, 38903831d35Sstevel * so we can decode that into both a base and 39003831d35Sstevel * a span. 39103831d35Sstevel */ 39203831d35Sstevel base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT; 39303831d35Sstevel size = MC_UK2SPAN(mc_decode[i]); 39403831d35Sstevel update_mem_bounds(boardid, base, size); 39503831d35Sstevel } 39603831d35Sstevel } 39703831d35Sstevel } 39803831d35Sstevel 39903831d35Sstevel /* 40003831d35Sstevel * This routine is run midway through the boot process. By the time we get 40103831d35Sstevel * here, we know about all the active CPU boards in the system, and we have 40203831d35Sstevel * extracted information about each board's memory from the memory 40303831d35Sstevel * controllers. We have also figured out which ranges of memory will be 40403831d35Sstevel * assigned to which memnodes, so we walk the slice table to build the table 40503831d35Sstevel * of memnodes. 40603831d35Sstevel */ 40703831d35Sstevel /* ARGSUSED */ 40803831d35Sstevel void 409986fd29aSsetje plat_build_mem_nodes(prom_memlist_t *list, size_t nelems) 41003831d35Sstevel { 41103831d35Sstevel int slice; 41203831d35Sstevel pfn_t basepfn; 41303831d35Sstevel pgcnt_t npgs; 41403831d35Sstevel 41503831d35Sstevel mem_node_pfn_shift = PFN_SLICE_SHIFT; 41603831d35Sstevel mem_node_physalign = (1ull << PA_SLICE_SHIFT); 41703831d35Sstevel 41803831d35Sstevel for (slice = 0; slice < SG_MAX_SLICE; slice++) { 41903831d35Sstevel if (slice_to_memnode[slice] == -1) 42003831d35Sstevel continue; 42103831d35Sstevel basepfn = (uint64_t)slice << PFN_SLICE_SHIFT; 42203831d35Sstevel npgs = 1ull << PFN_SLICE_SHIFT; 42303831d35Sstevel mem_node_add_slice(basepfn, basepfn + npgs - 1); 42403831d35Sstevel } 42503831d35Sstevel } 42603831d35Sstevel 42703831d35Sstevel int 42803831d35Sstevel plat_pfn_to_mem_node(pfn_t pfn) 42903831d35Sstevel { 43003831d35Sstevel int node; 43103831d35Sstevel 43203831d35Sstevel node = slice_to_memnode[PFN_2_SLICE(pfn)]; 43303831d35Sstevel 43403831d35Sstevel return (node); 43503831d35Sstevel } 43603831d35Sstevel 43703831d35Sstevel /* 43803831d35Sstevel * Serengeti support for lgroups. 43903831d35Sstevel * 44003831d35Sstevel * On Serengeti, an lgroup platform handle == board number. 44103831d35Sstevel * 44203831d35Sstevel * Mappings between lgroup handles and memnodes are managed 44303831d35Sstevel * in addition to mappings between memory slices and memnodes 44403831d35Sstevel * to support cross-board interleaving as well as multiple 44503831d35Sstevel * slices per board (e.g. >1GB DIMMs). The initial mapping 44603831d35Sstevel * of memnodes to lgroup handles is determined at boot time. 44703831d35Sstevel * A DR addition of memory adds a new mapping. A DR copy-rename 44803831d35Sstevel * swaps mappings. 44903831d35Sstevel */ 45003831d35Sstevel 45103831d35Sstevel /* 45203831d35Sstevel * Macro for extracting the board number from the CPU id 45303831d35Sstevel */ 45403831d35Sstevel #define CPUID_TO_BOARD(id) (((id) >> 2) & 0x7) 45503831d35Sstevel 45603831d35Sstevel /* 45703831d35Sstevel * Return the platform handle for the lgroup containing the given CPU 45803831d35Sstevel * 45903831d35Sstevel * For Serengeti, lgroup platform handle == board number 46003831d35Sstevel */ 46103831d35Sstevel lgrp_handle_t 46203831d35Sstevel plat_lgrp_cpu_to_hand(processorid_t id) 46303831d35Sstevel { 46403831d35Sstevel return (CPUID_TO_BOARD(id)); 46503831d35Sstevel } 46603831d35Sstevel 46703831d35Sstevel /* 46803831d35Sstevel * Platform specific lgroup initialization 46903831d35Sstevel */ 47003831d35Sstevel void 47103831d35Sstevel plat_lgrp_init(void) 47203831d35Sstevel { 47303831d35Sstevel int i; 47403831d35Sstevel extern uint32_t lgrp_expand_proc_thresh; 47503831d35Sstevel extern uint32_t lgrp_expand_proc_diff; 47603831d35Sstevel 47703831d35Sstevel /* 47803831d35Sstevel * Initialize lookup tables to invalid values so we catch 47903831d35Sstevel * any illegal use of them. 48003831d35Sstevel */ 48103831d35Sstevel for (i = 0; i < SG_MAX_SLICE; i++) { 48203831d35Sstevel slice_to_memnode[i] = -1; 48303831d35Sstevel } 48403831d35Sstevel 48503831d35Sstevel /* 48603831d35Sstevel * Set tuneables for Serengeti architecture 48703831d35Sstevel * 48803831d35Sstevel * lgrp_expand_proc_thresh is the minimum load on the lgroups 48903831d35Sstevel * this process is currently running on before considering 49003831d35Sstevel * expanding threads to another lgroup. 49103831d35Sstevel * 49203831d35Sstevel * lgrp_expand_proc_diff determines how much less the remote lgroup 49303831d35Sstevel * must be loaded before expanding to it. 49403831d35Sstevel * 49503831d35Sstevel * Bandwidth is maximized on Serengeti by spreading load across 49603831d35Sstevel * the machine. The impact to inter-thread communication isn't 49703831d35Sstevel * too costly since remote latencies are relatively low. These 49803831d35Sstevel * values equate to one CPU's load and so attempt to spread the 49903831d35Sstevel * load out across as many lgroups as possible one CPU at a time. 50003831d35Sstevel */ 50103831d35Sstevel lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX; 50203831d35Sstevel lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX; 50303831d35Sstevel } 50403831d35Sstevel 50503831d35Sstevel /* 50603831d35Sstevel * Platform notification of lgroup (re)configuration changes 50703831d35Sstevel */ 50803831d35Sstevel /*ARGSUSED*/ 50903831d35Sstevel void 51003831d35Sstevel plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg) 51103831d35Sstevel { 51203831d35Sstevel update_membounds_t *umb; 51303831d35Sstevel lgrp_config_mem_rename_t lmr; 51403831d35Sstevel lgrp_handle_t shand, thand; 51503831d35Sstevel int snode, tnode; 51603831d35Sstevel 51703831d35Sstevel switch (evt) { 51803831d35Sstevel 51903831d35Sstevel case LGRP_CONFIG_MEM_ADD: 52003831d35Sstevel umb = (update_membounds_t *)arg; 52103831d35Sstevel update_mem_bounds(umb->u_board, umb->u_base, umb->u_len); 52203831d35Sstevel 52303831d35Sstevel break; 52403831d35Sstevel 52503831d35Sstevel case LGRP_CONFIG_MEM_DEL: 52603831d35Sstevel /* We don't have to do anything */ 52703831d35Sstevel break; 52803831d35Sstevel 52903831d35Sstevel case LGRP_CONFIG_MEM_RENAME: 53003831d35Sstevel /* 53103831d35Sstevel * During a DR copy-rename operation, all of the memory 53203831d35Sstevel * on one board is moved to another board -- but the 53303831d35Sstevel * addresses/pfns and memnodes don't change. This means 53403831d35Sstevel * the memory has changed locations without changing identity. 53503831d35Sstevel * 53603831d35Sstevel * Source is where we are copying from and target is where we 53703831d35Sstevel * are copying to. After source memnode is copied to target 53803831d35Sstevel * memnode, the physical addresses of the target memnode are 53903831d35Sstevel * renamed to match what the source memnode had. Then target 54003831d35Sstevel * memnode can be removed and source memnode can take its 54103831d35Sstevel * place. 54203831d35Sstevel * 54303831d35Sstevel * To do this, swap the lgroup handle to memnode mappings for 54403831d35Sstevel * the boards, so target lgroup will have source memnode and 54503831d35Sstevel * source lgroup will have empty target memnode which is where 54603831d35Sstevel * its memory will go (if any is added to it later). 54703831d35Sstevel * 54803831d35Sstevel * Then source memnode needs to be removed from its lgroup 54903831d35Sstevel * and added to the target lgroup where the memory was living 55003831d35Sstevel * but under a different name/memnode. The memory was in the 55103831d35Sstevel * target memnode and now lives in the source memnode with 55203831d35Sstevel * different physical addresses even though it is the same 55303831d35Sstevel * memory. 55403831d35Sstevel */ 55503831d35Sstevel shand = arg & 0xffff; 55603831d35Sstevel thand = (arg & 0xffff0000) >> 16; 55703831d35Sstevel snode = plat_lgrphand_to_mem_node(shand); 55803831d35Sstevel tnode = plat_lgrphand_to_mem_node(thand); 55903831d35Sstevel 56003831d35Sstevel plat_assign_lgrphand_to_mem_node(thand, snode); 56103831d35Sstevel plat_assign_lgrphand_to_mem_node(shand, tnode); 56203831d35Sstevel 56303831d35Sstevel /* 56403831d35Sstevel * Remove source memnode of copy rename from its lgroup 56503831d35Sstevel * and add it to its new target lgroup 56603831d35Sstevel */ 56703831d35Sstevel lmr.lmem_rename_from = shand; 56803831d35Sstevel lmr.lmem_rename_to = thand; 56903831d35Sstevel 57003831d35Sstevel lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode, 57103831d35Sstevel (uintptr_t)&lmr); 57203831d35Sstevel 57303831d35Sstevel break; 57403831d35Sstevel 57503831d35Sstevel default: 57603831d35Sstevel break; 57703831d35Sstevel } 57803831d35Sstevel } 57903831d35Sstevel 58003831d35Sstevel /* 58103831d35Sstevel * Return latency between "from" and "to" lgroups 58203831d35Sstevel * 58303831d35Sstevel * This latency number can only be used for relative comparison 58403831d35Sstevel * between lgroups on the running system, cannot be used across platforms, 58503831d35Sstevel * and may not reflect the actual latency. It is platform and implementation 58603831d35Sstevel * specific, so platform gets to decide its value. It would be nice if the 58703831d35Sstevel * number was at least proportional to make comparisons more meaningful though. 58803831d35Sstevel * NOTE: The numbers below are supposed to be load latencies for uncached 58903831d35Sstevel * memory divided by 10. 59003831d35Sstevel */ 59103831d35Sstevel int 59203831d35Sstevel plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to) 59303831d35Sstevel { 59403831d35Sstevel /* 59503831d35Sstevel * Return min remote latency when there are more than two lgroups 59603831d35Sstevel * (root and child) and getting latency between two different lgroups 59703831d35Sstevel * or root is involved 59803831d35Sstevel */ 59903831d35Sstevel if (lgrp_optimizations() && (from != to || 60003831d35Sstevel from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE)) 60103831d35Sstevel return (28); 60203831d35Sstevel else 60303831d35Sstevel return (23); 60403831d35Sstevel } 60503831d35Sstevel 60603831d35Sstevel /* ARGSUSED */ 60703831d35Sstevel void 60803831d35Sstevel plat_freelist_process(int mnode) 60903831d35Sstevel { 61003831d35Sstevel } 61103831d35Sstevel 61203831d35Sstevel /* 61303831d35Sstevel * Find dip for chosen IOSRAM 61403831d35Sstevel */ 61503831d35Sstevel dev_info_t * 61603831d35Sstevel find_chosen_dip(void) 61703831d35Sstevel { 61803831d35Sstevel dev_info_t *dip; 61903831d35Sstevel char master_sbbc[MAXNAMELEN]; 62003831d35Sstevel pnode_t nodeid; 62103831d35Sstevel uint_t tunnel; 62203831d35Sstevel 62303831d35Sstevel /* 62403831d35Sstevel * find the /chosen SBBC node, prom interface will handle errors 62503831d35Sstevel */ 62603831d35Sstevel nodeid = prom_chosennode(); 62703831d35Sstevel 62803831d35Sstevel /* 62903831d35Sstevel * get the 'iosram' property from the /chosen node 63003831d35Sstevel */ 63103831d35Sstevel if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) { 63203831d35Sstevel SBBC_ERR(CE_PANIC, "No iosram property found! \n"); 63303831d35Sstevel } 63403831d35Sstevel 63503831d35Sstevel if (prom_phandle_to_path((phandle_t)tunnel, master_sbbc, 63603831d35Sstevel sizeof (master_sbbc)) < 0) { 63703831d35Sstevel SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n", 63803831d35Sstevel tunnel); 63903831d35Sstevel } 64003831d35Sstevel 64103831d35Sstevel chosen_nodeid = nodeid; 64203831d35Sstevel 64303831d35Sstevel /* 64403831d35Sstevel * load and attach the sgsbbc driver. 64503831d35Sstevel * This will also attach all the sgsbbc driver instances 64603831d35Sstevel */ 64703831d35Sstevel if (i_ddi_attach_hw_nodes("sgsbbc") != DDI_SUCCESS) { 64803831d35Sstevel cmn_err(CE_WARN, "sgsbbc failed to load\n"); 64903831d35Sstevel } 65003831d35Sstevel 65103831d35Sstevel /* translate a path name to a dev_info_t */ 65203831d35Sstevel dip = e_ddi_hold_devi_by_path(master_sbbc, 0); 65303831d35Sstevel if ((dip == NULL) || (ddi_get_nodeid(dip) != tunnel)) { 65403831d35Sstevel cmn_err(CE_PANIC, "i_ddi_path_to_devi(%x) failed for SBBC\n", 65503831d35Sstevel tunnel); 65603831d35Sstevel } 65703831d35Sstevel 65803831d35Sstevel /* make sure devi_ref is ZERO */ 65903831d35Sstevel ndi_rele_devi(dip); 66003831d35Sstevel 66103831d35Sstevel DCMNERR(CE_CONT, "Chosen IOSRAM is at %s \n", master_sbbc); 66203831d35Sstevel 66303831d35Sstevel return (dip); 66403831d35Sstevel } 66503831d35Sstevel 66603831d35Sstevel void 66703831d35Sstevel load_platform_drivers(void) 66803831d35Sstevel { 66903831d35Sstevel int ret; 67003831d35Sstevel 67103831d35Sstevel /* 67203831d35Sstevel * Load and attach the mc-us3 memory driver. 67303831d35Sstevel */ 67403831d35Sstevel if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS) 67503831d35Sstevel cmn_err(CE_WARN, "mc-us3 failed to load"); 67603831d35Sstevel else 67703831d35Sstevel (void) ddi_hold_driver(ddi_name_to_major("mc-us3")); 67803831d35Sstevel 67903831d35Sstevel /* 68003831d35Sstevel * Initialize the chosen IOSRAM before its clients 68103831d35Sstevel * are loaded. 68203831d35Sstevel */ 68303831d35Sstevel (void) find_chosen_dip(); 68403831d35Sstevel 68503831d35Sstevel /* 68603831d35Sstevel * Ideally, we'd do this in set_platform_defaults(), but 68703831d35Sstevel * at that point it's too early to look up symbols. 68803831d35Sstevel */ 68903831d35Sstevel iosram_write_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t)) 69003831d35Sstevel modgetsymvalue("iosram_write", 0); 69103831d35Sstevel 69203831d35Sstevel if (iosram_write_ptr == NULL) { 69303831d35Sstevel DCMNERR(CE_WARN, "load_platform_defaults: iosram_write()" 69403831d35Sstevel " not found; signatures will not be updated\n"); 69503831d35Sstevel } else { 69603831d35Sstevel /* 69703831d35Sstevel * The iosram read ptr is only needed if we can actually 69803831d35Sstevel * write CPU signatures, so only bother setting it if we 69903831d35Sstevel * set a valid write pointer, above. 70003831d35Sstevel */ 70103831d35Sstevel iosram_read_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t)) 70203831d35Sstevel modgetsymvalue("iosram_read", 0); 70303831d35Sstevel 70403831d35Sstevel if (iosram_read_ptr == NULL) 70503831d35Sstevel DCMNERR(CE_WARN, "load_platform_defaults: iosram_read()" 70603831d35Sstevel " not found\n"); 70703831d35Sstevel } 70803831d35Sstevel 70903831d35Sstevel /* 71003831d35Sstevel * Set todsg_use_sc to TRUE so that we will be getting date 71103831d35Sstevel * from the SC. 71203831d35Sstevel */ 71303831d35Sstevel todsg_use_sc = TRUE; 71403831d35Sstevel 71503831d35Sstevel /* 71603831d35Sstevel * Now is a good time to activate hardware watchdog (if one exists). 71703831d35Sstevel */ 71803831d35Sstevel mutex_enter(&tod_lock); 71903831d35Sstevel if (watchdog_enable) 72003831d35Sstevel ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds); 72103831d35Sstevel mutex_exit(&tod_lock); 72203831d35Sstevel if (ret != 0) 72303831d35Sstevel printf("Hardware watchdog enabled\n"); 72403831d35Sstevel 72503831d35Sstevel /* 72603831d35Sstevel * Load and attach the schizo pci bus nexus driver. 72703831d35Sstevel */ 72803831d35Sstevel if (i_ddi_attach_hw_nodes("pcisch") != DDI_SUCCESS) 72903831d35Sstevel cmn_err(CE_WARN, "pcisch failed to load"); 73003831d35Sstevel 73103831d35Sstevel plat_ecc_init(); 73203831d35Sstevel } 73303831d35Sstevel 73403831d35Sstevel /* 73503831d35Sstevel * No platform drivers on this platform 73603831d35Sstevel */ 73703831d35Sstevel char *platform_module_list[] = { 73803831d35Sstevel (char *)0 73903831d35Sstevel }; 74003831d35Sstevel 74103831d35Sstevel /*ARGSUSED*/ 74203831d35Sstevel void 74303831d35Sstevel plat_tod_fault(enum tod_fault_type tod_bad) 74403831d35Sstevel { 74503831d35Sstevel } 74603831d35Sstevel int 74703831d35Sstevel plat_max_boards() 74803831d35Sstevel { 74903831d35Sstevel return (SG_MAX_BDS); 75003831d35Sstevel } 75103831d35Sstevel int 75203831d35Sstevel plat_max_io_units_per_board() 75303831d35Sstevel { 75403831d35Sstevel return (SG_MAX_IO_PER_BD); 75503831d35Sstevel } 75603831d35Sstevel int 75703831d35Sstevel plat_max_cmp_units_per_board() 75803831d35Sstevel { 75903831d35Sstevel return (SG_MAX_CMPS_PER_BD); 76003831d35Sstevel } 76103831d35Sstevel int 76203831d35Sstevel plat_max_cpu_units_per_board() 76303831d35Sstevel { 76403831d35Sstevel return (SG_MAX_CPUS_PER_BD); 76503831d35Sstevel } 76603831d35Sstevel 76703831d35Sstevel int 76803831d35Sstevel plat_max_mc_units_per_board() 76903831d35Sstevel { 77003831d35Sstevel return (SG_MAX_CMPS_PER_BD); /* each CPU die has a memory controller */ 77103831d35Sstevel } 77203831d35Sstevel 77303831d35Sstevel int 77403831d35Sstevel plat_max_mem_units_per_board() 77503831d35Sstevel { 77603831d35Sstevel return (SG_MAX_MEM_PER_BD); 77703831d35Sstevel } 77803831d35Sstevel 77903831d35Sstevel int 78003831d35Sstevel plat_max_cpumem_boards(void) 78103831d35Sstevel { 78203831d35Sstevel return (SG_MAX_CPU_BDS); 78303831d35Sstevel } 78403831d35Sstevel 78503831d35Sstevel int 78603831d35Sstevel set_platform_max_ncpus(void) 78703831d35Sstevel { 78803831d35Sstevel return (sg_max_ncpus); 78903831d35Sstevel } 79003831d35Sstevel 79103831d35Sstevel void 79203831d35Sstevel plat_dmv_params(uint_t *hwint, uint_t *swint) 79303831d35Sstevel { 79403831d35Sstevel *hwint = MAX_UPA; 79503831d35Sstevel *swint = 0; 79603831d35Sstevel } 79703831d35Sstevel 79803831d35Sstevel /* 79903831d35Sstevel * Our nodename has been set, pass it along to the SC. 80003831d35Sstevel */ 80103831d35Sstevel void 80203831d35Sstevel plat_nodename_set(void) 80303831d35Sstevel { 80403831d35Sstevel sbbc_msg_t req; /* request */ 80503831d35Sstevel sbbc_msg_t resp; /* response */ 80603831d35Sstevel int rv; /* return value from call to mbox */ 80703831d35Sstevel struct nodename_info { 80803831d35Sstevel int32_t namelen; 80903831d35Sstevel char nodename[_SYS_NMLN]; 81003831d35Sstevel } nni; 81103831d35Sstevel int (*sg_mbox)(sbbc_msg_t *, sbbc_msg_t *, time_t) = NULL; 81203831d35Sstevel 81303831d35Sstevel /* 81403831d35Sstevel * find the symbol for the mailbox routine 81503831d35Sstevel */ 81603831d35Sstevel sg_mbox = (int (*)(sbbc_msg_t *, sbbc_msg_t *, time_t)) 81703831d35Sstevel modgetsymvalue("sbbc_mbox_request_response", 0); 81803831d35Sstevel 81903831d35Sstevel if (sg_mbox == NULL) { 82003831d35Sstevel cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox not found\n"); 82103831d35Sstevel return; 82203831d35Sstevel } 82303831d35Sstevel 82403831d35Sstevel /* 82503831d35Sstevel * construct the message telling the SC our nodename 82603831d35Sstevel */ 82703831d35Sstevel (void) strcpy(nni.nodename, utsname.nodename); 82803831d35Sstevel nni.namelen = (int32_t)strlen(nni.nodename); 82903831d35Sstevel 83003831d35Sstevel req.msg_type.type = INFO_MBOX; 83103831d35Sstevel req.msg_type.sub_type = INFO_MBOX_NODENAME; 83203831d35Sstevel req.msg_status = 0; 83303831d35Sstevel req.msg_len = (int)(nni.namelen + sizeof (nni.namelen)); 83403831d35Sstevel req.msg_bytes = 0; 83503831d35Sstevel req.msg_buf = (caddr_t)&nni; 83603831d35Sstevel req.msg_data[0] = 0; 83703831d35Sstevel req.msg_data[1] = 0; 83803831d35Sstevel 83903831d35Sstevel /* 84003831d35Sstevel * initialize the response back from the SC 84103831d35Sstevel */ 84203831d35Sstevel resp.msg_type.type = INFO_MBOX; 84303831d35Sstevel resp.msg_type.sub_type = INFO_MBOX_NODENAME; 84403831d35Sstevel resp.msg_status = 0; 84503831d35Sstevel resp.msg_len = 0; 84603831d35Sstevel resp.msg_bytes = 0; 84703831d35Sstevel resp.msg_buf = (caddr_t)0; 84803831d35Sstevel resp.msg_data[0] = 0; 84903831d35Sstevel resp.msg_data[1] = 0; 85003831d35Sstevel 85103831d35Sstevel /* 85203831d35Sstevel * ship it and check for success 85303831d35Sstevel */ 85403831d35Sstevel rv = (sg_mbox)(&req, &resp, sbbc_mbox_default_timeout); 85503831d35Sstevel 85603831d35Sstevel if (rv != 0) { 85703831d35Sstevel cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox retval %d\n", rv); 85803831d35Sstevel } else if (resp.msg_status != 0) { 85903831d35Sstevel cmn_err(CE_NOTE, "!plat_nodename_set: msg_status %d\n", 86003831d35Sstevel resp.msg_status); 86103831d35Sstevel } else { 86203831d35Sstevel DCMNERR(CE_NOTE, "!plat_nodename_set was successful\n"); 86303831d35Sstevel 86403831d35Sstevel /* 86503831d35Sstevel * It is necessary to exchange the capability bitmap 86603831d35Sstevel * with SC before sending any ecc error information and 86703831d35Sstevel * indictment. We are calling the plat_ecc_capability_send() 86803831d35Sstevel * here just after sending the nodename successfully. 86903831d35Sstevel */ 87003831d35Sstevel rv = plat_ecc_capability_send(); 87103831d35Sstevel if (rv == 0) { 87203831d35Sstevel DCMNERR(CE_NOTE, "!plat_ecc_capability_send was" 87303831d35Sstevel " successful\n"); 87403831d35Sstevel } 87503831d35Sstevel } 87603831d35Sstevel } 87703831d35Sstevel 87803831d35Sstevel /* 87903831d35Sstevel * flag to allow users switch between using OBP's 88003831d35Sstevel * prom_get_unum() and mc-us3 driver's p2get_mem_unum() 88103831d35Sstevel * (for main memory errors only). 88203831d35Sstevel */ 88303831d35Sstevel int sg_use_prom_get_unum = 0; 88403831d35Sstevel 88503831d35Sstevel /* 88603831d35Sstevel * Debugging flag: set to 1 to call into obp for get_unum, or set it to 0 88703831d35Sstevel * to call into the unum cache system. This is the E$ equivalent of 88803831d35Sstevel * sg_use_prom_get_unum. 88903831d35Sstevel */ 89003831d35Sstevel int sg_use_prom_ecache_unum = 0; 89103831d35Sstevel 89203831d35Sstevel /* used for logging ECC errors to the SC */ 89303831d35Sstevel #define SG_MEMORY_ECC 1 89403831d35Sstevel #define SG_ECACHE_ECC 2 89503831d35Sstevel #define SG_UNKNOWN_ECC (-1) 89603831d35Sstevel 89703831d35Sstevel /* 89803831d35Sstevel * plat_get_mem_unum() generates a string identifying either the 89903831d35Sstevel * memory or E$ DIMM(s) during error logging. Depending on whether 90003831d35Sstevel * the error is E$ or memory related, the appropriate support 90103831d35Sstevel * routine is called to assist in the string generation. 90203831d35Sstevel * 90303831d35Sstevel * - For main memory errors we can use the mc-us3 drivers p2getunum() 90403831d35Sstevel * (or prom_get_unum() for debugging purposes). 90503831d35Sstevel * 90603831d35Sstevel * - For E$ errors we call sg_get_ecacheunum() to generate the unum (or 90703831d35Sstevel * prom_serengeti_get_ecacheunum() for debugging purposes). 90803831d35Sstevel */ 90903831d35Sstevel 91003831d35Sstevel static int 91103831d35Sstevel sg_prom_get_unum(int synd_code, uint64_t paddr, char *buf, int buflen, 91203831d35Sstevel int *lenp) 91303831d35Sstevel { 91403831d35Sstevel if ((prom_get_unum(synd_code, (unsigned long long)paddr, 91503831d35Sstevel buf, buflen, lenp)) != 0) 91603831d35Sstevel return (EIO); 91703831d35Sstevel else if (*lenp <= 1) 91803831d35Sstevel return (EINVAL); 91903831d35Sstevel else 92003831d35Sstevel return (0); 92103831d35Sstevel } 92203831d35Sstevel 92303831d35Sstevel /*ARGSUSED*/ 92403831d35Sstevel int 92503831d35Sstevel plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id, 92603831d35Sstevel int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp) 92703831d35Sstevel { 92803831d35Sstevel /* 92903831d35Sstevel * unum_func will either point to the memory drivers p2get_mem_unum() 93003831d35Sstevel * or to prom_get_unum() for memory errors. 93103831d35Sstevel */ 93203831d35Sstevel int (*unum_func)(int synd_code, uint64_t paddr, char *buf, 93303831d35Sstevel int buflen, int *lenp) = p2get_mem_unum; 93403831d35Sstevel 93503831d35Sstevel /* 93603831d35Sstevel * check if it's a Memory or an Ecache error. 93703831d35Sstevel */ 93803831d35Sstevel if (flt_in_memory) { 93903831d35Sstevel /* 94003831d35Sstevel * It's a main memory error. 94103831d35Sstevel * 94203831d35Sstevel * For debugging we allow the user to switch between 94303831d35Sstevel * using OBP's get_unum and the memory driver's get_unum 94403831d35Sstevel * so we create a pointer to the functions and switch 94503831d35Sstevel * depending on the sg_use_prom_get_unum flag. 94603831d35Sstevel */ 94703831d35Sstevel if (sg_use_prom_get_unum) { 94803831d35Sstevel DCMNERR(CE_NOTE, "Using prom_get_unum from OBP"); 94903831d35Sstevel return (sg_prom_get_unum(synd_code, 95003831d35Sstevel P2ALIGN(flt_addr, 8), buf, buflen, lenp)); 95103831d35Sstevel } else if (unum_func != NULL) { 95203831d35Sstevel return (unum_func(synd_code, P2ALIGN(flt_addr, 8), 95303831d35Sstevel buf, buflen, lenp)); 95403831d35Sstevel } else { 95503831d35Sstevel return (ENOTSUP); 95603831d35Sstevel } 95703831d35Sstevel } else if (flt_status & ECC_ECACHE) { 95803831d35Sstevel /* 95903831d35Sstevel * It's an E$ error. 96003831d35Sstevel */ 96103831d35Sstevel if (sg_use_prom_ecache_unum) { 96203831d35Sstevel /* 96303831d35Sstevel * We call to OBP to handle this. 96403831d35Sstevel */ 96503831d35Sstevel DCMNERR(CE_NOTE, 96603831d35Sstevel "Using prom_serengeti_get_ecacheunum from OBP"); 96703831d35Sstevel if (prom_serengeti_get_ecacheunum(flt_bus_id, 96803831d35Sstevel P2ALIGN(flt_addr, 8), buf, buflen, lenp) != 0) { 96903831d35Sstevel return (EIO); 97003831d35Sstevel } 97103831d35Sstevel } else { 97203831d35Sstevel return (sg_get_ecacheunum(flt_bus_id, flt_addr, 97303831d35Sstevel buf, buflen, lenp)); 97403831d35Sstevel } 97503831d35Sstevel } else { 97603831d35Sstevel return (ENOTSUP); 97703831d35Sstevel } 97803831d35Sstevel 97903831d35Sstevel return (0); 98003831d35Sstevel } 98103831d35Sstevel 98203831d35Sstevel /* 98303831d35Sstevel * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3 98403831d35Sstevel * driver giving each platform the opportunity to add platform 98503831d35Sstevel * specific label information to the unum for ECC error logging purposes. 98603831d35Sstevel */ 98703831d35Sstevel void 98803831d35Sstevel plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm) 98903831d35Sstevel { 99003831d35Sstevel char new_unum[UNUM_NAMLEN] = ""; 99103831d35Sstevel int node = SG_PORTID_TO_NODEID(mcid); 99203831d35Sstevel int board = SG_CPU_BD_PORTID_TO_BD_NUM(mcid); 99303831d35Sstevel int position = SG_PORTID_TO_CPU_POSN(mcid); 99403831d35Sstevel 99503831d35Sstevel /* 99603831d35Sstevel * The mc-us3 driver deals with logical banks but for unum 99703831d35Sstevel * purposes we need to use physical banks so that the correct 99803831d35Sstevel * dimm can be physically located. Logical banks 0 and 2 99903831d35Sstevel * make up physical bank 0. Logical banks 1 and 3 make up 100003831d35Sstevel * physical bank 1. Here we do the necessary conversion. 100103831d35Sstevel */ 100203831d35Sstevel bank = (bank % 2); 100303831d35Sstevel 100403831d35Sstevel if (dimm == -1) { 100503831d35Sstevel SG_SET_FRU_NAME_NODE(new_unum, node); 100603831d35Sstevel SG_SET_FRU_NAME_CPU_BOARD(new_unum, board); 100703831d35Sstevel SG_SET_FRU_NAME_MODULE(new_unum, position); 100803831d35Sstevel SG_SET_FRU_NAME_BANK(new_unum, bank); 100903831d35Sstevel 101003831d35Sstevel } else { 101103831d35Sstevel SG_SET_FRU_NAME_NODE(new_unum, node); 101203831d35Sstevel SG_SET_FRU_NAME_CPU_BOARD(new_unum, board); 101303831d35Sstevel SG_SET_FRU_NAME_MODULE(new_unum, position); 101403831d35Sstevel SG_SET_FRU_NAME_BANK(new_unum, bank); 101503831d35Sstevel SG_SET_FRU_NAME_DIMM(new_unum, dimm); 101603831d35Sstevel 1017*07d06da5SSurya Prakki (void) strcat(new_unum, " "); 1018*07d06da5SSurya Prakki (void) strcat(new_unum, unum); 101903831d35Sstevel } 102003831d35Sstevel 1021*07d06da5SSurya Prakki (void) strcpy(unum, new_unum); 102203831d35Sstevel } 102303831d35Sstevel 102403831d35Sstevel int 102503831d35Sstevel plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp) 102603831d35Sstevel { 102703831d35Sstevel int node = SG_PORTID_TO_NODEID(cpuid); 102803831d35Sstevel int board = SG_CPU_BD_PORTID_TO_BD_NUM(cpuid); 102903831d35Sstevel 103003831d35Sstevel if (snprintf(buf, buflen, "/N%d/%s%d", node, 103103831d35Sstevel SG_HPU_TYPE_CPU_BOARD_ID, board) >= buflen) { 103203831d35Sstevel return (ENOSPC); 103303831d35Sstevel } else { 103403831d35Sstevel *lenp = strlen(buf); 103503831d35Sstevel return (0); 103603831d35Sstevel } 103703831d35Sstevel } 103803831d35Sstevel 103903831d35Sstevel /* 104003831d35Sstevel * We log all ECC events to the SC so we send a mailbox 104103831d35Sstevel * message to the SC passing it the relevant data. 104203831d35Sstevel * ECC mailbox messages are sent via a taskq mechanism to 104303831d35Sstevel * prevent impaired system performance during ECC floods. 104403831d35Sstevel * Indictments have already passed through a taskq, so they 104503831d35Sstevel * are not queued here. 104603831d35Sstevel */ 104703831d35Sstevel int 104803831d35Sstevel plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap) 104903831d35Sstevel { 105003831d35Sstevel sbbc_ecc_mbox_t *msgp; 105103831d35Sstevel size_t msg_size; 105203831d35Sstevel uint16_t msg_subtype; 105303831d35Sstevel int sleep_flag, log_error; 105403831d35Sstevel 105503831d35Sstevel if (sg_ecc_taskq_func == NULL) { 105603831d35Sstevel sg_ecc_taskq_func = (void (*)(sbbc_ecc_mbox_t *)) 105703831d35Sstevel modgetsymvalue("sbbc_mbox_queue_ecc_event", 0); 105803831d35Sstevel if (sg_ecc_taskq_func == NULL) { 105903831d35Sstevel cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: " 106003831d35Sstevel "sbbc_mbox_queue_ecc_event not found"); 106103831d35Sstevel return (ENODEV); 106203831d35Sstevel } 106303831d35Sstevel } 106403831d35Sstevel if (sg_ecc_mbox_func == NULL) { 106503831d35Sstevel sg_ecc_mbox_func = (int (*)(sbbc_ecc_mbox_t *)) 106603831d35Sstevel modgetsymvalue("sbbc_mbox_ecc_output", 0); 106703831d35Sstevel if (sg_ecc_mbox_func == NULL) { 106803831d35Sstevel cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: " 106903831d35Sstevel "sbbc_mbox_ecc_output not found"); 107003831d35Sstevel return (ENODEV); 107103831d35Sstevel } 107203831d35Sstevel } 107303831d35Sstevel 107403831d35Sstevel /* 107503831d35Sstevel * Initialize the request and response structures 107603831d35Sstevel */ 107703831d35Sstevel switch (msg_type) { 107803831d35Sstevel case PLAT_ECC_ERROR_MESSAGE: 107903831d35Sstevel msg_subtype = INFO_MBOX_ERROR_ECC; 108003831d35Sstevel msg_size = sizeof (plat_ecc_error_data_t); 108103831d35Sstevel sleep_flag = KM_NOSLEEP; 108203831d35Sstevel log_error = 1; 108303831d35Sstevel break; 108403831d35Sstevel case PLAT_ECC_ERROR2_MESSAGE: 108503831d35Sstevel msg_subtype = INFO_MBOX_ECC; 108603831d35Sstevel msg_size = sizeof (plat_ecc_error2_data_t); 108703831d35Sstevel sleep_flag = KM_NOSLEEP; 108803831d35Sstevel log_error = 1; 108903831d35Sstevel break; 109003831d35Sstevel case PLAT_ECC_INDICTMENT_MESSAGE: 109103831d35Sstevel msg_subtype = INFO_MBOX_ERROR_INDICT; 109203831d35Sstevel msg_size = sizeof (plat_ecc_indictment_data_t); 109303831d35Sstevel sleep_flag = KM_SLEEP; 109403831d35Sstevel log_error = 0; 109503831d35Sstevel break; 109603831d35Sstevel case PLAT_ECC_INDICTMENT2_MESSAGE: 109703831d35Sstevel msg_subtype = INFO_MBOX_ECC; 109803831d35Sstevel msg_size = sizeof (plat_ecc_indictment2_data_t); 109903831d35Sstevel sleep_flag = KM_SLEEP; 110003831d35Sstevel log_error = 0; 110103831d35Sstevel break; 110203831d35Sstevel case PLAT_ECC_CAPABILITY_MESSAGE: 110303831d35Sstevel msg_subtype = INFO_MBOX_ECC_CAP; 110403831d35Sstevel msg_size = sizeof (plat_capability_data_t) + 110503831d35Sstevel strlen(utsname.release) + strlen(utsname.version) + 2; 110603831d35Sstevel sleep_flag = KM_SLEEP; 110703831d35Sstevel log_error = 0; 110803831d35Sstevel break; 110903831d35Sstevel case PLAT_ECC_DIMM_SID_MESSAGE: 111003831d35Sstevel msg_subtype = INFO_MBOX_ECC; 111103831d35Sstevel msg_size = sizeof (plat_dimm_sid_request_data_t); 111203831d35Sstevel sleep_flag = KM_SLEEP; 111303831d35Sstevel log_error = 0; 111403831d35Sstevel break; 111503831d35Sstevel default: 111603831d35Sstevel return (EINVAL); 111703831d35Sstevel } 111803831d35Sstevel 111903831d35Sstevel msgp = (sbbc_ecc_mbox_t *)kmem_zalloc(sizeof (sbbc_ecc_mbox_t), 112003831d35Sstevel sleep_flag); 112103831d35Sstevel if (msgp == NULL) { 112203831d35Sstevel cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: " 112303831d35Sstevel "unable to allocate sbbc_ecc_mbox"); 112403831d35Sstevel return (ENOMEM); 112503831d35Sstevel } 112603831d35Sstevel 112703831d35Sstevel msgp->ecc_log_error = log_error; 112803831d35Sstevel 112903831d35Sstevel msgp->ecc_req.msg_type.type = INFO_MBOX; 113003831d35Sstevel msgp->ecc_req.msg_type.sub_type = msg_subtype; 113103831d35Sstevel msgp->ecc_req.msg_status = 0; 113203831d35Sstevel msgp->ecc_req.msg_len = (int)msg_size; 113303831d35Sstevel msgp->ecc_req.msg_bytes = 0; 113403831d35Sstevel msgp->ecc_req.msg_buf = (caddr_t)kmem_zalloc(msg_size, sleep_flag); 113503831d35Sstevel msgp->ecc_req.msg_data[0] = 0; 113603831d35Sstevel msgp->ecc_req.msg_data[1] = 0; 113703831d35Sstevel 113803831d35Sstevel if (msgp->ecc_req.msg_buf == NULL) { 113903831d35Sstevel cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: " 114003831d35Sstevel "unable to allocate request msg_buf"); 114103831d35Sstevel kmem_free((void *)msgp, sizeof (sbbc_ecc_mbox_t)); 114203831d35Sstevel return (ENOMEM); 114303831d35Sstevel } 114403831d35Sstevel bcopy(datap, (void *)msgp->ecc_req.msg_buf, msg_size); 114503831d35Sstevel 114603831d35Sstevel /* 114703831d35Sstevel * initialize the response back from the SC 114803831d35Sstevel */ 114903831d35Sstevel msgp->ecc_resp.msg_type.type = INFO_MBOX; 115003831d35Sstevel msgp->ecc_resp.msg_type.sub_type = msg_subtype; 115103831d35Sstevel msgp->ecc_resp.msg_status = 0; 115203831d35Sstevel msgp->ecc_resp.msg_len = 0; 115303831d35Sstevel msgp->ecc_resp.msg_bytes = 0; 115403831d35Sstevel msgp->ecc_resp.msg_buf = NULL; 115503831d35Sstevel msgp->ecc_resp.msg_data[0] = 0; 115603831d35Sstevel msgp->ecc_resp.msg_data[1] = 0; 115703831d35Sstevel 115803831d35Sstevel switch (msg_type) { 115903831d35Sstevel case PLAT_ECC_ERROR_MESSAGE: 116003831d35Sstevel case PLAT_ECC_ERROR2_MESSAGE: 116103831d35Sstevel /* 116203831d35Sstevel * For Error Messages, we go through a taskq. 116303831d35Sstevel * Queue up the message for processing 116403831d35Sstevel */ 116503831d35Sstevel (*sg_ecc_taskq_func)(msgp); 116603831d35Sstevel return (0); 116703831d35Sstevel 116803831d35Sstevel case PLAT_ECC_CAPABILITY_MESSAGE: 116903831d35Sstevel /* 117003831d35Sstevel * For indictment and capability messages, we've already gone 117103831d35Sstevel * through the taskq, so we can call the mailbox routine 117203831d35Sstevel * directly. Find the symbol for the routine that sends 117303831d35Sstevel * the mailbox msg 117403831d35Sstevel */ 117503831d35Sstevel msgp->ecc_resp.msg_len = (int)msg_size; 117603831d35Sstevel msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(msg_size, 117703831d35Sstevel sleep_flag); 117803831d35Sstevel /* FALLTHRU */ 117903831d35Sstevel 118003831d35Sstevel case PLAT_ECC_INDICTMENT_MESSAGE: 118103831d35Sstevel case PLAT_ECC_INDICTMENT2_MESSAGE: 118203831d35Sstevel return ((*sg_ecc_mbox_func)(msgp)); 118303831d35Sstevel 118403831d35Sstevel case PLAT_ECC_DIMM_SID_MESSAGE: 118503831d35Sstevel msgp->ecc_resp.msg_len = sizeof (plat_dimm_sid_board_data_t); 118603831d35Sstevel msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc( 118703831d35Sstevel sizeof (plat_dimm_sid_board_data_t), sleep_flag); 118803831d35Sstevel return ((*sg_ecc_mbox_func)(msgp)); 118903831d35Sstevel 119003831d35Sstevel default: 119103831d35Sstevel ASSERT(0); 119203831d35Sstevel return (EINVAL); 119303831d35Sstevel } 119403831d35Sstevel } 119503831d35Sstevel 119603831d35Sstevel /* 119703831d35Sstevel * m is redundant on serengeti as the multiplier is always 4 119803831d35Sstevel */ 119903831d35Sstevel /*ARGSUSED*/ 120003831d35Sstevel int 120103831d35Sstevel plat_make_fru_cpuid(int sb, int m, int proc) 120203831d35Sstevel { 120303831d35Sstevel return (MAKE_CPUID(sb, proc)); 120403831d35Sstevel } 120503831d35Sstevel 120603831d35Sstevel /* 120703831d35Sstevel * board number for a given proc 120803831d35Sstevel */ 120903831d35Sstevel int 121003831d35Sstevel plat_make_fru_boardnum(int proc) 121103831d35Sstevel { 121203831d35Sstevel return (SG_CPU_BD_PORTID_TO_BD_NUM(proc)); 121303831d35Sstevel } 121403831d35Sstevel 121503831d35Sstevel static 121603831d35Sstevel void 121703831d35Sstevel cpu_sgn_update(ushort_t sig, uchar_t state, uchar_t sub_state, int cpuid) 121803831d35Sstevel { 121903831d35Sstevel uint32_t signature = CPU_SIG_BLD(sig, state, sub_state); 122003831d35Sstevel sig_state_t current_sgn; 122103831d35Sstevel int i; 122203831d35Sstevel 122303831d35Sstevel if (iosram_write_ptr == NULL) { 122403831d35Sstevel /* 122503831d35Sstevel * If the IOSRAM write pointer isn't set, we won't be able 122603831d35Sstevel * to write signatures to ANYTHING, so we may as well just 122703831d35Sstevel * write out an error message (if desired) and exit this 122803831d35Sstevel * routine now... 122903831d35Sstevel */ 123003831d35Sstevel DCMNERR(CE_WARN, 123103831d35Sstevel "cpu_sgn_update: iosram_write() not found;" 123203831d35Sstevel " cannot write signature 0x%x for CPU(s) or domain\n", 123303831d35Sstevel signature); 123403831d35Sstevel return; 123503831d35Sstevel } 123603831d35Sstevel 123703831d35Sstevel 123803831d35Sstevel /* 123903831d35Sstevel * Differentiate a panic reboot from a non-panic reboot in the 124003831d35Sstevel * setting of the substate of the signature. 124103831d35Sstevel * 124203831d35Sstevel * If the new substate is REBOOT and we're rebooting due to a panic, 124303831d35Sstevel * then set the new substate to a special value indicating a panic 124403831d35Sstevel * reboot, SIGSUBST_PANIC_REBOOT. 124503831d35Sstevel * 124603831d35Sstevel * A panic reboot is detected by a current (previous) domain signature 124703831d35Sstevel * state of SIGST_EXIT, and a new signature substate of SIGSUBST_REBOOT. 124803831d35Sstevel * The domain signature state SIGST_EXIT is used as the panic flow 124903831d35Sstevel * progresses. 125003831d35Sstevel * 125103831d35Sstevel * At the end of the panic flow, the reboot occurs but we should now 125203831d35Sstevel * one that was involuntary, something that may be quite useful to know 125303831d35Sstevel * at OBP level. 125403831d35Sstevel */ 125503831d35Sstevel if (sub_state == SIGSUBST_REBOOT) { 125603831d35Sstevel if (iosram_read_ptr == NULL) { 125703831d35Sstevel DCMNERR(CE_WARN, 125803831d35Sstevel "cpu_sgn_update: iosram_read() not found;" 125903831d35Sstevel " could not check current domain signature\n"); 126003831d35Sstevel } else { 126103831d35Sstevel (void) (*iosram_read_ptr)(SBBC_SIGBLCK_KEY, 126203831d35Sstevel SG_SGNBLK_DOMAINSIG_OFFSET, 126303831d35Sstevel (char *)¤t_sgn, sizeof (current_sgn)); 126403831d35Sstevel if (current_sgn.state_t.state == SIGST_EXIT) 126503831d35Sstevel signature = CPU_SIG_BLD(sig, state, 126603831d35Sstevel SIGSUBST_PANIC_REBOOT); 126703831d35Sstevel } 126803831d35Sstevel } 126903831d35Sstevel 127003831d35Sstevel /* 127103831d35Sstevel * cpuid == -1 indicates that the operation applies to all cpus. 127203831d35Sstevel */ 127303831d35Sstevel if (cpuid >= 0) { 127403831d35Sstevel (void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY, 127503831d35Sstevel SG_SGNBLK_CPUSIG_OFFSET(cpuid), (char *)&signature, 127603831d35Sstevel sizeof (signature)); 127703831d35Sstevel } else { 127803831d35Sstevel for (i = 0; i < NCPU; i++) { 127903831d35Sstevel if (cpu[i] == NULL || !(cpu[i]->cpu_flags & 128003831d35Sstevel (CPU_EXISTS|CPU_QUIESCED))) { 128103831d35Sstevel continue; 128203831d35Sstevel } 128303831d35Sstevel (void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY, 128403831d35Sstevel SG_SGNBLK_CPUSIG_OFFSET(i), (char *)&signature, 128503831d35Sstevel sizeof (signature)); 128603831d35Sstevel } 128703831d35Sstevel } 128803831d35Sstevel 128903831d35Sstevel if (state == SIGST_OFFLINE || state == SIGST_DETACHED) { 129003831d35Sstevel return; 129103831d35Sstevel } 129203831d35Sstevel 129303831d35Sstevel (void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY, 129403831d35Sstevel SG_SGNBLK_DOMAINSIG_OFFSET, (char *)&signature, 129503831d35Sstevel sizeof (signature)); 129603831d35Sstevel } 129703831d35Sstevel 129803831d35Sstevel void 129903831d35Sstevel startup_platform(void) 130003831d35Sstevel { 1301575a7426Spt157919 /* set per-platform constants for mutex backoff */ 1302575a7426Spt157919 mutex_backoff_base = 1; 1303575a7426Spt157919 mutex_cap_factor = 32; 130403831d35Sstevel } 130503831d35Sstevel 130603831d35Sstevel /* 130703831d35Sstevel * A routine to convert a number (represented as a string) to 130803831d35Sstevel * the integer value it represents. 130903831d35Sstevel */ 131003831d35Sstevel 131103831d35Sstevel static int 131203831d35Sstevel isdigit(int ch) 131303831d35Sstevel { 131403831d35Sstevel return (ch >= '0' && ch <= '9'); 131503831d35Sstevel } 131603831d35Sstevel 131703831d35Sstevel #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') 131803831d35Sstevel 131903831d35Sstevel static int 132003831d35Sstevel strtoi(char *p, char **pos) 132103831d35Sstevel { 132203831d35Sstevel int n; 132303831d35Sstevel int c, neg = 0; 132403831d35Sstevel 132503831d35Sstevel if (!isdigit(c = *p)) { 132603831d35Sstevel while (isspace(c)) 132703831d35Sstevel c = *++p; 132803831d35Sstevel switch (c) { 132903831d35Sstevel case '-': 133003831d35Sstevel neg++; 133103831d35Sstevel /* FALLTHROUGH */ 133203831d35Sstevel case '+': 133303831d35Sstevel c = *++p; 133403831d35Sstevel } 133503831d35Sstevel if (!isdigit(c)) { 133603831d35Sstevel if (pos != NULL) 133703831d35Sstevel *pos = p; 133803831d35Sstevel return (0); 133903831d35Sstevel } 134003831d35Sstevel } 134103831d35Sstevel for (n = '0' - c; isdigit(c = *++p); ) { 134203831d35Sstevel n *= 10; /* two steps to avoid unnecessary overflow */ 134303831d35Sstevel n += '0' - c; /* accum neg to avoid surprises at MAX */ 134403831d35Sstevel } 134503831d35Sstevel if (pos != NULL) 134603831d35Sstevel *pos = p; 134703831d35Sstevel return (neg ? n : -n); 134803831d35Sstevel } 134903831d35Sstevel 135003831d35Sstevel /* 135103831d35Sstevel * Get the three parts of the Serengeti PROM version. 135203831d35Sstevel * Used for feature readiness tests. 135303831d35Sstevel * 135403831d35Sstevel * Return 0 if version extracted successfully, -1 otherwise. 135503831d35Sstevel */ 135603831d35Sstevel 135703831d35Sstevel int 135803831d35Sstevel sg_get_prom_version(int *sysp, int *intfp, int *bldp) 135903831d35Sstevel { 136003831d35Sstevel int plen; 136103831d35Sstevel char vers[512]; 136203831d35Sstevel static pnode_t node; 136303831d35Sstevel static char version[] = "version"; 136403831d35Sstevel char *verp, *ep; 136503831d35Sstevel 136603831d35Sstevel node = prom_finddevice("/openprom"); 136703831d35Sstevel if (node == OBP_BADNODE) 136803831d35Sstevel return (-1); 136903831d35Sstevel 137003831d35Sstevel plen = prom_getproplen(node, version); 137103831d35Sstevel if (plen <= 0 || plen >= sizeof (vers)) 137203831d35Sstevel return (-1); 137303831d35Sstevel (void) prom_getprop(node, version, vers); 137403831d35Sstevel vers[plen] = '\0'; 137503831d35Sstevel 137603831d35Sstevel /* Make sure it's an OBP flashprom */ 137703831d35Sstevel if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') { 137803831d35Sstevel cmn_err(CE_WARN, "sg_get_prom_version: " 137903831d35Sstevel "unknown <version> string in </openprom>\n"); 138003831d35Sstevel return (-1); 138103831d35Sstevel } 138203831d35Sstevel verp = &vers[4]; 138303831d35Sstevel 138403831d35Sstevel *sysp = strtoi(verp, &ep); 138503831d35Sstevel if (ep == verp || *ep != '.') 138603831d35Sstevel return (-1); 138703831d35Sstevel verp = ep + 1; 138803831d35Sstevel 138903831d35Sstevel *intfp = strtoi(verp, &ep); 139003831d35Sstevel if (ep == verp || *ep != '.') 139103831d35Sstevel return (-1); 139203831d35Sstevel verp = ep + 1; 139303831d35Sstevel 139403831d35Sstevel *bldp = strtoi(verp, &ep); 139503831d35Sstevel if (ep == verp || (*ep != '\0' && !isspace(*ep))) 139603831d35Sstevel return (-1); 139703831d35Sstevel return (0); 139803831d35Sstevel } 139903831d35Sstevel 140003831d35Sstevel /* 140103831d35Sstevel * Return 0 if system board Dynamic Reconfiguration 140203831d35Sstevel * is supported by the firmware, -1 otherwise. 140303831d35Sstevel */ 140403831d35Sstevel int 140503831d35Sstevel sg_prom_sb_dr_check(void) 140603831d35Sstevel { 140703831d35Sstevel static int prom_res = 1; 140803831d35Sstevel 140903831d35Sstevel if (prom_res == 1) { 141003831d35Sstevel int sys, intf, bld; 141103831d35Sstevel int rv; 141203831d35Sstevel 141303831d35Sstevel rv = sg_get_prom_version(&sys, &intf, &bld); 141403831d35Sstevel if (rv == 0 && sys == 5 && 141503831d35Sstevel (intf >= 12 || (intf == 11 && bld >= 200))) { 141603831d35Sstevel prom_res = 0; 141703831d35Sstevel } else { 141803831d35Sstevel prom_res = -1; 141903831d35Sstevel } 142003831d35Sstevel } 142103831d35Sstevel return (prom_res); 142203831d35Sstevel } 142303831d35Sstevel 142403831d35Sstevel /* 142503831d35Sstevel * Return 0 if cPCI Dynamic Reconfiguration 142603831d35Sstevel * is supported by the firmware, -1 otherwise. 142703831d35Sstevel */ 142803831d35Sstevel int 142903831d35Sstevel sg_prom_cpci_dr_check(void) 143003831d35Sstevel { 143103831d35Sstevel /* 143203831d35Sstevel * The version check is currently the same as for 143303831d35Sstevel * system boards. Since the two DR sub-systems are 143403831d35Sstevel * independent, this could change. 143503831d35Sstevel */ 143603831d35Sstevel return (sg_prom_sb_dr_check()); 143703831d35Sstevel } 143803831d35Sstevel 143903831d35Sstevel /* 144003831d35Sstevel * KDI functions - used by the in-situ kernel debugger (kmdb) to perform 144103831d35Sstevel * platform-specific operations. These functions execute when the world is 144203831d35Sstevel * stopped, and as such cannot make any blocking calls, hold locks, etc. 144303831d35Sstevel * promif functions are a special case, and may be used. 144403831d35Sstevel */ 144503831d35Sstevel 144603831d35Sstevel /* 144703831d35Sstevel * Our implementation of this KDI op updates the CPU signature in the system 144803831d35Sstevel * controller. Note that we set the signature to OBP_SIG, rather than DBG_SIG. 144903831d35Sstevel * The Forth words we execute will, among other things, transform our OBP_SIG 145003831d35Sstevel * into DBG_SIG. They won't function properly if we try to use DBG_SIG. 145103831d35Sstevel */ 145203831d35Sstevel static void 145303831d35Sstevel sg_system_claim(void) 145403831d35Sstevel { 1455d3d50737SRafael Vanoni lbolt_debug_entry(); 1456d3d50737SRafael Vanoni 145703831d35Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0); 145803831d35Sstevel } 145903831d35Sstevel 146003831d35Sstevel static void 146103831d35Sstevel sg_system_release(void) 146203831d35Sstevel { 146303831d35Sstevel prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0); 1464d3d50737SRafael Vanoni 1465d3d50737SRafael Vanoni lbolt_debug_return(); 146603831d35Sstevel } 146703831d35Sstevel 146803831d35Sstevel static void 146903831d35Sstevel sg_console_claim(void) 147003831d35Sstevel { 1471*07d06da5SSurya Prakki (void) prom_serengeti_set_console_input(SGCN_OBP_STR); 147203831d35Sstevel } 147303831d35Sstevel 147403831d35Sstevel static void 147503831d35Sstevel sg_console_release(void) 147603831d35Sstevel { 1477*07d06da5SSurya Prakki (void) prom_serengeti_set_console_input(SGCN_CLNT_STR); 147803831d35Sstevel } 147903831d35Sstevel 148003831d35Sstevel void 148103831d35Sstevel plat_kdi_init(kdi_t *kdi) 148203831d35Sstevel { 148303831d35Sstevel kdi->pkdi_system_claim = sg_system_claim; 148403831d35Sstevel kdi->pkdi_system_release = sg_system_release; 148503831d35Sstevel kdi->pkdi_console_claim = sg_console_claim; 148603831d35Sstevel kdi->pkdi_console_release = sg_console_release; 148703831d35Sstevel } 1488