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