xref: /titanic_52/usr/src/uts/intel/io/mc-amd/mcamd_drv.c (revision 97689f66b4d49d8e41fb47ce04dd38217fba0acb)
1e4b86885SCheng Sean Ye /*
2e4b86885SCheng Sean Ye  * CDDL HEADER START
3e4b86885SCheng Sean Ye  *
4e4b86885SCheng Sean Ye  * The contents of this file are subject to the terms of the
5e4b86885SCheng Sean Ye  * Common Development and Distribution License (the "License").
6e4b86885SCheng Sean Ye  * You may not use this file except in compliance with the License.
7e4b86885SCheng Sean Ye  *
8e4b86885SCheng Sean Ye  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9e4b86885SCheng Sean Ye  * or http://www.opensolaris.org/os/licensing.
10e4b86885SCheng Sean Ye  * See the License for the specific language governing permissions
11e4b86885SCheng Sean Ye  * and limitations under the License.
12e4b86885SCheng Sean Ye  *
13e4b86885SCheng Sean Ye  * When distributing Covered Code, include this CDDL HEADER in each
14e4b86885SCheng Sean Ye  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15e4b86885SCheng Sean Ye  * If applicable, add the following below this CDDL HEADER, with the
16e4b86885SCheng Sean Ye  * fields enclosed by brackets "[]" replaced with your own identifying
17e4b86885SCheng Sean Ye  * information: Portions Copyright [yyyy] [name of copyright owner]
18e4b86885SCheng Sean Ye  *
19e4b86885SCheng Sean Ye  * CDDL HEADER END
20e4b86885SCheng Sean Ye  */
21e4b86885SCheng Sean Ye 
22e4b86885SCheng Sean Ye /*
23f32a9dd1SSrihari Venkatesan  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24e4b86885SCheng Sean Ye  */
25e4b86885SCheng Sean Ye 
26e4b86885SCheng Sean Ye #include <sys/conf.h>
27e4b86885SCheng Sean Ye #include <sys/ddi.h>
28e4b86885SCheng Sean Ye #include <sys/ddifm.h>
29e4b86885SCheng Sean Ye #include <sys/sunddi.h>
30e4b86885SCheng Sean Ye #include <sys/sunndi.h>
31e4b86885SCheng Sean Ye #include <sys/stat.h>
32e4b86885SCheng Sean Ye #include <sys/modctl.h>
33e4b86885SCheng Sean Ye #include <sys/types.h>
34e4b86885SCheng Sean Ye #include <sys/cpuvar.h>
35e4b86885SCheng Sean Ye #include <sys/cmn_err.h>
36e4b86885SCheng Sean Ye #include <sys/kmem.h>
37e4b86885SCheng Sean Ye #include <sys/cred.h>
38e4b86885SCheng Sean Ye #include <sys/ksynch.h>
39e4b86885SCheng Sean Ye #include <sys/rwlock.h>
40e4b86885SCheng Sean Ye #include <sys/pghw.h>
41e4b86885SCheng Sean Ye #include <sys/open.h>
42e4b86885SCheng Sean Ye #include <sys/policy.h>
43e4b86885SCheng Sean Ye #include <sys/x86_archext.h>
44e4b86885SCheng Sean Ye #include <sys/cpu_module.h>
45e4b86885SCheng Sean Ye #include <qsort.h>
46e4b86885SCheng Sean Ye #include <sys/pci_cfgspace.h>
47e4b86885SCheng Sean Ye #include <sys/mc.h>
48e4b86885SCheng Sean Ye #include <sys/mc_amd.h>
49074bb90dSTom Pothier #include <sys/smbios.h>
50074bb90dSTom Pothier #include <sys/pci.h>
51e4b86885SCheng Sean Ye #include <mcamd.h>
52e4b86885SCheng Sean Ye #include <mcamd_dimmcfg.h>
53e4b86885SCheng Sean Ye #include <mcamd_pcicfg.h>
54e4b86885SCheng Sean Ye #include <mcamd_api.h>
55e4b86885SCheng Sean Ye #include <sys/fm/cpu/AMD.h>
56074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h>
57074bb90dSTom Pothier #include <sys/fm/protocol.h>
58074bb90dSTom Pothier #include <sys/fm/util.h>
59e4b86885SCheng Sean Ye 
60e4b86885SCheng Sean Ye /*
61e4b86885SCheng Sean Ye  * Set to prevent mc-amd from attaching.
62e4b86885SCheng Sean Ye  */
63e4b86885SCheng Sean Ye int mc_no_attach = 0;
64e4b86885SCheng Sean Ye 
65e4b86885SCheng Sean Ye /*
66e4b86885SCheng Sean Ye  * Of the 754/939/940 packages, only socket 940 supports quadrank registered
67e4b86885SCheng Sean Ye  * dimms.  Unfortunately, no memory-controller register indicates the
68e4b86885SCheng Sean Ye  * presence of quadrank dimm support or presence (i.e., in terms of number
69e4b86885SCheng Sean Ye  * of slots per cpu, and chip-select lines per slot,  The following may be set
70e4b86885SCheng Sean Ye  * in /etc/system to indicate the presence of quadrank support on a motherboard.
71e4b86885SCheng Sean Ye  *
72e4b86885SCheng Sean Ye  * There is no need to set this for F(1207) and S1g1.
73e4b86885SCheng Sean Ye  */
74e4b86885SCheng Sean Ye int mc_quadranksupport = 0;
75e4b86885SCheng Sean Ye 
76e4b86885SCheng Sean Ye mc_t *mc_list, *mc_last;
77e4b86885SCheng Sean Ye krwlock_t mc_lock;
78e4b86885SCheng Sean Ye int mc_hold_attached = 1;
79e4b86885SCheng Sean Ye 
80e4b86885SCheng Sean Ye #define	MAX(m, n) ((m) >= (n) ? (m) : (n))
81e4b86885SCheng Sean Ye #define	MIN(m, n) ((m) <= (n) ? (m) : (n))
82e4b86885SCheng Sean Ye 
83e4b86885SCheng Sean Ye /*
84e4b86885SCheng Sean Ye  * The following tuneable is used to determine the DRAM scrubbing rate.
85e4b86885SCheng Sean Ye  * The values range from 0x00-0x16 as described in the BKDG.  Zero
86e4b86885SCheng Sean Ye  * disables DRAM scrubbing.  Values above zero indicate rates in descending
87e4b86885SCheng Sean Ye  * order.
88e4b86885SCheng Sean Ye  *
89e4b86885SCheng Sean Ye  * The default value below is used on several Sun systems.  In the future
90e4b86885SCheng Sean Ye  * this code should assign values dynamically based on memory sizing.
91e4b86885SCheng Sean Ye  */
92e4b86885SCheng Sean Ye uint32_t mc_scrub_rate_dram = 0xd;	/* 64B every 163.8 us; 1GB per 45 min */
93e4b86885SCheng Sean Ye 
94e4b86885SCheng Sean Ye enum {
95e4b86885SCheng Sean Ye 	MC_SCRUB_BIOSDEFAULT,	/* retain system default value */
96e4b86885SCheng Sean Ye 	MC_SCRUB_FIXED,		/* assign mc_scrub_rate_* values */
97e4b86885SCheng Sean Ye 	MC_SCRUB_MAX		/* assign max of system and tunables */
98e4b86885SCheng Sean Ye } mc_scrub_policy = MC_SCRUB_MAX;
99e4b86885SCheng Sean Ye 
100e4b86885SCheng Sean Ye static void
101e4b86885SCheng Sean Ye mc_snapshot_destroy(mc_t *mc)
102e4b86885SCheng Sean Ye {
103e4b86885SCheng Sean Ye 	ASSERT(RW_LOCK_HELD(&mc_lock));
104e4b86885SCheng Sean Ye 
105e4b86885SCheng Sean Ye 	if (mc->mc_snapshot == NULL)
106e4b86885SCheng Sean Ye 		return;
107e4b86885SCheng Sean Ye 
108e4b86885SCheng Sean Ye 	kmem_free(mc->mc_snapshot, mc->mc_snapshotsz);
109e4b86885SCheng Sean Ye 	mc->mc_snapshot = NULL;
110e4b86885SCheng Sean Ye 	mc->mc_snapshotsz = 0;
111e4b86885SCheng Sean Ye 	mc->mc_snapshotgen++;
112e4b86885SCheng Sean Ye }
113e4b86885SCheng Sean Ye 
114e4b86885SCheng Sean Ye static int
115e4b86885SCheng Sean Ye mc_snapshot_update(mc_t *mc)
116e4b86885SCheng Sean Ye {
117e4b86885SCheng Sean Ye 	ASSERT(RW_LOCK_HELD(&mc_lock));
118e4b86885SCheng Sean Ye 
119e4b86885SCheng Sean Ye 	if (mc->mc_snapshot != NULL)
120e4b86885SCheng Sean Ye 		return (0);
121e4b86885SCheng Sean Ye 
122e4b86885SCheng Sean Ye 	if (nvlist_pack(mc->mc_nvl, &mc->mc_snapshot, &mc->mc_snapshotsz,
123e4b86885SCheng Sean Ye 	    NV_ENCODE_XDR, KM_SLEEP) != 0)
124e4b86885SCheng Sean Ye 		return (-1);
125e4b86885SCheng Sean Ye 
126e4b86885SCheng Sean Ye 	return (0);
127e4b86885SCheng Sean Ye }
128e4b86885SCheng Sean Ye 
129e4b86885SCheng Sean Ye static mc_t *
130e4b86885SCheng Sean Ye mc_lookup_by_chipid(int chipid)
131e4b86885SCheng Sean Ye {
132e4b86885SCheng Sean Ye 	mc_t *mc;
133e4b86885SCheng Sean Ye 
134e4b86885SCheng Sean Ye 	ASSERT(RW_LOCK_HELD(&mc_lock));
135e4b86885SCheng Sean Ye 
136e4b86885SCheng Sean Ye 	for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
137e4b86885SCheng Sean Ye 		if (mc->mc_props.mcp_num  == chipid)
138e4b86885SCheng Sean Ye 			return (mc);
139e4b86885SCheng Sean Ye 	}
140e4b86885SCheng Sean Ye 
141e4b86885SCheng Sean Ye 	return (NULL);
142e4b86885SCheng Sean Ye }
143e4b86885SCheng Sean Ye 
144e4b86885SCheng Sean Ye /*
145e4b86885SCheng Sean Ye  * Read config register pairs into the two arrays provided on the given
146e4b86885SCheng Sean Ye  * handle and at offsets as follows:
147e4b86885SCheng Sean Ye  *
148e4b86885SCheng Sean Ye  *	Index	Array r1 offset			Array r2 offset
149e4b86885SCheng Sean Ye  *	0	r1addr				r2addr
150e4b86885SCheng Sean Ye  *	1	r1addr + incr			r2addr + incr
151e4b86885SCheng Sean Ye  *	2	r1addr + 2 * incr		r2addr + 2 * incr
152e4b86885SCheng Sean Ye  *	...
153e4b86885SCheng Sean Ye  *	n - 1	r1addr + (n - 1) * incr		r2addr + (n - 1) * incr
154e4b86885SCheng Sean Ye  *
155e4b86885SCheng Sean Ye  * The number of registers to read into the r1 array is r1n; the number
156e4b86885SCheng Sean Ye  * for the r2 array is r2n.
157e4b86885SCheng Sean Ye  */
158e4b86885SCheng Sean Ye static void
159e4b86885SCheng Sean Ye mc_prop_read_pair(mc_pcicfg_hdl_t cfghdl, uint32_t *r1, off_t r1addr,
160e4b86885SCheng Sean Ye     int r1n, uint32_t *r2, off_t r2addr, int r2n, off_t incr)
161e4b86885SCheng Sean Ye {
162e4b86885SCheng Sean Ye 	int i;
163e4b86885SCheng Sean Ye 
164e4b86885SCheng Sean Ye 	for (i = 0; i < MAX(r1n, r2n); i++, r1addr += incr, r2addr += incr) {
165e4b86885SCheng Sean Ye 		if (i < r1n)
166e4b86885SCheng Sean Ye 			r1[i] = mc_pcicfg_get32(cfghdl, r1addr);
167e4b86885SCheng Sean Ye 		if (i < r2n)
168e4b86885SCheng Sean Ye 			r2[i] = mc_pcicfg_get32(cfghdl, r2addr);
169e4b86885SCheng Sean Ye 	}
170e4b86885SCheng Sean Ye }
171e4b86885SCheng Sean Ye 
17289e921d5SKuriakose Kuruvilla /*ARGSUSED*/
17389e921d5SKuriakose Kuruvilla static int
17489e921d5SKuriakose Kuruvilla mc_nvl_add_socket_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
17589e921d5SKuriakose Kuruvilla {
17689e921d5SKuriakose Kuruvilla 	uint32_t skt = *((uint32_t *)arg1);
17789e921d5SKuriakose Kuruvilla 	cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2;
17889e921d5SKuriakose Kuruvilla 
17989e921d5SKuriakose Kuruvilla 	if (cmi_hdl_getsockettype(whdl) == skt) {
18089e921d5SKuriakose Kuruvilla 		cmi_hdl_hold(whdl);	/* short-term hold */
18189e921d5SKuriakose Kuruvilla 		*hdlp = whdl;
18289e921d5SKuriakose Kuruvilla 		return (CMI_HDL_WALK_DONE);
18389e921d5SKuriakose Kuruvilla 	} else {
18489e921d5SKuriakose Kuruvilla 		return (CMI_HDL_WALK_NEXT);
18589e921d5SKuriakose Kuruvilla 	}
18689e921d5SKuriakose Kuruvilla }
187e4b86885SCheng Sean Ye 
188e4b86885SCheng Sean Ye static void
189e4b86885SCheng Sean Ye mc_nvl_add_socket(nvlist_t *nvl, mc_t *mc)
190e4b86885SCheng Sean Ye {
19189e921d5SKuriakose Kuruvilla 	cmi_hdl_t hdl = NULL;
19289e921d5SKuriakose Kuruvilla 	const char *s;
193e4b86885SCheng Sean Ye 
19489e921d5SKuriakose Kuruvilla 	cmi_hdl_walk(mc_nvl_add_socket_cb, (void *)&mc->mc_socket,
19589e921d5SKuriakose Kuruvilla 	    (void *)&hdl, NULL);
19689e921d5SKuriakose Kuruvilla 	if (hdl == NULL)
19789e921d5SKuriakose Kuruvilla 		s = "Unknown";  /* no cpu for this chipid found */
19889e921d5SKuriakose Kuruvilla 	else
19989e921d5SKuriakose Kuruvilla 		s = cmi_hdl_getsocketstr(hdl);
200e4b86885SCheng Sean Ye 
201e4b86885SCheng Sean Ye 	(void) nvlist_add_string(nvl, "socket", s);
20289e921d5SKuriakose Kuruvilla 
20389e921d5SKuriakose Kuruvilla 	if (hdl != NULL)
20489e921d5SKuriakose Kuruvilla 		cmi_hdl_rele(hdl);
205e4b86885SCheng Sean Ye }
206e4b86885SCheng Sean Ye 
207e4b86885SCheng Sean Ye static uint32_t
208e4b86885SCheng Sean Ye mc_ecc_enabled(mc_t *mc)
209e4b86885SCheng Sean Ye {
210e4b86885SCheng Sean Ye 	uint32_t rev = mc->mc_props.mcp_rev;
211e4b86885SCheng Sean Ye 	union mcreg_nbcfg nbcfg;
212e4b86885SCheng Sean Ye 
213e4b86885SCheng Sean Ye 	MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
214e4b86885SCheng Sean Ye 
215e4b86885SCheng Sean Ye 	return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ?
216e4b86885SCheng Sean Ye 	    MCREG_FIELD_F_preF(&nbcfg, EccEn) :
217e4b86885SCheng Sean Ye 	    MCREG_FIELD_F_revFG(&nbcfg, EccEn));
218e4b86885SCheng Sean Ye }
219e4b86885SCheng Sean Ye 
220e4b86885SCheng Sean Ye static uint32_t
221e4b86885SCheng Sean Ye mc_ck_enabled(mc_t *mc)
222e4b86885SCheng Sean Ye {
223e4b86885SCheng Sean Ye 	uint32_t rev = mc->mc_props.mcp_rev;
224e4b86885SCheng Sean Ye 	union mcreg_nbcfg nbcfg;
225e4b86885SCheng Sean Ye 
226e4b86885SCheng Sean Ye 	MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg;
227e4b86885SCheng Sean Ye 
228e4b86885SCheng Sean Ye 	return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ?
229e4b86885SCheng Sean Ye 	    MCREG_FIELD_F_preF(&nbcfg, ChipKillEccEn) :
230e4b86885SCheng Sean Ye 	    MCREG_FIELD_F_revFG(&nbcfg, ChipKillEccEn));
231e4b86885SCheng Sean Ye }
232e4b86885SCheng Sean Ye 
233e4b86885SCheng Sean Ye static void
234e4b86885SCheng Sean Ye mc_nvl_add_ecctype(nvlist_t *nvl, mc_t *mc)
235e4b86885SCheng Sean Ye {
236e4b86885SCheng Sean Ye 	(void) nvlist_add_string(nvl, "ecc-type", mc_ecc_enabled(mc) ?
237e4b86885SCheng Sean Ye 	    (mc_ck_enabled(mc) ? "ChipKill 128/16" : "Normal 64/8") : "None");
238e4b86885SCheng Sean Ye }
239e4b86885SCheng Sean Ye 
240e4b86885SCheng Sean Ye static void
241e4b86885SCheng Sean Ye mc_nvl_add_prop(nvlist_t *nvl, void *node, mcamd_propcode_t code, int reqval)
242e4b86885SCheng Sean Ye {
243e4b86885SCheng Sean Ye 	int valfound;
244e4b86885SCheng Sean Ye 	uint64_t value;
245e4b86885SCheng Sean Ye 	const char *name = mcamd_get_propname(code);
246e4b86885SCheng Sean Ye 
247e4b86885SCheng Sean Ye 	valfound = mcamd_get_numprop(NULL, (mcamd_node_t *)node, code, &value);
248e4b86885SCheng Sean Ye 
249e4b86885SCheng Sean Ye 	ASSERT(name != NULL && valfound);
250e4b86885SCheng Sean Ye 	if (name != NULL && valfound && (!reqval || value != MC_INVALNUM))
251e4b86885SCheng Sean Ye 		(void) nvlist_add_uint64(nvl, name, value);
252e4b86885SCheng Sean Ye }
253e4b86885SCheng Sean Ye 
254e4b86885SCheng Sean Ye static void
255e4b86885SCheng Sean Ye mc_nvl_add_cslist(nvlist_t *mcnvl, mc_t *mc)
256e4b86885SCheng Sean Ye {
257e4b86885SCheng Sean Ye 	mc_cs_t *mccs = mc->mc_cslist;
258e4b86885SCheng Sean Ye 	nvlist_t *cslist[MC_CHIP_NCS];
259e4b86885SCheng Sean Ye 	int nelem, i;
260e4b86885SCheng Sean Ye 
261e4b86885SCheng Sean Ye 	for (nelem = 0; mccs != NULL; mccs = mccs->mccs_next, nelem++) {
262e4b86885SCheng Sean Ye 		nvlist_t **csp = &cslist[nelem];
263e4b86885SCheng Sean Ye 		char csname[MCDCFG_CSNAMELEN];
264e4b86885SCheng Sean Ye 
265e4b86885SCheng Sean Ye 		(void) nvlist_alloc(csp, NV_UNIQUE_NAME, KM_SLEEP);
266e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM, 0);
267e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR, 0);
268e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK, 0);
269e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE, 0);
270e4b86885SCheng Sean Ye 
271e4b86885SCheng Sean Ye 		/*
272e4b86885SCheng Sean Ye 		 * It is possible for an mc_cs_t not to have associated
273e4b86885SCheng Sean Ye 		 * DIMM info if mcdcfg_lookup failed.
274e4b86885SCheng Sean Ye 		 */
275e4b86885SCheng Sean Ye 		if (mccs->mccs_csl[0] != NULL) {
276e4b86885SCheng Sean Ye 			mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM1, 1);
277e4b86885SCheng Sean Ye 			mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[0], csname,
278e4b86885SCheng Sean Ye 			    sizeof (csname));
279e4b86885SCheng Sean Ye 			(void) nvlist_add_string(*csp, "dimm1-csname", csname);
280e4b86885SCheng Sean Ye 		}
281e4b86885SCheng Sean Ye 
282e4b86885SCheng Sean Ye 		if (mccs->mccs_csl[1] != NULL) {
283e4b86885SCheng Sean Ye 			mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM2, 1);
284e4b86885SCheng Sean Ye 			mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[1], csname,
285e4b86885SCheng Sean Ye 			    sizeof (csname));
286e4b86885SCheng Sean Ye 			(void) nvlist_add_string(*csp, "dimm2-csname", csname);
287e4b86885SCheng Sean Ye 		}
288e4b86885SCheng Sean Ye 	}
289e4b86885SCheng Sean Ye 
290e4b86885SCheng Sean Ye 	/* Add cslist nvlist array even if zero members */
291e4b86885SCheng Sean Ye 	(void) nvlist_add_nvlist_array(mcnvl, "cslist", cslist, nelem);
292e4b86885SCheng Sean Ye 	for (i = 0; i < nelem; i++)
293e4b86885SCheng Sean Ye 		nvlist_free(cslist[i]);
294e4b86885SCheng Sean Ye }
295e4b86885SCheng Sean Ye 
296e4b86885SCheng Sean Ye static void
297e4b86885SCheng Sean Ye mc_nvl_add_dimmlist(nvlist_t *mcnvl, mc_t *mc)
298e4b86885SCheng Sean Ye {
299e4b86885SCheng Sean Ye 	nvlist_t *dimmlist[MC_CHIP_NDIMM];
300e4b86885SCheng Sean Ye 	mc_dimm_t *mcd;
301e4b86885SCheng Sean Ye 	int nelem, i;
302e4b86885SCheng Sean Ye 
303e4b86885SCheng Sean Ye 	for (nelem = 0, mcd = mc->mc_dimmlist; mcd != NULL;
304e4b86885SCheng Sean Ye 	    mcd = mcd->mcd_next, nelem++) {
305e4b86885SCheng Sean Ye 		nvlist_t **dimmp = &dimmlist[nelem];
306e4b86885SCheng Sean Ye 		uint64_t csnums[MC_CHIP_DIMMRANKMAX];
307e4b86885SCheng Sean Ye 		char csname[4][MCDCFG_CSNAMELEN];
308e4b86885SCheng Sean Ye 		char *csnamep[4];
309e4b86885SCheng Sean Ye 		int ncs = 0;
310e4b86885SCheng Sean Ye 
311e4b86885SCheng Sean Ye 		(void) nvlist_alloc(dimmp, NV_UNIQUE_NAME, KM_SLEEP);
312e4b86885SCheng Sean Ye 
313e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM, 1);
314e4b86885SCheng Sean Ye 		mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_SIZE, 1);
315e4b86885SCheng Sean Ye 
316e4b86885SCheng Sean Ye 		for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
317e4b86885SCheng Sean Ye 			if (mcd->mcd_cs[i] != NULL) {
318e4b86885SCheng Sean Ye 				csnums[ncs] =
319e4b86885SCheng Sean Ye 				    mcd->mcd_cs[i]->mccs_props.csp_num;
320e4b86885SCheng Sean Ye 				mcdcfg_csname(mc->mc_socket, mcd->mcd_csl[i],
321e4b86885SCheng Sean Ye 				    csname[ncs], MCDCFG_CSNAMELEN);
322e4b86885SCheng Sean Ye 				csnamep[ncs] = csname[ncs];
323e4b86885SCheng Sean Ye 				ncs++;
324e4b86885SCheng Sean Ye 			}
325e4b86885SCheng Sean Ye 		}
326e4b86885SCheng Sean Ye 
327e4b86885SCheng Sean Ye 		(void) nvlist_add_uint64_array(*dimmp, "csnums", csnums, ncs);
328e4b86885SCheng Sean Ye 		(void) nvlist_add_string_array(*dimmp, "csnames", csnamep, ncs);
329e4b86885SCheng Sean Ye 	}
330e4b86885SCheng Sean Ye 
331e4b86885SCheng Sean Ye 	/* Add dimmlist nvlist array even if zero members */
332e4b86885SCheng Sean Ye 	(void) nvlist_add_nvlist_array(mcnvl, "dimmlist", dimmlist, nelem);
333e4b86885SCheng Sean Ye 	for (i = 0; i < nelem; i++)
334e4b86885SCheng Sean Ye 		nvlist_free(dimmlist[i]);
335e4b86885SCheng Sean Ye }
336e4b86885SCheng Sean Ye 
337e4b86885SCheng Sean Ye static void
338e4b86885SCheng Sean Ye mc_nvl_add_htconfig(nvlist_t *mcnvl, mc_t *mc)
339e4b86885SCheng Sean Ye {
340e4b86885SCheng Sean Ye 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
341e4b86885SCheng Sean Ye 	union mcreg_htroute *htrp = (union mcreg_htroute *)&mcr->mcr_htroute[0];
342e4b86885SCheng Sean Ye 	union mcreg_nodeid *nip = (union mcreg_nodeid *)&mcr->mcr_htnodeid;
343e4b86885SCheng Sean Ye 	union mcreg_unitid *uip = (union mcreg_unitid *)&mcr->mcr_htunitid;
344e4b86885SCheng Sean Ye 	int ndcnt = HT_COHERENTNODES(nip);
345e4b86885SCheng Sean Ye 	uint32_t BCRte[MC_CHIP_MAXNODES];
346e4b86885SCheng Sean Ye 	uint32_t RPRte[MC_CHIP_MAXNODES];
347e4b86885SCheng Sean Ye 	uint32_t RQRte[MC_CHIP_MAXNODES];
348e4b86885SCheng Sean Ye 	nvlist_t *nvl;
349e4b86885SCheng Sean Ye 	int i;
350e4b86885SCheng Sean Ye 
351e4b86885SCheng Sean Ye 	(void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
352e4b86885SCheng Sean Ye 
353e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "NodeId", MCREG_FIELD_CMN(nip, NodeId));
354e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "CoherentNodes", HT_COHERENTNODES(nip));
355e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "SbNode", MCREG_FIELD_CMN(nip, SbNode));
356e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "LkNode", MCREG_FIELD_CMN(nip, LkNode));
357e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "SystemCoreCount",
358e4b86885SCheng Sean Ye 	    HT_SYSTEMCORECOUNT(nip));
359e4b86885SCheng Sean Ye 
360e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "C0Unit", MCREG_FIELD_CMN(uip, C0Unit));
361e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "C1Unit", MCREG_FIELD_CMN(uip, C1Unit));
362e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "McUnit", MCREG_FIELD_CMN(uip, McUnit));
363e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "HbUnit", MCREG_FIELD_CMN(uip, HbUnit));
364e4b86885SCheng Sean Ye 	(void) nvlist_add_uint32(nvl, "SbLink", MCREG_FIELD_CMN(uip, SbLink));
365e4b86885SCheng Sean Ye 
366e4b86885SCheng Sean Ye 	if (ndcnt <= MC_CHIP_MAXNODES) {
367e4b86885SCheng Sean Ye 		for (i = 0; i < ndcnt; i++, htrp++) {
368e4b86885SCheng Sean Ye 			BCRte[i] = MCREG_FIELD_CMN(htrp, BCRte);
369e4b86885SCheng Sean Ye 			RPRte[i] = MCREG_FIELD_CMN(htrp, RPRte);
370e4b86885SCheng Sean Ye 			RQRte[i] = MCREG_FIELD_CMN(htrp, RQRte);
371e4b86885SCheng Sean Ye 		}
372e4b86885SCheng Sean Ye 
373e4b86885SCheng Sean Ye 		(void) nvlist_add_uint32_array(nvl, "BroadcastRoutes",
374e4b86885SCheng Sean Ye 		    &BCRte[0], ndcnt);
375e4b86885SCheng Sean Ye 		(void) nvlist_add_uint32_array(nvl, "ResponseRoutes",
376e4b86885SCheng Sean Ye 		    &RPRte[0], ndcnt);
377e4b86885SCheng Sean Ye 		(void) nvlist_add_uint32_array(nvl, "RequestRoutes",
378e4b86885SCheng Sean Ye 		    &RQRte[0], ndcnt);
379e4b86885SCheng Sean Ye 	}
380e4b86885SCheng Sean Ye 
381e4b86885SCheng Sean Ye 	(void) nvlist_add_nvlist(mcnvl, "htconfig", nvl);
382e4b86885SCheng Sean Ye 	nvlist_free(nvl);
383e4b86885SCheng Sean Ye }
384e4b86885SCheng Sean Ye 
385e4b86885SCheng Sean Ye static nvlist_t *
386e4b86885SCheng Sean Ye mc_nvl_create(mc_t *mc)
387e4b86885SCheng Sean Ye {
388e4b86885SCheng Sean Ye 	nvlist_t *mcnvl;
389e4b86885SCheng Sean Ye 
390e4b86885SCheng Sean Ye 	(void) nvlist_alloc(&mcnvl, NV_UNIQUE_NAME, KM_SLEEP);
391e4b86885SCheng Sean Ye 
392e4b86885SCheng Sean Ye 	/*
393e4b86885SCheng Sean Ye 	 * Since this nvlist is used in populating the topo tree changes
394e4b86885SCheng Sean Ye 	 * made here may propogate through to changed property names etc
395e4b86885SCheng Sean Ye 	 * in the topo tree.  Some properties in the topo tree will be
396e4b86885SCheng Sean Ye 	 * contracted via ARC, so be careful what you change here.
397e4b86885SCheng Sean Ye 	 */
398e4b86885SCheng Sean Ye 	(void) nvlist_add_uint8(mcnvl, MC_NVLIST_VERSTR, MC_NVLIST_VERS1);
399e4b86885SCheng Sean Ye 
400e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM, 0);
401e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV, 0);
402e4b86885SCheng Sean Ye 	(void) nvlist_add_string(mcnvl, "revname", mc->mc_revname);
403e4b86885SCheng Sean Ye 	mc_nvl_add_socket(mcnvl, mc);
404e4b86885SCheng Sean Ye 	mc_nvl_add_ecctype(mcnvl, mc);
405e4b86885SCheng Sean Ye 
406e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR, 0);
407e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR, 0);
408e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILEN, 0);
409e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILSEL, 0);
410e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSINTLVFCTR, 0);
411e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAMHOLE_SIZE, 0);
412e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH, 0);
413e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAPREG, 0);
414e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BANKSWZL, 0);
415e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_MOD64MUX, 0);
416e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_SPARECS, 1);
417e4b86885SCheng Sean Ye 	mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BADCS, 1);
418e4b86885SCheng Sean Ye 
419e4b86885SCheng Sean Ye 	mc_nvl_add_cslist(mcnvl, mc);
420e4b86885SCheng Sean Ye 	mc_nvl_add_dimmlist(mcnvl, mc);
421e4b86885SCheng Sean Ye 	mc_nvl_add_htconfig(mcnvl, mc);
422e4b86885SCheng Sean Ye 
423e4b86885SCheng Sean Ye 	return (mcnvl);
424e4b86885SCheng Sean Ye }
425e4b86885SCheng Sean Ye 
426e4b86885SCheng Sean Ye /*
427e4b86885SCheng Sean Ye  * Link a dimm to its associated chip-selects and chip-select lines.
428e4b86885SCheng Sean Ye  * Total the size of all ranks of this dimm.
429e4b86885SCheng Sean Ye  */
430e4b86885SCheng Sean Ye static void
431e4b86885SCheng Sean Ye mc_dimm_csadd(mc_t *mc, mc_dimm_t *mcd, mc_cs_t *mccs, const mcdcfg_csl_t *csl)
432e4b86885SCheng Sean Ye {
433e4b86885SCheng Sean Ye 	int factor = (mc->mc_props.mcp_accwidth == 128) ? 2 : 1;
434e4b86885SCheng Sean Ye 	uint64_t sz = 0;
435e4b86885SCheng Sean Ye 	int i;
436e4b86885SCheng Sean Ye 
437e4b86885SCheng Sean Ye 	/* Skip to first unused rank slot */
438e4b86885SCheng Sean Ye 	for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
439e4b86885SCheng Sean Ye 		if (mcd->mcd_cs[i] == NULL) {
440e4b86885SCheng Sean Ye 			mcd->mcd_cs[i] = mccs;
441e4b86885SCheng Sean Ye 			mcd->mcd_csl[i] = csl;
442e4b86885SCheng Sean Ye 			sz += mccs->mccs_props.csp_size / factor;
443e4b86885SCheng Sean Ye 			break;
444e4b86885SCheng Sean Ye 		} else {
445e4b86885SCheng Sean Ye 			sz += mcd->mcd_cs[i]->mccs_props.csp_size / factor;
446e4b86885SCheng Sean Ye 		}
447e4b86885SCheng Sean Ye 	}
448e4b86885SCheng Sean Ye 
449e4b86885SCheng Sean Ye 	ASSERT(i != MC_CHIP_DIMMRANKMAX);
450e4b86885SCheng Sean Ye 
451e4b86885SCheng Sean Ye 	mcd->mcd_size = sz;
452e4b86885SCheng Sean Ye }
453e4b86885SCheng Sean Ye 
454e4b86885SCheng Sean Ye /*
455e4b86885SCheng Sean Ye  * Create a dimm structure and call to link it to its associated chip-selects.
456e4b86885SCheng Sean Ye  */
457e4b86885SCheng Sean Ye static mc_dimm_t *
458e4b86885SCheng Sean Ye mc_dimm_create(mc_t *mc, uint_t num)
459e4b86885SCheng Sean Ye {
460e4b86885SCheng Sean Ye 	mc_dimm_t *mcd = kmem_zalloc(sizeof (mc_dimm_t), KM_SLEEP);
461e4b86885SCheng Sean Ye 
462e4b86885SCheng Sean Ye 	mcd->mcd_hdr.mch_type = MC_NT_DIMM;
463e4b86885SCheng Sean Ye 	mcd->mcd_mc = mc;
464e4b86885SCheng Sean Ye 	mcd->mcd_num = num;
465e4b86885SCheng Sean Ye 
466e4b86885SCheng Sean Ye 	return (mcd);
467e4b86885SCheng Sean Ye }
468e4b86885SCheng Sean Ye 
469e4b86885SCheng Sean Ye /*
470e4b86885SCheng Sean Ye  * The chip-select structure includes an array of dimms associated with
471e4b86885SCheng Sean Ye  * that chip-select.  This function fills that array, and also builds
472e4b86885SCheng Sean Ye  * the list of all dimms on this memory controller mc_dimmlist.  The
473e4b86885SCheng Sean Ye  * caller has filled a structure with all there is to know about the
474e4b86885SCheng Sean Ye  * associated dimm(s).
475e4b86885SCheng Sean Ye  */
476e4b86885SCheng Sean Ye static void
477e4b86885SCheng Sean Ye mc_csdimms_create(mc_t *mc, mc_cs_t *mccs, mcdcfg_rslt_t *rsltp)
478e4b86885SCheng Sean Ye {
479e4b86885SCheng Sean Ye 	mc_dimm_t *found[MC_CHIP_DIMMPERCS];
480e4b86885SCheng Sean Ye 	mc_dimm_t *mcd;
481e4b86885SCheng Sean Ye 	int nfound = 0;
482e4b86885SCheng Sean Ye 	int i;
483e4b86885SCheng Sean Ye 
484e4b86885SCheng Sean Ye 	/*
485e4b86885SCheng Sean Ye 	 * Has some other chip-select already created this dimm or dimms?
486e4b86885SCheng Sean Ye 	 * If so then link to the dimm(s) from the mccs_dimm array,
487e4b86885SCheng Sean Ye 	 * record their topo numbers in the csp_dimmnums array, and link
488e4b86885SCheng Sean Ye 	 * the dimm(s) to the additional chip-select.
489e4b86885SCheng Sean Ye 	 */
490e4b86885SCheng Sean Ye 	for (mcd = mc->mc_dimmlist; mcd != NULL; mcd = mcd->mcd_next) {
491e4b86885SCheng Sean Ye 		for (i = 0; i < rsltp->ndimm; i++) {
492e4b86885SCheng Sean Ye 			if (mcd->mcd_num == rsltp->dimm[i].toponum)
493e4b86885SCheng Sean Ye 				found[nfound++] = mcd;
494e4b86885SCheng Sean Ye 		}
495e4b86885SCheng Sean Ye 	}
496e4b86885SCheng Sean Ye 	ASSERT(nfound == 0 || nfound == rsltp->ndimm);
497e4b86885SCheng Sean Ye 
498e4b86885SCheng Sean Ye 	for (i = 0; i < rsltp->ndimm; i++) {
499e4b86885SCheng Sean Ye 		if (nfound == 0) {
500e4b86885SCheng Sean Ye 			mcd = mc_dimm_create(mc, rsltp->dimm[i].toponum);
501e4b86885SCheng Sean Ye 			if (mc->mc_dimmlist == NULL)
502e4b86885SCheng Sean Ye 				mc->mc_dimmlist = mcd;
503e4b86885SCheng Sean Ye 			else
504e4b86885SCheng Sean Ye 				mc->mc_dimmlast->mcd_next = mcd;
505e4b86885SCheng Sean Ye 			mc->mc_dimmlast = mcd;
506e4b86885SCheng Sean Ye 		} else {
507e4b86885SCheng Sean Ye 			mcd = found[i];
508e4b86885SCheng Sean Ye 		}
509e4b86885SCheng Sean Ye 
510e4b86885SCheng Sean Ye 		mccs->mccs_dimm[i] = mcd;
511e4b86885SCheng Sean Ye 		mccs->mccs_csl[i] = rsltp->dimm[i].cslp;
512e4b86885SCheng Sean Ye 		mccs->mccs_props.csp_dimmnums[i] = mcd->mcd_num;
513e4b86885SCheng Sean Ye 		mc_dimm_csadd(mc, mcd, mccs, rsltp->dimm[i].cslp);
514e4b86885SCheng Sean Ye 
515e4b86885SCheng Sean Ye 	}
516e4b86885SCheng Sean Ye 
517e4b86885SCheng Sean Ye 	/* The rank number is constant across all constituent dimm(s) */
518e4b86885SCheng Sean Ye 	mccs->mccs_props.csp_dimmrank = rsltp->dimm[0].cslp->csl_rank;
519e4b86885SCheng Sean Ye }
520e4b86885SCheng Sean Ye 
521e4b86885SCheng Sean Ye /*
522e4b86885SCheng Sean Ye  * mc_dimmlist_create is called after we have discovered all enabled
523e4b86885SCheng Sean Ye  * (and spare or testfailed on revs F and G) chip-selects on the
524e4b86885SCheng Sean Ye  * given memory controller.  For each chip-select we must derive
525e4b86885SCheng Sean Ye  * the associated dimms, remembering that a chip-select csbase/csmask
526e4b86885SCheng Sean Ye  * pair may be associated with up to 2 chip-select lines (in 128 bit mode)
527e4b86885SCheng Sean Ye  * and that any one dimm may be associated with 1, 2, or 4 chip-selects
528e4b86885SCheng Sean Ye  * depending on whether it is single, dual or quadrank.
529e4b86885SCheng Sean Ye  */
530e4b86885SCheng Sean Ye static void
531e4b86885SCheng Sean Ye mc_dimmlist_create(mc_t *mc)
532e4b86885SCheng Sean Ye {
533e4b86885SCheng Sean Ye 	union mcreg_dramcfg_hi *drcfghip =
534e4b86885SCheng Sean Ye 	    (union mcreg_dramcfg_hi *)(&mc->mc_cfgregs.mcr_dramcfghi);
535e4b86885SCheng Sean Ye 	mc_props_t *mcp = &mc->mc_props;
536e4b86885SCheng Sean Ye 	uint32_t rev = mcp->mcp_rev;
537e4b86885SCheng Sean Ye 	mc_cs_t *mccs;
538e4b86885SCheng Sean Ye 	int r4 = 0, s4 = 0;
539e4b86885SCheng Sean Ye 
540e4b86885SCheng Sean Ye 	/*
541e4b86885SCheng Sean Ye 	 * Are we dealing with quadrank registered dimms?
542e4b86885SCheng Sean Ye 	 *
543e4b86885SCheng Sean Ye 	 * For socket 940 we can't tell and we'll assume we're not.
544e4b86885SCheng Sean Ye 	 * This can be over-ridden by the admin in /etc/system by setting
545e4b86885SCheng Sean Ye 	 * mc_quadranksupport nonzero.  A possible optimisation in systems
546e4b86885SCheng Sean Ye 	 * that export an SMBIOS table would be to count the number of
547e4b86885SCheng Sean Ye 	 * dimm slots per cpu - more than 4 would indicate no quadrank support
548e4b86885SCheng Sean Ye 	 * and 4 or fewer would indicate that if we see any of the upper
549e4b86885SCheng Sean Ye 	 * chip-selects enabled then a quadrank dimm is present.
550e4b86885SCheng Sean Ye 	 *
551e4b86885SCheng Sean Ye 	 * For socket F(1207) we can check a bit in the dram config high reg.
552e4b86885SCheng Sean Ye 	 *
553e4b86885SCheng Sean Ye 	 * Other socket types do not support registered dimms.
554e4b86885SCheng Sean Ye 	 */
555e4b86885SCheng Sean Ye 	if (mc->mc_socket == X86_SOCKET_940)
556e4b86885SCheng Sean Ye 		r4 = mc_quadranksupport != 0;
557e4b86885SCheng Sean Ye 	else if (mc->mc_socket == X86_SOCKET_F1207)
558e4b86885SCheng Sean Ye 		r4 = MCREG_FIELD_F_revFG(drcfghip, FourRankRDimm);
559e4b86885SCheng Sean Ye 
560e4b86885SCheng Sean Ye 	/*
561e4b86885SCheng Sean Ye 	 * Are we dealing with quadrank SO-DIMMs?  These are supported
562e4b86885SCheng Sean Ye 	 * in AM2 and S1g1 packages only, but in all rev F/G cases we
563e4b86885SCheng Sean Ye 	 * can detect their presence via a bit in the dram config high reg.
564e4b86885SCheng Sean Ye 	 */
565e4b86885SCheng Sean Ye 	if (MC_REV_MATCH(rev, MC_F_REVS_FG))
566e4b86885SCheng Sean Ye 		s4 = MCREG_FIELD_F_revFG(drcfghip, FourRankSODimm);
567e4b86885SCheng Sean Ye 
568e4b86885SCheng Sean Ye 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
569e4b86885SCheng Sean Ye 		mcdcfg_rslt_t rslt;
570e4b86885SCheng Sean Ye 
571e4b86885SCheng Sean Ye 		/*
572e4b86885SCheng Sean Ye 		 * If lookup fails we will not create dimm structures for
573e4b86885SCheng Sean Ye 		 * this chip-select.  In the mc_cs_t we will have both
574e4b86885SCheng Sean Ye 		 * csp_dimmnum members set to MC_INVALNUM and patounum
575e4b86885SCheng Sean Ye 		 * code will see from those that we do not have dimm info
576e4b86885SCheng Sean Ye 		 * for this chip-select.
577e4b86885SCheng Sean Ye 		 */
578e4b86885SCheng Sean Ye 		if (mcdcfg_lookup(rev, mcp->mcp_mod64mux, mcp->mcp_accwidth,
579e4b86885SCheng Sean Ye 		    mccs->mccs_props.csp_num, mc->mc_socket,
580e4b86885SCheng Sean Ye 		    r4, s4, &rslt) < 0)
581e4b86885SCheng Sean Ye 			continue;
582e4b86885SCheng Sean Ye 
583e4b86885SCheng Sean Ye 		mc_csdimms_create(mc, mccs, &rslt);
584e4b86885SCheng Sean Ye 	}
585e4b86885SCheng Sean Ye }
586e4b86885SCheng Sean Ye 
587e4b86885SCheng Sean Ye static mc_cs_t *
588e4b86885SCheng Sean Ye mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz,
589e4b86885SCheng Sean Ye     int csbe, int spare, int testfail)
590e4b86885SCheng Sean Ye {
591e4b86885SCheng Sean Ye 	mc_cs_t *mccs = kmem_zalloc(sizeof (mc_cs_t), KM_SLEEP);
592e4b86885SCheng Sean Ye 	mccs_props_t *csp = &mccs->mccs_props;
593e4b86885SCheng Sean Ye 	int i;
594e4b86885SCheng Sean Ye 
595e4b86885SCheng Sean Ye 	mccs->mccs_hdr.mch_type = MC_NT_CS;
596e4b86885SCheng Sean Ye 	mccs->mccs_mc = mc;
597e4b86885SCheng Sean Ye 	csp->csp_num = num;
598e4b86885SCheng Sean Ye 	csp->csp_base = base;
599e4b86885SCheng Sean Ye 	csp->csp_mask = mask;
600e4b86885SCheng Sean Ye 	csp->csp_size = sz;
601e4b86885SCheng Sean Ye 	csp->csp_csbe = csbe;
602e4b86885SCheng Sean Ye 	csp->csp_spare = spare;
603e4b86885SCheng Sean Ye 	csp->csp_testfail = testfail;
604e4b86885SCheng Sean Ye 
605e4b86885SCheng Sean Ye 	for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
606e4b86885SCheng Sean Ye 		csp->csp_dimmnums[i] = MC_INVALNUM;
607e4b86885SCheng Sean Ye 
608e4b86885SCheng Sean Ye 	if (spare)
609e4b86885SCheng Sean Ye 		mc->mc_props.mcp_sparecs = num;
610e4b86885SCheng Sean Ye 
611e4b86885SCheng Sean Ye 	return (mccs);
612e4b86885SCheng Sean Ye }
613e4b86885SCheng Sean Ye 
614e4b86885SCheng Sean Ye /*
615e4b86885SCheng Sean Ye  * For any cs# of this mc marked TestFail generate an ereport with
616e4b86885SCheng Sean Ye  * resource identifying the associated dimm(s).
617e4b86885SCheng Sean Ye  */
618e4b86885SCheng Sean Ye static void
619e4b86885SCheng Sean Ye mc_report_testfails(mc_t *mc)
620e4b86885SCheng Sean Ye {
621e4b86885SCheng Sean Ye 	mc_unum_t unum;
622e4b86885SCheng Sean Ye 	mc_cs_t *mccs;
623e4b86885SCheng Sean Ye 	int i;
624e4b86885SCheng Sean Ye 
625e4b86885SCheng Sean Ye 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
626e4b86885SCheng Sean Ye 		if (mccs->mccs_props.csp_testfail) {
627e4b86885SCheng Sean Ye 			unum.unum_board = 0;
628e4b86885SCheng Sean Ye 			unum.unum_chip = mc->mc_props.mcp_num;
629e4b86885SCheng Sean Ye 			unum.unum_mc = 0;
630e4b86885SCheng Sean Ye 			unum.unum_chan = MC_INVALNUM;
631e4b86885SCheng Sean Ye 			unum.unum_cs = mccs->mccs_props.csp_num;
632e4b86885SCheng Sean Ye 			unum.unum_rank = mccs->mccs_props.csp_dimmrank;
633e4b86885SCheng Sean Ye 			unum.unum_offset = MCAMD_RC_INVALID_OFFSET;
634e4b86885SCheng Sean Ye 			for (i = 0; i < MC_CHIP_DIMMPERCS; i++)
635e4b86885SCheng Sean Ye 				unum.unum_dimms[i] = MC_INVALNUM;
636e4b86885SCheng Sean Ye 
637e4b86885SCheng Sean Ye 			mcamd_ereport_post(mc, FM_EREPORT_CPU_AMD_MC_TESTFAIL,
638e4b86885SCheng Sean Ye 			    &unum,
639e4b86885SCheng Sean Ye 			    FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL);
640e4b86885SCheng Sean Ye 		}
641e4b86885SCheng Sean Ye 	}
642e4b86885SCheng Sean Ye }
643e4b86885SCheng Sean Ye 
644e4b86885SCheng Sean Ye /*
645e4b86885SCheng Sean Ye  * Function 0 - HyperTransport Technology Configuration
646e4b86885SCheng Sean Ye  */
647e4b86885SCheng Sean Ye static void
648e4b86885SCheng Sean Ye mc_mkprops_htcfg(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
649e4b86885SCheng Sean Ye {
650e4b86885SCheng Sean Ye 	union mcreg_nodeid nodeid;
651e4b86885SCheng Sean Ye 	off_t offset;
652e4b86885SCheng Sean Ye 	int i;
653e4b86885SCheng Sean Ye 
654e4b86885SCheng Sean Ye 	mc->mc_cfgregs.mcr_htnodeid = MCREG_VAL32(&nodeid) =
655e4b86885SCheng Sean Ye 	    mc_pcicfg_get32(cfghdl, MC_HT_REG_NODEID);
656e4b86885SCheng Sean Ye 
657e4b86885SCheng Sean Ye 	mc->mc_cfgregs.mcr_htunitid = mc_pcicfg_get32(cfghdl, MC_HT_REG_UNITID);
658e4b86885SCheng Sean Ye 
659e4b86885SCheng Sean Ye 	for (i = 0, offset = MC_HT_REG_RTBL_NODE_0;
660e4b86885SCheng Sean Ye 	    i < HT_COHERENTNODES(&nodeid);
661e4b86885SCheng Sean Ye 	    i++, offset += MC_HT_REG_RTBL_INCR)
662e4b86885SCheng Sean Ye 		mc->mc_cfgregs.mcr_htroute[i] = mc_pcicfg_get32(cfghdl, offset);
663e4b86885SCheng Sean Ye }
664e4b86885SCheng Sean Ye 
665e4b86885SCheng Sean Ye /*
666e4b86885SCheng Sean Ye  * Function 1 Configuration - Address Map (see BKDG 3.4.4 DRAM Address Map)
667e4b86885SCheng Sean Ye  *
668e4b86885SCheng Sean Ye  * Read the Function 1 Address Map for each potential DRAM node.  The Base
669e4b86885SCheng Sean Ye  * Address for a node gives the starting system address mapped at that node,
670e4b86885SCheng Sean Ye  * and the limit gives the last valid address mapped at that node.  Regions for
671e4b86885SCheng Sean Ye  * different nodes should not overlap, unless node-interleaving is enabled.
672e4b86885SCheng Sean Ye  * The base register also indicates the node-interleaving settings (IntlvEn).
673e4b86885SCheng Sean Ye  * The limit register includes IntlvSel which determines which 4K blocks will
674e4b86885SCheng Sean Ye  * be routed to this node and the destination node ID for addresses that fall
675e4b86885SCheng Sean Ye  * within the [base, limit] range - this must match the pair number.
676e4b86885SCheng Sean Ye  */
677e4b86885SCheng Sean Ye static void
678e4b86885SCheng Sean Ye mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
679e4b86885SCheng Sean Ye {
680e4b86885SCheng Sean Ye 	union mcreg_drambase basereg;
681e4b86885SCheng Sean Ye 	union mcreg_dramlimit limreg;
682e4b86885SCheng Sean Ye 	mc_props_t *mcp = &mc->mc_props;
683e4b86885SCheng Sean Ye 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
684e4b86885SCheng Sean Ye 	union mcreg_dramhole hole;
685e4b86885SCheng Sean Ye 	int nodeid = mc->mc_props.mcp_num;
686e4b86885SCheng Sean Ye 
687e4b86885SCheng Sean Ye 	mcr->mcr_drambase = MCREG_VAL32(&basereg) = mc_pcicfg_get32(cfghdl,
688e4b86885SCheng Sean Ye 	    MC_AM_REG_DRAMBASE_0 + nodeid * MC_AM_REG_DRAM_INCR);
689e4b86885SCheng Sean Ye 
690e4b86885SCheng Sean Ye 	mcr->mcr_dramlimit = MCREG_VAL32(&limreg) = mc_pcicfg_get32(cfghdl,
691e4b86885SCheng Sean Ye 	    MC_AM_REG_DRAMLIM_0 + nodeid * MC_AM_REG_DRAM_INCR);
692e4b86885SCheng Sean Ye 
693e4b86885SCheng Sean Ye 	/*
694e4b86885SCheng Sean Ye 	 * Derive some "cooked" properties for nodes that have a range of
695e4b86885SCheng Sean Ye 	 * physical addresses that are read or write enabled and for which
696e4b86885SCheng Sean Ye 	 * the DstNode matches the node we are attaching.
697e4b86885SCheng Sean Ye 	 */
698e4b86885SCheng Sean Ye 	if (MCREG_FIELD_CMN(&limreg, DRAMLimiti) != 0 &&
699e4b86885SCheng Sean Ye 	    MCREG_FIELD_CMN(&limreg, DstNode) == nodeid &&
700e4b86885SCheng Sean Ye 	    (MCREG_FIELD_CMN(&basereg, WE) || MCREG_FIELD_CMN(&basereg, RE))) {
701e4b86885SCheng Sean Ye 		mcp->mcp_base = MC_DRAMBASE(&basereg);
702e4b86885SCheng Sean Ye 		mcp->mcp_lim = MC_DRAMLIM(&limreg);
703e4b86885SCheng Sean Ye 		mcp->mcp_ilen = MCREG_FIELD_CMN(&basereg, IntlvEn);
704e4b86885SCheng Sean Ye 		mcp->mcp_ilsel = MCREG_FIELD_CMN(&limreg, IntlvSel);
705e4b86885SCheng Sean Ye 	}
706e4b86885SCheng Sean Ye 
707e4b86885SCheng Sean Ye 	/*
708e4b86885SCheng Sean Ye 	 * The Function 1 DRAM Hole Address Register tells us which node(s)
709e4b86885SCheng Sean Ye 	 * own the DRAM space that is hoisted above 4GB, together with the
710e4b86885SCheng Sean Ye 	 * hole base and offset for this node.  This was introduced in
711e4b86885SCheng Sean Ye 	 * revision E.
712e4b86885SCheng Sean Ye 	 */
713e4b86885SCheng Sean Ye 	if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_F_REV_E)) {
714e4b86885SCheng Sean Ye 		mcr->mcr_dramhole = MCREG_VAL32(&hole) =
715e4b86885SCheng Sean Ye 		    mc_pcicfg_get32(cfghdl, MC_AM_REG_HOLEADDR);
716e4b86885SCheng Sean Ye 
717e4b86885SCheng Sean Ye 		if (MCREG_FIELD_CMN(&hole, DramHoleValid))
718e4b86885SCheng Sean Ye 			mcp->mcp_dramhole_size = MC_DRAMHOLE_SIZE(&hole);
719e4b86885SCheng Sean Ye 	}
720e4b86885SCheng Sean Ye }
721e4b86885SCheng Sean Ye 
722e4b86885SCheng Sean Ye /*
723e4b86885SCheng Sean Ye  * Read some function 3 parameters via PCI Mechanism 1 accesses (which
724e4b86885SCheng Sean Ye  * will serialize any NB accesses).
725e4b86885SCheng Sean Ye  */
726e4b86885SCheng Sean Ye static void
727e4b86885SCheng Sean Ye mc_getmiscctl(mc_t *mc)
728e4b86885SCheng Sean Ye {
729e4b86885SCheng Sean Ye 	uint32_t rev = mc->mc_props.mcp_rev;
730e4b86885SCheng Sean Ye 	union mcreg_nbcfg nbcfg;
731e4b86885SCheng Sean Ye 	union mcreg_sparectl sparectl;
732e4b86885SCheng Sean Ye 
733e4b86885SCheng Sean Ye 	mc->mc_cfgregs.mcr_nbcfg = MCREG_VAL32(&nbcfg) =
734e4b86885SCheng Sean Ye 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG);
735e4b86885SCheng Sean Ye 
736e4b86885SCheng Sean Ye 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
737e4b86885SCheng Sean Ye 		mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl) =
738e4b86885SCheng Sean Ye 		    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
739e4b86885SCheng Sean Ye 		    MC_CTL_REG_SPARECTL);
740e4b86885SCheng Sean Ye 
741e4b86885SCheng Sean Ye 		if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) {
742e4b86885SCheng Sean Ye 			mc->mc_props.mcp_badcs =
743e4b86885SCheng Sean Ye 			    MCREG_FIELD_F_revFG(&sparectl, BadDramCs);
744e4b86885SCheng Sean Ye 		}
745e4b86885SCheng Sean Ye 	}
746e4b86885SCheng Sean Ye }
747e4b86885SCheng Sean Ye 
748e4b86885SCheng Sean Ye static int
749e4b86885SCheng Sean Ye csbasecmp(mc_cs_t **csapp, mc_cs_t **csbpp)
750e4b86885SCheng Sean Ye {
751e4b86885SCheng Sean Ye 	uint64_t basea = (*csapp)->mccs_props.csp_base;
752e4b86885SCheng Sean Ye 	uint64_t baseb = (*csbpp)->mccs_props.csp_base;
753e4b86885SCheng Sean Ye 
754e4b86885SCheng Sean Ye 	if (basea == baseb)
755e4b86885SCheng Sean Ye 		return (0);
756e4b86885SCheng Sean Ye 	else if (basea < baseb)
757e4b86885SCheng Sean Ye 		return (-1);
758e4b86885SCheng Sean Ye 	else
759e4b86885SCheng Sean Ye 		return (1);
760e4b86885SCheng Sean Ye }
761e4b86885SCheng Sean Ye 
762e4b86885SCheng Sean Ye /*
763e4b86885SCheng Sean Ye  * The following are for use in simulating TestFail for a chip-select
764e4b86885SCheng Sean Ye  * without poking at the hardware (which tends to get upset if you do
765e4b86885SCheng Sean Ye  * since the BIOS needs to restart to map a failed cs out).  For internal
766e4b86885SCheng Sean Ye  * testing only!  Note that setting these does not give the full experience -
767e4b86885SCheng Sean Ye  * the select chip-select *is* enabled and can give errors etc and the
768e4b86885SCheng Sean Ye  * patounum logic will get confused.
769e4b86885SCheng Sean Ye  */
770e4b86885SCheng Sean Ye int testfail_mcnum = -1;
771e4b86885SCheng Sean Ye int testfail_csnum = -1;
772e4b86885SCheng Sean Ye 
773e4b86885SCheng Sean Ye /*
774e4b86885SCheng Sean Ye  * Function 2 configuration - DRAM Controller
775e4b86885SCheng Sean Ye  */
776e4b86885SCheng Sean Ye static void
777e4b86885SCheng Sean Ye mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc)
778e4b86885SCheng Sean Ye {
779e4b86885SCheng Sean Ye 	union mcreg_csbase base[MC_CHIP_NCS];
780e4b86885SCheng Sean Ye 	union mcreg_csmask mask[MC_CHIP_NCS];
781e4b86885SCheng Sean Ye 	union mcreg_dramcfg_lo drcfg_lo;
782e4b86885SCheng Sean Ye 	union mcreg_dramcfg_hi drcfg_hi;
783e4b86885SCheng Sean Ye 	union mcreg_drammisc drmisc;
784e4b86885SCheng Sean Ye 	union mcreg_bankaddrmap baddrmap;
785e4b86885SCheng Sean Ye 	mc_props_t *mcp = &mc->mc_props;
786e4b86885SCheng Sean Ye 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
787e4b86885SCheng Sean Ye 	int maskdivisor;
788e4b86885SCheng Sean Ye 	int wide = 0;
789e4b86885SCheng Sean Ye 	uint32_t rev = mc->mc_props.mcp_rev;
790e4b86885SCheng Sean Ye 	int i;
791e4b86885SCheng Sean Ye 	mcamd_hdl_t hdl;
792e4b86885SCheng Sean Ye 
793e4b86885SCheng Sean Ye 	mcamd_mkhdl(&hdl);	/* to call into common code */
794e4b86885SCheng Sean Ye 
795e4b86885SCheng Sean Ye 	/*
796e4b86885SCheng Sean Ye 	 * Read Function 2 DRAM Configuration High and Low registers.  The High
797e4b86885SCheng Sean Ye 	 * part is mostly concerned with memory clocks etc and we'll not have
798e4b86885SCheng Sean Ye 	 * any use for that.  The Low component tells us if ECC is enabled,
799e4b86885SCheng Sean Ye 	 * if we're in 64- or 128-bit MC mode, how the upper chip-selects
800e4b86885SCheng Sean Ye 	 * are mapped, which chip-select pairs are using x4 parts, etc.
801e4b86885SCheng Sean Ye 	 */
802e4b86885SCheng Sean Ye 	MCREG_VAL32(&drcfg_lo) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGLO);
803e4b86885SCheng Sean Ye 	MCREG_VAL32(&drcfg_hi) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGHI);
804e4b86885SCheng Sean Ye 	mcr->mcr_dramcfglo = MCREG_VAL32(&drcfg_lo);
805e4b86885SCheng Sean Ye 	mcr->mcr_dramcfghi = MCREG_VAL32(&drcfg_hi);
806e4b86885SCheng Sean Ye 
807e4b86885SCheng Sean Ye 	/*
808e4b86885SCheng Sean Ye 	 * Note the DRAM controller width.  The 64/128 bit is in a different
809e4b86885SCheng Sean Ye 	 * bit position for revision F and G.
810e4b86885SCheng Sean Ye 	 */
811e4b86885SCheng Sean Ye 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
812e4b86885SCheng Sean Ye 		wide = MCREG_FIELD_F_revFG(&drcfg_lo, Width128);
813e4b86885SCheng Sean Ye 	} else {
814e4b86885SCheng Sean Ye 		wide = MCREG_FIELD_F_preF(&drcfg_lo, Width128);
815e4b86885SCheng Sean Ye 	}
816e4b86885SCheng Sean Ye 	mcp->mcp_accwidth = wide ? 128 : 64;
817e4b86885SCheng Sean Ye 
818e4b86885SCheng Sean Ye 	/*
819e4b86885SCheng Sean Ye 	 * Read Function 2 DRAM Controller Miscellaenous Regsiter for those
820e4b86885SCheng Sean Ye 	 * revs that support it.  This include the Mod64Mux indication on
821e4b86885SCheng Sean Ye 	 * these revs - for rev E it is in DRAM config low.
822e4b86885SCheng Sean Ye 	 */
823e4b86885SCheng Sean Ye 	if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
824e4b86885SCheng Sean Ye 		mcr->mcr_drammisc = MCREG_VAL32(&drmisc) =
825e4b86885SCheng Sean Ye 		    mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMMISC);
826e4b86885SCheng Sean Ye 		mcp->mcp_mod64mux = MCREG_FIELD_F_revFG(&drmisc, Mod64Mux);
827e4b86885SCheng Sean Ye 	} else if (MC_REV_MATCH(rev, MC_F_REV_E)) {
828e4b86885SCheng Sean Ye 		mcp->mcp_mod64mux = MCREG_FIELD_F_preF(&drcfg_lo, Mod64BitMux);
829e4b86885SCheng Sean Ye 	}
830e4b86885SCheng Sean Ye 
831e4b86885SCheng Sean Ye 	/*
832e4b86885SCheng Sean Ye 	 * Read Function 2 DRAM Bank Address Mapping.  This encodes the
833e4b86885SCheng Sean Ye 	 * type of DIMM module in use for each chip-select pair.
834e4b86885SCheng Sean Ye 	 * Prior ro revision F it also tells us whether BankSwizzle mode
835e4b86885SCheng Sean Ye 	 * is enabled - in rev F that has moved to dram config hi register.
836e4b86885SCheng Sean Ye 	 */
837e4b86885SCheng Sean Ye 	mcp->mcp_csbankmapreg = MCREG_VAL32(&baddrmap) =
838e4b86885SCheng Sean Ye 	    mc_pcicfg_get32(cfghdl, MC_DC_REG_BANKADDRMAP);
839e4b86885SCheng Sean Ye 
840e4b86885SCheng Sean Ye 	/*
841e4b86885SCheng Sean Ye 	 * Determine whether bank swizzle mode is active.  Bank swizzling was
842e4b86885SCheng Sean Ye 	 * introduced as an option in rev E,  but the bit that indicates it
843e4b86885SCheng Sean Ye 	 * is enabled has moved in revs F/G.
844e4b86885SCheng Sean Ye 	 */
845e4b86885SCheng Sean Ye 	if (MC_REV_MATCH(rev, MC_F_REV_E)) {
846e4b86885SCheng Sean Ye 		mcp->mcp_bnkswzl =
847e4b86885SCheng Sean Ye 		    MCREG_FIELD_F_preF(&baddrmap, BankSwizzleMode);
848e4b86885SCheng Sean Ye 	} else if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
849e4b86885SCheng Sean Ye 		mcp->mcp_bnkswzl = MCREG_FIELD_F_revFG(&drcfg_hi,
850e4b86885SCheng Sean Ye 		    BankSwizzleMode);
851e4b86885SCheng Sean Ye 	}
852e4b86885SCheng Sean Ye 
853e4b86885SCheng Sean Ye 	/*
854e4b86885SCheng Sean Ye 	 * Read the DRAM CS Base and DRAM CS Mask registers.  Revisions prior
855e4b86885SCheng Sean Ye 	 * to F have an equal number of base and mask registers; revision F
856e4b86885SCheng Sean Ye 	 * has twice as many base registers as masks.
857e4b86885SCheng Sean Ye 	 */
858e4b86885SCheng Sean Ye 	maskdivisor = MC_REV_MATCH(rev, MC_F_REVS_FG) ? 2 : 1;
859e4b86885SCheng Sean Ye 
860e4b86885SCheng Sean Ye 	mc_prop_read_pair(cfghdl,
861e4b86885SCheng Sean Ye 	    (uint32_t *)base, MC_DC_REG_CSBASE_0, MC_CHIP_NCS,
862e4b86885SCheng Sean Ye 	    (uint32_t *)mask, MC_DC_REG_CSMASK_0, MC_CHIP_NCS / maskdivisor,
863e4b86885SCheng Sean Ye 	    MC_DC_REG_CS_INCR);
864e4b86885SCheng Sean Ye 
865e4b86885SCheng Sean Ye 	/*
866e4b86885SCheng Sean Ye 	 * Create a cs node for each enabled chip-select as well as
867e4b86885SCheng Sean Ye 	 * any appointed online spare chip-selects and for any that have
868e4b86885SCheng Sean Ye 	 * failed test.
869e4b86885SCheng Sean Ye 	 */
870e4b86885SCheng Sean Ye 	for (i = 0; i < MC_CHIP_NCS; i++) {
871e4b86885SCheng Sean Ye 		mc_cs_t *mccs;
872e4b86885SCheng Sean Ye 		uint64_t csbase, csmask;
873e4b86885SCheng Sean Ye 		size_t sz;
874e4b86885SCheng Sean Ye 		int csbe, spare, testfail;
875e4b86885SCheng Sean Ye 
876e4b86885SCheng Sean Ye 		if (MC_REV_MATCH(rev, MC_F_REVS_FG)) {
877e4b86885SCheng Sean Ye 			csbe = MCREG_FIELD_F_revFG(&base[i], CSEnable);
878e4b86885SCheng Sean Ye 			spare = MCREG_FIELD_F_revFG(&base[i], Spare);
879e4b86885SCheng Sean Ye 			testfail = MCREG_FIELD_F_revFG(&base[i], TestFail);
880e4b86885SCheng Sean Ye 		} else {
881e4b86885SCheng Sean Ye 			csbe = MCREG_FIELD_F_preF(&base[i], CSEnable);
882e4b86885SCheng Sean Ye 			spare = 0;
883e4b86885SCheng Sean Ye 			testfail = 0;
884e4b86885SCheng Sean Ye 		}
885e4b86885SCheng Sean Ye 
886e4b86885SCheng Sean Ye 		/* Testing hook */
887e4b86885SCheng Sean Ye 		if (testfail_mcnum != -1 && testfail_csnum != -1 &&
888e4b86885SCheng Sean Ye 		    mcp->mcp_num == testfail_mcnum && i == testfail_csnum) {
889e4b86885SCheng Sean Ye 			csbe = spare = 0;
890e4b86885SCheng Sean Ye 			testfail = 1;
891e4b86885SCheng Sean Ye 			cmn_err(CE_NOTE, "Pretending MC %d CS %d failed test",
892e4b86885SCheng Sean Ye 			    testfail_mcnum, testfail_csnum);
893e4b86885SCheng Sean Ye 		}
894e4b86885SCheng Sean Ye 
895e4b86885SCheng Sean Ye 		/*
896e4b86885SCheng Sean Ye 		 * If the chip-select is not enabled then skip it unless
897e4b86885SCheng Sean Ye 		 * it is a designated online spare or is marked with TestFail.
898e4b86885SCheng Sean Ye 		 */
899e4b86885SCheng Sean Ye 		if (!csbe && !(spare || testfail))
900e4b86885SCheng Sean Ye 			continue;
901e4b86885SCheng Sean Ye 
902e4b86885SCheng Sean Ye 		/*
903e4b86885SCheng Sean Ye 		 * For an enabled or spare chip-select the Bank Address Mapping
904e4b86885SCheng Sean Ye 		 * register will be valid as will the chip-select mask.  The
905e4b86885SCheng Sean Ye 		 * base will not be valid but we'll read and store it anyway.
906e4b86885SCheng Sean Ye 		 * We will not know whether the spare is already swapped in
907e4b86885SCheng Sean Ye 		 * until MC function 3 attaches.
908e4b86885SCheng Sean Ye 		 */
909e4b86885SCheng Sean Ye 		if (csbe || spare) {
910e4b86885SCheng Sean Ye 			if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0)
911e4b86885SCheng Sean Ye 				continue;
912e4b86885SCheng Sean Ye 			csbase = MC_CSBASE(&base[i], rev);
913e4b86885SCheng Sean Ye 			csmask = MC_CSMASK(&mask[i / maskdivisor], rev);
914e4b86885SCheng Sean Ye 		} else {
915e4b86885SCheng Sean Ye 			sz = 0;
916e4b86885SCheng Sean Ye 			csbase = csmask = 0;
917e4b86885SCheng Sean Ye 		}
918e4b86885SCheng Sean Ye 
919e4b86885SCheng Sean Ye 		mccs = mc_cs_create(mc, i, csbase, csmask, sz,
920e4b86885SCheng Sean Ye 		    csbe, spare, testfail);
921e4b86885SCheng Sean Ye 
922e4b86885SCheng Sean Ye 		if (mc->mc_cslist == NULL)
923e4b86885SCheng Sean Ye 			mc->mc_cslist = mccs;
924e4b86885SCheng Sean Ye 		else
925e4b86885SCheng Sean Ye 			mc->mc_cslast->mccs_next = mccs;
926e4b86885SCheng Sean Ye 		mc->mc_cslast = mccs;
927e4b86885SCheng Sean Ye 
928e4b86885SCheng Sean Ye 		mccs->mccs_cfgregs.csr_csbase = MCREG_VAL32(&base[i]);
929e4b86885SCheng Sean Ye 		mccs->mccs_cfgregs.csr_csmask =
930e4b86885SCheng Sean Ye 		    MCREG_VAL32(&mask[i / maskdivisor]);
931e4b86885SCheng Sean Ye 
932e4b86885SCheng Sean Ye 		/*
933e4b86885SCheng Sean Ye 		 * Check for cs bank interleaving - some bits clear in the
934e4b86885SCheng Sean Ye 		 * lower mask.  All banks must/will have the same lomask bits
935e4b86885SCheng Sean Ye 		 * if cs interleaving is active.
936e4b86885SCheng Sean Ye 		 */
937e4b86885SCheng Sean Ye 		if (csbe && !mcp->mcp_csintlvfctr) {
938e4b86885SCheng Sean Ye 			int bitno, ibits = 0;
939e4b86885SCheng Sean Ye 			for (bitno = MC_CSMASKLO_LOBIT(rev);
940e4b86885SCheng Sean Ye 			    bitno <= MC_CSMASKLO_HIBIT(rev); bitno++) {
941e4b86885SCheng Sean Ye 				if (!(csmask & (1 << bitno)))
942e4b86885SCheng Sean Ye 					ibits++;
943e4b86885SCheng Sean Ye 			}
944e4b86885SCheng Sean Ye 			mcp->mcp_csintlvfctr = 1 << ibits;
945e4b86885SCheng Sean Ye 		}
946e4b86885SCheng Sean Ye 	}
947e4b86885SCheng Sean Ye 
948e4b86885SCheng Sean Ye 	/*
949e4b86885SCheng Sean Ye 	 * If there is no chip-select interleave on this node determine
950e4b86885SCheng Sean Ye 	 * whether the chip-select ranks are contiguous or if there
951e4b86885SCheng Sean Ye 	 * is a hole.
952e4b86885SCheng Sean Ye 	 */
953e4b86885SCheng Sean Ye 	if (mcp->mcp_csintlvfctr == 1) {
954e4b86885SCheng Sean Ye 		mc_cs_t *csp[MC_CHIP_NCS];
955e4b86885SCheng Sean Ye 		mc_cs_t *mccs;
956e4b86885SCheng Sean Ye 		int ncsbe = 0;
957e4b86885SCheng Sean Ye 
958e4b86885SCheng Sean Ye 		for (mccs = mc->mc_cslist; mccs != NULL;
959e4b86885SCheng Sean Ye 		    mccs = mccs->mccs_next) {
960e4b86885SCheng Sean Ye 			if (mccs->mccs_props.csp_csbe)
961e4b86885SCheng Sean Ye 				csp[ncsbe++] = mccs;
962e4b86885SCheng Sean Ye 		}
963e4b86885SCheng Sean Ye 
964e4b86885SCheng Sean Ye 		if (ncsbe != 0) {
965e4b86885SCheng Sean Ye 			qsort((void *)csp, ncsbe, sizeof (mc_cs_t *),
966e4b86885SCheng Sean Ye 			    (int (*)(const void *, const void *))csbasecmp);
967e4b86885SCheng Sean Ye 
968e4b86885SCheng Sean Ye 			for (i = 1; i < ncsbe; i++) {
969e4b86885SCheng Sean Ye 				if (csp[i]->mccs_props.csp_base !=
970e4b86885SCheng Sean Ye 				    csp[i - 1]->mccs_props.csp_base +
971e4b86885SCheng Sean Ye 				    csp[i - 1]->mccs_props.csp_size)
972e4b86885SCheng Sean Ye 					mc->mc_csdiscontig = 1;
973e4b86885SCheng Sean Ye 			}
974e4b86885SCheng Sean Ye 		}
975e4b86885SCheng Sean Ye 	}
976e4b86885SCheng Sean Ye 
977e4b86885SCheng Sean Ye 
978e4b86885SCheng Sean Ye 	/*
979e4b86885SCheng Sean Ye 	 * Since we do not attach to MC function 3 go ahead and read some
980e4b86885SCheng Sean Ye 	 * config parameters from it now.
981e4b86885SCheng Sean Ye 	 */
982e4b86885SCheng Sean Ye 	mc_getmiscctl(mc);
983e4b86885SCheng Sean Ye 
984e4b86885SCheng Sean Ye 	/*
985e4b86885SCheng Sean Ye 	 * Now that we have discovered all enabled/spare/testfail chip-selects
986e4b86885SCheng Sean Ye 	 * we divine the associated DIMM configuration.
987e4b86885SCheng Sean Ye 	 */
988e4b86885SCheng Sean Ye 	mc_dimmlist_create(mc);
989e4b86885SCheng Sean Ye }
990e4b86885SCheng Sean Ye 
991e4b86885SCheng Sean Ye typedef struct mc_bind_map {
992e4b86885SCheng Sean Ye 	const char *bm_bindnm;	 /* attachment binding name */
993e4b86885SCheng Sean Ye 	enum mc_funcnum bm_func; /* PCI config space function number for bind */
994e4b86885SCheng Sean Ye 	const char *bm_model;	 /* value for device node model property */
995e4b86885SCheng Sean Ye 	void (*bm_mkprops)(mc_pcicfg_hdl_t, mc_t *);
996e4b86885SCheng Sean Ye } mc_bind_map_t;
997e4b86885SCheng Sean Ye 
998e4b86885SCheng Sean Ye /*
999e4b86885SCheng Sean Ye  * Do not attach to MC function 3 - agpgart already attaches to that.
1000e4b86885SCheng Sean Ye  * Function 3 may be a good candidate for a nexus driver to fan it out
1001e4b86885SCheng Sean Ye  * into virtual devices by functionality.  We will use pci_mech1_getl
1002e4b86885SCheng Sean Ye  * to retrieve the function 3 parameters we require.
1003e4b86885SCheng Sean Ye  */
1004e4b86885SCheng Sean Ye 
1005e4b86885SCheng Sean Ye static const mc_bind_map_t mc_bind_map[] = {
1006e4b86885SCheng Sean Ye 	{ MC_FUNC_HTCONFIG_BINDNM, MC_FUNC_HTCONFIG,
1007e4b86885SCheng Sean Ye 	    "AMD Memory Controller (HT Configuration)", mc_mkprops_htcfg },
1008e4b86885SCheng Sean Ye 	{ MC_FUNC_ADDRMAP_BINDNM, MC_FUNC_ADDRMAP,
1009e4b86885SCheng Sean Ye 	    "AMD Memory Controller (Address Map)", mc_mkprops_addrmap },
1010e4b86885SCheng Sean Ye 	{ MC_FUNC_DRAMCTL_BINDNM, MC_FUNC_DRAMCTL,
1011e4b86885SCheng Sean Ye 	    "AMD Memory Controller (DRAM Controller & HT Trace)",
1012e4b86885SCheng Sean Ye 	    mc_mkprops_dramctl },
1013e4b86885SCheng Sean Ye 	NULL
1014e4b86885SCheng Sean Ye };
1015e4b86885SCheng Sean Ye 
1016e4b86885SCheng Sean Ye /*ARGSUSED*/
1017e4b86885SCheng Sean Ye static int
1018e4b86885SCheng Sean Ye mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
1019e4b86885SCheng Sean Ye {
1020e4b86885SCheng Sean Ye 	if (otyp != OTYP_CHR)
1021e4b86885SCheng Sean Ye 		return (EINVAL);
1022e4b86885SCheng Sean Ye 
1023e4b86885SCheng Sean Ye 	rw_enter(&mc_lock, RW_READER);
1024e4b86885SCheng Sean Ye 	if (mc_lookup_by_chipid(getminor(*devp)) == NULL) {
1025e4b86885SCheng Sean Ye 		rw_exit(&mc_lock);
1026e4b86885SCheng Sean Ye 		return (EINVAL);
1027e4b86885SCheng Sean Ye 	}
1028e4b86885SCheng Sean Ye 	rw_exit(&mc_lock);
1029e4b86885SCheng Sean Ye 
1030e4b86885SCheng Sean Ye 	return (0);
1031e4b86885SCheng Sean Ye }
1032e4b86885SCheng Sean Ye 
1033e4b86885SCheng Sean Ye /*ARGSUSED*/
1034e4b86885SCheng Sean Ye static int
1035e4b86885SCheng Sean Ye mc_close(dev_t dev, int flag, int otyp, cred_t *credp)
1036e4b86885SCheng Sean Ye {
1037e4b86885SCheng Sean Ye 	return (0);
1038e4b86885SCheng Sean Ye }
1039e4b86885SCheng Sean Ye 
1040e4b86885SCheng Sean Ye /*
1041e4b86885SCheng Sean Ye  * Enable swap from chip-select csnum to the spare chip-select on this
1042e4b86885SCheng Sean Ye  * memory controller (if any).
1043e4b86885SCheng Sean Ye  */
1044e4b86885SCheng Sean Ye 
1045e4b86885SCheng Sean Ye int mc_swapdonetime = 30;	/* max number of seconds to wait for SwapDone */
1046e4b86885SCheng Sean Ye 
1047e4b86885SCheng Sean Ye static int
1048e4b86885SCheng Sean Ye mc_onlinespare(mc_t *mc, int csnum)
1049e4b86885SCheng Sean Ye {
1050e4b86885SCheng Sean Ye 	mc_props_t *mcp = &mc->mc_props;
1051e4b86885SCheng Sean Ye 	union mcreg_sparectl sparectl;
1052e4b86885SCheng Sean Ye 	union mcreg_scrubctl scrubctl;
1053e4b86885SCheng Sean Ye 	mc_cs_t *mccs;
1054e4b86885SCheng Sean Ye 	hrtime_t tmax;
1055e4b86885SCheng Sean Ye 	int i = 0;
1056e4b86885SCheng Sean Ye 
1057e4b86885SCheng Sean Ye 	ASSERT(RW_WRITE_HELD(&mc_lock));
1058e4b86885SCheng Sean Ye 
1059e4b86885SCheng Sean Ye 	if (!MC_REV_MATCH(mcp->mcp_rev, MC_F_REVS_FG))
1060e4b86885SCheng Sean Ye 		return (ENOTSUP);	/* MC rev does not offer online spare */
1061e4b86885SCheng Sean Ye 	else if (mcp->mcp_sparecs == MC_INVALNUM)
1062e4b86885SCheng Sean Ye 		return (ENODEV);	/* Supported, but no spare configured */
1063e4b86885SCheng Sean Ye 	else if (mcp->mcp_badcs != MC_INVALNUM)
1064e4b86885SCheng Sean Ye 		return (EBUSY);		/* Spare already swapped in */
1065e4b86885SCheng Sean Ye 	else if (csnum == mcp->mcp_sparecs)
1066e4b86885SCheng Sean Ye 		return (EINVAL);	/* Can't spare the spare! */
1067e4b86885SCheng Sean Ye 
1068e4b86885SCheng Sean Ye 	for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) {
1069e4b86885SCheng Sean Ye 		if (mccs->mccs_props.csp_num == csnum)
1070e4b86885SCheng Sean Ye 			break;
1071e4b86885SCheng Sean Ye 	}
1072e4b86885SCheng Sean Ye 	if (mccs == NULL)
1073e4b86885SCheng Sean Ye 		return (EINVAL);	/* nominated bad CS does not exist */
1074e4b86885SCheng Sean Ye 
1075e4b86885SCheng Sean Ye 	/*
1076e4b86885SCheng Sean Ye 	 * If the DRAM Scrubber is not enabled then the swap cannot succeed.
1077e4b86885SCheng Sean Ye 	 */
1078e4b86885SCheng Sean Ye 	MCREG_VAL32(&scrubctl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
1079e4b86885SCheng Sean Ye 	    MC_CTL_REG_SCRUBCTL);
1080e4b86885SCheng Sean Ye 	if (MCREG_FIELD_CMN(&scrubctl, DramScrub) == 0)
1081e4b86885SCheng Sean Ye 		return (ENODEV);	/* DRAM scrubber not enabled */
1082e4b86885SCheng Sean Ye 
1083e4b86885SCheng Sean Ye 	/*
1084e4b86885SCheng Sean Ye 	 * Read Online Spare Comtrol Register again, just in case our
1085e4b86885SCheng Sean Ye 	 * state does not reflect reality.
1086e4b86885SCheng Sean Ye 	 */
1087e4b86885SCheng Sean Ye 	MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL,
1088e4b86885SCheng Sean Ye 	    MC_CTL_REG_SPARECTL);
1089e4b86885SCheng Sean Ye 
1090e4b86885SCheng Sean Ye 	if (MCREG_FIELD_F_revFG(&sparectl, SwapDone))
1091e4b86885SCheng Sean Ye 		return (EBUSY);
1092e4b86885SCheng Sean Ye 
1093e4b86885SCheng Sean Ye 	/* Write to the BadDramCs field */
1094e4b86885SCheng Sean Ye 	MCREG_FIELD_F_revFG(&sparectl, BadDramCs) = csnum;
1095e4b86885SCheng Sean Ye 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
1096e4b86885SCheng Sean Ye 	    MCREG_VAL32(&sparectl));
1097e4b86885SCheng Sean Ye 
1098e4b86885SCheng Sean Ye 	/* And request that the swap to the spare start */
1099e4b86885SCheng Sean Ye 	MCREG_FIELD_F_revFG(&sparectl, SwapEn) = 1;
1100e4b86885SCheng Sean Ye 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL,
1101e4b86885SCheng Sean Ye 	    MCREG_VAL32(&sparectl));
1102e4b86885SCheng Sean Ye 
1103e4b86885SCheng Sean Ye 	/*
1104e4b86885SCheng Sean Ye 	 * Poll for SwapDone - we have disabled notification by interrupt.
1105e4b86885SCheng Sean Ye 	 * Swap takes "several CPU cycles, depending on the DRAM speed, but
1106e4b86885SCheng Sean Ye 	 * is performed in the background" (Family 0Fh Bios Porting Guide).
1107e4b86885SCheng Sean Ye 	 * We're in a slow ioctl path so there is no harm in waiting around
1108e4b86885SCheng Sean Ye 	 * a bit - consumers of the ioctl must be aware that it may take
1109e4b86885SCheng Sean Ye 	 * a moment.  We will poll for up to mc_swapdonetime seconds,
1110e4b86885SCheng Sean Ye 	 * limiting that to 120s.
1111e4b86885SCheng Sean Ye 	 *
1112e4b86885SCheng Sean Ye 	 * The swap is performed by the DRAM scrubber (which must be enabled)
1113e4b86885SCheng Sean Ye 	 * whose scrub rate is accelerated for the duration of the swap.
1114e4b86885SCheng Sean Ye 	 * The maximum swap rate is 40.0ns per 64 bytes, so the maximum
1115e4b86885SCheng Sean Ye 	 * supported cs size of 16GB would take 10.7s at that max rate
1116e4b86885SCheng Sean Ye 	 * of 25000000 scrubs/second.
1117e4b86885SCheng Sean Ye 	 */
1118e4b86885SCheng Sean Ye 	tmax = gethrtime() + MIN(mc_swapdonetime, 120) * 1000000000ULL;
1119e4b86885SCheng Sean Ye 	do {
1120e4b86885SCheng Sean Ye 		if (i++ < 20)
1121e4b86885SCheng Sean Ye 			delay(drv_usectohz(100000));	/* 0.1s for up to 2s */
1122e4b86885SCheng Sean Ye 		else
1123e4b86885SCheng Sean Ye 			delay(drv_usectohz(500000));	/* 0.5s */
1124e4b86885SCheng Sean Ye 
1125e4b86885SCheng Sean Ye 		MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc,
1126e4b86885SCheng Sean Ye 		    MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
1127e4b86885SCheng Sean Ye 	} while (!MCREG_FIELD_F_revFG(&sparectl, SwapDone) &&
1128e4b86885SCheng Sean Ye 	    gethrtime() < tmax);
1129e4b86885SCheng Sean Ye 
1130e4b86885SCheng Sean Ye 	if (!MCREG_FIELD_F_revFG(&sparectl, SwapDone))
1131e4b86885SCheng Sean Ye 		return (ETIME);		/* Operation timed out */
1132e4b86885SCheng Sean Ye 
1133e4b86885SCheng Sean Ye 	mcp->mcp_badcs = csnum;
1134e4b86885SCheng Sean Ye 	mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl);
1135e4b86885SCheng Sean Ye 	mc->mc_spareswaptime = gethrtime();
1136e4b86885SCheng Sean Ye 
1137e4b86885SCheng Sean Ye 	return (0);
1138e4b86885SCheng Sean Ye }
1139e4b86885SCheng Sean Ye 
1140e4b86885SCheng Sean Ye /*ARGSUSED*/
1141e4b86885SCheng Sean Ye static int
1142e4b86885SCheng Sean Ye mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
1143e4b86885SCheng Sean Ye {
1144e4b86885SCheng Sean Ye 	int rc = 0;
1145e4b86885SCheng Sean Ye 	mc_t *mc;
1146e4b86885SCheng Sean Ye 
1147e4b86885SCheng Sean Ye 	if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT &&
1148e4b86885SCheng Sean Ye 	    cmd != MC_IOC_ONLINESPARE_EN)
1149e4b86885SCheng Sean Ye 		return (EINVAL);
1150e4b86885SCheng Sean Ye 
1151e4b86885SCheng Sean Ye 	rw_enter(&mc_lock, RW_READER);
1152e4b86885SCheng Sean Ye 
1153e4b86885SCheng Sean Ye 	if ((mc = mc_lookup_by_chipid(getminor(dev))) == NULL) {
1154e4b86885SCheng Sean Ye 		rw_exit(&mc_lock);
1155e4b86885SCheng Sean Ye 		return (EINVAL);
1156e4b86885SCheng Sean Ye 	}
1157e4b86885SCheng Sean Ye 
1158e4b86885SCheng Sean Ye 	switch (cmd) {
1159e4b86885SCheng Sean Ye 	case MC_IOC_SNAPSHOT_INFO: {
1160e4b86885SCheng Sean Ye 		mc_snapshot_info_t mcs;
1161e4b86885SCheng Sean Ye 
1162e4b86885SCheng Sean Ye 		if (mc_snapshot_update(mc) < 0) {
1163e4b86885SCheng Sean Ye 			rw_exit(&mc_lock);
1164e4b86885SCheng Sean Ye 			return (EIO);
1165e4b86885SCheng Sean Ye 		}
1166e4b86885SCheng Sean Ye 
1167e4b86885SCheng Sean Ye 		mcs.mcs_size = mc->mc_snapshotsz;
1168e4b86885SCheng Sean Ye 		mcs.mcs_gen = mc->mc_snapshotgen;
1169e4b86885SCheng Sean Ye 
1170e4b86885SCheng Sean Ye 		if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t),
1171e4b86885SCheng Sean Ye 		    mode) < 0)
1172e4b86885SCheng Sean Ye 			rc = EFAULT;
1173e4b86885SCheng Sean Ye 		break;
1174e4b86885SCheng Sean Ye 	}
1175e4b86885SCheng Sean Ye 
1176e4b86885SCheng Sean Ye 	case MC_IOC_SNAPSHOT:
1177e4b86885SCheng Sean Ye 		if (mc_snapshot_update(mc) < 0) {
1178e4b86885SCheng Sean Ye 			rw_exit(&mc_lock);
1179e4b86885SCheng Sean Ye 			return (EIO);
1180e4b86885SCheng Sean Ye 		}
1181e4b86885SCheng Sean Ye 
1182e4b86885SCheng Sean Ye 		if (ddi_copyout(mc->mc_snapshot, (void *)arg, mc->mc_snapshotsz,
1183e4b86885SCheng Sean Ye 		    mode) < 0)
1184e4b86885SCheng Sean Ye 			rc = EFAULT;
1185e4b86885SCheng Sean Ye 		break;
1186e4b86885SCheng Sean Ye 
1187e4b86885SCheng Sean Ye 	case MC_IOC_ONLINESPARE_EN:
1188e4b86885SCheng Sean Ye 		if (drv_priv(credp) != 0) {
1189e4b86885SCheng Sean Ye 			rw_exit(&mc_lock);
1190e4b86885SCheng Sean Ye 			return (EPERM);
1191e4b86885SCheng Sean Ye 		}
1192e4b86885SCheng Sean Ye 
1193e4b86885SCheng Sean Ye 		if (!rw_tryupgrade(&mc_lock)) {
1194e4b86885SCheng Sean Ye 			rw_exit(&mc_lock);
1195e4b86885SCheng Sean Ye 			return (EAGAIN);
1196e4b86885SCheng Sean Ye 		}
1197e4b86885SCheng Sean Ye 
1198e4b86885SCheng Sean Ye 		if ((rc = mc_onlinespare(mc, (int)arg)) == 0) {
1199e4b86885SCheng Sean Ye 			mc_snapshot_destroy(mc);
1200e4b86885SCheng Sean Ye 			nvlist_free(mc->mc_nvl);
1201e4b86885SCheng Sean Ye 			mc->mc_nvl = mc_nvl_create(mc);
1202e4b86885SCheng Sean Ye 		}
1203e4b86885SCheng Sean Ye 
1204e4b86885SCheng Sean Ye 		break;
1205e4b86885SCheng Sean Ye 	}
1206e4b86885SCheng Sean Ye 
1207e4b86885SCheng Sean Ye 	rw_exit(&mc_lock);
1208e4b86885SCheng Sean Ye 
1209e4b86885SCheng Sean Ye 	return (rc);
1210e4b86885SCheng Sean Ye }
1211e4b86885SCheng Sean Ye 
1212e4b86885SCheng Sean Ye static struct cb_ops mc_cb_ops = {
1213e4b86885SCheng Sean Ye 	mc_open,
1214e4b86885SCheng Sean Ye 	mc_close,
1215e4b86885SCheng Sean Ye 	nodev,		/* not a block driver */
1216e4b86885SCheng Sean Ye 	nodev,		/* no print routine */
1217e4b86885SCheng Sean Ye 	nodev,		/* no dump routine */
1218e4b86885SCheng Sean Ye 	nodev,		/* no read routine */
1219e4b86885SCheng Sean Ye 	nodev,		/* no write routine */
1220e4b86885SCheng Sean Ye 	mc_ioctl,
1221e4b86885SCheng Sean Ye 	nodev,		/* no devmap routine */
1222e4b86885SCheng Sean Ye 	nodev,		/* no mmap routine */
1223e4b86885SCheng Sean Ye 	nodev,		/* no segmap routine */
1224e4b86885SCheng Sean Ye 	nochpoll,	/* no chpoll routine */
1225e4b86885SCheng Sean Ye 	ddi_prop_op,
1226e4b86885SCheng Sean Ye 	0,		/* not a STREAMS driver */
1227e4b86885SCheng Sean Ye 	D_NEW | D_MP,	/* safe for multi-thread/multi-processor */
1228e4b86885SCheng Sean Ye };
1229e4b86885SCheng Sean Ye 
1230e4b86885SCheng Sean Ye /*ARGSUSED*/
1231e4b86885SCheng Sean Ye static int
1232e4b86885SCheng Sean Ye mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1233e4b86885SCheng Sean Ye {
1234e4b86885SCheng Sean Ye 	int rc = DDI_SUCCESS;
1235e4b86885SCheng Sean Ye 	mc_t *mc;
1236e4b86885SCheng Sean Ye 
1237e4b86885SCheng Sean Ye 	if (infocmd != DDI_INFO_DEVT2DEVINFO &&
1238e4b86885SCheng Sean Ye 	    infocmd != DDI_INFO_DEVT2INSTANCE) {
1239e4b86885SCheng Sean Ye 		*result = NULL;
1240e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1241e4b86885SCheng Sean Ye 	}
1242e4b86885SCheng Sean Ye 
1243e4b86885SCheng Sean Ye 	rw_enter(&mc_lock, RW_READER);
1244e4b86885SCheng Sean Ye 
1245e4b86885SCheng Sean Ye 	if ((mc = mc_lookup_by_chipid(getminor((dev_t)arg))) == NULL ||
1246e4b86885SCheng Sean Ye 	    mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi == NULL) {
1247e4b86885SCheng Sean Ye 		rc = DDI_FAILURE;
1248e4b86885SCheng Sean Ye 	} else if (infocmd == DDI_INFO_DEVT2DEVINFO) {
1249e4b86885SCheng Sean Ye 		*result = mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi;
1250e4b86885SCheng Sean Ye 	} else {
1251e4b86885SCheng Sean Ye 		*result = (void *)(uintptr_t)
1252e4b86885SCheng Sean Ye 		    mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_instance;
1253e4b86885SCheng Sean Ye 	}
1254e4b86885SCheng Sean Ye 
1255e4b86885SCheng Sean Ye 	rw_exit(&mc_lock);
1256e4b86885SCheng Sean Ye 
1257e4b86885SCheng Sean Ye 	return (rc);
1258e4b86885SCheng Sean Ye }
1259e4b86885SCheng Sean Ye 
1260e4b86885SCheng Sean Ye /*ARGSUSED2*/
1261e4b86885SCheng Sean Ye static int
1262e4b86885SCheng Sean Ye mc_fm_handle(dev_info_t *dip, ddi_fm_error_t *fmerr, const void *arg)
1263e4b86885SCheng Sean Ye {
1264e4b86885SCheng Sean Ye 	pci_ereport_post(dip, fmerr, NULL);
1265e4b86885SCheng Sean Ye 	return (fmerr->fme_status);
1266e4b86885SCheng Sean Ye }
1267e4b86885SCheng Sean Ye 
1268e4b86885SCheng Sean Ye static void
1269e4b86885SCheng Sean Ye mc_fm_init(dev_info_t *dip)
1270e4b86885SCheng Sean Ye {
1271e4b86885SCheng Sean Ye 	int fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE;
1272e4b86885SCheng Sean Ye 	ddi_fm_init(dip, &fmcap, NULL);
1273e4b86885SCheng Sean Ye 	pci_ereport_setup(dip);
1274e4b86885SCheng Sean Ye 	ddi_fm_handler_register(dip, mc_fm_handle, NULL);
1275e4b86885SCheng Sean Ye }
1276e4b86885SCheng Sean Ye 
1277074bb90dSTom Pothier static void
1278074bb90dSTom Pothier mc_read_smbios(mc_t *mc, dev_info_t *dip)
1279074bb90dSTom Pothier {
1280074bb90dSTom Pothier 
1281074bb90dSTom Pothier 	uint16_t bdf;
1282f32a9dd1SSrihari Venkatesan 	pci_regspec_t *pci_rp = NULL;
1283074bb90dSTom Pothier 	uint32_t phys_hi;
1284f32a9dd1SSrihari Venkatesan 	int m = 0;
1285074bb90dSTom Pothier 	uint_t chip_inst;
1286074bb90dSTom Pothier 	int rc = 0;
1287074bb90dSTom Pothier 
1288074bb90dSTom Pothier 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
1289074bb90dSTom Pothier 	    (caddr_t)&pci_rp, &m) == DDI_SUCCESS) {
1290074bb90dSTom Pothier 		phys_hi = pci_rp->pci_phys_hi;
1291074bb90dSTom Pothier 		bdf = (uint16_t)(PCI_REG_BDFR_G(phys_hi) >>
1292074bb90dSTom Pothier 		    PCI_REG_FUNC_SHIFT);
1293f32a9dd1SSrihari Venkatesan 		kmem_free(pci_rp, m);
1294f32a9dd1SSrihari Venkatesan 		pci_rp = NULL;
1295074bb90dSTom Pothier 
1296074bb90dSTom Pothier 		rc = fm_smb_mc_chipinst(bdf, &chip_inst);
1297074bb90dSTom Pothier 		if (rc == 0) {
1298074bb90dSTom Pothier 			mc->smb_chipid = chip_inst;
1299074bb90dSTom Pothier 		} else {
1300074bb90dSTom Pothier #ifdef DEBUG
1301*97689f66SSrihari Venkatesan 			cmn_err(CE_NOTE, "!mc read smbios chip info failed");
1302074bb90dSTom Pothier #endif /* DEBUG */
1303074bb90dSTom Pothier 			return;
1304074bb90dSTom Pothier 		}
1305074bb90dSTom Pothier 		mc->smb_bboard = fm_smb_mc_bboards(bdf);
1306074bb90dSTom Pothier #ifdef DEBUG
1307074bb90dSTom Pothier 		if (mc->smb_bboard == NULL)
1308074bb90dSTom Pothier 			cmn_err(CE_NOTE,
1309*97689f66SSrihari Venkatesan 			    "!mc read smbios base boards info failed");
1310074bb90dSTom Pothier #endif /* DEBUG */
1311074bb90dSTom Pothier 	}
1312f32a9dd1SSrihari Venkatesan 
1313f32a9dd1SSrihari Venkatesan 	if (pci_rp != NULL)
1314f32a9dd1SSrihari Venkatesan 		kmem_free(pci_rp, m);
1315074bb90dSTom Pothier }
1316074bb90dSTom Pothier 
1317e4b86885SCheng Sean Ye /*ARGSUSED*/
1318e4b86885SCheng Sean Ye static int
1319e4b86885SCheng Sean Ye mc_create_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
1320e4b86885SCheng Sean Ye {
1321e4b86885SCheng Sean Ye 	chipid_t chipid = *((chipid_t *)arg1);
1322e4b86885SCheng Sean Ye 	cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2;
1323e4b86885SCheng Sean Ye 
1324e4b86885SCheng Sean Ye 	if (cmi_hdl_chipid(whdl) == chipid) {
1325e4b86885SCheng Sean Ye 		cmi_hdl_hold(whdl);	/* short-term hold */
1326e4b86885SCheng Sean Ye 		*hdlp = whdl;
1327e4b86885SCheng Sean Ye 		return (CMI_HDL_WALK_DONE);
1328e4b86885SCheng Sean Ye 	} else {
1329e4b86885SCheng Sean Ye 		return (CMI_HDL_WALK_NEXT);
1330e4b86885SCheng Sean Ye 	}
1331e4b86885SCheng Sean Ye }
1332e4b86885SCheng Sean Ye 
1333e4b86885SCheng Sean Ye static mc_t *
1334074bb90dSTom Pothier mc_create(chipid_t chipid, dev_info_t *dip)
1335e4b86885SCheng Sean Ye {
1336e4b86885SCheng Sean Ye 	mc_t *mc;
1337e4b86885SCheng Sean Ye 	cmi_hdl_t hdl = NULL;
1338e4b86885SCheng Sean Ye 
1339e4b86885SCheng Sean Ye 	ASSERT(RW_WRITE_HELD(&mc_lock));
1340e4b86885SCheng Sean Ye 
1341e4b86885SCheng Sean Ye 	/*
1342e4b86885SCheng Sean Ye 	 * Find a handle for one of a chip's CPU.
1343e4b86885SCheng Sean Ye 	 *
1344e4b86885SCheng Sean Ye 	 * We can use one of the chip's CPUs since all cores
1345e4b86885SCheng Sean Ye 	 * of a chip share the same revision and socket type.
1346e4b86885SCheng Sean Ye 	 */
1347e4b86885SCheng Sean Ye 	cmi_hdl_walk(mc_create_cb, (void *)&chipid, (void *)&hdl, NULL);
1348e4b86885SCheng Sean Ye 	if (hdl == NULL)
1349e4b86885SCheng Sean Ye 		return (NULL);	/* no cpu for this chipid found! */
1350e4b86885SCheng Sean Ye 
1351e4b86885SCheng Sean Ye 	mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP);
1352e4b86885SCheng Sean Ye 
1353e4b86885SCheng Sean Ye 	mc->mc_hdr.mch_type = MC_NT_MC;
1354e4b86885SCheng Sean Ye 	mc->mc_props.mcp_num = chipid;
1355e4b86885SCheng Sean Ye 	mc->mc_props.mcp_sparecs = MC_INVALNUM;
1356e4b86885SCheng Sean Ye 	mc->mc_props.mcp_badcs = MC_INVALNUM;
1357e4b86885SCheng Sean Ye 
1358e4b86885SCheng Sean Ye 	mc->mc_props.mcp_rev = cmi_hdl_chiprev(hdl);
1359e4b86885SCheng Sean Ye 	mc->mc_revname = cmi_hdl_chiprevstr(hdl);
1360e4b86885SCheng Sean Ye 	mc->mc_socket = cmi_hdl_getsockettype(hdl);
1361e4b86885SCheng Sean Ye 
1362074bb90dSTom Pothier 	mc_read_smbios(mc, dip);
1363074bb90dSTom Pothier 
1364e4b86885SCheng Sean Ye 	if (mc_list == NULL)
1365e4b86885SCheng Sean Ye 		mc_list = mc;
1366e4b86885SCheng Sean Ye 	if (mc_last != NULL)
1367e4b86885SCheng Sean Ye 		mc_last->mc_next = mc;
1368e4b86885SCheng Sean Ye 
1369e4b86885SCheng Sean Ye 	mc->mc_next = NULL;
1370e4b86885SCheng Sean Ye 	mc_last = mc;
1371e4b86885SCheng Sean Ye 
1372e4b86885SCheng Sean Ye 	cmi_hdl_rele(hdl);
1373e4b86885SCheng Sean Ye 
1374e4b86885SCheng Sean Ye 	return (mc);
1375e4b86885SCheng Sean Ye }
1376e4b86885SCheng Sean Ye 
1377e4b86885SCheng Sean Ye /*
1378e4b86885SCheng Sean Ye  * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted
1379e4b86885SCheng Sean Ye  * from the specified 'cfg' register value using 'mask' and 'shift'.  If a
1380e4b86885SCheng Sean Ye  * value is zero, scrubbing is off so return the opposite value.  Otherwise
1381e4b86885SCheng Sean Ye  * the maximum rate is the smallest non-zero value of the two values.
1382e4b86885SCheng Sean Ye  */
1383e4b86885SCheng Sean Ye static uint32_t
1384e4b86885SCheng Sean Ye mc_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift)
1385e4b86885SCheng Sean Ye {
1386e4b86885SCheng Sean Ye 	uint32_t r2 = (cfg & mask) >> shift;
1387e4b86885SCheng Sean Ye 
1388e4b86885SCheng Sean Ye 	if (r1 != 0 && r2 != 0)
1389e4b86885SCheng Sean Ye 		return (MIN(r1, r2));
1390e4b86885SCheng Sean Ye 
1391e4b86885SCheng Sean Ye 	return (r1 ? r1 : r2);
1392e4b86885SCheng Sean Ye }
1393e4b86885SCheng Sean Ye 
1394e4b86885SCheng Sean Ye 
1395e4b86885SCheng Sean Ye /*
1396e4b86885SCheng Sean Ye  * Enable the memory scrubber.  We must use the mc_pcicfg_{get32,put32}_nohdl
1397e4b86885SCheng Sean Ye  * interfaces since we do not bind to function 3.
1398e4b86885SCheng Sean Ye  */
1399e4b86885SCheng Sean Ye cmi_errno_t
1400e4b86885SCheng Sean Ye mc_scrubber_enable(mc_t *mc)
1401e4b86885SCheng Sean Ye {
1402e4b86885SCheng Sean Ye 	mc_props_t *mcp = &mc->mc_props;
140392420f62SSurya Prakki 	chipid_t chipid = (chipid_t)mcp->mcp_num;
140492420f62SSurya Prakki 	uint32_t rev = (uint32_t)mcp->mcp_rev;
1405e4b86885SCheng Sean Ye 	mc_cfgregs_t *mcr = &mc->mc_cfgregs;
1406e4b86885SCheng Sean Ye 	union mcreg_scrubctl scrubctl;
1407e4b86885SCheng Sean Ye 	union mcreg_dramscrublo dalo;
1408e4b86885SCheng Sean Ye 	union mcreg_dramscrubhi dahi;
1409e4b86885SCheng Sean Ye 
1410e4b86885SCheng Sean Ye 	mcr->mcr_scrubctl = MCREG_VAL32(&scrubctl) =
1411e4b86885SCheng Sean Ye 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL);
1412e4b86885SCheng Sean Ye 
1413e4b86885SCheng Sean Ye 	mcr->mcr_scrubaddrlo = MCREG_VAL32(&dalo) =
1414e4b86885SCheng Sean Ye 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO);
1415e4b86885SCheng Sean Ye 
1416e4b86885SCheng Sean Ye 	mcr->mcr_scrubaddrhi = MCREG_VAL32(&dahi) =
1417e4b86885SCheng Sean Ye 	    mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI);
1418e4b86885SCheng Sean Ye 
1419e4b86885SCheng Sean Ye 	if (mc_scrub_policy == MC_SCRUB_BIOSDEFAULT)
1420e4b86885SCheng Sean Ye 		return (MCREG_FIELD_CMN(&scrubctl, DramScrub) !=
1421e4b86885SCheng Sean Ye 		    AMD_NB_SCRUBCTL_RATE_NONE ?
1422e4b86885SCheng Sean Ye 		    CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB);
1423e4b86885SCheng Sean Ye 
1424e4b86885SCheng Sean Ye 	/*
1425e4b86885SCheng Sean Ye 	 * Disable DRAM scrubbing while we fiddle.
1426e4b86885SCheng Sean Ye 	 */
1427e4b86885SCheng Sean Ye 	MCREG_FIELD_CMN(&scrubctl, DramScrub) = AMD_NB_SCRUBCTL_RATE_NONE;
1428e4b86885SCheng Sean Ye 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL,
1429e4b86885SCheng Sean Ye 	    MCREG_VAL32(&scrubctl));
1430e4b86885SCheng Sean Ye 
1431e4b86885SCheng Sean Ye 	/*
1432e4b86885SCheng Sean Ye 	 * Setup DRAM Scrub Address Low and High registers for the
1433e4b86885SCheng Sean Ye 	 * base address of this node, and to select srubber redirect.
1434e4b86885SCheng Sean Ye 	 */
1435e4b86885SCheng Sean Ye 	MCREG_FIELD_CMN(&dalo, ScrubReDirEn) = 1;
1436e4b86885SCheng Sean Ye 	MCREG_FIELD_CMN(&dalo, ScrubAddrLo) =
1437e4b86885SCheng Sean Ye 	    AMD_NB_SCRUBADDR_MKLO(mcp->mcp_base);
1438e4b86885SCheng Sean Ye 
1439e4b86885SCheng Sean Ye 	MCREG_FIELD_CMN(&dahi, ScrubAddrHi) =
1440e4b86885SCheng Sean Ye 	    AMD_NB_SCRUBADDR_MKHI(mcp->mcp_base);
1441e4b86885SCheng Sean Ye 
1442e4b86885SCheng Sean Ye 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO,
1443e4b86885SCheng Sean Ye 	    MCREG_VAL32(&dalo));
1444e4b86885SCheng Sean Ye 	mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI,
1445e4b86885SCheng Sean Ye 	    MCREG_VAL32(&dahi));
1446e4b86885SCheng Sean Ye 
1447e4b86885SCheng Sean Ye 	if (mc_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) {
1448e4b86885SCheng Sean Ye 		cmn_err(CE_WARN, "mc_scrub_rate_dram is too large; "
1449e4b86885SCheng Sean Ye 		    "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX);
1450e4b86885SCheng Sean Ye 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX;
1451e4b86885SCheng Sean Ye 	}
1452e4b86885SCheng Sean Ye 
1453e4b86885SCheng Sean Ye 	switch (mc_scrub_policy) {
1454e4b86885SCheng Sean Ye 	case MC_SCRUB_FIXED:
1455e4b86885SCheng Sean Ye 		/* Use the system value checked above */
1456e4b86885SCheng Sean Ye 		break;
1457e4b86885SCheng Sean Ye 
1458e4b86885SCheng Sean Ye 	default:
1459e4b86885SCheng Sean Ye 		cmn_err(CE_WARN, "Unknown mc_scrub_policy value %d - "
1460e4b86885SCheng Sean Ye 		    "using default policy of MC_SCRUB_MAX", mc_scrub_policy);
1461e4b86885SCheng Sean Ye 		/*FALLTHRU*/
1462e4b86885SCheng Sean Ye 
1463e4b86885SCheng Sean Ye 	case MC_SCRUB_MAX:
1464e4b86885SCheng Sean Ye 		mc_scrub_rate_dram = mc_scrubber_max(mc_scrub_rate_dram,
1465e4b86885SCheng Sean Ye 		    mcr->mcr_scrubctl, AMD_NB_SCRUBCTL_DRAM_MASK,
1466e4b86885SCheng Sean Ye 		    AMD_NB_SCRUBCTL_DRAM_SHIFT);
1467e4b86885SCheng Sean Ye 		break;
1468e4b86885SCheng Sean Ye 	}
1469e4b86885SCheng Sean Ye 
1470e4b86885SCheng Sean Ye 	/*
147192420f62SSurya Prakki 	 * OPTERON_ERRATUM_99:
1472e4b86885SCheng Sean Ye 	 * This erratum applies on revisions D and earlier.
14737d586c73SSurya Prakki 	 * This erratum also applies on revisions E and later,
14747d586c73SSurya Prakki 	 * if BIOS uses chip-select hoisting instead of DRAM hole
14757d586c73SSurya Prakki 	 * mapping.
1476e4b86885SCheng Sean Ye 	 *
14777d586c73SSurya Prakki 	 * Do not enable the dram scrubber if the chip-select ranges
1478e4b86885SCheng Sean Ye 	 * for the node are not contiguous.
1479e4b86885SCheng Sean Ye 	 */
1480e4b86885SCheng Sean Ye 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE &&
148192420f62SSurya Prakki 	    mc->mc_csdiscontig) {
1482e4b86885SCheng Sean Ye 		cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
1483e4b86885SCheng Sean Ye 		    "%s chip %d because DRAM hole is present on this node",
1484e4b86885SCheng Sean Ye 		    mc->mc_revname, chipid);
1485e4b86885SCheng Sean Ye 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE;
1486e4b86885SCheng Sean Ye 	}
1487e4b86885SCheng Sean Ye 
1488e4b86885SCheng Sean Ye 	/*
148992420f62SSurya Prakki 	 * OPTERON_ERRATUM_101:
1490e4b86885SCheng Sean Ye 	 * This erratum applies on revisions D and earlier.
1491e4b86885SCheng Sean Ye 	 *
1492e4b86885SCheng Sean Ye 	 * If the DRAM Base Address register's IntlvEn field indicates that
1493e4b86885SCheng Sean Ye 	 * node interleaving is enabled, we must disable the DRAM scrubber
1494e4b86885SCheng Sean Ye 	 * and return zero to indicate that Solaris should use s/w instead.
1495e4b86885SCheng Sean Ye 	 */
1496e4b86885SCheng Sean Ye 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE &&
1497e4b86885SCheng Sean Ye 	    mcp->mcp_ilen != 0 &&
1498e4b86885SCheng Sean Ye 	    !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) {
1499e4b86885SCheng Sean Ye 		cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision "
1500e4b86885SCheng Sean Ye 		    "%s chip %d because DRAM memory is node-interleaved",
1501e4b86885SCheng Sean Ye 		    mc->mc_revname, chipid);
1502e4b86885SCheng Sean Ye 		mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE;
1503e4b86885SCheng Sean Ye 	}
1504e4b86885SCheng Sean Ye 
1505e4b86885SCheng Sean Ye 	if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE) {
1506e4b86885SCheng Sean Ye 		MCREG_FIELD_CMN(&scrubctl, DramScrub) = mc_scrub_rate_dram;
1507e4b86885SCheng Sean Ye 		mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL,
1508e4b86885SCheng Sean Ye 		    MCREG_VAL32(&scrubctl));
1509e4b86885SCheng Sean Ye 	}
1510e4b86885SCheng Sean Ye 
1511e4b86885SCheng Sean Ye 	return (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE ?
1512e4b86885SCheng Sean Ye 	    CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB);
1513e4b86885SCheng Sean Ye }
1514e4b86885SCheng Sean Ye 
1515e4b86885SCheng Sean Ye /*ARGSUSED*/
1516e4b86885SCheng Sean Ye static int
1517e4b86885SCheng Sean Ye mc_attach_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3)
1518e4b86885SCheng Sean Ye {
1519e4b86885SCheng Sean Ye 	mc_t *mc = (mc_t *)arg1;
1520e4b86885SCheng Sean Ye 	mcamd_prop_t chipid = *((mcamd_prop_t *)arg2);
1521e4b86885SCheng Sean Ye 
1522e4b86885SCheng Sean Ye 	if (cmi_hdl_chipid(whdl) == chipid) {
1523e4b86885SCheng Sean Ye 		mcamd_mc_register(whdl, mc);
1524e4b86885SCheng Sean Ye 	}
1525e4b86885SCheng Sean Ye 
1526e4b86885SCheng Sean Ye 	return (CMI_HDL_WALK_NEXT);
1527e4b86885SCheng Sean Ye }
1528e4b86885SCheng Sean Ye 
1529e4b86885SCheng Sean Ye static int mc_sw_scrub_disabled = 0;
1530e4b86885SCheng Sean Ye 
1531e4b86885SCheng Sean Ye static int
1532e4b86885SCheng Sean Ye mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1533e4b86885SCheng Sean Ye {
1534e4b86885SCheng Sean Ye 	mc_pcicfg_hdl_t cfghdl;
1535e4b86885SCheng Sean Ye 	const mc_bind_map_t *bm;
1536e4b86885SCheng Sean Ye 	const char *bindnm;
1537e4b86885SCheng Sean Ye 	char *unitstr = NULL;
1538e4b86885SCheng Sean Ye 	enum mc_funcnum func;
1539e4b86885SCheng Sean Ye 	long unitaddr;
1540e4b86885SCheng Sean Ye 	int chipid, rc;
1541e4b86885SCheng Sean Ye 	mc_t *mc;
1542e4b86885SCheng Sean Ye 
1543e4b86885SCheng Sean Ye 	/*
1544e4b86885SCheng Sean Ye 	 * This driver has no hardware state, but does
1545e4b86885SCheng Sean Ye 	 * claim to have a reg property, so it will be
1546e4b86885SCheng Sean Ye 	 * called on suspend.  It is probably better to
1547e4b86885SCheng Sean Ye 	 * make sure it doesn't get called on suspend,
1548e4b86885SCheng Sean Ye 	 * but it is just as easy to make sure we just
1549e4b86885SCheng Sean Ye 	 * return DDI_SUCCESS if called.
1550e4b86885SCheng Sean Ye 	 */
1551e4b86885SCheng Sean Ye 	if (cmd == DDI_RESUME)
1552e4b86885SCheng Sean Ye 		return (DDI_SUCCESS);
1553e4b86885SCheng Sean Ye 
1554e4b86885SCheng Sean Ye 	if (cmd != DDI_ATTACH || mc_no_attach != 0)
1555e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1556e4b86885SCheng Sean Ye 
1557e4b86885SCheng Sean Ye 	bindnm = ddi_binding_name(dip);
1558e4b86885SCheng Sean Ye 	for (bm = mc_bind_map; bm->bm_bindnm != NULL; bm++) {
1559e4b86885SCheng Sean Ye 		if (strcmp(bindnm, bm->bm_bindnm) == 0) {
1560e4b86885SCheng Sean Ye 			func = bm->bm_func;
1561e4b86885SCheng Sean Ye 			break;
1562e4b86885SCheng Sean Ye 		}
1563e4b86885SCheng Sean Ye 	}
1564e4b86885SCheng Sean Ye 
1565e4b86885SCheng Sean Ye 	if (bm->bm_bindnm == NULL)
1566e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1567e4b86885SCheng Sean Ye 
1568e4b86885SCheng Sean Ye 	/*
1569e4b86885SCheng Sean Ye 	 * We need the device number, which corresponds to the processor node
1570e4b86885SCheng Sean Ye 	 * number plus 24.  The node number can then be used to associate this
1571e4b86885SCheng Sean Ye 	 * memory controller device with a given processor chip.
1572e4b86885SCheng Sean Ye 	 */
1573e4b86885SCheng Sean Ye 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
1574e4b86885SCheng Sean Ye 	    DDI_PROP_DONTPASS, "unit-address", &unitstr) != DDI_PROP_SUCCESS) {
1575e4b86885SCheng Sean Ye 		cmn_err(CE_WARN, "failed to find unit-address for %s", bindnm);
1576e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1577e4b86885SCheng Sean Ye 	}
1578e4b86885SCheng Sean Ye 
1579e4b86885SCheng Sean Ye 	rc = ddi_strtol(unitstr, NULL, 16, &unitaddr);
1580e4b86885SCheng Sean Ye 	ASSERT(rc == 0 && unitaddr >= MC_AMD_DEV_OFFSET);
1581e4b86885SCheng Sean Ye 
1582e4b86885SCheng Sean Ye 	if (rc != 0 || unitaddr < MC_AMD_DEV_OFFSET) {
1583e4b86885SCheng Sean Ye 		cmn_err(CE_WARN, "failed to parse unit address %s for %s\n",
1584e4b86885SCheng Sean Ye 		    unitstr, bindnm);
1585e4b86885SCheng Sean Ye 		ddi_prop_free(unitstr);
1586e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1587e4b86885SCheng Sean Ye 	}
1588e4b86885SCheng Sean Ye 	ddi_prop_free(unitstr);
1589e4b86885SCheng Sean Ye 
1590e4b86885SCheng Sean Ye 	chipid = unitaddr - MC_AMD_DEV_OFFSET;
1591e4b86885SCheng Sean Ye 
1592e4b86885SCheng Sean Ye 	rw_enter(&mc_lock, RW_WRITER);
1593e4b86885SCheng Sean Ye 
1594e4b86885SCheng Sean Ye 	for (mc = mc_list; mc != NULL; mc = mc->mc_next) {
1595e4b86885SCheng Sean Ye 		if (mc->mc_props.mcp_num == chipid)
1596e4b86885SCheng Sean Ye 			break;
1597e4b86885SCheng Sean Ye 	}
1598e4b86885SCheng Sean Ye 
1599e4b86885SCheng Sean Ye 	/* Integrate this memory controller device into existing set */
1600e4b86885SCheng Sean Ye 	if (mc == NULL) {
1601074bb90dSTom Pothier 		mc = mc_create(chipid, dip);
1602e4b86885SCheng Sean Ye 
1603e4b86885SCheng Sean Ye 		if (mc == NULL) {
1604e4b86885SCheng Sean Ye 			/*
1605e4b86885SCheng Sean Ye 			 * We don't complain here because this is a legitimate
1606e4b86885SCheng Sean Ye 			 * path for MP systems.  On those machines, we'll attach
1607e4b86885SCheng Sean Ye 			 * before all CPUs have been initialized, and thus the
1608e4b86885SCheng Sean Ye 			 * chip verification in mc_create will fail.  We'll be
1609e4b86885SCheng Sean Ye 			 * reattached later for those CPUs.
1610e4b86885SCheng Sean Ye 			 */
1611e4b86885SCheng Sean Ye 			rw_exit(&mc_lock);
1612e4b86885SCheng Sean Ye 			return (DDI_FAILURE);
1613e4b86885SCheng Sean Ye 		}
1614e4b86885SCheng Sean Ye 	} else {
1615e4b86885SCheng Sean Ye 		mc_snapshot_destroy(mc);
1616e4b86885SCheng Sean Ye 	}
1617e4b86885SCheng Sean Ye 
1618e4b86885SCheng Sean Ye 	/* Beyond this point, we're committed to creating this node */
1619e4b86885SCheng Sean Ye 
1620e4b86885SCheng Sean Ye 	mc_fm_init(dip);
1621e4b86885SCheng Sean Ye 
1622e4b86885SCheng Sean Ye 	ASSERT(mc->mc_funcs[func].mcf_devi == NULL);
1623e4b86885SCheng Sean Ye 	mc->mc_funcs[func].mcf_devi = dip;
1624e4b86885SCheng Sean Ye 	mc->mc_funcs[func].mcf_instance = ddi_get_instance(dip);
1625e4b86885SCheng Sean Ye 
1626e4b86885SCheng Sean Ye 	mc->mc_ref++;
1627e4b86885SCheng Sean Ye 
1628e4b86885SCheng Sean Ye 	/*
1629e4b86885SCheng Sean Ye 	 * Add the common properties to this node, and then add any properties
1630e4b86885SCheng Sean Ye 	 * that are specific to this node based upon its configuration space.
1631e4b86885SCheng Sean Ye 	 */
1632e4b86885SCheng Sean Ye 	(void) ddi_prop_update_string(DDI_DEV_T_NONE,
1633e4b86885SCheng Sean Ye 	    dip, "model", (char *)bm->bm_model);
1634e4b86885SCheng Sean Ye 
1635e4b86885SCheng Sean Ye 	(void) ddi_prop_update_int(DDI_DEV_T_NONE,
1636e4b86885SCheng Sean Ye 	    dip, "chip-id", mc->mc_props.mcp_num);
1637e4b86885SCheng Sean Ye 
1638e4b86885SCheng Sean Ye 	if (bm->bm_mkprops != NULL &&
1639e4b86885SCheng Sean Ye 	    mc_pcicfg_setup(mc, bm->bm_func, &cfghdl) == DDI_SUCCESS) {
1640e4b86885SCheng Sean Ye 		bm->bm_mkprops(cfghdl, mc);
1641e4b86885SCheng Sean Ye 		mc_pcicfg_teardown(cfghdl);
1642e4b86885SCheng Sean Ye 	}
1643e4b86885SCheng Sean Ye 
1644e4b86885SCheng Sean Ye 	/*
1645e4b86885SCheng Sean Ye 	 * If this is the last node to be attached for this memory controller,
1646e4b86885SCheng Sean Ye 	 * then create the minor node, enable scrubbers, and register with
1647e4b86885SCheng Sean Ye 	 * cpu module(s) for this chip.
1648e4b86885SCheng Sean Ye 	 */
1649e4b86885SCheng Sean Ye 	if (func == MC_FUNC_DEVIMAP) {
1650e4b86885SCheng Sean Ye 		mc_props_t *mcp = &mc->mc_props;
1651e4b86885SCheng Sean Ye 		int dram_present = 0;
1652e4b86885SCheng Sean Ye 
1653e4b86885SCheng Sean Ye 		if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR,
1654e4b86885SCheng Sean Ye 		    mcp->mcp_num, "ddi_mem_ctrl",
1655e4b86885SCheng Sean Ye 		    0) != DDI_SUCCESS) {
1656e4b86885SCheng Sean Ye 			cmn_err(CE_WARN, "failed to create minor node for chip "
1657e4b86885SCheng Sean Ye 			    "%d memory controller\n",
1658e4b86885SCheng Sean Ye 			    (chipid_t)mcp->mcp_num);
1659e4b86885SCheng Sean Ye 		}
1660e4b86885SCheng Sean Ye 
1661e4b86885SCheng Sean Ye 		/*
1662e4b86885SCheng Sean Ye 		 * Register the memory controller for every CPU of this chip.
1663e4b86885SCheng Sean Ye 		 *
1664e4b86885SCheng Sean Ye 		 * If there is memory present on this node and ECC is enabled
1665e4b86885SCheng Sean Ye 		 * attempt to enable h/w memory scrubbers for this node.
1666e4b86885SCheng Sean Ye 		 * If we are successful in enabling *any* hardware scrubbers,
1667e4b86885SCheng Sean Ye 		 * disable the software memory scrubber.
1668e4b86885SCheng Sean Ye 		 */
1669e4b86885SCheng Sean Ye 		cmi_hdl_walk(mc_attach_cb, (void *)mc, (void *)&mcp->mcp_num,
1670e4b86885SCheng Sean Ye 		    NULL);
1671e4b86885SCheng Sean Ye 
1672e4b86885SCheng Sean Ye 		if (mcp->mcp_lim != mcp->mcp_base) {
1673e4b86885SCheng Sean Ye 			/*
1674e4b86885SCheng Sean Ye 			 * This node may map non-dram memory alone, so we
1675e4b86885SCheng Sean Ye 			 * must check for an enabled chip-select to be
1676e4b86885SCheng Sean Ye 			 * sure there is dram present.
1677e4b86885SCheng Sean Ye 			 */
1678e4b86885SCheng Sean Ye 			mc_cs_t *mccs;
1679e4b86885SCheng Sean Ye 
1680e4b86885SCheng Sean Ye 			for (mccs = mc->mc_cslist; mccs != NULL;
1681e4b86885SCheng Sean Ye 			    mccs = mccs->mccs_next) {
1682e4b86885SCheng Sean Ye 				if (mccs->mccs_props.csp_csbe) {
1683e4b86885SCheng Sean Ye 					dram_present = 1;
1684e4b86885SCheng Sean Ye 					break;
1685e4b86885SCheng Sean Ye 				}
1686e4b86885SCheng Sean Ye 			}
1687e4b86885SCheng Sean Ye 		}
1688e4b86885SCheng Sean Ye 
1689e4b86885SCheng Sean Ye 		if (dram_present && !mc_ecc_enabled(mc)) {
1690e4b86885SCheng Sean Ye 			/*
1691e4b86885SCheng Sean Ye 			 * On a single chip system there is no point in
1692e4b86885SCheng Sean Ye 			 * scrubbing if there is no ECC on the single node.
1693e4b86885SCheng Sean Ye 			 * On a multichip system, necessarily Opteron using
1694e4b86885SCheng Sean Ye 			 * registered ECC-capable DIMMs, if there is memory
1695e4b86885SCheng Sean Ye 			 * present on a node but no ECC there then we'll assume
1696e4b86885SCheng Sean Ye 			 * ECC is disabled for all nodes and we will not enable
1697e4b86885SCheng Sean Ye 			 * the scrubber and wll also disable the software
1698e4b86885SCheng Sean Ye 			 * memscrub thread.
1699e4b86885SCheng Sean Ye 			 */
1700e4b86885SCheng Sean Ye 			rc = 1;
1701e4b86885SCheng Sean Ye 		} else if (!dram_present) {
1702e4b86885SCheng Sean Ye 			/* No memory on this node - others decide memscrub */
1703e4b86885SCheng Sean Ye 			rc = 0;
1704e4b86885SCheng Sean Ye 		} else {
1705e4b86885SCheng Sean Ye 			/*
1706e4b86885SCheng Sean Ye 			 * There is memory on this node and ECC is enabled.
1707e4b86885SCheng Sean Ye 			 * Call via the cpu module to enable memory scrubbing
1708e4b86885SCheng Sean Ye 			 * on this node - we could call directly but then
1709e4b86885SCheng Sean Ye 			 * we may overlap with a request to enable chip-cache
1710e4b86885SCheng Sean Ye 			 * scrubbing.
1711e4b86885SCheng Sean Ye 			 */
1712e4b86885SCheng Sean Ye 			rc = mc_scrubber_enable(mc);
1713e4b86885SCheng Sean Ye 		}
1714e4b86885SCheng Sean Ye 
1715e4b86885SCheng Sean Ye 		if (rc == CMI_SUCCESS && !mc_sw_scrub_disabled++)
1716e4b86885SCheng Sean Ye 			cmi_mc_sw_memscrub_disable();
1717e4b86885SCheng Sean Ye 
1718e4b86885SCheng Sean Ye 		mc_report_testfails(mc);
1719e4b86885SCheng Sean Ye 	}
1720e4b86885SCheng Sean Ye 
1721e4b86885SCheng Sean Ye 	/*
1722e4b86885SCheng Sean Ye 	 * Update nvlist for as far as we have gotten in attach/init.
1723e4b86885SCheng Sean Ye 	 */
1724e4b86885SCheng Sean Ye 	nvlist_free(mc->mc_nvl);
1725e4b86885SCheng Sean Ye 	mc->mc_nvl = mc_nvl_create(mc);
1726e4b86885SCheng Sean Ye 
1727e4b86885SCheng Sean Ye 	rw_exit(&mc_lock);
1728e4b86885SCheng Sean Ye 	return (DDI_SUCCESS);
1729e4b86885SCheng Sean Ye }
1730e4b86885SCheng Sean Ye 
1731e4b86885SCheng Sean Ye /*ARGSUSED*/
1732e4b86885SCheng Sean Ye static int
1733e4b86885SCheng Sean Ye mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1734e4b86885SCheng Sean Ye {
1735e4b86885SCheng Sean Ye 	/*
1736e4b86885SCheng Sean Ye 	 * See the comment about suspend in
1737e4b86885SCheng Sean Ye 	 * mc_attach().
1738e4b86885SCheng Sean Ye 	 */
1739e4b86885SCheng Sean Ye 	if (cmd == DDI_SUSPEND)
1740e4b86885SCheng Sean Ye 		return (DDI_SUCCESS);
1741e4b86885SCheng Sean Ye 	else
1742e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
1743e4b86885SCheng Sean Ye }
1744e4b86885SCheng Sean Ye 
174519397407SSherry Moore 
1746e4b86885SCheng Sean Ye static struct dev_ops mc_ops = {
1747e4b86885SCheng Sean Ye 	DEVO_REV,		/* devo_rev */
1748e4b86885SCheng Sean Ye 	0,			/* devo_refcnt */
1749e4b86885SCheng Sean Ye 	mc_getinfo,		/* devo_getinfo */
1750e4b86885SCheng Sean Ye 	nulldev,		/* devo_identify */
1751e4b86885SCheng Sean Ye 	nulldev,		/* devo_probe */
1752e4b86885SCheng Sean Ye 	mc_attach,		/* devo_attach */
1753e4b86885SCheng Sean Ye 	mc_detach,		/* devo_detach */
1754e4b86885SCheng Sean Ye 	nodev,			/* devo_reset */
1755e4b86885SCheng Sean Ye 	&mc_cb_ops,		/* devo_cb_ops */
1756e4b86885SCheng Sean Ye 	NULL,			/* devo_bus_ops */
175719397407SSherry Moore 	NULL,			/* devo_power */
175819397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
1759e4b86885SCheng Sean Ye };
1760e4b86885SCheng Sean Ye 
1761e4b86885SCheng Sean Ye static struct modldrv modldrv = {
1762e4b86885SCheng Sean Ye 	&mod_driverops,
1763e4b86885SCheng Sean Ye 	"Memory Controller for AMD processors",
1764e4b86885SCheng Sean Ye 	&mc_ops
1765e4b86885SCheng Sean Ye };
1766e4b86885SCheng Sean Ye 
1767e4b86885SCheng Sean Ye static struct modlinkage modlinkage = {
1768e4b86885SCheng Sean Ye 	MODREV_1,
1769e4b86885SCheng Sean Ye 	(void *)&modldrv,
1770e4b86885SCheng Sean Ye 	NULL
1771e4b86885SCheng Sean Ye };
1772e4b86885SCheng Sean Ye 
1773e4b86885SCheng Sean Ye int
1774e4b86885SCheng Sean Ye _init(void)
1775e4b86885SCheng Sean Ye {
1776e4b86885SCheng Sean Ye 	/*
1777e4b86885SCheng Sean Ye 	 * Refuse to load if there is no PCI config space support.
1778e4b86885SCheng Sean Ye 	 */
1779e4b86885SCheng Sean Ye 	if (pci_getl_func == NULL)
1780e4b86885SCheng Sean Ye 		return (ENOTSUP);
1781e4b86885SCheng Sean Ye 
1782e4b86885SCheng Sean Ye 	rw_init(&mc_lock, NULL, RW_DRIVER, NULL);
1783e4b86885SCheng Sean Ye 	return (mod_install(&modlinkage));
1784e4b86885SCheng Sean Ye }
1785e4b86885SCheng Sean Ye 
1786e4b86885SCheng Sean Ye int
1787e4b86885SCheng Sean Ye _info(struct modinfo *modinfop)
1788e4b86885SCheng Sean Ye {
1789e4b86885SCheng Sean Ye 	return (mod_info(&modlinkage, modinfop));
1790e4b86885SCheng Sean Ye }
1791e4b86885SCheng Sean Ye 
1792e4b86885SCheng Sean Ye int
1793e4b86885SCheng Sean Ye _fini(void)
1794e4b86885SCheng Sean Ye {
1795e4b86885SCheng Sean Ye 	int rc;
1796e4b86885SCheng Sean Ye 
1797e4b86885SCheng Sean Ye 	if ((rc = mod_remove(&modlinkage)) != 0)
1798e4b86885SCheng Sean Ye 		return (rc);
1799e4b86885SCheng Sean Ye 
1800e4b86885SCheng Sean Ye 	rw_destroy(&mc_lock);
1801e4b86885SCheng Sean Ye 	return (0);
1802e4b86885SCheng Sean Ye }
1803