15254Sgavinm /* 25254Sgavinm * CDDL HEADER START 35254Sgavinm * 45254Sgavinm * The contents of this file are subject to the terms of the 55254Sgavinm * Common Development and Distribution License (the "License"). 65254Sgavinm * You may not use this file except in compliance with the License. 75254Sgavinm * 85254Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95254Sgavinm * or http://www.opensolaris.org/os/licensing. 105254Sgavinm * See the License for the specific language governing permissions 115254Sgavinm * and limitations under the License. 125254Sgavinm * 135254Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 145254Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155254Sgavinm * If applicable, add the following below this CDDL HEADER, with the 165254Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 175254Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 185254Sgavinm * 195254Sgavinm * CDDL HEADER END 205254Sgavinm */ 215254Sgavinm 225254Sgavinm /* 2310026SKuriakose.Kuruvilla@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 245254Sgavinm * Use is subject to license terms. 255254Sgavinm */ 265254Sgavinm 275254Sgavinm /* 285254Sgavinm * "Generic AMD" model-specific support. If no more-specific support can 295254Sgavinm * be found, or such modules declines to initialize, then for AuthenticAMD 305254Sgavinm * cpus this module can have a crack at providing some AMD model-specific 315254Sgavinm * support that at least goes beyond common MCA architectural features 325254Sgavinm * if not down to the nitty-gritty level for a particular model. We 335254Sgavinm * are layered on top of a cpu module, likely cpu.generic, so there is no 345254Sgavinm * need for us to perform common architecturally-accessible functions. 355254Sgavinm */ 365254Sgavinm 375254Sgavinm #include <sys/types.h> 385254Sgavinm #include <sys/cmn_err.h> 395254Sgavinm #include <sys/modctl.h> 405254Sgavinm #include <sys/cpu_module.h> 415254Sgavinm #include <sys/mca_x86.h> 425254Sgavinm #include <sys/pci_cfgspace.h> 435254Sgavinm #include <sys/x86_archext.h> 445254Sgavinm #include <sys/mc_amd.h> 455254Sgavinm #include <sys/fm/protocol.h> 465254Sgavinm #include <sys/fm/cpu/GENAMD.h> 4710942STom.Pothier@Sun.COM #include <sys/fm/smb/fmsmb.h> 4810942STom.Pothier@Sun.COM #include <sys/fm/util.h> 495254Sgavinm #include <sys/nvpair.h> 505254Sgavinm #include <sys/controlregs.h> 515254Sgavinm #include <sys/pghw.h> 525254Sgavinm #include <sys/sunddi.h> 535327Sgavinm #include <sys/sysmacros.h> 545254Sgavinm #include <sys/cpu_module_ms_impl.h> 555254Sgavinm 565254Sgavinm #include "authamd.h" 575254Sgavinm 5810942STom.Pothier@Sun.COM extern int x86gentopo_legacy; /* x86 generic topo support */ 5910942STom.Pothier@Sun.COM 605254Sgavinm int authamd_ms_support_disable = 0; 615254Sgavinm 625254Sgavinm #define AUTHAMD_F_REVS_BCDE \ 635254Sgavinm (X86_CHIPREV_AMD_F_REV_B | X86_CHIPREV_AMD_F_REV_C0 | \ 645254Sgavinm X86_CHIPREV_AMD_F_REV_CG | X86_CHIPREV_AMD_F_REV_D | \ 655254Sgavinm X86_CHIPREV_AMD_F_REV_E) 665254Sgavinm 675254Sgavinm #define AUTHAMD_F_REVS_FG \ 685254Sgavinm (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) 695254Sgavinm 705254Sgavinm #define AUTHAMD_10_REVS_AB \ 715254Sgavinm (X86_CHIPREV_AMD_10_REV_A | X86_CHIPREV_AMD_10_REV_B) 725254Sgavinm 735254Sgavinm /* 745254Sgavinm * Bitmasks of support for various features. Try to enable features 755254Sgavinm * via inclusion in one of these bitmasks and check that at the 765254Sgavinm * feature imlementation - that way new family support may often simply 775254Sgavinm * simply need to update these bitmasks. 785254Sgavinm */ 795254Sgavinm 805254Sgavinm /* 815639Sgavinm * Models that include an on-chip NorthBridge. 825639Sgavinm */ 835639Sgavinm #define AUTHAMD_NBONCHIP(rev) \ 845639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 855639Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 865639Sgavinm 875639Sgavinm /* 885254Sgavinm * Families/revisions for which we can recognise main memory ECC errors. 895254Sgavinm */ 905254Sgavinm #define AUTHAMD_MEMECC_RECOGNISED(rev) \ 915254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 925254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 935254Sgavinm 945254Sgavinm /* 955254Sgavinm * Families/revisions that have an Online Spare Control Register 965254Sgavinm */ 975254Sgavinm #define AUTHAMD_HAS_ONLINESPARECTL(rev) \ 985254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) || \ 995254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1005254Sgavinm 1015254Sgavinm /* 1025327Sgavinm * Families/revisions for which we will perform NB MCA Config changes 1035327Sgavinm */ 1045327Sgavinm #define AUTHAMD_DO_NBMCACFG(rev) \ 1055639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1065327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1075327Sgavinm 1085327Sgavinm /* 1095327Sgavinm * Families/revisions that have chip cache scrubbers. 1105327Sgavinm */ 1115327Sgavinm #define AUTHAMD_HAS_CHIPSCRUB(rev) \ 1125639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1135327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1145327Sgavinm 1155327Sgavinm /* 1165254Sgavinm * Families/revisions that have a NB misc register or registers - 1175254Sgavinm * evaluates to 0 if no support, otherwise the number of MC4_MISCj. 1185254Sgavinm */ 1195254Sgavinm #define AUTHAMD_NBMISC_NUM(rev) \ 1205254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F)? 1 : \ 1215254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A) ? 3 : 0)) 1225254Sgavinm 1235254Sgavinm /* 1245254Sgavinm * Families/revision for which we wish not to machine check for GART 1255254Sgavinm * table walk errors - bit 10 of NB CTL. 1265254Sgavinm */ 1275254Sgavinm #define AUTHAMD_NOGARTTBLWLK_MC(rev) \ 1285254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1295254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1305254Sgavinm 1315254Sgavinm /* 1325327Sgavinm * Families/revisions that are potentially L3 capable 1335327Sgavinm */ 1345327Sgavinm #define AUTHAMD_L3CAPABLE(rev) \ 1355327Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1365327Sgavinm 1375327Sgavinm /* 1385254Sgavinm * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED 1395254Sgavinm * revisions as: 1405254Sgavinm * 1415254Sgavinm * - being reported by the NB 1425254Sgavinm * - being a compound bus/interconnect error (external to chip) 1435254Sgavinm * - having LL of LG 1445254Sgavinm * - having II of MEM (but could still be a master/target abort) 1455254Sgavinm * - having CECC or UECC set 1465254Sgavinm * 1475254Sgavinm * We do not check the extended error code (first nibble of the 1485254Sgavinm * model-specific error code on AMD) since this has changed from 1495254Sgavinm * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10). 1505254Sgavinm * Instead we use CECC/UECC to separate off the master/target 1515254Sgavinm * abort cases. 1525254Sgavinm * 1535254Sgavinm * We insist that the detector be the NorthBridge bank; although 1545254Sgavinm * IC/DC can report some main memory errors, they do not capture 1555254Sgavinm * an address at sufficient resolution to be useful and the NB will 1565254Sgavinm * report most errors. 1575254Sgavinm */ 1585254Sgavinm #define AUTHAMD_IS_MEMECCERR(bank, status) \ 1595254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 1605254Sgavinm MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \ 1615254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 1625254Sgavinm MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \ 1635254Sgavinm ((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC))) 1645254Sgavinm 1655254Sgavinm static authamd_error_disp_t authamd_memce_disp = { 1665254Sgavinm FM_EREPORT_CPU_GENAMD, 1675254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_CE, 1685254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE 1695254Sgavinm }; 1705254Sgavinm 1715254Sgavinm static authamd_error_disp_t authamd_memue_disp = { 1725254Sgavinm FM_EREPORT_CPU_GENAMD, 1735254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_UE, 1745254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE 1755254Sgavinm }; 1765254Sgavinm 1775254Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = { 1785254Sgavinm FM_EREPORT_CPU_GENAMD, 1795254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_CE, 1805254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE 1815254Sgavinm }; 1825254Sgavinm 1835254Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = { 1845254Sgavinm FM_EREPORT_CPU_GENAMD, 1855254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_UE, 1865254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE 1875254Sgavinm }; 1885254Sgavinm 1895254Sgavinm /* 1905254Sgavinm * We recognise GART walk errors as: 1915254Sgavinm * 1925254Sgavinm * - being reported by the NB 1935254Sgavinm * - being a compound TLB error 1945254Sgavinm * - having LL of LG and TT of GEN 1955254Sgavinm * - having UC set 1965254Sgavinm * - possibly having PCC set (if source CPU) 1975254Sgavinm */ 1985254Sgavinm #define AUTHAMD_IS_GARTERR(bank, status) \ 1995254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 2005254Sgavinm MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \ 2015254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 2025254Sgavinm MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \ 2035254Sgavinm (status) & MSR_MC_STATUS_UC) 2045254Sgavinm 2055254Sgavinm static authamd_error_disp_t authamd_gart_disp = { 2065254Sgavinm FM_EREPORT_CPU_GENAMD, /* use generic subclass */ 2075254Sgavinm FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */ 2085254Sgavinm 0 /* no additional payload */ 2095254Sgavinm }; 2105254Sgavinm 2115254Sgavinm 212*10947SSrihari.Venkatesan@Sun.COM static struct authamd_nodeshared *authamd_shared[AUTHAMD_MAX_NODES]; 2135254Sgavinm 2145254Sgavinm static int 2155254Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what) 2165254Sgavinm { 2175254Sgavinm return (atomic_set_long_excl(&authamd->amd_shared->acs_cfgonce, 2185254Sgavinm what) == 0 ? B_TRUE : B_FALSE); 2195254Sgavinm } 2205254Sgavinm 2215254Sgavinm static void 222*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(uint_t procnodeid, uint_t func, uint_t reg, uint32_t val) 2235254Sgavinm { 224*10947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2255254Sgavinm ASSERT((func & 7) == func); 2265254Sgavinm ASSERT((reg & 3) == 0 && reg < 256); 2275254Sgavinm 228*10947SSrihari.Venkatesan@Sun.COM cmi_pci_putl(0, procnodeid + 24, func, reg, 0, val); 2295254Sgavinm } 2305254Sgavinm 2315254Sgavinm static uint32_t 232*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(uint_t procnodeid, uint_t func, uint_t reg) 2335254Sgavinm { 234*10947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2355254Sgavinm ASSERT((func & 7) == func); 2365254Sgavinm ASSERT((reg & 3) == 0 && reg < 256); 2375254Sgavinm 238*10947SSrihari.Venkatesan@Sun.COM return (cmi_pci_getl(0, procnodeid + 24, func, reg, 0, 0)); 2395254Sgavinm } 2405254Sgavinm 2415254Sgavinm void 2425254Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2435254Sgavinm { 2445254Sgavinm uint64_t hwcr; 2455254Sgavinm 2465254Sgavinm if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) 2475254Sgavinm return; 2485254Sgavinm 2495254Sgavinm authamd->amd_hwcr = hwcr; 2505254Sgavinm 2515254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2525254Sgavinm hwcr |= AMD_HWCR_MCI_STATUS_WREN; 2535254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2545254Sgavinm } 2555254Sgavinm } 2565254Sgavinm 2575254Sgavinm void 2585254Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2595254Sgavinm { 2605254Sgavinm uint64_t hwcr = authamd->amd_hwcr; 2615254Sgavinm 2625254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2635254Sgavinm hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; 2645254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2655254Sgavinm } 2665254Sgavinm } 2675254Sgavinm 2685254Sgavinm /* 2695254Sgavinm * Read EccCnt repeatedly for all possible channel/chip-select combos: 2705254Sgavinm * 2715254Sgavinm * - read sparectl register 2725254Sgavinm * - if EccErrCntWrEn is set, clear that bit in the just-read value 2735254Sgavinm * and write it back to sparectl; this *may* clobber the EccCnt 2745254Sgavinm * for the channel/chip-select combination currently selected, so 2755254Sgavinm * we leave this bit clear if we had to clear it 2765254Sgavinm * - cycle through all channel/chip-select combinations writing each 2775254Sgavinm * combination to sparectl before reading the register back for 2785254Sgavinm * EccCnt for that combination; since EccErrCntWrEn is clear 2795254Sgavinm * the writes to select what count to read will not themselves 2805254Sgavinm * zero any counts 2815254Sgavinm */ 2825254Sgavinm static int 2835254Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl) 2845254Sgavinm { 2855254Sgavinm union mcreg_sparectl sparectl; 286*10947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = authamd->amd_shared->acs_procnodeid; 2875254Sgavinm uint_t family = authamd->amd_shared->acs_family; 2885254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 2895254Sgavinm int chan, cs; 2905254Sgavinm 2915254Sgavinm /* 2925254Sgavinm * Check for feature support; this macro will test down to the 2935254Sgavinm * family revision number, whereafter we'll switch on family 2945254Sgavinm * assuming that future revisions will use the same register 2955254Sgavinm * format. 2965254Sgavinm */ 2975254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) { 2985254Sgavinm bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt)); 2995254Sgavinm return (0); 3005254Sgavinm } 3015254Sgavinm 3025254Sgavinm MCREG_VAL32(&sparectl) = 303*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 304*10947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3055254Sgavinm 3065254Sgavinm switch (family) { 3075254Sgavinm case AUTHAMD_FAMILY_F: 3085254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0; 3095254Sgavinm break; 3105254Sgavinm 3115254Sgavinm case AUTHAMD_FAMILY_10: 3125254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0; 3135254Sgavinm break; 3145254Sgavinm } 3155254Sgavinm 3165254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 3175254Sgavinm switch (family) { 3185254Sgavinm case AUTHAMD_FAMILY_F: 3195254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 3205254Sgavinm chan; 3215254Sgavinm break; 3225254Sgavinm 3235254Sgavinm case AUTHAMD_FAMILY_10: 3245254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 3255254Sgavinm chan; 3265254Sgavinm break; 3275254Sgavinm } 3285254Sgavinm 3295254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 3305254Sgavinm switch (family) { 3315254Sgavinm case AUTHAMD_FAMILY_F: 3325254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 3335254Sgavinm EccErrCntDramCs) = cs; 3345254Sgavinm break; 3355254Sgavinm 3365254Sgavinm case AUTHAMD_FAMILY_10: 3375254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 3385254Sgavinm EccErrCntDramCs) = cs; 3395254Sgavinm break; 3405254Sgavinm } 3415254Sgavinm 342*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 3435254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 3445254Sgavinm 345*10947SSrihari.Venkatesan@Sun.COM MCREG_VAL32(&sparectl) = authamd_pcicfg_read(procnodeid, 3465254Sgavinm MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3475254Sgavinm 3485254Sgavinm switch (family) { 3495254Sgavinm case AUTHAMD_FAMILY_F: 3505254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3515254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCnt); 3525254Sgavinm break; 3535254Sgavinm case AUTHAMD_FAMILY_10: 3545254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3555254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCnt); 3565254Sgavinm break; 3575254Sgavinm } 3585254Sgavinm } 3595254Sgavinm } 3605254Sgavinm 3615254Sgavinm return (1); 3625254Sgavinm } 3635254Sgavinm 3645254Sgavinm /* 3655254Sgavinm * Clear EccCnt for all possible channel/chip-select combos: 3665254Sgavinm * 3675254Sgavinm * - set EccErrCntWrEn in sparectl, if necessary 3685254Sgavinm * - write 0 to EccCnt for all channel/chip-select combinations 3695254Sgavinm * - clear EccErrCntWrEn 3705254Sgavinm * 3715254Sgavinm * If requested also disable the interrupts taken on counter overflow 3725254Sgavinm * and on swap done. 3735254Sgavinm */ 3745254Sgavinm static void 3755254Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint) 3765254Sgavinm { 3775254Sgavinm union mcreg_sparectl sparectl; 378*10947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = authamd->amd_shared->acs_procnodeid; 3795254Sgavinm uint_t family = authamd->amd_shared->acs_family; 3805254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 3815254Sgavinm int chan, cs; 3825254Sgavinm 3835254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) 3845254Sgavinm return; 3855254Sgavinm 3865254Sgavinm MCREG_VAL32(&sparectl) = 387*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 388*10947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3895254Sgavinm 3905254Sgavinm switch (family) { 3915254Sgavinm case AUTHAMD_FAMILY_F: 3925254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; 3935254Sgavinm if (clrint) { 3945254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; 3955254Sgavinm MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; 3965254Sgavinm } 3975254Sgavinm break; 3985254Sgavinm 3995254Sgavinm case AUTHAMD_FAMILY_10: 4005254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1; 4015254Sgavinm if (clrint) { 4025254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0; 4035254Sgavinm MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0; 4045254Sgavinm } 4055254Sgavinm break; 4065254Sgavinm } 4075254Sgavinm 408*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4095254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4105254Sgavinm 4115254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 4125254Sgavinm switch (family) { 4135254Sgavinm case AUTHAMD_FAMILY_F: 4145254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 4155254Sgavinm chan; 4165254Sgavinm break; 4175254Sgavinm 4185254Sgavinm case AUTHAMD_FAMILY_10: 4195254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 4205254Sgavinm chan; 4215254Sgavinm break; 4225254Sgavinm } 4235254Sgavinm 4245254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 4255254Sgavinm switch (family) { 4265254Sgavinm case AUTHAMD_FAMILY_F: 4275254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4285254Sgavinm EccErrCntDramCs) = cs; 4295254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4305254Sgavinm EccErrCnt) = 0; 4315254Sgavinm break; 4325254Sgavinm 4335254Sgavinm case AUTHAMD_FAMILY_10: 4345254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4355254Sgavinm EccErrCntDramCs) = cs; 4365254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4375254Sgavinm EccErrCnt) = 0; 4385254Sgavinm break; 4395254Sgavinm } 4405254Sgavinm 441*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4425254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4435254Sgavinm } 4445254Sgavinm } 4455254Sgavinm } 4465254Sgavinm 44710026SKuriakose.Kuruvilla@Sun.COM 44810026SKuriakose.Kuruvilla@Sun.COM /* 44910026SKuriakose.Kuruvilla@Sun.COM * Return 45010026SKuriakose.Kuruvilla@Sun.COM * 1: supported 45110026SKuriakose.Kuruvilla@Sun.COM * 0: unsupported 45210026SKuriakose.Kuruvilla@Sun.COM */ 45310026SKuriakose.Kuruvilla@Sun.COM static int 454*10947SSrihari.Venkatesan@Sun.COM authamd_supported(cmi_hdl_t hdl) 45510026SKuriakose.Kuruvilla@Sun.COM { 456*10947SSrihari.Venkatesan@Sun.COM uint_t family = cmi_hdl_family(hdl); 45710026SKuriakose.Kuruvilla@Sun.COM 458*10947SSrihari.Venkatesan@Sun.COM switch (family) { 459*10947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_6: 460*10947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_F: 461*10947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_10: 462*10947SSrihari.Venkatesan@Sun.COM return (1); 463*10947SSrihari.Venkatesan@Sun.COM default: 464*10947SSrihari.Venkatesan@Sun.COM return (0); 46510026SKuriakose.Kuruvilla@Sun.COM } 46610026SKuriakose.Kuruvilla@Sun.COM } 46710026SKuriakose.Kuruvilla@Sun.COM 4685254Sgavinm /* 4695254Sgavinm * cms_init entry point. 4705254Sgavinm * 4715254Sgavinm * This module provides broad model-specific support for AMD families 4725254Sgavinm * 0x6, 0xf and 0x10. Future families will have to be evaluated once their 4735254Sgavinm * documentation is available. 4745254Sgavinm */ 4755254Sgavinm int 4765254Sgavinm authamd_init(cmi_hdl_t hdl, void **datap) 4775254Sgavinm { 4785254Sgavinm uint_t chipid = cmi_hdl_chipid(hdl); 479*10947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = cmi_hdl_procnodeid(hdl); 480*10947SSrihari.Venkatesan@Sun.COM struct authamd_nodeshared *sp, *osp; 4815254Sgavinm uint_t family = cmi_hdl_family(hdl); 48210026SKuriakose.Kuruvilla@Sun.COM uint32_t rev = cmi_hdl_chiprev(hdl); 4835254Sgavinm authamd_data_t *authamd; 4845254Sgavinm uint64_t cap; 4855254Sgavinm 48610026SKuriakose.Kuruvilla@Sun.COM if (authamd_ms_support_disable || 487*10947SSrihari.Venkatesan@Sun.COM !authamd_supported(hdl)) 4885254Sgavinm return (ENOTSUP); 4895254Sgavinm 4905254Sgavinm if (!(x86_feature & X86_MCA)) 4915254Sgavinm return (ENOTSUP); 4925254Sgavinm 4935254Sgavinm if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) 4945254Sgavinm return (ENOTSUP); 4955254Sgavinm 4965254Sgavinm if (!(cap & MCG_CAP_CTL_P)) 4975254Sgavinm return (ENOTSUP); 4985254Sgavinm 4995254Sgavinm authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP); 5005254Sgavinm cmi_hdl_hold(hdl); /* release in fini */ 5015254Sgavinm authamd->amd_hdl = hdl; 5025254Sgavinm 503*10947SSrihari.Venkatesan@Sun.COM if ((sp = authamd_shared[procnodeid]) == NULL) { 504*10947SSrihari.Venkatesan@Sun.COM sp = kmem_zalloc(sizeof (struct authamd_nodeshared), KM_SLEEP); 5056000Sgavinm sp->acs_chipid = chipid; 506*10947SSrihari.Venkatesan@Sun.COM sp->acs_procnodeid = procnodeid; 5076000Sgavinm sp->acs_family = family; 508*10947SSrihari.Venkatesan@Sun.COM sp->acs_rev = rev; 5096000Sgavinm membar_producer(); 5106000Sgavinm 511*10947SSrihari.Venkatesan@Sun.COM osp = atomic_cas_ptr(&authamd_shared[procnodeid], NULL, sp); 5125254Sgavinm if (osp != NULL) { 513*10947SSrihari.Venkatesan@Sun.COM kmem_free(sp, sizeof (struct authamd_nodeshared)); 5145254Sgavinm sp = osp; 5155254Sgavinm } 5165254Sgavinm } 5175254Sgavinm authamd->amd_shared = sp; 5185254Sgavinm 5195254Sgavinm return (0); 5205254Sgavinm } 5215254Sgavinm 5225254Sgavinm /* 5235254Sgavinm * cms_logout_size entry point. 5245254Sgavinm */ 5255254Sgavinm /*ARGSUSED*/ 5265254Sgavinm size_t 5275254Sgavinm authamd_logout_size(cmi_hdl_t hdl) 5285254Sgavinm { 5295254Sgavinm return (sizeof (struct authamd_logout)); 5305254Sgavinm } 5315254Sgavinm 5325254Sgavinm /* 5335254Sgavinm * cms_mcgctl_val entry point 5345254Sgavinm * 5355254Sgavinm * Instead of setting all bits to 1 we can set just those for the 5365254Sgavinm * error detector banks known to exist. 5375254Sgavinm */ 5385254Sgavinm /*ARGSUSED*/ 5395254Sgavinm uint64_t 5405254Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed) 5415254Sgavinm { 5425254Sgavinm return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed); 5435254Sgavinm } 5445254Sgavinm 5455254Sgavinm /* 5465254Sgavinm * cms_bankctl_skipinit entry point 5475254Sgavinm * 5485254Sgavinm * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC) 5495254Sgavinm * may produce spurious machine checks. 5505639Sgavinm * 5515639Sgavinm * Only allow a single core to setup the NorthBridge MCi_CTL register. 5525254Sgavinm */ 5535254Sgavinm /*ARGSUSED*/ 5545254Sgavinm boolean_t 5555254Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank) 5565254Sgavinm { 5575254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 5585639Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 5595254Sgavinm 5605639Sgavinm if (authamd->amd_shared->acs_family == AUTHAMD_FAMILY_6) 5615639Sgavinm return (bank == 0 ? B_TRUE : B_FALSE); 5625639Sgavinm 5635639Sgavinm if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) { 5645639Sgavinm return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) == 5655639Sgavinm B_TRUE ? B_FALSE : B_TRUE); 5665639Sgavinm } 5675639Sgavinm 5685639Sgavinm return (B_FALSE); 5695254Sgavinm } 5705254Sgavinm 5715254Sgavinm /* 5725254Sgavinm * cms_bankctl_val entry point 5735254Sgavinm */ 5745254Sgavinm uint64_t 5755254Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed) 5765254Sgavinm { 5775254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 5785254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 5795254Sgavinm uint64_t val = proposed; 5805254Sgavinm 5815254Sgavinm /* 5825254Sgavinm * The Intel MCA says we can write all 1's to enable #MC for 5835254Sgavinm * all errors, and AMD docs say much the same. But, depending 5845254Sgavinm * perhaps on other config registers, taking machine checks 5855254Sgavinm * for some errors such as GART TLB errors and master/target 5865254Sgavinm * aborts may be bad - they set UC and sometime also PCC, but 5875254Sgavinm * we should not always panic for these error types. 5885254Sgavinm * 5895254Sgavinm * Our cms_error_action entry point can suppress such panics, 5905254Sgavinm * however we can also use the cms_bankctl_val entry point to 5915254Sgavinm * veto enabling of some of the known villains in the first place. 5925254Sgavinm */ 5935254Sgavinm if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev)) 5945254Sgavinm val &= ~AMD_NB_EN_GARTTBLWK; 5955254Sgavinm 5965254Sgavinm return (val); 5975254Sgavinm } 5985254Sgavinm 5995254Sgavinm /* 6005327Sgavinm * Bits to add to NB MCA config (after watchdog config). 6015327Sgavinm */ 6025327Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN; 6035327Sgavinm 6045327Sgavinm /* 6055327Sgavinm * Bits to remove from NB MCA config (after watchdog config) 6065327Sgavinm */ 6075327Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN; 6085327Sgavinm 6095327Sgavinm /* 6105327Sgavinm * NB Watchdog policy, and rate we use if enabling. 6115327Sgavinm */ 6125327Sgavinm enum { 6135327Sgavinm AUTHAMD_NB_WDOG_LEAVEALONE, 6145327Sgavinm AUTHAMD_NB_WDOG_DISABLE, 6155327Sgavinm AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED, 6165327Sgavinm AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE 6175327Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED; 6185327Sgavinm 6195327Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 | 6205327Sgavinm AMD_NB_CFG_WDOGTMRBASESEL_1MS; 6215327Sgavinm 6225327Sgavinm /* 6235327Sgavinm * Per-core cache scrubbing policy and rates. 6245327Sgavinm */ 6255327Sgavinm enum { 6265327Sgavinm AUTHAMD_SCRUB_BIOSDEFAULT, /* leave as BIOS configured */ 6275327Sgavinm AUTHAMD_SCRUB_FIXED, /* assign our chosen rate */ 6285327Sgavinm AUTHAMD_SCRUB_MAX /* use higher of ours and BIOS rate */ 6295327Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX; 6305327Sgavinm 6315327Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf; /* 64K per 0.67 seconds */ 6325327Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe; /* 1MB per 5.3 seconds */ 6335327Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd; /* 1MB per 2.7 seconds */ 6345327Sgavinm 6355327Sgavinm static uint32_t 6365327Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm) 6375327Sgavinm { 6385327Sgavinm uint32_t rate; 6395327Sgavinm 6405327Sgavinm if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) { 6415327Sgavinm cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n", 6425327Sgavinm varnm, AMD_NB_SCRUBCTL_RATE_MAX); 6435327Sgavinm osrate = AMD_NB_SCRUBCTL_RATE_MAX; 6445327Sgavinm } 6455327Sgavinm 6465327Sgavinm switch (authamd_scrub_policy) { 6475327Sgavinm case AUTHAMD_SCRUB_FIXED: 6485327Sgavinm rate = osrate; 6495327Sgavinm break; 6505327Sgavinm 6515327Sgavinm default: 6525327Sgavinm cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - " 6535327Sgavinm "using default policy of AUTHAMD_SCRUB_MAX", 6545327Sgavinm authamd_scrub_policy); 6555327Sgavinm /*FALLTHRU*/ 6565327Sgavinm 6575327Sgavinm case AUTHAMD_SCRUB_MAX: 6585327Sgavinm if (osrate != 0 && biosrate != 0) 6595327Sgavinm rate = MIN(osrate, biosrate); /* small is fast */ 6605327Sgavinm else 6615327Sgavinm rate = osrate ? osrate : biosrate; 6625327Sgavinm } 6635327Sgavinm 6645327Sgavinm return (rate); 6655327Sgavinm } 6665327Sgavinm 6675327Sgavinm /* 6685254Sgavinm * cms_mca_init entry point. 6695254Sgavinm */ 6705254Sgavinm /*ARGSUSED*/ 6715254Sgavinm void 6725254Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks) 6735254Sgavinm { 6745254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 6755254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 676*10947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = authamd->amd_shared->acs_procnodeid; 6775254Sgavinm 6785254Sgavinm /* 6795254Sgavinm * On chips with a NB online spare control register take control 6805254Sgavinm * and clear ECC counts. 6815254Sgavinm */ 6825254Sgavinm if (AUTHAMD_HAS_ONLINESPARECTL(rev) && 6835254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) { 6845254Sgavinm authamd_clear_ecccnt(authamd, B_TRUE); 6855254Sgavinm } 6865254Sgavinm 6875254Sgavinm /* 6885254Sgavinm * And since we are claiming the telemetry stop the BIOS receiving 6895254Sgavinm * an SMI on NB threshold overflow. 6905254Sgavinm */ 6915254Sgavinm if (AUTHAMD_NBMISC_NUM(rev) && 6925254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) { 6935254Sgavinm union mcmsr_nbmisc nbm; 6945254Sgavinm int i; 6955254Sgavinm 6965254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 6975254Sgavinm 6985254Sgavinm for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) { 6995254Sgavinm if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i), 7005254Sgavinm (uint64_t *)&nbm) != CMI_SUCCESS) 7015254Sgavinm continue; 7025254Sgavinm 7035254Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && 7045254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) && 7055254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) { 7065254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0; 7075254Sgavinm } else if (X86_CHIPREV_ATLEAST(rev, 7085254Sgavinm X86_CHIPREV_AMD_10_REV_A) && 7095254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) && 7105254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) { 7115254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0; 7125254Sgavinm } 7135254Sgavinm 7145254Sgavinm (void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i), 7155254Sgavinm MCMSR_VAL(&nbm)); 7165254Sgavinm } 7175254Sgavinm 7185254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 7195254Sgavinm } 7205327Sgavinm 7215327Sgavinm /* 7225327Sgavinm * NB MCA Configuration Register. 7235327Sgavinm */ 7245327Sgavinm if (AUTHAMD_DO_NBMCACFG(rev) && 7255327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) { 726*10947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7275327Sgavinm MC_CTL_REG_NBCFG); 7285327Sgavinm 7295327Sgavinm switch (authamd_nb_watchdog_policy) { 7305327Sgavinm case AUTHAMD_NB_WDOG_LEAVEALONE: 7315327Sgavinm break; 7325327Sgavinm 7335327Sgavinm case AUTHAMD_NB_WDOG_DISABLE: 7345327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7355327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK); 7365327Sgavinm val |= AMD_NB_CFG_WDOGTMRDIS; 7375327Sgavinm break; 7385327Sgavinm 7395327Sgavinm default: 7405327Sgavinm cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d " 7415327Sgavinm "unrecognised, using default policy", 7425327Sgavinm authamd_nb_watchdog_policy); 7435327Sgavinm /*FALLTHRU*/ 7445327Sgavinm 7455327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED: 7465327Sgavinm if (!(val & AMD_NB_CFG_WDOGTMRDIS)) 7475327Sgavinm break; /* if enabled leave rate intact */ 7485327Sgavinm /*FALLTHRU*/ 7495327Sgavinm 7505327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE: 7515327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7525327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK | 7535327Sgavinm AMD_NB_CFG_WDOGTMRDIS); 7545327Sgavinm val |= authamd_nb_mcacfg_wdog; 7555327Sgavinm break; 7565327Sgavinm } 7575327Sgavinm 7585327Sgavinm /* 7595327Sgavinm * Bit 0 of the NB MCA Config register is reserved on family 7605327Sgavinm * 0x10. 7615327Sgavinm */ 7625327Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 7635327Sgavinm authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN; 7645327Sgavinm 7655327Sgavinm val &= ~authamd_nb_mcacfg_remove; 7665327Sgavinm val |= authamd_nb_mcacfg_add; 7675327Sgavinm 768*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 769*10947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_NBCFG, val); 7705327Sgavinm } 7715327Sgavinm 7725327Sgavinm /* 7735327Sgavinm * Cache scrubbing. We can't enable DRAM scrubbing since 7745327Sgavinm * we don't know the DRAM base for this node. 7755327Sgavinm */ 7765327Sgavinm if (AUTHAMD_HAS_CHIPSCRUB(rev) && 7775327Sgavinm authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT && 7785327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) { 779*10947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7805327Sgavinm MC_CTL_REG_SCRUBCTL); 7815327Sgavinm int l3cap = 0; 7825327Sgavinm 7835327Sgavinm if (AUTHAMD_L3CAPABLE(rev)) { 784*10947SSrihari.Venkatesan@Sun.COM l3cap = (authamd_pcicfg_read(procnodeid, 785*10947SSrihari.Venkatesan@Sun.COM MC_FUNC_MISCCTL, MC_CTL_REG_NBCAP) & 786*10947SSrihari.Venkatesan@Sun.COM MC_NBCAP_L3CAPABLE) != 0; 7875327Sgavinm } 7885327Sgavinm 7895327Sgavinm authamd_scrub_rate_dcache = 7905327Sgavinm authamd_scrubrate(authamd_scrub_rate_dcache, 7915327Sgavinm (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT, 7925327Sgavinm "authamd_scrub_rate_dcache"); 7935327Sgavinm 7945327Sgavinm authamd_scrub_rate_l2cache = 7955327Sgavinm authamd_scrubrate(authamd_scrub_rate_l2cache, 7965327Sgavinm (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT, 7975327Sgavinm "authamd_scrub_rate_l2cache"); 7985327Sgavinm 7995327Sgavinm authamd_scrub_rate_l3cache = l3cap ? 8005327Sgavinm authamd_scrubrate(authamd_scrub_rate_l3cache, 8015327Sgavinm (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT, 8025327Sgavinm "authamd_scrub_rate_l3cache") : 0; 8035327Sgavinm 8045327Sgavinm val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache, 8055327Sgavinm authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache, 8065327Sgavinm val & AMD_NB_SCRUBCTL_DRAM_MASK); 8075327Sgavinm 808*10947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 8095327Sgavinm MC_CTL_REG_SCRUBCTL, val); 8105327Sgavinm } 8115327Sgavinm 8125327Sgavinm } 8135327Sgavinm 8145327Sgavinm /* 8155327Sgavinm * cms_poll_ownermask entry point. 8165327Sgavinm */ 8175327Sgavinm uint64_t 8185327Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl) 8195327Sgavinm { 8205327Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 821*10947SSrihari.Venkatesan@Sun.COM struct authamd_nodeshared *acsp = authamd->amd_shared; 8225327Sgavinm hrtime_t now = gethrtime_waitfree(); 8235327Sgavinm hrtime_t last = acsp->acs_poll_timestamp; 8245327Sgavinm int dopoll = 0; 8255327Sgavinm 8265327Sgavinm if (now - last > 2 * pintvl || last == 0) { 8275327Sgavinm acsp->acs_pollowner = hdl; 8285327Sgavinm dopoll = 1; 8295327Sgavinm } else if (acsp->acs_pollowner == hdl) { 8305327Sgavinm dopoll = 1; 8315327Sgavinm } 8325327Sgavinm 8335327Sgavinm if (dopoll) 8345327Sgavinm acsp->acs_poll_timestamp = now; 8355327Sgavinm 8365327Sgavinm return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB)); 8375327Sgavinm 8385254Sgavinm } 8395254Sgavinm 8405254Sgavinm /* 8415254Sgavinm * cms_bank_logout entry point. 8425254Sgavinm */ 8435254Sgavinm /*ARGSUSED*/ 8445254Sgavinm void 8455254Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status, 8465254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 8475254Sgavinm { 8485254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 8495254Sgavinm struct authamd_logout *msl = mslogout; 8505254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 8515254Sgavinm 8525254Sgavinm if (msl == NULL) 8535254Sgavinm return; 8545254Sgavinm 8555254Sgavinm /* 8565254Sgavinm * For main memory ECC errors on revisions with an Online Spare 8575254Sgavinm * Control Register grab the ECC counts by channel and chip-select 8585254Sgavinm * and reset them to 0. 8595254Sgavinm */ 8605254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 8615254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status) && 8625254Sgavinm AUTHAMD_HAS_ONLINESPARECTL(rev)) { 8635254Sgavinm if (authamd_read_ecccnt(authamd, msl)) 8645254Sgavinm authamd_clear_ecccnt(authamd, B_FALSE); 8655254Sgavinm } 8665254Sgavinm } 8675254Sgavinm 8685254Sgavinm /* 8695254Sgavinm * cms_error_action entry point 8705254Sgavinm */ 8715254Sgavinm 8725254Sgavinm int authamd_forgive_uc = 0; /* For test/debug only */ 8735254Sgavinm int authamd_forgive_pcc = 0; /* For test/debug only */ 8745254Sgavinm int authamd_fake_poison = 0; /* For test/debug only */ 8755254Sgavinm 8765254Sgavinm /*ARGSUSED*/ 8775254Sgavinm uint32_t 8785254Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank, 8795254Sgavinm uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) 8805254Sgavinm { 8815254Sgavinm authamd_error_disp_t *disp; 8825254Sgavinm uint32_t rv = 0; 8835254Sgavinm 8845254Sgavinm if (authamd_forgive_uc) 8855254Sgavinm rv |= CMS_ERRSCOPE_CLEARED_UC; 8865254Sgavinm 8875254Sgavinm if (authamd_forgive_pcc) 8885254Sgavinm rv |= CMS_ERRSCOPE_CURCONTEXT_OK; 8895254Sgavinm 8905254Sgavinm if (authamd_fake_poison && status & MSR_MC_STATUS_UC) 8915254Sgavinm rv |= CMS_ERRSCOPE_POISONED; 8925254Sgavinm 8935254Sgavinm if (rv) 8945254Sgavinm return (rv); 8955254Sgavinm 8965254Sgavinm disp = authamd_disp_match(hdl, bank, status, addr, misc, mslogout); 8975254Sgavinm 8985254Sgavinm if (disp == &authamd_gart_disp) { 8995254Sgavinm /* 9005254Sgavinm * GART walk errors set UC and possibly PCC (if source CPU) 9015254Sgavinm * but should not be regarded as terminal. 9025254Sgavinm */ 9035254Sgavinm return (CMS_ERRSCOPE_IGNORE_ERR); 9045254Sgavinm } 9055254Sgavinm 9065254Sgavinm /* 9075254Sgavinm * May also want to consider master abort and target abort. These 9085254Sgavinm * also set UC and PCC (if src CPU) but the requester gets -1 9095254Sgavinm * and I believe the IO stuff in Solaris will handle that. 9105254Sgavinm */ 9115254Sgavinm 9125254Sgavinm return (rv); 9135254Sgavinm } 9145254Sgavinm 9155254Sgavinm /* 9165254Sgavinm * cms_disp_match entry point 9175254Sgavinm */ 9185254Sgavinm /*ARGSUSED*/ 9195254Sgavinm cms_cookie_t 9205254Sgavinm authamd_disp_match(cmi_hdl_t hdl, int bank, uint64_t status, 9215254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 9225254Sgavinm { 9235254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 9245254Sgavinm /* uint16_t errcode = MCAX86_ERRCODE(status); */ 9255254Sgavinm uint16_t exterrcode = AMD_EXT_ERRCODE(status); 9265254Sgavinm uint32_t rev = authamd->amd_shared->acs_rev; 9275254Sgavinm 9285254Sgavinm /* 9295254Sgavinm * Recognise main memory ECC errors 9305254Sgavinm */ 9315254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 9325254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status)) { 9335254Sgavinm if (status & AMD_BANK_STAT_CECC) { 9345254Sgavinm return (exterrcode == 0 ? &authamd_memce_disp : 9355254Sgavinm &authamd_ckmemce_disp); 9365254Sgavinm } else if (status & AMD_BANK_STAT_UECC) { 9375254Sgavinm return (exterrcode == 0 ? &authamd_memue_disp : 9385254Sgavinm &authamd_ckmemue_disp); 9395254Sgavinm } 9405254Sgavinm } 9415254Sgavinm 9425254Sgavinm /* 9435254Sgavinm * Recognise GART walk errors 9445254Sgavinm */ 9455254Sgavinm if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status)) 9465254Sgavinm return (&authamd_gart_disp); 9475254Sgavinm 9485254Sgavinm return (NULL); 9495254Sgavinm } 9505254Sgavinm 9515254Sgavinm /* 9525254Sgavinm * cms_ereport_class entry point 9535254Sgavinm */ 9545254Sgavinm /*ARGSUSED*/ 9555254Sgavinm void 9565254Sgavinm authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, 9575254Sgavinm const char **cpuclsp, const char **leafclsp) 9585254Sgavinm { 9595254Sgavinm const authamd_error_disp_t *aed = mscookie; 9605254Sgavinm 9615254Sgavinm if (aed == NULL) 9625254Sgavinm return; 9635254Sgavinm 9645254Sgavinm if (aed->aad_subclass != NULL) 9655254Sgavinm *cpuclsp = aed->aad_subclass; 9665254Sgavinm if (aed->aad_leafclass != NULL) 9675254Sgavinm *leafclsp = aed->aad_leafclass; 9685254Sgavinm } 9695254Sgavinm 9705254Sgavinm /*ARGSUSED*/ 9715254Sgavinm static void 9725254Sgavinm authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd, 9735254Sgavinm nvlist_t *ereport, nv_alloc_t *nva, void *mslogout) 9745254Sgavinm { 9755254Sgavinm nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9765254Sgavinm uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9775254Sgavinm authamd_logout_t *msl; 9785254Sgavinm nvlist_t *nvl; 9795254Sgavinm int nelems = 0; 980*10947SSrihari.Venkatesan@Sun.COM int i, chan, cs, mc; 98110942STom.Pothier@Sun.COM nvlist_t *board_list = NULL; 9825254Sgavinm 9835254Sgavinm if ((msl = mslogout) == NULL) 9845254Sgavinm return; 9855254Sgavinm 986*10947SSrihari.Venkatesan@Sun.COM /* Assume all processors have the same number of nodes */ 987*10947SSrihari.Venkatesan@Sun.COM mc = authamd->amd_shared->acs_procnodeid % 988*10947SSrihari.Venkatesan@Sun.COM cpuid_get_procnodes_per_pkg(CPU); 989*10947SSrihari.Venkatesan@Sun.COM 9905254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 9915254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 9925254Sgavinm if (msl->aal_eccerrcnt[chan][cs] == 0) 9935254Sgavinm continue; 9945254Sgavinm 9955254Sgavinm if ((nvl = fm_nvlist_create(nva)) == NULL) 9965254Sgavinm continue; 9975254Sgavinm 9985254Sgavinm elems[nelems] = nvl; 9995254Sgavinm counts[nelems++] = msl->aal_eccerrcnt[chan][cs]; 10005254Sgavinm 100110942STom.Pothier@Sun.COM if (!x86gentopo_legacy) { 100210942STom.Pothier@Sun.COM board_list = cmi_hdl_smb_bboard(hdl); 100310942STom.Pothier@Sun.COM if (board_list == NULL) 100410942STom.Pothier@Sun.COM continue; 100510942STom.Pothier@Sun.COM fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, 100610942STom.Pothier@Sun.COM NULL, NULL, board_list, 4, 100710942STom.Pothier@Sun.COM "chip", cmi_hdl_smb_chipid(hdl), 100810942STom.Pothier@Sun.COM "memory-controller", 0, 100910942STom.Pothier@Sun.COM "dram-channel", chan, 101010942STom.Pothier@Sun.COM "chip-select", cs); 101110942STom.Pothier@Sun.COM } else { 101210942STom.Pothier@Sun.COM fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, 101310942STom.Pothier@Sun.COM NULL, NULL, 5, 101410942STom.Pothier@Sun.COM "motherboard", 0, 101510942STom.Pothier@Sun.COM "chip", authamd->amd_shared->acs_chipid, 1016*10947SSrihari.Venkatesan@Sun.COM "memory-controller", mc, 101710942STom.Pothier@Sun.COM "dram-channel", chan, 101810942STom.Pothier@Sun.COM "chip-select", cs); 101910942STom.Pothier@Sun.COM } 10205254Sgavinm } 10215254Sgavinm } 10225254Sgavinm 10235254Sgavinm if (nelems == 0) 10245254Sgavinm return; 10255254Sgavinm 10265254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE, 10275254Sgavinm DATA_TYPE_NVLIST_ARRAY, nelems, elems, 10285254Sgavinm NULL); 10295254Sgavinm 10305254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT, 10315254Sgavinm DATA_TYPE_UINT8_ARRAY, nelems, &counts[0], 10325254Sgavinm NULL); 10335254Sgavinm 10345254Sgavinm for (i = 0; i < nelems; i++) 10355254Sgavinm fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE); 10365254Sgavinm } 10375254Sgavinm 10385254Sgavinm /* 10395254Sgavinm * cms_ereport_add_logout entry point 10405254Sgavinm */ 10415254Sgavinm /*ARGSUSED*/ 10425254Sgavinm void 10435254Sgavinm authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva, 10445254Sgavinm int bank, uint64_t status, uint64_t addr, uint64_t misc, 10455254Sgavinm void *mslogout, cms_cookie_t mscookie) 10465254Sgavinm { 10475254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 10485254Sgavinm const authamd_error_disp_t *aed = mscookie; 10495254Sgavinm uint64_t members; 10505254Sgavinm 10515254Sgavinm if (aed == NULL) 10525254Sgavinm return; 10535254Sgavinm 10545254Sgavinm members = aed->aad_ereport_members; 10555254Sgavinm 10565254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) { 10575254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND, 10585254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status), 10595254Sgavinm NULL); 10605254Sgavinm 10615254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10625254Sgavinm fm_payload_set(ereport, 10635254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 10645254Sgavinm DATA_TYPE_STRING, "E", 10655254Sgavinm NULL); 10665254Sgavinm } 10675254Sgavinm } 10685254Sgavinm 10695254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) { 10705254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND, 10715254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status), 10725254Sgavinm NULL); 10735254Sgavinm 10745254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10755254Sgavinm fm_payload_set(ereport, 10765254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 10775254Sgavinm DATA_TYPE_STRING, "C", 10785254Sgavinm NULL); 10795254Sgavinm } 10805254Sgavinm } 10815254Sgavinm 10825254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE && 10835254Sgavinm status & MSR_MC_STATUS_ADDRV) { 10845254Sgavinm authamd_ereport_add_resource(hdl, authamd, ereport, nva, 10855254Sgavinm mslogout); 10865254Sgavinm } 10875254Sgavinm } 10885254Sgavinm 10895254Sgavinm /* 10905254Sgavinm * cms_msrinject entry point 10915254Sgavinm */ 10925254Sgavinm cms_errno_t 10935254Sgavinm authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) 10945254Sgavinm { 10955254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 10965254Sgavinm cms_errno_t rv = CMSERR_BADMSRWRITE; 10975254Sgavinm 10985254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 10995254Sgavinm if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS) 11005254Sgavinm rv = CMS_SUCCESS; 11015254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 11025254Sgavinm 11035254Sgavinm return (rv); 11045254Sgavinm } 11055254Sgavinm 11065254Sgavinm cms_api_ver_t _cms_api_version = CMS_API_VERSION_0; 11075254Sgavinm 11085254Sgavinm const cms_ops_t _cms_ops = { 11095254Sgavinm authamd_init, /* cms_init */ 11105254Sgavinm NULL, /* cms_post_startup */ 11115254Sgavinm NULL, /* cms_post_mpstartup */ 11125254Sgavinm authamd_logout_size, /* cms_logout_size */ 11135254Sgavinm authamd_mcgctl_val, /* cms_mcgctl_val */ 11145254Sgavinm authamd_bankctl_skipinit, /* cms_bankctl_skipinit */ 11155254Sgavinm authamd_bankctl_val, /* cms_bankctl_val */ 11165254Sgavinm NULL, /* cms_bankstatus_skipinit */ 11175254Sgavinm NULL, /* cms_bankstatus_val */ 11185254Sgavinm authamd_mca_init, /* cms_mca_init */ 11195327Sgavinm authamd_poll_ownermask, /* cms_poll_ownermask */ 11205254Sgavinm authamd_bank_logout, /* cms_bank_logout */ 11215254Sgavinm authamd_error_action, /* cms_error_action */ 11225254Sgavinm authamd_disp_match, /* cms_disp_match */ 11235254Sgavinm authamd_ereport_class, /* cms_ereport_class */ 11245254Sgavinm NULL, /* cms_ereport_detector */ 11255254Sgavinm NULL, /* cms_ereport_includestack */ 11265254Sgavinm authamd_ereport_add_logout, /* cms_ereport_add_logout */ 11275254Sgavinm authamd_msrinject, /* cms_msrinject */ 11285254Sgavinm NULL, /* cms_fini */ 11295254Sgavinm }; 11305254Sgavinm 11315254Sgavinm static struct modlcpu modlcpu = { 11325254Sgavinm &mod_cpuops, 11335254Sgavinm "Generic AMD model-specific MCA" 11345254Sgavinm }; 11355254Sgavinm 11365254Sgavinm static struct modlinkage modlinkage = { 11375254Sgavinm MODREV_1, 11385254Sgavinm (void *)&modlcpu, 11395254Sgavinm NULL 11405254Sgavinm }; 11415254Sgavinm 11425254Sgavinm int 11435254Sgavinm _init(void) 11445254Sgavinm { 11455254Sgavinm return (mod_install(&modlinkage)); 11465254Sgavinm } 11475254Sgavinm 11485254Sgavinm int 11495254Sgavinm _info(struct modinfo *modinfop) 11505254Sgavinm { 11515254Sgavinm return (mod_info(&modlinkage, modinfop)); 11525254Sgavinm } 11535254Sgavinm 11545254Sgavinm int 11555254Sgavinm _fini(void) 11565254Sgavinm { 11575254Sgavinm return (mod_remove(&modlinkage)); 11585254Sgavinm } 1159