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