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 /* 23*11947Ssrihari.venkatesan@oracle.com * Copyright 2010 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 /* 138*11947Ssrihari.venkatesan@oracle.com * Families/revisions that support x8 ChipKill ECC 139*11947Ssrihari.venkatesan@oracle.com */ 140*11947Ssrihari.venkatesan@oracle.com #define AUTHAMD_SUPPORTS_X8ECC(rev) \ 141*11947Ssrihari.venkatesan@oracle.com (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_D)) 142*11947Ssrihari.venkatesan@oracle.com 143*11947Ssrihari.venkatesan@oracle.com /* 1445254Sgavinm * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED 1455254Sgavinm * revisions as: 1465254Sgavinm * 1475254Sgavinm * - being reported by the NB 1485254Sgavinm * - being a compound bus/interconnect error (external to chip) 1495254Sgavinm * - having LL of LG 1505254Sgavinm * - having II of MEM (but could still be a master/target abort) 1515254Sgavinm * - having CECC or UECC set 1525254Sgavinm * 1535254Sgavinm * We do not check the extended error code (first nibble of the 1545254Sgavinm * model-specific error code on AMD) since this has changed from 1555254Sgavinm * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10). 1565254Sgavinm * Instead we use CECC/UECC to separate off the master/target 1575254Sgavinm * abort cases. 1585254Sgavinm * 1595254Sgavinm * We insist that the detector be the NorthBridge bank; although 1605254Sgavinm * IC/DC can report some main memory errors, they do not capture 1615254Sgavinm * an address at sufficient resolution to be useful and the NB will 1625254Sgavinm * report most errors. 1635254Sgavinm */ 1645254Sgavinm #define AUTHAMD_IS_MEMECCERR(bank, status) \ 1655254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 1665254Sgavinm MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \ 1675254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 1685254Sgavinm MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \ 1695254Sgavinm ((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC))) 1705254Sgavinm 1715254Sgavinm static authamd_error_disp_t authamd_memce_disp = { 1725254Sgavinm FM_EREPORT_CPU_GENAMD, 1735254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_CE, 1745254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE 1755254Sgavinm }; 1765254Sgavinm 1775254Sgavinm static authamd_error_disp_t authamd_memue_disp = { 1785254Sgavinm FM_EREPORT_CPU_GENAMD, 1795254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_UE, 1805254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE 1815254Sgavinm }; 1825254Sgavinm 1835254Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = { 1845254Sgavinm FM_EREPORT_CPU_GENAMD, 1855254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_CE, 1865254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE 1875254Sgavinm }; 1885254Sgavinm 1895254Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = { 1905254Sgavinm FM_EREPORT_CPU_GENAMD, 1915254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_UE, 1925254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE 1935254Sgavinm }; 1945254Sgavinm 1955254Sgavinm /* 1965254Sgavinm * We recognise GART walk errors as: 1975254Sgavinm * 1985254Sgavinm * - being reported by the NB 1995254Sgavinm * - being a compound TLB error 2005254Sgavinm * - having LL of LG and TT of GEN 2015254Sgavinm * - having UC set 2025254Sgavinm * - possibly having PCC set (if source CPU) 2035254Sgavinm */ 2045254Sgavinm #define AUTHAMD_IS_GARTERR(bank, status) \ 2055254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 2065254Sgavinm MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \ 2075254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 2085254Sgavinm MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \ 2095254Sgavinm (status) & MSR_MC_STATUS_UC) 2105254Sgavinm 2115254Sgavinm static authamd_error_disp_t authamd_gart_disp = { 2125254Sgavinm FM_EREPORT_CPU_GENAMD, /* use generic subclass */ 2135254Sgavinm FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */ 2145254Sgavinm 0 /* no additional payload */ 2155254Sgavinm }; 2165254Sgavinm 2175254Sgavinm 21810947SSrihari.Venkatesan@Sun.COM static struct authamd_nodeshared *authamd_shared[AUTHAMD_MAX_NODES]; 2195254Sgavinm 2205254Sgavinm static int 2215254Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what) 2225254Sgavinm { 223*11947Ssrihari.venkatesan@oracle.com return (atomic_set_long_excl(&authamd->amd_shared->ans_cfgonce, 2245254Sgavinm what) == 0 ? B_TRUE : B_FALSE); 2255254Sgavinm } 2265254Sgavinm 2275254Sgavinm static void 22810947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(uint_t procnodeid, uint_t func, uint_t reg, uint32_t val) 2295254Sgavinm { 23010947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2315254Sgavinm ASSERT((func & 7) == func); 232*11947Ssrihari.venkatesan@oracle.com ASSERT((reg & 3) == 0 && reg < 4096); 2335254Sgavinm 23410947SSrihari.Venkatesan@Sun.COM cmi_pci_putl(0, procnodeid + 24, func, reg, 0, val); 2355254Sgavinm } 2365254Sgavinm 2375254Sgavinm static uint32_t 23810947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(uint_t procnodeid, uint_t func, uint_t reg) 2395254Sgavinm { 24010947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2415254Sgavinm ASSERT((func & 7) == func); 242*11947Ssrihari.venkatesan@oracle.com ASSERT((reg & 3) == 0 && reg < 4096); 2435254Sgavinm 24410947SSrihari.Venkatesan@Sun.COM return (cmi_pci_getl(0, procnodeid + 24, func, reg, 0, 0)); 2455254Sgavinm } 2465254Sgavinm 2475254Sgavinm void 2485254Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2495254Sgavinm { 2505254Sgavinm uint64_t hwcr; 2515254Sgavinm 2525254Sgavinm if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) 2535254Sgavinm return; 2545254Sgavinm 2555254Sgavinm authamd->amd_hwcr = hwcr; 2565254Sgavinm 2575254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2585254Sgavinm hwcr |= AMD_HWCR_MCI_STATUS_WREN; 2595254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2605254Sgavinm } 2615254Sgavinm } 2625254Sgavinm 2635254Sgavinm void 2645254Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2655254Sgavinm { 2665254Sgavinm uint64_t hwcr = authamd->amd_hwcr; 2675254Sgavinm 2685254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2695254Sgavinm hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; 2705254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2715254Sgavinm } 2725254Sgavinm } 2735254Sgavinm 2745254Sgavinm /* 2755254Sgavinm * Read EccCnt repeatedly for all possible channel/chip-select combos: 2765254Sgavinm * 2775254Sgavinm * - read sparectl register 2785254Sgavinm * - if EccErrCntWrEn is set, clear that bit in the just-read value 2795254Sgavinm * and write it back to sparectl; this *may* clobber the EccCnt 2805254Sgavinm * for the channel/chip-select combination currently selected, so 2815254Sgavinm * we leave this bit clear if we had to clear it 2825254Sgavinm * - cycle through all channel/chip-select combinations writing each 2835254Sgavinm * combination to sparectl before reading the register back for 2845254Sgavinm * EccCnt for that combination; since EccErrCntWrEn is clear 2855254Sgavinm * the writes to select what count to read will not themselves 2865254Sgavinm * zero any counts 2875254Sgavinm */ 2885254Sgavinm static int 2895254Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl) 2905254Sgavinm { 2915254Sgavinm union mcreg_sparectl sparectl; 292*11947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 293*11947Ssrihari.venkatesan@oracle.com uint_t family = authamd->amd_shared->ans_family; 294*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 2955254Sgavinm int chan, cs; 2965254Sgavinm 2975254Sgavinm /* 2985254Sgavinm * Check for feature support; this macro will test down to the 2995254Sgavinm * family revision number, whereafter we'll switch on family 3005254Sgavinm * assuming that future revisions will use the same register 3015254Sgavinm * format. 3025254Sgavinm */ 3035254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) { 3045254Sgavinm bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt)); 3055254Sgavinm return (0); 3065254Sgavinm } 3075254Sgavinm 3085254Sgavinm MCREG_VAL32(&sparectl) = 30910947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 31010947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3115254Sgavinm 3125254Sgavinm switch (family) { 3135254Sgavinm case AUTHAMD_FAMILY_F: 3145254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0; 3155254Sgavinm break; 3165254Sgavinm 3175254Sgavinm case AUTHAMD_FAMILY_10: 3185254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0; 3195254Sgavinm break; 3205254Sgavinm } 3215254Sgavinm 3225254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 3235254Sgavinm switch (family) { 3245254Sgavinm case AUTHAMD_FAMILY_F: 3255254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 3265254Sgavinm chan; 3275254Sgavinm break; 3285254Sgavinm 3295254Sgavinm case AUTHAMD_FAMILY_10: 3305254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 3315254Sgavinm chan; 3325254Sgavinm break; 3335254Sgavinm } 3345254Sgavinm 3355254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 3365254Sgavinm switch (family) { 3375254Sgavinm case AUTHAMD_FAMILY_F: 3385254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 3395254Sgavinm EccErrCntDramCs) = cs; 3405254Sgavinm break; 3415254Sgavinm 3425254Sgavinm case AUTHAMD_FAMILY_10: 3435254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 3445254Sgavinm EccErrCntDramCs) = cs; 3455254Sgavinm break; 3465254Sgavinm } 3475254Sgavinm 34810947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 3495254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 3505254Sgavinm 35110947SSrihari.Venkatesan@Sun.COM MCREG_VAL32(&sparectl) = authamd_pcicfg_read(procnodeid, 3525254Sgavinm MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3535254Sgavinm 3545254Sgavinm switch (family) { 3555254Sgavinm case AUTHAMD_FAMILY_F: 3565254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3575254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCnt); 3585254Sgavinm break; 3595254Sgavinm case AUTHAMD_FAMILY_10: 3605254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3615254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCnt); 3625254Sgavinm break; 3635254Sgavinm } 3645254Sgavinm } 3655254Sgavinm } 3665254Sgavinm 3675254Sgavinm return (1); 3685254Sgavinm } 3695254Sgavinm 3705254Sgavinm /* 3715254Sgavinm * Clear EccCnt for all possible channel/chip-select combos: 3725254Sgavinm * 3735254Sgavinm * - set EccErrCntWrEn in sparectl, if necessary 3745254Sgavinm * - write 0 to EccCnt for all channel/chip-select combinations 3755254Sgavinm * - clear EccErrCntWrEn 3765254Sgavinm * 3775254Sgavinm * If requested also disable the interrupts taken on counter overflow 3785254Sgavinm * and on swap done. 3795254Sgavinm */ 3805254Sgavinm static void 3815254Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint) 3825254Sgavinm { 3835254Sgavinm union mcreg_sparectl sparectl; 384*11947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 385*11947Ssrihari.venkatesan@oracle.com uint_t family = authamd->amd_shared->ans_family; 386*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 3875254Sgavinm int chan, cs; 3885254Sgavinm 3895254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) 3905254Sgavinm return; 3915254Sgavinm 3925254Sgavinm MCREG_VAL32(&sparectl) = 39310947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 39410947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3955254Sgavinm 3965254Sgavinm switch (family) { 3975254Sgavinm case AUTHAMD_FAMILY_F: 3985254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; 3995254Sgavinm if (clrint) { 4005254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; 4015254Sgavinm MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; 4025254Sgavinm } 4035254Sgavinm break; 4045254Sgavinm 4055254Sgavinm case AUTHAMD_FAMILY_10: 4065254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1; 4075254Sgavinm if (clrint) { 4085254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0; 4095254Sgavinm MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0; 4105254Sgavinm } 4115254Sgavinm break; 4125254Sgavinm } 4135254Sgavinm 41410947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4155254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4165254Sgavinm 4175254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 4185254Sgavinm switch (family) { 4195254Sgavinm case AUTHAMD_FAMILY_F: 4205254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 4215254Sgavinm chan; 4225254Sgavinm break; 4235254Sgavinm 4245254Sgavinm case AUTHAMD_FAMILY_10: 4255254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 4265254Sgavinm chan; 4275254Sgavinm break; 4285254Sgavinm } 4295254Sgavinm 4305254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 4315254Sgavinm switch (family) { 4325254Sgavinm case AUTHAMD_FAMILY_F: 4335254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4345254Sgavinm EccErrCntDramCs) = cs; 4355254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4365254Sgavinm EccErrCnt) = 0; 4375254Sgavinm break; 4385254Sgavinm 4395254Sgavinm case AUTHAMD_FAMILY_10: 4405254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4415254Sgavinm EccErrCntDramCs) = cs; 4425254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4435254Sgavinm EccErrCnt) = 0; 4445254Sgavinm break; 4455254Sgavinm } 4465254Sgavinm 44710947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4485254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4495254Sgavinm } 4505254Sgavinm } 4515254Sgavinm } 4525254Sgavinm 45310026SKuriakose.Kuruvilla@Sun.COM 45410026SKuriakose.Kuruvilla@Sun.COM /* 45510026SKuriakose.Kuruvilla@Sun.COM * Return 45610026SKuriakose.Kuruvilla@Sun.COM * 1: supported 45710026SKuriakose.Kuruvilla@Sun.COM * 0: unsupported 45810026SKuriakose.Kuruvilla@Sun.COM */ 45910026SKuriakose.Kuruvilla@Sun.COM static int 46010947SSrihari.Venkatesan@Sun.COM authamd_supported(cmi_hdl_t hdl) 46110026SKuriakose.Kuruvilla@Sun.COM { 46210947SSrihari.Venkatesan@Sun.COM uint_t family = cmi_hdl_family(hdl); 46310026SKuriakose.Kuruvilla@Sun.COM 46410947SSrihari.Venkatesan@Sun.COM switch (family) { 46510947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_6: 46610947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_F: 46710947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_10: 46810947SSrihari.Venkatesan@Sun.COM return (1); 46910947SSrihari.Venkatesan@Sun.COM default: 47010947SSrihari.Venkatesan@Sun.COM return (0); 47110026SKuriakose.Kuruvilla@Sun.COM } 47210026SKuriakose.Kuruvilla@Sun.COM } 47310026SKuriakose.Kuruvilla@Sun.COM 4745254Sgavinm /* 4755254Sgavinm * cms_init entry point. 4765254Sgavinm * 4775254Sgavinm * This module provides broad model-specific support for AMD families 4785254Sgavinm * 0x6, 0xf and 0x10. Future families will have to be evaluated once their 4795254Sgavinm * documentation is available. 4805254Sgavinm */ 4815254Sgavinm int 4825254Sgavinm authamd_init(cmi_hdl_t hdl, void **datap) 4835254Sgavinm { 4845254Sgavinm uint_t chipid = cmi_hdl_chipid(hdl); 48510947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = cmi_hdl_procnodeid(hdl); 48610947SSrihari.Venkatesan@Sun.COM struct authamd_nodeshared *sp, *osp; 4875254Sgavinm uint_t family = cmi_hdl_family(hdl); 48810026SKuriakose.Kuruvilla@Sun.COM uint32_t rev = cmi_hdl_chiprev(hdl); 4895254Sgavinm authamd_data_t *authamd; 4905254Sgavinm uint64_t cap; 4915254Sgavinm 49210026SKuriakose.Kuruvilla@Sun.COM if (authamd_ms_support_disable || 49310947SSrihari.Venkatesan@Sun.COM !authamd_supported(hdl)) 4945254Sgavinm return (ENOTSUP); 4955254Sgavinm 4965254Sgavinm if (!(x86_feature & X86_MCA)) 4975254Sgavinm return (ENOTSUP); 4985254Sgavinm 4995254Sgavinm if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) 5005254Sgavinm return (ENOTSUP); 5015254Sgavinm 5025254Sgavinm if (!(cap & MCG_CAP_CTL_P)) 5035254Sgavinm return (ENOTSUP); 5045254Sgavinm 5055254Sgavinm authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP); 5065254Sgavinm cmi_hdl_hold(hdl); /* release in fini */ 5075254Sgavinm authamd->amd_hdl = hdl; 5085254Sgavinm 50910947SSrihari.Venkatesan@Sun.COM if ((sp = authamd_shared[procnodeid]) == NULL) { 51010947SSrihari.Venkatesan@Sun.COM sp = kmem_zalloc(sizeof (struct authamd_nodeshared), KM_SLEEP); 511*11947Ssrihari.venkatesan@oracle.com sp->ans_chipid = chipid; 512*11947Ssrihari.venkatesan@oracle.com sp->ans_procnodeid = procnodeid; 513*11947Ssrihari.venkatesan@oracle.com sp->ans_family = family; 514*11947Ssrihari.venkatesan@oracle.com sp->ans_rev = rev; 5156000Sgavinm membar_producer(); 5166000Sgavinm 51710947SSrihari.Venkatesan@Sun.COM osp = atomic_cas_ptr(&authamd_shared[procnodeid], NULL, sp); 5185254Sgavinm if (osp != NULL) { 51910947SSrihari.Venkatesan@Sun.COM kmem_free(sp, sizeof (struct authamd_nodeshared)); 5205254Sgavinm sp = osp; 5215254Sgavinm } 5225254Sgavinm } 5235254Sgavinm authamd->amd_shared = sp; 5245254Sgavinm 5255254Sgavinm return (0); 5265254Sgavinm } 5275254Sgavinm 5285254Sgavinm /* 5295254Sgavinm * cms_logout_size entry point. 5305254Sgavinm */ 5315254Sgavinm /*ARGSUSED*/ 5325254Sgavinm size_t 5335254Sgavinm authamd_logout_size(cmi_hdl_t hdl) 5345254Sgavinm { 5355254Sgavinm return (sizeof (struct authamd_logout)); 5365254Sgavinm } 5375254Sgavinm 5385254Sgavinm /* 5395254Sgavinm * cms_mcgctl_val entry point 5405254Sgavinm * 5415254Sgavinm * Instead of setting all bits to 1 we can set just those for the 5425254Sgavinm * error detector banks known to exist. 5435254Sgavinm */ 5445254Sgavinm /*ARGSUSED*/ 5455254Sgavinm uint64_t 5465254Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed) 5475254Sgavinm { 5485254Sgavinm return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed); 5495254Sgavinm } 5505254Sgavinm 5515254Sgavinm /* 5525254Sgavinm * cms_bankctl_skipinit entry point 5535254Sgavinm * 5545254Sgavinm * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC) 5555254Sgavinm * may produce spurious machine checks. 5565639Sgavinm * 5575639Sgavinm * Only allow a single core to setup the NorthBridge MCi_CTL register. 5585254Sgavinm */ 5595254Sgavinm /*ARGSUSED*/ 5605254Sgavinm boolean_t 5615254Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank) 5625254Sgavinm { 5635254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 564*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 5655254Sgavinm 566*11947Ssrihari.venkatesan@oracle.com if (authamd->amd_shared->ans_family == AUTHAMD_FAMILY_6) 5675639Sgavinm return (bank == 0 ? B_TRUE : B_FALSE); 5685639Sgavinm 5695639Sgavinm if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) { 5705639Sgavinm return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) == 5715639Sgavinm B_TRUE ? B_FALSE : B_TRUE); 5725639Sgavinm } 5735639Sgavinm 5745639Sgavinm return (B_FALSE); 5755254Sgavinm } 5765254Sgavinm 5775254Sgavinm /* 5785254Sgavinm * cms_bankctl_val entry point 5795254Sgavinm */ 5805254Sgavinm uint64_t 5815254Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed) 5825254Sgavinm { 5835254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 584*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 5855254Sgavinm uint64_t val = proposed; 5865254Sgavinm 5875254Sgavinm /* 5885254Sgavinm * The Intel MCA says we can write all 1's to enable #MC for 5895254Sgavinm * all errors, and AMD docs say much the same. But, depending 5905254Sgavinm * perhaps on other config registers, taking machine checks 5915254Sgavinm * for some errors such as GART TLB errors and master/target 5925254Sgavinm * aborts may be bad - they set UC and sometime also PCC, but 5935254Sgavinm * we should not always panic for these error types. 5945254Sgavinm * 5955254Sgavinm * Our cms_error_action entry point can suppress such panics, 5965254Sgavinm * however we can also use the cms_bankctl_val entry point to 5975254Sgavinm * veto enabling of some of the known villains in the first place. 5985254Sgavinm */ 5995254Sgavinm if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev)) 6005254Sgavinm val &= ~AMD_NB_EN_GARTTBLWK; 6015254Sgavinm 6025254Sgavinm return (val); 6035254Sgavinm } 6045254Sgavinm 6055254Sgavinm /* 6065327Sgavinm * Bits to add to NB MCA config (after watchdog config). 6075327Sgavinm */ 6085327Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN; 6095327Sgavinm 6105327Sgavinm /* 6115327Sgavinm * Bits to remove from NB MCA config (after watchdog config) 6125327Sgavinm */ 6135327Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN; 6145327Sgavinm 6155327Sgavinm /* 6165327Sgavinm * NB Watchdog policy, and rate we use if enabling. 6175327Sgavinm */ 6185327Sgavinm enum { 6195327Sgavinm AUTHAMD_NB_WDOG_LEAVEALONE, 6205327Sgavinm AUTHAMD_NB_WDOG_DISABLE, 6215327Sgavinm AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED, 6225327Sgavinm AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE 6235327Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED; 6245327Sgavinm 6255327Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 | 6265327Sgavinm AMD_NB_CFG_WDOGTMRBASESEL_1MS; 6275327Sgavinm 6285327Sgavinm /* 6295327Sgavinm * Per-core cache scrubbing policy and rates. 6305327Sgavinm */ 6315327Sgavinm enum { 6325327Sgavinm AUTHAMD_SCRUB_BIOSDEFAULT, /* leave as BIOS configured */ 6335327Sgavinm AUTHAMD_SCRUB_FIXED, /* assign our chosen rate */ 6345327Sgavinm AUTHAMD_SCRUB_MAX /* use higher of ours and BIOS rate */ 6355327Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX; 6365327Sgavinm 6375327Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf; /* 64K per 0.67 seconds */ 6385327Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe; /* 1MB per 5.3 seconds */ 6395327Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd; /* 1MB per 2.7 seconds */ 6405327Sgavinm 6415327Sgavinm static uint32_t 6425327Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm) 6435327Sgavinm { 6445327Sgavinm uint32_t rate; 6455327Sgavinm 6465327Sgavinm if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) { 6475327Sgavinm cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n", 6485327Sgavinm varnm, AMD_NB_SCRUBCTL_RATE_MAX); 6495327Sgavinm osrate = AMD_NB_SCRUBCTL_RATE_MAX; 6505327Sgavinm } 6515327Sgavinm 6525327Sgavinm switch (authamd_scrub_policy) { 6535327Sgavinm case AUTHAMD_SCRUB_FIXED: 6545327Sgavinm rate = osrate; 6555327Sgavinm break; 6565327Sgavinm 6575327Sgavinm default: 6585327Sgavinm cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - " 6595327Sgavinm "using default policy of AUTHAMD_SCRUB_MAX", 6605327Sgavinm authamd_scrub_policy); 6615327Sgavinm /*FALLTHRU*/ 6625327Sgavinm 6635327Sgavinm case AUTHAMD_SCRUB_MAX: 6645327Sgavinm if (osrate != 0 && biosrate != 0) 6655327Sgavinm rate = MIN(osrate, biosrate); /* small is fast */ 6665327Sgavinm else 6675327Sgavinm rate = osrate ? osrate : biosrate; 6685327Sgavinm } 6695327Sgavinm 6705327Sgavinm return (rate); 6715327Sgavinm } 6725327Sgavinm 6735327Sgavinm /* 6745254Sgavinm * cms_mca_init entry point. 6755254Sgavinm */ 6765254Sgavinm /*ARGSUSED*/ 6775254Sgavinm void 6785254Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks) 6795254Sgavinm { 6805254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 681*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 682*11947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 6835254Sgavinm 6845254Sgavinm /* 6855254Sgavinm * On chips with a NB online spare control register take control 6865254Sgavinm * and clear ECC counts. 6875254Sgavinm */ 6885254Sgavinm if (AUTHAMD_HAS_ONLINESPARECTL(rev) && 6895254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) { 6905254Sgavinm authamd_clear_ecccnt(authamd, B_TRUE); 6915254Sgavinm } 6925254Sgavinm 6935254Sgavinm /* 6945254Sgavinm * And since we are claiming the telemetry stop the BIOS receiving 6955254Sgavinm * an SMI on NB threshold overflow. 6965254Sgavinm */ 6975254Sgavinm if (AUTHAMD_NBMISC_NUM(rev) && 6985254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) { 6995254Sgavinm union mcmsr_nbmisc nbm; 7005254Sgavinm int i; 7015254Sgavinm 7025254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 7035254Sgavinm 7045254Sgavinm for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) { 7055254Sgavinm if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i), 7065254Sgavinm (uint64_t *)&nbm) != CMI_SUCCESS) 7075254Sgavinm continue; 7085254Sgavinm 7095254Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && 7105254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) && 7115254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) { 7125254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0; 7135254Sgavinm } else if (X86_CHIPREV_ATLEAST(rev, 7145254Sgavinm X86_CHIPREV_AMD_10_REV_A) && 7155254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) && 7165254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) { 7175254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0; 7185254Sgavinm } 7195254Sgavinm 7205254Sgavinm (void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i), 7215254Sgavinm MCMSR_VAL(&nbm)); 7225254Sgavinm } 7235254Sgavinm 7245254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 7255254Sgavinm } 7265327Sgavinm 7275327Sgavinm /* 7285327Sgavinm * NB MCA Configuration Register. 7295327Sgavinm */ 7305327Sgavinm if (AUTHAMD_DO_NBMCACFG(rev) && 7315327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) { 73210947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7335327Sgavinm MC_CTL_REG_NBCFG); 7345327Sgavinm 7355327Sgavinm switch (authamd_nb_watchdog_policy) { 7365327Sgavinm case AUTHAMD_NB_WDOG_LEAVEALONE: 7375327Sgavinm break; 7385327Sgavinm 7395327Sgavinm case AUTHAMD_NB_WDOG_DISABLE: 7405327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7415327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK); 7425327Sgavinm val |= AMD_NB_CFG_WDOGTMRDIS; 7435327Sgavinm break; 7445327Sgavinm 7455327Sgavinm default: 7465327Sgavinm cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d " 7475327Sgavinm "unrecognised, using default policy", 7485327Sgavinm authamd_nb_watchdog_policy); 7495327Sgavinm /*FALLTHRU*/ 7505327Sgavinm 7515327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED: 7525327Sgavinm if (!(val & AMD_NB_CFG_WDOGTMRDIS)) 7535327Sgavinm break; /* if enabled leave rate intact */ 7545327Sgavinm /*FALLTHRU*/ 7555327Sgavinm 7565327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE: 7575327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7585327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK | 7595327Sgavinm AMD_NB_CFG_WDOGTMRDIS); 7605327Sgavinm val |= authamd_nb_mcacfg_wdog; 7615327Sgavinm break; 7625327Sgavinm } 7635327Sgavinm 7645327Sgavinm /* 7655327Sgavinm * Bit 0 of the NB MCA Config register is reserved on family 7665327Sgavinm * 0x10. 7675327Sgavinm */ 7685327Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 7695327Sgavinm authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN; 7705327Sgavinm 7715327Sgavinm val &= ~authamd_nb_mcacfg_remove; 7725327Sgavinm val |= authamd_nb_mcacfg_add; 7735327Sgavinm 77410947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 77510947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_NBCFG, val); 7765327Sgavinm } 7775327Sgavinm 7785327Sgavinm /* 7795327Sgavinm * Cache scrubbing. We can't enable DRAM scrubbing since 7805327Sgavinm * we don't know the DRAM base for this node. 7815327Sgavinm */ 7825327Sgavinm if (AUTHAMD_HAS_CHIPSCRUB(rev) && 7835327Sgavinm authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT && 7845327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) { 78510947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7865327Sgavinm MC_CTL_REG_SCRUBCTL); 7875327Sgavinm int l3cap = 0; 7885327Sgavinm 7895327Sgavinm if (AUTHAMD_L3CAPABLE(rev)) { 79010947SSrihari.Venkatesan@Sun.COM l3cap = (authamd_pcicfg_read(procnodeid, 79110947SSrihari.Venkatesan@Sun.COM MC_FUNC_MISCCTL, MC_CTL_REG_NBCAP) & 79210947SSrihari.Venkatesan@Sun.COM MC_NBCAP_L3CAPABLE) != 0; 7935327Sgavinm } 7945327Sgavinm 7955327Sgavinm authamd_scrub_rate_dcache = 7965327Sgavinm authamd_scrubrate(authamd_scrub_rate_dcache, 7975327Sgavinm (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT, 7985327Sgavinm "authamd_scrub_rate_dcache"); 7995327Sgavinm 8005327Sgavinm authamd_scrub_rate_l2cache = 8015327Sgavinm authamd_scrubrate(authamd_scrub_rate_l2cache, 8025327Sgavinm (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT, 8035327Sgavinm "authamd_scrub_rate_l2cache"); 8045327Sgavinm 8055327Sgavinm authamd_scrub_rate_l3cache = l3cap ? 8065327Sgavinm authamd_scrubrate(authamd_scrub_rate_l3cache, 8075327Sgavinm (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT, 8085327Sgavinm "authamd_scrub_rate_l3cache") : 0; 8095327Sgavinm 8105327Sgavinm val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache, 8115327Sgavinm authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache, 8125327Sgavinm val & AMD_NB_SCRUBCTL_DRAM_MASK); 8135327Sgavinm 81410947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 8155327Sgavinm MC_CTL_REG_SCRUBCTL, val); 8165327Sgavinm } 8175327Sgavinm 818*11947Ssrihari.venkatesan@oracle.com /* 819*11947Ssrihari.venkatesan@oracle.com * ECC symbol size. Defaults to 4. 820*11947Ssrihari.venkatesan@oracle.com * Set to 8 on systems that support x8 ECC and have it enabled. 821*11947Ssrihari.venkatesan@oracle.com */ 822*11947Ssrihari.venkatesan@oracle.com if (authamd_chip_once(authamd, AUTHAMD_CFGONCE_ECCSYMSZ)) { 823*11947Ssrihari.venkatesan@oracle.com authamd->amd_shared->ans_eccsymsz = "C4"; 824*11947Ssrihari.venkatesan@oracle.com if (AUTHAMD_SUPPORTS_X8ECC(rev) && 825*11947Ssrihari.venkatesan@oracle.com (authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 826*11947Ssrihari.venkatesan@oracle.com MC_CTL_REG_EXTNBCFG) & MC_EXTNBCFG_ECCSYMSZ)) 827*11947Ssrihari.venkatesan@oracle.com authamd->amd_shared->ans_eccsymsz = "C8"; 828*11947Ssrihari.venkatesan@oracle.com } 8295327Sgavinm } 8305327Sgavinm 8315327Sgavinm /* 8325327Sgavinm * cms_poll_ownermask entry point. 8335327Sgavinm */ 8345327Sgavinm uint64_t 8355327Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl) 8365327Sgavinm { 8375327Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 838*11947Ssrihari.venkatesan@oracle.com struct authamd_nodeshared *ansp = authamd->amd_shared; 8395327Sgavinm hrtime_t now = gethrtime_waitfree(); 840*11947Ssrihari.venkatesan@oracle.com hrtime_t last = ansp->ans_poll_timestamp; 8415327Sgavinm int dopoll = 0; 8425327Sgavinm 8435327Sgavinm if (now - last > 2 * pintvl || last == 0) { 844*11947Ssrihari.venkatesan@oracle.com ansp->ans_pollowner = hdl; 8455327Sgavinm dopoll = 1; 846*11947Ssrihari.venkatesan@oracle.com } else if (ansp->ans_pollowner == hdl) { 8475327Sgavinm dopoll = 1; 8485327Sgavinm } 8495327Sgavinm 8505327Sgavinm if (dopoll) 851*11947Ssrihari.venkatesan@oracle.com ansp->ans_poll_timestamp = now; 8525327Sgavinm 8535327Sgavinm return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB)); 8545327Sgavinm 8555254Sgavinm } 8565254Sgavinm 8575254Sgavinm /* 8585254Sgavinm * cms_bank_logout entry point. 8595254Sgavinm */ 8605254Sgavinm /*ARGSUSED*/ 8615254Sgavinm void 8625254Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status, 8635254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 8645254Sgavinm { 8655254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 8665254Sgavinm struct authamd_logout *msl = mslogout; 867*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 8685254Sgavinm 8695254Sgavinm if (msl == NULL) 8705254Sgavinm return; 8715254Sgavinm 8725254Sgavinm /* 8735254Sgavinm * For main memory ECC errors on revisions with an Online Spare 8745254Sgavinm * Control Register grab the ECC counts by channel and chip-select 8755254Sgavinm * and reset them to 0. 8765254Sgavinm */ 8775254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 8785254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status) && 8795254Sgavinm AUTHAMD_HAS_ONLINESPARECTL(rev)) { 8805254Sgavinm if (authamd_read_ecccnt(authamd, msl)) 8815254Sgavinm authamd_clear_ecccnt(authamd, B_FALSE); 8825254Sgavinm } 8835254Sgavinm } 8845254Sgavinm 8855254Sgavinm /* 8865254Sgavinm * cms_error_action entry point 8875254Sgavinm */ 8885254Sgavinm 8895254Sgavinm int authamd_forgive_uc = 0; /* For test/debug only */ 8905254Sgavinm int authamd_forgive_pcc = 0; /* For test/debug only */ 8915254Sgavinm int authamd_fake_poison = 0; /* For test/debug only */ 8925254Sgavinm 8935254Sgavinm /*ARGSUSED*/ 8945254Sgavinm uint32_t 8955254Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank, 8965254Sgavinm uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) 8975254Sgavinm { 8985254Sgavinm authamd_error_disp_t *disp; 8995254Sgavinm uint32_t rv = 0; 9005254Sgavinm 9015254Sgavinm if (authamd_forgive_uc) 9025254Sgavinm rv |= CMS_ERRSCOPE_CLEARED_UC; 9035254Sgavinm 9045254Sgavinm if (authamd_forgive_pcc) 9055254Sgavinm rv |= CMS_ERRSCOPE_CURCONTEXT_OK; 9065254Sgavinm 9075254Sgavinm if (authamd_fake_poison && status & MSR_MC_STATUS_UC) 9085254Sgavinm rv |= CMS_ERRSCOPE_POISONED; 9095254Sgavinm 9105254Sgavinm if (rv) 9115254Sgavinm return (rv); 9125254Sgavinm 9135254Sgavinm disp = authamd_disp_match(hdl, bank, status, addr, misc, mslogout); 9145254Sgavinm 9155254Sgavinm if (disp == &authamd_gart_disp) { 9165254Sgavinm /* 9175254Sgavinm * GART walk errors set UC and possibly PCC (if source CPU) 9185254Sgavinm * but should not be regarded as terminal. 9195254Sgavinm */ 9205254Sgavinm return (CMS_ERRSCOPE_IGNORE_ERR); 9215254Sgavinm } 9225254Sgavinm 9235254Sgavinm /* 9245254Sgavinm * May also want to consider master abort and target abort. These 9255254Sgavinm * also set UC and PCC (if src CPU) but the requester gets -1 9265254Sgavinm * and I believe the IO stuff in Solaris will handle that. 9275254Sgavinm */ 9285254Sgavinm 9295254Sgavinm return (rv); 9305254Sgavinm } 9315254Sgavinm 9325254Sgavinm /* 9335254Sgavinm * cms_disp_match entry point 9345254Sgavinm */ 9355254Sgavinm /*ARGSUSED*/ 9365254Sgavinm cms_cookie_t 9375254Sgavinm authamd_disp_match(cmi_hdl_t hdl, int bank, uint64_t status, 9385254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 9395254Sgavinm { 9405254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 9415254Sgavinm /* uint16_t errcode = MCAX86_ERRCODE(status); */ 9425254Sgavinm uint16_t exterrcode = AMD_EXT_ERRCODE(status); 943*11947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 9445254Sgavinm 9455254Sgavinm /* 9465254Sgavinm * Recognise main memory ECC errors 9475254Sgavinm */ 9485254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 9495254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status)) { 9505254Sgavinm if (status & AMD_BANK_STAT_CECC) { 9515254Sgavinm return (exterrcode == 0 ? &authamd_memce_disp : 9525254Sgavinm &authamd_ckmemce_disp); 9535254Sgavinm } else if (status & AMD_BANK_STAT_UECC) { 9545254Sgavinm return (exterrcode == 0 ? &authamd_memue_disp : 9555254Sgavinm &authamd_ckmemue_disp); 9565254Sgavinm } 9575254Sgavinm } 9585254Sgavinm 9595254Sgavinm /* 9605254Sgavinm * Recognise GART walk errors 9615254Sgavinm */ 9625254Sgavinm if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status)) 9635254Sgavinm return (&authamd_gart_disp); 9645254Sgavinm 9655254Sgavinm return (NULL); 9665254Sgavinm } 9675254Sgavinm 9685254Sgavinm /* 9695254Sgavinm * cms_ereport_class entry point 9705254Sgavinm */ 9715254Sgavinm /*ARGSUSED*/ 9725254Sgavinm void 9735254Sgavinm authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, 9745254Sgavinm const char **cpuclsp, const char **leafclsp) 9755254Sgavinm { 9765254Sgavinm const authamd_error_disp_t *aed = mscookie; 9775254Sgavinm 9785254Sgavinm if (aed == NULL) 9795254Sgavinm return; 9805254Sgavinm 9815254Sgavinm if (aed->aad_subclass != NULL) 9825254Sgavinm *cpuclsp = aed->aad_subclass; 9835254Sgavinm if (aed->aad_leafclass != NULL) 9845254Sgavinm *leafclsp = aed->aad_leafclass; 9855254Sgavinm } 9865254Sgavinm 9875254Sgavinm /*ARGSUSED*/ 9885254Sgavinm static void 9895254Sgavinm authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd, 9905254Sgavinm nvlist_t *ereport, nv_alloc_t *nva, void *mslogout) 9915254Sgavinm { 9925254Sgavinm nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9935254Sgavinm uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; 9945254Sgavinm authamd_logout_t *msl; 9955254Sgavinm nvlist_t *nvl; 9965254Sgavinm int nelems = 0; 99710947SSrihari.Venkatesan@Sun.COM int i, chan, cs, mc; 99810942STom.Pothier@Sun.COM nvlist_t *board_list = NULL; 9995254Sgavinm 10005254Sgavinm if ((msl = mslogout) == NULL) 10015254Sgavinm return; 10025254Sgavinm 100310947SSrihari.Venkatesan@Sun.COM /* Assume all processors have the same number of nodes */ 1004*11947Ssrihari.venkatesan@oracle.com mc = authamd->amd_shared->ans_procnodeid % 100510947SSrihari.Venkatesan@Sun.COM cpuid_get_procnodes_per_pkg(CPU); 100610947SSrihari.Venkatesan@Sun.COM 10075254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 10085254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 10095254Sgavinm if (msl->aal_eccerrcnt[chan][cs] == 0) 10105254Sgavinm continue; 10115254Sgavinm 10125254Sgavinm if ((nvl = fm_nvlist_create(nva)) == NULL) 10135254Sgavinm continue; 10145254Sgavinm 10155254Sgavinm elems[nelems] = nvl; 10165254Sgavinm counts[nelems++] = msl->aal_eccerrcnt[chan][cs]; 10175254Sgavinm 101810942STom.Pothier@Sun.COM if (!x86gentopo_legacy) { 101910942STom.Pothier@Sun.COM board_list = cmi_hdl_smb_bboard(hdl); 102010942STom.Pothier@Sun.COM if (board_list == NULL) 102110942STom.Pothier@Sun.COM continue; 102210942STom.Pothier@Sun.COM fm_fmri_hc_create(nvl, FM_HC_SCHEME_VERSION, 102310942STom.Pothier@Sun.COM NULL, NULL, board_list, 4, 102410942STom.Pothier@Sun.COM "chip", cmi_hdl_smb_chipid(hdl), 102510942STom.Pothier@Sun.COM "memory-controller", 0, 102610942STom.Pothier@Sun.COM "dram-channel", chan, 102710942STom.Pothier@Sun.COM "chip-select", cs); 102810942STom.Pothier@Sun.COM } else { 102910942STom.Pothier@Sun.COM fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, 103010942STom.Pothier@Sun.COM NULL, NULL, 5, 103110942STom.Pothier@Sun.COM "motherboard", 0, 1032*11947Ssrihari.venkatesan@oracle.com "chip", authamd->amd_shared->ans_chipid, 103310947SSrihari.Venkatesan@Sun.COM "memory-controller", mc, 103410942STom.Pothier@Sun.COM "dram-channel", chan, 103510942STom.Pothier@Sun.COM "chip-select", cs); 103610942STom.Pothier@Sun.COM } 10375254Sgavinm } 10385254Sgavinm } 10395254Sgavinm 10405254Sgavinm if (nelems == 0) 10415254Sgavinm return; 10425254Sgavinm 10435254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE, 10445254Sgavinm DATA_TYPE_NVLIST_ARRAY, nelems, elems, 10455254Sgavinm NULL); 10465254Sgavinm 10475254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT, 10485254Sgavinm DATA_TYPE_UINT8_ARRAY, nelems, &counts[0], 10495254Sgavinm NULL); 10505254Sgavinm 10515254Sgavinm for (i = 0; i < nelems; i++) 10525254Sgavinm fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE); 10535254Sgavinm } 10545254Sgavinm 10555254Sgavinm /* 10565254Sgavinm * cms_ereport_add_logout entry point 10575254Sgavinm */ 10585254Sgavinm /*ARGSUSED*/ 10595254Sgavinm void 10605254Sgavinm authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva, 10615254Sgavinm int bank, uint64_t status, uint64_t addr, uint64_t misc, 10625254Sgavinm void *mslogout, cms_cookie_t mscookie) 10635254Sgavinm { 10645254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 10655254Sgavinm const authamd_error_disp_t *aed = mscookie; 10665254Sgavinm uint64_t members; 10675254Sgavinm 10685254Sgavinm if (aed == NULL) 10695254Sgavinm return; 10705254Sgavinm 10715254Sgavinm members = aed->aad_ereport_members; 10725254Sgavinm 10735254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) { 10745254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND, 10755254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status), 10765254Sgavinm NULL); 10775254Sgavinm 10785254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10795254Sgavinm fm_payload_set(ereport, 10805254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 10815254Sgavinm DATA_TYPE_STRING, "E", 10825254Sgavinm NULL); 10835254Sgavinm } 10845254Sgavinm } 10855254Sgavinm 10865254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) { 10875254Sgavinm fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND, 10885254Sgavinm DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status), 10895254Sgavinm NULL); 10905254Sgavinm 10915254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { 10925254Sgavinm fm_payload_set(ereport, 10935254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, 1094*11947Ssrihari.venkatesan@oracle.com DATA_TYPE_STRING, authamd->amd_shared->ans_eccsymsz, 10955254Sgavinm NULL); 10965254Sgavinm } 10975254Sgavinm } 10985254Sgavinm 10995254Sgavinm if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE && 11005254Sgavinm status & MSR_MC_STATUS_ADDRV) { 11015254Sgavinm authamd_ereport_add_resource(hdl, authamd, ereport, nva, 11025254Sgavinm mslogout); 11035254Sgavinm } 11045254Sgavinm } 11055254Sgavinm 11065254Sgavinm /* 11075254Sgavinm * cms_msrinject entry point 11085254Sgavinm */ 11095254Sgavinm cms_errno_t 11105254Sgavinm authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) 11115254Sgavinm { 11125254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 11135254Sgavinm cms_errno_t rv = CMSERR_BADMSRWRITE; 11145254Sgavinm 11155254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 11165254Sgavinm if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS) 11175254Sgavinm rv = CMS_SUCCESS; 11185254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 11195254Sgavinm 11205254Sgavinm return (rv); 11215254Sgavinm } 11225254Sgavinm 112311168SYanmin.Sun@Sun.COM cms_api_ver_t _cms_api_version = CMS_API_VERSION_1; 11245254Sgavinm 11255254Sgavinm const cms_ops_t _cms_ops = { 11265254Sgavinm authamd_init, /* cms_init */ 11275254Sgavinm NULL, /* cms_post_startup */ 11285254Sgavinm NULL, /* cms_post_mpstartup */ 11295254Sgavinm authamd_logout_size, /* cms_logout_size */ 11305254Sgavinm authamd_mcgctl_val, /* cms_mcgctl_val */ 11315254Sgavinm authamd_bankctl_skipinit, /* cms_bankctl_skipinit */ 11325254Sgavinm authamd_bankctl_val, /* cms_bankctl_val */ 11335254Sgavinm NULL, /* cms_bankstatus_skipinit */ 11345254Sgavinm NULL, /* cms_bankstatus_val */ 11355254Sgavinm authamd_mca_init, /* cms_mca_init */ 11365327Sgavinm authamd_poll_ownermask, /* cms_poll_ownermask */ 11375254Sgavinm authamd_bank_logout, /* cms_bank_logout */ 11385254Sgavinm authamd_error_action, /* cms_error_action */ 11395254Sgavinm authamd_disp_match, /* cms_disp_match */ 11405254Sgavinm authamd_ereport_class, /* cms_ereport_class */ 11415254Sgavinm NULL, /* cms_ereport_detector */ 11425254Sgavinm NULL, /* cms_ereport_includestack */ 11435254Sgavinm authamd_ereport_add_logout, /* cms_ereport_add_logout */ 11445254Sgavinm authamd_msrinject, /* cms_msrinject */ 11455254Sgavinm NULL, /* cms_fini */ 11465254Sgavinm }; 11475254Sgavinm 11485254Sgavinm static struct modlcpu modlcpu = { 11495254Sgavinm &mod_cpuops, 11505254Sgavinm "Generic AMD model-specific MCA" 11515254Sgavinm }; 11525254Sgavinm 11535254Sgavinm static struct modlinkage modlinkage = { 11545254Sgavinm MODREV_1, 11555254Sgavinm (void *)&modlcpu, 11565254Sgavinm NULL 11575254Sgavinm }; 11585254Sgavinm 11595254Sgavinm int 11605254Sgavinm _init(void) 11615254Sgavinm { 11625254Sgavinm return (mod_install(&modlinkage)); 11635254Sgavinm } 11645254Sgavinm 11655254Sgavinm int 11665254Sgavinm _info(struct modinfo *modinfop) 11675254Sgavinm { 11685254Sgavinm return (mod_info(&modlinkage, modinfop)); 11695254Sgavinm } 11705254Sgavinm 11715254Sgavinm int 11725254Sgavinm _fini(void) 11735254Sgavinm { 11745254Sgavinm return (mod_remove(&modlinkage)); 11755254Sgavinm } 1176