xref: /onnv-gate/usr/src/uts/intel/io/mc-amd/mcamd_subr.c (revision 10942:eaa343de0d06)
17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM  * CDDL HEADER START
37532SSean.Ye@Sun.COM  *
47532SSean.Ye@Sun.COM  * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM  * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM  * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM  *
87532SSean.Ye@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM  * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM  * and limitations under the License.
127532SSean.Ye@Sun.COM  *
137532SSean.Ye@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM  *
197532SSean.Ye@Sun.COM  * CDDL HEADER END
207532SSean.Ye@Sun.COM  */
217532SSean.Ye@Sun.COM 
227532SSean.Ye@Sun.COM /*
23*10942STom.Pothier@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247532SSean.Ye@Sun.COM  * Use is subject to license terms.
257532SSean.Ye@Sun.COM  */
267532SSean.Ye@Sun.COM 
277532SSean.Ye@Sun.COM /*
287532SSean.Ye@Sun.COM  * Stub routines used to link in files from $SRC/common/mc
297532SSean.Ye@Sun.COM  */
307532SSean.Ye@Sun.COM 
317532SSean.Ye@Sun.COM #include <sys/types.h>
327532SSean.Ye@Sun.COM #include <sys/cmn_err.h>
337532SSean.Ye@Sun.COM #include <sys/ddi.h>
347532SSean.Ye@Sun.COM #include <sys/sunddi.h>
357532SSean.Ye@Sun.COM #include <sys/varargs.h>
367532SSean.Ye@Sun.COM #include <sys/fm/util.h>
377532SSean.Ye@Sun.COM #include <sys/fm/cpu/AMD.h>
38*10942STom.Pothier@Sun.COM #include <sys/fm/smb/fmsmb.h>
397532SSean.Ye@Sun.COM #include <sys/fm/protocol.h>
407532SSean.Ye@Sun.COM #include <sys/mc.h>
41*10942STom.Pothier@Sun.COM #include <sys/smbios.h>
42*10942STom.Pothier@Sun.COM #include <sys/smbios_impl.h>
437532SSean.Ye@Sun.COM 
447532SSean.Ye@Sun.COM #include <mcamd.h>
457532SSean.Ye@Sun.COM #include <mcamd_off.h>
467532SSean.Ye@Sun.COM 
477532SSean.Ye@Sun.COM int mcamd_debug = 0; /* see mcamd_api.h for MCAMD_DBG_* values */
487532SSean.Ye@Sun.COM 
49*10942STom.Pothier@Sun.COM extern int x86gentopo_legacy;
50*10942STom.Pothier@Sun.COM 
517532SSean.Ye@Sun.COM struct mc_offmap {
527532SSean.Ye@Sun.COM 	int mcom_code;
537532SSean.Ye@Sun.COM 	uint_t mcom_offset;
547532SSean.Ye@Sun.COM };
557532SSean.Ye@Sun.COM 
567532SSean.Ye@Sun.COM static uint_t
nodetype(mcamd_node_t * node)577532SSean.Ye@Sun.COM nodetype(mcamd_node_t *node)
587532SSean.Ye@Sun.COM {
597532SSean.Ye@Sun.COM 	mc_hdr_t *mch = (mc_hdr_t *)node;
607532SSean.Ye@Sun.COM 	return (mch->mch_type);
617532SSean.Ye@Sun.COM }
627532SSean.Ye@Sun.COM 
637532SSean.Ye@Sun.COM static void *
node2type(mcamd_node_t * node,int type)647532SSean.Ye@Sun.COM node2type(mcamd_node_t *node, int type)
657532SSean.Ye@Sun.COM {
667532SSean.Ye@Sun.COM 	mc_hdr_t *mch = (mc_hdr_t *)node;
677532SSean.Ye@Sun.COM 	ASSERT(mch->mch_type == type);
687532SSean.Ye@Sun.COM 	return (mch);
697532SSean.Ye@Sun.COM }
707532SSean.Ye@Sun.COM 
717532SSean.Ye@Sun.COM /*
727532SSean.Ye@Sun.COM  * Iterate over all memory controllers.
737532SSean.Ye@Sun.COM  */
747532SSean.Ye@Sun.COM /*ARGSUSED*/
757532SSean.Ye@Sun.COM mcamd_node_t *
mcamd_mc_next(mcamd_hdl_t * hdl,mcamd_node_t * root,mcamd_node_t * last)767532SSean.Ye@Sun.COM mcamd_mc_next(mcamd_hdl_t *hdl, mcamd_node_t *root, mcamd_node_t *last)
777532SSean.Ye@Sun.COM {
787532SSean.Ye@Sun.COM 	mc_t *mc;
797532SSean.Ye@Sun.COM 
807532SSean.Ye@Sun.COM 	ASSERT(RW_LOCK_HELD(&mc_lock));
817532SSean.Ye@Sun.COM 
827532SSean.Ye@Sun.COM 	if (last == NULL)
837532SSean.Ye@Sun.COM 		return ((mcamd_node_t *)mc_list);
847532SSean.Ye@Sun.COM 
857532SSean.Ye@Sun.COM 	mc = node2type(last, MC_NT_MC);
867532SSean.Ye@Sun.COM 
877532SSean.Ye@Sun.COM 	return ((mcamd_node_t *)mc->mc_next);
887532SSean.Ye@Sun.COM }
897532SSean.Ye@Sun.COM 
907532SSean.Ye@Sun.COM /*
917532SSean.Ye@Sun.COM  * Iterate over all chip-selects of a MC or all chip-selects of a DIMM
927532SSean.Ye@Sun.COM  * depending on the node type of 'node'.  In the DIMM case we do not
937532SSean.Ye@Sun.COM  * have a linked list of associated chip-selects but an array of pointer
947532SSean.Ye@Sun.COM  * to them.
957532SSean.Ye@Sun.COM  */
967532SSean.Ye@Sun.COM /*ARGSUSED*/
977532SSean.Ye@Sun.COM mcamd_node_t *
mcamd_cs_next(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_node_t * last)987532SSean.Ye@Sun.COM mcamd_cs_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
997532SSean.Ye@Sun.COM {
1007532SSean.Ye@Sun.COM 	uint_t nt = nodetype(node);
1017532SSean.Ye@Sun.COM 	mc_t *mc;
1027532SSean.Ye@Sun.COM 	mc_cs_t *mccs;
1037532SSean.Ye@Sun.COM 	mc_dimm_t *mcd;
1047532SSean.Ye@Sun.COM 	int i;
1057532SSean.Ye@Sun.COM 	void *retval;
1067532SSean.Ye@Sun.COM 
1077532SSean.Ye@Sun.COM 	ASSERT(nt == MC_NT_MC || nt == MC_NT_DIMM);
1087532SSean.Ye@Sun.COM 
1097532SSean.Ye@Sun.COM 	if (last == NULL) {
1107532SSean.Ye@Sun.COM 		switch (nt) {
1117532SSean.Ye@Sun.COM 		case MC_NT_MC:
1127532SSean.Ye@Sun.COM 			mc = node2type(node, MC_NT_MC);
1137532SSean.Ye@Sun.COM 			retval = mc->mc_cslist;
1147532SSean.Ye@Sun.COM 			break;
1157532SSean.Ye@Sun.COM 		case MC_NT_DIMM:
1167532SSean.Ye@Sun.COM 			mcd = node2type(node, MC_NT_DIMM);
1177532SSean.Ye@Sun.COM 			retval = mcd->mcd_cs[0];
1187532SSean.Ye@Sun.COM 			break;
1197532SSean.Ye@Sun.COM 		}
1207532SSean.Ye@Sun.COM 	} else {
1217532SSean.Ye@Sun.COM 		mccs = node2type(last, MC_NT_CS);
1227532SSean.Ye@Sun.COM 
1237532SSean.Ye@Sun.COM 		switch (nt) {
1247532SSean.Ye@Sun.COM 		case MC_NT_MC:
1257532SSean.Ye@Sun.COM 			retval = mccs->mccs_next;
1267532SSean.Ye@Sun.COM 			break;
1277532SSean.Ye@Sun.COM 		case MC_NT_DIMM:
1287532SSean.Ye@Sun.COM 			mcd = node2type(node, MC_NT_DIMM);
1297532SSean.Ye@Sun.COM 			for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
1307532SSean.Ye@Sun.COM 				if (mcd->mcd_cs[i] == mccs)
1317532SSean.Ye@Sun.COM 					break;
1327532SSean.Ye@Sun.COM 			}
1337532SSean.Ye@Sun.COM 			if (i == MC_CHIP_DIMMRANKMAX)
1347532SSean.Ye@Sun.COM 				cmn_err(CE_PANIC, "Bad last value for "
1357532SSean.Ye@Sun.COM 				    "mcamd_cs_next");
1367532SSean.Ye@Sun.COM 
1377532SSean.Ye@Sun.COM 			if (i == MC_CHIP_DIMMRANKMAX - 1)
1387532SSean.Ye@Sun.COM 				retval = NULL;
1397532SSean.Ye@Sun.COM 			else
1407532SSean.Ye@Sun.COM 				retval = mcd->mcd_cs[i + 1];
1417532SSean.Ye@Sun.COM 			break;
1427532SSean.Ye@Sun.COM 		}
1437532SSean.Ye@Sun.COM 	}
1447532SSean.Ye@Sun.COM 
1457532SSean.Ye@Sun.COM 	return ((mcamd_node_t *)retval);
1467532SSean.Ye@Sun.COM }
1477532SSean.Ye@Sun.COM 
1487532SSean.Ye@Sun.COM /*
1497532SSean.Ye@Sun.COM  * Iterate over all DIMMs of an MC or all DIMMs of a chip-select depending
1507532SSean.Ye@Sun.COM  * on the node type of 'node'.  In the chip-select case we do not have
1517532SSean.Ye@Sun.COM  * a linked list of associated DIMMs but an array of pointers to them.
1527532SSean.Ye@Sun.COM  */
1537532SSean.Ye@Sun.COM /*ARGSUSED*/
1547532SSean.Ye@Sun.COM mcamd_node_t *
mcamd_dimm_next(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_node_t * last)1557532SSean.Ye@Sun.COM mcamd_dimm_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
1567532SSean.Ye@Sun.COM {
1577532SSean.Ye@Sun.COM 	uint_t nt = nodetype(node);
1587532SSean.Ye@Sun.COM 	mc_t *mc;
1597532SSean.Ye@Sun.COM 	mc_cs_t *mccs;
1607532SSean.Ye@Sun.COM 	mc_dimm_t *mcd;
1617532SSean.Ye@Sun.COM 	int i;
1627532SSean.Ye@Sun.COM 	void *retval;
1637532SSean.Ye@Sun.COM 
1647532SSean.Ye@Sun.COM 	ASSERT(nt == MC_NT_MC || nt == MC_NT_CS);
1657532SSean.Ye@Sun.COM 
1667532SSean.Ye@Sun.COM 	if (last == NULL) {
1677532SSean.Ye@Sun.COM 		switch (nt) {
1687532SSean.Ye@Sun.COM 		case MC_NT_MC:
1697532SSean.Ye@Sun.COM 			mc = node2type(node, MC_NT_MC);
1707532SSean.Ye@Sun.COM 			retval =  mc->mc_dimmlist;
1717532SSean.Ye@Sun.COM 			break;
1727532SSean.Ye@Sun.COM 		case MC_NT_CS:
1737532SSean.Ye@Sun.COM 			mccs = node2type(node, MC_NT_CS);
1747532SSean.Ye@Sun.COM 			retval = mccs->mccs_dimm[0];
1757532SSean.Ye@Sun.COM 			break;
1767532SSean.Ye@Sun.COM 		}
1777532SSean.Ye@Sun.COM 	} else {
1787532SSean.Ye@Sun.COM 		mcd = node2type(last, MC_NT_DIMM);
1797532SSean.Ye@Sun.COM 
1807532SSean.Ye@Sun.COM 		switch (nt) {
1817532SSean.Ye@Sun.COM 		case MC_NT_MC:
1827532SSean.Ye@Sun.COM 			retval = mcd->mcd_next;
1837532SSean.Ye@Sun.COM 			break;
1847532SSean.Ye@Sun.COM 		case MC_NT_CS:
1857532SSean.Ye@Sun.COM 			mccs = node2type(node, MC_NT_CS);
1867532SSean.Ye@Sun.COM 			for (i = 0; i < MC_CHIP_DIMMPERCS; i++) {
1877532SSean.Ye@Sun.COM 				if (mccs->mccs_dimm[i] == mcd)
1887532SSean.Ye@Sun.COM 					break;
1897532SSean.Ye@Sun.COM 			}
1907532SSean.Ye@Sun.COM 			if (i == MC_CHIP_DIMMPERCS)
1917532SSean.Ye@Sun.COM 				cmn_err(CE_PANIC, "Bad last value for "
1927532SSean.Ye@Sun.COM 				    "mcamd_dimm_next");
1937532SSean.Ye@Sun.COM 
1947532SSean.Ye@Sun.COM 			if (i == MC_CHIP_DIMMPERCS - 1)
1957532SSean.Ye@Sun.COM 				retval = NULL;
1967532SSean.Ye@Sun.COM 			else
1977532SSean.Ye@Sun.COM 				retval = mccs->mccs_dimm[i + 1];
1987532SSean.Ye@Sun.COM 			break;
1997532SSean.Ye@Sun.COM 		}
2007532SSean.Ye@Sun.COM 	}
2017532SSean.Ye@Sun.COM 
2027532SSean.Ye@Sun.COM 	return ((mcamd_node_t *)retval);
2037532SSean.Ye@Sun.COM }
2047532SSean.Ye@Sun.COM 
2057532SSean.Ye@Sun.COM /*ARGSUSED*/
2067532SSean.Ye@Sun.COM mcamd_node_t *
mcamd_cs_mc(mcamd_hdl_t * hdl,mcamd_node_t * csnode)2077532SSean.Ye@Sun.COM mcamd_cs_mc(mcamd_hdl_t *hdl, mcamd_node_t *csnode)
2087532SSean.Ye@Sun.COM {
2097532SSean.Ye@Sun.COM 	mc_cs_t *mccs = node2type(csnode, MC_NT_CS);
2107532SSean.Ye@Sun.COM 	return ((mcamd_node_t *)mccs->mccs_mc);
2117532SSean.Ye@Sun.COM }
2127532SSean.Ye@Sun.COM 
2137532SSean.Ye@Sun.COM /*ARGSUSED*/
2147532SSean.Ye@Sun.COM mcamd_node_t *
mcamd_dimm_mc(mcamd_hdl_t * hdl,mcamd_node_t * dnode)2157532SSean.Ye@Sun.COM mcamd_dimm_mc(mcamd_hdl_t *hdl, mcamd_node_t *dnode)
2167532SSean.Ye@Sun.COM {
2177532SSean.Ye@Sun.COM 	mc_dimm_t *mcd = node2type(dnode, MC_NT_DIMM);
2187532SSean.Ye@Sun.COM 	return ((mcamd_node_t *)mcd->mcd_mc);
2197532SSean.Ye@Sun.COM }
2207532SSean.Ye@Sun.COM 
2217532SSean.Ye@Sun.COM /*
2227532SSean.Ye@Sun.COM  * Node properties.  A property is accessed through a property number code;
2237532SSean.Ye@Sun.COM  * we search these tables for a match (choosing table from node type) and
2247532SSean.Ye@Sun.COM  * return the uint64_t property at the indicated offset into the node
2257532SSean.Ye@Sun.COM  * structure.  All properties must be of type uint64_t.  It is assumed that
2267532SSean.Ye@Sun.COM  * property lookup does not have to be super-fast - we search linearly
2277532SSean.Ye@Sun.COM  * down the (small) lists.
2287532SSean.Ye@Sun.COM  */
2297532SSean.Ye@Sun.COM static const struct mc_offmap mcamd_mc_offmap[] = {
2307532SSean.Ye@Sun.COM 	{ MCAMD_PROP_NUM, MCAMD_MC_OFF_NUM },
2317532SSean.Ye@Sun.COM 	{ MCAMD_PROP_REV, MCAMD_MC_OFF_REV },
2327532SSean.Ye@Sun.COM 	{ MCAMD_PROP_BASE_ADDR, MCAMD_MC_OFF_BASE_ADDR },
2337532SSean.Ye@Sun.COM 	{ MCAMD_PROP_LIM_ADDR, MCAMD_MC_OFF_LIM_ADDR },
2347532SSean.Ye@Sun.COM 	{ MCAMD_PROP_ILEN, MCAMD_MC_OFF_ILEN },
2357532SSean.Ye@Sun.COM 	{ MCAMD_PROP_ILSEL, MCAMD_MC_OFF_ILSEL },
2367532SSean.Ye@Sun.COM 	{ MCAMD_PROP_CSINTLVFCTR, MCAMD_MC_OFF_CSINTLVFCTR },
2377532SSean.Ye@Sun.COM 	{ MCAMD_PROP_DRAMHOLE_SIZE, MCAMD_MC_OFF_DRAMHOLE_SIZE },
2387532SSean.Ye@Sun.COM 	{ MCAMD_PROP_ACCESS_WIDTH, MCAMD_MC_OFF_ACCWIDTH },
2397532SSean.Ye@Sun.COM 	{ MCAMD_PROP_CSBANKMAPREG, MCAMD_MC_OFF_CSBANKMAPREG },
2407532SSean.Ye@Sun.COM 	{ MCAMD_PROP_BANKSWZL, MCAMD_MC_OFF_BNKSWZL },
2417532SSean.Ye@Sun.COM 	{ MCAMD_PROP_MOD64MUX, MCAMD_MC_OFF_MOD64MUX },
2427532SSean.Ye@Sun.COM 	{ MCAMD_PROP_SPARECS, MCAMD_MC_OFF_SPARECS },
2437532SSean.Ye@Sun.COM 	{ MCAMD_PROP_BADCS, MCAMD_MC_OFF_BADCS },
2447532SSean.Ye@Sun.COM };
2457532SSean.Ye@Sun.COM 
2467532SSean.Ye@Sun.COM static const struct mc_offmap mcamd_cs_offmap[] = {
2477532SSean.Ye@Sun.COM 	{ MCAMD_PROP_NUM, MCAMD_CS_OFF_NUM },
2487532SSean.Ye@Sun.COM 	{ MCAMD_PROP_BASE_ADDR, MCAMD_CS_OFF_BASE_ADDR },
2497532SSean.Ye@Sun.COM 	{ MCAMD_PROP_MASK, MCAMD_CS_OFF_MASK },
2507532SSean.Ye@Sun.COM 	{ MCAMD_PROP_SIZE, MCAMD_CS_OFF_SIZE },
2517532SSean.Ye@Sun.COM 	{ MCAMD_PROP_CSBE, MCAMD_CS_OFF_CSBE },
2527532SSean.Ye@Sun.COM 	{ MCAMD_PROP_SPARE, MCAMD_CS_OFF_SPARE },
2537532SSean.Ye@Sun.COM 	{ MCAMD_PROP_TESTFAIL, MCAMD_CS_OFF_TESTFAIL },
2547532SSean.Ye@Sun.COM 	{ MCAMD_PROP_CSDIMM1, MCAMD_CS_OFF_DIMMNUMS },
2557532SSean.Ye@Sun.COM 	{ MCAMD_PROP_CSDIMM2, MCAMD_CS_OFF_DIMMNUMS +
2567532SSean.Ye@Sun.COM 	    MCAMD_CS_OFF_DIMMNUMS_INCR },
2577532SSean.Ye@Sun.COM 	{ MCAMD_PROP_DIMMRANK, MCAMD_CS_OFF_DIMMRANK },
2587532SSean.Ye@Sun.COM };
2597532SSean.Ye@Sun.COM 
2607532SSean.Ye@Sun.COM static const struct mc_offmap mcamd_dimm_offmap[] = {
2617532SSean.Ye@Sun.COM 	{ MCAMD_PROP_NUM, MCAMD_DIMM_OFF_NUM },
2627532SSean.Ye@Sun.COM 	{ MCAMD_PROP_SIZE, MCAMD_DIMM_OFF_SIZE },
2637532SSean.Ye@Sun.COM };
2647532SSean.Ye@Sun.COM 
2657532SSean.Ye@Sun.COM struct nt_offmap {
2667532SSean.Ye@Sun.COM 	const struct mc_offmap *omp;
2677532SSean.Ye@Sun.COM 	int mapents;
2687532SSean.Ye@Sun.COM };
2697532SSean.Ye@Sun.COM 
2707532SSean.Ye@Sun.COM /*ARGSUSED*/
2717532SSean.Ye@Sun.COM static int
findoffset(mcamd_hdl_t * hdl,mcamd_node_t * node,struct nt_offmap * arr,int code,uint_t * offset)2727532SSean.Ye@Sun.COM findoffset(mcamd_hdl_t *hdl, mcamd_node_t *node, struct nt_offmap *arr,
2737532SSean.Ye@Sun.COM     int code, uint_t *offset)
2747532SSean.Ye@Sun.COM {
2757532SSean.Ye@Sun.COM 	int i;
2767532SSean.Ye@Sun.COM 	mc_hdr_t *mch = (mc_hdr_t *)node;
2777532SSean.Ye@Sun.COM 	int nt = mch->mch_type;
2787532SSean.Ye@Sun.COM 	const struct mc_offmap *omp;
2797532SSean.Ye@Sun.COM 
2807532SSean.Ye@Sun.COM 	if (nt > MC_NT_NTYPES || (omp = arr[nt].omp) == NULL)
2817532SSean.Ye@Sun.COM 		return (0);
2827532SSean.Ye@Sun.COM 
2837532SSean.Ye@Sun.COM 	for (i = 0; i < arr[nt].mapents; i++, omp++) {
2847532SSean.Ye@Sun.COM 		if (omp->mcom_code == code) {
2857532SSean.Ye@Sun.COM 			*offset = omp->mcom_offset;
2867532SSean.Ye@Sun.COM 			return (1);
2877532SSean.Ye@Sun.COM 		}
2887532SSean.Ye@Sun.COM 	}
2897532SSean.Ye@Sun.COM 
2907532SSean.Ye@Sun.COM 	return (0);
2917532SSean.Ye@Sun.COM }
2927532SSean.Ye@Sun.COM 
2937532SSean.Ye@Sun.COM /*ARGSUSED*/
2947532SSean.Ye@Sun.COM int
mcamd_get_numprop(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_propcode_t code,mcamd_prop_t * valp)2957532SSean.Ye@Sun.COM mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node,
2967532SSean.Ye@Sun.COM     mcamd_propcode_t code, mcamd_prop_t *valp)
2977532SSean.Ye@Sun.COM {
2987532SSean.Ye@Sun.COM 	int found;
2997532SSean.Ye@Sun.COM 	uint_t offset;
3007532SSean.Ye@Sun.COM 
3017532SSean.Ye@Sun.COM 	struct nt_offmap props[] = {
3027532SSean.Ye@Sun.COM 		{ mcamd_mc_offmap,	/* MC_NT_MC */
3037532SSean.Ye@Sun.COM 		    sizeof (mcamd_mc_offmap) / sizeof (struct mc_offmap) },
3047532SSean.Ye@Sun.COM 		{ mcamd_cs_offmap,	/* MC_NT_CS */
3057532SSean.Ye@Sun.COM 		    sizeof (mcamd_cs_offmap) / sizeof (struct mc_offmap) },
3067532SSean.Ye@Sun.COM 		{ mcamd_dimm_offmap,	/* MC_NT_DIMM */
3077532SSean.Ye@Sun.COM 		    sizeof (mcamd_dimm_offmap) / sizeof (struct mc_offmap) }
3087532SSean.Ye@Sun.COM 	};
3097532SSean.Ye@Sun.COM 
3107532SSean.Ye@Sun.COM 	found = findoffset(hdl, node, &props[0], code, &offset);
3117532SSean.Ye@Sun.COM 	ASSERT(found);
3127532SSean.Ye@Sun.COM 
3137532SSean.Ye@Sun.COM 	if (found)
3147532SSean.Ye@Sun.COM 		*valp = *(uint64_t *)((uintptr_t)node + offset);
3157532SSean.Ye@Sun.COM 
3167532SSean.Ye@Sun.COM 	return (found == 1);
3177532SSean.Ye@Sun.COM }
3187532SSean.Ye@Sun.COM 
3197532SSean.Ye@Sun.COM int
mcamd_get_numprops(mcamd_hdl_t * hdl,...)3207532SSean.Ye@Sun.COM mcamd_get_numprops(mcamd_hdl_t *hdl, ...)
3217532SSean.Ye@Sun.COM {
3227532SSean.Ye@Sun.COM 	va_list ap;
3237532SSean.Ye@Sun.COM 	mcamd_node_t *node;
3247532SSean.Ye@Sun.COM 	mcamd_propcode_t code;
3257532SSean.Ye@Sun.COM 	mcamd_prop_t *valp;
3267532SSean.Ye@Sun.COM 
3277532SSean.Ye@Sun.COM 	va_start(ap, hdl);
3287532SSean.Ye@Sun.COM 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
3297532SSean.Ye@Sun.COM 		code = va_arg(ap, mcamd_propcode_t);
3307532SSean.Ye@Sun.COM 		valp = va_arg(ap, mcamd_prop_t *);
3317532SSean.Ye@Sun.COM 		if (!mcamd_get_numprop(hdl, node, code, valp))
3327532SSean.Ye@Sun.COM 			return (0);
3337532SSean.Ye@Sun.COM 	}
3347532SSean.Ye@Sun.COM 	va_end(ap);
3357532SSean.Ye@Sun.COM 	return (1);
3367532SSean.Ye@Sun.COM }
3377532SSean.Ye@Sun.COM 
3387532SSean.Ye@Sun.COM static const struct mc_offmap mcreg_offmap[] = {
3397532SSean.Ye@Sun.COM 	{ MCAMD_REG_DRAMBASE, MCAMD_MC_OFF_DRAMBASE_REG },
3407532SSean.Ye@Sun.COM 	{ MCAMD_REG_DRAMLIMIT, MCAMD_MC_OFF_DRAMLIMIT_REG },
3417532SSean.Ye@Sun.COM 	{ MCAMD_REG_DRAMHOLE, MCAMD_MC_OFF_DRAMHOLE_REG },
3427532SSean.Ye@Sun.COM 	{ MCAMD_REG_DRAMCFGLO, MCAMD_MC_OFF_DRAMCFGLO_REG },
3437532SSean.Ye@Sun.COM 	{ MCAMD_REG_DRAMCFGHI, MCAMD_MC_OFF_DRAMCFGHI_REG },
3447532SSean.Ye@Sun.COM };
3457532SSean.Ye@Sun.COM 
3467532SSean.Ye@Sun.COM static const struct mc_offmap csreg_offmap[] = {
3477532SSean.Ye@Sun.COM 	{ MCAMD_REG_CSBASE, MCAMD_CS_OFF_CSBASE_REG },
3487532SSean.Ye@Sun.COM 	{ MCAMD_REG_CSMASK, MCAMD_CS_OFF_CSMASK_REG },
3497532SSean.Ye@Sun.COM };
3507532SSean.Ye@Sun.COM 
3517532SSean.Ye@Sun.COM /*ARGSUSED*/
3527532SSean.Ye@Sun.COM int
mcamd_get_cfgreg(struct mcamd_hdl * hdl,mcamd_node_t * node,mcamd_regcode_t code,uint32_t * valp)3537532SSean.Ye@Sun.COM mcamd_get_cfgreg(struct mcamd_hdl *hdl, mcamd_node_t *node,
3547532SSean.Ye@Sun.COM     mcamd_regcode_t code, uint32_t *valp)
3557532SSean.Ye@Sun.COM {
3567532SSean.Ye@Sun.COM 	int found;
3577532SSean.Ye@Sun.COM 	uint_t offset;
3587532SSean.Ye@Sun.COM 
3597532SSean.Ye@Sun.COM 	struct nt_offmap regs[] = {
3607532SSean.Ye@Sun.COM 		{ mcreg_offmap,	/* MC_NT_MC */
3617532SSean.Ye@Sun.COM 		    sizeof (mcreg_offmap) / sizeof (struct mc_offmap) },
3627532SSean.Ye@Sun.COM 		{ csreg_offmap,	/* MC_NT_CS */
3637532SSean.Ye@Sun.COM 		    sizeof (csreg_offmap) / sizeof (struct mc_offmap) },
3647532SSean.Ye@Sun.COM 		{ NULL, 0 }		/* MC_NT_DIMM */
3657532SSean.Ye@Sun.COM 	};
3667532SSean.Ye@Sun.COM 
3677532SSean.Ye@Sun.COM 	found = findoffset(hdl, node, &regs[0], code, &offset);
3687532SSean.Ye@Sun.COM 	ASSERT(found);
3697532SSean.Ye@Sun.COM 
3707532SSean.Ye@Sun.COM 	ASSERT(found);
3717532SSean.Ye@Sun.COM 	if (found)
3727532SSean.Ye@Sun.COM 		*valp = *(uint32_t *)((uintptr_t)node + offset);
3737532SSean.Ye@Sun.COM 
3747532SSean.Ye@Sun.COM 	return (found == 1);
3757532SSean.Ye@Sun.COM }
3767532SSean.Ye@Sun.COM 
3777532SSean.Ye@Sun.COM int
mcamd_get_cfgregs(mcamd_hdl_t * hdl,...)3787532SSean.Ye@Sun.COM mcamd_get_cfgregs(mcamd_hdl_t *hdl, ...)
3797532SSean.Ye@Sun.COM {
3807532SSean.Ye@Sun.COM 	va_list ap;
3817532SSean.Ye@Sun.COM 	mcamd_node_t *node;
3827532SSean.Ye@Sun.COM 	mcamd_regcode_t code;
3837532SSean.Ye@Sun.COM 	uint32_t *valp;
3847532SSean.Ye@Sun.COM 
3857532SSean.Ye@Sun.COM 	va_start(ap, hdl);
3867532SSean.Ye@Sun.COM 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
3877532SSean.Ye@Sun.COM 		code = va_arg(ap, mcamd_regcode_t);
3887532SSean.Ye@Sun.COM 		valp = va_arg(ap, uint32_t *);
3897532SSean.Ye@Sun.COM 		if (!mcamd_get_cfgreg(hdl, node, code, valp))
3907532SSean.Ye@Sun.COM 			return (0);
3917532SSean.Ye@Sun.COM 	}
3927532SSean.Ye@Sun.COM 	va_end(ap);
3937532SSean.Ye@Sun.COM 	return (1);
3947532SSean.Ye@Sun.COM }
3957532SSean.Ye@Sun.COM 
3967532SSean.Ye@Sun.COM 
3977532SSean.Ye@Sun.COM int
mcamd_errno(mcamd_hdl_t * mcamd)3987532SSean.Ye@Sun.COM mcamd_errno(mcamd_hdl_t *mcamd)
3997532SSean.Ye@Sun.COM {
4007532SSean.Ye@Sun.COM 	return (mcamd->mcamd_errno);
4017532SSean.Ye@Sun.COM }
4027532SSean.Ye@Sun.COM 
4037532SSean.Ye@Sun.COM int
mcamd_set_errno(mcamd_hdl_t * mcamd,int err)4047532SSean.Ye@Sun.COM mcamd_set_errno(mcamd_hdl_t *mcamd, int err)
4057532SSean.Ye@Sun.COM {
4067532SSean.Ye@Sun.COM 	mcamd->mcamd_errno = err;
4077532SSean.Ye@Sun.COM 	return (-1);
4087532SSean.Ye@Sun.COM }
4097532SSean.Ye@Sun.COM 
4107532SSean.Ye@Sun.COM void
mcamd_dprintf(mcamd_hdl_t * mcamd,int mask,const char * fmt,...)4117532SSean.Ye@Sun.COM mcamd_dprintf(mcamd_hdl_t *mcamd, int mask, const char *fmt, ...)
4127532SSean.Ye@Sun.COM {
4137532SSean.Ye@Sun.COM 	va_list ap;
4147532SSean.Ye@Sun.COM 
4157532SSean.Ye@Sun.COM 	if (!(mcamd->mcamd_debug & mask))
4167532SSean.Ye@Sun.COM 		return;
4177532SSean.Ye@Sun.COM 
4187532SSean.Ye@Sun.COM 	va_start(ap, fmt);
4197532SSean.Ye@Sun.COM 	vcmn_err(mask & MCAMD_DBG_ERR ? CE_WARN : CE_NOTE, fmt, ap);
4207532SSean.Ye@Sun.COM 	va_end(ap);
4217532SSean.Ye@Sun.COM }
4227532SSean.Ye@Sun.COM 
4237532SSean.Ye@Sun.COM void
mcamd_mkhdl(mcamd_hdl_t * hdl)4247532SSean.Ye@Sun.COM mcamd_mkhdl(mcamd_hdl_t *hdl)
4257532SSean.Ye@Sun.COM {
4267532SSean.Ye@Sun.COM 	hdl->mcamd_errno = 0;
4277532SSean.Ye@Sun.COM 	hdl->mcamd_debug = mcamd_debug;
4287532SSean.Ye@Sun.COM }
4297532SSean.Ye@Sun.COM 
4307532SSean.Ye@Sun.COM cmi_errno_t
mcamd_cmierr(int err,mcamd_hdl_t * hdl)4317532SSean.Ye@Sun.COM mcamd_cmierr(int err, mcamd_hdl_t *hdl)
4327532SSean.Ye@Sun.COM {
4337532SSean.Ye@Sun.COM 	if (err == 0)
4347532SSean.Ye@Sun.COM 		return (CMI_SUCCESS);
4357532SSean.Ye@Sun.COM 
4367532SSean.Ye@Sun.COM 	switch (mcamd_errno(hdl)) {
4377532SSean.Ye@Sun.COM 	case EMCAMD_SYNDINVALID:
4387532SSean.Ye@Sun.COM 		return (CMIERR_MC_SYNDROME);
4397532SSean.Ye@Sun.COM 
4407532SSean.Ye@Sun.COM 	case EMCAMD_TREEINVALID:
4417532SSean.Ye@Sun.COM 		return (CMIERR_MC_BADSTATE);
4427532SSean.Ye@Sun.COM 
4437532SSean.Ye@Sun.COM 	case EMCAMD_NOADDR:
4447532SSean.Ye@Sun.COM 		return (CMIERR_MC_NOADDR);
4457532SSean.Ye@Sun.COM 
4467532SSean.Ye@Sun.COM 	case EMCAMD_INSUFF_RES:
4477532SSean.Ye@Sun.COM 		return (CMIERR_MC_ADDRBITS);
4487532SSean.Ye@Sun.COM 
4497532SSean.Ye@Sun.COM 	default:
4507532SSean.Ye@Sun.COM 		return (CMIERR_UNKNOWN);
4517532SSean.Ye@Sun.COM 	}
4527532SSean.Ye@Sun.COM 
4537532SSean.Ye@Sun.COM }
4547532SSean.Ye@Sun.COM 
4557532SSean.Ye@Sun.COM /*ARGSUSED*/
4567532SSean.Ye@Sun.COM cmi_errno_t
mcamd_patounum_wrap(void * arg,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)4577532SSean.Ye@Sun.COM mcamd_patounum_wrap(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
4587532SSean.Ye@Sun.COM     uint32_t synd, int syndtype, mc_unum_t *unump)
4597532SSean.Ye@Sun.COM {
4607532SSean.Ye@Sun.COM 	mcamd_hdl_t mcamd;
4617532SSean.Ye@Sun.COM 	int rc;
4627532SSean.Ye@Sun.COM 
4637532SSean.Ye@Sun.COM 	mcamd_mkhdl(&mcamd);
4647532SSean.Ye@Sun.COM 
4657532SSean.Ye@Sun.COM 	rw_enter(&mc_lock, RW_READER);
4667532SSean.Ye@Sun.COM 
4677532SSean.Ye@Sun.COM 	rc = mcamd_patounum(&mcamd, (mcamd_node_t *)mc_list, pa,
4687532SSean.Ye@Sun.COM 	    valid_hi, valid_lo, synd, syndtype, unump);
4697532SSean.Ye@Sun.COM 
4707532SSean.Ye@Sun.COM #ifdef DEBUG
4717532SSean.Ye@Sun.COM 	/*
4727532SSean.Ye@Sun.COM 	 * Apply the reverse operation to verify the result.  If there is
4737532SSean.Ye@Sun.COM 	 * a problem complain but continue.
4747532SSean.Ye@Sun.COM 	 */
4757532SSean.Ye@Sun.COM 	if (rc == 0 && MCAMD_RC_OFFSET_VALID(unump->unum_offset)) {
4767532SSean.Ye@Sun.COM 		uint64_t rpa;
4777532SSean.Ye@Sun.COM 		if (mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump,
4787532SSean.Ye@Sun.COM 		    &rpa) != 0 || rpa != pa) {
4797532SSean.Ye@Sun.COM 			mcamd_dprintf(&mcamd, MCAMD_DBG_ERR,
4807532SSean.Ye@Sun.COM 			    "mcamd_patounum_wrap: offset calculation "
4817532SSean.Ye@Sun.COM 			    "verification for PA 0x%llx failed\n", pa);
4827532SSean.Ye@Sun.COM 		}
4837532SSean.Ye@Sun.COM 	}
4847532SSean.Ye@Sun.COM #endif
4857532SSean.Ye@Sun.COM 	rw_exit(&mc_lock);
4867532SSean.Ye@Sun.COM 
4877532SSean.Ye@Sun.COM 	return (mcamd_cmierr(rc, &mcamd));
4887532SSean.Ye@Sun.COM }
4897532SSean.Ye@Sun.COM 
4907532SSean.Ye@Sun.COM static int
fmri2unum(nvlist_t * nvl,mc_unum_t * unump)4917532SSean.Ye@Sun.COM fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
4927532SSean.Ye@Sun.COM {
4937532SSean.Ye@Sun.COM 	int i;
4947532SSean.Ye@Sun.COM 	uint64_t offset;
4957532SSean.Ye@Sun.COM 	nvlist_t *hcsp, **hcl;
4967532SSean.Ye@Sun.COM 	uint_t npr;
4977532SSean.Ye@Sun.COM 
4987532SSean.Ye@Sun.COM 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
4997532SSean.Ye@Sun.COM 	    (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
5007532SSean.Ye@Sun.COM 	    &offset) != 0 && nvlist_lookup_uint64(hcsp,
5017532SSean.Ye@Sun.COM 	    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
5027532SSean.Ye@Sun.COM 	    nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
5037532SSean.Ye@Sun.COM 		return (0);
5047532SSean.Ye@Sun.COM 
5057532SSean.Ye@Sun.COM 
5067532SSean.Ye@Sun.COM 	bzero(unump, sizeof (mc_unum_t));
5077532SSean.Ye@Sun.COM 	unump->unum_chan = MC_INVALNUM;
5087532SSean.Ye@Sun.COM 	for (i = 0; i < MC_UNUM_NDIMM; i++)
5097532SSean.Ye@Sun.COM 		unump->unum_dimms[i] = MC_INVALNUM;
5107532SSean.Ye@Sun.COM 
5117532SSean.Ye@Sun.COM 	for (i = 0; i < npr; i++) {
5127532SSean.Ye@Sun.COM 		char *hcnm, *hcid;
5137532SSean.Ye@Sun.COM 		long v;
5147532SSean.Ye@Sun.COM 
5157532SSean.Ye@Sun.COM 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
5167532SSean.Ye@Sun.COM 		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
5177532SSean.Ye@Sun.COM 		    ddi_strtol(hcid, NULL, 0, &v) != 0)
5187532SSean.Ye@Sun.COM 			return (0);
5197532SSean.Ye@Sun.COM 
5207532SSean.Ye@Sun.COM 		if (strcmp(hcnm, "motherboard") == 0)
5217532SSean.Ye@Sun.COM 			unump->unum_board = (int)v;
5227532SSean.Ye@Sun.COM 		else if (strcmp(hcnm, "chip") == 0)
5237532SSean.Ye@Sun.COM 			unump->unum_chip = (int)v;
5247532SSean.Ye@Sun.COM 		else if (strcmp(hcnm, "memory-controller") == 0)
5257532SSean.Ye@Sun.COM 			unump->unum_mc = (int)v;
5267532SSean.Ye@Sun.COM 		else if (strcmp(hcnm, "chip-select") == 0)
5277532SSean.Ye@Sun.COM 			unump->unum_cs = (int)v;
5287532SSean.Ye@Sun.COM 		else if (strcmp(hcnm, "dimm") == 0)
5297532SSean.Ye@Sun.COM 			unump->unum_dimms[0] = (int)v;
5307532SSean.Ye@Sun.COM 		else if (strcmp(hcnm, "rank") == 0)
5317532SSean.Ye@Sun.COM 			unump->unum_rank = (int)v;
5327532SSean.Ye@Sun.COM 	}
5337532SSean.Ye@Sun.COM 
5347532SSean.Ye@Sun.COM 	unump->unum_offset = offset;
5357532SSean.Ye@Sun.COM 
5367532SSean.Ye@Sun.COM 	return (1);
5377532SSean.Ye@Sun.COM }
5387532SSean.Ye@Sun.COM 
5397532SSean.Ye@Sun.COM /*ARGSUSED*/
5407532SSean.Ye@Sun.COM cmi_errno_t
mcamd_unumtopa_wrap(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)5417532SSean.Ye@Sun.COM mcamd_unumtopa_wrap(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
5427532SSean.Ye@Sun.COM {
5437532SSean.Ye@Sun.COM 	mcamd_hdl_t mcamd;
5447532SSean.Ye@Sun.COM 	int rc;
5457532SSean.Ye@Sun.COM 	mc_unum_t unum;
5467532SSean.Ye@Sun.COM 
5477532SSean.Ye@Sun.COM 	ASSERT(unump == NULL || nvl == NULL);	/* enforced at cmi level */
5487532SSean.Ye@Sun.COM 
5497532SSean.Ye@Sun.COM 	if (unump == NULL) {
5507532SSean.Ye@Sun.COM 		if (!fmri2unum(nvl, &unum))
5517532SSean.Ye@Sun.COM 			return (CMIERR_MC_INVALUNUM);
5527532SSean.Ye@Sun.COM 		unump = &unum;
5537532SSean.Ye@Sun.COM 	}
5547532SSean.Ye@Sun.COM 
5557532SSean.Ye@Sun.COM 	mcamd_mkhdl(&mcamd);
5567532SSean.Ye@Sun.COM 
5577532SSean.Ye@Sun.COM 	rw_enter(&mc_lock, RW_READER);
5587532SSean.Ye@Sun.COM 	rc = mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump, pap);
5597532SSean.Ye@Sun.COM 	rw_exit(&mc_lock);
5607532SSean.Ye@Sun.COM 
5617532SSean.Ye@Sun.COM 	return (mcamd_cmierr(rc, &mcamd));
5627532SSean.Ye@Sun.COM }
5637532SSean.Ye@Sun.COM 
5647532SSean.Ye@Sun.COM static void
mc_ereport_dimm_resource(mc_unum_t * unump,nvlist_t * elems[],int * nump,mc_t * mc)565*10942STom.Pothier@Sun.COM mc_ereport_dimm_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump,
566*10942STom.Pothier@Sun.COM     mc_t *mc)
5677532SSean.Ye@Sun.COM {
5687532SSean.Ye@Sun.COM 	int i;
5697532SSean.Ye@Sun.COM 
5707532SSean.Ye@Sun.COM 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
5717532SSean.Ye@Sun.COM 		if (unump->unum_dimms[i] == MC_INVALNUM)
5727532SSean.Ye@Sun.COM 			break;
5737532SSean.Ye@Sun.COM 
5747532SSean.Ye@Sun.COM 		elems[(*nump)++] = fm_nvlist_create(NULL);
575*10942STom.Pothier@Sun.COM 
576*10942STom.Pothier@Sun.COM 		if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
577*10942STom.Pothier@Sun.COM 			fm_fmri_hc_create(elems[i], FM_HC_SCHEME_VERSION,
578*10942STom.Pothier@Sun.COM 			    NULL, NULL, mc->smb_bboard, 4,
579*10942STom.Pothier@Sun.COM 			    "chip", mc->smb_chipid,
580*10942STom.Pothier@Sun.COM 			    "memory-controller", unump->unum_mc,
581*10942STom.Pothier@Sun.COM 			    "dimm", unump->unum_dimms[i],
582*10942STom.Pothier@Sun.COM 			    "rank", unump->unum_rank);
583*10942STom.Pothier@Sun.COM 		} else {
584*10942STom.Pothier@Sun.COM 			fm_fmri_hc_set(elems[i], FM_HC_SCHEME_VERSION,
585*10942STom.Pothier@Sun.COM 			    NULL, NULL, 5,
586*10942STom.Pothier@Sun.COM 			    "motherboard",  unump->unum_board,
587*10942STom.Pothier@Sun.COM 			    "chip", unump->unum_chip,
588*10942STom.Pothier@Sun.COM 			    "memory-controller", unump->unum_mc,
589*10942STom.Pothier@Sun.COM 			    "dimm", unump->unum_dimms[i],
590*10942STom.Pothier@Sun.COM 			    "rank", unump->unum_rank);
591*10942STom.Pothier@Sun.COM 		}
5927532SSean.Ye@Sun.COM 	}
5937532SSean.Ye@Sun.COM }
5947532SSean.Ye@Sun.COM 
5957532SSean.Ye@Sun.COM static void
mc_ereport_cs_resource(mc_unum_t * unump,nvlist_t * elems[],int * nump,mc_t * mc)596*10942STom.Pothier@Sun.COM mc_ereport_cs_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump, mc_t *mc)
5977532SSean.Ye@Sun.COM {
5987532SSean.Ye@Sun.COM 	elems[0] = fm_nvlist_create(NULL);
599*10942STom.Pothier@Sun.COM 
600*10942STom.Pothier@Sun.COM 	if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
601*10942STom.Pothier@Sun.COM 		fm_fmri_hc_create(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL,
602*10942STom.Pothier@Sun.COM 		    mc->smb_bboard, 3,
603*10942STom.Pothier@Sun.COM 		    "chip", mc->smb_chipid,
604*10942STom.Pothier@Sun.COM 		    "memory-controller", unump->unum_mc,
605*10942STom.Pothier@Sun.COM 		    "chip-select", unump->unum_cs);
606*10942STom.Pothier@Sun.COM 	} else {
607*10942STom.Pothier@Sun.COM 		fm_fmri_hc_set(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL, 4,
608*10942STom.Pothier@Sun.COM 		    "motherboard",  unump->unum_board,
609*10942STom.Pothier@Sun.COM 		    "chip", unump->unum_chip,
610*10942STom.Pothier@Sun.COM 		    "memory-controller", unump->unum_mc,
611*10942STom.Pothier@Sun.COM 		    "chip-select", unump->unum_cs);
612*10942STom.Pothier@Sun.COM 	}
6137532SSean.Ye@Sun.COM 	*nump = 1;
6147532SSean.Ye@Sun.COM }
6157532SSean.Ye@Sun.COM 
6167532SSean.Ye@Sun.COM /*
6177532SSean.Ye@Sun.COM  * Create the 'resource' payload member from the unum info.  If valid
6187532SSean.Ye@Sun.COM  * dimm numbers are present in the unum info then create members
6197532SSean.Ye@Sun.COM  * identifying the dimm and rank;  otherwise if a valid chip-select
6207532SSean.Ye@Sun.COM  * number is indicated then create a member identifying the chip-select
6217532SSean.Ye@Sun.COM  * topology node.
6227532SSean.Ye@Sun.COM  */
6237532SSean.Ye@Sun.COM static void
mc_ereport_add_resource(nvlist_t * payload,mc_unum_t * unump,mc_t * mc)624*10942STom.Pothier@Sun.COM mc_ereport_add_resource(nvlist_t *payload, mc_unum_t *unump, mc_t *mc)
6257532SSean.Ye@Sun.COM {
6267532SSean.Ye@Sun.COM 	nvlist_t *elems[MC_UNUM_NDIMM];
6277532SSean.Ye@Sun.COM 	int nelems = 0;
6287532SSean.Ye@Sun.COM 	int i;
6297532SSean.Ye@Sun.COM 
6307532SSean.Ye@Sun.COM 	if (unump->unum_dimms[0] != MC_INVALNUM)
631*10942STom.Pothier@Sun.COM 		mc_ereport_dimm_resource(unump, elems, &nelems, mc);
6327532SSean.Ye@Sun.COM 	else if (unump->unum_cs != MC_INVALNUM)
633*10942STom.Pothier@Sun.COM 		mc_ereport_cs_resource(unump, elems, &nelems, mc);
6347532SSean.Ye@Sun.COM 
6357532SSean.Ye@Sun.COM 	if (nelems > 0) {
6367532SSean.Ye@Sun.COM 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
6377532SSean.Ye@Sun.COM 		    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
6387532SSean.Ye@Sun.COM 
6397532SSean.Ye@Sun.COM 		for (i = 0; i < nelems; i++)
6407532SSean.Ye@Sun.COM 			fm_nvlist_destroy(elems[i], FM_NVA_FREE);
6417532SSean.Ye@Sun.COM 	}
6427532SSean.Ye@Sun.COM }
6437532SSean.Ye@Sun.COM 
6447532SSean.Ye@Sun.COM static void
mc_ereport_add_payload(nvlist_t * ereport,uint64_t members,mc_unum_t * unump,mc_t * mc)645*10942STom.Pothier@Sun.COM mc_ereport_add_payload(nvlist_t *ereport, uint64_t members, mc_unum_t *unump,
646*10942STom.Pothier@Sun.COM     mc_t *mc)
6477532SSean.Ye@Sun.COM {
6487532SSean.Ye@Sun.COM 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE &&
6497532SSean.Ye@Sun.COM 	    unump != NULL)
650*10942STom.Pothier@Sun.COM 		mc_ereport_add_resource(ereport, unump, mc);
6517532SSean.Ye@Sun.COM }
6527532SSean.Ye@Sun.COM 
6537532SSean.Ye@Sun.COM static nvlist_t *
mc_fmri_create(mc_t * mc)6547532SSean.Ye@Sun.COM mc_fmri_create(mc_t *mc)
6557532SSean.Ye@Sun.COM {
6567532SSean.Ye@Sun.COM 	nvlist_t *nvl = fm_nvlist_create(NULL);
6577532SSean.Ye@Sun.COM 
658*10942STom.Pothier@Sun.COM 	if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
659*10942STom.Pothier@Sun.COM 		fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, NULL,
660*10942STom.Pothier@Sun.COM 		    mc->smb_bboard, 2,
661*10942STom.Pothier@Sun.COM 		    "chip", mc->smb_chipid,
662*10942STom.Pothier@Sun.COM 		    "memory-controller", 0);
663*10942STom.Pothier@Sun.COM 	} else {
664*10942STom.Pothier@Sun.COM 		fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3,
665*10942STom.Pothier@Sun.COM 		    "motherboard", 0,
666*10942STom.Pothier@Sun.COM 		    "chip", mc->mc_props.mcp_num,
667*10942STom.Pothier@Sun.COM 		    "memory-controller", 0);
668*10942STom.Pothier@Sun.COM 	}
6697532SSean.Ye@Sun.COM 
6707532SSean.Ye@Sun.COM 	return (nvl);
6717532SSean.Ye@Sun.COM }
6727532SSean.Ye@Sun.COM 
6737532SSean.Ye@Sun.COM /*
6747532SSean.Ye@Sun.COM  * Simple ereport generator for errors detected by the memory controller.
6757532SSean.Ye@Sun.COM  * Posts an ereport of class ereport.cpu.amd.<class_sfx> with a resource nvlist
6767532SSean.Ye@Sun.COM  * derived from the given mc_unum_t.  There are no other payload members.
6777532SSean.Ye@Sun.COM  * The mc argument is used to formulate a detector and this mc should
6787532SSean.Ye@Sun.COM  * correspond with that identified in the mc_unum_t.
6797532SSean.Ye@Sun.COM  *
6807532SSean.Ye@Sun.COM  * There is no control of which members to include the the resulting ereport -
6817532SSean.Ye@Sun.COM  * it will be an ereport formed using the given class suffix, detector
6827532SSean.Ye@Sun.COM  * indicated as the memory-controller and with a resource generated by
6837532SSean.Ye@Sun.COM  * expanding the given mc_unum_t.
6847532SSean.Ye@Sun.COM  *
6857532SSean.Ye@Sun.COM  * We do not use any special nv allocator here and so this is not suitable
6867532SSean.Ye@Sun.COM  * for use during panic.  It is intended for use during MC topology
6877532SSean.Ye@Sun.COM  * discovery and other controlled circumstances.
6887532SSean.Ye@Sun.COM  */
6897532SSean.Ye@Sun.COM void
mcamd_ereport_post(mc_t * mc,const char * class_sfx,mc_unum_t * unump,uint64_t payload)6907532SSean.Ye@Sun.COM mcamd_ereport_post(mc_t *mc, const char *class_sfx, mc_unum_t *unump,
6917532SSean.Ye@Sun.COM     uint64_t payload)
6927532SSean.Ye@Sun.COM {
6937532SSean.Ye@Sun.COM 	nvlist_t *ereport, *detector;
6947532SSean.Ye@Sun.COM 	char buf[FM_MAX_CLASS];
6957532SSean.Ye@Sun.COM 
6967532SSean.Ye@Sun.COM 	ereport = fm_nvlist_create(NULL);
6977532SSean.Ye@Sun.COM 	detector = mc_fmri_create(mc);
6987532SSean.Ye@Sun.COM 
6997532SSean.Ye@Sun.COM 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s", FM_ERROR_CPU,
7007532SSean.Ye@Sun.COM 	    "amd", class_sfx);
7017532SSean.Ye@Sun.COM 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
7027532SSean.Ye@Sun.COM 	    fm_ena_generate(gethrtime(), FM_ENA_FMT1), detector, NULL);
7037532SSean.Ye@Sun.COM 	fm_nvlist_destroy(detector, FM_NVA_FREE);
7047532SSean.Ye@Sun.COM 
705*10942STom.Pothier@Sun.COM 	mc_ereport_add_payload(ereport, payload, unump, mc);
7067532SSean.Ye@Sun.COM 
7077532SSean.Ye@Sun.COM 	(void) fm_ereport_post(ereport, EVCH_TRYHARD);
7087532SSean.Ye@Sun.COM 	fm_nvlist_destroy(ereport, FM_NVA_FREE);
7097532SSean.Ye@Sun.COM }
7107532SSean.Ye@Sun.COM 
7117532SSean.Ye@Sun.COM static const cmi_mc_ops_t mcamd_mc_ops = {
7127532SSean.Ye@Sun.COM 	mcamd_patounum_wrap,	/* cmi_mc_patounum */
7137532SSean.Ye@Sun.COM 	mcamd_unumtopa_wrap,	/* cmi_mc_unumtopa */
7147532SSean.Ye@Sun.COM 	NULL			/* cmi_mc_logout */
7157532SSean.Ye@Sun.COM };
7167532SSean.Ye@Sun.COM 
7177532SSean.Ye@Sun.COM void
mcamd_mc_register(cmi_hdl_t hdl,mc_t * mc)7187532SSean.Ye@Sun.COM mcamd_mc_register(cmi_hdl_t hdl, mc_t *mc)
7197532SSean.Ye@Sun.COM {
7207532SSean.Ye@Sun.COM 	cmi_mc_register(hdl, &mcamd_mc_ops, mc);
7217532SSean.Ye@Sun.COM }
722