1e4b86885SCheng Sean Ye /* 2e4b86885SCheng Sean Ye * CDDL HEADER START 3e4b86885SCheng Sean Ye * 4e4b86885SCheng Sean Ye * The contents of this file are subject to the terms of the 5e4b86885SCheng Sean Ye * Common Development and Distribution License (the "License"). 6e4b86885SCheng Sean Ye * You may not use this file except in compliance with the License. 7e4b86885SCheng Sean Ye * 8e4b86885SCheng Sean Ye * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9e4b86885SCheng Sean Ye * or http://www.opensolaris.org/os/licensing. 10e4b86885SCheng Sean Ye * See the License for the specific language governing permissions 11e4b86885SCheng Sean Ye * and limitations under the License. 12e4b86885SCheng Sean Ye * 13e4b86885SCheng Sean Ye * When distributing Covered Code, include this CDDL HEADER in each 14e4b86885SCheng Sean Ye * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15e4b86885SCheng Sean Ye * If applicable, add the following below this CDDL HEADER, with the 16e4b86885SCheng Sean Ye * fields enclosed by brackets "[]" replaced with your own identifying 17e4b86885SCheng Sean Ye * information: Portions Copyright [yyyy] [name of copyright owner] 18e4b86885SCheng Sean Ye * 19e4b86885SCheng Sean Ye * CDDL HEADER END 20e4b86885SCheng Sean Ye */ 21e4b86885SCheng Sean Ye 22e4b86885SCheng Sean Ye /* 23f32a9dd1SSrihari Venkatesan * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24e4b86885SCheng Sean Ye */ 25e4b86885SCheng Sean Ye 26e4b86885SCheng Sean Ye #include <sys/conf.h> 27e4b86885SCheng Sean Ye #include <sys/ddi.h> 28e4b86885SCheng Sean Ye #include <sys/ddifm.h> 29e4b86885SCheng Sean Ye #include <sys/sunddi.h> 30e4b86885SCheng Sean Ye #include <sys/sunndi.h> 31e4b86885SCheng Sean Ye #include <sys/stat.h> 32e4b86885SCheng Sean Ye #include <sys/modctl.h> 33e4b86885SCheng Sean Ye #include <sys/types.h> 34e4b86885SCheng Sean Ye #include <sys/cpuvar.h> 35e4b86885SCheng Sean Ye #include <sys/cmn_err.h> 36e4b86885SCheng Sean Ye #include <sys/kmem.h> 37e4b86885SCheng Sean Ye #include <sys/cred.h> 38e4b86885SCheng Sean Ye #include <sys/ksynch.h> 39e4b86885SCheng Sean Ye #include <sys/rwlock.h> 40e4b86885SCheng Sean Ye #include <sys/pghw.h> 41e4b86885SCheng Sean Ye #include <sys/open.h> 42e4b86885SCheng Sean Ye #include <sys/policy.h> 43e4b86885SCheng Sean Ye #include <sys/x86_archext.h> 44e4b86885SCheng Sean Ye #include <sys/cpu_module.h> 45e4b86885SCheng Sean Ye #include <qsort.h> 46e4b86885SCheng Sean Ye #include <sys/pci_cfgspace.h> 47e4b86885SCheng Sean Ye #include <sys/mc.h> 48e4b86885SCheng Sean Ye #include <sys/mc_amd.h> 49074bb90dSTom Pothier #include <sys/smbios.h> 50074bb90dSTom Pothier #include <sys/pci.h> 51e4b86885SCheng Sean Ye #include <mcamd.h> 52e4b86885SCheng Sean Ye #include <mcamd_dimmcfg.h> 53e4b86885SCheng Sean Ye #include <mcamd_pcicfg.h> 54e4b86885SCheng Sean Ye #include <mcamd_api.h> 55e4b86885SCheng Sean Ye #include <sys/fm/cpu/AMD.h> 56074bb90dSTom Pothier #include <sys/fm/smb/fmsmb.h> 57074bb90dSTom Pothier #include <sys/fm/protocol.h> 58074bb90dSTom Pothier #include <sys/fm/util.h> 59e4b86885SCheng Sean Ye 60e4b86885SCheng Sean Ye /* 61e4b86885SCheng Sean Ye * Set to prevent mc-amd from attaching. 62e4b86885SCheng Sean Ye */ 63e4b86885SCheng Sean Ye int mc_no_attach = 0; 64e4b86885SCheng Sean Ye 65e4b86885SCheng Sean Ye /* 66e4b86885SCheng Sean Ye * Of the 754/939/940 packages, only socket 940 supports quadrank registered 67e4b86885SCheng Sean Ye * dimms. Unfortunately, no memory-controller register indicates the 68e4b86885SCheng Sean Ye * presence of quadrank dimm support or presence (i.e., in terms of number 69e4b86885SCheng Sean Ye * of slots per cpu, and chip-select lines per slot, The following may be set 70e4b86885SCheng Sean Ye * in /etc/system to indicate the presence of quadrank support on a motherboard. 71e4b86885SCheng Sean Ye * 72e4b86885SCheng Sean Ye * There is no need to set this for F(1207) and S1g1. 73e4b86885SCheng Sean Ye */ 74e4b86885SCheng Sean Ye int mc_quadranksupport = 0; 75e4b86885SCheng Sean Ye 76e4b86885SCheng Sean Ye mc_t *mc_list, *mc_last; 77e4b86885SCheng Sean Ye krwlock_t mc_lock; 78e4b86885SCheng Sean Ye int mc_hold_attached = 1; 79e4b86885SCheng Sean Ye 80e4b86885SCheng Sean Ye #define MAX(m, n) ((m) >= (n) ? (m) : (n)) 81e4b86885SCheng Sean Ye #define MIN(m, n) ((m) <= (n) ? (m) : (n)) 82e4b86885SCheng Sean Ye 83e4b86885SCheng Sean Ye /* 84e4b86885SCheng Sean Ye * The following tuneable is used to determine the DRAM scrubbing rate. 85e4b86885SCheng Sean Ye * The values range from 0x00-0x16 as described in the BKDG. Zero 86e4b86885SCheng Sean Ye * disables DRAM scrubbing. Values above zero indicate rates in descending 87e4b86885SCheng Sean Ye * order. 88e4b86885SCheng Sean Ye * 89e4b86885SCheng Sean Ye * The default value below is used on several Sun systems. In the future 90e4b86885SCheng Sean Ye * this code should assign values dynamically based on memory sizing. 91e4b86885SCheng Sean Ye */ 92e4b86885SCheng Sean Ye uint32_t mc_scrub_rate_dram = 0xd; /* 64B every 163.8 us; 1GB per 45 min */ 93e4b86885SCheng Sean Ye 94e4b86885SCheng Sean Ye enum { 95e4b86885SCheng Sean Ye MC_SCRUB_BIOSDEFAULT, /* retain system default value */ 96e4b86885SCheng Sean Ye MC_SCRUB_FIXED, /* assign mc_scrub_rate_* values */ 97e4b86885SCheng Sean Ye MC_SCRUB_MAX /* assign max of system and tunables */ 98e4b86885SCheng Sean Ye } mc_scrub_policy = MC_SCRUB_MAX; 99e4b86885SCheng Sean Ye 100e4b86885SCheng Sean Ye static void 101e4b86885SCheng Sean Ye mc_snapshot_destroy(mc_t *mc) 102e4b86885SCheng Sean Ye { 103e4b86885SCheng Sean Ye ASSERT(RW_LOCK_HELD(&mc_lock)); 104e4b86885SCheng Sean Ye 105e4b86885SCheng Sean Ye if (mc->mc_snapshot == NULL) 106e4b86885SCheng Sean Ye return; 107e4b86885SCheng Sean Ye 108e4b86885SCheng Sean Ye kmem_free(mc->mc_snapshot, mc->mc_snapshotsz); 109e4b86885SCheng Sean Ye mc->mc_snapshot = NULL; 110e4b86885SCheng Sean Ye mc->mc_snapshotsz = 0; 111e4b86885SCheng Sean Ye mc->mc_snapshotgen++; 112e4b86885SCheng Sean Ye } 113e4b86885SCheng Sean Ye 114e4b86885SCheng Sean Ye static int 115e4b86885SCheng Sean Ye mc_snapshot_update(mc_t *mc) 116e4b86885SCheng Sean Ye { 117e4b86885SCheng Sean Ye ASSERT(RW_LOCK_HELD(&mc_lock)); 118e4b86885SCheng Sean Ye 119e4b86885SCheng Sean Ye if (mc->mc_snapshot != NULL) 120e4b86885SCheng Sean Ye return (0); 121e4b86885SCheng Sean Ye 122e4b86885SCheng Sean Ye if (nvlist_pack(mc->mc_nvl, &mc->mc_snapshot, &mc->mc_snapshotsz, 123e4b86885SCheng Sean Ye NV_ENCODE_XDR, KM_SLEEP) != 0) 124e4b86885SCheng Sean Ye return (-1); 125e4b86885SCheng Sean Ye 126e4b86885SCheng Sean Ye return (0); 127e4b86885SCheng Sean Ye } 128e4b86885SCheng Sean Ye 129e4b86885SCheng Sean Ye static mc_t * 130e4b86885SCheng Sean Ye mc_lookup_by_chipid(int chipid) 131e4b86885SCheng Sean Ye { 132e4b86885SCheng Sean Ye mc_t *mc; 133e4b86885SCheng Sean Ye 134e4b86885SCheng Sean Ye ASSERT(RW_LOCK_HELD(&mc_lock)); 135e4b86885SCheng Sean Ye 136e4b86885SCheng Sean Ye for (mc = mc_list; mc != NULL; mc = mc->mc_next) { 137e4b86885SCheng Sean Ye if (mc->mc_props.mcp_num == chipid) 138e4b86885SCheng Sean Ye return (mc); 139e4b86885SCheng Sean Ye } 140e4b86885SCheng Sean Ye 141e4b86885SCheng Sean Ye return (NULL); 142e4b86885SCheng Sean Ye } 143e4b86885SCheng Sean Ye 144e4b86885SCheng Sean Ye /* 145e4b86885SCheng Sean Ye * Read config register pairs into the two arrays provided on the given 146e4b86885SCheng Sean Ye * handle and at offsets as follows: 147e4b86885SCheng Sean Ye * 148e4b86885SCheng Sean Ye * Index Array r1 offset Array r2 offset 149e4b86885SCheng Sean Ye * 0 r1addr r2addr 150e4b86885SCheng Sean Ye * 1 r1addr + incr r2addr + incr 151e4b86885SCheng Sean Ye * 2 r1addr + 2 * incr r2addr + 2 * incr 152e4b86885SCheng Sean Ye * ... 153e4b86885SCheng Sean Ye * n - 1 r1addr + (n - 1) * incr r2addr + (n - 1) * incr 154e4b86885SCheng Sean Ye * 155e4b86885SCheng Sean Ye * The number of registers to read into the r1 array is r1n; the number 156e4b86885SCheng Sean Ye * for the r2 array is r2n. 157e4b86885SCheng Sean Ye */ 158e4b86885SCheng Sean Ye static void 159e4b86885SCheng Sean Ye mc_prop_read_pair(mc_pcicfg_hdl_t cfghdl, uint32_t *r1, off_t r1addr, 160e4b86885SCheng Sean Ye int r1n, uint32_t *r2, off_t r2addr, int r2n, off_t incr) 161e4b86885SCheng Sean Ye { 162e4b86885SCheng Sean Ye int i; 163e4b86885SCheng Sean Ye 164e4b86885SCheng Sean Ye for (i = 0; i < MAX(r1n, r2n); i++, r1addr += incr, r2addr += incr) { 165e4b86885SCheng Sean Ye if (i < r1n) 166e4b86885SCheng Sean Ye r1[i] = mc_pcicfg_get32(cfghdl, r1addr); 167e4b86885SCheng Sean Ye if (i < r2n) 168e4b86885SCheng Sean Ye r2[i] = mc_pcicfg_get32(cfghdl, r2addr); 169e4b86885SCheng Sean Ye } 170e4b86885SCheng Sean Ye } 171e4b86885SCheng Sean Ye 17289e921d5SKuriakose Kuruvilla /*ARGSUSED*/ 17389e921d5SKuriakose Kuruvilla static int 17489e921d5SKuriakose Kuruvilla mc_nvl_add_socket_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) 17589e921d5SKuriakose Kuruvilla { 17689e921d5SKuriakose Kuruvilla uint32_t skt = *((uint32_t *)arg1); 17789e921d5SKuriakose Kuruvilla cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2; 17889e921d5SKuriakose Kuruvilla 17989e921d5SKuriakose Kuruvilla if (cmi_hdl_getsockettype(whdl) == skt) { 18089e921d5SKuriakose Kuruvilla cmi_hdl_hold(whdl); /* short-term hold */ 18189e921d5SKuriakose Kuruvilla *hdlp = whdl; 18289e921d5SKuriakose Kuruvilla return (CMI_HDL_WALK_DONE); 18389e921d5SKuriakose Kuruvilla } else { 18489e921d5SKuriakose Kuruvilla return (CMI_HDL_WALK_NEXT); 18589e921d5SKuriakose Kuruvilla } 18689e921d5SKuriakose Kuruvilla } 187e4b86885SCheng Sean Ye 188e4b86885SCheng Sean Ye static void 189e4b86885SCheng Sean Ye mc_nvl_add_socket(nvlist_t *nvl, mc_t *mc) 190e4b86885SCheng Sean Ye { 19189e921d5SKuriakose Kuruvilla cmi_hdl_t hdl = NULL; 19289e921d5SKuriakose Kuruvilla const char *s; 193e4b86885SCheng Sean Ye 19489e921d5SKuriakose Kuruvilla cmi_hdl_walk(mc_nvl_add_socket_cb, (void *)&mc->mc_socket, 19589e921d5SKuriakose Kuruvilla (void *)&hdl, NULL); 19689e921d5SKuriakose Kuruvilla if (hdl == NULL) 19789e921d5SKuriakose Kuruvilla s = "Unknown"; /* no cpu for this chipid found */ 19889e921d5SKuriakose Kuruvilla else 19989e921d5SKuriakose Kuruvilla s = cmi_hdl_getsocketstr(hdl); 200e4b86885SCheng Sean Ye 201e4b86885SCheng Sean Ye (void) nvlist_add_string(nvl, "socket", s); 20289e921d5SKuriakose Kuruvilla 20389e921d5SKuriakose Kuruvilla if (hdl != NULL) 20489e921d5SKuriakose Kuruvilla cmi_hdl_rele(hdl); 205e4b86885SCheng Sean Ye } 206e4b86885SCheng Sean Ye 207e4b86885SCheng Sean Ye static uint32_t 208e4b86885SCheng Sean Ye mc_ecc_enabled(mc_t *mc) 209e4b86885SCheng Sean Ye { 210e4b86885SCheng Sean Ye uint32_t rev = mc->mc_props.mcp_rev; 211e4b86885SCheng Sean Ye union mcreg_nbcfg nbcfg; 212e4b86885SCheng Sean Ye 213e4b86885SCheng Sean Ye MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg; 214e4b86885SCheng Sean Ye 215e4b86885SCheng Sean Ye return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ? 216e4b86885SCheng Sean Ye MCREG_FIELD_F_preF(&nbcfg, EccEn) : 217e4b86885SCheng Sean Ye MCREG_FIELD_F_revFG(&nbcfg, EccEn)); 218e4b86885SCheng Sean Ye } 219e4b86885SCheng Sean Ye 220e4b86885SCheng Sean Ye static uint32_t 221e4b86885SCheng Sean Ye mc_ck_enabled(mc_t *mc) 222e4b86885SCheng Sean Ye { 223e4b86885SCheng Sean Ye uint32_t rev = mc->mc_props.mcp_rev; 224e4b86885SCheng Sean Ye union mcreg_nbcfg nbcfg; 225e4b86885SCheng Sean Ye 226e4b86885SCheng Sean Ye MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg; 227e4b86885SCheng Sean Ye 228e4b86885SCheng Sean Ye return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ? 229e4b86885SCheng Sean Ye MCREG_FIELD_F_preF(&nbcfg, ChipKillEccEn) : 230e4b86885SCheng Sean Ye MCREG_FIELD_F_revFG(&nbcfg, ChipKillEccEn)); 231e4b86885SCheng Sean Ye } 232e4b86885SCheng Sean Ye 233e4b86885SCheng Sean Ye static void 234e4b86885SCheng Sean Ye mc_nvl_add_ecctype(nvlist_t *nvl, mc_t *mc) 235e4b86885SCheng Sean Ye { 236e4b86885SCheng Sean Ye (void) nvlist_add_string(nvl, "ecc-type", mc_ecc_enabled(mc) ? 237e4b86885SCheng Sean Ye (mc_ck_enabled(mc) ? "ChipKill 128/16" : "Normal 64/8") : "None"); 238e4b86885SCheng Sean Ye } 239e4b86885SCheng Sean Ye 240e4b86885SCheng Sean Ye static void 241e4b86885SCheng Sean Ye mc_nvl_add_prop(nvlist_t *nvl, void *node, mcamd_propcode_t code, int reqval) 242e4b86885SCheng Sean Ye { 243e4b86885SCheng Sean Ye int valfound; 244e4b86885SCheng Sean Ye uint64_t value; 245e4b86885SCheng Sean Ye const char *name = mcamd_get_propname(code); 246e4b86885SCheng Sean Ye 247e4b86885SCheng Sean Ye valfound = mcamd_get_numprop(NULL, (mcamd_node_t *)node, code, &value); 248e4b86885SCheng Sean Ye 249e4b86885SCheng Sean Ye ASSERT(name != NULL && valfound); 250e4b86885SCheng Sean Ye if (name != NULL && valfound && (!reqval || value != MC_INVALNUM)) 251e4b86885SCheng Sean Ye (void) nvlist_add_uint64(nvl, name, value); 252e4b86885SCheng Sean Ye } 253e4b86885SCheng Sean Ye 254e4b86885SCheng Sean Ye static void 255e4b86885SCheng Sean Ye mc_nvl_add_cslist(nvlist_t *mcnvl, mc_t *mc) 256e4b86885SCheng Sean Ye { 257e4b86885SCheng Sean Ye mc_cs_t *mccs = mc->mc_cslist; 258e4b86885SCheng Sean Ye nvlist_t *cslist[MC_CHIP_NCS]; 259e4b86885SCheng Sean Ye int nelem, i; 260e4b86885SCheng Sean Ye 261e4b86885SCheng Sean Ye for (nelem = 0; mccs != NULL; mccs = mccs->mccs_next, nelem++) { 262e4b86885SCheng Sean Ye nvlist_t **csp = &cslist[nelem]; 263e4b86885SCheng Sean Ye char csname[MCDCFG_CSNAMELEN]; 264e4b86885SCheng Sean Ye 265e4b86885SCheng Sean Ye (void) nvlist_alloc(csp, NV_UNIQUE_NAME, KM_SLEEP); 266e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_NUM, 0); 267e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_BASE_ADDR, 0); 268e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_MASK, 0); 269e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_SIZE, 0); 270e4b86885SCheng Sean Ye 271e4b86885SCheng Sean Ye /* 272e4b86885SCheng Sean Ye * It is possible for an mc_cs_t not to have associated 273e4b86885SCheng Sean Ye * DIMM info if mcdcfg_lookup failed. 274e4b86885SCheng Sean Ye */ 275e4b86885SCheng Sean Ye if (mccs->mccs_csl[0] != NULL) { 276e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM1, 1); 277e4b86885SCheng Sean Ye mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[0], csname, 278e4b86885SCheng Sean Ye sizeof (csname)); 279e4b86885SCheng Sean Ye (void) nvlist_add_string(*csp, "dimm1-csname", csname); 280e4b86885SCheng Sean Ye } 281e4b86885SCheng Sean Ye 282e4b86885SCheng Sean Ye if (mccs->mccs_csl[1] != NULL) { 283e4b86885SCheng Sean Ye mc_nvl_add_prop(*csp, mccs, MCAMD_PROP_CSDIMM2, 1); 284e4b86885SCheng Sean Ye mcdcfg_csname(mc->mc_socket, mccs->mccs_csl[1], csname, 285e4b86885SCheng Sean Ye sizeof (csname)); 286e4b86885SCheng Sean Ye (void) nvlist_add_string(*csp, "dimm2-csname", csname); 287e4b86885SCheng Sean Ye } 288e4b86885SCheng Sean Ye } 289e4b86885SCheng Sean Ye 290e4b86885SCheng Sean Ye /* Add cslist nvlist array even if zero members */ 291e4b86885SCheng Sean Ye (void) nvlist_add_nvlist_array(mcnvl, "cslist", cslist, nelem); 292e4b86885SCheng Sean Ye for (i = 0; i < nelem; i++) 293e4b86885SCheng Sean Ye nvlist_free(cslist[i]); 294e4b86885SCheng Sean Ye } 295e4b86885SCheng Sean Ye 296e4b86885SCheng Sean Ye static void 297e4b86885SCheng Sean Ye mc_nvl_add_dimmlist(nvlist_t *mcnvl, mc_t *mc) 298e4b86885SCheng Sean Ye { 299e4b86885SCheng Sean Ye nvlist_t *dimmlist[MC_CHIP_NDIMM]; 300e4b86885SCheng Sean Ye mc_dimm_t *mcd; 301e4b86885SCheng Sean Ye int nelem, i; 302e4b86885SCheng Sean Ye 303e4b86885SCheng Sean Ye for (nelem = 0, mcd = mc->mc_dimmlist; mcd != NULL; 304e4b86885SCheng Sean Ye mcd = mcd->mcd_next, nelem++) { 305e4b86885SCheng Sean Ye nvlist_t **dimmp = &dimmlist[nelem]; 306e4b86885SCheng Sean Ye uint64_t csnums[MC_CHIP_DIMMRANKMAX]; 307e4b86885SCheng Sean Ye char csname[4][MCDCFG_CSNAMELEN]; 308e4b86885SCheng Sean Ye char *csnamep[4]; 309e4b86885SCheng Sean Ye int ncs = 0; 310e4b86885SCheng Sean Ye 311e4b86885SCheng Sean Ye (void) nvlist_alloc(dimmp, NV_UNIQUE_NAME, KM_SLEEP); 312e4b86885SCheng Sean Ye 313e4b86885SCheng Sean Ye mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_NUM, 1); 314e4b86885SCheng Sean Ye mc_nvl_add_prop(*dimmp, mcd, MCAMD_PROP_SIZE, 1); 315e4b86885SCheng Sean Ye 316e4b86885SCheng Sean Ye for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) { 317e4b86885SCheng Sean Ye if (mcd->mcd_cs[i] != NULL) { 318e4b86885SCheng Sean Ye csnums[ncs] = 319e4b86885SCheng Sean Ye mcd->mcd_cs[i]->mccs_props.csp_num; 320e4b86885SCheng Sean Ye mcdcfg_csname(mc->mc_socket, mcd->mcd_csl[i], 321e4b86885SCheng Sean Ye csname[ncs], MCDCFG_CSNAMELEN); 322e4b86885SCheng Sean Ye csnamep[ncs] = csname[ncs]; 323e4b86885SCheng Sean Ye ncs++; 324e4b86885SCheng Sean Ye } 325e4b86885SCheng Sean Ye } 326e4b86885SCheng Sean Ye 327e4b86885SCheng Sean Ye (void) nvlist_add_uint64_array(*dimmp, "csnums", csnums, ncs); 328e4b86885SCheng Sean Ye (void) nvlist_add_string_array(*dimmp, "csnames", csnamep, ncs); 329e4b86885SCheng Sean Ye } 330e4b86885SCheng Sean Ye 331e4b86885SCheng Sean Ye /* Add dimmlist nvlist array even if zero members */ 332e4b86885SCheng Sean Ye (void) nvlist_add_nvlist_array(mcnvl, "dimmlist", dimmlist, nelem); 333e4b86885SCheng Sean Ye for (i = 0; i < nelem; i++) 334e4b86885SCheng Sean Ye nvlist_free(dimmlist[i]); 335e4b86885SCheng Sean Ye } 336e4b86885SCheng Sean Ye 337e4b86885SCheng Sean Ye static void 338e4b86885SCheng Sean Ye mc_nvl_add_htconfig(nvlist_t *mcnvl, mc_t *mc) 339e4b86885SCheng Sean Ye { 340e4b86885SCheng Sean Ye mc_cfgregs_t *mcr = &mc->mc_cfgregs; 341e4b86885SCheng Sean Ye union mcreg_htroute *htrp = (union mcreg_htroute *)&mcr->mcr_htroute[0]; 342e4b86885SCheng Sean Ye union mcreg_nodeid *nip = (union mcreg_nodeid *)&mcr->mcr_htnodeid; 343e4b86885SCheng Sean Ye union mcreg_unitid *uip = (union mcreg_unitid *)&mcr->mcr_htunitid; 344e4b86885SCheng Sean Ye int ndcnt = HT_COHERENTNODES(nip); 345e4b86885SCheng Sean Ye uint32_t BCRte[MC_CHIP_MAXNODES]; 346e4b86885SCheng Sean Ye uint32_t RPRte[MC_CHIP_MAXNODES]; 347e4b86885SCheng Sean Ye uint32_t RQRte[MC_CHIP_MAXNODES]; 348e4b86885SCheng Sean Ye nvlist_t *nvl; 349e4b86885SCheng Sean Ye int i; 350e4b86885SCheng Sean Ye 351e4b86885SCheng Sean Ye (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 352e4b86885SCheng Sean Ye 353e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "NodeId", MCREG_FIELD_CMN(nip, NodeId)); 354e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "CoherentNodes", HT_COHERENTNODES(nip)); 355e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "SbNode", MCREG_FIELD_CMN(nip, SbNode)); 356e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "LkNode", MCREG_FIELD_CMN(nip, LkNode)); 357e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "SystemCoreCount", 358e4b86885SCheng Sean Ye HT_SYSTEMCORECOUNT(nip)); 359e4b86885SCheng Sean Ye 360e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "C0Unit", MCREG_FIELD_CMN(uip, C0Unit)); 361e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "C1Unit", MCREG_FIELD_CMN(uip, C1Unit)); 362e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "McUnit", MCREG_FIELD_CMN(uip, McUnit)); 363e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "HbUnit", MCREG_FIELD_CMN(uip, HbUnit)); 364e4b86885SCheng Sean Ye (void) nvlist_add_uint32(nvl, "SbLink", MCREG_FIELD_CMN(uip, SbLink)); 365e4b86885SCheng Sean Ye 366e4b86885SCheng Sean Ye if (ndcnt <= MC_CHIP_MAXNODES) { 367e4b86885SCheng Sean Ye for (i = 0; i < ndcnt; i++, htrp++) { 368e4b86885SCheng Sean Ye BCRte[i] = MCREG_FIELD_CMN(htrp, BCRte); 369e4b86885SCheng Sean Ye RPRte[i] = MCREG_FIELD_CMN(htrp, RPRte); 370e4b86885SCheng Sean Ye RQRte[i] = MCREG_FIELD_CMN(htrp, RQRte); 371e4b86885SCheng Sean Ye } 372e4b86885SCheng Sean Ye 373e4b86885SCheng Sean Ye (void) nvlist_add_uint32_array(nvl, "BroadcastRoutes", 374e4b86885SCheng Sean Ye &BCRte[0], ndcnt); 375e4b86885SCheng Sean Ye (void) nvlist_add_uint32_array(nvl, "ResponseRoutes", 376e4b86885SCheng Sean Ye &RPRte[0], ndcnt); 377e4b86885SCheng Sean Ye (void) nvlist_add_uint32_array(nvl, "RequestRoutes", 378e4b86885SCheng Sean Ye &RQRte[0], ndcnt); 379e4b86885SCheng Sean Ye } 380e4b86885SCheng Sean Ye 381e4b86885SCheng Sean Ye (void) nvlist_add_nvlist(mcnvl, "htconfig", nvl); 382e4b86885SCheng Sean Ye nvlist_free(nvl); 383e4b86885SCheng Sean Ye } 384e4b86885SCheng Sean Ye 385e4b86885SCheng Sean Ye static nvlist_t * 386e4b86885SCheng Sean Ye mc_nvl_create(mc_t *mc) 387e4b86885SCheng Sean Ye { 388e4b86885SCheng Sean Ye nvlist_t *mcnvl; 389e4b86885SCheng Sean Ye 390e4b86885SCheng Sean Ye (void) nvlist_alloc(&mcnvl, NV_UNIQUE_NAME, KM_SLEEP); 391e4b86885SCheng Sean Ye 392e4b86885SCheng Sean Ye /* 393e4b86885SCheng Sean Ye * Since this nvlist is used in populating the topo tree changes 394e4b86885SCheng Sean Ye * made here may propogate through to changed property names etc 395e4b86885SCheng Sean Ye * in the topo tree. Some properties in the topo tree will be 396e4b86885SCheng Sean Ye * contracted via ARC, so be careful what you change here. 397e4b86885SCheng Sean Ye */ 398e4b86885SCheng Sean Ye (void) nvlist_add_uint8(mcnvl, MC_NVLIST_VERSTR, MC_NVLIST_VERS1); 399e4b86885SCheng Sean Ye 400e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_NUM, 0); 401e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_REV, 0); 402e4b86885SCheng Sean Ye (void) nvlist_add_string(mcnvl, "revname", mc->mc_revname); 403e4b86885SCheng Sean Ye mc_nvl_add_socket(mcnvl, mc); 404e4b86885SCheng Sean Ye mc_nvl_add_ecctype(mcnvl, mc); 405e4b86885SCheng Sean Ye 406e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BASE_ADDR, 0); 407e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_LIM_ADDR, 0); 408e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILEN, 0); 409e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ILSEL, 0); 410e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSINTLVFCTR, 0); 411e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_DRAMHOLE_SIZE, 0); 412e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_ACCESS_WIDTH, 0); 413e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_CSBANKMAPREG, 0); 414e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BANKSWZL, 0); 415e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_MOD64MUX, 0); 416e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_SPARECS, 1); 417e4b86885SCheng Sean Ye mc_nvl_add_prop(mcnvl, mc, MCAMD_PROP_BADCS, 1); 418e4b86885SCheng Sean Ye 419e4b86885SCheng Sean Ye mc_nvl_add_cslist(mcnvl, mc); 420e4b86885SCheng Sean Ye mc_nvl_add_dimmlist(mcnvl, mc); 421e4b86885SCheng Sean Ye mc_nvl_add_htconfig(mcnvl, mc); 422e4b86885SCheng Sean Ye 423e4b86885SCheng Sean Ye return (mcnvl); 424e4b86885SCheng Sean Ye } 425e4b86885SCheng Sean Ye 426e4b86885SCheng Sean Ye /* 427e4b86885SCheng Sean Ye * Link a dimm to its associated chip-selects and chip-select lines. 428e4b86885SCheng Sean Ye * Total the size of all ranks of this dimm. 429e4b86885SCheng Sean Ye */ 430e4b86885SCheng Sean Ye static void 431e4b86885SCheng Sean Ye mc_dimm_csadd(mc_t *mc, mc_dimm_t *mcd, mc_cs_t *mccs, const mcdcfg_csl_t *csl) 432e4b86885SCheng Sean Ye { 433e4b86885SCheng Sean Ye int factor = (mc->mc_props.mcp_accwidth == 128) ? 2 : 1; 434e4b86885SCheng Sean Ye uint64_t sz = 0; 435e4b86885SCheng Sean Ye int i; 436e4b86885SCheng Sean Ye 437e4b86885SCheng Sean Ye /* Skip to first unused rank slot */ 438e4b86885SCheng Sean Ye for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) { 439e4b86885SCheng Sean Ye if (mcd->mcd_cs[i] == NULL) { 440e4b86885SCheng Sean Ye mcd->mcd_cs[i] = mccs; 441e4b86885SCheng Sean Ye mcd->mcd_csl[i] = csl; 442e4b86885SCheng Sean Ye sz += mccs->mccs_props.csp_size / factor; 443e4b86885SCheng Sean Ye break; 444e4b86885SCheng Sean Ye } else { 445e4b86885SCheng Sean Ye sz += mcd->mcd_cs[i]->mccs_props.csp_size / factor; 446e4b86885SCheng Sean Ye } 447e4b86885SCheng Sean Ye } 448e4b86885SCheng Sean Ye 449e4b86885SCheng Sean Ye ASSERT(i != MC_CHIP_DIMMRANKMAX); 450e4b86885SCheng Sean Ye 451e4b86885SCheng Sean Ye mcd->mcd_size = sz; 452e4b86885SCheng Sean Ye } 453e4b86885SCheng Sean Ye 454e4b86885SCheng Sean Ye /* 455e4b86885SCheng Sean Ye * Create a dimm structure and call to link it to its associated chip-selects. 456e4b86885SCheng Sean Ye */ 457e4b86885SCheng Sean Ye static mc_dimm_t * 458e4b86885SCheng Sean Ye mc_dimm_create(mc_t *mc, uint_t num) 459e4b86885SCheng Sean Ye { 460e4b86885SCheng Sean Ye mc_dimm_t *mcd = kmem_zalloc(sizeof (mc_dimm_t), KM_SLEEP); 461e4b86885SCheng Sean Ye 462e4b86885SCheng Sean Ye mcd->mcd_hdr.mch_type = MC_NT_DIMM; 463e4b86885SCheng Sean Ye mcd->mcd_mc = mc; 464e4b86885SCheng Sean Ye mcd->mcd_num = num; 465e4b86885SCheng Sean Ye 466e4b86885SCheng Sean Ye return (mcd); 467e4b86885SCheng Sean Ye } 468e4b86885SCheng Sean Ye 469e4b86885SCheng Sean Ye /* 470e4b86885SCheng Sean Ye * The chip-select structure includes an array of dimms associated with 471e4b86885SCheng Sean Ye * that chip-select. This function fills that array, and also builds 472e4b86885SCheng Sean Ye * the list of all dimms on this memory controller mc_dimmlist. The 473e4b86885SCheng Sean Ye * caller has filled a structure with all there is to know about the 474e4b86885SCheng Sean Ye * associated dimm(s). 475e4b86885SCheng Sean Ye */ 476e4b86885SCheng Sean Ye static void 477e4b86885SCheng Sean Ye mc_csdimms_create(mc_t *mc, mc_cs_t *mccs, mcdcfg_rslt_t *rsltp) 478e4b86885SCheng Sean Ye { 479e4b86885SCheng Sean Ye mc_dimm_t *found[MC_CHIP_DIMMPERCS]; 480e4b86885SCheng Sean Ye mc_dimm_t *mcd; 481e4b86885SCheng Sean Ye int nfound = 0; 482e4b86885SCheng Sean Ye int i; 483e4b86885SCheng Sean Ye 484e4b86885SCheng Sean Ye /* 485e4b86885SCheng Sean Ye * Has some other chip-select already created this dimm or dimms? 486e4b86885SCheng Sean Ye * If so then link to the dimm(s) from the mccs_dimm array, 487e4b86885SCheng Sean Ye * record their topo numbers in the csp_dimmnums array, and link 488e4b86885SCheng Sean Ye * the dimm(s) to the additional chip-select. 489e4b86885SCheng Sean Ye */ 490e4b86885SCheng Sean Ye for (mcd = mc->mc_dimmlist; mcd != NULL; mcd = mcd->mcd_next) { 491e4b86885SCheng Sean Ye for (i = 0; i < rsltp->ndimm; i++) { 492e4b86885SCheng Sean Ye if (mcd->mcd_num == rsltp->dimm[i].toponum) 493e4b86885SCheng Sean Ye found[nfound++] = mcd; 494e4b86885SCheng Sean Ye } 495e4b86885SCheng Sean Ye } 496e4b86885SCheng Sean Ye ASSERT(nfound == 0 || nfound == rsltp->ndimm); 497e4b86885SCheng Sean Ye 498e4b86885SCheng Sean Ye for (i = 0; i < rsltp->ndimm; i++) { 499e4b86885SCheng Sean Ye if (nfound == 0) { 500e4b86885SCheng Sean Ye mcd = mc_dimm_create(mc, rsltp->dimm[i].toponum); 501e4b86885SCheng Sean Ye if (mc->mc_dimmlist == NULL) 502e4b86885SCheng Sean Ye mc->mc_dimmlist = mcd; 503e4b86885SCheng Sean Ye else 504e4b86885SCheng Sean Ye mc->mc_dimmlast->mcd_next = mcd; 505e4b86885SCheng Sean Ye mc->mc_dimmlast = mcd; 506e4b86885SCheng Sean Ye } else { 507e4b86885SCheng Sean Ye mcd = found[i]; 508e4b86885SCheng Sean Ye } 509e4b86885SCheng Sean Ye 510e4b86885SCheng Sean Ye mccs->mccs_dimm[i] = mcd; 511e4b86885SCheng Sean Ye mccs->mccs_csl[i] = rsltp->dimm[i].cslp; 512e4b86885SCheng Sean Ye mccs->mccs_props.csp_dimmnums[i] = mcd->mcd_num; 513e4b86885SCheng Sean Ye mc_dimm_csadd(mc, mcd, mccs, rsltp->dimm[i].cslp); 514e4b86885SCheng Sean Ye 515e4b86885SCheng Sean Ye } 516e4b86885SCheng Sean Ye 517e4b86885SCheng Sean Ye /* The rank number is constant across all constituent dimm(s) */ 518e4b86885SCheng Sean Ye mccs->mccs_props.csp_dimmrank = rsltp->dimm[0].cslp->csl_rank; 519e4b86885SCheng Sean Ye } 520e4b86885SCheng Sean Ye 521e4b86885SCheng Sean Ye /* 522e4b86885SCheng Sean Ye * mc_dimmlist_create is called after we have discovered all enabled 523e4b86885SCheng Sean Ye * (and spare or testfailed on revs F and G) chip-selects on the 524e4b86885SCheng Sean Ye * given memory controller. For each chip-select we must derive 525e4b86885SCheng Sean Ye * the associated dimms, remembering that a chip-select csbase/csmask 526e4b86885SCheng Sean Ye * pair may be associated with up to 2 chip-select lines (in 128 bit mode) 527e4b86885SCheng Sean Ye * and that any one dimm may be associated with 1, 2, or 4 chip-selects 528e4b86885SCheng Sean Ye * depending on whether it is single, dual or quadrank. 529e4b86885SCheng Sean Ye */ 530e4b86885SCheng Sean Ye static void 531e4b86885SCheng Sean Ye mc_dimmlist_create(mc_t *mc) 532e4b86885SCheng Sean Ye { 533e4b86885SCheng Sean Ye union mcreg_dramcfg_hi *drcfghip = 534e4b86885SCheng Sean Ye (union mcreg_dramcfg_hi *)(&mc->mc_cfgregs.mcr_dramcfghi); 535e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 536e4b86885SCheng Sean Ye uint32_t rev = mcp->mcp_rev; 537e4b86885SCheng Sean Ye mc_cs_t *mccs; 538e4b86885SCheng Sean Ye int r4 = 0, s4 = 0; 539e4b86885SCheng Sean Ye 540e4b86885SCheng Sean Ye /* 541e4b86885SCheng Sean Ye * Are we dealing with quadrank registered dimms? 542e4b86885SCheng Sean Ye * 543e4b86885SCheng Sean Ye * For socket 940 we can't tell and we'll assume we're not. 544e4b86885SCheng Sean Ye * This can be over-ridden by the admin in /etc/system by setting 545e4b86885SCheng Sean Ye * mc_quadranksupport nonzero. A possible optimisation in systems 546e4b86885SCheng Sean Ye * that export an SMBIOS table would be to count the number of 547e4b86885SCheng Sean Ye * dimm slots per cpu - more than 4 would indicate no quadrank support 548e4b86885SCheng Sean Ye * and 4 or fewer would indicate that if we see any of the upper 549e4b86885SCheng Sean Ye * chip-selects enabled then a quadrank dimm is present. 550e4b86885SCheng Sean Ye * 551e4b86885SCheng Sean Ye * For socket F(1207) we can check a bit in the dram config high reg. 552e4b86885SCheng Sean Ye * 553e4b86885SCheng Sean Ye * Other socket types do not support registered dimms. 554e4b86885SCheng Sean Ye */ 555e4b86885SCheng Sean Ye if (mc->mc_socket == X86_SOCKET_940) 556e4b86885SCheng Sean Ye r4 = mc_quadranksupport != 0; 557e4b86885SCheng Sean Ye else if (mc->mc_socket == X86_SOCKET_F1207) 558e4b86885SCheng Sean Ye r4 = MCREG_FIELD_F_revFG(drcfghip, FourRankRDimm); 559e4b86885SCheng Sean Ye 560e4b86885SCheng Sean Ye /* 561e4b86885SCheng Sean Ye * Are we dealing with quadrank SO-DIMMs? These are supported 562e4b86885SCheng Sean Ye * in AM2 and S1g1 packages only, but in all rev F/G cases we 563e4b86885SCheng Sean Ye * can detect their presence via a bit in the dram config high reg. 564e4b86885SCheng Sean Ye */ 565e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REVS_FG)) 566e4b86885SCheng Sean Ye s4 = MCREG_FIELD_F_revFG(drcfghip, FourRankSODimm); 567e4b86885SCheng Sean Ye 568e4b86885SCheng Sean Ye for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) { 569e4b86885SCheng Sean Ye mcdcfg_rslt_t rslt; 570e4b86885SCheng Sean Ye 571e4b86885SCheng Sean Ye /* 572e4b86885SCheng Sean Ye * If lookup fails we will not create dimm structures for 573e4b86885SCheng Sean Ye * this chip-select. In the mc_cs_t we will have both 574e4b86885SCheng Sean Ye * csp_dimmnum members set to MC_INVALNUM and patounum 575e4b86885SCheng Sean Ye * code will see from those that we do not have dimm info 576e4b86885SCheng Sean Ye * for this chip-select. 577e4b86885SCheng Sean Ye */ 578e4b86885SCheng Sean Ye if (mcdcfg_lookup(rev, mcp->mcp_mod64mux, mcp->mcp_accwidth, 579e4b86885SCheng Sean Ye mccs->mccs_props.csp_num, mc->mc_socket, 580e4b86885SCheng Sean Ye r4, s4, &rslt) < 0) 581e4b86885SCheng Sean Ye continue; 582e4b86885SCheng Sean Ye 583e4b86885SCheng Sean Ye mc_csdimms_create(mc, mccs, &rslt); 584e4b86885SCheng Sean Ye } 585e4b86885SCheng Sean Ye } 586e4b86885SCheng Sean Ye 587e4b86885SCheng Sean Ye static mc_cs_t * 588e4b86885SCheng Sean Ye mc_cs_create(mc_t *mc, uint_t num, uint64_t base, uint64_t mask, size_t sz, 589e4b86885SCheng Sean Ye int csbe, int spare, int testfail) 590e4b86885SCheng Sean Ye { 591e4b86885SCheng Sean Ye mc_cs_t *mccs = kmem_zalloc(sizeof (mc_cs_t), KM_SLEEP); 592e4b86885SCheng Sean Ye mccs_props_t *csp = &mccs->mccs_props; 593e4b86885SCheng Sean Ye int i; 594e4b86885SCheng Sean Ye 595e4b86885SCheng Sean Ye mccs->mccs_hdr.mch_type = MC_NT_CS; 596e4b86885SCheng Sean Ye mccs->mccs_mc = mc; 597e4b86885SCheng Sean Ye csp->csp_num = num; 598e4b86885SCheng Sean Ye csp->csp_base = base; 599e4b86885SCheng Sean Ye csp->csp_mask = mask; 600e4b86885SCheng Sean Ye csp->csp_size = sz; 601e4b86885SCheng Sean Ye csp->csp_csbe = csbe; 602e4b86885SCheng Sean Ye csp->csp_spare = spare; 603e4b86885SCheng Sean Ye csp->csp_testfail = testfail; 604e4b86885SCheng Sean Ye 605e4b86885SCheng Sean Ye for (i = 0; i < MC_CHIP_DIMMPERCS; i++) 606e4b86885SCheng Sean Ye csp->csp_dimmnums[i] = MC_INVALNUM; 607e4b86885SCheng Sean Ye 608e4b86885SCheng Sean Ye if (spare) 609e4b86885SCheng Sean Ye mc->mc_props.mcp_sparecs = num; 610e4b86885SCheng Sean Ye 611e4b86885SCheng Sean Ye return (mccs); 612e4b86885SCheng Sean Ye } 613e4b86885SCheng Sean Ye 614e4b86885SCheng Sean Ye /* 615e4b86885SCheng Sean Ye * For any cs# of this mc marked TestFail generate an ereport with 616e4b86885SCheng Sean Ye * resource identifying the associated dimm(s). 617e4b86885SCheng Sean Ye */ 618e4b86885SCheng Sean Ye static void 619e4b86885SCheng Sean Ye mc_report_testfails(mc_t *mc) 620e4b86885SCheng Sean Ye { 621e4b86885SCheng Sean Ye mc_unum_t unum; 622e4b86885SCheng Sean Ye mc_cs_t *mccs; 623e4b86885SCheng Sean Ye int i; 624e4b86885SCheng Sean Ye 625e4b86885SCheng Sean Ye for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) { 626e4b86885SCheng Sean Ye if (mccs->mccs_props.csp_testfail) { 627e4b86885SCheng Sean Ye unum.unum_board = 0; 628e4b86885SCheng Sean Ye unum.unum_chip = mc->mc_props.mcp_num; 629e4b86885SCheng Sean Ye unum.unum_mc = 0; 630e4b86885SCheng Sean Ye unum.unum_chan = MC_INVALNUM; 631e4b86885SCheng Sean Ye unum.unum_cs = mccs->mccs_props.csp_num; 632e4b86885SCheng Sean Ye unum.unum_rank = mccs->mccs_props.csp_dimmrank; 633e4b86885SCheng Sean Ye unum.unum_offset = MCAMD_RC_INVALID_OFFSET; 634e4b86885SCheng Sean Ye for (i = 0; i < MC_CHIP_DIMMPERCS; i++) 635e4b86885SCheng Sean Ye unum.unum_dimms[i] = MC_INVALNUM; 636e4b86885SCheng Sean Ye 637e4b86885SCheng Sean Ye mcamd_ereport_post(mc, FM_EREPORT_CPU_AMD_MC_TESTFAIL, 638e4b86885SCheng Sean Ye &unum, 639e4b86885SCheng Sean Ye FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL); 640e4b86885SCheng Sean Ye } 641e4b86885SCheng Sean Ye } 642e4b86885SCheng Sean Ye } 643e4b86885SCheng Sean Ye 644e4b86885SCheng Sean Ye /* 645e4b86885SCheng Sean Ye * Function 0 - HyperTransport Technology Configuration 646e4b86885SCheng Sean Ye */ 647e4b86885SCheng Sean Ye static void 648e4b86885SCheng Sean Ye mc_mkprops_htcfg(mc_pcicfg_hdl_t cfghdl, mc_t *mc) 649e4b86885SCheng Sean Ye { 650e4b86885SCheng Sean Ye union mcreg_nodeid nodeid; 651e4b86885SCheng Sean Ye off_t offset; 652e4b86885SCheng Sean Ye int i; 653e4b86885SCheng Sean Ye 654e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_htnodeid = MCREG_VAL32(&nodeid) = 655e4b86885SCheng Sean Ye mc_pcicfg_get32(cfghdl, MC_HT_REG_NODEID); 656e4b86885SCheng Sean Ye 657e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_htunitid = mc_pcicfg_get32(cfghdl, MC_HT_REG_UNITID); 658e4b86885SCheng Sean Ye 659e4b86885SCheng Sean Ye for (i = 0, offset = MC_HT_REG_RTBL_NODE_0; 660e4b86885SCheng Sean Ye i < HT_COHERENTNODES(&nodeid); 661e4b86885SCheng Sean Ye i++, offset += MC_HT_REG_RTBL_INCR) 662e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_htroute[i] = mc_pcicfg_get32(cfghdl, offset); 663e4b86885SCheng Sean Ye } 664e4b86885SCheng Sean Ye 665e4b86885SCheng Sean Ye /* 666e4b86885SCheng Sean Ye * Function 1 Configuration - Address Map (see BKDG 3.4.4 DRAM Address Map) 667e4b86885SCheng Sean Ye * 668e4b86885SCheng Sean Ye * Read the Function 1 Address Map for each potential DRAM node. The Base 669e4b86885SCheng Sean Ye * Address for a node gives the starting system address mapped at that node, 670e4b86885SCheng Sean Ye * and the limit gives the last valid address mapped at that node. Regions for 671e4b86885SCheng Sean Ye * different nodes should not overlap, unless node-interleaving is enabled. 672e4b86885SCheng Sean Ye * The base register also indicates the node-interleaving settings (IntlvEn). 673e4b86885SCheng Sean Ye * The limit register includes IntlvSel which determines which 4K blocks will 674e4b86885SCheng Sean Ye * be routed to this node and the destination node ID for addresses that fall 675e4b86885SCheng Sean Ye * within the [base, limit] range - this must match the pair number. 676e4b86885SCheng Sean Ye */ 677e4b86885SCheng Sean Ye static void 678e4b86885SCheng Sean Ye mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc) 679e4b86885SCheng Sean Ye { 680e4b86885SCheng Sean Ye union mcreg_drambase basereg; 681e4b86885SCheng Sean Ye union mcreg_dramlimit limreg; 682e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 683e4b86885SCheng Sean Ye mc_cfgregs_t *mcr = &mc->mc_cfgregs; 684e4b86885SCheng Sean Ye union mcreg_dramhole hole; 685e4b86885SCheng Sean Ye int nodeid = mc->mc_props.mcp_num; 686e4b86885SCheng Sean Ye 687e4b86885SCheng Sean Ye mcr->mcr_drambase = MCREG_VAL32(&basereg) = mc_pcicfg_get32(cfghdl, 688e4b86885SCheng Sean Ye MC_AM_REG_DRAMBASE_0 + nodeid * MC_AM_REG_DRAM_INCR); 689e4b86885SCheng Sean Ye 690e4b86885SCheng Sean Ye mcr->mcr_dramlimit = MCREG_VAL32(&limreg) = mc_pcicfg_get32(cfghdl, 691e4b86885SCheng Sean Ye MC_AM_REG_DRAMLIM_0 + nodeid * MC_AM_REG_DRAM_INCR); 692e4b86885SCheng Sean Ye 693e4b86885SCheng Sean Ye /* 694e4b86885SCheng Sean Ye * Derive some "cooked" properties for nodes that have a range of 695e4b86885SCheng Sean Ye * physical addresses that are read or write enabled and for which 696e4b86885SCheng Sean Ye * the DstNode matches the node we are attaching. 697e4b86885SCheng Sean Ye */ 698e4b86885SCheng Sean Ye if (MCREG_FIELD_CMN(&limreg, DRAMLimiti) != 0 && 699e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&limreg, DstNode) == nodeid && 700e4b86885SCheng Sean Ye (MCREG_FIELD_CMN(&basereg, WE) || MCREG_FIELD_CMN(&basereg, RE))) { 701e4b86885SCheng Sean Ye mcp->mcp_base = MC_DRAMBASE(&basereg); 702e4b86885SCheng Sean Ye mcp->mcp_lim = MC_DRAMLIM(&limreg); 703e4b86885SCheng Sean Ye mcp->mcp_ilen = MCREG_FIELD_CMN(&basereg, IntlvEn); 704e4b86885SCheng Sean Ye mcp->mcp_ilsel = MCREG_FIELD_CMN(&limreg, IntlvSel); 705e4b86885SCheng Sean Ye } 706e4b86885SCheng Sean Ye 707e4b86885SCheng Sean Ye /* 708e4b86885SCheng Sean Ye * The Function 1 DRAM Hole Address Register tells us which node(s) 709e4b86885SCheng Sean Ye * own the DRAM space that is hoisted above 4GB, together with the 710e4b86885SCheng Sean Ye * hole base and offset for this node. This was introduced in 711e4b86885SCheng Sean Ye * revision E. 712e4b86885SCheng Sean Ye */ 713e4b86885SCheng Sean Ye if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_F_REV_E)) { 714e4b86885SCheng Sean Ye mcr->mcr_dramhole = MCREG_VAL32(&hole) = 715e4b86885SCheng Sean Ye mc_pcicfg_get32(cfghdl, MC_AM_REG_HOLEADDR); 716e4b86885SCheng Sean Ye 717e4b86885SCheng Sean Ye if (MCREG_FIELD_CMN(&hole, DramHoleValid)) 718e4b86885SCheng Sean Ye mcp->mcp_dramhole_size = MC_DRAMHOLE_SIZE(&hole); 719e4b86885SCheng Sean Ye } 720e4b86885SCheng Sean Ye } 721e4b86885SCheng Sean Ye 722e4b86885SCheng Sean Ye /* 723e4b86885SCheng Sean Ye * Read some function 3 parameters via PCI Mechanism 1 accesses (which 724e4b86885SCheng Sean Ye * will serialize any NB accesses). 725e4b86885SCheng Sean Ye */ 726e4b86885SCheng Sean Ye static void 727e4b86885SCheng Sean Ye mc_getmiscctl(mc_t *mc) 728e4b86885SCheng Sean Ye { 729e4b86885SCheng Sean Ye uint32_t rev = mc->mc_props.mcp_rev; 730e4b86885SCheng Sean Ye union mcreg_nbcfg nbcfg; 731e4b86885SCheng Sean Ye union mcreg_sparectl sparectl; 732e4b86885SCheng Sean Ye 733e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_nbcfg = MCREG_VAL32(&nbcfg) = 734e4b86885SCheng Sean Ye mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG); 735e4b86885SCheng Sean Ye 736e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { 737e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl) = 738e4b86885SCheng Sean Ye mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, 739e4b86885SCheng Sean Ye MC_CTL_REG_SPARECTL); 740e4b86885SCheng Sean Ye 741e4b86885SCheng Sean Ye if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) { 742e4b86885SCheng Sean Ye mc->mc_props.mcp_badcs = 743e4b86885SCheng Sean Ye MCREG_FIELD_F_revFG(&sparectl, BadDramCs); 744e4b86885SCheng Sean Ye } 745e4b86885SCheng Sean Ye } 746e4b86885SCheng Sean Ye } 747e4b86885SCheng Sean Ye 748e4b86885SCheng Sean Ye static int 749e4b86885SCheng Sean Ye csbasecmp(mc_cs_t **csapp, mc_cs_t **csbpp) 750e4b86885SCheng Sean Ye { 751e4b86885SCheng Sean Ye uint64_t basea = (*csapp)->mccs_props.csp_base; 752e4b86885SCheng Sean Ye uint64_t baseb = (*csbpp)->mccs_props.csp_base; 753e4b86885SCheng Sean Ye 754e4b86885SCheng Sean Ye if (basea == baseb) 755e4b86885SCheng Sean Ye return (0); 756e4b86885SCheng Sean Ye else if (basea < baseb) 757e4b86885SCheng Sean Ye return (-1); 758e4b86885SCheng Sean Ye else 759e4b86885SCheng Sean Ye return (1); 760e4b86885SCheng Sean Ye } 761e4b86885SCheng Sean Ye 762e4b86885SCheng Sean Ye /* 763e4b86885SCheng Sean Ye * The following are for use in simulating TestFail for a chip-select 764e4b86885SCheng Sean Ye * without poking at the hardware (which tends to get upset if you do 765e4b86885SCheng Sean Ye * since the BIOS needs to restart to map a failed cs out). For internal 766e4b86885SCheng Sean Ye * testing only! Note that setting these does not give the full experience - 767e4b86885SCheng Sean Ye * the select chip-select *is* enabled and can give errors etc and the 768e4b86885SCheng Sean Ye * patounum logic will get confused. 769e4b86885SCheng Sean Ye */ 770e4b86885SCheng Sean Ye int testfail_mcnum = -1; 771e4b86885SCheng Sean Ye int testfail_csnum = -1; 772e4b86885SCheng Sean Ye 773e4b86885SCheng Sean Ye /* 774e4b86885SCheng Sean Ye * Function 2 configuration - DRAM Controller 775e4b86885SCheng Sean Ye */ 776e4b86885SCheng Sean Ye static void 777e4b86885SCheng Sean Ye mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) 778e4b86885SCheng Sean Ye { 779e4b86885SCheng Sean Ye union mcreg_csbase base[MC_CHIP_NCS]; 780e4b86885SCheng Sean Ye union mcreg_csmask mask[MC_CHIP_NCS]; 781e4b86885SCheng Sean Ye union mcreg_dramcfg_lo drcfg_lo; 782e4b86885SCheng Sean Ye union mcreg_dramcfg_hi drcfg_hi; 783e4b86885SCheng Sean Ye union mcreg_drammisc drmisc; 784e4b86885SCheng Sean Ye union mcreg_bankaddrmap baddrmap; 785e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 786e4b86885SCheng Sean Ye mc_cfgregs_t *mcr = &mc->mc_cfgregs; 787e4b86885SCheng Sean Ye int maskdivisor; 788e4b86885SCheng Sean Ye int wide = 0; 789e4b86885SCheng Sean Ye uint32_t rev = mc->mc_props.mcp_rev; 790e4b86885SCheng Sean Ye int i; 791e4b86885SCheng Sean Ye mcamd_hdl_t hdl; 792e4b86885SCheng Sean Ye 793e4b86885SCheng Sean Ye mcamd_mkhdl(&hdl); /* to call into common code */ 794e4b86885SCheng Sean Ye 795e4b86885SCheng Sean Ye /* 796e4b86885SCheng Sean Ye * Read Function 2 DRAM Configuration High and Low registers. The High 797e4b86885SCheng Sean Ye * part is mostly concerned with memory clocks etc and we'll not have 798e4b86885SCheng Sean Ye * any use for that. The Low component tells us if ECC is enabled, 799e4b86885SCheng Sean Ye * if we're in 64- or 128-bit MC mode, how the upper chip-selects 800e4b86885SCheng Sean Ye * are mapped, which chip-select pairs are using x4 parts, etc. 801e4b86885SCheng Sean Ye */ 802e4b86885SCheng Sean Ye MCREG_VAL32(&drcfg_lo) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGLO); 803e4b86885SCheng Sean Ye MCREG_VAL32(&drcfg_hi) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMCFGHI); 804e4b86885SCheng Sean Ye mcr->mcr_dramcfglo = MCREG_VAL32(&drcfg_lo); 805e4b86885SCheng Sean Ye mcr->mcr_dramcfghi = MCREG_VAL32(&drcfg_hi); 806e4b86885SCheng Sean Ye 807e4b86885SCheng Sean Ye /* 808e4b86885SCheng Sean Ye * Note the DRAM controller width. The 64/128 bit is in a different 809e4b86885SCheng Sean Ye * bit position for revision F and G. 810e4b86885SCheng Sean Ye */ 811e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { 812e4b86885SCheng Sean Ye wide = MCREG_FIELD_F_revFG(&drcfg_lo, Width128); 813e4b86885SCheng Sean Ye } else { 814e4b86885SCheng Sean Ye wide = MCREG_FIELD_F_preF(&drcfg_lo, Width128); 815e4b86885SCheng Sean Ye } 816e4b86885SCheng Sean Ye mcp->mcp_accwidth = wide ? 128 : 64; 817e4b86885SCheng Sean Ye 818e4b86885SCheng Sean Ye /* 819e4b86885SCheng Sean Ye * Read Function 2 DRAM Controller Miscellaenous Regsiter for those 820e4b86885SCheng Sean Ye * revs that support it. This include the Mod64Mux indication on 821e4b86885SCheng Sean Ye * these revs - for rev E it is in DRAM config low. 822e4b86885SCheng Sean Ye */ 823e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { 824e4b86885SCheng Sean Ye mcr->mcr_drammisc = MCREG_VAL32(&drmisc) = 825e4b86885SCheng Sean Ye mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMMISC); 826e4b86885SCheng Sean Ye mcp->mcp_mod64mux = MCREG_FIELD_F_revFG(&drmisc, Mod64Mux); 827e4b86885SCheng Sean Ye } else if (MC_REV_MATCH(rev, MC_F_REV_E)) { 828e4b86885SCheng Sean Ye mcp->mcp_mod64mux = MCREG_FIELD_F_preF(&drcfg_lo, Mod64BitMux); 829e4b86885SCheng Sean Ye } 830e4b86885SCheng Sean Ye 831e4b86885SCheng Sean Ye /* 832e4b86885SCheng Sean Ye * Read Function 2 DRAM Bank Address Mapping. This encodes the 833e4b86885SCheng Sean Ye * type of DIMM module in use for each chip-select pair. 834e4b86885SCheng Sean Ye * Prior ro revision F it also tells us whether BankSwizzle mode 835e4b86885SCheng Sean Ye * is enabled - in rev F that has moved to dram config hi register. 836e4b86885SCheng Sean Ye */ 837e4b86885SCheng Sean Ye mcp->mcp_csbankmapreg = MCREG_VAL32(&baddrmap) = 838e4b86885SCheng Sean Ye mc_pcicfg_get32(cfghdl, MC_DC_REG_BANKADDRMAP); 839e4b86885SCheng Sean Ye 840e4b86885SCheng Sean Ye /* 841e4b86885SCheng Sean Ye * Determine whether bank swizzle mode is active. Bank swizzling was 842e4b86885SCheng Sean Ye * introduced as an option in rev E, but the bit that indicates it 843e4b86885SCheng Sean Ye * is enabled has moved in revs F/G. 844e4b86885SCheng Sean Ye */ 845e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REV_E)) { 846e4b86885SCheng Sean Ye mcp->mcp_bnkswzl = 847e4b86885SCheng Sean Ye MCREG_FIELD_F_preF(&baddrmap, BankSwizzleMode); 848e4b86885SCheng Sean Ye } else if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { 849e4b86885SCheng Sean Ye mcp->mcp_bnkswzl = MCREG_FIELD_F_revFG(&drcfg_hi, 850e4b86885SCheng Sean Ye BankSwizzleMode); 851e4b86885SCheng Sean Ye } 852e4b86885SCheng Sean Ye 853e4b86885SCheng Sean Ye /* 854e4b86885SCheng Sean Ye * Read the DRAM CS Base and DRAM CS Mask registers. Revisions prior 855e4b86885SCheng Sean Ye * to F have an equal number of base and mask registers; revision F 856e4b86885SCheng Sean Ye * has twice as many base registers as masks. 857e4b86885SCheng Sean Ye */ 858e4b86885SCheng Sean Ye maskdivisor = MC_REV_MATCH(rev, MC_F_REVS_FG) ? 2 : 1; 859e4b86885SCheng Sean Ye 860e4b86885SCheng Sean Ye mc_prop_read_pair(cfghdl, 861e4b86885SCheng Sean Ye (uint32_t *)base, MC_DC_REG_CSBASE_0, MC_CHIP_NCS, 862e4b86885SCheng Sean Ye (uint32_t *)mask, MC_DC_REG_CSMASK_0, MC_CHIP_NCS / maskdivisor, 863e4b86885SCheng Sean Ye MC_DC_REG_CS_INCR); 864e4b86885SCheng Sean Ye 865e4b86885SCheng Sean Ye /* 866e4b86885SCheng Sean Ye * Create a cs node for each enabled chip-select as well as 867e4b86885SCheng Sean Ye * any appointed online spare chip-selects and for any that have 868e4b86885SCheng Sean Ye * failed test. 869e4b86885SCheng Sean Ye */ 870e4b86885SCheng Sean Ye for (i = 0; i < MC_CHIP_NCS; i++) { 871e4b86885SCheng Sean Ye mc_cs_t *mccs; 872e4b86885SCheng Sean Ye uint64_t csbase, csmask; 873e4b86885SCheng Sean Ye size_t sz; 874e4b86885SCheng Sean Ye int csbe, spare, testfail; 875e4b86885SCheng Sean Ye 876e4b86885SCheng Sean Ye if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { 877e4b86885SCheng Sean Ye csbe = MCREG_FIELD_F_revFG(&base[i], CSEnable); 878e4b86885SCheng Sean Ye spare = MCREG_FIELD_F_revFG(&base[i], Spare); 879e4b86885SCheng Sean Ye testfail = MCREG_FIELD_F_revFG(&base[i], TestFail); 880e4b86885SCheng Sean Ye } else { 881e4b86885SCheng Sean Ye csbe = MCREG_FIELD_F_preF(&base[i], CSEnable); 882e4b86885SCheng Sean Ye spare = 0; 883e4b86885SCheng Sean Ye testfail = 0; 884e4b86885SCheng Sean Ye } 885e4b86885SCheng Sean Ye 886e4b86885SCheng Sean Ye /* Testing hook */ 887e4b86885SCheng Sean Ye if (testfail_mcnum != -1 && testfail_csnum != -1 && 888e4b86885SCheng Sean Ye mcp->mcp_num == testfail_mcnum && i == testfail_csnum) { 889e4b86885SCheng Sean Ye csbe = spare = 0; 890e4b86885SCheng Sean Ye testfail = 1; 891e4b86885SCheng Sean Ye cmn_err(CE_NOTE, "Pretending MC %d CS %d failed test", 892e4b86885SCheng Sean Ye testfail_mcnum, testfail_csnum); 893e4b86885SCheng Sean Ye } 894e4b86885SCheng Sean Ye 895e4b86885SCheng Sean Ye /* 896e4b86885SCheng Sean Ye * If the chip-select is not enabled then skip it unless 897e4b86885SCheng Sean Ye * it is a designated online spare or is marked with TestFail. 898e4b86885SCheng Sean Ye */ 899e4b86885SCheng Sean Ye if (!csbe && !(spare || testfail)) 900e4b86885SCheng Sean Ye continue; 901e4b86885SCheng Sean Ye 902e4b86885SCheng Sean Ye /* 903e4b86885SCheng Sean Ye * For an enabled or spare chip-select the Bank Address Mapping 904e4b86885SCheng Sean Ye * register will be valid as will the chip-select mask. The 905e4b86885SCheng Sean Ye * base will not be valid but we'll read and store it anyway. 906e4b86885SCheng Sean Ye * We will not know whether the spare is already swapped in 907e4b86885SCheng Sean Ye * until MC function 3 attaches. 908e4b86885SCheng Sean Ye */ 909e4b86885SCheng Sean Ye if (csbe || spare) { 910e4b86885SCheng Sean Ye if (mcamd_cs_size(&hdl, (mcamd_node_t *)mc, i, &sz) < 0) 911e4b86885SCheng Sean Ye continue; 912e4b86885SCheng Sean Ye csbase = MC_CSBASE(&base[i], rev); 913e4b86885SCheng Sean Ye csmask = MC_CSMASK(&mask[i / maskdivisor], rev); 914e4b86885SCheng Sean Ye } else { 915e4b86885SCheng Sean Ye sz = 0; 916e4b86885SCheng Sean Ye csbase = csmask = 0; 917e4b86885SCheng Sean Ye } 918e4b86885SCheng Sean Ye 919e4b86885SCheng Sean Ye mccs = mc_cs_create(mc, i, csbase, csmask, sz, 920e4b86885SCheng Sean Ye csbe, spare, testfail); 921e4b86885SCheng Sean Ye 922e4b86885SCheng Sean Ye if (mc->mc_cslist == NULL) 923e4b86885SCheng Sean Ye mc->mc_cslist = mccs; 924e4b86885SCheng Sean Ye else 925e4b86885SCheng Sean Ye mc->mc_cslast->mccs_next = mccs; 926e4b86885SCheng Sean Ye mc->mc_cslast = mccs; 927e4b86885SCheng Sean Ye 928e4b86885SCheng Sean Ye mccs->mccs_cfgregs.csr_csbase = MCREG_VAL32(&base[i]); 929e4b86885SCheng Sean Ye mccs->mccs_cfgregs.csr_csmask = 930e4b86885SCheng Sean Ye MCREG_VAL32(&mask[i / maskdivisor]); 931e4b86885SCheng Sean Ye 932e4b86885SCheng Sean Ye /* 933e4b86885SCheng Sean Ye * Check for cs bank interleaving - some bits clear in the 934e4b86885SCheng Sean Ye * lower mask. All banks must/will have the same lomask bits 935e4b86885SCheng Sean Ye * if cs interleaving is active. 936e4b86885SCheng Sean Ye */ 937e4b86885SCheng Sean Ye if (csbe && !mcp->mcp_csintlvfctr) { 938e4b86885SCheng Sean Ye int bitno, ibits = 0; 939e4b86885SCheng Sean Ye for (bitno = MC_CSMASKLO_LOBIT(rev); 940e4b86885SCheng Sean Ye bitno <= MC_CSMASKLO_HIBIT(rev); bitno++) { 941e4b86885SCheng Sean Ye if (!(csmask & (1 << bitno))) 942e4b86885SCheng Sean Ye ibits++; 943e4b86885SCheng Sean Ye } 944e4b86885SCheng Sean Ye mcp->mcp_csintlvfctr = 1 << ibits; 945e4b86885SCheng Sean Ye } 946e4b86885SCheng Sean Ye } 947e4b86885SCheng Sean Ye 948e4b86885SCheng Sean Ye /* 949e4b86885SCheng Sean Ye * If there is no chip-select interleave on this node determine 950e4b86885SCheng Sean Ye * whether the chip-select ranks are contiguous or if there 951e4b86885SCheng Sean Ye * is a hole. 952e4b86885SCheng Sean Ye */ 953e4b86885SCheng Sean Ye if (mcp->mcp_csintlvfctr == 1) { 954e4b86885SCheng Sean Ye mc_cs_t *csp[MC_CHIP_NCS]; 955e4b86885SCheng Sean Ye mc_cs_t *mccs; 956e4b86885SCheng Sean Ye int ncsbe = 0; 957e4b86885SCheng Sean Ye 958e4b86885SCheng Sean Ye for (mccs = mc->mc_cslist; mccs != NULL; 959e4b86885SCheng Sean Ye mccs = mccs->mccs_next) { 960e4b86885SCheng Sean Ye if (mccs->mccs_props.csp_csbe) 961e4b86885SCheng Sean Ye csp[ncsbe++] = mccs; 962e4b86885SCheng Sean Ye } 963e4b86885SCheng Sean Ye 964e4b86885SCheng Sean Ye if (ncsbe != 0) { 965e4b86885SCheng Sean Ye qsort((void *)csp, ncsbe, sizeof (mc_cs_t *), 966e4b86885SCheng Sean Ye (int (*)(const void *, const void *))csbasecmp); 967e4b86885SCheng Sean Ye 968e4b86885SCheng Sean Ye for (i = 1; i < ncsbe; i++) { 969e4b86885SCheng Sean Ye if (csp[i]->mccs_props.csp_base != 970e4b86885SCheng Sean Ye csp[i - 1]->mccs_props.csp_base + 971e4b86885SCheng Sean Ye csp[i - 1]->mccs_props.csp_size) 972e4b86885SCheng Sean Ye mc->mc_csdiscontig = 1; 973e4b86885SCheng Sean Ye } 974e4b86885SCheng Sean Ye } 975e4b86885SCheng Sean Ye } 976e4b86885SCheng Sean Ye 977e4b86885SCheng Sean Ye 978e4b86885SCheng Sean Ye /* 979e4b86885SCheng Sean Ye * Since we do not attach to MC function 3 go ahead and read some 980e4b86885SCheng Sean Ye * config parameters from it now. 981e4b86885SCheng Sean Ye */ 982e4b86885SCheng Sean Ye mc_getmiscctl(mc); 983e4b86885SCheng Sean Ye 984e4b86885SCheng Sean Ye /* 985e4b86885SCheng Sean Ye * Now that we have discovered all enabled/spare/testfail chip-selects 986e4b86885SCheng Sean Ye * we divine the associated DIMM configuration. 987e4b86885SCheng Sean Ye */ 988e4b86885SCheng Sean Ye mc_dimmlist_create(mc); 989e4b86885SCheng Sean Ye } 990e4b86885SCheng Sean Ye 991e4b86885SCheng Sean Ye typedef struct mc_bind_map { 992e4b86885SCheng Sean Ye const char *bm_bindnm; /* attachment binding name */ 993e4b86885SCheng Sean Ye enum mc_funcnum bm_func; /* PCI config space function number for bind */ 994e4b86885SCheng Sean Ye const char *bm_model; /* value for device node model property */ 995e4b86885SCheng Sean Ye void (*bm_mkprops)(mc_pcicfg_hdl_t, mc_t *); 996e4b86885SCheng Sean Ye } mc_bind_map_t; 997e4b86885SCheng Sean Ye 998e4b86885SCheng Sean Ye /* 999e4b86885SCheng Sean Ye * Do not attach to MC function 3 - agpgart already attaches to that. 1000e4b86885SCheng Sean Ye * Function 3 may be a good candidate for a nexus driver to fan it out 1001e4b86885SCheng Sean Ye * into virtual devices by functionality. We will use pci_mech1_getl 1002e4b86885SCheng Sean Ye * to retrieve the function 3 parameters we require. 1003e4b86885SCheng Sean Ye */ 1004e4b86885SCheng Sean Ye 1005e4b86885SCheng Sean Ye static const mc_bind_map_t mc_bind_map[] = { 1006e4b86885SCheng Sean Ye { MC_FUNC_HTCONFIG_BINDNM, MC_FUNC_HTCONFIG, 1007e4b86885SCheng Sean Ye "AMD Memory Controller (HT Configuration)", mc_mkprops_htcfg }, 1008e4b86885SCheng Sean Ye { MC_FUNC_ADDRMAP_BINDNM, MC_FUNC_ADDRMAP, 1009e4b86885SCheng Sean Ye "AMD Memory Controller (Address Map)", mc_mkprops_addrmap }, 1010e4b86885SCheng Sean Ye { MC_FUNC_DRAMCTL_BINDNM, MC_FUNC_DRAMCTL, 1011e4b86885SCheng Sean Ye "AMD Memory Controller (DRAM Controller & HT Trace)", 1012e4b86885SCheng Sean Ye mc_mkprops_dramctl }, 1013e4b86885SCheng Sean Ye NULL 1014e4b86885SCheng Sean Ye }; 1015e4b86885SCheng Sean Ye 1016e4b86885SCheng Sean Ye /*ARGSUSED*/ 1017e4b86885SCheng Sean Ye static int 1018e4b86885SCheng Sean Ye mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 1019e4b86885SCheng Sean Ye { 1020e4b86885SCheng Sean Ye if (otyp != OTYP_CHR) 1021e4b86885SCheng Sean Ye return (EINVAL); 1022e4b86885SCheng Sean Ye 1023e4b86885SCheng Sean Ye rw_enter(&mc_lock, RW_READER); 1024e4b86885SCheng Sean Ye if (mc_lookup_by_chipid(getminor(*devp)) == NULL) { 1025e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1026e4b86885SCheng Sean Ye return (EINVAL); 1027e4b86885SCheng Sean Ye } 1028e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1029e4b86885SCheng Sean Ye 1030e4b86885SCheng Sean Ye return (0); 1031e4b86885SCheng Sean Ye } 1032e4b86885SCheng Sean Ye 1033e4b86885SCheng Sean Ye /*ARGSUSED*/ 1034e4b86885SCheng Sean Ye static int 1035e4b86885SCheng Sean Ye mc_close(dev_t dev, int flag, int otyp, cred_t *credp) 1036e4b86885SCheng Sean Ye { 1037e4b86885SCheng Sean Ye return (0); 1038e4b86885SCheng Sean Ye } 1039e4b86885SCheng Sean Ye 1040e4b86885SCheng Sean Ye /* 1041e4b86885SCheng Sean Ye * Enable swap from chip-select csnum to the spare chip-select on this 1042e4b86885SCheng Sean Ye * memory controller (if any). 1043e4b86885SCheng Sean Ye */ 1044e4b86885SCheng Sean Ye 1045e4b86885SCheng Sean Ye int mc_swapdonetime = 30; /* max number of seconds to wait for SwapDone */ 1046e4b86885SCheng Sean Ye 1047e4b86885SCheng Sean Ye static int 1048e4b86885SCheng Sean Ye mc_onlinespare(mc_t *mc, int csnum) 1049e4b86885SCheng Sean Ye { 1050e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 1051e4b86885SCheng Sean Ye union mcreg_sparectl sparectl; 1052e4b86885SCheng Sean Ye union mcreg_scrubctl scrubctl; 1053e4b86885SCheng Sean Ye mc_cs_t *mccs; 1054e4b86885SCheng Sean Ye hrtime_t tmax; 1055e4b86885SCheng Sean Ye int i = 0; 1056e4b86885SCheng Sean Ye 1057e4b86885SCheng Sean Ye ASSERT(RW_WRITE_HELD(&mc_lock)); 1058e4b86885SCheng Sean Ye 1059e4b86885SCheng Sean Ye if (!MC_REV_MATCH(mcp->mcp_rev, MC_F_REVS_FG)) 1060e4b86885SCheng Sean Ye return (ENOTSUP); /* MC rev does not offer online spare */ 1061e4b86885SCheng Sean Ye else if (mcp->mcp_sparecs == MC_INVALNUM) 1062e4b86885SCheng Sean Ye return (ENODEV); /* Supported, but no spare configured */ 1063e4b86885SCheng Sean Ye else if (mcp->mcp_badcs != MC_INVALNUM) 1064e4b86885SCheng Sean Ye return (EBUSY); /* Spare already swapped in */ 1065e4b86885SCheng Sean Ye else if (csnum == mcp->mcp_sparecs) 1066e4b86885SCheng Sean Ye return (EINVAL); /* Can't spare the spare! */ 1067e4b86885SCheng Sean Ye 1068e4b86885SCheng Sean Ye for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) { 1069e4b86885SCheng Sean Ye if (mccs->mccs_props.csp_num == csnum) 1070e4b86885SCheng Sean Ye break; 1071e4b86885SCheng Sean Ye } 1072e4b86885SCheng Sean Ye if (mccs == NULL) 1073e4b86885SCheng Sean Ye return (EINVAL); /* nominated bad CS does not exist */ 1074e4b86885SCheng Sean Ye 1075e4b86885SCheng Sean Ye /* 1076e4b86885SCheng Sean Ye * If the DRAM Scrubber is not enabled then the swap cannot succeed. 1077e4b86885SCheng Sean Ye */ 1078e4b86885SCheng Sean Ye MCREG_VAL32(&scrubctl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, 1079e4b86885SCheng Sean Ye MC_CTL_REG_SCRUBCTL); 1080e4b86885SCheng Sean Ye if (MCREG_FIELD_CMN(&scrubctl, DramScrub) == 0) 1081e4b86885SCheng Sean Ye return (ENODEV); /* DRAM scrubber not enabled */ 1082e4b86885SCheng Sean Ye 1083e4b86885SCheng Sean Ye /* 1084e4b86885SCheng Sean Ye * Read Online Spare Comtrol Register again, just in case our 1085e4b86885SCheng Sean Ye * state does not reflect reality. 1086e4b86885SCheng Sean Ye */ 1087e4b86885SCheng Sean Ye MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, 1088e4b86885SCheng Sean Ye MC_CTL_REG_SPARECTL); 1089e4b86885SCheng Sean Ye 1090e4b86885SCheng Sean Ye if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) 1091e4b86885SCheng Sean Ye return (EBUSY); 1092e4b86885SCheng Sean Ye 1093e4b86885SCheng Sean Ye /* Write to the BadDramCs field */ 1094e4b86885SCheng Sean Ye MCREG_FIELD_F_revFG(&sparectl, BadDramCs) = csnum; 1095e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, 1096e4b86885SCheng Sean Ye MCREG_VAL32(&sparectl)); 1097e4b86885SCheng Sean Ye 1098e4b86885SCheng Sean Ye /* And request that the swap to the spare start */ 1099e4b86885SCheng Sean Ye MCREG_FIELD_F_revFG(&sparectl, SwapEn) = 1; 1100e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, 1101e4b86885SCheng Sean Ye MCREG_VAL32(&sparectl)); 1102e4b86885SCheng Sean Ye 1103e4b86885SCheng Sean Ye /* 1104e4b86885SCheng Sean Ye * Poll for SwapDone - we have disabled notification by interrupt. 1105e4b86885SCheng Sean Ye * Swap takes "several CPU cycles, depending on the DRAM speed, but 1106e4b86885SCheng Sean Ye * is performed in the background" (Family 0Fh Bios Porting Guide). 1107e4b86885SCheng Sean Ye * We're in a slow ioctl path so there is no harm in waiting around 1108e4b86885SCheng Sean Ye * a bit - consumers of the ioctl must be aware that it may take 1109e4b86885SCheng Sean Ye * a moment. We will poll for up to mc_swapdonetime seconds, 1110e4b86885SCheng Sean Ye * limiting that to 120s. 1111e4b86885SCheng Sean Ye * 1112e4b86885SCheng Sean Ye * The swap is performed by the DRAM scrubber (which must be enabled) 1113e4b86885SCheng Sean Ye * whose scrub rate is accelerated for the duration of the swap. 1114e4b86885SCheng Sean Ye * The maximum swap rate is 40.0ns per 64 bytes, so the maximum 1115e4b86885SCheng Sean Ye * supported cs size of 16GB would take 10.7s at that max rate 1116e4b86885SCheng Sean Ye * of 25000000 scrubs/second. 1117e4b86885SCheng Sean Ye */ 1118e4b86885SCheng Sean Ye tmax = gethrtime() + MIN(mc_swapdonetime, 120) * 1000000000ULL; 1119e4b86885SCheng Sean Ye do { 1120e4b86885SCheng Sean Ye if (i++ < 20) 1121e4b86885SCheng Sean Ye delay(drv_usectohz(100000)); /* 0.1s for up to 2s */ 1122e4b86885SCheng Sean Ye else 1123e4b86885SCheng Sean Ye delay(drv_usectohz(500000)); /* 0.5s */ 1124e4b86885SCheng Sean Ye 1125e4b86885SCheng Sean Ye MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, 1126e4b86885SCheng Sean Ye MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 1127e4b86885SCheng Sean Ye } while (!MCREG_FIELD_F_revFG(&sparectl, SwapDone) && 1128e4b86885SCheng Sean Ye gethrtime() < tmax); 1129e4b86885SCheng Sean Ye 1130e4b86885SCheng Sean Ye if (!MCREG_FIELD_F_revFG(&sparectl, SwapDone)) 1131e4b86885SCheng Sean Ye return (ETIME); /* Operation timed out */ 1132e4b86885SCheng Sean Ye 1133e4b86885SCheng Sean Ye mcp->mcp_badcs = csnum; 1134e4b86885SCheng Sean Ye mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl); 1135e4b86885SCheng Sean Ye mc->mc_spareswaptime = gethrtime(); 1136e4b86885SCheng Sean Ye 1137e4b86885SCheng Sean Ye return (0); 1138e4b86885SCheng Sean Ye } 1139e4b86885SCheng Sean Ye 1140e4b86885SCheng Sean Ye /*ARGSUSED*/ 1141e4b86885SCheng Sean Ye static int 1142e4b86885SCheng Sean Ye mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 1143e4b86885SCheng Sean Ye { 1144e4b86885SCheng Sean Ye int rc = 0; 1145e4b86885SCheng Sean Ye mc_t *mc; 1146e4b86885SCheng Sean Ye 1147e4b86885SCheng Sean Ye if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT && 1148e4b86885SCheng Sean Ye cmd != MC_IOC_ONLINESPARE_EN) 1149e4b86885SCheng Sean Ye return (EINVAL); 1150e4b86885SCheng Sean Ye 1151e4b86885SCheng Sean Ye rw_enter(&mc_lock, RW_READER); 1152e4b86885SCheng Sean Ye 1153e4b86885SCheng Sean Ye if ((mc = mc_lookup_by_chipid(getminor(dev))) == NULL) { 1154e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1155e4b86885SCheng Sean Ye return (EINVAL); 1156e4b86885SCheng Sean Ye } 1157e4b86885SCheng Sean Ye 1158e4b86885SCheng Sean Ye switch (cmd) { 1159e4b86885SCheng Sean Ye case MC_IOC_SNAPSHOT_INFO: { 1160e4b86885SCheng Sean Ye mc_snapshot_info_t mcs; 1161e4b86885SCheng Sean Ye 1162e4b86885SCheng Sean Ye if (mc_snapshot_update(mc) < 0) { 1163e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1164e4b86885SCheng Sean Ye return (EIO); 1165e4b86885SCheng Sean Ye } 1166e4b86885SCheng Sean Ye 1167e4b86885SCheng Sean Ye mcs.mcs_size = mc->mc_snapshotsz; 1168e4b86885SCheng Sean Ye mcs.mcs_gen = mc->mc_snapshotgen; 1169e4b86885SCheng Sean Ye 1170e4b86885SCheng Sean Ye if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t), 1171e4b86885SCheng Sean Ye mode) < 0) 1172e4b86885SCheng Sean Ye rc = EFAULT; 1173e4b86885SCheng Sean Ye break; 1174e4b86885SCheng Sean Ye } 1175e4b86885SCheng Sean Ye 1176e4b86885SCheng Sean Ye case MC_IOC_SNAPSHOT: 1177e4b86885SCheng Sean Ye if (mc_snapshot_update(mc) < 0) { 1178e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1179e4b86885SCheng Sean Ye return (EIO); 1180e4b86885SCheng Sean Ye } 1181e4b86885SCheng Sean Ye 1182e4b86885SCheng Sean Ye if (ddi_copyout(mc->mc_snapshot, (void *)arg, mc->mc_snapshotsz, 1183e4b86885SCheng Sean Ye mode) < 0) 1184e4b86885SCheng Sean Ye rc = EFAULT; 1185e4b86885SCheng Sean Ye break; 1186e4b86885SCheng Sean Ye 1187e4b86885SCheng Sean Ye case MC_IOC_ONLINESPARE_EN: 1188e4b86885SCheng Sean Ye if (drv_priv(credp) != 0) { 1189e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1190e4b86885SCheng Sean Ye return (EPERM); 1191e4b86885SCheng Sean Ye } 1192e4b86885SCheng Sean Ye 1193e4b86885SCheng Sean Ye if (!rw_tryupgrade(&mc_lock)) { 1194e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1195e4b86885SCheng Sean Ye return (EAGAIN); 1196e4b86885SCheng Sean Ye } 1197e4b86885SCheng Sean Ye 1198e4b86885SCheng Sean Ye if ((rc = mc_onlinespare(mc, (int)arg)) == 0) { 1199e4b86885SCheng Sean Ye mc_snapshot_destroy(mc); 1200e4b86885SCheng Sean Ye nvlist_free(mc->mc_nvl); 1201e4b86885SCheng Sean Ye mc->mc_nvl = mc_nvl_create(mc); 1202e4b86885SCheng Sean Ye } 1203e4b86885SCheng Sean Ye 1204e4b86885SCheng Sean Ye break; 1205e4b86885SCheng Sean Ye } 1206e4b86885SCheng Sean Ye 1207e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1208e4b86885SCheng Sean Ye 1209e4b86885SCheng Sean Ye return (rc); 1210e4b86885SCheng Sean Ye } 1211e4b86885SCheng Sean Ye 1212e4b86885SCheng Sean Ye static struct cb_ops mc_cb_ops = { 1213e4b86885SCheng Sean Ye mc_open, 1214e4b86885SCheng Sean Ye mc_close, 1215e4b86885SCheng Sean Ye nodev, /* not a block driver */ 1216e4b86885SCheng Sean Ye nodev, /* no print routine */ 1217e4b86885SCheng Sean Ye nodev, /* no dump routine */ 1218e4b86885SCheng Sean Ye nodev, /* no read routine */ 1219e4b86885SCheng Sean Ye nodev, /* no write routine */ 1220e4b86885SCheng Sean Ye mc_ioctl, 1221e4b86885SCheng Sean Ye nodev, /* no devmap routine */ 1222e4b86885SCheng Sean Ye nodev, /* no mmap routine */ 1223e4b86885SCheng Sean Ye nodev, /* no segmap routine */ 1224e4b86885SCheng Sean Ye nochpoll, /* no chpoll routine */ 1225e4b86885SCheng Sean Ye ddi_prop_op, 1226e4b86885SCheng Sean Ye 0, /* not a STREAMS driver */ 1227e4b86885SCheng Sean Ye D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 1228e4b86885SCheng Sean Ye }; 1229e4b86885SCheng Sean Ye 1230e4b86885SCheng Sean Ye /*ARGSUSED*/ 1231e4b86885SCheng Sean Ye static int 1232e4b86885SCheng Sean Ye mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 1233e4b86885SCheng Sean Ye { 1234e4b86885SCheng Sean Ye int rc = DDI_SUCCESS; 1235e4b86885SCheng Sean Ye mc_t *mc; 1236e4b86885SCheng Sean Ye 1237e4b86885SCheng Sean Ye if (infocmd != DDI_INFO_DEVT2DEVINFO && 1238e4b86885SCheng Sean Ye infocmd != DDI_INFO_DEVT2INSTANCE) { 1239e4b86885SCheng Sean Ye *result = NULL; 1240e4b86885SCheng Sean Ye return (DDI_FAILURE); 1241e4b86885SCheng Sean Ye } 1242e4b86885SCheng Sean Ye 1243e4b86885SCheng Sean Ye rw_enter(&mc_lock, RW_READER); 1244e4b86885SCheng Sean Ye 1245e4b86885SCheng Sean Ye if ((mc = mc_lookup_by_chipid(getminor((dev_t)arg))) == NULL || 1246e4b86885SCheng Sean Ye mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi == NULL) { 1247e4b86885SCheng Sean Ye rc = DDI_FAILURE; 1248e4b86885SCheng Sean Ye } else if (infocmd == DDI_INFO_DEVT2DEVINFO) { 1249e4b86885SCheng Sean Ye *result = mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_devi; 1250e4b86885SCheng Sean Ye } else { 1251e4b86885SCheng Sean Ye *result = (void *)(uintptr_t) 1252e4b86885SCheng Sean Ye mc->mc_funcs[MC_FUNC_DEVIMAP].mcf_instance; 1253e4b86885SCheng Sean Ye } 1254e4b86885SCheng Sean Ye 1255e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1256e4b86885SCheng Sean Ye 1257e4b86885SCheng Sean Ye return (rc); 1258e4b86885SCheng Sean Ye } 1259e4b86885SCheng Sean Ye 1260e4b86885SCheng Sean Ye /*ARGSUSED2*/ 1261e4b86885SCheng Sean Ye static int 1262e4b86885SCheng Sean Ye mc_fm_handle(dev_info_t *dip, ddi_fm_error_t *fmerr, const void *arg) 1263e4b86885SCheng Sean Ye { 1264e4b86885SCheng Sean Ye pci_ereport_post(dip, fmerr, NULL); 1265e4b86885SCheng Sean Ye return (fmerr->fme_status); 1266e4b86885SCheng Sean Ye } 1267e4b86885SCheng Sean Ye 1268e4b86885SCheng Sean Ye static void 1269e4b86885SCheng Sean Ye mc_fm_init(dev_info_t *dip) 1270e4b86885SCheng Sean Ye { 1271e4b86885SCheng Sean Ye int fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE; 1272e4b86885SCheng Sean Ye ddi_fm_init(dip, &fmcap, NULL); 1273e4b86885SCheng Sean Ye pci_ereport_setup(dip); 1274e4b86885SCheng Sean Ye ddi_fm_handler_register(dip, mc_fm_handle, NULL); 1275e4b86885SCheng Sean Ye } 1276e4b86885SCheng Sean Ye 1277074bb90dSTom Pothier static void 1278074bb90dSTom Pothier mc_read_smbios(mc_t *mc, dev_info_t *dip) 1279074bb90dSTom Pothier { 1280074bb90dSTom Pothier 1281074bb90dSTom Pothier uint16_t bdf; 1282f32a9dd1SSrihari Venkatesan pci_regspec_t *pci_rp = NULL; 1283074bb90dSTom Pothier uint32_t phys_hi; 1284f32a9dd1SSrihari Venkatesan int m = 0; 1285074bb90dSTom Pothier uint_t chip_inst; 1286074bb90dSTom Pothier int rc = 0; 1287074bb90dSTom Pothier 1288074bb90dSTom Pothier if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 1289074bb90dSTom Pothier (caddr_t)&pci_rp, &m) == DDI_SUCCESS) { 1290074bb90dSTom Pothier phys_hi = pci_rp->pci_phys_hi; 1291074bb90dSTom Pothier bdf = (uint16_t)(PCI_REG_BDFR_G(phys_hi) >> 1292074bb90dSTom Pothier PCI_REG_FUNC_SHIFT); 1293f32a9dd1SSrihari Venkatesan kmem_free(pci_rp, m); 1294f32a9dd1SSrihari Venkatesan pci_rp = NULL; 1295074bb90dSTom Pothier 1296074bb90dSTom Pothier rc = fm_smb_mc_chipinst(bdf, &chip_inst); 1297074bb90dSTom Pothier if (rc == 0) { 1298074bb90dSTom Pothier mc->smb_chipid = chip_inst; 1299074bb90dSTom Pothier } else { 1300074bb90dSTom Pothier #ifdef DEBUG 1301*97689f66SSrihari Venkatesan cmn_err(CE_NOTE, "!mc read smbios chip info failed"); 1302074bb90dSTom Pothier #endif /* DEBUG */ 1303074bb90dSTom Pothier return; 1304074bb90dSTom Pothier } 1305074bb90dSTom Pothier mc->smb_bboard = fm_smb_mc_bboards(bdf); 1306074bb90dSTom Pothier #ifdef DEBUG 1307074bb90dSTom Pothier if (mc->smb_bboard == NULL) 1308074bb90dSTom Pothier cmn_err(CE_NOTE, 1309*97689f66SSrihari Venkatesan "!mc read smbios base boards info failed"); 1310074bb90dSTom Pothier #endif /* DEBUG */ 1311074bb90dSTom Pothier } 1312f32a9dd1SSrihari Venkatesan 1313f32a9dd1SSrihari Venkatesan if (pci_rp != NULL) 1314f32a9dd1SSrihari Venkatesan kmem_free(pci_rp, m); 1315074bb90dSTom Pothier } 1316074bb90dSTom Pothier 1317e4b86885SCheng Sean Ye /*ARGSUSED*/ 1318e4b86885SCheng Sean Ye static int 1319e4b86885SCheng Sean Ye mc_create_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) 1320e4b86885SCheng Sean Ye { 1321e4b86885SCheng Sean Ye chipid_t chipid = *((chipid_t *)arg1); 1322e4b86885SCheng Sean Ye cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2; 1323e4b86885SCheng Sean Ye 1324e4b86885SCheng Sean Ye if (cmi_hdl_chipid(whdl) == chipid) { 1325e4b86885SCheng Sean Ye cmi_hdl_hold(whdl); /* short-term hold */ 1326e4b86885SCheng Sean Ye *hdlp = whdl; 1327e4b86885SCheng Sean Ye return (CMI_HDL_WALK_DONE); 1328e4b86885SCheng Sean Ye } else { 1329e4b86885SCheng Sean Ye return (CMI_HDL_WALK_NEXT); 1330e4b86885SCheng Sean Ye } 1331e4b86885SCheng Sean Ye } 1332e4b86885SCheng Sean Ye 1333e4b86885SCheng Sean Ye static mc_t * 1334074bb90dSTom Pothier mc_create(chipid_t chipid, dev_info_t *dip) 1335e4b86885SCheng Sean Ye { 1336e4b86885SCheng Sean Ye mc_t *mc; 1337e4b86885SCheng Sean Ye cmi_hdl_t hdl = NULL; 1338e4b86885SCheng Sean Ye 1339e4b86885SCheng Sean Ye ASSERT(RW_WRITE_HELD(&mc_lock)); 1340e4b86885SCheng Sean Ye 1341e4b86885SCheng Sean Ye /* 1342e4b86885SCheng Sean Ye * Find a handle for one of a chip's CPU. 1343e4b86885SCheng Sean Ye * 1344e4b86885SCheng Sean Ye * We can use one of the chip's CPUs since all cores 1345e4b86885SCheng Sean Ye * of a chip share the same revision and socket type. 1346e4b86885SCheng Sean Ye */ 1347e4b86885SCheng Sean Ye cmi_hdl_walk(mc_create_cb, (void *)&chipid, (void *)&hdl, NULL); 1348e4b86885SCheng Sean Ye if (hdl == NULL) 1349e4b86885SCheng Sean Ye return (NULL); /* no cpu for this chipid found! */ 1350e4b86885SCheng Sean Ye 1351e4b86885SCheng Sean Ye mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP); 1352e4b86885SCheng Sean Ye 1353e4b86885SCheng Sean Ye mc->mc_hdr.mch_type = MC_NT_MC; 1354e4b86885SCheng Sean Ye mc->mc_props.mcp_num = chipid; 1355e4b86885SCheng Sean Ye mc->mc_props.mcp_sparecs = MC_INVALNUM; 1356e4b86885SCheng Sean Ye mc->mc_props.mcp_badcs = MC_INVALNUM; 1357e4b86885SCheng Sean Ye 1358e4b86885SCheng Sean Ye mc->mc_props.mcp_rev = cmi_hdl_chiprev(hdl); 1359e4b86885SCheng Sean Ye mc->mc_revname = cmi_hdl_chiprevstr(hdl); 1360e4b86885SCheng Sean Ye mc->mc_socket = cmi_hdl_getsockettype(hdl); 1361e4b86885SCheng Sean Ye 1362074bb90dSTom Pothier mc_read_smbios(mc, dip); 1363074bb90dSTom Pothier 1364e4b86885SCheng Sean Ye if (mc_list == NULL) 1365e4b86885SCheng Sean Ye mc_list = mc; 1366e4b86885SCheng Sean Ye if (mc_last != NULL) 1367e4b86885SCheng Sean Ye mc_last->mc_next = mc; 1368e4b86885SCheng Sean Ye 1369e4b86885SCheng Sean Ye mc->mc_next = NULL; 1370e4b86885SCheng Sean Ye mc_last = mc; 1371e4b86885SCheng Sean Ye 1372e4b86885SCheng Sean Ye cmi_hdl_rele(hdl); 1373e4b86885SCheng Sean Ye 1374e4b86885SCheng Sean Ye return (mc); 1375e4b86885SCheng Sean Ye } 1376e4b86885SCheng Sean Ye 1377e4b86885SCheng Sean Ye /* 1378e4b86885SCheng Sean Ye * Return the maximum scrubbing rate between r1 and r2, where r2 is extracted 1379e4b86885SCheng Sean Ye * from the specified 'cfg' register value using 'mask' and 'shift'. If a 1380e4b86885SCheng Sean Ye * value is zero, scrubbing is off so return the opposite value. Otherwise 1381e4b86885SCheng Sean Ye * the maximum rate is the smallest non-zero value of the two values. 1382e4b86885SCheng Sean Ye */ 1383e4b86885SCheng Sean Ye static uint32_t 1384e4b86885SCheng Sean Ye mc_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift) 1385e4b86885SCheng Sean Ye { 1386e4b86885SCheng Sean Ye uint32_t r2 = (cfg & mask) >> shift; 1387e4b86885SCheng Sean Ye 1388e4b86885SCheng Sean Ye if (r1 != 0 && r2 != 0) 1389e4b86885SCheng Sean Ye return (MIN(r1, r2)); 1390e4b86885SCheng Sean Ye 1391e4b86885SCheng Sean Ye return (r1 ? r1 : r2); 1392e4b86885SCheng Sean Ye } 1393e4b86885SCheng Sean Ye 1394e4b86885SCheng Sean Ye 1395e4b86885SCheng Sean Ye /* 1396e4b86885SCheng Sean Ye * Enable the memory scrubber. We must use the mc_pcicfg_{get32,put32}_nohdl 1397e4b86885SCheng Sean Ye * interfaces since we do not bind to function 3. 1398e4b86885SCheng Sean Ye */ 1399e4b86885SCheng Sean Ye cmi_errno_t 1400e4b86885SCheng Sean Ye mc_scrubber_enable(mc_t *mc) 1401e4b86885SCheng Sean Ye { 1402e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 140392420f62SSurya Prakki chipid_t chipid = (chipid_t)mcp->mcp_num; 140492420f62SSurya Prakki uint32_t rev = (uint32_t)mcp->mcp_rev; 1405e4b86885SCheng Sean Ye mc_cfgregs_t *mcr = &mc->mc_cfgregs; 1406e4b86885SCheng Sean Ye union mcreg_scrubctl scrubctl; 1407e4b86885SCheng Sean Ye union mcreg_dramscrublo dalo; 1408e4b86885SCheng Sean Ye union mcreg_dramscrubhi dahi; 1409e4b86885SCheng Sean Ye 1410e4b86885SCheng Sean Ye mcr->mcr_scrubctl = MCREG_VAL32(&scrubctl) = 1411e4b86885SCheng Sean Ye mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL); 1412e4b86885SCheng Sean Ye 1413e4b86885SCheng Sean Ye mcr->mcr_scrubaddrlo = MCREG_VAL32(&dalo) = 1414e4b86885SCheng Sean Ye mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO); 1415e4b86885SCheng Sean Ye 1416e4b86885SCheng Sean Ye mcr->mcr_scrubaddrhi = MCREG_VAL32(&dahi) = 1417e4b86885SCheng Sean Ye mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI); 1418e4b86885SCheng Sean Ye 1419e4b86885SCheng Sean Ye if (mc_scrub_policy == MC_SCRUB_BIOSDEFAULT) 1420e4b86885SCheng Sean Ye return (MCREG_FIELD_CMN(&scrubctl, DramScrub) != 1421e4b86885SCheng Sean Ye AMD_NB_SCRUBCTL_RATE_NONE ? 1422e4b86885SCheng Sean Ye CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB); 1423e4b86885SCheng Sean Ye 1424e4b86885SCheng Sean Ye /* 1425e4b86885SCheng Sean Ye * Disable DRAM scrubbing while we fiddle. 1426e4b86885SCheng Sean Ye */ 1427e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&scrubctl, DramScrub) = AMD_NB_SCRUBCTL_RATE_NONE; 1428e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, 1429e4b86885SCheng Sean Ye MCREG_VAL32(&scrubctl)); 1430e4b86885SCheng Sean Ye 1431e4b86885SCheng Sean Ye /* 1432e4b86885SCheng Sean Ye * Setup DRAM Scrub Address Low and High registers for the 1433e4b86885SCheng Sean Ye * base address of this node, and to select srubber redirect. 1434e4b86885SCheng Sean Ye */ 1435e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&dalo, ScrubReDirEn) = 1; 1436e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&dalo, ScrubAddrLo) = 1437e4b86885SCheng Sean Ye AMD_NB_SCRUBADDR_MKLO(mcp->mcp_base); 1438e4b86885SCheng Sean Ye 1439e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&dahi, ScrubAddrHi) = 1440e4b86885SCheng Sean Ye AMD_NB_SCRUBADDR_MKHI(mcp->mcp_base); 1441e4b86885SCheng Sean Ye 1442e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO, 1443e4b86885SCheng Sean Ye MCREG_VAL32(&dalo)); 1444e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI, 1445e4b86885SCheng Sean Ye MCREG_VAL32(&dahi)); 1446e4b86885SCheng Sean Ye 1447e4b86885SCheng Sean Ye if (mc_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) { 1448e4b86885SCheng Sean Ye cmn_err(CE_WARN, "mc_scrub_rate_dram is too large; " 1449e4b86885SCheng Sean Ye "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); 1450e4b86885SCheng Sean Ye mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX; 1451e4b86885SCheng Sean Ye } 1452e4b86885SCheng Sean Ye 1453e4b86885SCheng Sean Ye switch (mc_scrub_policy) { 1454e4b86885SCheng Sean Ye case MC_SCRUB_FIXED: 1455e4b86885SCheng Sean Ye /* Use the system value checked above */ 1456e4b86885SCheng Sean Ye break; 1457e4b86885SCheng Sean Ye 1458e4b86885SCheng Sean Ye default: 1459e4b86885SCheng Sean Ye cmn_err(CE_WARN, "Unknown mc_scrub_policy value %d - " 1460e4b86885SCheng Sean Ye "using default policy of MC_SCRUB_MAX", mc_scrub_policy); 1461e4b86885SCheng Sean Ye /*FALLTHRU*/ 1462e4b86885SCheng Sean Ye 1463e4b86885SCheng Sean Ye case MC_SCRUB_MAX: 1464e4b86885SCheng Sean Ye mc_scrub_rate_dram = mc_scrubber_max(mc_scrub_rate_dram, 1465e4b86885SCheng Sean Ye mcr->mcr_scrubctl, AMD_NB_SCRUBCTL_DRAM_MASK, 1466e4b86885SCheng Sean Ye AMD_NB_SCRUBCTL_DRAM_SHIFT); 1467e4b86885SCheng Sean Ye break; 1468e4b86885SCheng Sean Ye } 1469e4b86885SCheng Sean Ye 1470e4b86885SCheng Sean Ye /* 147192420f62SSurya Prakki * OPTERON_ERRATUM_99: 1472e4b86885SCheng Sean Ye * This erratum applies on revisions D and earlier. 14737d586c73SSurya Prakki * This erratum also applies on revisions E and later, 14747d586c73SSurya Prakki * if BIOS uses chip-select hoisting instead of DRAM hole 14757d586c73SSurya Prakki * mapping. 1476e4b86885SCheng Sean Ye * 14777d586c73SSurya Prakki * Do not enable the dram scrubber if the chip-select ranges 1478e4b86885SCheng Sean Ye * for the node are not contiguous. 1479e4b86885SCheng Sean Ye */ 1480e4b86885SCheng Sean Ye if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE && 148192420f62SSurya Prakki mc->mc_csdiscontig) { 1482e4b86885SCheng Sean Ye cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " 1483e4b86885SCheng Sean Ye "%s chip %d because DRAM hole is present on this node", 1484e4b86885SCheng Sean Ye mc->mc_revname, chipid); 1485e4b86885SCheng Sean Ye mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE; 1486e4b86885SCheng Sean Ye } 1487e4b86885SCheng Sean Ye 1488e4b86885SCheng Sean Ye /* 148992420f62SSurya Prakki * OPTERON_ERRATUM_101: 1490e4b86885SCheng Sean Ye * This erratum applies on revisions D and earlier. 1491e4b86885SCheng Sean Ye * 1492e4b86885SCheng Sean Ye * If the DRAM Base Address register's IntlvEn field indicates that 1493e4b86885SCheng Sean Ye * node interleaving is enabled, we must disable the DRAM scrubber 1494e4b86885SCheng Sean Ye * and return zero to indicate that Solaris should use s/w instead. 1495e4b86885SCheng Sean Ye */ 1496e4b86885SCheng Sean Ye if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE && 1497e4b86885SCheng Sean Ye mcp->mcp_ilen != 0 && 1498e4b86885SCheng Sean Ye !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { 1499e4b86885SCheng Sean Ye cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " 1500e4b86885SCheng Sean Ye "%s chip %d because DRAM memory is node-interleaved", 1501e4b86885SCheng Sean Ye mc->mc_revname, chipid); 1502e4b86885SCheng Sean Ye mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE; 1503e4b86885SCheng Sean Ye } 1504e4b86885SCheng Sean Ye 1505e4b86885SCheng Sean Ye if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE) { 1506e4b86885SCheng Sean Ye MCREG_FIELD_CMN(&scrubctl, DramScrub) = mc_scrub_rate_dram; 1507e4b86885SCheng Sean Ye mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, 1508e4b86885SCheng Sean Ye MCREG_VAL32(&scrubctl)); 1509e4b86885SCheng Sean Ye } 1510e4b86885SCheng Sean Ye 1511e4b86885SCheng Sean Ye return (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE ? 1512e4b86885SCheng Sean Ye CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB); 1513e4b86885SCheng Sean Ye } 1514e4b86885SCheng Sean Ye 1515e4b86885SCheng Sean Ye /*ARGSUSED*/ 1516e4b86885SCheng Sean Ye static int 1517e4b86885SCheng Sean Ye mc_attach_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) 1518e4b86885SCheng Sean Ye { 1519e4b86885SCheng Sean Ye mc_t *mc = (mc_t *)arg1; 1520e4b86885SCheng Sean Ye mcamd_prop_t chipid = *((mcamd_prop_t *)arg2); 1521e4b86885SCheng Sean Ye 1522e4b86885SCheng Sean Ye if (cmi_hdl_chipid(whdl) == chipid) { 1523e4b86885SCheng Sean Ye mcamd_mc_register(whdl, mc); 1524e4b86885SCheng Sean Ye } 1525e4b86885SCheng Sean Ye 1526e4b86885SCheng Sean Ye return (CMI_HDL_WALK_NEXT); 1527e4b86885SCheng Sean Ye } 1528e4b86885SCheng Sean Ye 1529e4b86885SCheng Sean Ye static int mc_sw_scrub_disabled = 0; 1530e4b86885SCheng Sean Ye 1531e4b86885SCheng Sean Ye static int 1532e4b86885SCheng Sean Ye mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1533e4b86885SCheng Sean Ye { 1534e4b86885SCheng Sean Ye mc_pcicfg_hdl_t cfghdl; 1535e4b86885SCheng Sean Ye const mc_bind_map_t *bm; 1536e4b86885SCheng Sean Ye const char *bindnm; 1537e4b86885SCheng Sean Ye char *unitstr = NULL; 1538e4b86885SCheng Sean Ye enum mc_funcnum func; 1539e4b86885SCheng Sean Ye long unitaddr; 1540e4b86885SCheng Sean Ye int chipid, rc; 1541e4b86885SCheng Sean Ye mc_t *mc; 1542e4b86885SCheng Sean Ye 1543e4b86885SCheng Sean Ye /* 1544e4b86885SCheng Sean Ye * This driver has no hardware state, but does 1545e4b86885SCheng Sean Ye * claim to have a reg property, so it will be 1546e4b86885SCheng Sean Ye * called on suspend. It is probably better to 1547e4b86885SCheng Sean Ye * make sure it doesn't get called on suspend, 1548e4b86885SCheng Sean Ye * but it is just as easy to make sure we just 1549e4b86885SCheng Sean Ye * return DDI_SUCCESS if called. 1550e4b86885SCheng Sean Ye */ 1551e4b86885SCheng Sean Ye if (cmd == DDI_RESUME) 1552e4b86885SCheng Sean Ye return (DDI_SUCCESS); 1553e4b86885SCheng Sean Ye 1554e4b86885SCheng Sean Ye if (cmd != DDI_ATTACH || mc_no_attach != 0) 1555e4b86885SCheng Sean Ye return (DDI_FAILURE); 1556e4b86885SCheng Sean Ye 1557e4b86885SCheng Sean Ye bindnm = ddi_binding_name(dip); 1558e4b86885SCheng Sean Ye for (bm = mc_bind_map; bm->bm_bindnm != NULL; bm++) { 1559e4b86885SCheng Sean Ye if (strcmp(bindnm, bm->bm_bindnm) == 0) { 1560e4b86885SCheng Sean Ye func = bm->bm_func; 1561e4b86885SCheng Sean Ye break; 1562e4b86885SCheng Sean Ye } 1563e4b86885SCheng Sean Ye } 1564e4b86885SCheng Sean Ye 1565e4b86885SCheng Sean Ye if (bm->bm_bindnm == NULL) 1566e4b86885SCheng Sean Ye return (DDI_FAILURE); 1567e4b86885SCheng Sean Ye 1568e4b86885SCheng Sean Ye /* 1569e4b86885SCheng Sean Ye * We need the device number, which corresponds to the processor node 1570e4b86885SCheng Sean Ye * number plus 24. The node number can then be used to associate this 1571e4b86885SCheng Sean Ye * memory controller device with a given processor chip. 1572e4b86885SCheng Sean Ye */ 1573e4b86885SCheng Sean Ye if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 1574e4b86885SCheng Sean Ye DDI_PROP_DONTPASS, "unit-address", &unitstr) != DDI_PROP_SUCCESS) { 1575e4b86885SCheng Sean Ye cmn_err(CE_WARN, "failed to find unit-address for %s", bindnm); 1576e4b86885SCheng Sean Ye return (DDI_FAILURE); 1577e4b86885SCheng Sean Ye } 1578e4b86885SCheng Sean Ye 1579e4b86885SCheng Sean Ye rc = ddi_strtol(unitstr, NULL, 16, &unitaddr); 1580e4b86885SCheng Sean Ye ASSERT(rc == 0 && unitaddr >= MC_AMD_DEV_OFFSET); 1581e4b86885SCheng Sean Ye 1582e4b86885SCheng Sean Ye if (rc != 0 || unitaddr < MC_AMD_DEV_OFFSET) { 1583e4b86885SCheng Sean Ye cmn_err(CE_WARN, "failed to parse unit address %s for %s\n", 1584e4b86885SCheng Sean Ye unitstr, bindnm); 1585e4b86885SCheng Sean Ye ddi_prop_free(unitstr); 1586e4b86885SCheng Sean Ye return (DDI_FAILURE); 1587e4b86885SCheng Sean Ye } 1588e4b86885SCheng Sean Ye ddi_prop_free(unitstr); 1589e4b86885SCheng Sean Ye 1590e4b86885SCheng Sean Ye chipid = unitaddr - MC_AMD_DEV_OFFSET; 1591e4b86885SCheng Sean Ye 1592e4b86885SCheng Sean Ye rw_enter(&mc_lock, RW_WRITER); 1593e4b86885SCheng Sean Ye 1594e4b86885SCheng Sean Ye for (mc = mc_list; mc != NULL; mc = mc->mc_next) { 1595e4b86885SCheng Sean Ye if (mc->mc_props.mcp_num == chipid) 1596e4b86885SCheng Sean Ye break; 1597e4b86885SCheng Sean Ye } 1598e4b86885SCheng Sean Ye 1599e4b86885SCheng Sean Ye /* Integrate this memory controller device into existing set */ 1600e4b86885SCheng Sean Ye if (mc == NULL) { 1601074bb90dSTom Pothier mc = mc_create(chipid, dip); 1602e4b86885SCheng Sean Ye 1603e4b86885SCheng Sean Ye if (mc == NULL) { 1604e4b86885SCheng Sean Ye /* 1605e4b86885SCheng Sean Ye * We don't complain here because this is a legitimate 1606e4b86885SCheng Sean Ye * path for MP systems. On those machines, we'll attach 1607e4b86885SCheng Sean Ye * before all CPUs have been initialized, and thus the 1608e4b86885SCheng Sean Ye * chip verification in mc_create will fail. We'll be 1609e4b86885SCheng Sean Ye * reattached later for those CPUs. 1610e4b86885SCheng Sean Ye */ 1611e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1612e4b86885SCheng Sean Ye return (DDI_FAILURE); 1613e4b86885SCheng Sean Ye } 1614e4b86885SCheng Sean Ye } else { 1615e4b86885SCheng Sean Ye mc_snapshot_destroy(mc); 1616e4b86885SCheng Sean Ye } 1617e4b86885SCheng Sean Ye 1618e4b86885SCheng Sean Ye /* Beyond this point, we're committed to creating this node */ 1619e4b86885SCheng Sean Ye 1620e4b86885SCheng Sean Ye mc_fm_init(dip); 1621e4b86885SCheng Sean Ye 1622e4b86885SCheng Sean Ye ASSERT(mc->mc_funcs[func].mcf_devi == NULL); 1623e4b86885SCheng Sean Ye mc->mc_funcs[func].mcf_devi = dip; 1624e4b86885SCheng Sean Ye mc->mc_funcs[func].mcf_instance = ddi_get_instance(dip); 1625e4b86885SCheng Sean Ye 1626e4b86885SCheng Sean Ye mc->mc_ref++; 1627e4b86885SCheng Sean Ye 1628e4b86885SCheng Sean Ye /* 1629e4b86885SCheng Sean Ye * Add the common properties to this node, and then add any properties 1630e4b86885SCheng Sean Ye * that are specific to this node based upon its configuration space. 1631e4b86885SCheng Sean Ye */ 1632e4b86885SCheng Sean Ye (void) ddi_prop_update_string(DDI_DEV_T_NONE, 1633e4b86885SCheng Sean Ye dip, "model", (char *)bm->bm_model); 1634e4b86885SCheng Sean Ye 1635e4b86885SCheng Sean Ye (void) ddi_prop_update_int(DDI_DEV_T_NONE, 1636e4b86885SCheng Sean Ye dip, "chip-id", mc->mc_props.mcp_num); 1637e4b86885SCheng Sean Ye 1638e4b86885SCheng Sean Ye if (bm->bm_mkprops != NULL && 1639e4b86885SCheng Sean Ye mc_pcicfg_setup(mc, bm->bm_func, &cfghdl) == DDI_SUCCESS) { 1640e4b86885SCheng Sean Ye bm->bm_mkprops(cfghdl, mc); 1641e4b86885SCheng Sean Ye mc_pcicfg_teardown(cfghdl); 1642e4b86885SCheng Sean Ye } 1643e4b86885SCheng Sean Ye 1644e4b86885SCheng Sean Ye /* 1645e4b86885SCheng Sean Ye * If this is the last node to be attached for this memory controller, 1646e4b86885SCheng Sean Ye * then create the minor node, enable scrubbers, and register with 1647e4b86885SCheng Sean Ye * cpu module(s) for this chip. 1648e4b86885SCheng Sean Ye */ 1649e4b86885SCheng Sean Ye if (func == MC_FUNC_DEVIMAP) { 1650e4b86885SCheng Sean Ye mc_props_t *mcp = &mc->mc_props; 1651e4b86885SCheng Sean Ye int dram_present = 0; 1652e4b86885SCheng Sean Ye 1653e4b86885SCheng Sean Ye if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR, 1654e4b86885SCheng Sean Ye mcp->mcp_num, "ddi_mem_ctrl", 1655e4b86885SCheng Sean Ye 0) != DDI_SUCCESS) { 1656e4b86885SCheng Sean Ye cmn_err(CE_WARN, "failed to create minor node for chip " 1657e4b86885SCheng Sean Ye "%d memory controller\n", 1658e4b86885SCheng Sean Ye (chipid_t)mcp->mcp_num); 1659e4b86885SCheng Sean Ye } 1660e4b86885SCheng Sean Ye 1661e4b86885SCheng Sean Ye /* 1662e4b86885SCheng Sean Ye * Register the memory controller for every CPU of this chip. 1663e4b86885SCheng Sean Ye * 1664e4b86885SCheng Sean Ye * If there is memory present on this node and ECC is enabled 1665e4b86885SCheng Sean Ye * attempt to enable h/w memory scrubbers for this node. 1666e4b86885SCheng Sean Ye * If we are successful in enabling *any* hardware scrubbers, 1667e4b86885SCheng Sean Ye * disable the software memory scrubber. 1668e4b86885SCheng Sean Ye */ 1669e4b86885SCheng Sean Ye cmi_hdl_walk(mc_attach_cb, (void *)mc, (void *)&mcp->mcp_num, 1670e4b86885SCheng Sean Ye NULL); 1671e4b86885SCheng Sean Ye 1672e4b86885SCheng Sean Ye if (mcp->mcp_lim != mcp->mcp_base) { 1673e4b86885SCheng Sean Ye /* 1674e4b86885SCheng Sean Ye * This node may map non-dram memory alone, so we 1675e4b86885SCheng Sean Ye * must check for an enabled chip-select to be 1676e4b86885SCheng Sean Ye * sure there is dram present. 1677e4b86885SCheng Sean Ye */ 1678e4b86885SCheng Sean Ye mc_cs_t *mccs; 1679e4b86885SCheng Sean Ye 1680e4b86885SCheng Sean Ye for (mccs = mc->mc_cslist; mccs != NULL; 1681e4b86885SCheng Sean Ye mccs = mccs->mccs_next) { 1682e4b86885SCheng Sean Ye if (mccs->mccs_props.csp_csbe) { 1683e4b86885SCheng Sean Ye dram_present = 1; 1684e4b86885SCheng Sean Ye break; 1685e4b86885SCheng Sean Ye } 1686e4b86885SCheng Sean Ye } 1687e4b86885SCheng Sean Ye } 1688e4b86885SCheng Sean Ye 1689e4b86885SCheng Sean Ye if (dram_present && !mc_ecc_enabled(mc)) { 1690e4b86885SCheng Sean Ye /* 1691e4b86885SCheng Sean Ye * On a single chip system there is no point in 1692e4b86885SCheng Sean Ye * scrubbing if there is no ECC on the single node. 1693e4b86885SCheng Sean Ye * On a multichip system, necessarily Opteron using 1694e4b86885SCheng Sean Ye * registered ECC-capable DIMMs, if there is memory 1695e4b86885SCheng Sean Ye * present on a node but no ECC there then we'll assume 1696e4b86885SCheng Sean Ye * ECC is disabled for all nodes and we will not enable 1697e4b86885SCheng Sean Ye * the scrubber and wll also disable the software 1698e4b86885SCheng Sean Ye * memscrub thread. 1699e4b86885SCheng Sean Ye */ 1700e4b86885SCheng Sean Ye rc = 1; 1701e4b86885SCheng Sean Ye } else if (!dram_present) { 1702e4b86885SCheng Sean Ye /* No memory on this node - others decide memscrub */ 1703e4b86885SCheng Sean Ye rc = 0; 1704e4b86885SCheng Sean Ye } else { 1705e4b86885SCheng Sean Ye /* 1706e4b86885SCheng Sean Ye * There is memory on this node and ECC is enabled. 1707e4b86885SCheng Sean Ye * Call via the cpu module to enable memory scrubbing 1708e4b86885SCheng Sean Ye * on this node - we could call directly but then 1709e4b86885SCheng Sean Ye * we may overlap with a request to enable chip-cache 1710e4b86885SCheng Sean Ye * scrubbing. 1711e4b86885SCheng Sean Ye */ 1712e4b86885SCheng Sean Ye rc = mc_scrubber_enable(mc); 1713e4b86885SCheng Sean Ye } 1714e4b86885SCheng Sean Ye 1715e4b86885SCheng Sean Ye if (rc == CMI_SUCCESS && !mc_sw_scrub_disabled++) 1716e4b86885SCheng Sean Ye cmi_mc_sw_memscrub_disable(); 1717e4b86885SCheng Sean Ye 1718e4b86885SCheng Sean Ye mc_report_testfails(mc); 1719e4b86885SCheng Sean Ye } 1720e4b86885SCheng Sean Ye 1721e4b86885SCheng Sean Ye /* 1722e4b86885SCheng Sean Ye * Update nvlist for as far as we have gotten in attach/init. 1723e4b86885SCheng Sean Ye */ 1724e4b86885SCheng Sean Ye nvlist_free(mc->mc_nvl); 1725e4b86885SCheng Sean Ye mc->mc_nvl = mc_nvl_create(mc); 1726e4b86885SCheng Sean Ye 1727e4b86885SCheng Sean Ye rw_exit(&mc_lock); 1728e4b86885SCheng Sean Ye return (DDI_SUCCESS); 1729e4b86885SCheng Sean Ye } 1730e4b86885SCheng Sean Ye 1731e4b86885SCheng Sean Ye /*ARGSUSED*/ 1732e4b86885SCheng Sean Ye static int 1733e4b86885SCheng Sean Ye mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1734e4b86885SCheng Sean Ye { 1735e4b86885SCheng Sean Ye /* 1736e4b86885SCheng Sean Ye * See the comment about suspend in 1737e4b86885SCheng Sean Ye * mc_attach(). 1738e4b86885SCheng Sean Ye */ 1739e4b86885SCheng Sean Ye if (cmd == DDI_SUSPEND) 1740e4b86885SCheng Sean Ye return (DDI_SUCCESS); 1741e4b86885SCheng Sean Ye else 1742e4b86885SCheng Sean Ye return (DDI_FAILURE); 1743e4b86885SCheng Sean Ye } 1744e4b86885SCheng Sean Ye 174519397407SSherry Moore 1746e4b86885SCheng Sean Ye static struct dev_ops mc_ops = { 1747e4b86885SCheng Sean Ye DEVO_REV, /* devo_rev */ 1748e4b86885SCheng Sean Ye 0, /* devo_refcnt */ 1749e4b86885SCheng Sean Ye mc_getinfo, /* devo_getinfo */ 1750e4b86885SCheng Sean Ye nulldev, /* devo_identify */ 1751e4b86885SCheng Sean Ye nulldev, /* devo_probe */ 1752e4b86885SCheng Sean Ye mc_attach, /* devo_attach */ 1753e4b86885SCheng Sean Ye mc_detach, /* devo_detach */ 1754e4b86885SCheng Sean Ye nodev, /* devo_reset */ 1755e4b86885SCheng Sean Ye &mc_cb_ops, /* devo_cb_ops */ 1756e4b86885SCheng Sean Ye NULL, /* devo_bus_ops */ 175719397407SSherry Moore NULL, /* devo_power */ 175819397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 1759e4b86885SCheng Sean Ye }; 1760e4b86885SCheng Sean Ye 1761e4b86885SCheng Sean Ye static struct modldrv modldrv = { 1762e4b86885SCheng Sean Ye &mod_driverops, 1763e4b86885SCheng Sean Ye "Memory Controller for AMD processors", 1764e4b86885SCheng Sean Ye &mc_ops 1765e4b86885SCheng Sean Ye }; 1766e4b86885SCheng Sean Ye 1767e4b86885SCheng Sean Ye static struct modlinkage modlinkage = { 1768e4b86885SCheng Sean Ye MODREV_1, 1769e4b86885SCheng Sean Ye (void *)&modldrv, 1770e4b86885SCheng Sean Ye NULL 1771e4b86885SCheng Sean Ye }; 1772e4b86885SCheng Sean Ye 1773e4b86885SCheng Sean Ye int 1774e4b86885SCheng Sean Ye _init(void) 1775e4b86885SCheng Sean Ye { 1776e4b86885SCheng Sean Ye /* 1777e4b86885SCheng Sean Ye * Refuse to load if there is no PCI config space support. 1778e4b86885SCheng Sean Ye */ 1779e4b86885SCheng Sean Ye if (pci_getl_func == NULL) 1780e4b86885SCheng Sean Ye return (ENOTSUP); 1781e4b86885SCheng Sean Ye 1782e4b86885SCheng Sean Ye rw_init(&mc_lock, NULL, RW_DRIVER, NULL); 1783e4b86885SCheng Sean Ye return (mod_install(&modlinkage)); 1784e4b86885SCheng Sean Ye } 1785e4b86885SCheng Sean Ye 1786e4b86885SCheng Sean Ye int 1787e4b86885SCheng Sean Ye _info(struct modinfo *modinfop) 1788e4b86885SCheng Sean Ye { 1789e4b86885SCheng Sean Ye return (mod_info(&modlinkage, modinfop)); 1790e4b86885SCheng Sean Ye } 1791e4b86885SCheng Sean Ye 1792e4b86885SCheng Sean Ye int 1793e4b86885SCheng Sean Ye _fini(void) 1794e4b86885SCheng Sean Ye { 1795e4b86885SCheng Sean Ye int rc; 1796e4b86885SCheng Sean Ye 1797e4b86885SCheng Sean Ye if ((rc = mod_remove(&modlinkage)) != 0) 1798e4b86885SCheng Sean Ye return (rc); 1799e4b86885SCheng Sean Ye 1800e4b86885SCheng Sean Ye rw_destroy(&mc_lock); 1801e4b86885SCheng Sean Ye return (0); 1802e4b86885SCheng Sean Ye } 1803