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