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