120c794b3Sgavinm /*
220c794b3Sgavinm  * CDDL HEADER START
320c794b3Sgavinm  *
420c794b3Sgavinm  * The contents of this file are subject to the terms of the
520c794b3Sgavinm  * Common Development and Distribution License (the "License").
620c794b3Sgavinm  * You may not use this file except in compliance with the License.
720c794b3Sgavinm  *
820c794b3Sgavinm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
920c794b3Sgavinm  * or http://www.opensolaris.org/os/licensing.
1020c794b3Sgavinm  * See the License for the specific language governing permissions
1120c794b3Sgavinm  * and limitations under the License.
1220c794b3Sgavinm  *
1320c794b3Sgavinm  * When distributing Covered Code, include this CDDL HEADER in each
1420c794b3Sgavinm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1520c794b3Sgavinm  * If applicable, add the following below this CDDL HEADER, with the
1620c794b3Sgavinm  * fields enclosed by brackets "[]" replaced with your own identifying
1720c794b3Sgavinm  * information: Portions Copyright [yyyy] [name of copyright owner]
1820c794b3Sgavinm  *
1920c794b3Sgavinm  * CDDL HEADER END
2020c794b3Sgavinm  */
2120c794b3Sgavinm 
2220c794b3Sgavinm /*
232cb5535aSrobj  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2420c794b3Sgavinm  * Use is subject to license terms.
2520c794b3Sgavinm  */
2620c794b3Sgavinm 
2720c794b3Sgavinm #pragma ident	"%Z%%M%	%I%	%E% SMI"
2820c794b3Sgavinm 
2920c794b3Sgavinm /*
3020c794b3Sgavinm  * AMD memory enumeration
3120c794b3Sgavinm  */
3220c794b3Sgavinm 
3320c794b3Sgavinm #include <sys/types.h>
3420c794b3Sgavinm #include <unistd.h>
3520c794b3Sgavinm #include <stropts.h>
3620c794b3Sgavinm #include <sys/fm/protocol.h>
3720c794b3Sgavinm #include <sys/mc.h>
3820c794b3Sgavinm #include <sys/mc_amd.h>
3920c794b3Sgavinm #include <fm/topo_mod.h>
4020c794b3Sgavinm #include <strings.h>
4120c794b3Sgavinm #include <sys/stat.h>
4220c794b3Sgavinm #include <fcntl.h>
4320c794b3Sgavinm 
4420c794b3Sgavinm #include "chip.h"
4520c794b3Sgavinm 
4620c794b3Sgavinm #define	MAX_CHANNUM	1
4720c794b3Sgavinm #define	MAX_DIMMNUM	7
4820c794b3Sgavinm #define	MAX_CSNUM	7
4920c794b3Sgavinm 
5020c794b3Sgavinm static const topo_pgroup_info_t cs_pgroup =
5120c794b3Sgavinm 	{ PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
5220c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup =
5320c794b3Sgavinm 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
5420c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup =
5520c794b3Sgavinm 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
5620c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup =
5720c794b3Sgavinm 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
5820c794b3Sgavinm static const topo_pgroup_info_t chan_pgroup =
5920c794b3Sgavinm 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
6020c794b3Sgavinm 
6120c794b3Sgavinm static const topo_method_t dimm_methods[] = {
6220c794b3Sgavinm 	{ SIMPLE_DIMM_LBL, "Property method", 0,
6320c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, simple_dimm_label},
6420c794b3Sgavinm 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0,
6520c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, simple_dimm_label_mp},
6620c794b3Sgavinm 	{ SEQ_DIMM_LBL, "Property method", 0,
6720c794b3Sgavinm 	    TOPO_STABILITY_INTERNAL, seq_dimm_label},
682cb5535aSrobj 	{ G4_DIMM_LBL, "Property method", 0,
692cb5535aSrobj 	    TOPO_STABILITY_INTERNAL, g4_dimm_label},
70*918a0d8aSrobj 	{ G12F_DIMM_LBL, "Property method", 0,
71*918a0d8aSrobj 	    TOPO_STABILITY_INTERNAL, g12f_dimm_label},
722cb5535aSrobj 	{ GET_DIMM_SERIAL, "Property method", 0,
732cb5535aSrobj 	    TOPO_STABILITY_INTERNAL, get_dimm_serial},
7420c794b3Sgavinm 	{ NULL }
7520c794b3Sgavinm };
7620c794b3Sgavinm 
7720c794b3Sgavinm static const topo_method_t rank_methods[] = {
7820c794b3Sgavinm 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
7920c794b3Sgavinm 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
8020c794b3Sgavinm 	    mem_asru_compute },
812cb5535aSrobj 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
822cb5535aSrobj 	    TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL,
832cb5535aSrobj 	    rank_fmri_present },
8420c794b3Sgavinm 	{ NULL }
8520c794b3Sgavinm };
8620c794b3Sgavinm 
8720c794b3Sgavinm static const topo_method_t gen_cs_methods[] = {
8820c794b3Sgavinm 	{ TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC,
8920c794b3Sgavinm 	    TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL,
9020c794b3Sgavinm 	    mem_asru_compute },
915108f83cSrobj 	{ SIMPLE_CS_LBL_MP, "Property method", 0,
925108f83cSrobj 	    TOPO_STABILITY_INTERNAL, simple_cs_label_mp},
9320c794b3Sgavinm 	{ NULL }
9420c794b3Sgavinm };
9520c794b3Sgavinm 
9620c794b3Sgavinm static nvlist_t *cs_fmri[MC_CHIP_NCS];
9720c794b3Sgavinm 
9820c794b3Sgavinm /*
9920c794b3Sgavinm  * Called when there is no memory-controller driver to provide topology
10020c794b3Sgavinm  * information.  Generate a maximal memory topology that is appropriate
10120c794b3Sgavinm  * for the chip revision.  The memory-controller node has already been
10220c794b3Sgavinm  * bound as mcnode, and the parent of that is cnode.
10320c794b3Sgavinm  *
10420c794b3Sgavinm  * We create a tree of dram-channel and chip-select nodes below the
10520c794b3Sgavinm  * memory-controller node.  There will be two dram channels and 8 chip-selects
10620c794b3Sgavinm  * below each, regardless of actual socket type, processor revision and so on.
10720c794b3Sgavinm  * This is adequate for generic diagnosis up to family 0x10 revision C.
10820c794b3Sgavinm  * When support for revision D is implemented (or maybe C) we should take
10920c794b3Sgavinm  * the opportunity to rework the topology tree completely (socket change will
11020c794b3Sgavinm  * mean there can be no diagnosis history tied to the topology).
11120c794b3Sgavinm  */
11220c794b3Sgavinm /*ARGSUSED*/
11320c794b3Sgavinm static int
11420c794b3Sgavinm amd_generic_mc_create(topo_mod_t *mod, tnode_t *cnode, tnode_t *mcnode,
11520c794b3Sgavinm     int family, int model, int stepping, nvlist_t *auth)
11620c794b3Sgavinm {
11720c794b3Sgavinm 	int chan, cs;
11820c794b3Sgavinm 
11920c794b3Sgavinm 	/*
12020c794b3Sgavinm 	 * Elsewhere we have already returned for families less than 0xf.
12120c794b3Sgavinm 	 * This "generic" topology is adequate for all of family 0xf and
12220c794b3Sgavinm 	 * for revisions A, B and C of family 0x10 (A = model 0, B = model 1,
12320c794b3Sgavinm 	 * we'll guess C = model 3 at this point).
12420c794b3Sgavinm 	 */
12520c794b3Sgavinm 	if (family > 0x10 || (family == 0x10 && model > 3))
12620c794b3Sgavinm 		return (1);
12720c794b3Sgavinm 
12820c794b3Sgavinm 	if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0,
12920c794b3Sgavinm 	    MAX_CHANNUM) < 0) {
13020c794b3Sgavinm 		whinge(mod, NULL, "amd_generic_mc_create: range create for "
13120c794b3Sgavinm 		    "channels failed\n");
13220c794b3Sgavinm 		return (-1);
13320c794b3Sgavinm 	}
13420c794b3Sgavinm 
13520c794b3Sgavinm 	for (chan = 0; chan <= MAX_CHANNUM; chan++) {
13620c794b3Sgavinm 		tnode_t *chnode;
13720c794b3Sgavinm 		nvlist_t *fmri;
13820c794b3Sgavinm 		int err;
13920c794b3Sgavinm 
14020c794b3Sgavinm 		if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth,
14120c794b3Sgavinm 		    &fmri) != 0) {
14220c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: mkrsrc "
14320c794b3Sgavinm 			    "failed\n");
14420c794b3Sgavinm 			return (-1);
14520c794b3Sgavinm 		}
14620c794b3Sgavinm 
14720c794b3Sgavinm 		if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME,
14820c794b3Sgavinm 		    chan, fmri)) == NULL) {
14920c794b3Sgavinm 			nvlist_free(fmri);
15020c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: node "
15120c794b3Sgavinm 			    "bind failed\n");
15220c794b3Sgavinm 			return (-1);
15320c794b3Sgavinm 		}
15420c794b3Sgavinm 
15520c794b3Sgavinm 		nvlist_free(fmri);
15620c794b3Sgavinm 
15720c794b3Sgavinm 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
15820c794b3Sgavinm 
15920c794b3Sgavinm 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
16020c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err);
16120c794b3Sgavinm 
16220c794b3Sgavinm 		if (topo_node_range_create(mod, chnode, CS_NODE_NAME,
16320c794b3Sgavinm 		    0, MAX_CSNUM) < 0) {
16420c794b3Sgavinm 			whinge(mod, NULL, "amd_generic_mc_create: "
16520c794b3Sgavinm 			    "range create for cs failed\n");
16620c794b3Sgavinm 			return (-1);
16720c794b3Sgavinm 		}
16820c794b3Sgavinm 
16920c794b3Sgavinm 		for (cs = 0; cs <= MAX_CSNUM; cs++) {
17020c794b3Sgavinm 			tnode_t *csnode;
17120c794b3Sgavinm 
17220c794b3Sgavinm 			if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth,
17320c794b3Sgavinm 			    &fmri) != 0) {
17420c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
17520c794b3Sgavinm 				    "mkrsrc for cs failed\n");
17620c794b3Sgavinm 				return (-1);
17720c794b3Sgavinm 			}
17820c794b3Sgavinm 
17920c794b3Sgavinm 			if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME,
18020c794b3Sgavinm 			    cs, fmri)) == NULL) {
18120c794b3Sgavinm 				nvlist_free(fmri);
18220c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
18320c794b3Sgavinm 				    "bind for cs failed\n");
18420c794b3Sgavinm 				return (-1);
18520c794b3Sgavinm 			}
18620c794b3Sgavinm 
18720c794b3Sgavinm 			/*
18820c794b3Sgavinm 			 * Dynamic ASRU for page faults within a chip-select.
18920c794b3Sgavinm 			 * The topology does not represent pages (there are
19020c794b3Sgavinm 			 * too many) so when a page is faulted we generate
19120c794b3Sgavinm 			 * an ASRU to represent the individual page.
19220c794b3Sgavinm 			 */
19320c794b3Sgavinm 			if (topo_method_register(mod, csnode,
19420c794b3Sgavinm 			    gen_cs_methods) < 0)
19520c794b3Sgavinm 				whinge(mod, NULL, "amd_generic_mc_create: "
19620c794b3Sgavinm 				    "method registration failed\n");
19720c794b3Sgavinm 
19820c794b3Sgavinm 			(void) topo_node_asru_set(csnode, fmri,
19920c794b3Sgavinm 			    TOPO_ASRU_COMPUTE, &err);
20020c794b3Sgavinm 
20120c794b3Sgavinm 			nvlist_free(fmri);
20220c794b3Sgavinm 		}
20320c794b3Sgavinm 	}
20420c794b3Sgavinm 
20520c794b3Sgavinm 	return (0);
20620c794b3Sgavinm }
20720c794b3Sgavinm 
20820c794b3Sgavinm static nvlist_t *
20920c794b3Sgavinm amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id)
21020c794b3Sgavinm {
21120c794b3Sgavinm 	mc_snapshot_info_t mcs;
21220c794b3Sgavinm 	void *buf = NULL;
21320c794b3Sgavinm 	uint8_t ver;
21420c794b3Sgavinm 
21520c794b3Sgavinm 	nvlist_t *nvl = NULL;
21620c794b3Sgavinm 	char path[64];
21720c794b3Sgavinm 	int fd, err;
21820c794b3Sgavinm 
21920c794b3Sgavinm 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
22020c794b3Sgavinm 	fd = open(path, O_RDONLY);
22120c794b3Sgavinm 
22220c794b3Sgavinm 	if (fd == -1) {
22320c794b3Sgavinm 		/*
22420c794b3Sgavinm 		 * Some v20z and v40z systems may have had the 3rd-party
22520c794b3Sgavinm 		 * NWSnps packagae installed which installs a /dev/mc
22620c794b3Sgavinm 		 * link.  So try again via /devices.
22720c794b3Sgavinm 		 */
22820c794b3Sgavinm 		(void) snprintf(path, sizeof (path),
22920c794b3Sgavinm 		    "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd",
23020c794b3Sgavinm 		    MC_AMD_DEV_OFFSET + id);
23120c794b3Sgavinm 		fd = open(path, O_RDONLY);
23220c794b3Sgavinm 	}
23320c794b3Sgavinm 
23420c794b3Sgavinm 	if (fd == -1)
23520c794b3Sgavinm 		return (NULL);	/* do not whinge */
23620c794b3Sgavinm 
23720c794b3Sgavinm 	if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
23820c794b3Sgavinm 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
23920c794b3Sgavinm 	    ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) {
24020c794b3Sgavinm 
24120c794b3Sgavinm 		whinge(mod, NULL, "mc failed to snapshot %s: %s\n",
24220c794b3Sgavinm 		    path, strerror(errno));
24320c794b3Sgavinm 
24420c794b3Sgavinm 		free(buf);
24520c794b3Sgavinm 		(void) close(fd);
24620c794b3Sgavinm 		return (NULL);
24720c794b3Sgavinm 	}
24820c794b3Sgavinm 
24920c794b3Sgavinm 	(void) close(fd);
25020c794b3Sgavinm 	err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
25120c794b3Sgavinm 	topo_mod_free(mod, buf, mcs.mcs_size);
25220c794b3Sgavinm 
25320c794b3Sgavinm 
25420c794b3Sgavinm 	if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) {
25520c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist is not versioned\n");
25620c794b3Sgavinm 		nvlist_free(nvl);
25720c794b3Sgavinm 		return (NULL);
25820c794b3Sgavinm 	} else if (ver != MC_NVLIST_VERS1) {
25920c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist version mismatch\n");
26020c794b3Sgavinm 		nvlist_free(nvl);
26120c794b3Sgavinm 		return (NULL);
26220c794b3Sgavinm 	}
26320c794b3Sgavinm 
26420c794b3Sgavinm 	return (err ? NULL : nvl);
26520c794b3Sgavinm }
26620c794b3Sgavinm 
26720c794b3Sgavinm int
26820c794b3Sgavinm amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl,
26920c794b3Sgavinm     nvlist_t *auth)
27020c794b3Sgavinm {
27120c794b3Sgavinm 	uint64_t *csnumarr;
27220c794b3Sgavinm 	char **csnamearr;
27320c794b3Sgavinm 	uint_t ncs, ncsname;
27420c794b3Sgavinm 	tnode_t *ranknode;
27520c794b3Sgavinm 	nvlist_t *fmri, *pfmri = NULL;
27620c794b3Sgavinm 	uint64_t dsz, rsz;
27720c794b3Sgavinm 	int nerr = 0;
27820c794b3Sgavinm 	int err;
27920c794b3Sgavinm 	int i;
28020c794b3Sgavinm 
28120c794b3Sgavinm 	if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr,
28220c794b3Sgavinm 	    &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames",
28320c794b3Sgavinm 	    &csnamearr, &ncsname) != 0 || ncs != ncsname) {
28420c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: "
28520c794b3Sgavinm 		    "csnums/csnames extraction failed\n");
28620c794b3Sgavinm 		return (nerr);
28720c794b3Sgavinm 	}
28820c794b3Sgavinm 
28920c794b3Sgavinm 	if (topo_node_resource(pnode, &pfmri, &err) < 0) {
29020c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: parent fmri lookup "
29120c794b3Sgavinm 		    "failed\n");
29220c794b3Sgavinm 		return (nerr);
29320c794b3Sgavinm 	}
29420c794b3Sgavinm 
29520c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) {
29620c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: range create failed\n");
29720c794b3Sgavinm 		nvlist_free(pfmri);
29820c794b3Sgavinm 		return (nerr);
29920c794b3Sgavinm 	}
30020c794b3Sgavinm 
30120c794b3Sgavinm 	if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz,
30220c794b3Sgavinm 	    &err) == 0) {
30320c794b3Sgavinm 		rsz = dsz / ncs;
30420c794b3Sgavinm 	} else {
30520c794b3Sgavinm 		whinge(mod, &nerr, "amd_rank_create: parent dimm has no "
30620c794b3Sgavinm 		    "size\n");
30720c794b3Sgavinm 		return (nerr);
30820c794b3Sgavinm 	}
30920c794b3Sgavinm 
31020c794b3Sgavinm 	for (i = 0; i < ncs; i++) {
31120c794b3Sgavinm 		if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) {
31220c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n");
31320c794b3Sgavinm 			continue;
31420c794b3Sgavinm 		}
31520c794b3Sgavinm 
31620c794b3Sgavinm 		if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i,
31720c794b3Sgavinm 		    fmri)) == NULL) {
31820c794b3Sgavinm 			nvlist_free(fmri);
31920c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: node bind "
32020c794b3Sgavinm 			    "failed\n");
32120c794b3Sgavinm 			continue;
32220c794b3Sgavinm 		}
32320c794b3Sgavinm 
32420c794b3Sgavinm 		nvlist_free(fmri);
32520c794b3Sgavinm 
32620c794b3Sgavinm 		(void) topo_node_fru_set(ranknode, pfmri, 0, &err);
32720c794b3Sgavinm 
32820c794b3Sgavinm 		/*
32920c794b3Sgavinm 		 * If a rank is faulted the asru is the associated
33020c794b3Sgavinm 		 * chip-select, but if a page within a rank is faulted
33120c794b3Sgavinm 		 * the asru is just that page.  Hence the dual preconstructed
33220c794b3Sgavinm 		 * and computed ASRU.
33320c794b3Sgavinm 		 */
33420c794b3Sgavinm 		if (topo_method_register(mod, ranknode, rank_methods) < 0)
33520c794b3Sgavinm 			whinge(mod, &nerr, "amd_rank_create: "
33620c794b3Sgavinm 			    "topo_method_register failed");
33720c794b3Sgavinm 
33820c794b3Sgavinm 		(void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]],
33920c794b3Sgavinm 		    TOPO_ASRU_COMPUTE, &err);
34020c794b3Sgavinm 
34120c794b3Sgavinm 		(void) topo_pgroup_create(ranknode, &rank_pgroup, &err);
34220c794b3Sgavinm 
34320c794b3Sgavinm 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size",
34420c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, rsz, &err);
34520c794b3Sgavinm 
34620c794b3Sgavinm 		(void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname",
34720c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, csnamearr[i], &err);
34820c794b3Sgavinm 
34920c794b3Sgavinm 		(void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum",
35020c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, csnumarr[i], &err);
35120c794b3Sgavinm 	}
35220c794b3Sgavinm 
35320c794b3Sgavinm 	nvlist_free(pfmri);
35420c794b3Sgavinm 
35520c794b3Sgavinm 	return (nerr);
35620c794b3Sgavinm }
35720c794b3Sgavinm 
35820c794b3Sgavinm static int
35920c794b3Sgavinm amd_dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
36020c794b3Sgavinm     nvlist_t *mc, nvlist_t *auth)
36120c794b3Sgavinm {
36220c794b3Sgavinm 	int i, err, nerr = 0;
36320c794b3Sgavinm 	nvpair_t *nvp;
36420c794b3Sgavinm 	tnode_t *dimmnode;
36520c794b3Sgavinm 	nvlist_t *fmri, *asru, **dimmarr = NULL;
36620c794b3Sgavinm 	uint64_t num;
36720c794b3Sgavinm 	uint_t ndimm;
36820c794b3Sgavinm 
36920c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) {
37020c794b3Sgavinm 		whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n");
37120c794b3Sgavinm 		return (-1);
37220c794b3Sgavinm 	}
37320c794b3Sgavinm 
37420c794b3Sgavinm 	if (ndimm == 0)
37520c794b3Sgavinm 		return (0);	/* no dimms present on this node */
37620c794b3Sgavinm 
37720c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) {
37820c794b3Sgavinm 		whinge(mod, NULL, "amd_dimm_create: range create failed\n");
37920c794b3Sgavinm 		return (-1);
38020c794b3Sgavinm 	}
38120c794b3Sgavinm 
38220c794b3Sgavinm 	for (i = 0; i < ndimm; i++) {
38320c794b3Sgavinm 		if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) {
38420c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: dimm num property "
38520c794b3Sgavinm 			    "missing\n");
38620c794b3Sgavinm 			continue;
38720c794b3Sgavinm 		}
38820c794b3Sgavinm 
38920c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) {
39020c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n");
39120c794b3Sgavinm 			continue;
39220c794b3Sgavinm 		}
39320c794b3Sgavinm 
39420c794b3Sgavinm 		if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri))
39520c794b3Sgavinm 		    == NULL) {
39620c794b3Sgavinm 			nvlist_free(fmri);
39720c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: node bind "
39820c794b3Sgavinm 			    "failed\n");
39920c794b3Sgavinm 			continue;
40020c794b3Sgavinm 		}
40120c794b3Sgavinm 
40220c794b3Sgavinm 		if (topo_method_register(mod, dimmnode, dimm_methods) < 0)
4032cb5535aSrobj 			whinge(mod, &nerr, "amd_dimm_create: "
40420c794b3Sgavinm 			    "topo_method_register failed");
40520c794b3Sgavinm 
40620c794b3Sgavinm 		/*
40720c794b3Sgavinm 		 * Use the mem computation method directly to publish the asru
40820c794b3Sgavinm 		 * in the "mem" scheme.
40920c794b3Sgavinm 		 */
41020c794b3Sgavinm 		if (mem_asru_create(mod, fmri, &asru) == 0) {
41120c794b3Sgavinm 			(void) topo_node_asru_set(dimmnode, asru, 0, &err);
41220c794b3Sgavinm 			nvlist_free(asru);
41320c794b3Sgavinm 		} else {
41420c794b3Sgavinm 
41520c794b3Sgavinm 			nvlist_free(fmri);
41620c794b3Sgavinm 			whinge(mod, &nerr, "amd_dimm_create: "
41720c794b3Sgavinm 			    "mem_asru_create failed\n");
41820c794b3Sgavinm 			continue;
41920c794b3Sgavinm 		}
42020c794b3Sgavinm 
42120c794b3Sgavinm 		(void) topo_node_fru_set(dimmnode, fmri, 0, &err);
42220c794b3Sgavinm 
42320c794b3Sgavinm 		nvlist_free(fmri);
42420c794b3Sgavinm 
42520c794b3Sgavinm 		(void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err);
42620c794b3Sgavinm 
42720c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL;
42820c794b3Sgavinm 		    nvp = nvlist_next_nvpair(dimmarr[i], nvp)) {
42920c794b3Sgavinm 			if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY &&
43020c794b3Sgavinm 			    strcmp(nvpair_name(nvp), "csnums") == 0 ||
43120c794b3Sgavinm 			    nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY &&
43220c794b3Sgavinm 			    strcmp(nvpair_name(nvp), "csnames") == 0)
43320c794b3Sgavinm 				continue;	/* used in amd_rank_create() */
43420c794b3Sgavinm 
43520c794b3Sgavinm 			nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode);
43620c794b3Sgavinm 		}
43720c794b3Sgavinm 
43820c794b3Sgavinm 		nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth);
43920c794b3Sgavinm 	}
44020c794b3Sgavinm 
44120c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
44220c794b3Sgavinm }
44320c794b3Sgavinm 
44420c794b3Sgavinm static int
44520c794b3Sgavinm amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc,
44620c794b3Sgavinm     nvlist_t *auth)
44720c794b3Sgavinm {
44820c794b3Sgavinm 	int i, err, nerr = 0;
44920c794b3Sgavinm 	nvpair_t *nvp;
45020c794b3Sgavinm 	tnode_t *csnode;
45120c794b3Sgavinm 	nvlist_t *fmri, **csarr = NULL;
45220c794b3Sgavinm 	uint64_t csnum;
45320c794b3Sgavinm 	uint_t ncs;
45420c794b3Sgavinm 
45520c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0)
45620c794b3Sgavinm 		return (-1);
45720c794b3Sgavinm 
45820c794b3Sgavinm 	if (ncs == 0)
45920c794b3Sgavinm 		return (0);	/* no chip-selects configured on this node */
46020c794b3Sgavinm 
46120c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0)
46220c794b3Sgavinm 		return (-1);
46320c794b3Sgavinm 
46420c794b3Sgavinm 	for (i = 0; i < ncs; i++) {
46520c794b3Sgavinm 		if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) {
46620c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: cs num property "
46720c794b3Sgavinm 			    "missing\n");
46820c794b3Sgavinm 			continue;
46920c794b3Sgavinm 		}
47020c794b3Sgavinm 
47120c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) {
47220c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n");
47320c794b3Sgavinm 			continue;
47420c794b3Sgavinm 		}
47520c794b3Sgavinm 
47620c794b3Sgavinm 		if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri))
47720c794b3Sgavinm 		    == NULL) {
47820c794b3Sgavinm 			nvlist_free(fmri);
47920c794b3Sgavinm 			whinge(mod, &nerr, "amd_cs_create: node bind failed\n");
48020c794b3Sgavinm 			continue;
48120c794b3Sgavinm 		}
48220c794b3Sgavinm 
48320c794b3Sgavinm 		cs_fmri[csnum] = fmri;	/* nvlist will be freed in mc_create */
48420c794b3Sgavinm 
48520c794b3Sgavinm 		(void) topo_node_asru_set(csnode, fmri, 0, &err);
48620c794b3Sgavinm 
48720c794b3Sgavinm 		(void) topo_pgroup_create(csnode, &cs_pgroup, &err);
48820c794b3Sgavinm 
48920c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL;
49020c794b3Sgavinm 		    nvp = nvlist_next_nvpair(csarr[i], nvp)) {
49120c794b3Sgavinm 			nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode);
49220c794b3Sgavinm 		}
49320c794b3Sgavinm 	}
49420c794b3Sgavinm 
49520c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
49620c794b3Sgavinm }
49720c794b3Sgavinm 
49820c794b3Sgavinm static int
49920c794b3Sgavinm amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
50020c794b3Sgavinm     nvlist_t *auth)
50120c794b3Sgavinm {
50220c794b3Sgavinm 	tnode_t *chnode;
50320c794b3Sgavinm 	nvlist_t *fmri;
50420c794b3Sgavinm 	char *socket;
50520c794b3Sgavinm 	int i, nchan;
50620c794b3Sgavinm 	int err, nerr = 0;
50720c794b3Sgavinm 
50820c794b3Sgavinm 	/*
50920c794b3Sgavinm 	 * We will enumerate the number of channels present even if only
51020c794b3Sgavinm 	 * channel A is in use (i.e., running in 64-bit mode).  Only
51120c794b3Sgavinm 	 * the socket 754 package has a single channel.
51220c794b3Sgavinm 	 */
51320c794b3Sgavinm 	if (topo_prop_get_string(pnode, PGNAME(MCT), "socket",
51420c794b3Sgavinm 	    &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0)
51520c794b3Sgavinm 		nchan = 1;
51620c794b3Sgavinm 	else
51720c794b3Sgavinm 		nchan = 2;
51820c794b3Sgavinm 
51920c794b3Sgavinm 	topo_mod_strfree(mod, socket);
52020c794b3Sgavinm 
52120c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0)
52220c794b3Sgavinm 		return (-1);
52320c794b3Sgavinm 
52420c794b3Sgavinm 	for (i = 0; i < nchan; i++) {
52520c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
52620c794b3Sgavinm 			whinge(mod, &nerr, "amd_dramchan_create: mkrsrc "
52720c794b3Sgavinm 			    "failed\n");
52820c794b3Sgavinm 			continue;
52920c794b3Sgavinm 		}
53020c794b3Sgavinm 
53120c794b3Sgavinm 		if ((chnode = topo_node_bind(mod, pnode, name, i, fmri))
53220c794b3Sgavinm 		    == NULL) {
53320c794b3Sgavinm 			nvlist_free(fmri);
53420c794b3Sgavinm 			whinge(mod, &nerr, "amd_dramchan_create: node bind "
53520c794b3Sgavinm 			    "failed\n");
53620c794b3Sgavinm 			continue;
53720c794b3Sgavinm 		}
53820c794b3Sgavinm 
53920c794b3Sgavinm 		nvlist_free(fmri);
54020c794b3Sgavinm 
54120c794b3Sgavinm 		(void) topo_pgroup_create(chnode, &chan_pgroup, &err);
54220c794b3Sgavinm 
54320c794b3Sgavinm 		(void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel",
54420c794b3Sgavinm 		    TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err);
54520c794b3Sgavinm 	}
54620c794b3Sgavinm 
54720c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
54820c794b3Sgavinm }
54920c794b3Sgavinm 
55020c794b3Sgavinm static int
55120c794b3Sgavinm amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl)
55220c794b3Sgavinm {
55320c794b3Sgavinm 	nvpair_t *nvp;
55420c794b3Sgavinm 	int nerr = 0;
55520c794b3Sgavinm 
55620c794b3Sgavinm 	if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) {
55720c794b3Sgavinm 		whinge(mod, &nerr, "amd_htconfig: must pass a chip node!");
55820c794b3Sgavinm 		return (-1);
55920c794b3Sgavinm 	}
56020c794b3Sgavinm 
56120c794b3Sgavinm 	for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL;
56220c794b3Sgavinm 	    nvp = nvlist_next_nvpair(htnvl, nvp)) {
56320c794b3Sgavinm 		if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0)
56420c794b3Sgavinm 			nerr++;
56520c794b3Sgavinm 	}
56620c794b3Sgavinm 
56720c794b3Sgavinm 	return (nerr == 0 ? 0 : -1);
56820c794b3Sgavinm }
56920c794b3Sgavinm 
57020c794b3Sgavinm void
57120c794b3Sgavinm amd_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
57220c794b3Sgavinm     int family, int model, int stepping, int *nerrp)
57320c794b3Sgavinm {
57420c794b3Sgavinm 	tnode_t *mcnode;
57520c794b3Sgavinm 	nvlist_t *fmri;
57620c794b3Sgavinm 	nvpair_t *nvp;
57720c794b3Sgavinm 	nvlist_t *mc = NULL;
57820c794b3Sgavinm 	int i;
57920c794b3Sgavinm 
58020c794b3Sgavinm 	/*
58120c794b3Sgavinm 	 * Return with no error for anything before AMD family 0xf - we
58220c794b3Sgavinm 	 * won't generate even a generic memory topolofy for earlier
58320c794b3Sgavinm 	 * families.
58420c794b3Sgavinm 	 */
58520c794b3Sgavinm 	if (family < 0xf)
58620c794b3Sgavinm 		return;
58720c794b3Sgavinm 
58820c794b3Sgavinm 	if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) {
58920c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: mkrsrc failed\n");
59020c794b3Sgavinm 		return;
59120c794b3Sgavinm 	}
59220c794b3Sgavinm 
59320c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) {
59420c794b3Sgavinm 		nvlist_free(fmri);
59520c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: node range create failed\n");
59620c794b3Sgavinm 		return;
59720c794b3Sgavinm 	}
59820c794b3Sgavinm 
59920c794b3Sgavinm 	if ((mcnode = topo_node_bind(mod, pnode, name, 0,
60020c794b3Sgavinm 	    fmri)) == NULL) {
60120c794b3Sgavinm 		nvlist_free(mc);
60220c794b3Sgavinm 		topo_node_range_destroy(pnode, name);
60320c794b3Sgavinm 		nvlist_free(fmri);
60420c794b3Sgavinm 		whinge(mod, nerrp, "mc_create: mc bind failed\n");
60520c794b3Sgavinm 		return;
60620c794b3Sgavinm 	}
60720c794b3Sgavinm 	(void) topo_node_fru_set(mcnode, NULL, 0, nerrp);
60820c794b3Sgavinm 	nvlist_free(fmri);
60920c794b3Sgavinm 
61020c794b3Sgavinm 	if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) {
61120c794b3Sgavinm 		/*
61220c794b3Sgavinm 		 * If a memory-controller driver exists for this chip model
61320c794b3Sgavinm 		 * it has not attached or has otherwise malfunctioned;
61420c794b3Sgavinm 		 * alternatively no memory-controller driver exists for this
61520c794b3Sgavinm 		 * (presumably newly-released) cpu model.  We fallback to
61620c794b3Sgavinm 		 * creating a generic maximal topology.
61720c794b3Sgavinm 		 */
61820c794b3Sgavinm 		if (amd_generic_mc_create(mod, pnode, mcnode,
61920c794b3Sgavinm 		    family, model, stepping, auth) != 0)
62020c794b3Sgavinm 			++*nerrp;
62120c794b3Sgavinm 		return;
62220c794b3Sgavinm 	}
62320c794b3Sgavinm 
62420c794b3Sgavinm 	/*
62520c794b3Sgavinm 	 * Add memory controller properties
62620c794b3Sgavinm 	 */
62720c794b3Sgavinm 	(void) topo_pgroup_create(mcnode, &mc_pgroup, nerrp);
62820c794b3Sgavinm 
62920c794b3Sgavinm 	for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL;
63020c794b3Sgavinm 	    nvp = nvlist_next_nvpair(mc, nvp)) {
63120c794b3Sgavinm 		char *name = nvpair_name(nvp);
63220c794b3Sgavinm 		data_type_t type = nvpair_type(nvp);
63320c794b3Sgavinm 
63420c794b3Sgavinm 		if (type == DATA_TYPE_NVLIST_ARRAY &&
63520c794b3Sgavinm 		    (strcmp(name, "cslist") == 0 ||
63620c794b3Sgavinm 		    strcmp(name, "dimmlist") == 0)) {
63720c794b3Sgavinm 			continue;
63820c794b3Sgavinm 		} else if (type == DATA_TYPE_UINT8 &&
63920c794b3Sgavinm 		    strcmp(name, MC_NVLIST_VERSTR) == 0) {
64020c794b3Sgavinm 			continue;
64120c794b3Sgavinm 		} else if (type == DATA_TYPE_NVLIST &&
64220c794b3Sgavinm 		    strcmp(name, "htconfig") == 0) {
64320c794b3Sgavinm 			nvlist_t *htnvl;
64420c794b3Sgavinm 
64520c794b3Sgavinm 			(void) nvpair_value_nvlist(nvp, &htnvl);
64620c794b3Sgavinm 			if (amd_htconfig(mod, pnode, htnvl) != 0)
64720c794b3Sgavinm 				++*nerrp;
64820c794b3Sgavinm 		} else {
64920c794b3Sgavinm 			if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0)
65020c794b3Sgavinm 				++*nerrp;
65120c794b3Sgavinm 		}
65220c794b3Sgavinm 	}
65320c794b3Sgavinm 
65420c794b3Sgavinm 	if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 ||
65520c794b3Sgavinm 	    amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 ||
65620c794b3Sgavinm 	    amd_dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0)
65720c794b3Sgavinm 		++*nerrp;
65820c794b3Sgavinm 
65920c794b3Sgavinm 	/*
66020c794b3Sgavinm 	 * Free the fmris for the chip-selects allocated in amd_cs_create
66120c794b3Sgavinm 	 */
66220c794b3Sgavinm 	for (i = 0; i < MC_CHIP_NCS; i++) {
66320c794b3Sgavinm 		if (cs_fmri[i] != NULL) {
66420c794b3Sgavinm 			nvlist_free(cs_fmri[i]);
66520c794b3Sgavinm 			cs_fmri[i] = NULL;
66620c794b3Sgavinm 		}
66720c794b3Sgavinm 	}
66820c794b3Sgavinm 
66920c794b3Sgavinm 	nvlist_free(mc);
67020c794b3Sgavinm }
671