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  */
2220c794b3Sgavinm /*
23efd31e1dSTrang Do  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24eb00b1c8SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
2520c794b3Sgavinm  */
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>
5020c794b3Sgavinm #include "chip.h"
5220c794b3Sgavinm #ifndef MAX
5320c794b3Sgavinm #define	MAX(a, b)	((a) > (b) ? (a) : (b))
5420c794b3Sgavinm #endif
56eb00b1c8SRobert Mustacchi /*
57eb00b1c8SRobert Mustacchi  * These are names that we use for properties. In the v0 scheme we try to pull
58eb00b1c8SRobert Mustacchi  * these directly from the memory controller (which also causes a lot more
59eb00b1c8SRobert Mustacchi  * variation). In the v1 scheme we translate to the name that topo wants to use,
60eb00b1c8SRobert Mustacchi  * regardless of what the parent is called.
61eb00b1c8SRobert Mustacchi  */
62eb00b1c8SRobert Mustacchi #define	MC_PROP_ECC	"memory-ecc"
63eb00b1c8SRobert Mustacchi #define	MC_PROP_POLICY	"memory-policy"
64eb00b1c8SRobert Mustacchi #define	CHAN_PROP_MODE	"channel-mode"
65eb00b1c8SRobert Mustacchi #define	DIMM_SIZE	"size"
66eb00b1c8SRobert Mustacchi #define	DIMM_STRING_SIZE	"dimm-size"
67eb00b1c8SRobert Mustacchi #define	DIMM_COL	"ncolumn"
68eb00b1c8SRobert Mustacchi #define	DIMM_ROW	"nrow"
69eb00b1c8SRobert Mustacchi #define	DIMM_DENSITY	"density"
70eb00b1c8SRobert Mustacchi #define	DIMM_WIDTH	"width"
71eb00b1c8SRobert Mustacchi #define	DIMM_RANKS	"ranks"
72eb00b1c8SRobert Mustacchi #define	DIMM_BANKS	"nbanks"
73eb00b1c8SRobert Mustacchi #define	DIMM_HDRL	"hdrl-enabled"
74eb00b1c8SRobert Mustacchi #define	DIMM_HDRL_PARITY	"hdrl-parity"
75eb00b1c8SRobert Mustacchi #define	DIMM_3DRANK	"3d-subranks"
76ae6d4bc3SRobert Mustacchi #define	RANK_STATUS	"dimm-rank-status"
77ae6d4bc3SRobert Mustacchi #define	RANK_SIZE	"dimm-rank-size"
78eb00b1c8SRobert Mustacchi 
7920c794b3Sgavinm static const topo_pgroup_info_t dimm_channel_pgroup =
8120c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup =
8320c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup =
8520c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup =
8820c794b3Sgavinm static const topo_method_t dimm_methods[] = {
8920c794b3Sgavinm 	{ SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
9020c794b3Sgavinm 	    simple_dimm_label},
9120c794b3Sgavinm 	{ SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL,
9220c794b3Sgavinm 	    simple_dimm_label_mp},
9320c794b3Sgavinm 	{ SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL,
9420c794b3Sgavinm 	    seq_dimm_label},
9520c794b3Sgavinm 	{ NULL }
9620c794b3Sgavinm };
98e4b86885SCheng Sean Ye extern const topo_method_t rank_methods[];
99e4b86885SCheng Sean Ye extern const topo_method_t ntv_page_retire_methods[];
100e4b86885SCheng Sean Ye 
10120c794b3Sgavinm static int mc_fd;
10320c794b3Sgavinm int
mc_offchip_open()10420c794b3Sgavinm mc_offchip_open()
10520c794b3Sgavinm {
10620c794b3Sgavinm 	mc_fd = open("/dev/mc/mc", O_RDONLY);
10720c794b3Sgavinm 	return (mc_fd != -1);
10820c794b3Sgavinm }
110e3d60c9bSAdrian Frost static int
mc_onchip(topo_instance_t id)111e3d60c9bSAdrian Frost mc_onchip(topo_instance_t id)
112e3d60c9bSAdrian Frost {
113e3d60c9bSAdrian Frost 	char path[64];
114e3d60c9bSAdrian Frost 
115e3d60c9bSAdrian Frost 	(void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id);
116e3d60c9bSAdrian Frost 	mc_fd = open(path, O_RDONLY);
117e3d60c9bSAdrian Frost 	return (mc_fd != -1);
118e3d60c9bSAdrian Frost }
119e3d60c9bSAdrian Frost 
12085738508SVuong Nguyen static void
mc_add_ranks(topo_mod_t * mod,tnode_t * dnode,nvlist_t * auth,int dimm,nvlist_t ** ranks_nvp,int start_rank,int nranks,char * serial,char * part,char * rev,int maxranks)12120c794b3Sgavinm mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm,
12285738508SVuong Nguyen     nvlist_t **ranks_nvp, int start_rank, int nranks, char *serial, char *part,
12385738508SVuong Nguyen     char *rev, int maxranks)
12420c794b3Sgavinm {
12520c794b3Sgavinm 	int i;
12620c794b3Sgavinm 	int rank;
12720c794b3Sgavinm 	tnode_t *rnode;
12820c794b3Sgavinm 	nvpair_t *nvp;
12920c794b3Sgavinm 	nvlist_t *fmri;
13020c794b3Sgavinm 	int err = 0;
13285738508SVuong Nguyen 	/*
13385738508SVuong Nguyen 	 * If start_rank is defined, it is assigned to the first rank of this
13485738508SVuong Nguyen 	 * dimm.
13585738508SVuong Nguyen 	 */
13685738508SVuong Nguyen 	rank = start_rank >= 0 ? start_rank : dimm * maxranks;
13720c794b3Sgavinm 	if (topo_node_range_create(mod, dnode, RANK, rank,
13820c794b3Sgavinm 	    rank + nranks - 1) < 0) {
13985738508SVuong Nguyen 		whinge(mod, NULL, "mc_add_ranks: node range create failed"
14020c794b3Sgavinm 		    " for rank\n");
14120c794b3Sgavinm 		return;
14220c794b3Sgavinm 	}
14320c794b3Sgavinm 	for (i = 0; i < nranks; i++) {
14420c794b3Sgavinm 		fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION,
14520c794b3Sgavinm 		    RANK, rank, NULL, auth, part, rev, serial);
14620c794b3Sgavinm 		if (fmri == NULL) {
14720c794b3Sgavinm 			whinge(mod, NULL,
14820c794b3Sgavinm 			    "mc_add_ranks: topo_mod_hcfmri failed\n");
14920c794b3Sgavinm 			return;
15020c794b3Sgavinm 		}
15120c794b3Sgavinm 		if ((rnode = topo_node_bind(mod, dnode, RANK, rank,
15220c794b3Sgavinm 		    fmri)) == NULL) {
15320c794b3Sgavinm 			nvlist_free(fmri);
15420c794b3Sgavinm 			whinge(mod, NULL, "mc_add_ranks: node bind failed"
15520c794b3Sgavinm 			    " for ranks\n");
15620c794b3Sgavinm 			return;
15720c794b3Sgavinm 		}
15820c794b3Sgavinm 		(void) topo_node_fru_set(rnode, NULL, 0, &err);
16020c794b3Sgavinm 		if (topo_method_register(mod, rnode, rank_methods) < 0)
161ae6d4bc3SRobert Mustacchi 			whinge(mod, &err, "mc_add_ranks: "
16220c794b3Sgavinm 			    "topo_method_register failed");
164e4b86885SCheng Sean Ye 		if (! is_xpv() && topo_method_register(mod, rnode,
165e4b86885SCheng Sean Ye 		    ntv_page_retire_methods) < 0)
166e4b86885SCheng Sean Ye 			whinge(mod, &err, "mc_add_ranks: "
167e4b86885SCheng Sean Ye 			    "topo_method_register failed");
168e4b86885SCheng Sean Ye 
16920c794b3Sgavinm 		(void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err);
171074bb90dSTom Pothier 		if (FM_AWARE_SMBIOS(mod))
172d3c57c1fSTom Pothier 			(void) topo_node_label_set(rnode, NULL, &err);
173074bb90dSTom Pothier 
17420c794b3Sgavinm 		nvlist_free(fmri);
17620c794b3Sgavinm 		(void) topo_pgroup_create(rnode, &rank_pgroup, &err);
17720c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL;
17820c794b3Sgavinm 		    nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) {
17920c794b3Sgavinm 			(void) nvprop_add(mod, nvp, PGNAME(RANK), rnode);
18020c794b3Sgavinm 		}
18120c794b3Sgavinm 		rank++;
18220c794b3Sgavinm 	}
18320c794b3Sgavinm }
18520c794b3Sgavinm static void
mc_add_dimms(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,nvlist_t * auth,nvlist_t ** nvl,uint_t ndimms,int maxdimms,int maxranks)186074bb90dSTom Pothier mc_add_dimms(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
187074bb90dSTom Pothier     nvlist_t *auth, nvlist_t **nvl, uint_t ndimms, int maxdimms, int maxranks)
18820c794b3Sgavinm {
18920c794b3Sgavinm 	int i;
19020c794b3Sgavinm 	nvlist_t *fmri;
19120c794b3Sgavinm 	tnode_t *dnode;
19220c794b3Sgavinm 	nvpair_t *nvp;
19320c794b3Sgavinm 	int err;
19420c794b3Sgavinm 	nvlist_t **ranks_nvp;
19585738508SVuong Nguyen 	int32_t start_rank = -1;
19620c794b3Sgavinm 	uint_t nranks = 0;
197491f61a1SYanmin Sun 	uint32_t dimm_number;
19820c794b3Sgavinm 	char *serial = NULL;
19920c794b3Sgavinm 	char *part = NULL;
20020c794b3Sgavinm 	char *rev = NULL;
20120c794b3Sgavinm 	char *label = NULL;
20220c794b3Sgavinm 	char *name;
203*58d4b16fSRobert Mustacchi 	id_t smbid = -1;
205491f61a1SYanmin Sun 	if (topo_node_range_create(mod, pnode, DIMM, 0,
206491f61a1SYanmin Sun 	    maxdimms ? maxdimms-1 : ndimms-1) < 0) {
20720c794b3Sgavinm 		whinge(mod, NULL,
20820c794b3Sgavinm 		    "mc_add_dimms: node range create failed\n");
20920c794b3Sgavinm 		return;
21020c794b3Sgavinm 	}
21120c794b3Sgavinm 	for (i = 0; i < ndimms; i++) {
212491f61a1SYanmin Sun 		dimm_number = i;
21320c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
21420c794b3Sgavinm 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
21520c794b3Sgavinm 			name = nvpair_name(nvp);
21620c794b3Sgavinm 			if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) {
21720c794b3Sgavinm 				(void) nvpair_value_nvlist_array(nvp,
21820c794b3Sgavinm 				    &ranks_nvp, &nranks);
21985738508SVuong Nguyen 			} else if (strcmp(name, MCINTEL_NVLIST_1ST_RANK) == 0) {
22085738508SVuong Nguyen 				(void) nvpair_value_int32(nvp, &start_rank);
22120c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) {
22220c794b3Sgavinm 				(void) nvpair_value_string(nvp, &serial);
22320c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_PART) == 0) {
22420c794b3Sgavinm 				(void) nvpair_value_string(nvp, &part);
22520c794b3Sgavinm 			} else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) {
22620c794b3Sgavinm 				(void) nvpair_value_string(nvp, &rev);
22720c794b3Sgavinm 			} else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) {
22820c794b3Sgavinm 				(void) nvpair_value_string(nvp, &label);
229491f61a1SYanmin Sun 			} else if (strcmp(name, MCINTEL_NVLIST_DIMM_NUM) == 0) {
230491f61a1SYanmin Sun 				(void) nvpair_value_uint32(nvp, &dimm_number);
23120c794b3Sgavinm 			}
23220c794b3Sgavinm 		}
23320c794b3Sgavinm 		fmri = NULL;
234074bb90dSTom Pothier 
235074bb90dSTom Pothier 		if (FM_AWARE_SMBIOS(mod)) {
236074bb90dSTom Pothier 			int channum;
237074bb90dSTom Pothier 
238074bb90dSTom Pothier 			channum = topo_node_instance(pnode);
239efd31e1dSTrang Do 			smbid = memnode_to_smbiosid(mod, chip_smbid,
240efd31e1dSTrang Do 			    DIMM_NODE_NAME, i, &channum);
241074bb90dSTom Pothier 			if (serial == NULL)
242074bb90dSTom Pothier 				serial = (char *)chip_serial_smbios_get(mod,
243074bb90dSTom Pothier 				    smbid);
244074bb90dSTom Pothier 			if (part == NULL)
245074bb90dSTom Pothier 				part = (char *)chip_part_smbios_get(mod,
246074bb90dSTom Pothier 				    smbid);
247074bb90dSTom Pothier 			if (rev == NULL)
248074bb90dSTom Pothier 				rev = (char *)chip_rev_smbios_get(mod,
249074bb90dSTom Pothier 				    smbid);
250074bb90dSTom Pothier 		}
251074bb90dSTom Pothier 
25220c794b3Sgavinm 		fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
253491f61a1SYanmin Sun 		    DIMM, dimm_number, NULL, auth, part, rev, serial);
25420c794b3Sgavinm 		if (fmri == NULL) {
25520c794b3Sgavinm 			whinge(mod, NULL,
25620c794b3Sgavinm 			    "mc_add_dimms: topo_mod_hcfmri failed\n");
25720c794b3Sgavinm 			return;
25820c794b3Sgavinm 		}
259491f61a1SYanmin Sun 		if ((dnode = topo_node_bind(mod, pnode, DIMM, dimm_number,
26020c794b3Sgavinm 		    fmri)) == NULL) {
26120c794b3Sgavinm 			nvlist_free(fmri);
26220c794b3Sgavinm 			whinge(mod, NULL, "mc_add_dimms: node bind failed"
26320c794b3Sgavinm 			    " for dimm\n");
26420c794b3Sgavinm 			return;
26520c794b3Sgavinm 		}
267074bb90dSTom Pothier 		if (!FM_AWARE_SMBIOS(mod))
268074bb90dSTom Pothier 			if (topo_method_register(mod, dnode, dimm_methods) < 0)
269074bb90dSTom Pothier 				whinge(mod, NULL, "mc_add_dimms: "
270074bb90dSTom Pothier 				    "topo_method_register failed");
27220c794b3Sgavinm 		(void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
27420c794b3Sgavinm 		for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL;
27520c794b3Sgavinm 		    nvp = nvlist_next_nvpair(nvl[i], nvp)) {
27620c794b3Sgavinm 			name = nvpair_name(nvp);
27720c794b3Sgavinm 			if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 &&
27885738508SVuong Nguyen 			    strcmp(name, FM_FAULT_FRU_LABEL) != 0 &&
27985738508SVuong Nguyen 			    strcmp(name, MCINTEL_NVLIST_1ST_RANK) != 0) {
28020c794b3Sgavinm 				(void) nvprop_add(mod, nvp, PGNAME(DIMM),
28120c794b3Sgavinm 				    dnode);
28220c794b3Sgavinm 			}
28320c794b3Sgavinm 		}
284074bb90dSTom Pothier 
285074bb90dSTom Pothier 		if (FM_AWARE_SMBIOS(mod)) {
286074bb90dSTom Pothier 			nvlist_free(fmri);
287074bb90dSTom Pothier 			(void) topo_node_resource(dnode, &fmri, &err);
288074bb90dSTom Pothier 			/*
289074bb90dSTom Pothier 			 * We will use a full absolute parent/child label
290074bb90dSTom Pothier 			 */
291074bb90dSTom Pothier 			label = (char *)chip_label_smbios_get(mod,
292074bb90dSTom Pothier 			    pnode, smbid, label);
293074bb90dSTom Pothier 		}
294074bb90dSTom Pothier 
295074bb90dSTom Pothier 		(void) topo_node_label_set(dnode, label, &err);
296074bb90dSTom Pothier 
297074bb90dSTom Pothier 		if (FM_AWARE_SMBIOS(mod))
298074bb90dSTom Pothier 			topo_mod_strfree(mod, label);
299074bb90dSTom Pothier 
300074bb90dSTom Pothier 		(void) topo_node_fru_set(dnode, fmri, 0, &err);
301074bb90dSTom Pothier 		(void) topo_node_asru_set(dnode, fmri, 0, &err);
302074bb90dSTom Pothier 		nvlist_free(fmri);
30420c794b3Sgavinm 		if (nranks) {
305491f61a1SYanmin Sun 			mc_add_ranks(mod, dnode, auth, dimm_number, ranks_nvp,
306491f61a1SYanmin Sun 			    start_rank, nranks, serial, part, rev, maxranks);
30720c794b3Sgavinm 		}
30820c794b3Sgavinm 	}
30920c794b3Sgavinm }
31120c794b3Sgavinm static int
mc_add_channel(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,int channel,nvlist_t * auth,nvlist_t * nvl,int maxdimms,int maxranks)312074bb90dSTom Pothier mc_add_channel(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
313074bb90dSTom Pothier     int channel, nvlist_t *auth, nvlist_t *nvl, int maxdimms, int maxranks)
31420c794b3Sgavinm {
31520c794b3Sgavinm 	tnode_t *mc_channel;
31620c794b3Sgavinm 	nvlist_t *fmri;
31720c794b3Sgavinm 	nvlist_t **dimm_nvl;
318e3d60c9bSAdrian Frost 	nvpair_t *nvp;
319e3d60c9bSAdrian Frost 	char *name;
32020c794b3Sgavinm 	uint_t ndimms;
32120c794b3Sgavinm 	int err;
32320c794b3Sgavinm 	if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) {
32420c794b3Sgavinm 		whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n");
32520c794b3Sgavinm 		return (-1);
32620c794b3Sgavinm 	}
32720c794b3Sgavinm 	if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel,
32820c794b3Sgavinm 	    fmri)) == NULL) {
32920c794b3Sgavinm 		whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n",
33020c794b3Sgavinm 		    DRAMCHANNEL);
33120c794b3Sgavinm 		nvlist_free(fmri);
33220c794b3Sgavinm 		return (-1);
33320c794b3Sgavinm 	}
33420c794b3Sgavinm 	(void) topo_node_fru_set(mc_channel, NULL, 0, &err);
33520c794b3Sgavinm 	nvlist_free(fmri);
33620c794b3Sgavinm 	(void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err);
337074bb90dSTom Pothier 
338074bb90dSTom Pothier 	if (FM_AWARE_SMBIOS(mod))
339074bb90dSTom Pothier 		(void) topo_node_label_set(mc_channel, NULL, &err);
340074bb90dSTom Pothier 
34120c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl,
34220c794b3Sgavinm 	    &ndimms) == 0) {
343074bb90dSTom Pothier 		mc_add_dimms(mod, chip_smbid, mc_channel, auth, dimm_nvl,
344074bb90dSTom Pothier 		    ndimms, maxdimms, maxranks);
345e3d60c9bSAdrian Frost 	}
346e3d60c9bSAdrian Frost 	for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
347e3d60c9bSAdrian Frost 	    nvp = nvlist_next_nvpair(nvl, nvp)) {
348e3d60c9bSAdrian Frost 		name = nvpair_name(nvp);
349e3d60c9bSAdrian Frost 		if (strcmp(name, MCINTEL_NVLIST_DIMMS) != 0) {
350e3d60c9bSAdrian Frost 			(void) nvprop_add(mod, nvp, PGNAME(CHAN),
351e3d60c9bSAdrian Frost 			    mc_channel);
352e3d60c9bSAdrian Frost 		}
35320c794b3Sgavinm 	}
354074bb90dSTom Pothier 
35520c794b3Sgavinm 	return (0);
35620c794b3Sgavinm }
35820c794b3Sgavinm static int
mc_nb_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * nvl)359074bb90dSTom Pothier mc_nb_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
360074bb90dSTom Pothier     const char *name, nvlist_t *auth, nvlist_t *nvl)
36120c794b3Sgavinm {
36220c794b3Sgavinm 	int err;
36320c794b3Sgavinm 	int i, j;
36420c794b3Sgavinm 	int channel;
365e3d60c9bSAdrian Frost 	uint8_t nmc;
366e3d60c9bSAdrian Frost 	uint8_t maxranks;
367491f61a1SYanmin Sun 	uint8_t maxdimms;
36820c794b3Sgavinm 	tnode_t *mcnode;
36920c794b3Sgavinm 	nvlist_t *fmri;
37020c794b3Sgavinm 	nvlist_t **channel_nvl;
371e3d60c9bSAdrian Frost 	nvpair_t *nvp;
372e3d60c9bSAdrian Frost 	char *pname;
37320c794b3Sgavinm 	uint_t nchannels;
37520c794b3Sgavinm 	if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl,
37620c794b3Sgavinm 	    &nchannels) != 0) {
37720c794b3Sgavinm 		whinge(mod, NULL,
37820c794b3Sgavinm 		    "mc_nb_create: failed to find channel information\n");
37920c794b3Sgavinm 		return (-1);
38020c794b3Sgavinm 	}
38185738508SVuong Nguyen 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NMEM, &nmc) == 0) {
38285738508SVuong Nguyen 		/*
38385738508SVuong Nguyen 		 * Assume channels are evenly divided among the controllers.
38485738508SVuong Nguyen 		 * Convert nchannels to channels per controller
38585738508SVuong Nguyen 		 */
38685738508SVuong Nguyen 		nchannels = nchannels / nmc;
38785738508SVuong Nguyen 	} else {
388e3d60c9bSAdrian Frost 		/*
389e3d60c9bSAdrian Frost 		 * if number of memory controllers is not specified then there
390e3d60c9bSAdrian Frost 		 * are two channels per controller and the nchannels is total
391e3d60c9bSAdrian Frost 		 * we will set up nmc as number of controllers and convert
392e3d60c9bSAdrian Frost 		 * nchannels to channels per controller
393e3d60c9bSAdrian Frost 		 */
394e3d60c9bSAdrian Frost 		nmc = nchannels / 2;
395e3d60c9bSAdrian Frost 		nchannels = nchannels / nmc;
396e3d60c9bSAdrian Frost 	}
397e3d60c9bSAdrian Frost 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NRANKS, &maxranks) != 0)
398e3d60c9bSAdrian Frost 		maxranks = 2;
399491f61a1SYanmin Sun 	if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_NDIMMS, &maxdimms) != 0)
400491f61a1SYanmin Sun 		maxdimms = 0;
40120c794b3Sgavinm 	if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) {
40220c794b3Sgavinm 		whinge(mod, NULL,
40320c794b3Sgavinm 		    "mc_nb_create: node range create failed\n");
40420c794b3Sgavinm 		return (-1);
40520c794b3Sgavinm 	}
40620c794b3Sgavinm 	channel = 0;
40720c794b3Sgavinm 	for (i = 0; i < nmc; i++) {
40820c794b3Sgavinm 		if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) {
40920c794b3Sgavinm 			whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n");