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