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