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 /*
2385738508SVuong Nguyen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2420c794b3Sgavinm  * Use is subject to license terms.
2520c794b3Sgavinm  */
2620c794b3Sgavinm 
2720c794b3Sgavinm #include <unistd.h>
2820c794b3Sgavinm #include <stdio.h>
2920c794b3Sgavinm #include <stdlib.h>
3020c794b3Sgavinm #include <stdarg.h>
3120c794b3Sgavinm #include <string.h>
3220c794b3Sgavinm #include <strings.h>
3320c794b3Sgavinm #include <limits.h>
3420c794b3Sgavinm #include <alloca.h>
3520c794b3Sgavinm #include <kstat.h>
3620c794b3Sgavinm #include <fcntl.h>
3720c794b3Sgavinm #include <errno.h>
3820c794b3Sgavinm #include <libnvpair.h>
3920c794b3Sgavinm #include <sys/types.h>
4020c794b3Sgavinm #include <sys/bitmap.h>
4120c794b3Sgavinm #include <sys/processor.h>
4220c794b3Sgavinm #include <sys/param.h>
4320c794b3Sgavinm #include <sys/fm/protocol.h>
4420c794b3Sgavinm #include <sys/systeminfo.h>
4520c794b3Sgavinm #include <sys/mc.h>
4620c794b3Sgavinm #include <sys/mc_amd.h>
4720c794b3Sgavinm #include <sys/mc_intel.h>
4820c794b3Sgavinm #include <fm/topo_mod.h>
4920c794b3Sgavinm 
5020c794b3Sgavinm #include "chip.h"
5120c794b3Sgavinm 
5220c794b3Sgavinm #ifndef MAX
5320c794b3Sgavinm #define	MAX(a, b)	((a) > (b) ? (a) : (b))
5420c794b3Sgavinm #endif
5520c794b3Sgavinm 
5620c794b3Sgavinm static const topo_pgroup_info_t dimm_channel_pgroup =
5720c794b3Sgavinm 	{ PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
5820c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup =
5920c794b3Sgavinm 	{ PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
6020c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup =
6120c794b3Sgavinm 	{ PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
6220c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup =
6320c794b3Sgavinm 	{ PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
6420c794b3Sgavinm 
6520c794b3Sgavinm static const topo_method_t dimm_methods[] = {
6620c794b3Sgavinm 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
6720c794b3Sgavinm 	    simple_dimm_label},
6820c794b3Sgavinm 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
6920c794b3Sgavinm 	    simple_dimm_label_mp},
7020c794b3Sgavinm 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
7120c794b3Sgavinm 	    seq_dimm_label},
7220c794b3Sgavinm 	{ NULL }
7320c794b3Sgavinm };
7420c794b3Sgavinm 
75e4b86885SCheng Sean Ye extern const topo_method_t rank_methods[];
76e4b86885SCheng Sean Ye extern const topo_method_t ntv_page_retire_methods[];
77e4b86885SCheng Sean Ye 
7820c794b3Sgavinm static int mc_fd;
7920c794b3Sgavinm 
8020c794b3Sgavinm int
8120c794b3Sgavinm mc_offchip_open()
8220c794b3Sgavinm {
8320c794b3Sgavinm 	mc_fd = open("/dev/mc/mc", O_RDONLY);
8420c794b3Sgavinm 	return (mc_fd != -1);
8520c794b3Sgavinm }
8620c794b3Sgavinm 
87e3d60c9bSAdrian Frost static int
88e3d60c9bSAdrian Frost mc_onchip(topo_instance_t id)
89e3d60c9bSAdrian Frost {
90e3d60c9bSAdrian Frost 	char path[64];
91e3d60c9bSAdrian Frost 
92e3d60c9bSAdrian Frost 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
93e3d60c9bSAdrian Frost 	mc_fd = open(path, O_RDONLY);
94e3d60c9bSAdrian Frost 	return (mc_fd != -1);
95e3d60c9bSAdrian Frost }
96e3d60c9bSAdrian Frost 
9785738508SVuong Nguyen static void
9820c794b3Sgavinm mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
9985738508SVuong Nguyen     nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part,
10085738508SVuong Nguyen     char *rev, int maxranks)
10120c794b3Sgavinm {
10220c794b3Sgavinm 	int i;
10320c794b3Sgavinm 	int rank;
10420c794b3Sgavinm 	tnode_t *rnode;
10520c794b3Sgavinm 	nvpair_t *nvp;
10620c794b3Sgavinm 	nvlist_t *fmri;
10720c794b3Sgavinm 	int err = 0;
10820c794b3Sgavinm 
10985738508SVuong Nguyen 	/*
11085738508SVuong Nguyen 	 * If start_rank is defined, it is assigned to the first rank of this
11185738508SVuong Nguyen 	 * dimm.
11285738508SVuong Nguyen 	 */
11385738508SVuong Nguyen 	rank = start_rank >= 0 ? start_rank : dimm * maxranks;
11420c794b3Sgavinm 	if (topo_node_range_create(mod, dnode, RANK, rank,
11520c794b3Sgavinm 	    rank + nranks - 1) < 0) {
11685738508SVuong Nguyen 		whinge(mod, NULL, "mc_add_ranks: node range create failed"
11720c794b3Sgavinm 		    " for rank\n");
11820c794b3Sgavinm 		return;
11920c794b3Sgavinm 	}
12020c794b3Sgavinm 	for (i = 0; i < nranks; i++) {
12120c794b3Sgavinm 		fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
12220c794b3Sgavinm 		    RANK, rank, NULL, auth, part, rev, serial);
12320c794b3Sgavinm 		if (fmri == NULL) {
12420c794b3Sgavinm 			whinge(mod, NULL,
12520c794b3Sgavinm 			    "mc_add_ranks: topo_mod_hcfmri failed\n");
12620c794b3Sgavinm 			return;
12720c794b3Sgavinm 		}
12820c794b3Sgavinm 		if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
12920c794b3Sgavinm 		    fmri)) == NULL) {
13020c794b3Sgavinm 			nvlist_free(fmri);
13120c794b3Sgavinm 			whinge(mod, NULL, "mc_add_ranks: node bind failed"
13220c794b3Sgavinm 			    " for ranks\n");
13320c794b3Sgavinm 			return;
13420c794b3Sgavinm 		}
13520c794b3Sgavinm 		(void) topo_node_fru_set(rnode, NULL, 0, &err);
13620c794b3Sgavinm 
13720c794b3Sgavinm 		if (topo_method_register(mod, rnode, rank_methods) < 0)
13820c794b3Sgavinm 			whinge(mod, &err, "rank_create: "
13920c794b3Sgavinm 			    "topo_method_register failed");
14020c794b3Sgavinm 
141e4b86885SCheng Sean Ye 		if (! is_xpv() && topo_method_register(mod, rnode,
142e4b86885SCheng Sean Ye 		    ntv_page_retire_methods) < 0)
143e4b86885SCheng Sean Ye 			whinge(mod, &err, "mc_add_ranks: "
144e4b86885SCheng Sean Ye 			    "topo_method_register failed");
145e4b86885SCheng Sean Ye 
14620c794b3Sgavinm 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
14720c794b3Sgavinm 
14820c794b3Sgavinm 		nvlist_free(fmri);
14920c794b3Sgavinm 
15020c794b3Sgavinm 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
15120c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
15220c794b3Sgavinm 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
15320c794b3Sgavinm 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
15420c794b3Sgavinm 		}
15520c794b3Sgavinm 		rank++;
15620c794b3Sgavinm 	}
15720c794b3Sgavinm }
15820c794b3Sgavinm 
15920c794b3Sgavinm static void
16020c794b3Sgavinm mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
161*491f61a1SYanmin Sun     nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks)
16220c794b3Sgavinm {
16320c794b3Sgavinm 	int i;
16420c794b3Sgavinm 	nvlist_t *fmri;
16520c794b3Sgavinm 	tnode_t *dnode;
16620c794b3Sgavinm 	nvpair_t *nvp;
16720c794b3Sgavinm 	int err;
16820c794b3Sgavinm 	nvlist_t **ranks_nvp;
16985738508SVuong Nguyen 	int32_t start_rank = -1;
17020c794b3Sgavinm 	uint_t nranks = 0;
171*491f61a1SYanmin Sun 	uint32_t dimm_number;
17220c794b3Sgavinm 	char *serial = NULL;
17320c794b3Sgavinm 	char *part = NULL;
17420c794b3Sgavinm 	char *rev = NULL;
17520c794b3Sgavinm 	char *label = NULL;
17620c794b3Sgavinm 	char *name;
17720c794b3Sgavinm 
178*491f61a1SYanmin Sun 	if (topo_node_range_create(mod, pnode, DIMM, 0,
179*491f61a1SYanmin Sun 	    maxdimms ? maxdimms-1 : ndimms-1) < 0) {
18020c794b3Sgavinm 		whinge(mod, NULL,
18120c794b3Sgavinm 		    "mc_add_dimms: node range create failed\n");
18220c794b3Sgavinm 		return;
18320c794b3Sgavinm 	}
18420c794b3Sgavinm 	for (i = 0; i < ndimms; i++) {
185*491f61a1SYanmin Sun 		dimm_number = i;
18620c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
18720c794b3Sgavinm 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
18820c794b3Sgavinm 			name = nvpair_name(nvp);
18920c794b3Sgavinm 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
19020c794b3Sgavinm 				(void) nvpair_value_nvlist_array(nvp,
19120c794b3Sgavinm 				    &ranks_nvp, &nranks);
19285738508SVuong Nguyen 			} else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) {
19385738508SVuong Nguyen 				(void) nvpair_value_int32(nvp, &start_rank);
19420c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
19520c794b3Sgavinm 				(void) nvpair_value_string(nvp, &serial);
19620c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
19720c794b3Sgavinm 				(void) nvpair_value_string(nvp, &part);
19820c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
19920c794b3Sgavinm 				(void) nvpair_value_string(nvp, &rev);
20020c794b3Sgavinm 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
20120c794b3Sgavinm 				(void) nvpair_value_string(nvp, &label);
202*491f61a1SYanmin Sun 			} else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) {
203*491f61a1SYanmin Sun 				(void) nvpair_value_uint32(nvp, &dimm_number);
20420c794b3Sgavinm 			}
20520c794b3Sgavinm 		}
20620c794b3Sgavinm 		fmri = NULL;
20720c794b3Sgavinm 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
208*491f61a1SYanmin Sun 		    DIMM, dimm_number, NULL, auth, part, rev, serial);
20920c794b3Sgavinm 		if (fmri == NULL) {
21020c794b3Sgavinm 			whinge(mod, NULL,
21120c794b3Sgavinm 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
21220c794b3Sgavinm 			return;
21320c794b3Sgavinm 		}
214*491f61a1SYanmin Sun 		if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number,
21520c794b3Sgavinm 		    fmri)) == NULL) {
21620c794b3Sgavinm 			nvlist_free(fmri);
21720c794b3Sgavinm 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
21820c794b3Sgavinm 			    " for dimm\n");
21920c794b3Sgavinm 			return;
22020c794b3Sgavinm 		}
22120c794b3Sgavinm 
22220c794b3Sgavinm 		if (topo_method_register(mod, dnode, dimm_methods) < 0)
22320c794b3Sgavinm 			whinge(mod, NULL, "mc_add_dimms: "
22420c794b3Sgavinm 			    "topo_method_register failed");
22520c794b3Sgavinm 
22620c794b3Sgavinm 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
22720c794b3Sgavinm 		nvlist_free(fmri);
22820c794b3Sgavinm 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
22920c794b3Sgavinm 
23020c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
23120c794b3Sgavinm 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
23220c794b3Sgavinm 			name = nvpair_name(nvp);
23320c794b3Sgavinm 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
23485738508SVuong Nguyen 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0 &&
23585738508SVuong Nguyen 			    strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) {
23620c794b3Sgavinm 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
23720c794b3Sgavinm 				    dnode);
23820c794b3Sgavinm 			}
23920c794b3Sgavinm 		}
24020c794b3Sgavinm 		if (label)
24120c794b3Sgavinm 			(void) topo_node_label_set(dnode, label, &err);
24220c794b3Sgavinm 
24320c794b3Sgavinm 		if (nranks) {
244*491f61a1SYanmin Sun 			mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp,
245*491f61a1SYanmin Sun 			    start_rank, nranks, serial, part, rev, maxranks);
24620c794b3Sgavinm 		}
24720c794b3Sgavinm 	}
24820c794b3Sgavinm }
24920c794b3Sgavinm 
25020c794b3Sgavinm static int
25120c794b3Sgavinm mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth,
252*491f61a1SYanmin Sun     nvlist_t *nvl, int maxdimms, int maxranks)
25320c794b3Sgavinm {
25420c794b3Sgavinm 	tnode_t *mc_channel;
25520c794b3Sgavinm 	nvlist_t *fmri;
25620c794b3Sgavinm 	nvlist_t **dimm_nvl;
257e3d60c9bSAdrian Frost 	nvpair_t *nvp;
258e3d60c9bSAdrian Frost 	char *name;
25920c794b3Sgavinm 	uint_t ndimms;
26020c794b3Sgavinm 	int err;
26120c794b3Sgavinm 
26220c794b3Sgavinm 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
26320c794b3Sgavinm 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
26420c794b3Sgavinm 		return (-1);
26520c794b3Sgavinm 	}
26620c794b3Sgavinm 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
26720c794b3Sgavinm 	    fmri)) == NULL) {
26820c794b3Sgavinm 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
26920c794b3Sgavinm 		    DRAMCHANNEL);
27020c794b3Sgavinm 		nvlist_free(fmri);
27120c794b3Sgavinm 		return (-1);
27220c794b3Sgavinm 	}
27320c794b3Sgavinm 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
27420c794b3Sgavinm 	nvlist_free(fmri);
27520c794b3Sgavinm 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
27620c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
27720c794b3Sgavinm 	    &ndimms) == 0) {
278*491f61a1SYanmin Sun 		mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms, maxdimms,
279*491f61a1SYanmin Sun 		    maxranks);
280e3d60c9bSAdrian Frost 	}
281e3d60c9bSAdrian Frost 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
282e3d60c9bSAdrian Frost 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
283e3d60c9bSAdrian Frost 		name = nvpair_name(nvp);
284e3d60c9bSAdrian Frost 		if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
285e3d60c9bSAdrian Frost 			(void) nvprop_add(mod, nvp, PGNAME(CHAN),
286e3d60c9bSAdrian Frost 			    mc_channel);
287e3d60c9bSAdrian Frost 		}
28820c794b3Sgavinm 	}
28920c794b3Sgavinm 	return (0);
29020c794b3Sgavinm }
29120c794b3Sgavinm 
29220c794b3Sgavinm static int
29320c794b3Sgavinm mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth,
29420c794b3Sgavinm     nvlist_t *nvl)
29520c794b3Sgavinm {
29620c794b3Sgavinm 	int err;
29720c794b3Sgavinm 	int i, j;
29820c794b3Sgavinm 	int channel;
299e3d60c9bSAdrian Frost 	uint8_t nmc;
300e3d60c9bSAdrian Frost 	uint8_t maxranks;
301*491f61a1SYanmin Sun 	uint8_t maxdimms;
30220c794b3Sgavinm 	tnode_t *mcnode;
30320c794b3Sgavinm 	nvlist_t *fmri;
30420c794b3Sgavinm 	nvlist_t **channel_nvl;
305e3d60c9bSAdrian Frost 	nvpair_t *nvp;
306e3d60c9bSAdrian Frost 	char *pname;
30720c794b3Sgavinm 	uint_t nchannels;
30820c794b3Sgavinm 
30920c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
31020c794b3Sgavinm 	    &nchannels) != 0) {
31120c794b3Sgavinm 		whinge(mod, NULL,
31220c794b3Sgavinm 		    "mc_nb_create: failed to find channel information\n");
31320c794b3Sgavinm 		return (-1);
31420c794b3Sgavinm 	}
31585738508SVuong Nguyen 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) {
31685738508SVuong Nguyen 		/*
31785738508SVuong Nguyen 		 * Assume channels are evenly divided among the controllers.
31885738508SVuong Nguyen 		 * Convert nchannels to channels per controller
31985738508SVuong Nguyen 		 */
32085738508SVuong Nguyen 		nchannels = nchannels / nmc;
32185738508SVuong Nguyen 	} else {
322e3d60c9bSAdrian Frost 		/*
323e3d60c9bSAdrian Frost 		 * if number of memory controllers is not specified then there
324e3d60c9bSAdrian Frost 		 * are two channels per controller and the nchannels is total
325e3d60c9bSAdrian Frost 		 * we will set up nmc as number of controllers and convert
326e3d60c9bSAdrian Frost 		 * nchannels to channels per controller
327e3d60c9bSAdrian Frost 		 */
328e3d60c9bSAdrian Frost 		nmc = nchannels / 2;
329e3d60c9bSAdrian Frost 		nchannels = nchannels / nmc;
330e3d60c9bSAdrian Frost 	}
331e3d60c9bSAdrian Frost 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
332e3d60c9bSAdrian Frost 		maxranks = 2;
333*491f61a1SYanmin Sun 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0)
334*491f61a1SYanmin Sun 		maxdimms = 0;
33520c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
33620c794b3Sgavinm 		whinge(mod, NULL,
33720c794b3Sgavinm 		    "mc_nb_create: node range create failed\n");
33820c794b3Sgavinm 		return (-1);
33920c794b3Sgavinm 	}
34020c794b3Sgavinm 	channel = 0;
34120c794b3Sgavinm 	for (i = 0; i < nmc; i++) {
34220c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
34320c794b3Sgavinm 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");
34420c794b3Sgavinm 			return (-1);
34520c794b3Sgavinm 		}
34620c794b3Sgavinm 		if ((mcnode = topo_node_bind(mod, pnode, name, i,
34720c794b3Sgavinm 		    fmri)) == NULL) {
348e3d60c9bSAdrian Frost 			whinge(mod, NULL, "mc_nb_create: node bind failed"
34920c794b3Sgavinm 			    " for memory-controller\n");
35020c794b3Sgavinm 			nvlist_free(fmri);
35120c794b3Sgavinm 			return (-1);
35220c794b3Sgavinm 		}
35320c794b3Sgavinm 
35420c794b3Sgavinm 		(void) topo_node_fru_set(mcnode, NULL, 0, &err);
35520c794b3Sgavinm 		nvlist_free(fmri);
35620c794b3Sgavinm 		(void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
35720c794b3Sgavinm 
35820c794b3Sgavinm 		if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
359e3d60c9bSAdrian Frost 		    channel + nchannels - 1) < 0) {
36020c794b3Sgavinm 			whinge(mod, NULL,
36120c794b3Sgavinm 			    "mc_nb_create: channel node range create failed\n");
36220c794b3Sgavinm 			return (-1);
36320c794b3Sgavinm 		}
364e3d60c9bSAdrian Frost 		for (j = 0; j < nchannels; j++) {
36520c794b3Sgavinm 			if (mc_add_channel(mod, mcnode, channel, auth,
366*491f61a1SYanmin Sun 			    channel_nvl[channel], maxdimms, maxranks) < 0) {
36720c794b3Sgavinm 				return (-1);
36820c794b3Sgavinm 			}
36920c794b3Sgavinm 			channel++;
37020c794b3Sgavinm 		}
371e3d60c9bSAdrian Frost 		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
372e3d60c9bSAdrian Frost 		    nvp = nvlist_next_nvpair(nvl, nvp)) {
373e3d60c9bSAdrian Frost 			pname = nvpair_name(nvp);
374e3d60c9bSAdrian Frost 			if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
375e3d60c9bSAdrian Frost 			    strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
376e3d60c9bSAdrian Frost 			    strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
377*491f61a1SYanmin Sun 			    strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 &&
378e3d60c9bSAdrian Frost 			    strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
379e3d60c9bSAdrian Frost 			    strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
380e3d60c9bSAdrian Frost 				(void) nvprop_add(mod, nvp, PGNAME(MCT),
381e3d60c9bSAdrian Frost 				    mcnode);
382e3d60c9bSAdrian Frost 			}
383e3d60c9bSAdrian Frost 		}
38420c794b3Sgavinm 	}
38520c794b3Sgavinm 
38620c794b3Sgavinm 	return (NULL);
38720c794b3Sgavinm }
38820c794b3Sgavinm 
38920c794b3Sgavinm int
390e3d60c9bSAdrian Frost mc_node_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
39120c794b3Sgavinm     nvlist_t *auth)
39220c794b3Sgavinm {
39320c794b3Sgavinm 	mc_snapshot_info_t mcs;
39420c794b3Sgavinm 	void *buf = NULL;
39520c794b3Sgavinm 	nvlist_t *nvl;
39620c794b3Sgavinm 	uint8_t ver;
39720c794b3Sgavinm 	int rc;
39820c794b3Sgavinm 
39920c794b3Sgavinm 	if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
40020c794b3Sgavinm 	    (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
40120c794b3Sgavinm 	    ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
40220c794b3Sgavinm 
40320c794b3Sgavinm 		whinge(mod, NULL, "mc failed to snapshot %s\n",
40420c794b3Sgavinm 		    strerror(errno));
40520c794b3Sgavinm 
40620c794b3Sgavinm 		free(buf);
40720c794b3Sgavinm 		(void) close(mc_fd);
40820c794b3Sgavinm 		return (NULL);
40920c794b3Sgavinm 	}
41020c794b3Sgavinm 	(void) close(mc_fd);
41120c794b3Sgavinm 	(void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
41220c794b3Sgavinm 	topo_mod_free(mod, buf, mcs.mcs_size);
41320c794b3Sgavinm 
41420c794b3Sgavinm 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
41520c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist is not versioned\n");
41620c794b3Sgavinm 		nvlist_free(nvl);
41720c794b3Sgavinm 		return (NULL);
41820c794b3Sgavinm 	} else if (ver != MCINTEL_NVLIST_VERS0) {
41920c794b3Sgavinm 		whinge(mod, NULL, "mc nvlist version mismatch\n");
42020c794b3Sgavinm 		nvlist_free(nvl);
42120c794b3Sgavinm 		return (NULL);
42220c794b3Sgavinm 	}
42320c794b3Sgavinm 
42420c794b3Sgavinm 	rc = mc_nb_create(mod, pnode, name, auth, nvl);
42520c794b3Sgavinm 
42620c794b3Sgavinm 	nvlist_free(nvl);
42720c794b3Sgavinm 	return (rc);
42820c794b3Sgavinm }
429e3d60c9bSAdrian Frost 
430e3d60c9bSAdrian Frost void
431e3d60c9bSAdrian Frost onchip_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
432e3d60c9bSAdrian Frost     nvlist_t *auth)
433e3d60c9bSAdrian Frost {
434e3d60c9bSAdrian Frost 	if (mc_onchip(topo_node_instance(pnode)))
435e3d60c9bSAdrian Frost 		(void) mc_node_create(mod, pnode, name, auth);
436e3d60c9bSAdrian Frost }
437e3d60c9bSAdrian Frost 
438e3d60c9bSAdrian Frost int
439e3d60c9bSAdrian Frost mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
440e3d60c9bSAdrian Frost     nvlist_t *auth)
441e3d60c9bSAdrian Frost {
442e3d60c9bSAdrian Frost 	return (mc_node_create(mod, pnode, name, auth));
443e3d60c9bSAdrian Frost }
444