17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
58a40a695Sgavinm  * Common Development and Distribution License (the "License").
68a40a695Sgavinm  * You may not use this file except in compliance with the License.
77aec1d6eScindi  *
87aec1d6eScindi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97aec1d6eScindi  * or http://www.opensolaris.org/os/licensing.
107aec1d6eScindi  * See the License for the specific language governing permissions
117aec1d6eScindi  * and limitations under the License.
127aec1d6eScindi  *
137aec1d6eScindi  * When distributing Covered Code, include this CDDL HEADER in each
147aec1d6eScindi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157aec1d6eScindi  * If applicable, add the following below this CDDL HEADER, with the
167aec1d6eScindi  * fields enclosed by brackets "[]" replaced with your own identifying
177aec1d6eScindi  * information: Portions Copyright [yyyy] [name of copyright owner]
187aec1d6eScindi  *
197aec1d6eScindi  * CDDL HEADER END
207aec1d6eScindi  */
217aec1d6eScindi 
227aec1d6eScindi /*
23*074bb90dSTom Pothier  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247aec1d6eScindi  * Use is subject to license terms.
257aec1d6eScindi  */
267aec1d6eScindi 
277aec1d6eScindi /*
287aec1d6eScindi  * Stub routines used to link in files from $SRC/common/mc
297aec1d6eScindi  */
307aec1d6eScindi 
317aec1d6eScindi #include <sys/types.h>
327aec1d6eScindi #include <sys/cmn_err.h>
337aec1d6eScindi #include <sys/ddi.h>
347aec1d6eScindi #include <sys/sunddi.h>
357aec1d6eScindi #include <sys/varargs.h>
368a40a695Sgavinm #include <sys/fm/util.h>
378a40a695Sgavinm #include <sys/fm/cpu/AMD.h>
38*074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h>
397aec1d6eScindi #include <sys/fm/protocol.h>
407aec1d6eScindi #include <sys/mc.h>
41*074bb90dSTom Pothier #include <sys/smbios.h>
42*074bb90dSTom Pothier #include <sys/smbios_impl.h>
437aec1d6eScindi 
447aec1d6eScindi #include <mcamd.h>
457aec1d6eScindi #include <mcamd_off.h>
467aec1d6eScindi 
477aec1d6eScindi int mcamd_debug = 0; /* see mcamd_api.h for MCAMD_DBG_* values */
487aec1d6eScindi 
49*074bb90dSTom Pothier extern int x86gentopo_legacy;
50*074bb90dSTom Pothier 
518a40a695Sgavinm struct mc_offmap {
528a40a695Sgavinm 	int mcom_code;
538a40a695Sgavinm 	uint_t mcom_offset;
547aec1d6eScindi };
557aec1d6eScindi 
567aec1d6eScindi static uint_t
nodetype(mcamd_node_t * node)577aec1d6eScindi nodetype(mcamd_node_t *node)
587aec1d6eScindi {
597aec1d6eScindi 	mc_hdr_t *mch = (mc_hdr_t *)node;
607aec1d6eScindi 	return (mch->mch_type);
617aec1d6eScindi }
627aec1d6eScindi 
637aec1d6eScindi static void *
node2type(mcamd_node_t * node,int type)647aec1d6eScindi node2type(mcamd_node_t *node, int type)
657aec1d6eScindi {
667aec1d6eScindi 	mc_hdr_t *mch = (mc_hdr_t *)node;
677aec1d6eScindi 	ASSERT(mch->mch_type == type);
687aec1d6eScindi 	return (mch);
697aec1d6eScindi }
707aec1d6eScindi 
717aec1d6eScindi /*
727aec1d6eScindi  * Iterate over all memory controllers.
737aec1d6eScindi  */
747aec1d6eScindi /*ARGSUSED*/
757aec1d6eScindi mcamd_node_t *
mcamd_mc_next(mcamd_hdl_t * hdl,mcamd_node_t * root,mcamd_node_t * last)767aec1d6eScindi mcamd_mc_next(mcamd_hdl_t *hdl, mcamd_node_t *root, mcamd_node_t *last)
777aec1d6eScindi {
787aec1d6eScindi 	mc_t *mc;
797aec1d6eScindi 
807aec1d6eScindi 	ASSERT(RW_LOCK_HELD(&mc_lock));
817aec1d6eScindi 
827aec1d6eScindi 	if (last == NULL)
837aec1d6eScindi 		return ((mcamd_node_t *)mc_list);
847aec1d6eScindi 
857aec1d6eScindi 	mc = node2type(last, MC_NT_MC);
867aec1d6eScindi 
877aec1d6eScindi 	return ((mcamd_node_t *)mc->mc_next);
887aec1d6eScindi }
897aec1d6eScindi 
907aec1d6eScindi /*
917aec1d6eScindi  * Iterate over all chip-selects of a MC or all chip-selects of a DIMM
927aec1d6eScindi  * depending on the node type of 'node'.  In the DIMM case we do not
937aec1d6eScindi  * have a linked list of associated chip-selects but an array of pointer
947aec1d6eScindi  * to them.
957aec1d6eScindi  */
967aec1d6eScindi /*ARGSUSED*/
977aec1d6eScindi mcamd_node_t *
mcamd_cs_next(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_node_t * last)987aec1d6eScindi mcamd_cs_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
997aec1d6eScindi {
1007aec1d6eScindi 	uint_t nt = nodetype(node);
1017aec1d6eScindi 	mc_t *mc;
1027aec1d6eScindi 	mc_cs_t *mccs;
1037aec1d6eScindi 	mc_dimm_t *mcd;
1047aec1d6eScindi 	int i;
1057aec1d6eScindi 	void *retval;
1067aec1d6eScindi 
1077aec1d6eScindi 	ASSERT(nt == MC_NT_MC || nt == MC_NT_DIMM);
1087aec1d6eScindi 
1097aec1d6eScindi 	if (last == NULL) {
1107aec1d6eScindi 		switch (nt) {
1117aec1d6eScindi 		case MC_NT_MC:
1127aec1d6eScindi 			mc = node2type(node, MC_NT_MC);
1137aec1d6eScindi 			retval = mc->mc_cslist;
1147aec1d6eScindi 			break;
1157aec1d6eScindi 		case MC_NT_DIMM:
1167aec1d6eScindi 			mcd = node2type(node, MC_NT_DIMM);
1177aec1d6eScindi 			retval = mcd->mcd_cs[0];
1187aec1d6eScindi 			break;
1197aec1d6eScindi 		}
1207aec1d6eScindi 	} else {
1217aec1d6eScindi 		mccs = node2type(last, MC_NT_CS);
1227aec1d6eScindi 
1237aec1d6eScindi 		switch (nt) {
1247aec1d6eScindi 		case MC_NT_MC:
1257aec1d6eScindi 			retval = mccs->mccs_next;
1267aec1d6eScindi 			break;
1277aec1d6eScindi 		case MC_NT_DIMM:
1287aec1d6eScindi 			mcd = node2type(node, MC_NT_DIMM);
1297aec1d6eScindi 			for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
1307aec1d6eScindi 				if (mcd->mcd_cs[i] == mccs)
1317aec1d6eScindi 					break;
1327aec1d6eScindi 			}
1337aec1d6eScindi 			if (i == MC_CHIP_DIMMRANKMAX)
1347aec1d6eScindi 				cmn_err(CE_PANIC, "Bad last value for "
1357aec1d6eScindi 				    "mcamd_cs_next");
1367aec1d6eScindi 
1377aec1d6eScindi 			if (i == MC_CHIP_DIMMRANKMAX - 1)
1387aec1d6eScindi 				retval = NULL;
1397aec1d6eScindi 			else
1407aec1d6eScindi 				retval = mcd->mcd_cs[i + 1];
1417aec1d6eScindi 			break;
1427aec1d6eScindi 		}
1437aec1d6eScindi 	}
1447aec1d6eScindi 
1457aec1d6eScindi 	return ((mcamd_node_t *)retval);
1467aec1d6eScindi }
1477aec1d6eScindi 
1487aec1d6eScindi /*
1497aec1d6eScindi  * Iterate over all DIMMs of an MC or all DIMMs of a chip-select depending
1508a40a695Sgavinm  * on the node type of 'node'.  In the chip-select case we do not have
1517aec1d6eScindi  * a linked list of associated DIMMs but an array of pointers to them.
1527aec1d6eScindi  */
1537aec1d6eScindi /*ARGSUSED*/
1547aec1d6eScindi mcamd_node_t *
mcamd_dimm_next(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_node_t * last)1557aec1d6eScindi mcamd_dimm_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
1567aec1d6eScindi {
1577aec1d6eScindi 	uint_t nt = nodetype(node);
1587aec1d6eScindi 	mc_t *mc;
1597aec1d6eScindi 	mc_cs_t *mccs;
1607aec1d6eScindi 	mc_dimm_t *mcd;
1617aec1d6eScindi 	int i;
1627aec1d6eScindi 	void *retval;
1637aec1d6eScindi 
1647aec1d6eScindi 	ASSERT(nt == MC_NT_MC || nt == MC_NT_CS);
1657aec1d6eScindi 
1667aec1d6eScindi 	if (last == NULL) {
1677aec1d6eScindi 		switch (nt) {
1687aec1d6eScindi 		case MC_NT_MC:
1697aec1d6eScindi 			mc = node2type(node, MC_NT_MC);
1708a40a695Sgavinm 			retval =  mc->mc_dimmlist;
1717aec1d6eScindi 			break;
1727aec1d6eScindi 		case MC_NT_CS:
1737aec1d6eScindi 			mccs = node2type(node, MC_NT_CS);
1747aec1d6eScindi 			retval = mccs->mccs_dimm[0];
1757aec1d6eScindi 			break;
1767aec1d6eScindi 		}
1777aec1d6eScindi 	} else {
1787aec1d6eScindi 		mcd = node2type(last, MC_NT_DIMM);
1797aec1d6eScindi 
1807aec1d6eScindi 		switch (nt) {
1817aec1d6eScindi 		case MC_NT_MC:
1827aec1d6eScindi 			retval = mcd->mcd_next;
1837aec1d6eScindi 			break;
1847aec1d6eScindi 		case MC_NT_CS:
1857aec1d6eScindi 			mccs = node2type(node, MC_NT_CS);
1867aec1d6eScindi 			for (i = 0; i < MC_CHIP_DIMMPERCS; i++) {
1877aec1d6eScindi 				if (mccs->mccs_dimm[i] == mcd)
1887aec1d6eScindi 					break;
1897aec1d6eScindi 			}
1907aec1d6eScindi 			if (i == MC_CHIP_DIMMPERCS)
1917aec1d6eScindi 				cmn_err(CE_PANIC, "Bad last value for "
1927aec1d6eScindi 				    "mcamd_dimm_next");
1937aec1d6eScindi 
1947aec1d6eScindi 			if (i == MC_CHIP_DIMMPERCS - 1)
1957aec1d6eScindi 				retval = NULL;
1967aec1d6eScindi 			else
1977aec1d6eScindi 				retval = mccs->mccs_dimm[i + 1];
1987aec1d6eScindi 			break;
1997aec1d6eScindi 		}
2007aec1d6eScindi 	}
2017aec1d6eScindi 
2027aec1d6eScindi 	return ((mcamd_node_t *)retval);
2037aec1d6eScindi }
2047aec1d6eScindi 
2057aec1d6eScindi /*ARGSUSED*/
2067aec1d6eScindi mcamd_node_t *
mcamd_cs_mc(mcamd_hdl_t * hdl,mcamd_node_t * csnode)2077aec1d6eScindi mcamd_cs_mc(mcamd_hdl_t *hdl, mcamd_node_t *csnode)
2087aec1d6eScindi {
2097aec1d6eScindi 	mc_cs_t *mccs = node2type(csnode, MC_NT_CS);
2107aec1d6eScindi 	return ((mcamd_node_t *)mccs->mccs_mc);
2117aec1d6eScindi }
2127aec1d6eScindi 
2137aec1d6eScindi /*ARGSUSED*/
2147aec1d6eScindi mcamd_node_t *
mcamd_dimm_mc(mcamd_hdl_t * hdl,mcamd_node_t * dnode)2157aec1d6eScindi mcamd_dimm_mc(mcamd_hdl_t *hdl, mcamd_node_t *dnode)
2167aec1d6eScindi {
2177aec1d6eScindi 	mc_dimm_t *mcd = node2type(dnode, MC_NT_DIMM);
2187aec1d6eScindi 	return ((mcamd_node_t *)mcd->mcd_mc);
2197aec1d6eScindi }
2207aec1d6eScindi 
2217aec1d6eScindi /*
2227aec1d6eScindi  * Node properties.  A property is accessed through a property number code;
2237aec1d6eScindi  * we search these tables for a match (choosing table from node type) and
2247aec1d6eScindi  * return the uint64_t property at the indicated offset into the node
2257aec1d6eScindi  * structure.  All properties must be of type uint64_t.  It is assumed that
2267aec1d6eScindi  * property lookup does not have to be super-fast - we search linearly
2277aec1d6eScindi  * down the (small) lists.
2287aec1d6eScindi  */
2298a40a695Sgavinm static const struct mc_offmap mcamd_mc_offmap[] = {
2307aec1d6eScindi 	{ MCAMD_PROP_NUM, MCAMD_MC_OFF_NUM },
2317aec1d6eScindi 	{ MCAMD_PROP_REV, MCAMD_MC_OFF_REV },
2327aec1d6eScindi 	{ MCAMD_PROP_BASE_ADDR, MCAMD_MC_OFF_BASE_ADDR },
2337aec1d6eScindi 	{ MCAMD_PROP_LIM_ADDR, MCAMD_MC_OFF_LIM_ADDR },
2348a40a695Sgavinm 	{ MCAMD_PROP_ILEN, MCAMD_MC_OFF_ILEN },
2358a40a695Sgavinm 	{ MCAMD_PROP_ILSEL, MCAMD_MC_OFF_ILSEL },
2368a40a695Sgavinm 	{ MCAMD_PROP_CSINTLVFCTR, MCAMD_MC_OFF_CSINTLVFCTR },
2378a40a695Sgavinm 	{ MCAMD_PROP_DRAMHOLE_SIZE, MCAMD_MC_OFF_DRAMHOLE_SIZE },
2387aec1d6eScindi 	{ MCAMD_PROP_ACCESS_WIDTH, MCAMD_MC_OFF_ACCWIDTH },
2398a40a695Sgavinm 	{ MCAMD_PROP_CSBANKMAPREG, MCAMD_MC_OFF_CSBANKMAPREG },
2408a40a695Sgavinm 	{ MCAMD_PROP_BANKSWZL, MCAMD_MC_OFF_BNKSWZL },
2418a40a695Sgavinm 	{ MCAMD_PROP_MOD64MUX, MCAMD_MC_OFF_MOD64MUX },
2428a40a695Sgavinm 	{ MCAMD_PROP_SPARECS, MCAMD_MC_OFF_SPARECS },
2438a40a695Sgavinm 	{ MCAMD_PROP_BADCS, MCAMD_MC_OFF_BADCS },
2447aec1d6eScindi };
2457aec1d6eScindi 
2468a40a695Sgavinm static const struct mc_offmap mcamd_cs_offmap[] = {
2477aec1d6eScindi 	{ MCAMD_PROP_NUM, MCAMD_CS_OFF_NUM },
2487aec1d6eScindi 	{ MCAMD_PROP_BASE_ADDR, MCAMD_CS_OFF_BASE_ADDR },
2497aec1d6eScindi 	{ MCAMD_PROP_MASK, MCAMD_CS_OFF_MASK },
2507aec1d6eScindi 	{ MCAMD_PROP_SIZE, MCAMD_CS_OFF_SIZE },
2518a40a695Sgavinm 	{ MCAMD_PROP_CSBE, MCAMD_CS_OFF_CSBE },
2528a40a695Sgavinm 	{ MCAMD_PROP_SPARE, MCAMD_CS_OFF_SPARE },
2538a40a695Sgavinm 	{ MCAMD_PROP_TESTFAIL, MCAMD_CS_OFF_TESTFAIL },
2548a40a695Sgavinm 	{ MCAMD_PROP_CSDIMM1, MCAMD_CS_OFF_DIMMNUMS },
2558a40a695Sgavinm 	{ MCAMD_PROP_CSDIMM2, MCAMD_CS_OFF_DIMMNUMS +
2568a40a695Sgavinm 	    MCAMD_CS_OFF_DIMMNUMS_INCR },
2578a40a695Sgavinm 	{ MCAMD_PROP_DIMMRANK, MCAMD_CS_OFF_DIMMRANK },
2587aec1d6eScindi };
2597aec1d6eScindi 
2608a40a695Sgavinm static const struct mc_offmap mcamd_dimm_offmap[] = {
2617aec1d6eScindi 	{ MCAMD_PROP_NUM, MCAMD_DIMM_OFF_NUM },
2628a40a695Sgavinm 	{ MCAMD_PROP_SIZE, MCAMD_DIMM_OFF_SIZE },
2638a40a695Sgavinm };
2648a40a695Sgavinm 
2658a40a695Sgavinm struct nt_offmap {
2668a40a695Sgavinm 	const struct mc_offmap *omp;
2678a40a695Sgavinm 	int mapents;
2687aec1d6eScindi };
2697aec1d6eScindi 
2707aec1d6eScindi /*ARGSUSED*/
2718a40a695Sgavinm static int
findoffset(mcamd_hdl_t * hdl,mcamd_node_t * node,struct nt_offmap * arr,int code,uint_t * offset)2728a40a695Sgavinm findoffset(mcamd_hdl_t *hdl, mcamd_node_t *node, struct nt_offmap *arr,
2738a40a695Sgavinm     int code, uint_t *offset)
2747aec1d6eScindi {
2757aec1d6eScindi 	int i;
2767aec1d6eScindi 	mc_hdr_t *mch = (mc_hdr_t *)node;
2777aec1d6eScindi 	int nt = mch->mch_type;
2788a40a695Sgavinm 	const struct mc_offmap *omp;
2797aec1d6eScindi 
2808a40a695Sgavinm 	if (nt > MC_NT_NTYPES || (omp = arr[nt].omp) == NULL)
2818a40a695Sgavinm 		return (0);
2828a40a695Sgavinm 
2838a40a695Sgavinm 	for (i = 0; i < arr[nt].mapents; i++, omp++) {
2848a40a695Sgavinm 		if (omp->mcom_code == code) {
2858a40a695Sgavinm 			*offset = omp->mcom_offset;
2868a40a695Sgavinm 			return (1);
2877aec1d6eScindi 		}
2887aec1d6eScindi 	}
2897aec1d6eScindi 
2908a40a695Sgavinm 	return (0);
2918a40a695Sgavinm }
2928a40a695Sgavinm 
2938a40a695Sgavinm /*ARGSUSED*/
2948a40a695Sgavinm int
mcamd_get_numprop(mcamd_hdl_t * hdl,mcamd_node_t * node,mcamd_propcode_t code,mcamd_prop_t * valp)2958a40a695Sgavinm mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node,
2968a40a695Sgavinm     mcamd_propcode_t code, mcamd_prop_t *valp)
2978a40a695Sgavinm {
2988a40a695Sgavinm 	int found;
2998a40a695Sgavinm 	uint_t offset;
3008a40a695Sgavinm 
3018a40a695Sgavinm 	struct nt_offmap props[] = {
3028a40a695Sgavinm 		{ mcamd_mc_offmap,	/* MC_NT_MC */
3038a40a695Sgavinm 		    sizeof (mcamd_mc_offmap) / sizeof (struct mc_offmap) },
3048a40a695Sgavinm 		{ mcamd_cs_offmap,	/* MC_NT_CS */
3058a40a695Sgavinm 		    sizeof (mcamd_cs_offmap) / sizeof (struct mc_offmap) },
3068a40a695Sgavinm 		{ mcamd_dimm_offmap,	/* MC_NT_DIMM */
3078a40a695Sgavinm 		    sizeof (mcamd_dimm_offmap) / sizeof (struct mc_offmap) }
3088a40a695Sgavinm 	};
3098a40a695Sgavinm 
3108a40a695Sgavinm 	found = findoffset(hdl, node, &props[0], code, &offset);
3117aec1d6eScindi 	ASSERT(found);
3128a40a695Sgavinm 
3138a40a695Sgavinm 	if (found)
3148a40a695Sgavinm 		*valp = *(uint64_t *)((uintptr_t)node + offset);
3158a40a695Sgavinm 
3168a40a695Sgavinm 	return (found == 1);
3178a40a695Sgavinm }
3188a40a695Sgavinm 
3198a40a695Sgavinm int
mcamd_get_numprops(mcamd_hdl_t * hdl,...)3208a40a695Sgavinm mcamd_get_numprops(mcamd_hdl_t *hdl, ...)
3218a40a695Sgavinm {
3228a40a695Sgavinm 	va_list ap;
3238a40a695Sgavinm 	mcamd_node_t *node;
3248a40a695Sgavinm 	mcamd_propcode_t code;
3258a40a695Sgavinm 	mcamd_prop_t *valp;
3268a40a695Sgavinm 
3278a40a695Sgavinm 	va_start(ap, hdl);
3288a40a695Sgavinm 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
3298a40a695Sgavinm 		code = va_arg(ap, mcamd_propcode_t);
3308a40a695Sgavinm 		valp = va_arg(ap, mcamd_prop_t *);
3318a40a695Sgavinm 		if (!mcamd_get_numprop(hdl, node, code, valp))
3328a40a695Sgavinm 			return (0);
3337aec1d6eScindi 	}
3348a40a695Sgavinm 	va_end(ap);
3358a40a695Sgavinm 	return (1);
3368a40a695Sgavinm }
3378a40a695Sgavinm 
3388a40a695Sgavinm static const struct mc_offmap mcreg_offmap[] = {
3398a40a695Sgavinm 	{ MCAMD_REG_DRAMBASE, MCAMD_MC_OFF_DRAMBASE_REG },
3408a40a695Sgavinm 	{ MCAMD_REG_DRAMLIMIT, MCAMD_MC_OFF_DRAMLIMIT_REG },
3418a40a695Sgavinm 	{ MCAMD_REG_DRAMHOLE, MCAMD_MC_OFF_DRAMHOLE_REG },
3428a40a695Sgavinm 	{ MCAMD_REG_DRAMCFGLO, MCAMD_MC_OFF_DRAMCFGLO_REG },
3438a40a695Sgavinm 	{ MCAMD_REG_DRAMCFGHI, MCAMD_MC_OFF_DRAMCFGHI_REG },
3448a40a695Sgavinm };
3458a40a695Sgavinm 
3468a40a695Sgavinm static const struct mc_offmap csreg_offmap[] = {
3478a40a695Sgavinm 	{ MCAMD_REG_CSBASE, MCAMD_CS_OFF_CSBASE_REG },
3488a40a695Sgavinm 	{ MCAMD_REG_CSMASK, MCAMD_CS_OFF_CSMASK_REG },
3498a40a695Sgavinm };
3508a40a695Sgavinm 
3518a40a695Sgavinm /*ARGSUSED*/
3528a40a695Sgavinm int
mcamd_get_cfgreg(struct mcamd_hdl * hdl,mcamd_node_t * node,mcamd_regcode_t code,uint32_t * valp)3538a40a695Sgavinm mcamd_get_cfgreg(struct mcamd_hdl *hdl, mcamd_node_t *node,
3548a40a695Sgavinm     mcamd_regcode_t code, uint32_t *valp)
3558a40a695Sgavinm {
3568a40a695Sgavinm 	int found;
3578a40a695Sgavinm 	uint_t offset;
3588a40a695Sgavinm 
3598a40a695Sgavinm 	struct nt_offmap regs[] = {
3608a40a695Sgavinm 		{ mcreg_offmap,	/* MC_NT_MC */
3618a40a695Sgavinm 		    sizeof (mcreg_offmap) / sizeof (struct mc_offmap) },
3628a40a695Sgavinm 		{ csreg_offmap,	/* MC_NT_CS */
3638a40a695Sgavinm 		    sizeof (csreg_offmap) / sizeof (struct mc_offmap) },
3648a40a695Sgavinm 		{ NULL, 0 }		/* MC_NT_DIMM */
3658a40a695Sgavinm 	};
3668a40a695Sgavinm 
3678a40a695Sgavinm 	found = findoffset(hdl, node, &regs[0], code, &offset);
3688a40a695Sgavinm 	ASSERT(found);
3698a40a695Sgavinm 
3708a40a695Sgavinm 	ASSERT(found);
3718a40a695Sgavinm 	if (found)
3728a40a695Sgavinm 		*valp = *(uint32_t *)((uintptr_t)node + offset);
3737aec1d6eScindi 
3747aec1d6eScindi 	return (found == 1);
3757aec1d6eScindi }
3767aec1d6eScindi 
3778a40a695Sgavinm int
mcamd_get_cfgregs(mcamd_hdl_t * hdl,...)3788a40a695Sgavinm mcamd_get_cfgregs(mcamd_hdl_t *hdl, ...)
3798a40a695Sgavinm {
3808a40a695Sgavinm 	va_list ap;
3818a40a695Sgavinm 	mcamd_node_t *node;
3828a40a695Sgavinm 	mcamd_regcode_t code;
3838a40a695Sgavinm 	uint32_t *valp;
3848a40a695Sgavinm 
3858a40a695Sgavinm 	va_start(ap, hdl);
3868a40a695Sgavinm 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
3878a40a695Sgavinm 		code = va_arg(ap, mcamd_regcode_t);
3888a40a695Sgavinm 		valp = va_arg(ap, uint32_t *);
3898a40a695Sgavinm 		if (!mcamd_get_cfgreg(hdl, node, code, valp))
3908a40a695Sgavinm 			return (0);
3918a40a695Sgavinm 	}
3928a40a695Sgavinm 	va_end(ap);
3938a40a695Sgavinm 	return (1);
3948a40a695Sgavinm }
3958a40a695Sgavinm 
3968a40a695Sgavinm 
3977aec1d6eScindi int
mcamd_errno(mcamd_hdl_t * mcamd)3987aec1d6eScindi mcamd_errno(mcamd_hdl_t *mcamd)
3997aec1d6eScindi {
4007aec1d6eScindi 	return (mcamd->mcamd_errno);
4017aec1d6eScindi }
4027aec1d6eScindi 
4037aec1d6eScindi int
mcamd_set_errno(mcamd_hdl_t * mcamd,int err)4047aec1d6eScindi mcamd_set_errno(mcamd_hdl_t *mcamd, int err)
4057aec1d6eScindi {
4067aec1d6eScindi 	mcamd->mcamd_errno = err;
4077aec1d6eScindi 	return (-1);
4087aec1d6eScindi }
4097aec1d6eScindi 
4107aec1d6eScindi void
mcamd_dprintf(mcamd_hdl_t * mcamd,int mask,const char * fmt,...)4117aec1d6eScindi mcamd_dprintf(mcamd_hdl_t *mcamd, int mask, const char *fmt, ...)
4127aec1d6eScindi {
4137aec1d6eScindi 	va_list ap;
4147aec1d6eScindi 
4157aec1d6eScindi 	if (!(mcamd->mcamd_debug & mask))
4167aec1d6eScindi 		return;
4177aec1d6eScindi 
4187aec1d6eScindi 	va_start(ap, fmt);
4197aec1d6eScindi 	vcmn_err(mask & MCAMD_DBG_ERR ? CE_WARN : CE_NOTE, fmt, ap);
4207aec1d6eScindi 	va_end(ap);
4217aec1d6eScindi }
4227aec1d6eScindi 
4237aec1d6eScindi void
mcamd_mkhdl(mcamd_hdl_t * hdl)4247aec1d6eScindi mcamd_mkhdl(mcamd_hdl_t *hdl)
4257aec1d6eScindi {
4267aec1d6eScindi 	hdl->mcamd_errno = 0;
4277aec1d6eScindi 	hdl->mcamd_debug = mcamd_debug;
4287aec1d6eScindi }
4297aec1d6eScindi 
43020c794b3Sgavinm cmi_errno_t
mcamd_cmierr(int err,mcamd_hdl_t * hdl)43120c794b3Sgavinm mcamd_cmierr(int err, mcamd_hdl_t *hdl)
43220c794b3Sgavinm {
43320c794b3Sgavinm 	if (err == 0)
43420c794b3Sgavinm 		return (CMI_SUCCESS);
43520c794b3Sgavinm 
43620c794b3Sgavinm 	switch (mcamd_errno(hdl)) {
43720c794b3Sgavinm 	case EMCAMD_SYNDINVALID:
43820c794b3Sgavinm 		return (CMIERR_MC_SYNDROME);
43920c794b3Sgavinm 
44020c794b3Sgavinm 	case EMCAMD_TREEINVALID:
44120c794b3Sgavinm 		return (CMIERR_MC_BADSTATE);
44220c794b3Sgavinm 
44320c794b3Sgavinm 	case EMCAMD_NOADDR:
44420c794b3Sgavinm 		return (CMIERR_MC_NOADDR);
44520c794b3Sgavinm 
44620c794b3Sgavinm 	case EMCAMD_INSUFF_RES:
44720c794b3Sgavinm 		return (CMIERR_MC_ADDRBITS);
44820c794b3Sgavinm 
44920c794b3Sgavinm 	default:
45020c794b3Sgavinm 		return (CMIERR_UNKNOWN);
45120c794b3Sgavinm 	}
45220c794b3Sgavinm 
45320c794b3Sgavinm }
45420c794b3Sgavinm 
4557aec1d6eScindi /*ARGSUSED*/
45620c794b3Sgavinm 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)4574156fc34Sgavinm mcamd_patounum_wrap(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
4584156fc34Sgavinm     uint32_t synd, int syndtype, mc_unum_t *unump)
4597aec1d6eScindi {
4607aec1d6eScindi 	mcamd_hdl_t mcamd;
4617aec1d6eScindi 	int rc;
4627aec1d6eScindi 
4637aec1d6eScindi 	mcamd_mkhdl(&mcamd);
4647aec1d6eScindi 
4657aec1d6eScindi 	rw_enter(&mc_lock, RW_READER);
4667aec1d6eScindi 
4674156fc34Sgavinm 	rc = mcamd_patounum(&mcamd, (mcamd_node_t *)mc_list, pa,
4684156fc34Sgavinm 	    valid_hi, valid_lo, synd, syndtype, unump);
4697aec1d6eScindi 
4707aec1d6eScindi #ifdef DEBUG
4717aec1d6eScindi 	/*
4727aec1d6eScindi 	 * Apply the reverse operation to verify the result.  If there is
4737aec1d6eScindi 	 * a problem complain but continue.
4747aec1d6eScindi 	 */
4757aec1d6eScindi 	if (rc == 0 && MCAMD_RC_OFFSET_VALID(unump->unum_offset)) {
4767aec1d6eScindi 		uint64_t rpa;
4777aec1d6eScindi 		if (mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump,
4787aec1d6eScindi 		    &rpa) != 0 || rpa != pa) {
4797aec1d6eScindi 			mcamd_dprintf(&mcamd, MCAMD_DBG_ERR,
4807aec1d6eScindi 			    "mcamd_patounum_wrap: offset calculation "
4817aec1d6eScindi 			    "verification for PA 0x%llx failed\n", pa);
4827aec1d6eScindi 		}
4837aec1d6eScindi 	}
4847aec1d6eScindi #endif
4857aec1d6eScindi 	rw_exit(&mc_lock);
4867aec1d6eScindi 
48720c794b3Sgavinm 	return (mcamd_cmierr(rc, &mcamd));
4887aec1d6eScindi }
4897aec1d6eScindi 
4907aec1d6eScindi static int
fmri2unum(nvlist_t * nvl,mc_unum_t * unump)4917aec1d6eScindi fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
4927aec1d6eScindi {
4937aec1d6eScindi 	int i;
4947aec1d6eScindi 	uint64_t offset;
495e4b86885SCheng Sean Ye 	nvlist_t *hcsp, **hcl;
4967aec1d6eScindi 	uint_t npr;
4977aec1d6eScindi 
498e4b86885SCheng Sean Ye 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
499e4b86885SCheng Sean Ye 	    (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
500e4b86885SCheng Sean Ye 	    &offset) != 0 && nvlist_lookup_uint64(hcsp,
501e4b86885SCheng Sean Ye 	    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
502e4b86885SCheng Sean Ye 	    nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
5037aec1d6eScindi 		return (0);
5047aec1d6eScindi 
5057aec1d6eScindi 
5067aec1d6eScindi 	bzero(unump, sizeof (mc_unum_t));
50720c794b3Sgavinm 	unump->unum_chan = MC_INVALNUM;
5087aec1d6eScindi 	for (i = 0; i < MC_UNUM_NDIMM; i++)
5098a40a695Sgavinm 		unump->unum_dimms[i] = MC_INVALNUM;
5107aec1d6eScindi 
5117aec1d6eScindi 	for (i = 0; i < npr; i++) {
5127aec1d6eScindi 		char *hcnm, *hcid;
5137aec1d6eScindi 		long v;
5147aec1d6eScindi 
5157aec1d6eScindi 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
5167aec1d6eScindi 		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
5177aec1d6eScindi 		    ddi_strtol(hcid, NULL, 0, &v) != 0)
5187aec1d6eScindi 			return (0);
5197aec1d6eScindi 
5207aec1d6eScindi 		if (strcmp(hcnm, "motherboard") == 0)
5217aec1d6eScindi 			unump->unum_board = (int)v;
5227aec1d6eScindi 		else if (strcmp(hcnm, "chip") == 0)
5237aec1d6eScindi 			unump->unum_chip = (int)v;
5247aec1d6eScindi 		else if (strcmp(hcnm, "memory-controller") == 0)
5257aec1d6eScindi 			unump->unum_mc = (int)v;
5267aec1d6eScindi 		else if (strcmp(hcnm, "chip-select") == 0)
5277aec1d6eScindi 			unump->unum_cs = (int)v;
5287aec1d6eScindi 		else if (strcmp(hcnm, "dimm") == 0)
5297aec1d6eScindi 			unump->unum_dimms[0] = (int)v;
5308a40a695Sgavinm 		else if (strcmp(hcnm, "rank") == 0)
5318a40a695Sgavinm 			unump->unum_rank = (int)v;
5327aec1d6eScindi 	}
5337aec1d6eScindi 
5347aec1d6eScindi 	unump->unum_offset = offset;
5357aec1d6eScindi 
5367aec1d6eScindi 	return (1);
5377aec1d6eScindi }
5387aec1d6eScindi 
5397aec1d6eScindi /*ARGSUSED*/
54020c794b3Sgavinm cmi_errno_t
mcamd_unumtopa_wrap(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)5417aec1d6eScindi mcamd_unumtopa_wrap(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
5427aec1d6eScindi {
5437aec1d6eScindi 	mcamd_hdl_t mcamd;
5447aec1d6eScindi 	int rc;
5457aec1d6eScindi 	mc_unum_t unum;
5467aec1d6eScindi 
54720c794b3Sgavinm 	ASSERT(unump == NULL || nvl == NULL);	/* enforced at cmi level */
5487aec1d6eScindi 
5497aec1d6eScindi 	if (unump == NULL) {
5507aec1d6eScindi 		if (!fmri2unum(nvl, &unum))
55120c794b3Sgavinm 			return (CMIERR_MC_INVALUNUM);
5527aec1d6eScindi 		unump = &unum;
5537aec1d6eScindi 	}
5547aec1d6eScindi 
5557aec1d6eScindi 	mcamd_mkhdl(&mcamd);
5567aec1d6eScindi 
5577aec1d6eScindi 	rw_enter(&mc_lock, RW_READER);
5587aec1d6eScindi 	rc = mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump, pap);
5597aec1d6eScindi 	rw_exit(&mc_lock);
5607aec1d6eScindi 
56120c794b3Sgavinm 	return (mcamd_cmierr(rc, &mcamd));
5627aec1d6eScindi }
5638a40a695Sgavinm 
5648a40a695Sgavinm static void
mc_ereport_dimm_resource(mc_unum_t * unump,nvlist_t * elems[],int * nump,mc_t * mc)565*074bb90dSTom Pothier mc_ereport_dimm_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump,
566*074bb90dSTom Pothier     mc_t *mc)
5678a40a695Sgavinm {
5688a40a695Sgavinm 	int i;
5698a40a695Sgavinm 
5708a40a695Sgavinm 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
5718a40a695Sgavinm 		if (unump->unum_dimms[i] == MC_INVALNUM)
5728a40a695Sgavinm 			break;
5738a40a695Sgavinm 
5748a40a695Sgavinm 		elems[(*nump)++] = fm_nvlist_create(NULL);
575*074bb90dSTom Pothier 
576*074bb90dSTom Pothier 		if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
577*074bb90dSTom Pothier 			fm_fmri_hc_create(elems[i], FM_HC_SCHEME_VERSION,
578*074bb90dSTom Pothier 			    NULL, NULL, mc->smb_bboard, 4,
579*074bb90dSTom Pothier 			    "chip", mc->smb_chipid,
580*074bb90dSTom Pothier 			    "memory-controller", unump->unum_mc,
581*074bb90dSTom Pothier 			    "dimm", unump->unum_dimms[i],
582*074bb90dSTom Pothier 			    "rank", unump->unum_rank);
583*074bb90dSTom Pothier 		} else {
584*074bb90dSTom Pothier 			fm_fmri_hc_set(elems[i], FM_HC_SCHEME_VERSION,
585*074bb90dSTom Pothier 			    NULL, NULL, 5,
586*074bb90dSTom Pothier 			    "motherboard",  unump->unum_board,
587*074bb90dSTom Pothier 			    "chip", unump->unum_chip,
588*074bb90dSTom Pothier 			    "memory-controller", unump->unum_mc,
589*074bb90dSTom Pothier 			    "dimm", unump->unum_dimms[i],
590*074bb90dSTom Pothier 			    "rank", unump->unum_rank);
591*074bb90dSTom Pothier 		}
5928a40a695Sgavinm 	}
5938a40a695Sgavinm }
5948a40a695Sgavinm 
5958a40a695Sgavinm static void
mc_ereport_cs_resource(mc_unum_t * unump,nvlist_t * elems[],int * nump,mc_t * mc)596*074bb90dSTom Pothier mc_ereport_cs_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump, mc_t *mc)
5978a40a695Sgavinm {
5988a40a695Sgavinm 	elems[0] = fm_nvlist_create(NULL);
599*074bb90dSTom Pothier 
600*074bb90dSTom Pothier 	if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
601*074bb90dSTom Pothier 		fm_fmri_hc_create(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL,
602*074bb90dSTom Pothier 		    mc->smb_bboard, 3,
603*074bb90dSTom Pothier 		    "chip", mc->smb_chipid,
604*074bb90dSTom Pothier 		    "memory-controller", unump->unum_mc,
605*074bb90dSTom Pothier 		    "chip-select", unump->unum_cs);
606*074bb90dSTom Pothier 	} else {
607*074bb90dSTom Pothier 		fm_fmri_hc_set(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL, 4,
608*074bb90dSTom Pothier 		    "motherboard",  unump->unum_board,
609*074bb90dSTom Pothier 		    "chip", unump->unum_chip,
610*074bb90dSTom Pothier 		    "memory-controller", unump->unum_mc,
611*074bb90dSTom Pothier 		    "chip-select", unump->unum_cs);
612*074bb90dSTom Pothier 	}
6138a40a695Sgavinm 	*nump = 1;
6148a40a695Sgavinm }
6158a40a695Sgavinm 
6168a40a695Sgavinm /*
6178a40a695Sgavinm  * Create the 'resource' payload member from the unum info.  If valid
6188a40a695Sgavinm  * dimm numbers are present in the unum info then create members
6198a40a695Sgavinm  * identifying the dimm and rank;  otherwise if a valid chip-select
6208a40a695Sgavinm  * number is indicated then create a member identifying the chip-select
6218a40a695Sgavinm  * topology node.
6228a40a695Sgavinm  */
6238a40a695Sgavinm static void
mc_ereport_add_resource(nvlist_t * payload,mc_unum_t * unump,mc_t * mc)624*074bb90dSTom Pothier mc_ereport_add_resource(nvlist_t *payload, mc_unum_t *unump, mc_t *mc)
6258a40a695Sgavinm {
6268a40a695Sgavinm 	nvlist_t *elems[MC_UNUM_NDIMM];
6278a40a695Sgavinm 	int nelems = 0;
6288a40a695Sgavinm 	int i;
6298a40a695Sgavinm 
6308a40a695Sgavinm 	if (unump->unum_dimms[0] != MC_INVALNUM)
631*074bb90dSTom Pothier 		mc_ereport_dimm_resource(unump, elems, &nelems, mc);
6328a40a695Sgavinm 	else if (unump->unum_cs != MC_INVALNUM)
633*074bb90dSTom Pothier 		mc_ereport_cs_resource(unump, elems, &nelems, mc);
6348a40a695Sgavinm 
6358a40a695Sgavinm 	if (nelems > 0) {
6368a40a695Sgavinm 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
6378a40a695Sgavinm 		    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
6388a40a695Sgavinm 
6398a40a695Sgavinm 		for (i = 0; i < nelems; i++)
6408a40a695Sgavinm 			fm_nvlist_destroy(elems[i], FM_NVA_FREE);
6418a40a695Sgavinm 	}
6428a40a695Sgavinm }
6438a40a695Sgavinm 
6448a40a695Sgavinm static void
mc_ereport_add_payload(nvlist_t * ereport,uint64_t members,mc_unum_t * unump,mc_t * mc)645*074bb90dSTom Pothier mc_ereport_add_payload(nvlist_t *ereport, uint64_t members, mc_unum_t *unump,
646*074bb90dSTom Pothier     mc_t *mc)
6478a40a695Sgavinm {
6488a40a695Sgavinm 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE &&
6498a40a695Sgavinm 	    unump != NULL)
650*074bb90dSTom Pothier 		mc_ereport_add_resource(ereport, unump, mc);
6518a40a695Sgavinm }
6528a40a695Sgavinm 
6538a40a695Sgavinm static nvlist_t *
mc_fmri_create(mc_t * mc)6548a40a695Sgavinm mc_fmri_create(mc_t *mc)
6558a40a695Sgavinm {
6568a40a695Sgavinm 	nvlist_t *nvl = fm_nvlist_create(NULL);
6578a40a695Sgavinm 
658*074bb90dSTom Pothier 	if (!x86gentopo_legacy && mc->smb_bboard != NULL) {
659*074bb90dSTom Pothier 		fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, NULL, NULL,
660*074bb90dSTom Pothier 		    mc->smb_bboard, 2,
661*074bb90dSTom Pothier 		    "chip", mc->smb_chipid,
662*074bb90dSTom Pothier 		    "memory-controller", 0);
663*074bb90dSTom Pothier 	} else {
664*074bb90dSTom Pothier 		fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3,
665*074bb90dSTom Pothier 		    "motherboard", 0,
666*074bb90dSTom Pothier 		    "chip", mc->mc_props.mcp_num,
667*074bb90dSTom Pothier 		    "memory-controller", 0);
668*074bb90dSTom Pothier 	}
6698a40a695Sgavinm 
6708a40a695Sgavinm 	return (nvl);
6718a40a695Sgavinm }
6728a40a695Sgavinm 
6738a40a695Sgavinm /*
6748a40a695Sgavinm  * Simple ereport generator for errors detected by the memory controller.
6758a40a695Sgavinm  * Posts an ereport of class ereport.cpu.amd.<class_sfx> with a resource nvlist
6768a40a695Sgavinm  * derived from the given mc_unum_t.  There are no other payload members.
6778a40a695Sgavinm  * The mc argument is used to formulate a detector and this mc should
6788a40a695Sgavinm  * correspond with that identified in the mc_unum_t.
6798a40a695Sgavinm  *
6808a40a695Sgavinm  * There is no control of which members to include the the resulting ereport -
6818a40a695Sgavinm  * it will be an ereport formed using the given class suffix, detector
6828a40a695Sgavinm  * indicated as the memory-controller and with a resource generated by
6838a40a695Sgavinm  * expanding the given mc_unum_t.
6848a40a695Sgavinm  *
6858a40a695Sgavinm  * We do not use any special nv allocator here and so this is not suitable
6868a40a695Sgavinm  * for use during panic.  It is intended for use during MC topology
6878a40a695Sgavinm  * discovery and other controlled circumstances.
6888a40a695Sgavinm  */
6898a40a695Sgavinm void
mcamd_ereport_post(mc_t * mc,const char * class_sfx,mc_unum_t * unump,uint64_t payload)6908a40a695Sgavinm mcamd_ereport_post(mc_t *mc, const char *class_sfx, mc_unum_t *unump,
6918a40a695Sgavinm     uint64_t payload)
6928a40a695Sgavinm {
6938a40a695Sgavinm 	nvlist_t *ereport, *detector;
6948a40a695Sgavinm 	char buf[FM_MAX_CLASS];
6958a40a695Sgavinm 
6968a40a695Sgavinm 	ereport = fm_nvlist_create(NULL);
6978a40a695Sgavinm 	detector = mc_fmri_create(mc);
6988a40a695Sgavinm 
6998a40a695Sgavinm 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s", FM_ERROR_CPU,
7008a40a695Sgavinm 	    "amd", class_sfx);
7018a40a695Sgavinm 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
7028a40a695Sgavinm 	    fm_ena_generate(gethrtime(), FM_ENA_FMT1), detector, NULL);
7038a40a695Sgavinm 	fm_nvlist_destroy(detector, FM_NVA_FREE);
7048a40a695Sgavinm 
705*074bb90dSTom Pothier 	mc_ereport_add_payload(ereport, payload, unump, mc);
7068a40a695Sgavinm 
7078a40a695Sgavinm 	(void) fm_ereport_post(ereport, EVCH_TRYHARD);
7088a40a695Sgavinm 	fm_nvlist_destroy(ereport, FM_NVA_FREE);
7098a40a695Sgavinm }
71020c794b3Sgavinm 
71120c794b3Sgavinm static const cmi_mc_ops_t mcamd_mc_ops = {
71220c794b3Sgavinm 	mcamd_patounum_wrap,	/* cmi_mc_patounum */
71320c794b3Sgavinm 	mcamd_unumtopa_wrap,	/* cmi_mc_unumtopa */
71420c794b3Sgavinm 	NULL			/* cmi_mc_logout */
71520c794b3Sgavinm };
71620c794b3Sgavinm 
71720c794b3Sgavinm void
mcamd_mc_register(cmi_hdl_t hdl,mc_t * mc)71820c794b3Sgavinm mcamd_mc_register(cmi_hdl_t hdl, mc_t *mc)
71920c794b3Sgavinm {
72020c794b3Sgavinm 	cmi_mc_register(hdl, &mcamd_mc_ops, mc);
72120c794b3Sgavinm }
722