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