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 /*
23efd31e1dSTrang Do * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24eb00b1c8SRobert Mustacchi * Copyright 2019 Joyent, Inc.
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
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 =
8020c794b3Sgavinm { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
8120c794b3Sgavinm static const topo_pgroup_info_t dimm_pgroup =
8220c794b3Sgavinm { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
8320c794b3Sgavinm static const topo_pgroup_info_t rank_pgroup =
8420c794b3Sgavinm { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
8520c794b3Sgavinm static const topo_pgroup_info_t mc_pgroup =
8620c794b3Sgavinm { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
8720c794b3Sgavinm
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 };
9720c794b3Sgavinm
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;
10220c794b3Sgavinm
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 }
10920c794b3Sgavinm
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;
13120c794b3Sgavinm
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);
15920c794b3Sgavinm
16020c794b3Sgavinm if (topo_method_register(mod, rnode, rank_methods) < 0)
161ae6d4bc3SRobert Mustacchi whinge(mod, &err, "mc_add_ranks: "
16220c794b3Sgavinm "topo_method_register failed");
16320c794b3Sgavinm
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);
17020c794b3Sgavinm
171074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod))
172d3c57c1fSTom Pothier (void) topo_node_label_set(rnode, NULL, &err);
173074bb90dSTom Pothier
17420c794b3Sgavinm nvlist_free(fmri);
17520c794b3Sgavinm
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 }
18420c794b3Sgavinm
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;
20420c794b3Sgavinm
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 }
26620c794b3Sgavinm
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");
27120c794b3Sgavinm
27220c794b3Sgavinm (void) topo_pgroup_create(dnode, &dimm_pgroup, &err);
27320c794b3Sgavinm
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);
30320c794b3Sgavinm
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 }
31020c794b3Sgavinm
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;
32220c794b3Sgavinm
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 }
35720c794b3Sgavinm
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;
37420c794b3Sgavinm
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");
41020c794b3Sgavinm return (-1);
41120c794b3Sgavinm }
41220c794b3Sgavinm if ((mcnode = topo_node_bind(mod, pnode, name, i,
41320c794b3Sgavinm fmri)) == NULL) {
414e3d60c9bSAdrian Frost whinge(mod, NULL, "mc_nb_create: node bind failed"
41520c794b3Sgavinm " for memory-controller\n");
41620c794b3Sgavinm nvlist_free(fmri);
41720c794b3Sgavinm return (-1);
41820c794b3Sgavinm }
41920c794b3Sgavinm
42020c794b3Sgavinm (void) topo_node_fru_set(mcnode, NULL, 0, &err);
42120c794b3Sgavinm nvlist_free(fmri);
42220c794b3Sgavinm (void) topo_pgroup_create(mcnode, &mc_pgroup, &err);
42320c794b3Sgavinm
424074bb90dSTom Pothier if (FM_AWARE_SMBIOS(mod))
425074bb90dSTom Pothier (void) topo_node_label_set(mcnode, NULL, &err);
426074bb90dSTom Pothier
42720c794b3Sgavinm if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel,
428e3d60c9bSAdrian Frost channel + nchannels - 1) < 0) {
42920c794b3Sgavinm whinge(mod, NULL,
43020c794b3Sgavinm "mc_nb_create: channel node range create failed\n");
43120c794b3Sgavinm return (-1);
43220c794b3Sgavinm }
433e3d60c9bSAdrian Frost for (j = 0; j < nchannels; j++) {
434074bb90dSTom Pothier if (mc_add_channel(mod, chip_smbid, mcnode, channel,
435074bb90dSTom Pothier auth, channel_nvl[channel], maxdimms,
436074bb90dSTom Pothier maxranks) < 0) {
43720c794b3Sgavinm return (-1);
43820c794b3Sgavinm }
43920c794b3Sgavinm channel++;
44020c794b3Sgavinm }
441e3d60c9bSAdrian Frost for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
442e3d60c9bSAdrian Frost nvp = nvlist_next_nvpair(nvl, nvp)) {
443e3d60c9bSAdrian Frost pname = nvpair_name(nvp);
444e3d60c9bSAdrian Frost if (strcmp(pname, MCINTEL_NVLIST_MC) != 0 &&
445e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_NMEM) != 0 &&
446e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_NRANKS) != 0 &&
447491f61a1SYanmin Sun strcmp(pname, MCINTEL_NVLIST_NDIMMS) != 0 &&
448e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_VERSTR) != 0 &&
449e3d60c9bSAdrian Frost strcmp(pname, MCINTEL_NVLIST_MEM) != 0) {
450e3d60c9bSAdrian Frost (void) nvprop_add(mod, nvp, PGNAME(MCT),
451e3d60c9bSAdrian Frost mcnode);
452e3d60c9bSAdrian Frost }
453e3d60c9bSAdrian Frost }
45420c794b3Sgavinm }
45520c794b3Sgavinm
4565cc5d5ceSToomas Soome return (0);
45720c794b3Sgavinm }
45820c794b3Sgavinm
459ae6d4bc3SRobert Mustacchi static int
mc_rank_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * dimm_nvl,uint64_t rsize,uint32_t id)460ae6d4bc3SRobert Mustacchi mc_rank_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
461ae6d4bc3SRobert Mustacchi nvlist_t *dimm_nvl, uint64_t rsize, uint32_t id)
462ae6d4bc3SRobert Mustacchi {
463ae6d4bc3SRobert Mustacchi nvlist_t *fmri;
464ae6d4bc3SRobert Mustacchi tnode_t *rank;
465ae6d4bc3SRobert Mustacchi int err;
466ae6d4bc3SRobert Mustacchi boolean_t *disabled;
467ae6d4bc3SRobert Mustacchi uint_t ndisabled;
468ae6d4bc3SRobert Mustacchi const char *status;
469ae6d4bc3SRobert Mustacchi
470ae6d4bc3SRobert Mustacchi fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, RANK, id,
471ae6d4bc3SRobert Mustacchi NULL, auth, NULL, NULL, NULL);
472ae6d4bc3SRobert Mustacchi if (fmri == NULL) {
473ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_mod_hcfmri "
474ae6d4bc3SRobert Mustacchi "failed\n");
475ae6d4bc3SRobert Mustacchi return (-1);
476ae6d4bc3SRobert Mustacchi }
477ae6d4bc3SRobert Mustacchi
478ae6d4bc3SRobert Mustacchi if ((rank = topo_node_bind(mod, pnode, RANK, id, fmri)) == NULL) {
479ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: node bind failed for "
480ae6d4bc3SRobert Mustacchi "DIMM\n");
481ae6d4bc3SRobert Mustacchi nvlist_free(fmri);
482ae6d4bc3SRobert Mustacchi return (-1);
483ae6d4bc3SRobert Mustacchi }
484ae6d4bc3SRobert Mustacchi
485ae6d4bc3SRobert Mustacchi if (topo_method_register(mod, rank, rank_methods) < 0) {
486ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_method_register "
487ae6d4bc3SRobert Mustacchi "failed for rank_methods: %d", topo_mod_errno(mod));
488ae6d4bc3SRobert Mustacchi }
489ae6d4bc3SRobert Mustacchi
490ae6d4bc3SRobert Mustacchi if (!is_xpv() && topo_method_register(mod, rank,
491ae6d4bc3SRobert Mustacchi ntv_page_retire_methods) != 0) {
492ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: topo_method_register "
493ae6d4bc3SRobert Mustacchi "failed for page retire: %d", topo_mod_errno(mod));
494ae6d4bc3SRobert Mustacchi }
495ae6d4bc3SRobert Mustacchi
496ae6d4bc3SRobert Mustacchi if (topo_node_asru_set(rank, fmri, TOPO_ASRU_COMPUTE, &err) != 0) {
497ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set asru: %d",
498ae6d4bc3SRobert Mustacchi err);
499ae6d4bc3SRobert Mustacchi nvlist_free(fmri);
500ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
501ae6d4bc3SRobert Mustacchi }
502ae6d4bc3SRobert Mustacchi
503ae6d4bc3SRobert Mustacchi if (topo_node_fru_set(rank, NULL, 0, &err) != 0) {
504ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: fru set failed: "
505ae6d4bc3SRobert Mustacchi "%d\n", err);
506ae6d4bc3SRobert Mustacchi nvlist_free(fmri);
507ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
508ae6d4bc3SRobert Mustacchi }
509ae6d4bc3SRobert Mustacchi nvlist_free(fmri);
510ae6d4bc3SRobert Mustacchi
511ae6d4bc3SRobert Mustacchi if (topo_pgroup_create(rank, &rank_pgroup, &err) != 0) {
512ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to create "
513ae6d4bc3SRobert Mustacchi "property group: %d\n", err);
514ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
515ae6d4bc3SRobert Mustacchi }
516ae6d4bc3SRobert Mustacchi
517ae6d4bc3SRobert Mustacchi /*
518ae6d4bc3SRobert Mustacchi * The traditional northbridge driver broke down each rank into the
519ae6d4bc3SRobert Mustacchi * interleave targets that led to it. At this time, the imc driver (the
520ae6d4bc3SRobert Mustacchi * only v1 provider) does not supply that information and therefore we
521ae6d4bc3SRobert Mustacchi * cannot set that. Instead we just set basic properties on this, the
522ae6d4bc3SRobert Mustacchi * size of the rank and whether or not it is disabled.
523ae6d4bc3SRobert Mustacchi */
524ae6d4bc3SRobert Mustacchi if (rsize != 0 && topo_prop_set_uint64(rank, PGNAME(RANK), RANK_SIZE,
525ae6d4bc3SRobert Mustacchi TOPO_PROP_IMMUTABLE, rsize, &err) != 0) {
526ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set %s "
527ae6d4bc3SRobert Mustacchi "property: %d", RANK_SIZE, err);
528ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
529ae6d4bc3SRobert Mustacchi }
530ae6d4bc3SRobert Mustacchi
531ae6d4bc3SRobert Mustacchi if (nvlist_lookup_boolean_array(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RDIS,
532ae6d4bc3SRobert Mustacchi &disabled, &ndisabled) != 0) {
533ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: Couldn't find disabled "
534ae6d4bc3SRobert Mustacchi "ranks array");
535ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
536ae6d4bc3SRobert Mustacchi }
537ae6d4bc3SRobert Mustacchi
538ae6d4bc3SRobert Mustacchi if (id >= ndisabled) {
539ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: Found rank %u with id "
540ae6d4bc3SRobert Mustacchi "larger than supported by hardware", id);
541ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
542ae6d4bc3SRobert Mustacchi }
543ae6d4bc3SRobert Mustacchi
544ae6d4bc3SRobert Mustacchi status = disabled[id] ? "disabled" : "enabled";
545ae6d4bc3SRobert Mustacchi if (topo_prop_set_string(rank, PGNAME(RANK), RANK_STATUS,
546ae6d4bc3SRobert Mustacchi TOPO_PROP_IMMUTABLE, status, &err) != 0) {
547ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_rank_create_v1: failed to set %s "
548ae6d4bc3SRobert Mustacchi "property: %d", RANK_STATUS, err);
549ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
550ae6d4bc3SRobert Mustacchi }
551ae6d4bc3SRobert Mustacchi
552ae6d4bc3SRobert Mustacchi return (0);
553ae6d4bc3SRobert Mustacchi }
554ae6d4bc3SRobert Mustacchi
555eb00b1c8SRobert Mustacchi static int
mc_dimm_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * dimm_nvl,uint_t id)556eb00b1c8SRobert Mustacchi mc_dimm_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
557eb00b1c8SRobert Mustacchi nvlist_t *dimm_nvl, uint_t id)
558eb00b1c8SRobert Mustacchi {
559eb00b1c8SRobert Mustacchi int err, ret;
560eb00b1c8SRobert Mustacchi tnode_t *dimm;
561eb00b1c8SRobert Mustacchi nvlist_t *fmri;
562eb00b1c8SRobert Mustacchi boolean_t present;
563ae6d4bc3SRobert Mustacchi uint64_t size, density, rsize;
564ae6d4bc3SRobert Mustacchi uint32_t cols, rows, width, ranks, banks, i;
565eb00b1c8SRobert Mustacchi
566eb00b1c8SRobert Mustacchi /*
567eb00b1c8SRobert Mustacchi * First, figure out if this DIMM is present. If not, we don't bother
568eb00b1c8SRobert Mustacchi * creating anything.
569eb00b1c8SRobert Mustacchi */
570eb00b1c8SRobert Mustacchi if (nvlist_lookup_boolean_value(dimm_nvl,
571eb00b1c8SRobert Mustacchi MCINTEL_NVLIST_V1_DIMM_PRESENT, &present) != 0) {
572eb00b1c8SRobert Mustacchi return (-1);
573eb00b1c8SRobert Mustacchi } else if (!present) {
574eb00b1c8SRobert Mustacchi return (0);
575eb00b1c8SRobert Mustacchi }
576eb00b1c8SRobert Mustacchi
577eb00b1c8SRobert Mustacchi fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, DIMM, id,
578eb00b1c8SRobert Mustacchi NULL, auth, NULL, NULL, NULL);
579eb00b1c8SRobert Mustacchi if (fmri == NULL) {
580eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: topo_mod_hcfmri "
581eb00b1c8SRobert Mustacchi "failed\n");
582eb00b1c8SRobert Mustacchi return (-1);
583eb00b1c8SRobert Mustacchi }
584eb00b1c8SRobert Mustacchi
585eb00b1c8SRobert Mustacchi if ((dimm = topo_node_bind(mod, pnode, DIMM, id, fmri)) == NULL) {
586eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: node bind failed for "
587eb00b1c8SRobert Mustacchi "DIMM\n");
588eb00b1c8SRobert Mustacchi nvlist_free(fmri);
589eb00b1c8SRobert Mustacchi return (-1);
590eb00b1c8SRobert Mustacchi }
591eb00b1c8SRobert Mustacchi
592eb00b1c8SRobert Mustacchi if (topo_node_fru_set(dimm, NULL, 0, &err) != 0) {
593eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: fru set failed: "
594eb00b1c8SRobert Mustacchi "%d\n", err);
595eb00b1c8SRobert Mustacchi nvlist_free(fmri);
596eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
597eb00b1c8SRobert Mustacchi }
598eb00b1c8SRobert Mustacchi nvlist_free(fmri);
599eb00b1c8SRobert Mustacchi
600eb00b1c8SRobert Mustacchi if (topo_pgroup_create(dimm, &dimm_pgroup, &err) != 0) {
601eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: failed to create "
602eb00b1c8SRobert Mustacchi "property group: %d\n", err);
603eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
604eb00b1c8SRobert Mustacchi }
605eb00b1c8SRobert Mustacchi
606eb00b1c8SRobert Mustacchi ret = 0;
607eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_SIZE,
608eb00b1c8SRobert Mustacchi &size) == 0) {
609eb00b1c8SRobert Mustacchi char buf[64];
610eb00b1c8SRobert Mustacchi const char *suffix;
611ae6d4bc3SRobert Mustacchi uint64_t tsize;
612eb00b1c8SRobert Mustacchi ret |= topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_SIZE,
613eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, size, &err);
614ae6d4bc3SRobert Mustacchi tsize = size;
615eb00b1c8SRobert Mustacchi
616eb00b1c8SRobert Mustacchi /*
617eb00b1c8SRobert Mustacchi * We must manually cons up a dimm-size property which is the
618eb00b1c8SRobert Mustacchi * size of the dimm as a round number with a prefix such as M or
619eb00b1c8SRobert Mustacchi * G. This is used by Intel CPU eversholt rules. Older memory
620eb00b1c8SRobert Mustacchi * controller drivers did this in the driver, but we instead opt
621eb00b1c8SRobert Mustacchi * to do so in user land.
622eb00b1c8SRobert Mustacchi */
623ae6d4bc3SRobert Mustacchi if (tsize >= (1ULL << 40)) {
624ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 40);
625eb00b1c8SRobert Mustacchi suffix = "T";
626ae6d4bc3SRobert Mustacchi } else if (tsize >= (1ULL << 30)) {
627ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 30);
628eb00b1c8SRobert Mustacchi suffix = "G";
629ae6d4bc3SRobert Mustacchi } else if (tsize >= (1ULL << 20)) {
630ae6d4bc3SRobert Mustacchi tsize /= (1ULL << 20);
631eb00b1c8SRobert Mustacchi suffix = "M";
632eb00b1c8SRobert Mustacchi } else {
633eb00b1c8SRobert Mustacchi suffix = NULL;
634eb00b1c8SRobert Mustacchi }
635eb00b1c8SRobert Mustacchi
636eb00b1c8SRobert Mustacchi if (suffix != NULL) {
637ae6d4bc3SRobert Mustacchi if (snprintf(buf, sizeof (buf), "%"PRIu64"%s", tsize,
638eb00b1c8SRobert Mustacchi suffix) >= sizeof (buf)) {
639eb00b1c8SRobert Mustacchi whinge(mod, NULL, "failed to construct DIMM "
640eb00b1c8SRobert Mustacchi "size due to buffer overflow");
641eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
642eb00b1c8SRobert Mustacchi }
643ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(dimm, PGNAME(DIMM),
644eb00b1c8SRobert Mustacchi DIMM_STRING_SIZE, TOPO_PROP_IMMUTABLE, buf, &err);
645ae6d4bc3SRobert Mustacchi if (ret != 0) {
646ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
647ae6d4bc3SRobert Mustacchi }
648eb00b1c8SRobert Mustacchi }
649ae6d4bc3SRobert Mustacchi } else {
650ae6d4bc3SRobert Mustacchi size = 0;
651eb00b1c8SRobert Mustacchi }
652eb00b1c8SRobert Mustacchi
653eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NCOLS,
654eb00b1c8SRobert Mustacchi &cols) == 0) {
655ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_COL,
656eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, cols, &err);
657ae6d4bc3SRobert Mustacchi if (ret != 0) {
658ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
659ae6d4bc3SRobert Mustacchi }
660eb00b1c8SRobert Mustacchi }
661eb00b1c8SRobert Mustacchi
662eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_NROWS,
663eb00b1c8SRobert Mustacchi &rows) == 0) {
664ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_ROW,
665eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, rows, &err);
666ae6d4bc3SRobert Mustacchi if (ret != 0) {
667ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
668ae6d4bc3SRobert Mustacchi }
669eb00b1c8SRobert Mustacchi }
670eb00b1c8SRobert Mustacchi
671eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint64(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_DENSITY,
672eb00b1c8SRobert Mustacchi &density) == 0) {
673ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint64(dimm, PGNAME(DIMM), DIMM_DENSITY,
674eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, density, &err);
675ae6d4bc3SRobert Mustacchi if (ret != 0) {
676ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
677ae6d4bc3SRobert Mustacchi }
678eb00b1c8SRobert Mustacchi }
679eb00b1c8SRobert Mustacchi
680eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_WIDTH,
681eb00b1c8SRobert Mustacchi &width) == 0) {
682ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_WIDTH,
683eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, width, &err);
684ae6d4bc3SRobert Mustacchi if (ret != 0) {
685ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
686ae6d4bc3SRobert Mustacchi }
687eb00b1c8SRobert Mustacchi }
688eb00b1c8SRobert Mustacchi
689eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_BANKS,
690eb00b1c8SRobert Mustacchi &banks) == 0) {
691ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_BANKS,
692eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, banks, &err);
693ae6d4bc3SRobert Mustacchi if (ret != 0) {
694ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
695ae6d4bc3SRobert Mustacchi }
696ae6d4bc3SRobert Mustacchi } else {
697ae6d4bc3SRobert Mustacchi banks = 0;
698eb00b1c8SRobert Mustacchi }
699eb00b1c8SRobert Mustacchi
700eb00b1c8SRobert Mustacchi if (nvlist_lookup_uint32(dimm_nvl, MCINTEL_NVLIST_V1_DIMM_RANKS,
701eb00b1c8SRobert Mustacchi &ranks) == 0) {
702ae6d4bc3SRobert Mustacchi ret = topo_prop_set_uint32(dimm, PGNAME(DIMM), DIMM_RANKS,
703eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, ranks, &err);
704ae6d4bc3SRobert Mustacchi if (ret != 0) {
705ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
706ae6d4bc3SRobert Mustacchi }
707ae6d4bc3SRobert Mustacchi }
708ae6d4bc3SRobert Mustacchi
709ae6d4bc3SRobert Mustacchi if (ret != 0) {
710ae6d4bc3SRobert Mustacchi return (-1);
711ae6d4bc3SRobert Mustacchi }
712ae6d4bc3SRobert Mustacchi
713ae6d4bc3SRobert Mustacchi if (topo_node_range_create(mod, dimm, RANK, 0, ranks - 1) < 0) {
714ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_dimm_create_v1: rank node range "
715ae6d4bc3SRobert Mustacchi "create failed\n");
716ae6d4bc3SRobert Mustacchi return (-1);
717ae6d4bc3SRobert Mustacchi }
718ae6d4bc3SRobert Mustacchi
719ae6d4bc3SRobert Mustacchi rsize = 0;
720ae6d4bc3SRobert Mustacchi if (size != 0 && banks != 0) {
721ae6d4bc3SRobert Mustacchi rsize = size / banks;
722eb00b1c8SRobert Mustacchi }
723eb00b1c8SRobert Mustacchi
724ae6d4bc3SRobert Mustacchi for (i = 0; i < ranks; i++) {
725ae6d4bc3SRobert Mustacchi if (mc_rank_create_v1(mod, dimm, auth, dimm_nvl, rsize, i) !=
726ae6d4bc3SRobert Mustacchi 0) {
727ae6d4bc3SRobert Mustacchi return (-1);
728ae6d4bc3SRobert Mustacchi }
729ae6d4bc3SRobert Mustacchi }
730ae6d4bc3SRobert Mustacchi
731ae6d4bc3SRobert Mustacchi return (0);
732eb00b1c8SRobert Mustacchi }
733eb00b1c8SRobert Mustacchi
734eb00b1c8SRobert Mustacchi static int
mc_channel_create_v1(topo_mod_t * mod,tnode_t * pnode,nvlist_t * auth,nvlist_t * chan_nvl,uint_t id,const char * cmode)735eb00b1c8SRobert Mustacchi mc_channel_create_v1(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth,
736eb00b1c8SRobert Mustacchi nvlist_t *chan_nvl, uint_t id, const char *cmode)
737eb00b1c8SRobert Mustacchi {
738eb00b1c8SRobert Mustacchi int err;
739eb00b1c8SRobert Mustacchi tnode_t *chnode;
740eb00b1c8SRobert Mustacchi nvlist_t *fmri;
741eb00b1c8SRobert Mustacchi nvlist_t **dimms;
742eb00b1c8SRobert Mustacchi uint_t ndimms, i;
743eb00b1c8SRobert Mustacchi
744eb00b1c8SRobert Mustacchi if (mkrsrc(mod, pnode, DRAMCHANNEL, id, auth, &fmri) != 0) {
745eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: mkrsrc failed\n");
746eb00b1c8SRobert Mustacchi return (-1);
747eb00b1c8SRobert Mustacchi }
748eb00b1c8SRobert Mustacchi
749eb00b1c8SRobert Mustacchi if ((chnode = topo_node_bind(mod, pnode, DRAMCHANNEL, id, fmri)) ==
750eb00b1c8SRobert Mustacchi NULL) {
751eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: node bind failed"
752eb00b1c8SRobert Mustacchi " for dram-channel\n");
753eb00b1c8SRobert Mustacchi nvlist_free(fmri);
754eb00b1c8SRobert Mustacchi return (-1);
755eb00b1c8SRobert Mustacchi }
756eb00b1c8SRobert Mustacchi
757eb00b1c8SRobert Mustacchi nvlist_free(fmri);
758eb00b1c8SRobert Mustacchi if (topo_node_fru_set(chnode, NULL, 0, &err) != 0) {
759eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: fru set failed: "
760eb00b1c8SRobert Mustacchi "%d\n", err);
761eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
762eb00b1c8SRobert Mustacchi }
763eb00b1c8SRobert Mustacchi
764eb00b1c8SRobert Mustacchi if (topo_pgroup_create(chnode, &dimm_channel_pgroup, &err) != 0) {
765eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: failed to create "
766eb00b1c8SRobert Mustacchi "property group: %d\n", err);
767eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
768eb00b1c8SRobert Mustacchi }
769eb00b1c8SRobert Mustacchi
770eb00b1c8SRobert Mustacchi if (topo_prop_set_string(chnode, PGNAME(CHAN), CHAN_PROP_MODE,
771eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, cmode, &err) != 0) {
772eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
773eb00b1c8SRobert Mustacchi }
774eb00b1c8SRobert Mustacchi
775eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(chan_nvl, MCINTEL_NVLIST_V1_CHAN_DIMMS,
776eb00b1c8SRobert Mustacchi &dimms, &ndimms) != 0) {
777eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: No DIMMS provided");
778eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
779eb00b1c8SRobert Mustacchi }
780eb00b1c8SRobert Mustacchi
781eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, chnode, DIMM, 0, ndimms - 1) < 0) {
782eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_channel_create_v1: dimm node range "
783eb00b1c8SRobert Mustacchi "create failed\n");
784eb00b1c8SRobert Mustacchi return (-1);
785eb00b1c8SRobert Mustacchi }
786eb00b1c8SRobert Mustacchi
787eb00b1c8SRobert Mustacchi for (i = 0; i < ndimms; i++) {
788eb00b1c8SRobert Mustacchi if (mc_dimm_create_v1(mod, chnode, auth, dimms[i], i) != 0)
789eb00b1c8SRobert Mustacchi return (-1);
790eb00b1c8SRobert Mustacchi }
791eb00b1c8SRobert Mustacchi
792eb00b1c8SRobert Mustacchi return (0);
793eb00b1c8SRobert Mustacchi }
794eb00b1c8SRobert Mustacchi
795eb00b1c8SRobert Mustacchi static int
mc_imc_create_v1(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * mc_nvl,uint_t id)796eb00b1c8SRobert Mustacchi mc_imc_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
797eb00b1c8SRobert Mustacchi nvlist_t *auth, nvlist_t *mc_nvl, uint_t id)
798eb00b1c8SRobert Mustacchi {
799eb00b1c8SRobert Mustacchi int err, ret;
800eb00b1c8SRobert Mustacchi tnode_t *mcnode;
801eb00b1c8SRobert Mustacchi nvlist_t *fmri, **channels;
802eb00b1c8SRobert Mustacchi boolean_t ecc;
803eb00b1c8SRobert Mustacchi char *page, *cmode;
804eb00b1c8SRobert Mustacchi uint_t nchans, i;
805eb00b1c8SRobert Mustacchi
806eb00b1c8SRobert Mustacchi if (mkrsrc(mod, pnode, name, id, auth, &fmri) != 0) {
807ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: mkrsrc failed\n");
808eb00b1c8SRobert Mustacchi return (-1);
809eb00b1c8SRobert Mustacchi }
810eb00b1c8SRobert Mustacchi
811eb00b1c8SRobert Mustacchi if ((mcnode = topo_node_bind(mod, pnode, name, id, fmri)) == NULL) {
812ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: node bind failed"
813eb00b1c8SRobert Mustacchi " for memory-controller\n");
814eb00b1c8SRobert Mustacchi nvlist_free(fmri);
815eb00b1c8SRobert Mustacchi return (-1);
816eb00b1c8SRobert Mustacchi }
817eb00b1c8SRobert Mustacchi
818eb00b1c8SRobert Mustacchi nvlist_free(fmri);
819eb00b1c8SRobert Mustacchi if (topo_node_fru_set(mcnode, NULL, 0, &err) != 0) {
820ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: fru set failed: "
821eb00b1c8SRobert Mustacchi "%d\n", err);
822eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
823eb00b1c8SRobert Mustacchi }
824eb00b1c8SRobert Mustacchi
825eb00b1c8SRobert Mustacchi if (topo_pgroup_create(mcnode, &mc_pgroup, &err) != 0) {
826ae6d4bc3SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: failed to create "
827eb00b1c8SRobert Mustacchi "property group: %d\n", err);
828eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, err));
829eb00b1c8SRobert Mustacchi }
830eb00b1c8SRobert Mustacchi
831eb00b1c8SRobert Mustacchi /*
832eb00b1c8SRobert Mustacchi * Add properties to the controller. Our contract allows for these
833eb00b1c8SRobert Mustacchi * properties to be missing.
834eb00b1c8SRobert Mustacchi */
835eb00b1c8SRobert Mustacchi ret = 0;
836eb00b1c8SRobert Mustacchi if (nvlist_lookup_boolean_value(mc_nvl, MCINTEL_NVLIST_V1_MC_ECC,
837eb00b1c8SRobert Mustacchi &ecc) == 0) {
838eb00b1c8SRobert Mustacchi const char *pval = ecc ? "enabled" : "disabled";
839ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_ECC,
840eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, pval, &err);
841ae6d4bc3SRobert Mustacchi if (ret != 0) {
842ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
843ae6d4bc3SRobert Mustacchi }
844eb00b1c8SRobert Mustacchi }
845eb00b1c8SRobert Mustacchi
846eb00b1c8SRobert Mustacchi if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_POLICY,
847eb00b1c8SRobert Mustacchi &page) == 0) {
848ae6d4bc3SRobert Mustacchi ret = topo_prop_set_string(mcnode, PGNAME(MCT), MC_PROP_POLICY,
849eb00b1c8SRobert Mustacchi TOPO_PROP_IMMUTABLE, page, &err);
850ae6d4bc3SRobert Mustacchi if (ret != 0) {
851ae6d4bc3SRobert Mustacchi return (topo_mod_seterrno(mod, err));
852ae6d4bc3SRobert Mustacchi }
853eb00b1c8SRobert Mustacchi }
854eb00b1c8SRobert Mustacchi
855eb00b1c8SRobert Mustacchi if (nvlist_lookup_string(mc_nvl, MCINTEL_NVLIST_V1_MC_CHAN_MODE,
856eb00b1c8SRobert Mustacchi &cmode) != 0) {
857eb00b1c8SRobert Mustacchi cmode = NULL;
858eb00b1c8SRobert Mustacchi }
859eb00b1c8SRobert Mustacchi
860eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(mc_nvl, MCINTEL_NVLIST_V1_MC_CHANNELS,
861eb00b1c8SRobert Mustacchi &channels, &nchans) != 0) {
862eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: missing channels entry");
863eb00b1c8SRobert Mustacchi return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
864eb00b1c8SRobert Mustacchi }
865eb00b1c8SRobert Mustacchi
866eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, 0,
867eb00b1c8SRobert Mustacchi nchans - 1) < 0) {
868eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_imc_create_v1: channel node range create "
869eb00b1c8SRobert Mustacchi "failed\n");
870eb00b1c8SRobert Mustacchi return (-1);
871eb00b1c8SRobert Mustacchi }
872eb00b1c8SRobert Mustacchi
873eb00b1c8SRobert Mustacchi for (i = 0; i < nchans; i++) {
874eb00b1c8SRobert Mustacchi if (mc_channel_create_v1(mod, mcnode, auth, channels[i], i,
875eb00b1c8SRobert Mustacchi cmode) != 0) {
876eb00b1c8SRobert Mustacchi return (-1);
877eb00b1c8SRobert Mustacchi }
878eb00b1c8SRobert Mustacchi }
879eb00b1c8SRobert Mustacchi
880eb00b1c8SRobert Mustacchi return (0);
881eb00b1c8SRobert Mustacchi }
882eb00b1c8SRobert Mustacchi
883eb00b1c8SRobert Mustacchi static int
mc_nb_create_v1(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth,nvlist_t * nvl)884eb00b1c8SRobert Mustacchi mc_nb_create_v1(topo_mod_t *mod, tnode_t *pnode, const char *name,
885eb00b1c8SRobert Mustacchi nvlist_t *auth, nvlist_t *nvl)
886eb00b1c8SRobert Mustacchi {
887eb00b1c8SRobert Mustacchi nvlist_t **mc_nvl;
888eb00b1c8SRobert Mustacchi uint_t nmc, i;
889eb00b1c8SRobert Mustacchi
890eb00b1c8SRobert Mustacchi if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_V1_MCS,
891eb00b1c8SRobert Mustacchi &mc_nvl, &nmc) != 0) {
892eb00b1c8SRobert Mustacchi whinge(mod, NULL, "mc_nb_create_v1: failed to find memory "
893eb00b1c8SRobert Mustacchi "controller information\n");
894eb00b1c8SRobert Mustacchi return (-1);
895eb00b1c8SRobert Mustacchi }
896eb00b1c8SRobert Mustacchi
897eb00b1c8SRobert Mustacchi if (topo_node_range_create(mod, pnode, name, 0, nmc - 1) < 0) {
898eb00b1c8SRobert Mustacchi whinge(mod, NULL,
899ae6d4bc3SRobert Mustacchi "mc_nb_create_v1: node range create failed\n");
900eb00b1c8SRobert Mustacchi return (-1);
901eb00b1c8SRobert Mustacchi }
902eb00b1c8SRobert Mustacchi
903eb00b1c8SRobert Mustacchi for (i = 0; i < nmc; i++) {
904eb00b1c8SRobert Mustacchi if (mc_imc_create_v1(mod, pnode, name, auth, mc_nvl[i], i) != 0)
905eb00b1c8SRobert Mustacchi return (-1);
906eb00b1c8SRobert Mustacchi }
907eb00b1c8SRobert Mustacchi
908eb00b1c8SRobert Mustacchi return (0);
909eb00b1c8SRobert Mustacchi }
910eb00b1c8SRobert Mustacchi
91120c794b3Sgavinm int
mc_node_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)912074bb90dSTom Pothier mc_node_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
913074bb90dSTom Pothier const char *name, nvlist_t *auth)
91420c794b3Sgavinm {
91520c794b3Sgavinm mc_snapshot_info_t mcs;
91620c794b3Sgavinm void *buf = NULL;
91720c794b3Sgavinm nvlist_t *nvl;
91820c794b3Sgavinm uint8_t ver;
91920c794b3Sgavinm int rc;
92020c794b3Sgavinm
92120c794b3Sgavinm if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 ||
92220c794b3Sgavinm (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL ||
92320c794b3Sgavinm ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) {
92420c794b3Sgavinm
92520c794b3Sgavinm whinge(mod, NULL, "mc failed to snapshot %s\n",
92620c794b3Sgavinm strerror(errno));
92720c794b3Sgavinm
92820c794b3Sgavinm free(buf);
92920c794b3Sgavinm (void) close(mc_fd);
9305cc5d5ceSToomas Soome return (0);
93120c794b3Sgavinm }
93220c794b3Sgavinm (void) close(mc_fd);
93320c794b3Sgavinm (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0);
93420c794b3Sgavinm topo_mod_free(mod, buf, mcs.mcs_size);
93520c794b3Sgavinm
93620c794b3Sgavinm if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) {
93720c794b3Sgavinm whinge(mod, NULL, "mc nvlist is not versioned\n");
93820c794b3Sgavinm nvlist_free(nvl);
9395cc5d5ceSToomas Soome return (0);
940eb00b1c8SRobert Mustacchi } else if (ver != MCINTEL_NVLIST_VERS0 &&
941eb00b1c8SRobert Mustacchi ver != MCINTEL_NVLIST_VERS1) {
94220c794b3Sgavinm whinge(mod, NULL, "mc nvlist version mismatch\n");
94320c794b3Sgavinm nvlist_free(nvl);
9445cc5d5ceSToomas Soome return (0);
94520c794b3Sgavinm }
94620c794b3Sgavinm
947eb00b1c8SRobert Mustacchi if (ver == MCINTEL_NVLIST_VERS1) {
948eb00b1c8SRobert Mustacchi rc = mc_nb_create_v1(mod, pnode, name, auth, nvl);
949eb00b1c8SRobert Mustacchi } else {
950eb00b1c8SRobert Mustacchi rc = mc_nb_create(mod, chip_smbid, pnode, name, auth, nvl);
951eb00b1c8SRobert Mustacchi }
95220c794b3Sgavinm
95320c794b3Sgavinm nvlist_free(nvl);
95420c794b3Sgavinm return (rc);
95520c794b3Sgavinm }
956e3d60c9bSAdrian Frost
957e3d60c9bSAdrian Frost void
onchip_mc_create(topo_mod_t * mod,uint16_t chip_smbid,tnode_t * pnode,const char * name,nvlist_t * auth)958074bb90dSTom Pothier onchip_mc_create(topo_mod_t *mod, uint16_t chip_smbid, tnode_t *pnode,
959074bb90dSTom Pothier const char *name, nvlist_t *auth)
960e3d60c9bSAdrian Frost {
961e3d60c9bSAdrian Frost if (mc_onchip(topo_node_instance(pnode)))
962074bb90dSTom Pothier (void) mc_node_create(mod, chip_smbid, pnode, name, auth);
963e3d60c9bSAdrian Frost }
964e3d60c9bSAdrian Frost
965e3d60c9bSAdrian Frost int
mc_offchip_create(topo_mod_t * mod,tnode_t * pnode,const char * name,nvlist_t * auth)966e3d60c9bSAdrian Frost mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
967e3d60c9bSAdrian Frost nvlist_t *auth)
968e3d60c9bSAdrian Frost {
969074bb90dSTom Pothier return (mc_node_create(mod, IGNORE_ID, pnode, name, auth));
970e3d60c9bSAdrian Frost }
971