17aec1d6eScindi /*
27aec1d6eScindi  * CDDL HEADER START
37aec1d6eScindi  *
47aec1d6eScindi  * The contents of this file are subject to the terms of the
53ad553a7Sgavinm  * Common Development and Distribution License (the "License").
63ad553a7Sgavinm  * 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 /*
23f32a9dd1SSrihari Venkatesan  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24*22e4c3acSKeith M Wesolowski  * Copyright 2022 Oxide Computer Co.
257aec1d6eScindi  */
267aec1d6eScindi 
277aec1d6eScindi #include <sys/conf.h>
287aec1d6eScindi #include <sys/ddi.h>
297aec1d6eScindi #include <sys/ddifm.h>
307aec1d6eScindi #include <sys/sunddi.h>
317aec1d6eScindi #include <sys/sunndi.h>
327aec1d6eScindi #include <sys/stat.h>
337aec1d6eScindi #include <sys/modctl.h>
347aec1d6eScindi #include <sys/types.h>
357aec1d6eScindi #include <sys/cpuvar.h>
367aec1d6eScindi #include <sys/cmn_err.h>
377aec1d6eScindi #include <sys/kmem.h>
387aec1d6eScindi #include <sys/cred.h>
397aec1d6eScindi #include <sys/ksynch.h>
407aec1d6eScindi #include <sys/rwlock.h>
41fb2f18f8Sesaxe #include <sys/pghw.h>
427aec1d6eScindi #include <sys/open.h>
437aec1d6eScindi #include <sys/policy.h>
447aec1d6eScindi #include <sys/x86_archext.h>
457aec1d6eScindi #include <sys/cpu_module.h>
468a40a695Sgavinm #include <qsort.h>
47e4b86885SCheng Sean Ye #include <sys/pci_cfgspace.h>
488a40a695Sgavinm #include <sys/mc.h>
497aec1d6eScindi #include <sys/mc_amd.h>
50074bb90dSTom Pothier #include <sys/smbios.h>
51074bb90dSTom Pothier #include <sys/pci.h>
527aec1d6eScindi #include <mcamd.h>
538a40a695Sgavinm #include <mcamd_dimmcfg.h>
548a40a695Sgavinm #include <mcamd_pcicfg.h>
557aec1d6eScindi #include <mcamd_api.h>
568a40a695Sgavinm #include <sys/fm/cpu/AMD.h>
57074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h>
58074bb90dSTom Pothier #include <sys/fm/protocol.h>
59074bb90dSTom Pothier #include <sys/fm/util.h>
607aec1d6eScindi 
6120c794b3Sgavinm /*
6220c794b3Sgavinm  * Set to prevent mc-amd from attaching.
6320c794b3Sgavinm  */
6420c794b3Sgavinm int mc_no_attach = 0;
6520c794b3Sgavinm 
668a40a695Sgavinm /*
678a40a695Sgavinm  * Of the 754/939/940 packages, only socket 940 supports quadrank registered
688a40a695Sgavinm  * dimms.  Unfortunately, no memory-controller register indicates the
698a40a695Sgavinm  * presence of quadrank dimm support or presence (i.e., in terms of number
708a40a695Sgavinm  * of slots per cpu, and chip-select lines per slot,  The following may be set
718a40a695Sgavinm  * in /etc/system to indicate the presence of quadrank support on a motherboard.
728a40a695Sgavinm  *
738a40a695Sgavinm  * There is no need to set this for F(1207) and S1g1.
748a40a695Sgavinm  */
758a40a695Sgavinm int mc_quadranksupport = 0;
767aec1d6eScindi 
778a40a695Sgavinm mc_t *mc_list, *mc_last;
787aec1d6eScindi krwlock_t mc_lock;
797aec1d6eScindi int mc_hold_attached = 1;
807aec1d6eScindi 
818a40a695Sgavinm #define	MAX(m, n) ((m) >= (n) ? (m) : (n))
828a40a695Sgavinm #define	MIN(m, n) ((m) <= (n) ? (m) : (n))
838a40a695Sgavinm 
8420c794b3Sgavinm /*
8520c794b3Sgavinm  * The following tuneable is used to determine the DRAM scrubbing rate.
8620c794b3Sgavinm  * The values range from 0x00-0x16 as described in the BKDG.  Zero
8720c794b3Sgavinm  * disables DRAM scrubbing.  Values above zero indicate rates in descending
8820c794b3Sgavinm  * order.
8920c794b3Sgavinm  *
9020c794b3Sgavinm  * The default value below is used on several Sun systems.  In the future
9120c794b3Sgavinm  * this code should assign values dynamically based on memory sizing.
9220c794b3Sgavinm  */
9320c794b3Sgavinm uint32_t mc_scrub_rate_dram = 0xd;	/* 64B every 163.8 us; 1GB per 45 min */
9420c794b3Sgavinm 
9520c794b3Sgavinm enum {
9620c794b3Sgavinm 	MC_SCRUB_BIOSDEFAULT,	/* retain system default value */
9720c794b3Sgavinm 	MC_SCRUB_FIXED,		/* assign mc_scrub_rate_* values */
9820c794b3Sgavinm 	MC_SCRUB_MAX		/* assign max of system and tunables */
9920c794b3Sgavinm } mc_scrub_policy = MC_SCRUB_MAX;
10020c794b3Sgavinm 
1017aec1d6eScindi static void
mc_snapshot_destroy(mc_t * mc)1027aec1d6eScindi mc_snapshot_destroy(mc_t *mc)
1037aec1d6eScindi {
1047aec1d6eScindi 	ASSERT(RW_LOCK_HELD(&mc_lock));
1057aec1d6eScindi 
1067aec1d6eScindi 	if (mc->mc_snapshot == NULL)
1077aec1d6eScindi 		return;
1087aec1d6eScindi 
1097aec1d6eScindi 	kmem_free(mc->mc_snapshot, mc->mc_snapshotsz);
1107aec1d6eScindi 	mc->mc_snapshot = NULL;
1118a40a695Sgavinm 	mc->mc_snapshotsz = 0;
1127aec1d6eScindi 	mc->mc_snapshotgen++;
1137aec1d6eScindi }
1147aec1d6eScindi 
1157aec1d6eScindi static int
mc_snapshot_update(mc_t * mc)1167aec1d6eScindi mc_snapshot_update(mc_t *mc)
1177aec1d6eScindi {
1187aec1d6eScindi 	ASSERT(RW_LOCK_HELD(&mc_lock));
1197aec1d6eScindi 
1207aec1d6eScindi 	if (mc->mc_snapshot != NULL)
1217aec1d6eScindi 		return (0);
1227aec1d6eScindi 
1237aec1d6eScindi 	if (nvlist_pack(mc->mc_nvl, &mc->mc_snapshot, &mc->mc_snapshotsz,
1247aec1d6eScindi 	    NV_ENCODE_XDR, KM_SLEEP) != 0)
1257aec1d6eScindi 		return (-1);
1267aec1d6eScindi 
1277aec1d6eScindi 	return (0);
1287aec1d6eScindi }
1297aec1d6eScindi 
1307aec1d6eScindi static mc_t *
mc_lookup_by_chipid(int chipid)1318a40a695Sgavinm mc_lookup_by_chipid(int chipid)
1327aec1d6eScindi {
1337aec1d6eScindi 	mc_t *mc;
1347aec1d6eScindi 
1357aec1d6eScindi 	ASSERT(RW_LOCK_HELD(&mc_lock));
1367aec1d6eScindi 
1377aec1d6eScindi 	for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
138a6604450Sesaxe 		if (mc->mc_props.mcp_num  == chipid)
1398a40a695Sgavinm 			return (mc);
1407aec1d6eScindi 	}
1417aec1d6eScindi 
1427aec1d6eScindi 	return (NULL);
1437aec1d6eScindi }
1447aec1d6eScindi 
1458a40a695Sgavinm /*
1468a40a695Sgavinm  * Read config register pairs into the two arrays provided on the given
1478a40a695Sgavinm  * handle and at offsets as follows:
1488a40a695Sgavinm  *
1498a40a695Sgavinm  *	Index	Array r1 offset			Array r2 offset
1508a40a695Sgavinm  *	0	r1addr				r2addr
1518a40a695Sgavinm  *	1	r1addr + incr			r2addr + incr
1528a40a695Sgavinm  *	2	r1addr + 2 * incr		r2addr + 2 * incr
1538a40a695Sgavinm  *	...
1548a40a695Sgavinm  *	n - 1	r1addr + (n - 1) * incr		r2addr + (n - 1) * incr
1558a40a695Sgavinm  *
1568a40a695Sgavinm  * The number of registers to read into the r1 array is r1n; the number
1578a40a695Sgavinm  * for the r2 array is r2n.
1588a40a695Sgavinm  */
1598a40a695Sgavinm static void
mc_prop_read_pair(mc_pcicfg_hdl_t cfghdl,uint32_t * r1,off_t r1addr,int r1n,uint32_t * r2,off_t r2addr,int r2n,off_t incr)1608a40a695Sgavinm mc_prop_read_pair(mc_pcicfg_hdl_t cfghdl, uint32_t *r1, off_t r1addr,
1618a40a695Sgavinm     int r1n, uint32_t *r2, off_t r2addr, int r2n, off_t incr)
1627aec1d6eScindi {
1638a40a695Sgavinm 	int i;
1647aec1d6eScindi 
1658a40a695Sgavinm 	for (i = 0; i < MAX(r1n, r2n); i++, r1addr += incr, r2addr += incr) {
1668a40a695Sgavinm 		if (i < r1n)
1678a40a695Sgavinm 			r1[i] = mc_pcicfg_get32(cfghdl, r1addr);
1688a40a695Sgavinm 		if (i < r2n)
1698a40a695Sgavinm 			r2[i] = mc_pcicfg_get32(cfghdl, r2addr);
1708a40a695Sgavinm 	}
1717aec1d6eScindi }
1727aec1d6eScindi 
17389e921d5SKuriakose Kuruvilla /*ARGSUSED*/
17489e921d5SKuriakose Kuruvilla static int
mc_nvl_add_socket_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)17589e921d5SKuriakose Kuruvilla mc_nvl_add_socket_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
17689e921d5SKuriakose Kuruvilla {
17789e921d5SKuriakose Kuruvilla 	uint32_t skt = *((uint32_t *)arg1);
17889e921d5SKuriakose Kuruvilla 	cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2;
17989e921d5SKuriakose Kuruvilla 
18089e921d5SKuriakose Kuruvilla 	if (cmi_hdl_getsockettype(whdl) == skt) {
18189e921d5SKuriakose Kuruvilla 		cmi_hdl_hold(whdl);	/* short-term hold */
18289e921d5SKuriakose Kuruvilla 		*hdlp = whdl;
18389e921d5SKuriakose Kuruvilla 		return (CMI_HDL_WALK_DONE);
18489e921d5SKuriakose Kuruvilla 	} else {
18589e921d5SKuriakose Kuruvilla 		return (CMI_HDL_WALK_NEXT);
18689e921d5SKuriakose Kuruvilla 	}
18789e921d5SKuriakose Kuruvilla }
1887aec1d6eScindi 
1898a40a695Sgavinm static void
mc_nvl_add_socket(nvlist_t * nvl,mc_t * mc)1908a40a695Sgavinm mc_nvl_add_socket(nvlist_t *nvl, mc_t *mc)
1918a40a695Sgavinm {
19289e921d5SKuriakose Kuruvilla 	cmi_hdl_t hdl = NULL;
19389e921d5SKuriakose Kuruvilla 	const char *s;
1947aec1d6eScindi 
19589e921d5SKuriakose Kuruvilla 	cmi_hdl_walk(mc_nvl_add_socket_cb, (void *)&mc->mc_socket,
19689e921d5SKuriakose Kuruvilla 	    (void *)&hdl, NULL);
19789e921d5SKuriakose Kuruvilla 	if (hdl == NULL)
19889e921d5SKuriakose Kuruvilla 		s = "Unknown";  /* no cpu for this chipid found */
19989e921d5SKuriakose Kuruvilla 	else
20089e921d5SKuriakose Kuruvilla 		s = cmi_hdl_getsocketstr(hdl);
2017aec1d6eScindi 
2028a40a695Sgavinm 	(void) nvlist_add_string(nvl, "socket", s);
20389e921d5SKuriakose Kuruvilla 
20489e921d5SKuriakose Kuruvilla 	if (hdl != NULL)
20589e921d5SKuriakose Kuruvilla 		cmi_hdl_rele(hdl);
2067aec1d6eScindi }
2077aec1d6eScindi 
2088a40a695Sgavinm static uint32_t
mc_ecc_enabled(mc_t * mc)2098a40a695Sgavinm mc_ecc_enabled(mc_t *mc)
2107aec1d6eScindi {
211*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = mc->mc_props.mcp_rev;
2128a40a695Sgavinm 	union mcreg_nbcfg nbcfg;
2137aec1d6eScindi 
2148a40a695Sgavinm 	MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
2157aec1d6eScindi 
21620c794b3Sgavinm 	return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ?
21720c794b3Sgavinm 	    MCREG_FIELD_F_preF(&nbcfg, EccEn) :
21820c794b3Sgavinm 	    MCREG_FIELD_F_revFG(&nbcfg, EccEn));
2198a40a695Sgavinm }
2207aec1d6eScindi 
2218a40a695Sgavinm static uint32_t
mc_ck_enabled(mc_t * mc)2228a40a695Sgavinm mc_ck_enabled(mc_t *mc)
2238a40a695Sgavinm {
224*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = mc->mc_props.mcp_rev;
2258a40a695Sgavinm 	union mcreg_nbcfg nbcfg;
2267aec1d6eScindi 
2278a40a695Sgavinm 	MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
2288a40a695Sgavinm 
22920c794b3Sgavinm 	return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ?
23020c794b3Sgavinm 	    MCREG_FIELD_F_preF(&nbcfg, ChipKillEccEn) :
23120c794b3Sgavinm 	    MCREG_FIELD_F_revFG(&nbcfg, ChipKillEccEn));
2327aec1d6eScindi }
2337aec1d6eScindi 
2347aec1d6eScindi static void
mc_nvl_add_ecctype(nvlist_t * nvl,mc_t * mc)2358a40a695Sgavinm mc_nvl_add_ecctype(nvlist_t *nvl, mc_t *mc)
2367aec1d6eScindi {
2378a40a695Sgavinm 	(void) nvlist_add_string(nvl, "ecc-type", mc_ecc_enabled(mc) ?
2388a40a695Sgavinm 	    (mc_ck_enabled(mc) ? "ChipKill 128/16" : "Normal 64/8") : "None");
2397aec1d6eScindi }
2407aec1d6eScindi 
2417aec1d6eScindi static void
mc_nvl_add_prop(nvlist_t * nvl,void * node,mcamd_propcode_t code,int reqval)2428a40a695Sgavinm mc_nvl_add_prop(nvlist_t *nvl, void *node, mcamd_propcode_t code, int reqval)
2437aec1d6eScindi {
2447aec1d6eScindi 	int valfound;
2457aec1d6eScindi 	uint64_t value;
2467aec1d6eScindi 	const char *name = mcamd_get_propname(code);
2477aec1d6eScindi 
2487aec1d6eScindi 	valfound = mcamd_get_numprop(NULL, (mcamd_node_t *)node, code, &value);
2497aec1d6eScindi 
2507aec1d6eScindi 	ASSERT(name != NULL && valfound);
2518a40a695Sgavinm 	if (name != NULL && valfound && (!reqval || value != MC_INVALNUM))
2527aec1d6eScindi 		(void) nvlist_add_uint64(nvl, name, value);
2537aec1d6eScindi }
2547aec1d6eScindi 
2559dd0f810Scindi static void
mc_nvl_add_cslist(nvlist_t * mcnvl,mc_t * mc)2569dd0f810Scindi mc_nvl_add_cslist(nvlist_t *mcnvl, mc_t *mc)
2577aec1d6eScindi {
2587aec1d6eScindi 	mc_cs_t *mccs = mc->mc_cslist;
2599dd0f810Scindi 	nvlist_t *cslist[MC_CHIP_NCS];
2607aec1d6eScindi 	int nelem, i;
2617aec1d6eScindi 
2627aec1d6eScindi 	for (nelem = 0; mccs != NULL; mccs = mccs->mccs_next, nelem++) {
2637aec1d6eScindi 		nvlist_t **csp = &cslist[nelem];
2648a40a695Sgavinm 		char csname[MCDCFG_CSNAMELEN];
2657aec1d6eScindi 
2667aec1d6eScindi 		(void) nvlist_alloc(csp, NV_UNIQUE_NAME, KM_SLEEP);
2678a40a695Sgavinm 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM, 0);
2688a40a695Sgavinm 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR, 0);
2698a40a695Sgavinm 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK, 0);
2708a40a695Sgavinm 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE, 0);
2718a40a695Sgavinm 
272715d5c9bSgavinm 		/*
273715d5c9bSgavinm 		 * It is possible for an mc_cs_t not to have associated
274715d5c9bSgavinm 		 * DIMM info if mcdcfg_lookup failed.
275715d5c9bSgavinm 		 */
276715d5c9bSgavinm 		if (mccs->mccs_csl[0] != NULL) {
277715d5c9bSgavinm 			mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM1, 1);
278715d5c9bSgavinm 			mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[0], csname,
279715d5c9bSgavinm 			    sizeof (csname));
280715d5c9bSgavinm 			(void) nvlist_add_string(*csp, "dimm1-csname", csname);
281715d5c9bSgavinm 		}
2828a40a695Sgavinm 
2838a40a695Sgavinm 		if (mccs->mccs_csl[1] != NULL) {
284715d5c9bSgavinm 			mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM2, 1);
2858a40a695Sgavinm 			mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[1], csname,
2868a40a695Sgavinm 			    sizeof (csname));
2878a40a695Sgavinm 			(void) nvlist_add_string(*csp, "dimm2-csname", csname);
2888a40a695Sgavinm 		}
2897aec1d6eScindi 	}
2907aec1d6eScindi 
291715d5c9bSgavinm 	/* Add cslist nvlist array even if zero members */
2927aec1d6eScindi 	(void) nvlist_add_nvlist_array(mcnvl, "cslist", cslist, nelem);
2937aec1d6eScindi 	for (i = 0; i < nelem; i++)
2947aec1d6eScindi 		nvlist_free(cslist[i]);
2959dd0f810Scindi }
2969dd0f810Scindi 
2979dd0f810Scindi static void
mc_nvl_add_dimmlist(nvlist_t * mcnvl,mc_t * mc)2989dd0f810Scindi mc_nvl_add_dimmlist(nvlist_t *mcnvl, mc_t *mc)
2999dd0f810Scindi {
3009dd0f810Scindi 	nvlist_t *dimmlist[MC_CHIP_NDIMM];
3019dd0f810Scindi 	mc_dimm_t *mcd;
3029dd0f810Scindi 	int nelem, i;
3037aec1d6eScindi 
3048a40a695Sgavinm 	for (nelem = 0, mcd = mc->mc_dimmlist; mcd != NULL;
3057aec1d6eScindi 	    mcd = mcd->mcd_next, nelem++) {
3067aec1d6eScindi 		nvlist_t **dimmp = &dimmlist[nelem];
3077aec1d6eScindi 		uint64_t csnums[MC_CHIP_DIMMRANKMAX];
3088a40a695Sgavinm 		char csname[4][MCDCFG_CSNAMELEN];
3098a40a695Sgavinm 		char *csnamep[4];
3108a40a695Sgavinm 		int ncs = 0;
3117aec1d6eScindi 
3127aec1d6eScindi 		(void) nvlist_alloc(dimmp, NV_UNIQUE_NAME, KM_SLEEP);
3137aec1d6eScindi 
3148a40a695Sgavinm 		mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM, 1);
3158a40a695Sgavinm 		mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_SIZE, 1);
3167aec1d6eScindi 
3177aec1d6eScindi 		for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
3188a40a695Sgavinm 			if (mcd->mcd_cs[i] != NULL) {
3198a40a695Sgavinm 				csnums[ncs] =
3208a40a695Sgavinm 				    mcd->mcd_cs[i]->mccs_props.csp_num;
3218a40a695Sgavinm 				mcdcfg_csname(mc->mc_socket, mcd->mcd_csl[i],
3228a40a695Sgavinm 				    csname[ncs], MCDCFG_CSNAMELEN);
3238a40a695Sgavinm 				csnamep[ncs] = csname[ncs];
3248a40a695Sgavinm 				ncs++;
3258a40a695Sgavinm 			}
3267aec1d6eScindi 		}
3277aec1d6eScindi 
3287aec1d6eScindi 		(void) nvlist_add_uint64_array(*dimmp, "csnums", csnums, ncs);
3298a40a695Sgavinm 		(void) nvlist_add_string_array(*dimmp, "csnames", csnamep, ncs);
3307aec1d6eScindi 	}
3317aec1d6eScindi 
332715d5c9bSgavinm 	/* Add dimmlist nvlist array even if zero members */
3337aec1d6eScindi 	(void) nvlist_add_nvlist_array(mcnvl, "dimmlist", dimmlist, nelem);
3347aec1d6eScindi 	for (i = 0; i < nelem; i++)
3357aec1d6eScindi 		nvlist_free(dimmlist[i]);
3369dd0f810Scindi }
3379dd0f810Scindi 
3389dd0f810Scindi static void
mc_nvl_add_htconfig(nvlist_t * mcnvl,mc_t * mc)3399dd0f810Scindi mc_nvl_add_htconfig(nvlist_t *mcnvl, mc_t *mc)
3409dd0f810Scindi {
3419dd0f810Scindi 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
3429dd0f810Scindi 	union mcreg_htroute *htrp = (union mcreg_htroute *)&mcr->mcr_htroute[0];
3439dd0f810Scindi 	union mcreg_nodeid *nip = (union mcreg_nodeid *)&mcr->mcr_htnodeid;
3449dd0f810Scindi 	union mcreg_unitid *uip = (union mcreg_unitid *)&mcr->mcr_htunitid;
3459dd0f810Scindi 	int ndcnt = HT_COHERENTNODES(nip);
3469dd0f810Scindi 	uint32_t BCRte[MC_CHIP_MAXNODES];
3479dd0f810Scindi 	uint32_t RPRte[MC_CHIP_MAXNODES];
3489dd0f810Scindi 	uint32_t RQRte[MC_CHIP_MAXNODES];
3499dd0f810Scindi 	nvlist_t *nvl;
3509dd0f810Scindi 	int i;
3519dd0f810Scindi 
3529dd0f810Scindi 	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
3539dd0f810Scindi 
3549dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "NodeId", MCREG_FIELD_CMN(nip, NodeId));
3559dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "CoherentNodes", HT_COHERENTNODES(nip));
3569dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "SbNode", MCREG_FIELD_CMN(nip, SbNode));
3579dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "LkNode", MCREG_FIELD_CMN(nip, LkNode));
3589dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "SystemCoreCount",
3599dd0f810Scindi 	    HT_SYSTEMCORECOUNT(nip));
3609dd0f810Scindi 
3619dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "C0Unit", MCREG_FIELD_CMN(uip, C0Unit));
3629dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "C1Unit", MCREG_FIELD_CMN(uip, C1Unit));
3639dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "McUnit", MCREG_FIELD_CMN(uip, McUnit));
3649dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "HbUnit", MCREG_FIELD_CMN(uip, HbUnit));
3659dd0f810Scindi 	(void) nvlist_add_uint32(nvl, "SbLink", MCREG_FIELD_CMN(uip, SbLink));
3669dd0f810Scindi 
3679dd0f810Scindi 	if (ndcnt <= MC_CHIP_MAXNODES) {
3689dd0f810Scindi 		for (i = 0; i < ndcnt; i++, htrp++) {
3699dd0f810Scindi 			BCRte[i] = MCREG_FIELD_CMN(htrp, BCRte);
3709dd0f810Scindi 			RPRte[i] = MCREG_FIELD_CMN(htrp, RPRte);
3719dd0f810Scindi 			RQRte[i] = MCREG_FIELD_CMN(htrp, RQRte);
3729dd0f810Scindi 		}
3739dd0f810Scindi 
3749dd0f810Scindi 		(void) nvlist_add_uint32_array(nvl, "BroadcastRoutes",
3759dd0f810Scindi 		    &BCRte[0], ndcnt);
3769dd0f810Scindi 		(void) nvlist_add_uint32_array(nvl, "ResponseRoutes",
3779dd0f810Scindi 		    &RPRte[0], ndcnt);
3789dd0f810Scindi 		(void) nvlist_add_uint32_array(nvl, "RequestRoutes",
3799dd0f810Scindi 		    &RQRte[0], ndcnt);
3809dd0f810Scindi 	}
3819dd0f810Scindi 
3829dd0f810Scindi 	(void) nvlist_add_nvlist(mcnvl, "htconfig", nvl);
3839dd0f810Scindi 	nvlist_free(nvl);
3849dd0f810Scindi }
3859dd0f810Scindi 
3869dd0f810Scindi static nvlist_t *
mc_nvl_create(mc_t * mc)3879dd0f810Scindi mc_nvl_create(mc_t *mc)
3889dd0f810Scindi {
3899dd0f810Scindi 	nvlist_t *mcnvl;
3909dd0f810Scindi 
3919dd0f810Scindi 	(void) nvlist_alloc(&mcnvl, NV_UNIQUE_NAME, KM_SLEEP);
3929dd0f810Scindi 
3939dd0f810Scindi 	/*
3949dd0f810Scindi 	 * Since this nvlist is used in populating the topo tree changes
3959dd0f810Scindi 	 * made here may propogate through to changed property names etc
3969dd0f810Scindi 	 * in the topo tree.  Some properties in the topo tree will be
3979dd0f810Scindi 	 * contracted via ARC, so be careful what you change here.
3989dd0f810Scindi 	 */
3999dd0f810Scindi 	(void) nvlist_add_uint8(mcnvl, MC_NVLIST_VERSTR, MC_NVLIST_VERS1);
4009dd0f810Scindi 
4019dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM, 0);
4029dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV, 0);
4039dd0f810Scindi 	(void) nvlist_add_string(mcnvl, "revname", mc->mc_revname);
4049dd0f810Scindi 	mc_nvl_add_socket(mcnvl, mc);
4059dd0f810Scindi 	mc_nvl_add_ecctype(mcnvl, mc);
4069dd0f810Scindi 
4079dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR, 0);
4089dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR, 0);
4099dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILEN, 0);
4109dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILSEL, 0);
4119dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSINTLVFCTR, 0);
4129dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAMHOLE_SIZE, 0);
4139dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH, 0);
4149dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAPREG, 0);
4159dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BANKSWZL, 0);
4169dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_MOD64MUX, 0);
4179dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_SPARECS, 1);
4189dd0f810Scindi 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BADCS, 1);
4199dd0f810Scindi 
4209dd0f810Scindi 	mc_nvl_add_cslist(mcnvl, mc);
4219dd0f810Scindi 	mc_nvl_add_dimmlist(mcnvl, mc);
4229dd0f810Scindi 	mc_nvl_add_htconfig(mcnvl, mc);
4237aec1d6eScindi 
4247aec1d6eScindi 	return (mcnvl);
4257aec1d6eScindi }
4267aec1d6eScindi 
4278a40a695Sgavinm /*
4288a40a695Sgavinm  * Link a dimm to its associated chip-selects and chip-select lines.
4298a40a695Sgavinm  * Total the size of all ranks of this dimm.
4308a40a695Sgavinm  */
4317aec1d6eScindi static void
mc_dimm_csadd(mc_t * mc,mc_dimm_t * mcd,mc_cs_t * mccs,const mcdcfg_csl_t * csl)4328a40a695Sgavinm mc_dimm_csadd(mc_t *mc, mc_dimm_t *mcd, mc_cs_t *mccs, const mcdcfg_csl_t *csl)
4337aec1d6eScindi {
4348a40a695Sgavinm 	int factor = (mc->mc_props.mcp_accwidth == 128) ? 2 : 1;
4358a40a695Sgavinm 	uint64_t sz = 0;
4367aec1d6eScindi 	int i;
4377aec1d6eScindi 
4388a40a695Sgavinm 	/* Skip to first unused rank slot */
4397aec1d6eScindi 	for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
4407aec1d6eScindi 		if (mcd->mcd_cs[i] == NULL) {
4417aec1d6eScindi 			mcd->mcd_cs[i] = mccs;
4428a40a695Sgavinm 			mcd->mcd_csl[i] = csl;
4438a40a695Sgavinm 			sz += mccs->mccs_props.csp_size / factor;
4447aec1d6eScindi 			break;
4458a40a695Sgavinm 		} else {
4468a40a695Sgavinm 			sz += mcd->mcd_cs[i]->mccs_props.csp_size / factor;
4477aec1d6eScindi 		}
4487aec1d6eScindi 	}
4498a40a695Sgavinm 
4507aec1d6eScindi 	ASSERT(i != MC_CHIP_DIMMRANKMAX);
4518a40a695Sgavinm 
4528a40a695Sgavinm 	mcd->mcd_size = sz;
4537aec1d6eScindi }
4547aec1d6eScindi 
4558a40a695Sgavinm /*
4568a40a695Sgavinm  * Create a dimm structure and call to link it to its associated chip-selects.
4578a40a695Sgavinm  */
4587aec1d6eScindi static mc_dimm_t *
mc_dimm_create(mc_t * mc,uint_t num)4598a40a695Sgavinm mc_dimm_create(mc_t *mc, uint_t num)
4607aec1d6eScindi {
4617aec1d6eScindi 	mc_dimm_t *mcd = kmem_zalloc(sizeof (mc_dimm_t), KM_SLEEP);
4627aec1d6eScindi 
4637aec1d6eScindi 	mcd->mcd_hdr.mch_type = MC_NT_DIMM;
4647aec1d6eScindi 	mcd->mcd_mc = mc;
4657aec1d6eScindi 	mcd->mcd_num = num;
4667aec1d6eScindi 
4677aec1d6eScindi 	return (mcd);
4687aec1d6eScindi }
4697aec1d6eScindi 
4707aec1d6eScindi /*
4718a40a695Sgavinm  * The chip-select structure includes an array of dimms associated with
4728a40a695Sgavinm  * that chip-select.  This function fills that array, and also builds
4738a40a695Sgavinm  * the list of all dimms on this memory controller mc_dimmlist.  The
4748a40a695Sgavinm  * caller has filled a structure with all there is to know about the
4758a40a695Sgavinm  * associated dimm(s).
4767aec1d6eScindi  */
4777aec1d6eScindi static void
mc_csdimms_create(mc_t * mc,mc_cs_t * mccs,mcdcfg_rslt_t * rsltp)4788a40a695Sgavinm mc_csdimms_create(mc_t *mc, mc_cs_t *mccs, mcdcfg_rslt_t *rsltp)
4797aec1d6eScindi {
4808a40a695Sgavinm 	mc_dimm_t *found[MC_CHIP_DIMMPERCS];
4817aec1d6eScindi 	mc_dimm_t *mcd;
4827aec1d6eScindi 	int nfound = 0;
4838a40a695Sgavinm 	int i;
4847aec1d6eScindi 
4857aec1d6eScindi 	/*
4867aec1d6eScindi 	 * Has some other chip-select already created this dimm or dimms?
4878a40a695Sgavinm 	 * If so then link to the dimm(s) from the mccs_dimm array,
4888a40a695Sgavinm 	 * record their topo numbers in the csp_dimmnums array, and link
4898a40a695Sgavinm 	 * the dimm(s) to the additional chip-select.
4907aec1d6eScindi 	 */
4918a40a695Sgavinm 	for (mcd = mc->mc_dimmlist; mcd != NULL; mcd = mcd->mcd_next) {
4928a40a695Sgavinm 		for (i = 0; i < rsltp->ndimm; i++) {
4938a40a695Sgavinm 			if (mcd->mcd_num == rsltp->dimm[i].toponum)
4948a40a695Sgavinm 				found[nfound++] = mcd;
4957aec1d6eScindi 		}
4967aec1d6eScindi 	}
4978a40a695Sgavinm 	ASSERT(nfound == 0 || nfound == rsltp->ndimm);
4988a40a695Sgavinm 
4998a40a695Sgavinm 	for (i = 0; i < rsltp->ndimm; i++) {
5008a40a695Sgavinm 		if (nfound == 0) {
5018a40a695Sgavinm 			mcd = mc_dimm_create(mc, rsltp->dimm[i].toponum);
5028a40a695Sgavinm 			if (mc->mc_dimmlist == NULL)
5038a40a695Sgavinm 				mc->mc_dimmlist = mcd;
5048a40a695Sgavinm 			else
5058a40a695Sgavinm 				mc->mc_dimmlast->mcd_next = mcd;
5068a40a695Sgavinm 			mc->mc_dimmlast = mcd;
5078a40a695Sgavinm 		} else {
5088a40a695Sgavinm 			mcd = found[i];
5098a40a695Sgavinm 		}
5107aec1d6eScindi 
5118a40a695Sgavinm 		mccs->mccs_dimm[i] = mcd;
5128a40a695Sgavinm 		mccs->mccs_csl[i] = rsltp->dimm[i].cslp;
5138a40a695Sgavinm 		mccs->mccs_props.csp_dimmnums[i] = mcd->mcd_num;
5148a40a695Sgavinm 		mc_dimm_csadd(mc, mcd, mccs, rsltp->dimm[i].cslp);
5157aec1d6eScindi 
5167aec1d6eScindi 	}
5177aec1d6eScindi 
5188a40a695Sgavinm 	/* The rank number is constant across all constituent dimm(s) */
5198a40a695Sgavinm 	mccs->mccs_props.csp_dimmrank = rsltp->dimm[0].cslp->csl_rank;
5207aec1d6eScindi }
5217aec1d6eScindi 
5227aec1d6eScindi /*
5238a40a695Sgavinm  * mc_dimmlist_create is called after we have discovered all enabled
5248a40a695Sgavinm  * (and spare or testfailed on revs F and G) chip-selects on the
5258a40a695Sgavinm  * given memory controller.  For each chip-select we must derive
5268a40a695Sgavinm  * the associated dimms, remembering that a chip-select csbase/csmask
5278a40a695Sgavinm  * pair may be associated with up to 2 chip-select lines (in 128 bit mode)
5288a40a695Sgavinm  * and that any one dimm may be associated with 1, 2, or 4 chip-selects
5298a40a695Sgavinm  * depending on whether it is single, dual or quadrank.
5307aec1d6eScindi  */
5317aec1d6eScindi static void
mc_dimmlist_create(mc_t * mc)5327aec1d6eScindi mc_dimmlist_create(mc_t *mc)
5337aec1d6eScindi {
5348a40a695Sgavinm 	union mcreg_dramcfg_hi *drcfghip =
5358a40a695Sgavinm 	    (union mcreg_dramcfg_hi *)(&mc->mc_cfgregs.mcr_dramcfghi);
5368a40a695Sgavinm 	mc_props_t *mcp = &mc->mc_props;
537*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = mcp->mcp_rev;
5387aec1d6eScindi 	mc_cs_t *mccs;
5398a40a695Sgavinm 	int r4 = 0, s4 = 0;
5407aec1d6eScindi 
5418a40a695Sgavinm 	/*
5428a40a695Sgavinm 	 * Are we dealing with quadrank registered dimms?
5438a40a695Sgavinm 	 *
5448a40a695Sgavinm 	 * For socket 940 we can't tell and we'll assume we're not.
5458a40a695Sgavinm 	 * This can be over-ridden by the admin in /etc/system by setting
5468a40a695Sgavinm 	 * mc_quadranksupport nonzero.  A possible optimisation in systems
5478a40a695Sgavinm 	 * that export an SMBIOS table would be to count the number of
5488a40a695Sgavinm 	 * dimm slots per cpu - more than 4 would indicate no quadrank support
5498a40a695Sgavinm 	 * and 4 or fewer would indicate that if we see any of the upper
5508a40a695Sgavinm 	 * chip-selects enabled then a quadrank dimm is present.
5518a40a695Sgavinm 	 *
5528a40a695Sgavinm 	 * For socket F(1207) we can check a bit in the dram config high reg.
5538a40a695Sgavinm 	 *
5548a40a695Sgavinm 	 * Other socket types do not support registered dimms.
5558a40a695Sgavinm 	 */
5568a40a695Sgavinm 	if (mc->mc_socket == X86_SOCKET_940)
5578a40a695Sgavinm 		r4 = mc_quadranksupport != 0;
5588a40a695Sgavinm 	else if (mc->mc_socket == X86_SOCKET_F1207)
55920c794b3Sgavinm 		r4 = MCREG_FIELD_F_revFG(drcfghip, FourRankRDimm);
5608a40a695Sgavinm 
5618a40a695Sgavinm 	/*
5628a40a695Sgavinm 	 * Are we dealing with quadrank SO-DIMMs?  These are supported
5638a40a695Sgavinm 	 * in AM2 and S1g1 packages only, but in all rev F/G cases we
5648a40a695Sgavinm 	 * can detect their presence via a bit in the dram config high reg.
5658a40a695Sgavinm 	 */
56620c794b3Sgavinm 	if (MC_REV_MATCH(rev, MC_F_REVS_FG))
56720c794b3Sgavinm 		s4 = MCREG_FIELD_F_revFG(drcfghip, FourRankSODimm);
5687aec1d6eScindi 
5697aec1d6eScindi 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
5708a40a695Sgavinm 		mcdcfg_rslt_t rslt;
5717aec1d6eScindi 
572715d5c9bSgavinm 		/*
573715d5c9bSgavinm 		 * If lookup fails we will not create dimm structures for
574715d5c9bSgavinm 		 * this chip-select.  In the mc_cs_t we will have both
575715d5c9bSgavinm 		 * csp_dimmnum members set to MC_INVALNUM and patounum
576715d5c9bSgavinm 		 * code will see from those that we do not have dimm info
577715d5c9bSgavinm 		 * for this chip-select.
578715d5c9bSgavinm 		 */
5798a40a695Sgavinm 		if (mcdcfg_lookup(rev, mcp->mcp_mod64mux, mcp->mcp_accwidth,
5808a40a695Sgavinm 		    mccs->mccs_props.csp_num, mc->mc_socket,
5818a40a695Sgavinm 		    r4, s4, &rslt) < 0)
5828a40a695Sgavinm 			continue;
5838a40a695Sgavinm 
5848a40a695Sgavinm 		mc_csdimms_create(mc, mccs, &rslt);
5857aec1d6eScindi 	}
5867aec1d6eScindi }
5877aec1d6eScindi 
5887aec1d6eScindi static mc_cs_t *
mc_cs_create(mc_t * mc,uint_t num,uint64_t base,uint64_t mask,size_t sz,int csbe,int spare,int testfail)5898a40a695Sgavinm mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz,
5908a40a695Sgavinm     int csbe, int spare, int testfail)
5917aec1d6eScindi {
5927aec1d6eScindi 	mc_cs_t *mccs = kmem_zalloc(sizeof (mc_cs_t), KM_SLEEP);
5938a40a695Sgavinm 	mccs_props_t *csp = &mccs->mccs_props;
5948a40a695Sgavinm 	int i;
5957aec1d6eScindi 
5967aec1d6eScindi 	mccs->mccs_hdr.mch_type = MC_NT_CS;
5977aec1d6eScindi 	mccs->mccs_mc = mc;
5988a40a695Sgavinm 	csp->csp_num = num;
5998a40a695Sgavinm 	csp->csp_base = base;
6008a40a695Sgavinm 	csp->csp_mask = mask;
6018a40a695Sgavinm 	csp->csp_size = sz;
6028a40a695Sgavinm 	csp->csp_csbe = csbe;
6038a40a695Sgavinm 	csp->csp_spare = spare;
6048a40a695Sgavinm 	csp->csp_testfail = testfail;
6058a40a695Sgavinm 
6068a40a695Sgavinm 	for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
6078a40a695Sgavinm 		csp->csp_dimmnums[i] = MC_INVALNUM;
6088a40a695Sgavinm 
6098a40a695Sgavinm 	if (spare)
6108a40a695Sgavinm 		mc->mc_props.mcp_sparecs = num;
6117aec1d6eScindi 
6127aec1d6eScindi 	return (mccs);
6137aec1d6eScindi }
6147aec1d6eScindi 
6158a40a695Sgavinm /*
6168a40a695Sgavinm  * For any cs# of this mc marked TestFail generate an ereport with
6178a40a695Sgavinm  * resource identifying the associated dimm(s).
6188a40a695Sgavinm  */
6198a40a695Sgavinm static void
mc_report_testfails(mc_t * mc)6208a40a695Sgavinm mc_report_testfails(mc_t *mc)
6218a40a695Sgavinm {
6228a40a695Sgavinm 	mc_unum_t unum;
6238a40a695Sgavinm 	mc_cs_t *mccs;
6248a40a695Sgavinm 	int i;
6258a40a695Sgavinm 
6268a40a695Sgavinm 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
6278a40a695Sgavinm 		if (mccs->mccs_props.csp_testfail) {
6288a40a695Sgavinm 			unum.unum_board = 0;
629a6604450Sesaxe 			unum.unum_chip = mc->mc_props.mcp_num;
6308a40a695Sgavinm 			unum.unum_mc = 0;
63120c794b3Sgavinm 			unum.unum_chan = MC_INVALNUM;
6328a40a695Sgavinm 			unum.unum_cs = mccs->mccs_props.csp_num;
6338a40a695Sgavinm 			unum.unum_rank = mccs->mccs_props.csp_dimmrank;
6348a40a695Sgavinm 			unum.unum_offset = MCAMD_RC_INVALID_OFFSET;
6358a40a695Sgavinm 			for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
6368a40a695Sgavinm 				unum.unum_dimms[i] = MC_INVALNUM;
6378a40a695Sgavinm 
6388a40a695Sgavinm 			mcamd_ereport_post(mc, FM_EREPORT_CPU_AMD_MC_TESTFAIL,
6398a40a695Sgavinm 			    &unum,
6408a40a695Sgavinm 			    FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL);
6418a40a695Sgavinm 		}
6428a40a695Sgavinm 	}
6438a40a695Sgavinm }
6448a40a695Sgavinm 
6459dd0f810Scindi /*
6469dd0f810Scindi  * Function 0 - HyperTransport Technology Configuration
6479dd0f810Scindi  */
6489dd0f810Scindi static void
mc_mkprops_htcfg(mc_pcicfg_hdl_t cfghdl,mc_t * mc)6499dd0f810Scindi mc_mkprops_htcfg(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
6509dd0f810Scindi {
6519dd0f810Scindi 	union mcreg_nodeid nodeid;
6529dd0f810Scindi 	off_t offset;
6539dd0f810Scindi 	int i;
6549dd0f810Scindi 
6559dd0f810Scindi 	mc->mc_cfgregs.mcr_htnodeid = MCREG_VAL32(&nodeid) =
6569dd0f810Scindi 	    mc_pcicfg_get32(cfghdl, MC_HT_REG_NODEID);
6579dd0f810Scindi 
6589dd0f810Scindi 	mc->mc_cfgregs.mcr_htunitid = mc_pcicfg_get32(cfghdl, MC_HT_REG_UNITID);
6599dd0f810Scindi 
6609dd0f810Scindi 	for (i = 0, offset = MC_HT_REG_RTBL_NODE_0;
6619dd0f810Scindi 	    i < HT_COHERENTNODES(&nodeid);
6629dd0f810Scindi 	    i++, offset += MC_HT_REG_RTBL_INCR)
6639dd0f810Scindi 		mc->mc_cfgregs.mcr_htroute[i] = mc_pcicfg_get32(cfghdl, offset);
6649dd0f810Scindi }
6659dd0f810Scindi 
6667aec1d6eScindi /*
6677aec1d6eScindi  * Function 1 Configuration - Address Map (see BKDG 3.4.4 DRAM Address Map)
6687aec1d6eScindi  *
6697aec1d6eScindi  * Read the Function 1 Address Map for each potential DRAM node.  The Base
6707aec1d6eScindi  * Address for a node gives the starting system address mapped at that node,
6717aec1d6eScindi  * and the limit gives the last valid address mapped at that node.  Regions for
6727aec1d6eScindi  * different nodes should not overlap, unless node-interleaving is enabled.
6737aec1d6eScindi  * The base register also indicates the node-interleaving settings (IntlvEn).
6747aec1d6eScindi  * The limit register includes IntlvSel which determines which 4K blocks will
6757aec1d6eScindi  * be routed to this node and the destination node ID for addresses that fall
6767aec1d6eScindi  * within the [base, limit] range - this must match the pair number.
6777aec1d6eScindi  */
6787aec1d6eScindi static void
mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl,mc_t * mc)6798a40a695Sgavinm mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
6807aec1d6eScindi {
68120c794b3Sgavinm 	union mcreg_drambase basereg;
68220c794b3Sgavinm 	union mcreg_dramlimit limreg;
6837aec1d6eScindi 	mc_props_t *mcp = &mc->mc_props;
6848a40a695Sgavinm 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
6858a40a695Sgavinm 	union mcreg_dramhole hole;
68620c794b3Sgavinm 	int nodeid = mc->mc_props.mcp_num;
6877aec1d6eScindi 
68820c794b3Sgavinm 	mcr->mcr_drambase = MCREG_VAL32(&basereg) = mc_pcicfg_get32(cfghdl,
68920c794b3Sgavinm 	    MC_AM_REG_DRAMBASE_0 + nodeid * MC_AM_REG_DRAM_INCR);
6908a40a695Sgavinm 
69120c794b3Sgavinm 	mcr->mcr_dramlimit = MCREG_VAL32(&limreg) = mc_pcicfg_get32(cfghdl,
69220c794b3Sgavinm 	    MC_AM_REG_DRAMLIM_0 + nodeid * MC_AM_REG_DRAM_INCR);
6938a40a695Sgavinm 
69420c794b3Sgavinm 	/*
69520c794b3Sgavinm 	 * Derive some "cooked" properties for nodes that have a range of
69620c794b3Sgavinm 	 * physical addresses that are read or write enabled and for which
69720c794b3Sgavinm 	 * the DstNode matches the node we are attaching.
69820c794b3Sgavinm 	 */
69920c794b3Sgavinm 	if (MCREG_FIELD_CMN(&limreg, DRAMLimiti) != 0 &&
70020c794b3Sgavinm 	    MCREG_FIELD_CMN(&limreg, DstNode) == nodeid &&
70120c794b3Sgavinm 	    (MCREG_FIELD_CMN(&basereg, WE) || MCREG_FIELD_CMN(&basereg, RE))) {
70220c794b3Sgavinm 		mcp->mcp_base = MC_DRAMBASE(&basereg);
70320c794b3Sgavinm 		mcp->mcp_lim = MC_DRAMLIM(&limreg);
70420c794b3Sgavinm 		mcp->mcp_ilen = MCREG_FIELD_CMN(&basereg, IntlvEn);
70520c794b3Sgavinm 		mcp->mcp_ilsel = MCREG_FIELD_CMN(&limreg, IntlvSel);
7067aec1d6eScindi 	}
7077aec1d6eScindi 
7087aec1d6eScindi 	/*
7097aec1d6eScindi 	 * The Function 1 DRAM Hole Address Register tells us which node(s)
7107aec1d6eScindi 	 * own the DRAM space that is hoisted above 4GB, together with the
7118a40a695Sgavinm 	 * hole base and offset for this node.  This was introduced in
7128a40a695Sgavinm 	 * revision E.
7137aec1d6eScindi 	 */
71420c794b3Sgavinm 	if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_F_REV_E)) {
71520c794b3Sgavinm 		mcr->mcr_dramhole = MCREG_VAL32(&hole) =
7168a40a695Sgavinm 		    mc_pcicfg_get32(cfghdl, MC_AM_REG_HOLEADDR);
7178a40a695Sgavinm 
71820c794b3Sgavinm 		if (MCREG_FIELD_CMN(&hole, DramHoleValid))
7198a40a695Sgavinm 			mcp->mcp_dramhole_size = MC_DRAMHOLE_SIZE(&hole);
7208a40a695Sgavinm 	}
7218a40a695Sgavinm }
7228a40a695Sgavinm 
7238a40a695Sgavinm /*
7248a40a695Sgavinm  * Read some function 3 parameters via PCI Mechanism 1 accesses (which
7258a40a695Sgavinm  * will serialize any NB accesses).
7268a40a695Sgavinm  */
7278a40a695Sgavinm static void
mc_getmiscctl(mc_t * mc)7288a40a695Sgavinm mc_getmiscctl(mc_t *mc)
7298a40a695Sgavinm {
730*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = mc->mc_props.mcp_rev;
7318a40a695Sgavinm 	union mcreg_nbcfg nbcfg;
7328a40a695Sgavinm 	union mcreg_sparectl sparectl;
7338a40a695Sgavinm 
7348a40a695Sgavinm 	mc->mc_cfgregs.mcr_nbcfg = MCREG_VAL32(&nbcfg) =
7358a40a695Sgavinm 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
7368a40a695Sgavinm 
73720c794b3Sgavinm 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
7388a40a695Sgavinm 		mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl) =
7398a40a695Sgavinm 		    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
7408a40a695Sgavinm 		    MC_CTL_REG_SPARECTL);
7418a40a695Sgavinm 
74220c794b3Sgavinm 		if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) {
7438a40a695Sgavinm 			mc->mc_props.mcp_badcs =
74420c794b3Sgavinm 			    MCREG_FIELD_F_revFG(&sparectl, BadDramCs);
7458a40a695Sgavinm 		}
7468a40a695Sgavinm 	}
7477aec1d6eScindi }
7487aec1d6eScindi 
7498a40a695Sgavinm static int
csbasecmp(mc_cs_t ** csapp,mc_cs_t ** csbpp)7508a40a695Sgavinm csbasecmp(mc_cs_t **csapp, mc_cs_t **csbpp)
7518a40a695Sgavinm {
7528a40a695Sgavinm 	uint64_t basea = (*csapp)->mccs_props.csp_base;
7538a40a695Sgavinm 	uint64_t baseb = (*csbpp)->mccs_props.csp_base;
7548a40a695Sgavinm 
7558a40a695Sgavinm 	if (basea == baseb)
7568a40a695Sgavinm 		return (0);
7578a40a695Sgavinm 	else if (basea < baseb)
7588a40a695Sgavinm 		return (-1);
7598a40a695Sgavinm 	else
7608a40a695Sgavinm 		return (1);
7618a40a695Sgavinm }
7628a40a695Sgavinm 
7638a40a695Sgavinm /*
7648a40a695Sgavinm  * The following are for use in simulating TestFail for a chip-select
7658a40a695Sgavinm  * without poking at the hardware (which tends to get upset if you do
7668a40a695Sgavinm  * since the BIOS needs to restart to map a failed cs out).  For internal
7678a40a695Sgavinm  * testing only!  Note that setting these does not give the full experience -
7688a40a695Sgavinm  * the select chip-select *is* enabled and can give errors etc and the
7698a40a695Sgavinm  * patounum logic will get confused.
7708a40a695Sgavinm  */
7718a40a695Sgavinm int testfail_mcnum = -1;
7728a40a695Sgavinm int testfail_csnum = -1;
7738a40a695Sgavinm 
7747aec1d6eScindi /*
7757aec1d6eScindi  * Function 2 configuration - DRAM Controller
7767aec1d6eScindi  */
7777aec1d6eScindi static void
mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl,mc_t * mc)7788a40a695Sgavinm mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
7797aec1d6eScindi {
7808a40a695Sgavinm 	union mcreg_csbase base[MC_CHIP_NCS];
7818a40a695Sgavinm 	union mcreg_csmask mask[MC_CHIP_NCS];
7828a40a695Sgavinm 	union mcreg_dramcfg_lo drcfg_lo;
7838a40a695Sgavinm 	union mcreg_dramcfg_hi drcfg_hi;
7848a40a695Sgavinm 	union mcreg_drammisc drmisc;
7858a40a695Sgavinm 	union mcreg_bankaddrmap baddrmap;
7867aec1d6eScindi 	mc_props_t *mcp = &mc->mc_props;
7878a40a695Sgavinm 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
7888a40a695Sgavinm 	int maskdivisor;
7898a40a695Sgavinm 	int wide = 0;
790*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = mc->mc_props.mcp_rev;
7917aec1d6eScindi 	int i;
7927aec1d6eScindi 	mcamd_hdl_t hdl;
7937aec1d6eScindi 
7947aec1d6eScindi 	mcamd_mkhdl(&hdl);	/* to call into common code */
7957aec1d6eScindi 
7967aec1d6eScindi 	/*
7978a40a695Sgavinm 	 * Read Function 2 DRAM Configuration High and Low registers.  The High
7988a40a695Sgavinm 	 * part is mostly concerned with memory clocks etc and we'll not have
7997aec1d6eScindi 	 * any use for that.  The Low component tells us if ECC is enabled,
8007aec1d6eScindi 	 * if we're in 64- or 128-bit MC mode, how the upper chip-selects
8017aec1d6eScindi 	 * are mapped, which chip-select pairs are using x4 parts, etc.
8027aec1d6eScindi 	 */
8038a40a695Sgavinm 	MCREG_VAL32(&drcfg_lo) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGLO);
8048a40a695Sgavinm 	MCREG_VAL32(&drcfg_hi) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGHI);
8058a40a695Sgavinm 	mcr->mcr_dramcfglo = MCREG_VAL32(&drcfg_lo);
8068a40a695Sgavinm 	mcr->mcr_dramcfghi = MCREG_VAL32(&drcfg_hi);
8077aec1d6eScindi 
8088a40a695Sgavinm 	/*
8098a40a695Sgavinm 	 * Note the DRAM controller width.  The 64/128 bit is in a different
8108a40a695Sgavinm 	 * bit position for revision F and G.
8118a40a695Sgavinm 	 */
81220c794b3Sgavinm 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
81320c794b3Sgavinm 		wide = MCREG_FIELD_F_revFG(&drcfg_lo, Width128);
8148a40a695Sgavinm 	} else {
81520c794b3Sgavinm 		wide = MCREG_FIELD_F_preF(&drcfg_lo, Width128);
8168a40a695Sgavinm 	}
8177aec1d6eScindi 	mcp->mcp_accwidth = wide ? 128 : 64;
8187aec1d6eScindi 
8197aec1d6eScindi 	/*
8208a40a695Sgavinm 	 * Read Function 2 DRAM Controller Miscellaenous Regsiter for those
8218a40a695Sgavinm 	 * revs that support it.  This include the Mod64Mux indication on
8228a40a695Sgavinm 	 * these revs - for rev E it is in DRAM config low.
8237aec1d6eScindi 	 */
82420c794b3Sgavinm 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
8258a40a695Sgavinm 		mcr->mcr_drammisc = MCREG_VAL32(&drmisc) =
8268a40a695Sgavinm 		    mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMMISC);
82720c794b3Sgavinm 		mcp->mcp_mod64mux = MCREG_FIELD_F_revFG(&drmisc, Mod64Mux);
82820c794b3Sgavinm 	} else if (MC_REV_MATCH(rev, MC_F_REV_E)) {
82920c794b3Sgavinm 		mcp->mcp_mod64mux = MCREG_FIELD_F_preF(&drcfg_lo, Mod64BitMux);
8308a40a695Sgavinm 	}
8317aec1d6eScindi 
8327aec1d6eScindi 	/*
8338a40a695Sgavinm 	 * Read Function 2 DRAM Bank Address Mapping.  This encodes the
8348a40a695Sgavinm 	 * type of DIMM module in use for each chip-select pair.
8358a40a695Sgavinm 	 * Prior ro revision F it also tells us whether BankSwizzle mode
8368a40a695Sgavinm 	 * is enabled - in rev F that has moved to dram config hi register.
8377aec1d6eScindi 	 */
8388a40a695Sgavinm 	mcp->mcp_csbankmapreg = MCREG_VAL32(&baddrmap) =
8398a40a695Sgavinm 	    mc_pcicfg_get32(cfghdl, MC_DC_REG_BANKADDRMAP);
8407aec1d6eScindi 
8417aec1d6eScindi 	/*
8428a40a695Sgavinm 	 * Determine whether bank swizzle mode is active.  Bank swizzling was
8438a40a695Sgavinm 	 * introduced as an option in rev E,  but the bit that indicates it
8448a40a695Sgavinm 	 * is enabled has moved in revs F/G.
8458a40a695Sgavinm 	 */
84620c794b3Sgavinm 	if (MC_REV_MATCH(rev, MC_F_REV_E)) {
84720c794b3Sgavinm 		mcp->mcp_bnkswzl =
84820c794b3Sgavinm 		    MCREG_FIELD_F_preF(&baddrmap, BankSwizzleMode);
84920c794b3Sgavinm 	} else if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
85020c794b3Sgavinm 		mcp->mcp_bnkswzl = MCREG_FIELD_F_revFG(&drcfg_hi,
8518a40a695Sgavinm 		    BankSwizzleMode);
8528a40a695Sgavinm 	}
8538a40a695Sgavinm 
8548a40a695Sgavinm 	/*
8558a40a695Sgavinm 	 * Read the DRAM CS Base and DRAM CS Mask registers.  Revisions prior
8568a40a695Sgavinm 	 * to F have an equal number of base and mask registers; revision F
8578a40a695Sgavinm 	 * has twice as many base registers as masks.
8588a40a695Sgavinm 	 */
85920c794b3Sgavinm 	maskdivisor = MC_REV_MATCH(rev, MC_F_REVS_FG) ? 2 : 1;
8608a40a695Sgavinm 
8618a40a695Sgavinm 	mc_prop_read_pair(cfghdl,
8628a40a695Sgavinm 	    (uint32_t *)base, MC_DC_REG_CSBASE_0, MC_CHIP_NCS,
8638a40a695Sgavinm 	    (uint32_t *)mask, MC_DC_REG_CSMASK_0, MC_CHIP_NCS / maskdivisor,
8648a40a695Sgavinm 	    MC_DC_REG_CS_INCR);
8658a40a695Sgavinm 
8668a40a695Sgavinm 	/*
8678a40a695Sgavinm 	 * Create a cs node for each enabled chip-select as well as
8688a40a695Sgavinm 	 * any appointed online spare chip-selects and for any that have
8698a40a695Sgavinm 	 * failed test.
8707aec1d6eScindi 	 */
8717aec1d6eScindi 	for (i = 0; i < MC_CHIP_NCS; i++) {
8727aec1d6eScindi 		mc_cs_t *mccs;
8738a40a695Sgavinm 		uint64_t csbase, csmask;
8747aec1d6eScindi 		size_t sz;
8758a40a695Sgavinm 		int csbe, spare, testfail;
8767aec1d6eScindi 
87720c794b3Sgavinm 		if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
87820c794b3Sgavinm 			csbe = MCREG_FIELD_F_revFG(&base[i], CSEnable);
87920c794b3Sgavinm 			spare = MCREG_FIELD_F_revFG(&base[i], Spare);
88020c794b3Sgavinm 			testfail = MCREG_FIELD_F_revFG(&base[i], TestFail);
8818a40a695Sgavinm 		} else {
88220c794b3Sgavinm 			csbe = MCREG_FIELD_F_preF(&base[i], CSEnable);
8838a40a695Sgavinm 			spare = 0;
8848a40a695Sgavinm 			testfail = 0;
8857aec1d6eScindi 		}
8867aec1d6eScindi 
8878a40a695Sgavinm 		/* Testing hook */
8888a40a695Sgavinm 		if (testfail_mcnum != -1 && testfail_csnum != -1 &&
8898a40a695Sgavinm 		    mcp->mcp_num == testfail_mcnum && i == testfail_csnum) {
8908a40a695Sgavinm 			csbe = spare = 0;
8918a40a695Sgavinm 			testfail = 1;
8928a40a695Sgavinm 			cmn_err(CE_NOTE, "Pretending MC %d CS %d failed test",
8938a40a695Sgavinm 			    testfail_mcnum, testfail_csnum);
8948a40a695Sgavinm 		}
8958a40a695Sgavinm 
8968a40a695Sgavinm 		/*
8978a40a695Sgavinm 		 * If the chip-select is not enabled then skip it unless
8988a40a695Sgavinm 		 * it is a designated online spare or is marked with TestFail.
8998a40a695Sgavinm 		 */
9008a40a695Sgavinm 		if (!csbe && !(spare || testfail))
9017aec1d6eScindi 			continue;
9027aec1d6eScindi 
9038a40a695Sgavinm 		/*
9048a40a695Sgavinm 		 * For an enabled or spare chip-select the Bank Address Mapping
9058a40a695Sgavinm 		 * register will be valid as will the chip-select mask.  The
9068a40a695Sgavinm 		 * base will not be valid but we'll read and store it anyway.
9078a40a695Sgavinm 		 * We will not know whether the spare is already swapped in
9088a40a695Sgavinm 		 * until MC function 3 attaches.
9098a40a695Sgavinm 		 */
9108a40a695Sgavinm 		if (csbe || spare) {
9118a40a695Sgavinm 			if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0)
9128a40a695Sgavinm 				continue;
9138a40a695Sgavinm 			csbase = MC_CSBASE(&base[i], rev);
9148a40a695Sgavinm 			csmask = MC_CSMASK(&mask[i / maskdivisor], rev);
9158a40a695Sgavinm 		} else {
9168a40a695Sgavinm 			sz = 0;
9178a40a695Sgavinm 			csbase = csmask = 0;
9188a40a695Sgavinm 		}
9198a40a695Sgavinm 
9208a40a695Sgavinm 		mccs = mc_cs_create(mc, i, csbase, csmask, sz,
9218a40a695Sgavinm 		    csbe, spare, testfail);
9227aec1d6eScindi 
9237aec1d6eScindi 		if (mc->mc_cslist == NULL)
9247aec1d6eScindi 			mc->mc_cslist = mccs;
9257aec1d6eScindi 		else
9267aec1d6eScindi 			mc->mc_cslast->mccs_next = mccs;
9277aec1d6eScindi 		mc->mc_cslast = mccs;
9287aec1d6eScindi 
9298a40a695Sgavinm 		mccs->mccs_cfgregs.csr_csbase = MCREG_VAL32(&base[i]);
9308a40a695Sgavinm 		mccs->mccs_cfgregs.csr_csmask =
9318a40a695Sgavinm 		    MCREG_VAL32(&mask[i / maskdivisor]);
9328a40a695Sgavinm 
9337aec1d6eScindi 		/*
9347aec1d6eScindi 		 * Check for cs bank interleaving - some bits clear in the
9357aec1d6eScindi 		 * lower mask.  All banks must/will have the same lomask bits
9367aec1d6eScindi 		 * if cs interleaving is active.
9377aec1d6eScindi 		 */
9388a40a695Sgavinm 		if (csbe && !mcp->mcp_csintlvfctr) {
9397aec1d6eScindi 			int bitno, ibits = 0;
9408a40a695Sgavinm 			for (bitno = MC_CSMASKLO_LOBIT(rev);
9418a40a695Sgavinm 			    bitno <= MC_CSMASKLO_HIBIT(rev); bitno++) {
9427aec1d6eScindi 				if (!(csmask & (1 << bitno)))
9437aec1d6eScindi 					ibits++;
9447aec1d6eScindi 			}
9458a40a695Sgavinm 			mcp->mcp_csintlvfctr = 1 << ibits;
9468a40a695Sgavinm 		}
9478a40a695Sgavinm 	}
9488a40a695Sgavinm 
9498a40a695Sgavinm 	/*
9508a40a695Sgavinm 	 * If there is no chip-select interleave on this node determine
9518a40a695Sgavinm 	 * whether the chip-select ranks are contiguous or if there
9528a40a695Sgavinm 	 * is a hole.
9538a40a695Sgavinm 	 */
9548a40a695Sgavinm 	if (mcp->mcp_csintlvfctr == 1) {
9558a40a695Sgavinm 		mc_cs_t *csp[MC_CHIP_NCS];
9568a40a695Sgavinm 		mc_cs_t *mccs;
9578a40a695Sgavinm 		int ncsbe = 0;
9588a40a695Sgavinm 
9598a40a695Sgavinm 		for (mccs = mc->mc_cslist; mccs != NULL;
9608a40a695Sgavinm 		    mccs = mccs->mccs_next) {
9618a40a695Sgavinm 			if (mccs->mccs_props.csp_csbe)
9628a40a695Sgavinm 				csp[ncsbe++] = mccs;
9638a40a695Sgavinm 		}
9648a40a695Sgavinm 
9658a40a695Sgavinm 		if (ncsbe != 0) {
9668a40a695Sgavinm 			qsort((void *)csp, ncsbe, sizeof (mc_cs_t *),
9678a40a695Sgavinm 			    (int (*)(const void *, const void *))csbasecmp);
9688a40a695Sgavinm 
9698a40a695Sgavinm 			for (i = 1; i < ncsbe; i++) {
9708a40a695Sgavinm 				if (csp[i]->mccs_props.csp_base !=
9718a40a695Sgavinm 				    csp[i - 1]->mccs_props.csp_base +
9728a40a695Sgavinm 				    csp[i - 1]->mccs_props.csp_size)
9738a40a695Sgavinm 					mc->mc_csdiscontig = 1;
9748a40a695Sgavinm 			}
9757aec1d6eScindi 		}
9767aec1d6eScindi 	}
9777aec1d6eScindi 
9788a40a695Sgavinm 
9797aec1d6eScindi 	/*
9808a40a695Sgavinm 	 * Since we do not attach to MC function 3 go ahead and read some
9818a40a695Sgavinm 	 * config parameters from it now.
9828a40a695Sgavinm 	 */
9838a40a695Sgavinm 	mc_getmiscctl(mc);
9848a40a695Sgavinm 
9858a40a695Sgavinm 	/*
9868a40a695Sgavinm 	 * Now that we have discovered all enabled/spare/testfail chip-selects
9878a40a695Sgavinm 	 * we divine the associated DIMM configuration.
9887aec1d6eScindi 	 */
9897aec1d6eScindi 	mc_dimmlist_create(mc);
9907aec1d6eScindi }
9917aec1d6eScindi 
9927aec1d6eScindi typedef struct mc_bind_map {
9938a40a695Sgavinm 	const char *bm_bindnm;	 /* attachment binding name */
9948a40a695Sgavinm 	enum mc_funcnum bm_func; /* PCI config space function number for bind */
9958a40a695Sgavinm 	const char *bm_model;	 /* value for device node model property */
9968a40a695Sgavinm 	void (*bm_mkprops)(mc_pcicfg_hdl_t, mc_t *);
9977aec1d6eScindi } mc_bind_map_t;
9987aec1d6eScindi 
9998a40a695Sgavinm /*
10008a40a695Sgavinm  * Do not attach to MC function 3 - agpgart already attaches to that.
10018a40a695Sgavinm  * Function 3 may be a good candidate for a nexus driver to fan it out
10028a40a695Sgavinm  * into virtual devices by functionality.  We will use pci_mech1_getl
10038a40a695Sgavinm  * to retrieve the function 3 parameters we require.
10048a40a695Sgavinm  */
10058a40a695Sgavinm 
10067aec1d6eScindi static const mc_bind_map_t mc_bind_map[] = {
10077aec1d6eScindi 	{ MC_FUNC_HTCONFIG_BINDNM, MC_FUNC_HTCONFIG,
10089dd0f810Scindi 	    "AMD Memory Controller (HT Configuration)", mc_mkprops_htcfg },
10097aec1d6eScindi 	{ MC_FUNC_ADDRMAP_BINDNM, MC_FUNC_ADDRMAP,
10108a40a695Sgavinm 	    "AMD Memory Controller (Address Map)", mc_mkprops_addrmap },
10117aec1d6eScindi 	{ MC_FUNC_DRAMCTL_BINDNM, MC_FUNC_DRAMCTL,
10128a40a695Sgavinm 	    "AMD Memory Controller (DRAM Controller & HT Trace)",
10138a40a695Sgavinm 	    mc_mkprops_dramctl },
10147aec1d6eScindi 	NULL
10157aec1d6eScindi };
10167aec1d6eScindi 
10177aec1d6eScindi /*ARGSUSED*/
10187aec1d6eScindi static int
mc_open(dev_t * devp,int flag,int otyp,cred_t * credp)10197aec1d6eScindi mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
10207aec1d6eScindi {
10217aec1d6eScindi 	if (otyp != OTYP_CHR)
10227aec1d6eScindi 		return (EINVAL);
10237aec1d6eScindi 
10247aec1d6eScindi 	rw_enter(&mc_lock, RW_READER);
10257aec1d6eScindi 	if (mc_lookup_by_chipid(getminor(*devp)) == NULL) {
10267aec1d6eScindi 		rw_exit(&mc_lock);
10277aec1d6eScindi 		return (EINVAL);
10287aec1d6eScindi 	}
10297aec1d6eScindi 	rw_exit(&mc_lock);
10307aec1d6eScindi 
10317aec1d6eScindi 	return (0);
10327aec1d6eScindi }
10337aec1d6eScindi 
10347aec1d6eScindi /*ARGSUSED*/
10357aec1d6eScindi static int
mc_close(dev_t dev,int flag,int otyp,cred_t * credp)10367aec1d6eScindi mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
10377aec1d6eScindi {
10387aec1d6eScindi 	return (0);
10397aec1d6eScindi }
10407aec1d6eScindi 
10418a40a695Sgavinm /*
10428a40a695Sgavinm  * Enable swap from chip-select csnum to the spare chip-select on this
10438a40a695Sgavinm  * memory controller (if any).
10448a40a695Sgavinm  */
10458a40a695Sgavinm 
10468a40a695Sgavinm int mc_swapdonetime = 30;	/* max number of seconds to wait for SwapDone */
10478a40a695Sgavinm 
10488a40a695Sgavinm static int
mc_onlinespare(mc_t * mc,int csnum)10498a40a695Sgavinm mc_onlinespare(mc_t *mc, int csnum)
10508a40a695Sgavinm {
10518a40a695Sgavinm 	mc_props_t *mcp = &mc->mc_props;
10528a40a695Sgavinm 	union mcreg_sparectl sparectl;
10538a40a695Sgavinm 	union mcreg_scrubctl scrubctl;
10548a40a695Sgavinm 	mc_cs_t *mccs;
10558a40a695Sgavinm 	hrtime_t tmax;
10568a40a695Sgavinm 	int i = 0;
10578a40a695Sgavinm 
10588a40a695Sgavinm 	ASSERT(RW_WRITE_HELD(&mc_lock));
10598a40a695Sgavinm 
106020c794b3Sgavinm 	if (!MC_REV_MATCH(mcp->mcp_rev, MC_F_REVS_FG))
10618a40a695Sgavinm 		return (ENOTSUP);	/* MC rev does not offer online spare */
10628a40a695Sgavinm 	else if (mcp->mcp_sparecs == MC_INVALNUM)
10638a40a695Sgavinm 		return (ENODEV);	/* Supported, but no spare configured */
10648a40a695Sgavinm 	else if (mcp->mcp_badcs != MC_INVALNUM)
10658a40a695Sgavinm 		return (EBUSY);		/* Spare already swapped in */
10668a40a695Sgavinm 	else if (csnum == mcp->mcp_sparecs)
10678a40a695Sgavinm 		return (EINVAL);	/* Can't spare the spare! */
10688a40a695Sgavinm 
10698a40a695Sgavinm 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
10708a40a695Sgavinm 		if (mccs->mccs_props.csp_num == csnum)
10718a40a695Sgavinm 			break;
10728a40a695Sgavinm 	}
10738a40a695Sgavinm 	if (mccs == NULL)
10748a40a695Sgavinm 		return (EINVAL);	/* nominated bad CS does not exist */
10758a40a695Sgavinm 
10768a40a695Sgavinm 	/*
10778a40a695Sgavinm 	 * If the DRAM Scrubber is not enabled then the swap cannot succeed.
10788a40a695Sgavinm 	 */
10798a40a695Sgavinm 	MCREG_VAL32(&scrubctl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
10808a40a695Sgavinm 	    MC_CTL_REG_SCRUBCTL);
10818a40a695Sgavinm 	if (MCREG_FIELD_CMN(&scrubctl, DramScrub) == 0)
10828a40a695Sgavinm 		return (ENODEV);	/* DRAM scrubber not enabled */
10838a40a695Sgavinm 
10848a40a695Sgavinm 	/*
10858a40a695Sgavinm 	 * Read Online Spare Comtrol Register again, just in case our
10868a40a695Sgavinm 	 * state does not reflect reality.
10878a40a695Sgavinm 	 */
10888a40a695Sgavinm 	MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
10898a40a695Sgavinm 	    MC_CTL_REG_SPARECTL);
10908a40a695Sgavinm 
109120c794b3Sgavinm 	if (MCREG_FIELD_F_revFG(&sparectl, SwapDone))
10928a40a695Sgavinm 		return (EBUSY);
10938a40a695Sgavinm 
10948a40a695Sgavinm 	/* Write to the BadDramCs field */
109520c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, BadDramCs) = csnum;
10968a40a695Sgavinm 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
10978a40a695Sgavinm 	    MCREG_VAL32(&sparectl));
10988a40a695Sgavinm 
10998a40a695Sgavinm 	/* And request that the swap to the spare start */
110020c794b3Sgavinm 	MCREG_FIELD_F_revFG(&sparectl, SwapEn) = 1;
11018a40a695Sgavinm 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
11028a40a695Sgavinm 	    MCREG_VAL32(&sparectl));
11038a40a695Sgavinm 
11048a40a695Sgavinm 	/*
11058a40a695Sgavinm 	 * Poll for SwapDone - we have disabled notification by interrupt.
11068a40a695Sgavinm 	 * Swap takes "several CPU cycles, depending on the DRAM speed, but
11078a40a695Sgavinm 	 * is performed in the background" (Family 0Fh Bios Porting Guide).
11088a40a695Sgavinm 	 * We're in a slow ioctl path so there is no harm in waiting around
11098a40a695Sgavinm 	 * a bit - consumers of the ioctl must be aware that it may take
11108a40a695Sgavinm 	 * a moment.  We will poll for up to mc_swapdonetime seconds,
11118a40a695Sgavinm 	 * limiting that to 120s.
11128a40a695Sgavinm 	 *
11138a40a695Sgavinm 	 * The swap is performed by the DRAM scrubber (which must be enabled)
11148a40a695Sgavinm 	 * whose scrub rate is accelerated for the duration of the swap.
11158a40a695Sgavinm 	 * The maximum swap rate is 40.0ns per 64 bytes, so the maximum
11168a40a695Sgavinm 	 * supported cs size of 16GB would take 10.7s at that max rate
11178a40a695Sgavinm 	 * of 25000000 scrubs/second.
11188a40a695Sgavinm 	 */
11198a40a695Sgavinm 	tmax = gethrtime() + MIN(mc_swapdonetime, 120) * 1000000000ULL;
11208a40a695Sgavinm 	do {
11218a40a695Sgavinm 		if (i++ < 20)
11228a40a695Sgavinm 			delay(drv_usectohz(100000));	/* 0.1s for up to 2s */
11238a40a695Sgavinm 		else
11248a40a695Sgavinm 			delay(drv_usectohz(500000));	/* 0.5s */
11258a40a695Sgavinm 
11268a40a695Sgavinm 		MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc,
11278a40a695Sgavinm 		    MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
112820c794b3Sgavinm 	} while (!MCREG_FIELD_F_revFG(&sparectl, SwapDone) &&
11298a40a695Sgavinm 	    gethrtime() < tmax);
11308a40a695Sgavinm 
113120c794b3Sgavinm 	if (!MCREG_FIELD_F_revFG(&sparectl, SwapDone))
11328a40a695Sgavinm 		return (ETIME);		/* Operation timed out */
11338a40a695Sgavinm 
11348a40a695Sgavinm 	mcp->mcp_badcs = csnum;
11358a40a695Sgavinm 	mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl);
11368a40a695Sgavinm 	mc->mc_spareswaptime = gethrtime();
11378a40a695Sgavinm 
11388a40a695Sgavinm 	return (0);
11398a40a695Sgavinm }
11408a40a695Sgavinm 
11417aec1d6eScindi /*ARGSUSED*/
11427aec1d6eScindi static int
mc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)11437aec1d6eScindi mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
11447aec1d6eScindi {
11457aec1d6eScindi 	int rc = 0;
11467aec1d6eScindi 	mc_t *mc;
11477aec1d6eScindi 
11488a40a695Sgavinm 	if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT &&
11498a40a695Sgavinm 	    cmd != MC_IOC_ONLINESPARE_EN)
11507aec1d6eScindi 		return (EINVAL);
11517aec1d6eScindi 
11527aec1d6eScindi 	rw_enter(&mc_lock, RW_READER);
11537aec1d6eScindi 
11547aec1d6eScindi 	if ((mc = mc_lookup_by_chipid(getminor(dev))) == NULL) {
11557aec1d6eScindi 		rw_exit(&mc_lock);
11567aec1d6eScindi 		return (EINVAL);
11577aec1d6eScindi 	}
11587aec1d6eScindi 
11597aec1d6eScindi 	switch (cmd) {
11607aec1d6eScindi 	case MC_IOC_SNAPSHOT_INFO: {
11617aec1d6eScindi 		mc_snapshot_info_t mcs;
11627aec1d6eScindi 
11638a40a695Sgavinm 		if (mc_snapshot_update(mc) < 0) {
11648a40a695Sgavinm 			rw_exit(&mc_lock);
11658a40a695Sgavinm 			return (EIO);
11668a40a695Sgavinm 		}
11678a40a695Sgavinm 
11687aec1d6eScindi 		mcs.mcs_size = mc->mc_snapshotsz;
11697aec1d6eScindi 		mcs.mcs_gen = mc->mc_snapshotgen;
11707aec1d6eScindi 
11717aec1d6eScindi 		if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t),
11727aec1d6eScindi 		    mode) < 0)
11737aec1d6eScindi 			rc = EFAULT;
11747aec1d6eScindi 		break;
11757aec1d6eScindi 	}
11767aec1d6eScindi 
11777aec1d6eScindi 	case MC_IOC_SNAPSHOT:
11788a40a695Sgavinm 		if (mc_snapshot_update(mc) < 0) {
11798a40a695Sgavinm 			rw_exit(&mc_lock);
11808a40a695Sgavinm 			return (EIO);
11818a40a695Sgavinm 		}
11828a40a695Sgavinm 
11837aec1d6eScindi 		if (ddi_copyout(mc->mc_snapshot, (void *)arg, mc->mc_snapshotsz,
11847aec1d6eScindi 		    mode) < 0)
11857aec1d6eScindi 			rc = EFAULT;
11867aec1d6eScindi 		break;
11878a40a695Sgavinm 
11888a40a695Sgavinm 	case MC_IOC_ONLINESPARE_EN:
11898a40a695Sgavinm 		if (drv_priv(credp) != 0) {
11908a40a695Sgavinm 			rw_exit(&mc_lock);
11918a40a695Sgavinm 			return (EPERM);
11928a40a695Sgavinm 		}
11938a40a695Sgavinm 
11948a40a695Sgavinm 		if (!rw_tryupgrade(&mc_lock)) {
11958a40a695Sgavinm 			rw_exit(&mc_lock);
11968a40a695Sgavinm 			return (EAGAIN);
11978a40a695Sgavinm 		}
11988a40a695Sgavinm 
11998a40a695Sgavinm 		if ((rc = mc_onlinespare(mc, (int)arg)) == 0) {
12008a40a695Sgavinm 			mc_snapshot_destroy(mc);
12018a40a695Sgavinm 			nvlist_free(mc->mc_nvl);
12028a40a695Sgavinm 			mc->mc_nvl = mc_nvl_create(mc);
12038a40a695Sgavinm 		}
12048a40a695Sgavinm 
12058a40a695Sgavinm 		break;
12067aec1d6eScindi 	}
12077aec1d6eScindi 
12087aec1d6eScindi 	rw_exit(&mc_lock);
12097aec1d6eScindi 
12107aec1d6eScindi 	return (rc);
12117aec1d6eScindi }
12127aec1d6eScindi 
12137aec1d6eScindi static struct cb_ops mc_cb_ops = {
12147aec1d6eScindi 	mc_open,
12157aec1d6eScindi 	mc_close,
12167aec1d6eScindi 	nodev,		/* not a block driver */
12177aec1d6eScindi 	nodev,		/* no print routine */
12187aec1d6eScindi 	nodev,		/* no dump routine */
12197aec1d6eScindi 	nodev,		/* no read routine */
12207aec1d6eScindi 	nodev,		/* no write routine */
12217aec1d6eScindi 	mc_ioctl,
12227aec1d6eScindi 	nodev,		/* no devmap routine */
12237aec1d6eScindi 	nodev,		/* no mmap routine */
12247aec1d6eScindi 	nodev,		/* no segmap routine */
12257aec1d6eScindi 	nochpoll,	/* no chpoll routine */
12267aec1d6eScindi 	ddi_prop_op,
12277aec1d6eScindi 	0,		/* not a STREAMS driver */
12287aec1d6eScindi 	D_NEW | D_MP,	/* safe for multi-thread/multi-processor */
12297aec1d6eScindi };
12307aec1d6eScindi 
12317aec1d6eScindi /*ARGSUSED*/
12327aec1d6eScindi static int
mc_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)12337aec1d6eScindi mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
12347aec1d6eScindi {
12357aec1d6eScindi 	int rc = DDI_SUCCESS;
12367aec1d6eScindi 	mc_t *mc;
12377aec1d6eScindi 
12387aec1d6eScindi 	if (infocmd != DDI_INFO_DEVT2DEVINFO &&
12397aec1d6eScindi 	    infocmd != DDI_INFO_DEVT2INSTANCE) {
12407aec1d6eScindi 		*result = NULL;
12417aec1d6eScindi 		return (DDI_FAILURE);
12427aec1d6eScindi 	}
12437aec1d6eScindi 
12447aec1d6eScindi 	rw_enter(&mc_lock, RW_READER);
12457aec1d6eScindi 
12467aec1d6eScindi 	if ((mc = mc_lookup_by_chipid(getminor((dev_t)arg))) == NULL ||
12477aec1d6eScindi 	    mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi == NULL) {
12487aec1d6eScindi 		rc = DDI_FAILURE;
12497aec1d6eScindi 	} else if (infocmd == DDI_INFO_DEVT2DEVINFO) {
12507aec1d6eScindi 		*result = mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi;
12517aec1d6eScindi 	} else {
12527aec1d6eScindi 		*result = (void *)(uintptr_t)
12537aec1d6eScindi 		    mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_instance;
12547aec1d6eScindi 	}
12557aec1d6eScindi 
12567aec1d6eScindi 	rw_exit(&mc_lock);
12577aec1d6eScindi 
12587aec1d6eScindi 	return (rc);
12597aec1d6eScindi }
12607aec1d6eScindi 
12617aec1d6eScindi /*ARGSUSED2*/
12627aec1d6eScindi static int
mc_fm_handle(dev_info_t * dip,ddi_fm_error_t * fmerr,const void * arg)12637aec1d6eScindi mc_fm_handle(dev_info_t *dip, ddi_fm_error_t *fmerr, const void *arg)
12647aec1d6eScindi {
12657aec1d6eScindi 	pci_ereport_post(dip, fmerr, NULL);
126600d0963fSdilpreet 	return (fmerr->fme_status);
12677aec1d6eScindi }
12687aec1d6eScindi 
12697aec1d6eScindi static void
mc_fm_init(dev_info_t * dip)12707aec1d6eScindi mc_fm_init(dev_info_t *dip)
12717aec1d6eScindi {
12727aec1d6eScindi 	int fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE;
12737aec1d6eScindi 	ddi_fm_init(dip, &fmcap, NULL);
12747aec1d6eScindi 	pci_ereport_setup(dip);
12757aec1d6eScindi 	ddi_fm_handler_register(dip, mc_fm_handle, NULL);
12767aec1d6eScindi }
12777aec1d6eScindi 
1278074bb90dSTom Pothier static void
mc_read_smbios(mc_t * mc,dev_info_t * dip)1279074bb90dSTom Pothier mc_read_smbios(mc_t *mc, dev_info_t *dip)
1280074bb90dSTom Pothier {
1281074bb90dSTom Pothier 
1282074bb90dSTom Pothier 	uint16_t bdf;
1283f32a9dd1SSrihari Venkatesan 	pci_regspec_t *pci_rp = NULL;
1284074bb90dSTom Pothier 	uint32_t phys_hi;
1285f32a9dd1SSrihari Venkatesan 	int m = 0;
1286074bb90dSTom Pothier 	uint_t chip_inst;
1287074bb90dSTom Pothier 	int rc = 0;
1288074bb90dSTom Pothier 
1289074bb90dSTom Pothier 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
1290074bb90dSTom Pothier 	    (caddr_t)&pci_rp, &m) == DDI_SUCCESS) {
1291074bb90dSTom Pothier 		phys_hi = pci_rp->pci_phys_hi;
1292074bb90dSTom Pothier 		bdf = (uint16_t)(PCI_REG_BDFR_G(phys_hi) >>
1293074bb90dSTom Pothier 		    PCI_REG_FUNC_SHIFT);
1294f32a9dd1SSrihari Venkatesan 		kmem_free(pci_rp, m);
1295f32a9dd1SSrihari Venkatesan 		pci_rp = NULL;
1296074bb90dSTom Pothier 
1297074bb90dSTom Pothier 		rc = fm_smb_mc_chipinst(bdf, &chip_inst);
1298074bb90dSTom Pothier 		if (rc == 0) {
1299074bb90dSTom Pothier 			mc->smb_chipid = chip_inst;
1300074bb90dSTom Pothier 		} else {
1301074bb90dSTom Pothier #ifdef DEBUG
130297689f66SSrihari Venkatesan 			cmn_err(CE_NOTE, "!mc read smbios chip info failed");
1303074bb90dSTom Pothier #endif /* DEBUG */
1304074bb90dSTom Pothier 			return;
1305074bb90dSTom Pothier 		}
1306074bb90dSTom Pothier 		mc->smb_bboard = fm_smb_mc_bboards(bdf);
1307074bb90dSTom Pothier #ifdef DEBUG
1308074bb90dSTom Pothier 		if (mc->smb_bboard == NULL)
1309074bb90dSTom Pothier 			cmn_err(CE_NOTE,
131097689f66SSrihari Venkatesan 			    "!mc read smbios base boards info failed");
1311074bb90dSTom Pothier #endif /* DEBUG */
1312074bb90dSTom Pothier 	}
1313f32a9dd1SSrihari Venkatesan 
1314f32a9dd1SSrihari Venkatesan 	if (pci_rp != NULL)
1315f32a9dd1SSrihari Venkatesan 		kmem_free(pci_rp, m);
1316074bb90dSTom Pothier }
1317074bb90dSTom Pothier 
131820c794b3Sgavinm /*ARGSUSED*/
131920c794b3Sgavinm static int
mc_create_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)132020c794b3Sgavinm mc_create_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
13217aec1d6eScindi {
132220c794b3Sgavinm 	chipid_t chipid = *((chipid_t *)arg1);
132320c794b3Sgavinm 	cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2;
132420c794b3Sgavinm 
132520c794b3Sgavinm 	if (cmi_hdl_chipid(whdl) == chipid) {
132620c794b3Sgavinm 		cmi_hdl_hold(whdl);	/* short-term hold */
132720c794b3Sgavinm 		*hdlp = whdl;
132820c794b3Sgavinm 		return (CMI_HDL_WALK_DONE);
132920c794b3Sgavinm 	} else {
133020c794b3Sgavinm 		return (CMI_HDL_WALK_NEXT);
133120c794b3Sgavinm 	}
13327aec1d6eScindi }
13337aec1d6eScindi 
13347aec1d6eScindi static mc_t *
mc_create(chipid_t chipid,dev_info_t * dip)1335074bb90dSTom Pothier mc_create(chipid_t chipid, dev_info_t *dip)
13367aec1d6eScindi {
13377aec1d6eScindi 	mc_t *mc;
133820c794b3Sgavinm 	cmi_hdl_t hdl = NULL;
13397aec1d6eScindi 
13407aec1d6eScindi 	ASSERT(RW_WRITE_HELD(&mc_lock));
13417aec1d6eScindi 
13428a40a695Sgavinm 	/*
134320c794b3Sgavinm 	 * Find a handle for one of a chip's CPU.
1344a6604450Sesaxe 	 *
1345fb2f18f8Sesaxe 	 * We can use one of the chip's CPUs since all cores
13468a40a695Sgavinm 	 * of a chip share the same revision and socket type.
13478a40a695Sgavinm 	 */
134820c794b3Sgavinm 	cmi_hdl_walk(mc_create_cb, (void *)&chipid, (void *)&hdl, NULL);
134920c794b3Sgavinm 	if (hdl == NULL)
135020c794b3Sgavinm 		return (NULL);	/* no cpu for this chipid found! */
1351a6604450Sesaxe 
135220c794b3Sgavinm 	mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP);
1353a6604450Sesaxe 
1354a6604450Sesaxe 	mc->mc_hdr.mch_type = MC_NT_MC;
1355a6604450Sesaxe 	mc->mc_props.mcp_num = chipid;
1356a6604450Sesaxe 	mc->mc_props.mcp_sparecs = MC_INVALNUM;
1357a6604450Sesaxe 	mc->mc_props.mcp_badcs = MC_INVALNUM;
1358a6604450Sesaxe 
135920c794b3Sgavinm 	mc->mc_props.mcp_rev = cmi_hdl_chiprev(hdl);
136020c794b3Sgavinm 	mc->mc_revname = cmi_hdl_chiprevstr(hdl);
136120c794b3Sgavinm 	mc->mc_socket = cmi_hdl_getsockettype(hdl);
1362a6604450Sesaxe 
1363074bb90dSTom Pothier 	mc_read_smbios(mc, dip);
1364074bb90dSTom Pothier 
13658a40a695Sgavinm 	if (mc_list == NULL)
13668a40a695Sgavinm 		mc_list = mc;
13678a40a695Sgavinm 	if (mc_last != NULL)
13688a40a695Sgavinm 		mc_last->mc_next = mc;
13698a40a695Sgavinm 
13708a40a695Sgavinm 	mc->mc_next = NULL;
13718a40a695Sgavinm 	mc_last = mc;
13727aec1d6eScindi 
137320c794b3Sgavinm 	cmi_hdl_rele(hdl);
137420c794b3Sgavinm 
13757aec1d6eScindi 	return (mc);
13767aec1d6eScindi }
13777aec1d6eScindi 
137820c794b3Sgavinm /*
137920c794b3Sgavinm  * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted
138020c794b3Sgavinm  * from the specified 'cfg' register value using 'mask' and 'shift'.  If a
138120c794b3Sgavinm  * value is zero, scrubbing is off so return the opposite value.  Otherwise
138220c794b3Sgavinm  * the maximum rate is the smallest non-zero value of the two values.
138320c794b3Sgavinm  */
138420c794b3Sgavinm static uint32_t
mc_scrubber_max(uint32_t r1,uint32_t cfg,uint32_t mask,uint32_t shift)138520c794b3Sgavinm mc_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift)
138620c794b3Sgavinm {
138720c794b3Sgavinm 	uint32_t r2 = (cfg & mask) >> shift;
138820c794b3Sgavinm 
138920c794b3Sgavinm 	if (r1 != 0 && r2 != 0)
139020c794b3Sgavinm 		return (MIN(r1, r2));
139120c794b3Sgavinm 
139220c794b3Sgavinm 	return (r1 ? r1 : r2);
139320c794b3Sgavinm }
139420c794b3Sgavinm 
139520c794b3Sgavinm 
139620c794b3Sgavinm /*
139720c794b3Sgavinm  * Enable the memory scrubber.  We must use the mc_pcicfg_{get32,put32}_nohdl
139820c794b3Sgavinm  * interfaces since we do not bind to function 3.
139920c794b3Sgavinm  */
140020c794b3Sgavinm cmi_errno_t
mc_scrubber_enable(mc_t * mc)140120c794b3Sgavinm mc_scrubber_enable(mc_t *mc)
140220c794b3Sgavinm {
140320c794b3Sgavinm 	mc_props_t *mcp = &mc->mc_props;
140492420f62SSurya Prakki 	chipid_t chipid = (chipid_t)mcp->mcp_num;
1405*22e4c3acSKeith M Wesolowski 	x86_chiprev_t rev = (x86_chiprev_t)mcp->mcp_rev;
140620c794b3Sgavinm 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
140720c794b3Sgavinm 	union mcreg_scrubctl scrubctl;
140820c794b3Sgavinm 	union mcreg_dramscrublo dalo;
140920c794b3Sgavinm 	union mcreg_dramscrubhi dahi;
141020c794b3Sgavinm 
141120c794b3Sgavinm 	mcr->mcr_scrubctl = MCREG_VAL32(&scrubctl) =
141220c794b3Sgavinm 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL);
141320c794b3Sgavinm 
141420c794b3Sgavinm 	mcr->mcr_scrubaddrlo = MCREG_VAL32(&dalo) =
141520c794b3Sgavinm 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO);
141620c794b3Sgavinm 
141720c794b3Sgavinm 	mcr->mcr_scrubaddrhi = MCREG_VAL32(&dahi) =
141820c794b3Sgavinm 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI);
141920c794b3Sgavinm 
142020c794b3Sgavinm 	if (mc_scrub_policy == MC_SCRUB_BIOSDEFAULT)
142120c794b3Sgavinm 		return (MCREG_FIELD_CMN(&scrubctl, DramScrub) !=
142220c794b3Sgavinm 		    AMD_NB_SCRUBCTL_RATE_NONE ?
142320c794b3Sgavinm 		    CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB);
142420c794b3Sgavinm 
142520c794b3Sgavinm 	/*
142620c794b3Sgavinm 	 * Disable DRAM scrubbing while we fiddle.
142720c794b3Sgavinm 	 */
142820c794b3Sgavinm 	MCREG_FIELD_CMN(&scrubctl, DramScrub) = AMD_NB_SCRUBCTL_RATE_NONE;
142920c794b3Sgavinm 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL,
143020c794b3Sgavinm 	    MCREG_VAL32(&scrubctl));
143120c794b3Sgavinm 
143220c794b3Sgavinm 	/*
143320c794b3Sgavinm 	 * Setup DRAM Scrub Address Low and High registers for the
143420c794b3Sgavinm 	 * base address of this node, and to select srubber redirect.
143520c794b3Sgavinm 	 */
143620c794b3Sgavinm 	MCREG_FIELD_CMN(&dalo, ScrubReDirEn) = 1;
143720c794b3Sgavinm 	MCREG_FIELD_CMN(&dalo, ScrubAddrLo) =
143820c794b3Sgavinm 	    AMD_NB_SCRUBADDR_MKLO(mcp->mcp_base);
143920c794b3Sgavinm 
144020c794b3Sgavinm 	MCREG_FIELD_CMN(&dahi, ScrubAddrHi) =
144120c794b3Sgavinm 	    AMD_NB_SCRUBADDR_MKHI(mcp->mcp_base);
144220c794b3Sgavinm 
144320c794b3Sgavinm 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO,
144420c794b3Sgavinm 	    MCREG_VAL32(&dalo));
144520c794b3Sgavinm 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI,
144620c794b3Sgavinm 	    MCREG_VAL32(&dahi));
144720c794b3Sgavinm 
144820c794b3Sgavinm 	if (mc_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) {
144920c794b3Sgavinm 		cmn_err(CE_WARN, "mc_scrub_rate_dram is too large; "
145020c794b3Sgavinm 		    "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX);
145120c794b3Sgavinm 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX;
145220c794b3Sgavinm 	}
145320c794b3Sgavinm 
145420c794b3Sgavinm 	switch (mc_scrub_policy) {
145520c794b3Sgavinm 	case MC_SCRUB_FIXED:
145620c794b3Sgavinm 		/* Use the system value checked above */
145720c794b3Sgavinm 		break;
145820c794b3Sgavinm 
145920c794b3Sgavinm 	default:
146020c794b3Sgavinm 		cmn_err(CE_WARN, "Unknown mc_scrub_policy value %d - "
146120c794b3Sgavinm 		    "using default policy of MC_SCRUB_MAX", mc_scrub_policy);
146220c794b3Sgavinm 		/*FALLTHRU*/
146320c794b3Sgavinm 
146420c794b3Sgavinm 	case MC_SCRUB_MAX:
146520c794b3Sgavinm 		mc_scrub_rate_dram = mc_scrubber_max(mc_scrub_rate_dram,
146620c794b3Sgavinm 		    mcr->mcr_scrubctl, AMD_NB_SCRUBCTL_DRAM_MASK,
146720c794b3Sgavinm 		    AMD_NB_SCRUBCTL_DRAM_SHIFT);
146820c794b3Sgavinm 		break;
146920c794b3Sgavinm 	}
147020c794b3Sgavinm 
147120c794b3Sgavinm 	/*
147292420f62SSurya Prakki 	 * OPTERON_ERRATUM_99:
147320c794b3Sgavinm 	 * This erratum applies on revisions D and earlier.
14747d586c73SSurya Prakki 	 * This erratum also applies on revisions E and later,
14757d586c73SSurya Prakki 	 * if BIOS uses chip-select hoisting instead of DRAM hole
14767d586c73SSurya Prakki 	 * mapping.
147720c794b3Sgavinm 	 *
14787d586c73SSurya Prakki 	 * Do not enable the dram scrubber if the chip-select ranges
147920c794b3Sgavinm 	 * for the node are not contiguous.
148020c794b3Sgavinm 	 */
148120c794b3Sgavinm 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE &&
148292420f62SSurya Prakki 	    mc->mc_csdiscontig) {
148320c794b3Sgavinm 		cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
148420c794b3Sgavinm 		    "%s chip %d because DRAM hole is present on this node",
148520c794b3Sgavinm 		    mc->mc_revname, chipid);
148620c794b3Sgavinm 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE;
148720c794b3Sgavinm 	}
148820c794b3Sgavinm 
148920c794b3Sgavinm 	/*
149092420f62SSurya Prakki 	 * OPTERON_ERRATUM_101:
149120c794b3Sgavinm 	 * This erratum applies on revisions D and earlier.
149220c794b3Sgavinm 	 *
149320c794b3Sgavinm 	 * If the DRAM Base Address register's IntlvEn field indicates that
149420c794b3Sgavinm 	 * node interleaving is enabled, we must disable the DRAM scrubber
149520c794b3Sgavinm 	 * and return zero to indicate that Solaris should use s/w instead.
149620c794b3Sgavinm 	 */
149720c794b3Sgavinm 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE &&
149820c794b3Sgavinm 	    mcp->mcp_ilen != 0 &&
1499*22e4c3acSKeith M Wesolowski 	    !chiprev_at_least(rev, X86_CHIPREV_AMD_LEGACY_F_REV_E)) {
150020c794b3Sgavinm 		cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
150120c794b3Sgavinm 		    "%s chip %d because DRAM memory is node-interleaved",
150220c794b3Sgavinm 		    mc->mc_revname, chipid);
150320c794b3Sgavinm 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE;
150420c794b3Sgavinm 	}
150520c794b3Sgavinm 
150620c794b3Sgavinm 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE) {
150720c794b3Sgavinm 		MCREG_FIELD_CMN(&scrubctl, DramScrub) = mc_scrub_rate_dram;
150820c794b3Sgavinm 		mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL,
150920c794b3Sgavinm 		    MCREG_VAL32(&scrubctl));
151020c794b3Sgavinm 	}
151120c794b3Sgavinm 
151220c794b3Sgavinm 	return (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE ?
151320c794b3Sgavinm 	    CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB);
151420c794b3Sgavinm }
151520c794b3Sgavinm 
151620c794b3Sgavinm /*ARGSUSED*/
151720c794b3Sgavinm static int
mc_attach_cb(cmi_hdl_t whdl,void * arg1,void * arg2,void * arg3)151820c794b3Sgavinm mc_attach_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
151920c794b3Sgavinm {
152020c794b3Sgavinm 	mc_t *mc = (mc_t *)arg1;
152120c794b3Sgavinm 	mcamd_prop_t chipid = *((mcamd_prop_t *)arg2);
152220c794b3Sgavinm 
152320c794b3Sgavinm 	if (cmi_hdl_chipid(whdl) == chipid) {
152420c794b3Sgavinm 		mcamd_mc_register(whdl, mc);
152520c794b3Sgavinm 	}
152620c794b3Sgavinm 
152720c794b3Sgavinm 	return (CMI_HDL_WALK_NEXT);
152820c794b3Sgavinm }
152920c794b3Sgavinm 
15308a40a695Sgavinm static int mc_sw_scrub_disabled = 0;
15318a40a695Sgavinm 
15327aec1d6eScindi static int
mc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)15337aec1d6eScindi mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
15347aec1d6eScindi {
15358a40a695Sgavinm 	mc_pcicfg_hdl_t cfghdl;
15367aec1d6eScindi 	const mc_bind_map_t *bm;
15377aec1d6eScindi 	const char *bindnm;
15387aec1d6eScindi 	char *unitstr = NULL;
15398a40a695Sgavinm 	enum mc_funcnum func;
15407aec1d6eScindi 	long unitaddr;
15418a40a695Sgavinm 	int chipid, rc;
15427aec1d6eScindi 	mc_t *mc;
15437aec1d6eScindi 
15442df1fe9cSrandyf 	/*
15452df1fe9cSrandyf 	 * This driver has no hardware state, but does
15462df1fe9cSrandyf 	 * claim to have a reg property, so it will be
15472df1fe9cSrandyf 	 * called on suspend.  It is probably better to
15482df1fe9cSrandyf 	 * make sure it doesn't get called on suspend,
15492df1fe9cSrandyf 	 * but it is just as easy to make sure we just
15502df1fe9cSrandyf 	 * return DDI_SUCCESS if called.
15512df1fe9cSrandyf 	 */
15522df1fe9cSrandyf 	if (cmd == DDI_RESUME)
15532df1fe9cSrandyf 		return (DDI_SUCCESS);
15542df1fe9cSrandyf 
155520c794b3Sgavinm 	if (cmd != DDI_ATTACH || mc_no_attach != 0)
15567aec1d6eScindi 		return (DDI_FAILURE);
15577aec1d6eScindi 
15587aec1d6eScindi 	bindnm = ddi_binding_name(dip);
15597aec1d6eScindi 	for (bm = mc_bind_map; bm->bm_bindnm != NULL; bm++) {
15607aec1d6eScindi 		if (strcmp(bindnm, bm->bm_bindnm) == 0) {
15617aec1d6eScindi 			func = bm->bm_func;
15627aec1d6eScindi 			break;
15637aec1d6eScindi 		}
15647aec1d6eScindi 	}
15657aec1d6eScindi 
15667aec1d6eScindi 	if (bm->bm_bindnm == NULL)
15677aec1d6eScindi 		return (DDI_FAILURE);
15687aec1d6eScindi 
15697aec1d6eScindi 	/*
15707aec1d6eScindi 	 * We need the device number, which corresponds to the processor node
15717aec1d6eScindi 	 * number plus 24.  The node number can then be used to associate this
15727aec1d6eScindi 	 * memory controller device with a given processor chip.
15737aec1d6eScindi 	 */
15740b9e3e76Smws 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
15750b9e3e76Smws 	    DDI_PROP_DONTPASS, "unit-address", &unitstr) != DDI_PROP_SUCCESS) {
15760b9e3e76Smws 		cmn_err(CE_WARN, "failed to find unit-address for %s", bindnm);
15770b9e3e76Smws 		return (DDI_FAILURE);
15780b9e3e76Smws 	}
15790b9e3e76Smws 
15807aec1d6eScindi 	rc = ddi_strtol(unitstr, NULL, 16, &unitaddr);
15817aec1d6eScindi 	ASSERT(rc == 0 && unitaddr >= MC_AMD_DEV_OFFSET);
15820b9e3e76Smws 
15837aec1d6eScindi 	if (rc != 0 || unitaddr < MC_AMD_DEV_OFFSET) {
15847aec1d6eScindi 		cmn_err(CE_WARN, "failed to parse unit address %s for %s\n",
15857aec1d6eScindi 		    unitstr, bindnm);
15868a40a695Sgavinm 		ddi_prop_free(unitstr);
15877aec1d6eScindi 		return (DDI_FAILURE);
15887aec1d6eScindi 	}
15898a40a695Sgavinm 	ddi_prop_free(unitstr);
15907aec1d6eScindi 
15917aec1d6eScindi 	chipid = unitaddr - MC_AMD_DEV_OFFSET;
15927aec1d6eScindi 
15937aec1d6eScindi 	rw_enter(&mc_lock, RW_WRITER);
15947aec1d6eScindi 
15957aec1d6eScindi 	for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
1596a6604450Sesaxe 		if (mc->mc_props.mcp_num == chipid)
15977aec1d6eScindi 			break;
15987aec1d6eScindi 	}
15997aec1d6eScindi 
16007aec1d6eScindi 	/* Integrate this memory controller device into existing set */
16017aec1d6eScindi 	if (mc == NULL) {
1602074bb90dSTom Pothier 		mc = mc_create(chipid, dip);
16037aec1d6eScindi 
16047aec1d6eScindi 		if (mc == NULL) {
16057aec1d6eScindi 			/*
16067aec1d6eScindi 			 * We don't complain here because this is a legitimate
16077aec1d6eScindi 			 * path for MP systems.  On those machines, we'll attach
16087aec1d6eScindi 			 * before all CPUs have been initialized, and thus the
16097aec1d6eScindi 			 * chip verification in mc_create will fail.  We'll be
16107aec1d6eScindi 			 * reattached later for those CPUs.
16117aec1d6eScindi 			 */
16127aec1d6eScindi 			rw_exit(&mc_lock);
16137aec1d6eScindi 			return (DDI_FAILURE);
16147aec1d6eScindi 		}
16157aec1d6eScindi 	} else {
16167aec1d6eScindi 		mc_snapshot_destroy(mc);
16177aec1d6eScindi 	}
16187aec1d6eScindi 
16197aec1d6eScindi 	/* Beyond this point, we're committed to creating this node */
16207aec1d6eScindi 
16217aec1d6eScindi 	mc_fm_init(dip);
16227aec1d6eScindi 
16237aec1d6eScindi 	ASSERT(mc->mc_funcs[func].mcf_devi == NULL);
16247aec1d6eScindi 	mc->mc_funcs[func].mcf_devi = dip;
16257aec1d6eScindi 	mc->mc_funcs[func].mcf_instance = ddi_get_instance(dip);
16267aec1d6eScindi 
16277aec1d6eScindi 	mc->mc_ref++;
16287aec1d6eScindi 
16297aec1d6eScindi 	/*
16307aec1d6eScindi 	 * Add the common properties to this node, and then add any properties
16317aec1d6eScindi 	 * that are specific to this node based upon its configuration space.
16327aec1d6eScindi 	 */
16337aec1d6eScindi 	(void) ddi_prop_update_string(DDI_DEV_T_NONE,
16347aec1d6eScindi 	    dip, "model", (char *)bm->bm_model);
16357aec1d6eScindi 
16367aec1d6eScindi 	(void) ddi_prop_update_int(DDI_DEV_T_NONE,
1637a6604450Sesaxe 	    dip, "chip-id", mc->mc_props.mcp_num);
16387aec1d6eScindi 
16397aec1d6eScindi 	if (bm->bm_mkprops != NULL &&
16408a40a695Sgavinm 	    mc_pcicfg_setup(mc, bm->bm_func, &cfghdl) == DDI_SUCCESS) {
16418a40a695Sgavinm 		bm->bm_mkprops(cfghdl, mc);
16428a40a695Sgavinm 		mc_pcicfg_teardown(cfghdl);
16437aec1d6eScindi 	}
16447aec1d6eScindi 
16457aec1d6eScindi 	/*
16467aec1d6eScindi 	 * If this is the last node to be attached for this memory controller,
16478a40a695Sgavinm 	 * then create the minor node, enable scrubbers, and register with
16488a40a695Sgavinm 	 * cpu module(s) for this chip.
16497aec1d6eScindi 	 */
16507aec1d6eScindi 	if (func == MC_FUNC_DEVIMAP) {
16517aec1d6eScindi 		mc_props_t *mcp = &mc->mc_props;
16528a40a695Sgavinm 		int dram_present = 0;
16537aec1d6eScindi 
16547aec1d6eScindi 		if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR,
165520c794b3Sgavinm 		    mcp->mcp_num, "ddi_mem_ctrl",
1656fb2f18f8Sesaxe 		    0) != DDI_SUCCESS) {
16577aec1d6eScindi 			cmn_err(CE_WARN, "failed to create minor node for chip "
1658a6604450Sesaxe 			    "%d memory controller\n",
165920c794b3Sgavinm 			    (chipid_t)mcp->mcp_num);
16607aec1d6eScindi 		}
16617aec1d6eScindi 
16627aec1d6eScindi 		/*
16637aec1d6eScindi 		 * Register the memory controller for every CPU of this chip.
16648a40a695Sgavinm 		 *
16658a40a695Sgavinm 		 * If there is memory present on this node and ECC is enabled
16668a40a695Sgavinm 		 * attempt to enable h/w memory scrubbers for this node.
166720c794b3Sgavinm 		 * If we are successful in enabling *any* hardware scrubbers,
16688a40a695Sgavinm 		 * disable the software memory scrubber.
16697aec1d6eScindi 		 */
167020c794b3Sgavinm 		cmi_hdl_walk(mc_attach_cb, (void *)mc, (void *)&mcp->mcp_num,
167120c794b3Sgavinm 		    NULL);
16727aec1d6eScindi 
167320c794b3Sgavinm 		if (mcp->mcp_lim != mcp->mcp_base) {
16748a40a695Sgavinm 			/*
16758a40a695Sgavinm 			 * This node may map non-dram memory alone, so we
16768a40a695Sgavinm 			 * must check for an enabled chip-select to be
16778a40a695Sgavinm 			 * sure there is dram present.
16788a40a695Sgavinm 			 */
16798a40a695Sgavinm 			mc_cs_t *mccs;
16808a40a695Sgavinm 
16818a40a695Sgavinm 			for (mccs = mc->mc_cslist; mccs != NULL;
16828a40a695Sgavinm 			    mccs = mccs->mccs_next) {
16838a40a695Sgavinm 				if (mccs->mccs_props.csp_csbe) {
16848a40a695Sgavinm 					dram_present = 1;
16858a40a695Sgavinm 					break;
16868a40a695Sgavinm 				}
16878a40a695Sgavinm 			}
16888a40a695Sgavinm 		}
16898a40a695Sgavinm 
16908a40a695Sgavinm 		if (dram_present && !mc_ecc_enabled(mc)) {
16918a40a695Sgavinm 			/*
16928a40a695Sgavinm 			 * On a single chip system there is no point in
16938a40a695Sgavinm 			 * scrubbing if there is no ECC on the single node.
16948a40a695Sgavinm 			 * On a multichip system, necessarily Opteron using
16958a40a695Sgavinm 			 * registered ECC-capable DIMMs, if there is memory
16968a40a695Sgavinm 			 * present on a node but no ECC there then we'll assume
16978a40a695Sgavinm 			 * ECC is disabled for all nodes and we will not enable
16988a40a695Sgavinm 			 * the scrubber and wll also disable the software
16998a40a695Sgavinm 			 * memscrub thread.
17008a40a695Sgavinm 			 */
17018a40a695Sgavinm 			rc = 1;
17028a40a695Sgavinm 		} else if (!dram_present) {
17038a40a695Sgavinm 			/* No memory on this node - others decide memscrub */
17048a40a695Sgavinm 			rc = 0;
17058a40a695Sgavinm 		} else {
170620c794b3Sgavinm 			/*
170720c794b3Sgavinm 			 * There is memory on this node and ECC is enabled.
170820c794b3Sgavinm 			 * Call via the cpu module to enable memory scrubbing
170920c794b3Sgavinm 			 * on this node - we could call directly but then
171020c794b3Sgavinm 			 * we may overlap with a request to enable chip-cache
171120c794b3Sgavinm 			 * scrubbing.
171220c794b3Sgavinm 			 */
171320c794b3Sgavinm 			rc = mc_scrubber_enable(mc);
17148a40a695Sgavinm 		}
17158a40a695Sgavinm 
171620c794b3Sgavinm 		if (rc == CMI_SUCCESS && !mc_sw_scrub_disabled++)
1717e4b86885SCheng Sean Ye 			cmi_mc_sw_memscrub_disable();
17188a40a695Sgavinm 
17198a40a695Sgavinm 		mc_report_testfails(mc);
17207aec1d6eScindi 	}
17217aec1d6eScindi 
17228a40a695Sgavinm 	/*
17238a40a695Sgavinm 	 * Update nvlist for as far as we have gotten in attach/init.
17248a40a695Sgavinm 	 */
17257aec1d6eScindi 	nvlist_free(mc->mc_nvl);
17267aec1d6eScindi 	mc->mc_nvl = mc_nvl_create(mc);
17277aec1d6eScindi 
17287aec1d6eScindi 	rw_exit(&mc_lock);
17297aec1d6eScindi 	return (DDI_SUCCESS);
17307aec1d6eScindi }
17317aec1d6eScindi 
17327aec1d6eScindi /*ARGSUSED*/
17337aec1d6eScindi static int
mc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)17347aec1d6eScindi mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
17357aec1d6eScindi {
17362df1fe9cSrandyf 	/*
17372df1fe9cSrandyf 	 * See the comment about suspend in
17382df1fe9cSrandyf 	 * mc_attach().
17392df1fe9cSrandyf 	 */
17402df1fe9cSrandyf 	if (cmd == DDI_SUSPEND)
17412df1fe9cSrandyf 		return (DDI_SUCCESS);
17422df1fe9cSrandyf 	else
17432df1fe9cSrandyf 		return (DDI_FAILURE);
17447aec1d6eScindi }
17457aec1d6eScindi 
174619397407SSherry Moore 
17477aec1d6eScindi static struct dev_ops mc_ops = {
17487aec1d6eScindi 	DEVO_REV,		/* devo_rev */
17497aec1d6eScindi 	0,			/* devo_refcnt */
17507aec1d6eScindi 	mc_getinfo,		/* devo_getinfo */
17517aec1d6eScindi 	nulldev,		/* devo_identify */
17527aec1d6eScindi 	nulldev,		/* devo_probe */
17537aec1d6eScindi 	mc_attach,		/* devo_attach */
17547aec1d6eScindi 	mc_detach,		/* devo_detach */
17557aec1d6eScindi 	nodev,			/* devo_reset */
17567aec1d6eScindi 	&mc_cb_ops,		/* devo_cb_ops */
17577aec1d6eScindi 	NULL,			/* devo_bus_ops */
175819397407SSherry Moore 	NULL,			/* devo_power */
175919397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
17607aec1d6eScindi };
17617aec1d6eScindi 
17627aec1d6eScindi static struct modldrv modldrv = {
17637aec1d6eScindi 	&mod_driverops,
17647aec1d6eScindi 	"Memory Controller for AMD processors",
17657aec1d6eScindi 	&mc_ops
17667aec1d6eScindi };
17677aec1d6eScindi 
17687aec1d6eScindi static struct modlinkage modlinkage = {
17697aec1d6eScindi 	MODREV_1,
17707aec1d6eScindi 	(void *)&modldrv,
17717aec1d6eScindi 	NULL
17727aec1d6eScindi };
17737aec1d6eScindi 
17747aec1d6eScindi int
_init(void)17757aec1d6eScindi _init(void)
17767aec1d6eScindi {
1777e4b86885SCheng Sean Ye 	/*
1778e4b86885SCheng Sean Ye 	 * Refuse to load if there is no PCI config space support.
1779e4b86885SCheng Sean Ye 	 */
1780e4b86885SCheng Sean Ye 	if (pci_getl_func == NULL)
1781e4b86885SCheng Sean Ye 		return (ENOTSUP);
1782e4b86885SCheng Sean Ye 
17837aec1d6eScindi 	rw_init(&mc_lock, NULL, RW_DRIVER, NULL);
17847aec1d6eScindi 	return (mod_install(&modlinkage));
17857aec1d6eScindi }
17867aec1d6eScindi 
17877aec1d6eScindi int
_info(struct modinfo * modinfop)17887aec1d6eScindi _info(struct modinfo *modinfop)
17897aec1d6eScindi {
17907aec1d6eScindi 	return (mod_info(&modlinkage, modinfop));
17917aec1d6eScindi }
17927aec1d6eScindi 
17937aec1d6eScindi int
_fini(void)17947aec1d6eScindi _fini(void)
17957aec1d6eScindi {
17967aec1d6eScindi 	int rc;
17977aec1d6eScindi 
17987aec1d6eScindi 	if ((rc = mod_remove(&modlinkage)) != 0)
17997aec1d6eScindi 		return (rc);
18007aec1d6eScindi 
18017aec1d6eScindi 	rw_destroy(&mc_lock);
18027aec1d6eScindi 	return (0);
18037aec1d6eScindi }
1804