1*20c794b3Sgavinm /*
2*20c794b3Sgavinm  * CDDL HEADER START
3*20c794b3Sgavinm  *
4*20c794b3Sgavinm  * The contents of this file are subject to the terms of the
5*20c794b3Sgavinm  * Common Development and Distribution License (the "License").
6*20c794b3Sgavinm  * You may not use this file except in compliance with the License.
7*20c794b3Sgavinm  *
8*20c794b3Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*20c794b3Sgavinm  * or http://www.opensolaris.org/os/licensing.
10*20c794b3Sgavinm  * See the License for the specific language governing permissions
11*20c794b3Sgavinm  * and limitations under the License.
12*20c794b3Sgavinm  *
13*20c794b3Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
14*20c794b3Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*20c794b3Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
16*20c794b3Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
17*20c794b3Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
18*20c794b3Sgavinm  *
19*20c794b3Sgavinm  * CDDL HEADER END
20*20c794b3Sgavinm  */
21*20c794b3Sgavinm 
22*20c794b3Sgavinm /*
23*20c794b3Sgavinm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24*20c794b3Sgavinm  * Use is subject to license terms.
25*20c794b3Sgavinm  */
26*20c794b3Sgavinm 
27*20c794b3Sgavinm #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*20c794b3Sgavinm 
29*20c794b3Sgavinm /*
30*20c794b3Sgavinm  * AMD memory enumeration
31*20c794b3Sgavinm  */
32*20c794b3Sgavinm 
33*20c794b3Sgavinm #include <sys/types.h>
34*20c794b3Sgavinm #include <unistd.h>
35*20c794b3Sgavinm #include <stropts.h>
36*20c794b3Sgavinm #include <sys/fm/protocol.h>
37*20c794b3Sgavinm #include <sys/mc.h>
38*20c794b3Sgavinm #include <sys/mc_amd.h>
39*20c794b3Sgavinm #include <fm/topo_mod.h>
40*20c794b3Sgavinm #include <strings.h>
41*20c794b3Sgavinm #include <sys/stat.h>
42*20c794b3Sgavinm #include <fcntl.h>
43*20c794b3Sgavinm 
44*20c794b3Sgavinm #include "chip.h"
45*20c794b3Sgavinm 
46*20c794b3Sgavinm #define	MAX_CHANNUM	1
47*20c794b3Sgavinm #define	MAX_DIMMNUM	7
48*20c794b3Sgavinm #define	MAX_CSNUM	7
49*20c794b3Sgavinm 
50*20c794b3Sgavinm static const topo_pgroup_info_t cs_pgroup =
51*20c794b3Sgavinm 	{ PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
52*20c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup =
53*20c794b3Sgavinm 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
54*20c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup =
55*20c794b3Sgavinm 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
56*20c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup =
57*20c794b3Sgavinm 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
58*20c794b3Sgavinm static const topo_pgroup_info_t chan_pgroup =
59*20c794b3Sgavinm 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
60*20c794b3Sgavinm 
61*20c794b3Sgavinm static const topo_method_t dimm_methods[] = {
62*20c794b3Sgavinm 	{ SIMPLE_DIMM_LBL, "Property method", 0,
63*20c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, simple_dimm_label},
64*20c794b3Sgavinm 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0,
65*20c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
66*20c794b3Sgavinm 	{ SEQ_DIMM_LBL, "Property method", 0,
67*20c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, seq_dimm_label},
68*20c794b3Sgavinm 	{ NULL }
69*20c794b3Sgavinm };
70*20c794b3Sgavinm 
71*20c794b3Sgavinm static const topo_method_t rank_methods[] = {
72*20c794b3Sgavinm 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
73*20c794b3Sgavinm 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
74*20c794b3Sgavinm 	    mem_asru_compute },
75*20c794b3Sgavinm 	{ NULL }
76*20c794b3Sgavinm };
77*20c794b3Sgavinm 
78*20c794b3Sgavinm static const topo_method_t gen_cs_methods[] = {
79*20c794b3Sgavinm 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
80*20c794b3Sgavinm 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
81*20c794b3Sgavinm 	    mem_asru_compute },
82*20c794b3Sgavinm 	{ NULL }
83*20c794b3Sgavinm };
84*20c794b3Sgavinm 
85*20c794b3Sgavinm static nvlist_t *cs_fmri[MC_CHIP_NCS];
86*20c794b3Sgavinm 
87*20c794b3Sgavinm /*
88*20c794b3Sgavinm  * Called when there is no memory-controller driver to provide topology
89*20c794b3Sgavinm  * information.  Generate a maximal memory topology that is appropriate
90*20c794b3Sgavinm  * for the chip revision.  The memory-controller node has already been
91*20c794b3Sgavinm  * bound as mcnode, and the parent of that is cnode.
92*20c794b3Sgavinm  *
93*20c794b3Sgavinm  * We create a tree of dram-channel and chip-select nodes below the
94*20c794b3Sgavinm  * memory-controller node.  There will be two dram channels and 8 chip-selects
95*20c794b3Sgavinm  * below each, regardless of actual socket type, processor revision and so on.
96*20c794b3Sgavinm  * This is adequate for generic diagnosis up to family 0x10 revision C.
97*20c794b3Sgavinm  * When support for revision D is implemented (or maybe C) we should take
98*20c794b3Sgavinm  * the opportunity to rework the topology tree completely (socket change will
99*20c794b3Sgavinm  * mean there can be no diagnosis history tied to the topology).
100*20c794b3Sgavinm  */
101*20c794b3Sgavinm /*ARGSUSED*/
102*20c794b3Sgavinm static int
103*20c794b3Sgavinm amd_generic_mc_create(topo_mod_t *mod, tnode_t *cnode, tnode_t *mcnode,
104*20c794b3Sgavinm     int family, int model, int stepping, nvlist_t *auth)
105*20c794b3Sgavinm {
106*20c794b3Sgavinm 	int chan, cs;
107*20c794b3Sgavinm 
108*20c794b3Sgavinm 	/*
109*20c794b3Sgavinm 	 * Elsewhere we have already returned for families less than 0xf.
110*20c794b3Sgavinm 	 * This "generic" topology is adequate for all of family 0xf and
111*20c794b3Sgavinm 	 * for revisions A, B and C of family 0x10 (A = model 0, B = model 1,
112*20c794b3Sgavinm 	 * we'll guess C = model 3 at this point).
113*20c794b3Sgavinm 	 */
114*20c794b3Sgavinm 	if (family > 0x10 || (family == 0x10 && model > 3))
115*20c794b3Sgavinm 		return (1);
116*20c794b3Sgavinm 
117*20c794b3Sgavinm 	if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
118*20c794b3Sgavinm 	    MAX_CHANNUM) < 0) {
119*20c794b3Sgavinm 		whinge(mod, NULL, "amd_generic_mc_create: range create for "
120*20c794b3Sgavinm 		    "channels failed\n");
121*20c794b3Sgavinm 		return (-1);
122*20c794b3Sgavinm 	}
123*20c794b3Sgavinm 
124*20c794b3Sgavinm 	for (chan = 0; chan <= MAX_CHANNUM; chan++) {
125*20c794b3Sgavinm 		tnode_t *chnode;
126*20c794b3Sgavinm 		nvlist_t *fmri;
127*20c794b3Sgavinm 		int err;
128*20c794b3Sgavinm 
129*20c794b3Sgavinm 		if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
130*20c794b3Sgavinm 		    &fmri) != 0) {
131*20c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
132*20c794b3Sgavinm 			    "failed\n");
133*20c794b3Sgavinm 			return (-1);
134*20c794b3Sgavinm 		}
135*20c794b3Sgavinm 
136*20c794b3Sgavinm 		if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
137*20c794b3Sgavinm 		    chan, fmri)) == NULL) {
138*20c794b3Sgavinm 			nvlist_free(fmri);
139*20c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: node "
140*20c794b3Sgavinm 			    "bind failed\n");
141*20c794b3Sgavinm 			return (-1);
142*20c794b3Sgavinm 		}
143*20c794b3Sgavinm 
144*20c794b3Sgavinm 		nvlist_free(fmri);
145*20c794b3Sgavinm 
146*20c794b3Sgavinm 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
147*20c794b3Sgavinm 
148*20c794b3Sgavinm 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
149*20c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
150*20c794b3Sgavinm 
151*20c794b3Sgavinm 		if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
152*20c794b3Sgavinm 		    0, MAX_CSNUM) < 0) {
153*20c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: "
154*20c794b3Sgavinm 			    "range create for cs failed\n");
155*20c794b3Sgavinm 			return (-1);
156*20c794b3Sgavinm 		}
157*20c794b3Sgavinm 
158*20c794b3Sgavinm 		for (cs = 0; cs <= MAX_CSNUM; cs++) {
159*20c794b3Sgavinm 			tnode_t *csnode;
160*20c794b3Sgavinm 
161*20c794b3Sgavinm 			if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
162*20c794b3Sgavinm 			    &fmri) != 0) {
163*20c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
164*20c794b3Sgavinm 				    "mkrsrc for cs failed\n");
165*20c794b3Sgavinm 				return (-1);
166*20c794b3Sgavinm 			}
167*20c794b3Sgavinm 
168*20c794b3Sgavinm 			if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
169*20c794b3Sgavinm 			    cs, fmri)) == NULL) {
170*20c794b3Sgavinm 				nvlist_free(fmri);
171*20c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
172*20c794b3Sgavinm 				    "bind for cs failed\n");
173*20c794b3Sgavinm 				return (-1);
174*20c794b3Sgavinm 			}
175*20c794b3Sgavinm 
176*20c794b3Sgavinm 			/*
177*20c794b3Sgavinm 			 * Dynamic ASRU for page faults within a chip-select.
178*20c794b3Sgavinm 			 * The topology does not represent pages (there are
179*20c794b3Sgavinm 			 * too many) so when a page is faulted we generate
180*20c794b3Sgavinm 			 * an ASRU to represent the individual page.
181*20c794b3Sgavinm 			 */
182*20c794b3Sgavinm 			if (topo_method_register(mod, csnode,
183*20c794b3Sgavinm 			    gen_cs_methods) < 0)
184*20c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
185*20c794b3Sgavinm 				    "method registration failed\n");
186*20c794b3Sgavinm 
187*20c794b3Sgavinm 			(void) topo_node_asru_set(csnode, fmri,
188*20c794b3Sgavinm 			    TOPO_ASRU_COMPUTE, &err);
189*20c794b3Sgavinm 
190*20c794b3Sgavinm 			nvlist_free(fmri);
191*20c794b3Sgavinm 		}
192*20c794b3Sgavinm 	}
193*20c794b3Sgavinm 
194*20c794b3Sgavinm 	return (0);
195*20c794b3Sgavinm }
196*20c794b3Sgavinm 
197*20c794b3Sgavinm static nvlist_t *
198*20c794b3Sgavinm amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
199*20c794b3Sgavinm {
200*20c794b3Sgavinm 	mc_snapshot_info_t mcs;
201*20c794b3Sgavinm 	void *buf = NULL;
202*20c794b3Sgavinm 	uint8_t ver;
203*20c794b3Sgavinm 
204*20c794b3Sgavinm 	nvlist_t *nvl = NULL;
205*20c794b3Sgavinm 	char path[64];
206*20c794b3Sgavinm 	int fd, err;
207*20c794b3Sgavinm 
208*20c794b3Sgavinm 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
209*20c794b3Sgavinm 	fd = open(path, O_RDONLY);
210*20c794b3Sgavinm 
211*20c794b3Sgavinm 	if (fd == -1) {
212*20c794b3Sgavinm 		/*
213*20c794b3Sgavinm 		 * Some v20z and v40z systems may have had the 3rd-party
214*20c794b3Sgavinm 		 * NWSnps packagae installed which installs a /dev/mc
215*20c794b3Sgavinm 		 * link.  So try again via /devices.
216*20c794b3Sgavinm 		 */
217*20c794b3Sgavinm 		(void) snprintf(path, sizeof (path),
218*20c794b3Sgavinm 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
219*20c794b3Sgavinm 		    MC_AMD_DEV_OFFSET + id);
220*20c794b3Sgavinm 		fd = open(path, O_RDONLY);
221*20c794b3Sgavinm 	}
222*20c794b3Sgavinm 
223*20c794b3Sgavinm 	if (fd == -1)
224*20c794b3Sgavinm 		return (NULL);	/* do not whinge */
225*20c794b3Sgavinm 
226*20c794b3Sgavinm 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
227*20c794b3Sgavinm 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
228*20c794b3Sgavinm 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
229*20c794b3Sgavinm 
230*20c794b3Sgavinm 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
231*20c794b3Sgavinm 		    path, strerror(errno));
232*20c794b3Sgavinm 
233*20c794b3Sgavinm 		free(buf);
234*20c794b3Sgavinm 		(void) close(fd);
235*20c794b3Sgavinm 		return (NULL);
236*20c794b3Sgavinm 	}
237*20c794b3Sgavinm 
238*20c794b3Sgavinm 	(void) close(fd);
239*20c794b3Sgavinm 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
240*20c794b3Sgavinm 	topo_mod_free(mod, buf, mcs.mcs_size);
241*20c794b3Sgavinm 
242*20c794b3Sgavinm 
243*20c794b3Sgavinm 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
244*20c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist is not versioned\n");
245*20c794b3Sgavinm 		nvlist_free(nvl);
246*20c794b3Sgavinm 		return (NULL);
247*20c794b3Sgavinm 	} else if (ver != MC_NVLIST_VERS1) {
248*20c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist version mismatch\n");
249*20c794b3Sgavinm 		nvlist_free(nvl);
250*20c794b3Sgavinm 		return (NULL);
251*20c794b3Sgavinm 	}
252*20c794b3Sgavinm 
253*20c794b3Sgavinm 	return (err ? NULL : nvl);
254*20c794b3Sgavinm }
255*20c794b3Sgavinm 
256*20c794b3Sgavinm int
257*20c794b3Sgavinm amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
258*20c794b3Sgavinm     nvlist_t *auth)
259*20c794b3Sgavinm {
260*20c794b3Sgavinm 	uint64_t *csnumarr;
261*20c794b3Sgavinm 	char **csnamearr;
262*20c794b3Sgavinm 	uint_t ncs, ncsname;
263*20c794b3Sgavinm 	tnode_t *ranknode;
264*20c794b3Sgavinm 	nvlist_t *fmri, *pfmri = NULL;
265*20c794b3Sgavinm 	uint64_t dsz, rsz;
266*20c794b3Sgavinm 	int nerr = 0;
267*20c794b3Sgavinm 	int err;
268*20c794b3Sgavinm 	int i;
269*20c794b3Sgavinm 
270*20c794b3Sgavinm 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
271*20c794b3Sgavinm 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
272*20c794b3Sgavinm 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
273*20c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: "
274*20c794b3Sgavinm 		    "csnums/csnames extraction failed\n");
275*20c794b3Sgavinm 		return (nerr);
276*20c794b3Sgavinm 	}
277*20c794b3Sgavinm 
278*20c794b3Sgavinm 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
279*20c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
280*20c794b3Sgavinm 		    "failed\n");
281*20c794b3Sgavinm 		return (nerr);
282*20c794b3Sgavinm 	}
283*20c794b3Sgavinm 
284*20c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
285*20c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: range create failed\n");
286*20c794b3Sgavinm 		nvlist_free(pfmri);
287*20c794b3Sgavinm 		return (nerr);
288*20c794b3Sgavinm 	}
289*20c794b3Sgavinm 
290*20c794b3Sgavinm 	if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
291*20c794b3Sgavinm 	    &err) == 0) {
292*20c794b3Sgavinm 		rsz = dsz / ncs;
293*20c794b3Sgavinm 	} else {
294*20c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
295*20c794b3Sgavinm 		    "size\n");
296*20c794b3Sgavinm 		return (nerr);
297*20c794b3Sgavinm 	}
298*20c794b3Sgavinm 
299*20c794b3Sgavinm 	for (i = 0; i < ncs; i++) {
300*20c794b3Sgavinm 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
301*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
302*20c794b3Sgavinm 			continue;
303*20c794b3Sgavinm 		}
304*20c794b3Sgavinm 
305*20c794b3Sgavinm 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
306*20c794b3Sgavinm 		    fmri)) == NULL) {
307*20c794b3Sgavinm 			nvlist_free(fmri);
308*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: node bind "
309*20c794b3Sgavinm 			    "failed\n");
310*20c794b3Sgavinm 			continue;
311*20c794b3Sgavinm 		}
312*20c794b3Sgavinm 
313*20c794b3Sgavinm 		nvlist_free(fmri);
314*20c794b3Sgavinm 
315*20c794b3Sgavinm 		(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
316*20c794b3Sgavinm 
317*20c794b3Sgavinm 		/*
318*20c794b3Sgavinm 		 * If a rank is faulted the asru is the associated
319*20c794b3Sgavinm 		 * chip-select, but if a page within a rank is faulted
320*20c794b3Sgavinm 		 * the asru is just that page.  Hence the dual preconstructed
321*20c794b3Sgavinm 		 * and computed ASRU.
322*20c794b3Sgavinm 		 */
323*20c794b3Sgavinm 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
324*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: "
325*20c794b3Sgavinm 			    "topo_method_register failed");
326*20c794b3Sgavinm 
327*20c794b3Sgavinm 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
328*20c794b3Sgavinm 		    TOPO_ASRU_COMPUTE, &err);
329*20c794b3Sgavinm 
330*20c794b3Sgavinm 		(void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
331*20c794b3Sgavinm 
332*20c794b3Sgavinm 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
333*20c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, rsz, &err);
334*20c794b3Sgavinm 
335*20c794b3Sgavinm 		(void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
336*20c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
337*20c794b3Sgavinm 
338*20c794b3Sgavinm 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
339*20c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
340*20c794b3Sgavinm 	}
341*20c794b3Sgavinm 
342*20c794b3Sgavinm 	nvlist_free(pfmri);
343*20c794b3Sgavinm 
344*20c794b3Sgavinm 	return (nerr);
345*20c794b3Sgavinm }
346*20c794b3Sgavinm 
347*20c794b3Sgavinm static int
348*20c794b3Sgavinm amd_dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
349*20c794b3Sgavinm     nvlist_t *mc, nvlist_t *auth)
350*20c794b3Sgavinm {
351*20c794b3Sgavinm 	int i, err, nerr = 0;
352*20c794b3Sgavinm 	nvpair_t *nvp;
353*20c794b3Sgavinm 	tnode_t *dimmnode;
354*20c794b3Sgavinm 	nvlist_t *fmri, *asru, **dimmarr = NULL;
355*20c794b3Sgavinm 	uint64_t num;
356*20c794b3Sgavinm 	uint_t ndimm;
357*20c794b3Sgavinm 
358*20c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
359*20c794b3Sgavinm 		whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
360*20c794b3Sgavinm 		return (-1);
361*20c794b3Sgavinm 	}
362*20c794b3Sgavinm 
363*20c794b3Sgavinm 	if (ndimm == 0)
364*20c794b3Sgavinm 		return (0);	/* no dimms present on this node */
365*20c794b3Sgavinm 
366*20c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
367*20c794b3Sgavinm 		whinge(mod, NULL, "amd_dimm_create: range create failed\n");
368*20c794b3Sgavinm 		return (-1);
369*20c794b3Sgavinm 	}
370*20c794b3Sgavinm 
371*20c794b3Sgavinm 	for (i = 0; i < ndimm; i++) {
372*20c794b3Sgavinm 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
373*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: dimm num property "
374*20c794b3Sgavinm 			    "missing\n");
375*20c794b3Sgavinm 			continue;
376*20c794b3Sgavinm 		}
377*20c794b3Sgavinm 
378*20c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
379*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
380*20c794b3Sgavinm 			continue;
381*20c794b3Sgavinm 		}
382*20c794b3Sgavinm 
383*20c794b3Sgavinm 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
384*20c794b3Sgavinm 		    == NULL) {
385*20c794b3Sgavinm 			nvlist_free(fmri);
386*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: node bind "
387*20c794b3Sgavinm 			    "failed\n");
388*20c794b3Sgavinm 			continue;
389*20c794b3Sgavinm 		}
390*20c794b3Sgavinm 
391*20c794b3Sgavinm 		if (topo_method_register(mod, dimmnode, dimm_methods) < 0)
392*20c794b3Sgavinm 			whinge(mod, &nerr, "dimm_create: "
393*20c794b3Sgavinm 			    "topo_method_register failed");
394*20c794b3Sgavinm 
395*20c794b3Sgavinm 		/*
396*20c794b3Sgavinm 		 * Use the mem computation method directly to publish the asru
397*20c794b3Sgavinm 		 * in the "mem" scheme.
398*20c794b3Sgavinm 		 */
399*20c794b3Sgavinm 		if (mem_asru_create(mod, fmri, &asru) == 0) {
400*20c794b3Sgavinm 			(void) topo_node_asru_set(dimmnode, asru, 0, &err);
401*20c794b3Sgavinm 			nvlist_free(asru);
402*20c794b3Sgavinm 		} else {
403*20c794b3Sgavinm 
404*20c794b3Sgavinm 			nvlist_free(fmri);
405*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: "
406*20c794b3Sgavinm 			    "mem_asru_create failed\n");
407*20c794b3Sgavinm 			continue;
408*20c794b3Sgavinm 		}
409*20c794b3Sgavinm 
410*20c794b3Sgavinm 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
411*20c794b3Sgavinm 
412*20c794b3Sgavinm 		nvlist_free(fmri);
413*20c794b3Sgavinm 
414*20c794b3Sgavinm 		(void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
415*20c794b3Sgavinm 
416*20c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
417*20c794b3Sgavinm 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
418*20c794b3Sgavinm 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
419*20c794b3Sgavinm 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
420*20c794b3Sgavinm 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
421*20c794b3Sgavinm 			    strcmp(nvpair_name(nvp), "csnames") == 0)
422*20c794b3Sgavinm 				continue;	/* used in amd_rank_create() */
423*20c794b3Sgavinm 
424*20c794b3Sgavinm 			nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
425*20c794b3Sgavinm 		}
426*20c794b3Sgavinm 
427*20c794b3Sgavinm 		nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
428*20c794b3Sgavinm 	}
429*20c794b3Sgavinm 
430*20c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
431*20c794b3Sgavinm }
432*20c794b3Sgavinm 
433*20c794b3Sgavinm static int
434*20c794b3Sgavinm amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
435*20c794b3Sgavinm     nvlist_t *auth)
436*20c794b3Sgavinm {
437*20c794b3Sgavinm 	int i, err, nerr = 0;
438*20c794b3Sgavinm 	nvpair_t *nvp;
439*20c794b3Sgavinm 	tnode_t *csnode;
440*20c794b3Sgavinm 	nvlist_t *fmri, **csarr = NULL;
441*20c794b3Sgavinm 	uint64_t csnum;
442*20c794b3Sgavinm 	uint_t ncs;
443*20c794b3Sgavinm 
444*20c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
445*20c794b3Sgavinm 		return (-1);
446*20c794b3Sgavinm 
447*20c794b3Sgavinm 	if (ncs == 0)
448*20c794b3Sgavinm 		return (0);	/* no chip-selects configured on this node */
449*20c794b3Sgavinm 
450*20c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
451*20c794b3Sgavinm 		return (-1);
452*20c794b3Sgavinm 
453*20c794b3Sgavinm 	for (i = 0; i < ncs; i++) {
454*20c794b3Sgavinm 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
455*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: cs num property "
456*20c794b3Sgavinm 			    "missing\n");
457*20c794b3Sgavinm 			continue;
458*20c794b3Sgavinm 		}
459*20c794b3Sgavinm 
460*20c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
461*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
462*20c794b3Sgavinm 			continue;
463*20c794b3Sgavinm 		}
464*20c794b3Sgavinm 
465*20c794b3Sgavinm 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
466*20c794b3Sgavinm 		    == NULL) {
467*20c794b3Sgavinm 			nvlist_free(fmri);
468*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
469*20c794b3Sgavinm 			continue;
470*20c794b3Sgavinm 		}
471*20c794b3Sgavinm 
472*20c794b3Sgavinm 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
473*20c794b3Sgavinm 
474*20c794b3Sgavinm 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
475*20c794b3Sgavinm 
476*20c794b3Sgavinm 		(void) topo_pgroup_create(csnode, &cs_pgroup, &err);
477*20c794b3Sgavinm 
478*20c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
479*20c794b3Sgavinm 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
480*20c794b3Sgavinm 			nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
481*20c794b3Sgavinm 		}
482*20c794b3Sgavinm 	}
483*20c794b3Sgavinm 
484*20c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
485*20c794b3Sgavinm }
486*20c794b3Sgavinm 
487*20c794b3Sgavinm static int
488*20c794b3Sgavinm amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
489*20c794b3Sgavinm     nvlist_t *auth)
490*20c794b3Sgavinm {
491*20c794b3Sgavinm 	tnode_t *chnode;
492*20c794b3Sgavinm 	nvlist_t *fmri;
493*20c794b3Sgavinm 	char *socket;
494*20c794b3Sgavinm 	int i, nchan;
495*20c794b3Sgavinm 	int err, nerr = 0;
496*20c794b3Sgavinm 
497*20c794b3Sgavinm 	/*
498*20c794b3Sgavinm 	 * We will enumerate the number of channels present even if only
499*20c794b3Sgavinm 	 * channel A is in use (i.e., running in 64-bit mode).  Only
500*20c794b3Sgavinm 	 * the socket 754 package has a single channel.
501*20c794b3Sgavinm 	 */
502*20c794b3Sgavinm 	if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
503*20c794b3Sgavinm 	    &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
504*20c794b3Sgavinm 		nchan = 1;
505*20c794b3Sgavinm 	else
506*20c794b3Sgavinm 		nchan = 2;
507*20c794b3Sgavinm 
508*20c794b3Sgavinm 	topo_mod_strfree(mod, socket);
509*20c794b3Sgavinm 
510*20c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
511*20c794b3Sgavinm 		return (-1);
512*20c794b3Sgavinm 
513*20c794b3Sgavinm 	for (i = 0; i < nchan; i++) {
514*20c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
515*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
516*20c794b3Sgavinm 			    "failed\n");
517*20c794b3Sgavinm 			continue;
518*20c794b3Sgavinm 		}
519*20c794b3Sgavinm 
520*20c794b3Sgavinm 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
521*20c794b3Sgavinm 		    == NULL) {
522*20c794b3Sgavinm 			nvlist_free(fmri);
523*20c794b3Sgavinm 			whinge(mod, &nerr, "amd_dramchan_create: node bind "
524*20c794b3Sgavinm 			    "failed\n");
525*20c794b3Sgavinm 			continue;
526*20c794b3Sgavinm 		}
527*20c794b3Sgavinm 
528*20c794b3Sgavinm 		nvlist_free(fmri);
529*20c794b3Sgavinm 
530*20c794b3Sgavinm 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
531*20c794b3Sgavinm 
532*20c794b3Sgavinm 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
533*20c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
534*20c794b3Sgavinm 	}
535*20c794b3Sgavinm 
536*20c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
537*20c794b3Sgavinm }
538*20c794b3Sgavinm 
539*20c794b3Sgavinm static int
540*20c794b3Sgavinm amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
541*20c794b3Sgavinm {
542*20c794b3Sgavinm 	nvpair_t *nvp;
543*20c794b3Sgavinm 	int nerr = 0;
544*20c794b3Sgavinm 
545*20c794b3Sgavinm 	if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
546*20c794b3Sgavinm 		whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
547*20c794b3Sgavinm 		return (-1);
548*20c794b3Sgavinm 	}
549*20c794b3Sgavinm 
550*20c794b3Sgavinm 	for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
551*20c794b3Sgavinm 	    nvp = nvlist_next_nvpair(htnvl, nvp)) {
552*20c794b3Sgavinm 		if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
553*20c794b3Sgavinm 			nerr++;
554*20c794b3Sgavinm 	}
555*20c794b3Sgavinm 
556*20c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
557*20c794b3Sgavinm }
558*20c794b3Sgavinm 
559*20c794b3Sgavinm void
560*20c794b3Sgavinm amd_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
561*20c794b3Sgavinm     int family, int model, int stepping, int *nerrp)
562*20c794b3Sgavinm {
563*20c794b3Sgavinm 	tnode_t *mcnode;
564*20c794b3Sgavinm 	nvlist_t *fmri;
565*20c794b3Sgavinm 	nvpair_t *nvp;
566*20c794b3Sgavinm 	nvlist_t *mc = NULL;
567*20c794b3Sgavinm 	int i;
568*20c794b3Sgavinm 
569*20c794b3Sgavinm 	/*
570*20c794b3Sgavinm 	 * Return with no error for anything before AMD family 0xf - we
571*20c794b3Sgavinm 	 * won't generate even a generic memory topolofy for earlier
572*20c794b3Sgavinm 	 * families.
573*20c794b3Sgavinm 	 */
574*20c794b3Sgavinm 	if (family < 0xf)
575*20c794b3Sgavinm 		return;
576*20c794b3Sgavinm 
577*20c794b3Sgavinm 	if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) {
578*20c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
579*20c794b3Sgavinm 		return;
580*20c794b3Sgavinm 	}
581*20c794b3Sgavinm 
582*20c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
583*20c794b3Sgavinm 		nvlist_free(fmri);
584*20c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: node range create failed\n");
585*20c794b3Sgavinm 		return;
586*20c794b3Sgavinm 	}
587*20c794b3Sgavinm 
588*20c794b3Sgavinm 	if ((mcnode = topo_node_bind(mod, pnode, name, 0,
589*20c794b3Sgavinm 	    fmri)) == NULL) {
590*20c794b3Sgavinm 		nvlist_free(mc);
591*20c794b3Sgavinm 		topo_node_range_destroy(pnode, name);
592*20c794b3Sgavinm 		nvlist_free(fmri);
593*20c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: mc bind failed\n");
594*20c794b3Sgavinm 		return;
595*20c794b3Sgavinm 	}
596*20c794b3Sgavinm 	(void) topo_node_fru_set(mcnode, NULL, 0, nerrp);
597*20c794b3Sgavinm 	nvlist_free(fmri);
598*20c794b3Sgavinm 
599*20c794b3Sgavinm 	if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
600*20c794b3Sgavinm 		/*
601*20c794b3Sgavinm 		 * If a memory-controller driver exists for this chip model
602*20c794b3Sgavinm 		 * it has not attached or has otherwise malfunctioned;
603*20c794b3Sgavinm 		 * alternatively no memory-controller driver exists for this
604*20c794b3Sgavinm 		 * (presumably newly-released) cpu model.  We fallback to
605*20c794b3Sgavinm 		 * creating a generic maximal topology.
606*20c794b3Sgavinm 		 */
607*20c794b3Sgavinm 		if (amd_generic_mc_create(mod, pnode, mcnode,
608*20c794b3Sgavinm 		    family, model, stepping, auth) != 0)
609*20c794b3Sgavinm 			++*nerrp;
610*20c794b3Sgavinm 		return;
611*20c794b3Sgavinm 	}
612*20c794b3Sgavinm 
613*20c794b3Sgavinm 	/*
614*20c794b3Sgavinm 	 * Add memory controller properties
615*20c794b3Sgavinm 	 */
616*20c794b3Sgavinm 	(void) topo_pgroup_create(mcnode, &mc_pgroup, nerrp);
617*20c794b3Sgavinm 
618*20c794b3Sgavinm 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
619*20c794b3Sgavinm 	    nvp = nvlist_next_nvpair(mc, nvp)) {
620*20c794b3Sgavinm 		char *name = nvpair_name(nvp);
621*20c794b3Sgavinm 		data_type_t type = nvpair_type(nvp);
622*20c794b3Sgavinm 
623*20c794b3Sgavinm 		if (type == DATA_TYPE_NVLIST_ARRAY &&
624*20c794b3Sgavinm 		    (strcmp(name, "cslist") == 0 ||
625*20c794b3Sgavinm 		    strcmp(name, "dimmlist") == 0)) {
626*20c794b3Sgavinm 			continue;
627*20c794b3Sgavinm 		} else if (type == DATA_TYPE_UINT8 &&
628*20c794b3Sgavinm 		    strcmp(name, MC_NVLIST_VERSTR) == 0) {
629*20c794b3Sgavinm 			continue;
630*20c794b3Sgavinm 		} else if (type == DATA_TYPE_NVLIST &&
631*20c794b3Sgavinm 		    strcmp(name, "htconfig") == 0) {
632*20c794b3Sgavinm 			nvlist_t *htnvl;
633*20c794b3Sgavinm 
634*20c794b3Sgavinm 			(void) nvpair_value_nvlist(nvp, &htnvl);
635*20c794b3Sgavinm 			if (amd_htconfig(mod, pnode, htnvl) != 0)
636*20c794b3Sgavinm 				++*nerrp;
637*20c794b3Sgavinm 		} else {
638*20c794b3Sgavinm 			if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
639*20c794b3Sgavinm 				++*nerrp;
640*20c794b3Sgavinm 		}
641*20c794b3Sgavinm 	}
642*20c794b3Sgavinm 
643*20c794b3Sgavinm 	if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
644*20c794b3Sgavinm 	    amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
645*20c794b3Sgavinm 	    amd_dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
646*20c794b3Sgavinm 		++*nerrp;
647*20c794b3Sgavinm 
648*20c794b3Sgavinm 	/*
649*20c794b3Sgavinm 	 * Free the fmris for the chip-selects allocated in amd_cs_create
650*20c794b3Sgavinm 	 */
651*20c794b3Sgavinm 	for (i = 0; i < MC_CHIP_NCS; i++) {
652*20c794b3Sgavinm 		if (cs_fmri[i] != NULL) {
653*20c794b3Sgavinm 			nvlist_free(cs_fmri[i]);
654*20c794b3Sgavinm 			cs_fmri[i] = NULL;
655*20c794b3Sgavinm 		}
656*20c794b3Sgavinm 	}
657*20c794b3Sgavinm 
658*20c794b3Sgavinm 	nvlist_free(mc);
659*20c794b3Sgavinm }
660