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*12437SAdrian.Frost@Sun.COM * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 245254Sgavinm */ 255254Sgavinm 265254Sgavinm /* 275254Sgavinm * "Generic AMD" model-specific support. If no more-specific support can 285254Sgavinm * be found, or such modules declines to initialize, then for AuthenticAMD 295254Sgavinm * cpus this module can have a crack at providing some AMD model-specific 305254Sgavinm * support that at least goes beyond common MCA architectural features 315254Sgavinm * if not down to the nitty-gritty level for a particular model. We 325254Sgavinm * are layered on top of a cpu module, likely cpu.generic, so there is no 335254Sgavinm * need for us to perform common architecturally-accessible functions. 345254Sgavinm */ 355254Sgavinm 365254Sgavinm #include <sys/types.h> 375254Sgavinm #include <sys/cmn_err.h> 385254Sgavinm #include <sys/modctl.h> 395254Sgavinm #include <sys/cpu_module.h> 405254Sgavinm #include <sys/mca_x86.h> 415254Sgavinm #include <sys/pci_cfgspace.h> 425254Sgavinm #include <sys/x86_archext.h> 435254Sgavinm #include <sys/mc_amd.h> 445254Sgavinm #include <sys/fm/protocol.h> 455254Sgavinm #include <sys/fm/cpu/GENAMD.h> 4610942STom.Pothier@Sun.COM #include <sys/fm/smb/fmsmb.h> 4710942STom.Pothier@Sun.COM #include <sys/fm/util.h> 485254Sgavinm #include <sys/nvpair.h> 495254Sgavinm #include <sys/controlregs.h> 505254Sgavinm #include <sys/pghw.h> 515254Sgavinm #include <sys/sunddi.h> 525327Sgavinm #include <sys/sysmacros.h> 535254Sgavinm #include <sys/cpu_module_ms_impl.h> 545254Sgavinm 555254Sgavinm #include "authamd.h" 565254Sgavinm 5710942STom.Pothier@Sun.COM extern int x86gentopo_legacy; /* x86 generic topo support */ 5810942STom.Pothier@Sun.COM 595254Sgavinm int authamd_ms_support_disable = 0; 605254Sgavinm 615254Sgavinm #define AUTHAMD_F_REVS_BCDE \ 625254Sgavinm (X86_CHIPREV_AMD_F_REV_B | X86_CHIPREV_AMD_F_REV_C0 | \ 635254Sgavinm X86_CHIPREV_AMD_F_REV_CG | X86_CHIPREV_AMD_F_REV_D | \ 645254Sgavinm X86_CHIPREV_AMD_F_REV_E) 655254Sgavinm 665254Sgavinm #define AUTHAMD_F_REVS_FG \ 675254Sgavinm (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) 685254Sgavinm 695254Sgavinm #define AUTHAMD_10_REVS_AB \ 705254Sgavinm (X86_CHIPREV_AMD_10_REV_A | X86_CHIPREV_AMD_10_REV_B) 715254Sgavinm 725254Sgavinm /* 735254Sgavinm * Bitmasks of support for various features. Try to enable features 745254Sgavinm * via inclusion in one of these bitmasks and check that at the 755254Sgavinm * feature imlementation - that way new family support may often simply 765254Sgavinm * simply need to update these bitmasks. 775254Sgavinm */ 785254Sgavinm 795254Sgavinm /* 805639Sgavinm * Models that include an on-chip NorthBridge. 815639Sgavinm */ 825639Sgavinm #define AUTHAMD_NBONCHIP(rev) \ 835639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 845639Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 855639Sgavinm 865639Sgavinm /* 875254Sgavinm * Families/revisions for which we can recognise main memory ECC errors. 885254Sgavinm */ 895254Sgavinm #define AUTHAMD_MEMECC_RECOGNISED(rev) \ 905254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 915254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 925254Sgavinm 935254Sgavinm /* 945254Sgavinm * Families/revisions that have an Online Spare Control Register 955254Sgavinm */ 965254Sgavinm #define AUTHAMD_HAS_ONLINESPARECTL(rev) \ 975254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) || \ 985254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 995254Sgavinm 1005254Sgavinm /* 1015327Sgavinm * Families/revisions for which we will perform NB MCA Config changes 1025327Sgavinm */ 1035327Sgavinm #define AUTHAMD_DO_NBMCACFG(rev) \ 1045639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1055327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1065327Sgavinm 1075327Sgavinm /* 1085327Sgavinm * Families/revisions that have chip cache scrubbers. 1095327Sgavinm */ 1105327Sgavinm #define AUTHAMD_HAS_CHIPSCRUB(rev) \ 1115639Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1125327Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1135327Sgavinm 1145327Sgavinm /* 1155254Sgavinm * Families/revisions that have a NB misc register or registers - 1165254Sgavinm * evaluates to 0 if no support, otherwise the number of MC4_MISCj. 1175254Sgavinm */ 1185254Sgavinm #define AUTHAMD_NBMISC_NUM(rev) \ 1195254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F)? 1 : \ 1205254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A) ? 3 : 0)) 1215254Sgavinm 1225254Sgavinm /* 1235254Sgavinm * Families/revision for which we wish not to machine check for GART 1245254Sgavinm * table walk errors - bit 10 of NB CTL. 1255254Sgavinm */ 1265254Sgavinm #define AUTHAMD_NOGARTTBLWLK_MC(rev) \ 1275254Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ 1285254Sgavinm X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1295254Sgavinm 1305254Sgavinm /* 1315327Sgavinm * Families/revisions that are potentially L3 capable 1325327Sgavinm */ 1335327Sgavinm #define AUTHAMD_L3CAPABLE(rev) \ 1345327Sgavinm (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 1355327Sgavinm 1365327Sgavinm /* 13711947Ssrihari.venkatesan@oracle.com * Families/revisions that support x8 ChipKill ECC 13811947Ssrihari.venkatesan@oracle.com */ 13911947Ssrihari.venkatesan@oracle.com #define AUTHAMD_SUPPORTS_X8ECC(rev) \ 14011947Ssrihari.venkatesan@oracle.com (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_D)) 14111947Ssrihari.venkatesan@oracle.com 14211947Ssrihari.venkatesan@oracle.com /* 1435254Sgavinm * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED 1445254Sgavinm * revisions as: 1455254Sgavinm * 1465254Sgavinm * - being reported by the NB 1475254Sgavinm * - being a compound bus/interconnect error (external to chip) 1485254Sgavinm * - having LL of LG 1495254Sgavinm * - having II of MEM (but could still be a master/target abort) 1505254Sgavinm * - having CECC or UECC set 1515254Sgavinm * 1525254Sgavinm * We do not check the extended error code (first nibble of the 1535254Sgavinm * model-specific error code on AMD) since this has changed from 1545254Sgavinm * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10). 1555254Sgavinm * Instead we use CECC/UECC to separate off the master/target 1565254Sgavinm * abort cases. 1575254Sgavinm * 1585254Sgavinm * We insist that the detector be the NorthBridge bank; although 1595254Sgavinm * IC/DC can report some main memory errors, they do not capture 1605254Sgavinm * an address at sufficient resolution to be useful and the NB will 1615254Sgavinm * report most errors. 1625254Sgavinm */ 1635254Sgavinm #define AUTHAMD_IS_MEMECCERR(bank, status) \ 1645254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 1655254Sgavinm MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \ 1665254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 1675254Sgavinm MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \ 1685254Sgavinm ((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC))) 1695254Sgavinm 1705254Sgavinm static authamd_error_disp_t authamd_memce_disp = { 1715254Sgavinm FM_EREPORT_CPU_GENAMD, 1725254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_CE, 1735254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE 1745254Sgavinm }; 1755254Sgavinm 1765254Sgavinm static authamd_error_disp_t authamd_memue_disp = { 1775254Sgavinm FM_EREPORT_CPU_GENAMD, 1785254Sgavinm FM_EREPORT_CPU_GENAMD_MEM_UE, 1795254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE 1805254Sgavinm }; 1815254Sgavinm 1825254Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = { 1835254Sgavinm FM_EREPORT_CPU_GENAMD, 1845254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_CE, 1855254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE 1865254Sgavinm }; 1875254Sgavinm 1885254Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = { 1895254Sgavinm FM_EREPORT_CPU_GENAMD, 1905254Sgavinm FM_EREPORT_CPU_GENAMD_CKMEM_UE, 1915254Sgavinm FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE 1925254Sgavinm }; 1935254Sgavinm 1945254Sgavinm /* 1955254Sgavinm * We recognise GART walk errors as: 1965254Sgavinm * 1975254Sgavinm * - being reported by the NB 1985254Sgavinm * - being a compound TLB error 1995254Sgavinm * - having LL of LG and TT of GEN 2005254Sgavinm * - having UC set 2015254Sgavinm * - possibly having PCC set (if source CPU) 2025254Sgavinm */ 2035254Sgavinm #define AUTHAMD_IS_GARTERR(bank, status) \ 2045254Sgavinm ((bank) == AMD_MCA_BANK_NB && \ 2055254Sgavinm MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \ 2065254Sgavinm MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ 2075254Sgavinm MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \ 2085254Sgavinm (status) & MSR_MC_STATUS_UC) 2095254Sgavinm 2105254Sgavinm static authamd_error_disp_t authamd_gart_disp = { 2115254Sgavinm FM_EREPORT_CPU_GENAMD, /* use generic subclass */ 2125254Sgavinm FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */ 2135254Sgavinm 0 /* no additional payload */ 2145254Sgavinm }; 2155254Sgavinm 2165254Sgavinm 21710947SSrihari.Venkatesan@Sun.COM static struct authamd_nodeshared *authamd_shared[AUTHAMD_MAX_NODES]; 2185254Sgavinm 2195254Sgavinm static int 2205254Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what) 2215254Sgavinm { 22211947Ssrihari.venkatesan@oracle.com return (atomic_set_long_excl(&authamd->amd_shared->ans_cfgonce, 2235254Sgavinm what) == 0 ? B_TRUE : B_FALSE); 2245254Sgavinm } 2255254Sgavinm 2265254Sgavinm static void 22710947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(uint_t procnodeid, uint_t func, uint_t reg, uint32_t val) 2285254Sgavinm { 22910947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2305254Sgavinm ASSERT((func & 7) == func); 23111947Ssrihari.venkatesan@oracle.com ASSERT((reg & 3) == 0 && reg < 4096); 2325254Sgavinm 23310947SSrihari.Venkatesan@Sun.COM cmi_pci_putl(0, procnodeid + 24, func, reg, 0, val); 2345254Sgavinm } 2355254Sgavinm 2365254Sgavinm static uint32_t 23710947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(uint_t procnodeid, uint_t func, uint_t reg) 2385254Sgavinm { 23910947SSrihari.Venkatesan@Sun.COM ASSERT(procnodeid + 24 <= 31); 2405254Sgavinm ASSERT((func & 7) == func); 24111947Ssrihari.venkatesan@oracle.com ASSERT((reg & 3) == 0 && reg < 4096); 2425254Sgavinm 24310947SSrihari.Venkatesan@Sun.COM return (cmi_pci_getl(0, procnodeid + 24, func, reg, 0, 0)); 2445254Sgavinm } 2455254Sgavinm 2465254Sgavinm void 2475254Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2485254Sgavinm { 2495254Sgavinm uint64_t hwcr; 2505254Sgavinm 2515254Sgavinm if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) 2525254Sgavinm return; 2535254Sgavinm 2545254Sgavinm authamd->amd_hwcr = hwcr; 2555254Sgavinm 2565254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2575254Sgavinm hwcr |= AMD_HWCR_MCI_STATUS_WREN; 2585254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2595254Sgavinm } 2605254Sgavinm } 2615254Sgavinm 2625254Sgavinm void 2635254Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd) 2645254Sgavinm { 2655254Sgavinm uint64_t hwcr = authamd->amd_hwcr; 2665254Sgavinm 2675254Sgavinm if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { 2685254Sgavinm hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; 2695254Sgavinm (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); 2705254Sgavinm } 2715254Sgavinm } 2725254Sgavinm 2735254Sgavinm /* 2745254Sgavinm * Read EccCnt repeatedly for all possible channel/chip-select combos: 2755254Sgavinm * 2765254Sgavinm * - read sparectl register 2775254Sgavinm * - if EccErrCntWrEn is set, clear that bit in the just-read value 2785254Sgavinm * and write it back to sparectl; this *may* clobber the EccCnt 2795254Sgavinm * for the channel/chip-select combination currently selected, so 2805254Sgavinm * we leave this bit clear if we had to clear it 2815254Sgavinm * - cycle through all channel/chip-select combinations writing each 2825254Sgavinm * combination to sparectl before reading the register back for 2835254Sgavinm * EccCnt for that combination; since EccErrCntWrEn is clear 2845254Sgavinm * the writes to select what count to read will not themselves 2855254Sgavinm * zero any counts 2865254Sgavinm */ 2875254Sgavinm static int 2885254Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl) 2895254Sgavinm { 2905254Sgavinm union mcreg_sparectl sparectl; 29111947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 29211947Ssrihari.venkatesan@oracle.com uint_t family = authamd->amd_shared->ans_family; 29311947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 2945254Sgavinm int chan, cs; 2955254Sgavinm 2965254Sgavinm /* 2975254Sgavinm * Check for feature support; this macro will test down to the 2985254Sgavinm * family revision number, whereafter we'll switch on family 2995254Sgavinm * assuming that future revisions will use the same register 3005254Sgavinm * format. 3015254Sgavinm */ 3025254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) { 3035254Sgavinm bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt)); 3045254Sgavinm return (0); 3055254Sgavinm } 3065254Sgavinm 3075254Sgavinm MCREG_VAL32(&sparectl) = 30810947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 30910947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3105254Sgavinm 3115254Sgavinm switch (family) { 3125254Sgavinm case AUTHAMD_FAMILY_F: 3135254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0; 3145254Sgavinm break; 3155254Sgavinm 3165254Sgavinm case AUTHAMD_FAMILY_10: 3175254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0; 3185254Sgavinm break; 3195254Sgavinm } 3205254Sgavinm 3215254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 3225254Sgavinm switch (family) { 3235254Sgavinm case AUTHAMD_FAMILY_F: 3245254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 3255254Sgavinm chan; 3265254Sgavinm break; 3275254Sgavinm 3285254Sgavinm case AUTHAMD_FAMILY_10: 3295254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 3305254Sgavinm chan; 3315254Sgavinm break; 3325254Sgavinm } 3335254Sgavinm 3345254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 3355254Sgavinm switch (family) { 3365254Sgavinm case AUTHAMD_FAMILY_F: 3375254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 3385254Sgavinm EccErrCntDramCs) = cs; 3395254Sgavinm break; 3405254Sgavinm 3415254Sgavinm case AUTHAMD_FAMILY_10: 3425254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 3435254Sgavinm EccErrCntDramCs) = cs; 3445254Sgavinm break; 3455254Sgavinm } 3465254Sgavinm 34710947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 3485254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 3495254Sgavinm 35010947SSrihari.Venkatesan@Sun.COM MCREG_VAL32(&sparectl) = authamd_pcicfg_read(procnodeid, 3515254Sgavinm MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); 3525254Sgavinm 3535254Sgavinm switch (family) { 3545254Sgavinm case AUTHAMD_FAMILY_F: 3555254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3565254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCnt); 3575254Sgavinm break; 3585254Sgavinm case AUTHAMD_FAMILY_10: 3595254Sgavinm msl->aal_eccerrcnt[chan][cs] = 3605254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCnt); 3615254Sgavinm break; 3625254Sgavinm } 3635254Sgavinm } 3645254Sgavinm } 3655254Sgavinm 3665254Sgavinm return (1); 3675254Sgavinm } 3685254Sgavinm 3695254Sgavinm /* 3705254Sgavinm * Clear EccCnt for all possible channel/chip-select combos: 3715254Sgavinm * 3725254Sgavinm * - set EccErrCntWrEn in sparectl, if necessary 3735254Sgavinm * - write 0 to EccCnt for all channel/chip-select combinations 3745254Sgavinm * - clear EccErrCntWrEn 3755254Sgavinm * 3765254Sgavinm * If requested also disable the interrupts taken on counter overflow 3775254Sgavinm * and on swap done. 3785254Sgavinm */ 3795254Sgavinm static void 3805254Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint) 3815254Sgavinm { 3825254Sgavinm union mcreg_sparectl sparectl; 38311947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 38411947Ssrihari.venkatesan@oracle.com uint_t family = authamd->amd_shared->ans_family; 38511947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 3865254Sgavinm int chan, cs; 3875254Sgavinm 3885254Sgavinm if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) 3895254Sgavinm return; 3905254Sgavinm 3915254Sgavinm MCREG_VAL32(&sparectl) = 39210947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 39310947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_SPARECTL); 3945254Sgavinm 3955254Sgavinm switch (family) { 3965254Sgavinm case AUTHAMD_FAMILY_F: 3975254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; 3985254Sgavinm if (clrint) { 3995254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; 4005254Sgavinm MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; 4015254Sgavinm } 4025254Sgavinm break; 4035254Sgavinm 4045254Sgavinm case AUTHAMD_FAMILY_10: 4055254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1; 4065254Sgavinm if (clrint) { 4075254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0; 4085254Sgavinm MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0; 4095254Sgavinm } 4105254Sgavinm break; 4115254Sgavinm } 4125254Sgavinm 41310947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4145254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4155254Sgavinm 4165254Sgavinm for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { 4175254Sgavinm switch (family) { 4185254Sgavinm case AUTHAMD_FAMILY_F: 4195254Sgavinm MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = 4205254Sgavinm chan; 4215254Sgavinm break; 4225254Sgavinm 4235254Sgavinm case AUTHAMD_FAMILY_10: 4245254Sgavinm MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = 4255254Sgavinm chan; 4265254Sgavinm break; 4275254Sgavinm } 4285254Sgavinm 4295254Sgavinm for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { 4305254Sgavinm switch (family) { 4315254Sgavinm case AUTHAMD_FAMILY_F: 4325254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4335254Sgavinm EccErrCntDramCs) = cs; 4345254Sgavinm MCREG_FIELD_F_revFG(&sparectl, 4355254Sgavinm EccErrCnt) = 0; 4365254Sgavinm break; 4375254Sgavinm 4385254Sgavinm case AUTHAMD_FAMILY_10: 4395254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4405254Sgavinm EccErrCntDramCs) = cs; 4415254Sgavinm MCREG_FIELD_10_revAB(&sparectl, 4425254Sgavinm EccErrCnt) = 0; 4435254Sgavinm break; 4445254Sgavinm } 4455254Sgavinm 44610947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 4475254Sgavinm MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); 4485254Sgavinm } 4495254Sgavinm } 4505254Sgavinm } 4515254Sgavinm 45210026SKuriakose.Kuruvilla@Sun.COM 45310026SKuriakose.Kuruvilla@Sun.COM /* 45410026SKuriakose.Kuruvilla@Sun.COM * Return 45510026SKuriakose.Kuruvilla@Sun.COM * 1: supported 45610026SKuriakose.Kuruvilla@Sun.COM * 0: unsupported 45710026SKuriakose.Kuruvilla@Sun.COM */ 45810026SKuriakose.Kuruvilla@Sun.COM static int 45910947SSrihari.Venkatesan@Sun.COM authamd_supported(cmi_hdl_t hdl) 46010026SKuriakose.Kuruvilla@Sun.COM { 46110947SSrihari.Venkatesan@Sun.COM uint_t family = cmi_hdl_family(hdl); 46210026SKuriakose.Kuruvilla@Sun.COM 46310947SSrihari.Venkatesan@Sun.COM switch (family) { 46410947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_6: 46510947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_F: 46610947SSrihari.Venkatesan@Sun.COM case AUTHAMD_FAMILY_10: 46710947SSrihari.Venkatesan@Sun.COM return (1); 46810947SSrihari.Venkatesan@Sun.COM default: 46910947SSrihari.Venkatesan@Sun.COM return (0); 47010026SKuriakose.Kuruvilla@Sun.COM } 47110026SKuriakose.Kuruvilla@Sun.COM } 47210026SKuriakose.Kuruvilla@Sun.COM 4735254Sgavinm /* 4745254Sgavinm * cms_init entry point. 4755254Sgavinm * 4765254Sgavinm * This module provides broad model-specific support for AMD families 4775254Sgavinm * 0x6, 0xf and 0x10. Future families will have to be evaluated once their 4785254Sgavinm * documentation is available. 4795254Sgavinm */ 4805254Sgavinm int 4815254Sgavinm authamd_init(cmi_hdl_t hdl, void **datap) 4825254Sgavinm { 4835254Sgavinm uint_t chipid = cmi_hdl_chipid(hdl); 48410947SSrihari.Venkatesan@Sun.COM uint_t procnodeid = cmi_hdl_procnodeid(hdl); 48510947SSrihari.Venkatesan@Sun.COM struct authamd_nodeshared *sp, *osp; 4865254Sgavinm uint_t family = cmi_hdl_family(hdl); 48710026SKuriakose.Kuruvilla@Sun.COM uint32_t rev = cmi_hdl_chiprev(hdl); 4885254Sgavinm authamd_data_t *authamd; 4895254Sgavinm uint64_t cap; 4905254Sgavinm 49110026SKuriakose.Kuruvilla@Sun.COM if (authamd_ms_support_disable || 49210947SSrihari.Venkatesan@Sun.COM !authamd_supported(hdl)) 4935254Sgavinm return (ENOTSUP); 4945254Sgavinm 4955254Sgavinm if (!(x86_feature & X86_MCA)) 4965254Sgavinm return (ENOTSUP); 4975254Sgavinm 4985254Sgavinm if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) 4995254Sgavinm return (ENOTSUP); 5005254Sgavinm 5015254Sgavinm if (!(cap & MCG_CAP_CTL_P)) 5025254Sgavinm return (ENOTSUP); 5035254Sgavinm 5045254Sgavinm authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP); 5055254Sgavinm cmi_hdl_hold(hdl); /* release in fini */ 5065254Sgavinm authamd->amd_hdl = hdl; 5075254Sgavinm 50810947SSrihari.Venkatesan@Sun.COM if ((sp = authamd_shared[procnodeid]) == NULL) { 50910947SSrihari.Venkatesan@Sun.COM sp = kmem_zalloc(sizeof (struct authamd_nodeshared), KM_SLEEP); 51011947Ssrihari.venkatesan@oracle.com sp->ans_chipid = chipid; 51111947Ssrihari.venkatesan@oracle.com sp->ans_procnodeid = procnodeid; 51211947Ssrihari.venkatesan@oracle.com sp->ans_family = family; 51311947Ssrihari.venkatesan@oracle.com sp->ans_rev = rev; 5146000Sgavinm membar_producer(); 5156000Sgavinm 51610947SSrihari.Venkatesan@Sun.COM osp = atomic_cas_ptr(&authamd_shared[procnodeid], NULL, sp); 5175254Sgavinm if (osp != NULL) { 51810947SSrihari.Venkatesan@Sun.COM kmem_free(sp, sizeof (struct authamd_nodeshared)); 5195254Sgavinm sp = osp; 5205254Sgavinm } 5215254Sgavinm } 5225254Sgavinm authamd->amd_shared = sp; 5235254Sgavinm 5245254Sgavinm return (0); 5255254Sgavinm } 5265254Sgavinm 5275254Sgavinm /* 5285254Sgavinm * cms_logout_size entry point. 5295254Sgavinm */ 5305254Sgavinm /*ARGSUSED*/ 5315254Sgavinm size_t 5325254Sgavinm authamd_logout_size(cmi_hdl_t hdl) 5335254Sgavinm { 5345254Sgavinm return (sizeof (struct authamd_logout)); 5355254Sgavinm } 5365254Sgavinm 5375254Sgavinm /* 5385254Sgavinm * cms_mcgctl_val entry point 5395254Sgavinm * 5405254Sgavinm * Instead of setting all bits to 1 we can set just those for the 5415254Sgavinm * error detector banks known to exist. 5425254Sgavinm */ 5435254Sgavinm /*ARGSUSED*/ 5445254Sgavinm uint64_t 5455254Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed) 5465254Sgavinm { 5475254Sgavinm return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed); 5485254Sgavinm } 5495254Sgavinm 5505254Sgavinm /* 5515254Sgavinm * cms_bankctl_skipinit entry point 5525254Sgavinm * 5535254Sgavinm * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC) 5545254Sgavinm * may produce spurious machine checks. 5555639Sgavinm * 5565639Sgavinm * Only allow a single core to setup the NorthBridge MCi_CTL register. 5575254Sgavinm */ 5585254Sgavinm /*ARGSUSED*/ 5595254Sgavinm boolean_t 5605254Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank) 5615254Sgavinm { 5625254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 56311947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 5645254Sgavinm 56511947Ssrihari.venkatesan@oracle.com if (authamd->amd_shared->ans_family == AUTHAMD_FAMILY_6) 5665639Sgavinm return (bank == 0 ? B_TRUE : B_FALSE); 5675639Sgavinm 5685639Sgavinm if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) { 5695639Sgavinm return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) == 5705639Sgavinm B_TRUE ? B_FALSE : B_TRUE); 5715639Sgavinm } 5725639Sgavinm 5735639Sgavinm return (B_FALSE); 5745254Sgavinm } 5755254Sgavinm 5765254Sgavinm /* 5775254Sgavinm * cms_bankctl_val entry point 5785254Sgavinm */ 5795254Sgavinm uint64_t 5805254Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed) 5815254Sgavinm { 5825254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 58311947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 5845254Sgavinm uint64_t val = proposed; 5855254Sgavinm 5865254Sgavinm /* 5875254Sgavinm * The Intel MCA says we can write all 1's to enable #MC for 5885254Sgavinm * all errors, and AMD docs say much the same. But, depending 5895254Sgavinm * perhaps on other config registers, taking machine checks 5905254Sgavinm * for some errors such as GART TLB errors and master/target 5915254Sgavinm * aborts may be bad - they set UC and sometime also PCC, but 5925254Sgavinm * we should not always panic for these error types. 5935254Sgavinm * 5945254Sgavinm * Our cms_error_action entry point can suppress such panics, 5955254Sgavinm * however we can also use the cms_bankctl_val entry point to 5965254Sgavinm * veto enabling of some of the known villains in the first place. 5975254Sgavinm */ 5985254Sgavinm if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev)) 5995254Sgavinm val &= ~AMD_NB_EN_GARTTBLWK; 6005254Sgavinm 6015254Sgavinm return (val); 6025254Sgavinm } 6035254Sgavinm 6045254Sgavinm /* 6055327Sgavinm * Bits to add to NB MCA config (after watchdog config). 6065327Sgavinm */ 6075327Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN; 6085327Sgavinm 6095327Sgavinm /* 6105327Sgavinm * Bits to remove from NB MCA config (after watchdog config) 6115327Sgavinm */ 6125327Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN; 6135327Sgavinm 6145327Sgavinm /* 6155327Sgavinm * NB Watchdog policy, and rate we use if enabling. 6165327Sgavinm */ 6175327Sgavinm enum { 6185327Sgavinm AUTHAMD_NB_WDOG_LEAVEALONE, 6195327Sgavinm AUTHAMD_NB_WDOG_DISABLE, 6205327Sgavinm AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED, 6215327Sgavinm AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE 6225327Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED; 6235327Sgavinm 6245327Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 | 6255327Sgavinm AMD_NB_CFG_WDOGTMRBASESEL_1MS; 6265327Sgavinm 6275327Sgavinm /* 6285327Sgavinm * Per-core cache scrubbing policy and rates. 6295327Sgavinm */ 6305327Sgavinm enum { 6315327Sgavinm AUTHAMD_SCRUB_BIOSDEFAULT, /* leave as BIOS configured */ 6325327Sgavinm AUTHAMD_SCRUB_FIXED, /* assign our chosen rate */ 6335327Sgavinm AUTHAMD_SCRUB_MAX /* use higher of ours and BIOS rate */ 6345327Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX; 6355327Sgavinm 6365327Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf; /* 64K per 0.67 seconds */ 6375327Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe; /* 1MB per 5.3 seconds */ 6385327Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd; /* 1MB per 2.7 seconds */ 6395327Sgavinm 6405327Sgavinm static uint32_t 6415327Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm) 6425327Sgavinm { 6435327Sgavinm uint32_t rate; 6445327Sgavinm 6455327Sgavinm if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) { 6465327Sgavinm cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n", 6475327Sgavinm varnm, AMD_NB_SCRUBCTL_RATE_MAX); 6485327Sgavinm osrate = AMD_NB_SCRUBCTL_RATE_MAX; 6495327Sgavinm } 6505327Sgavinm 6515327Sgavinm switch (authamd_scrub_policy) { 6525327Sgavinm case AUTHAMD_SCRUB_FIXED: 6535327Sgavinm rate = osrate; 6545327Sgavinm break; 6555327Sgavinm 6565327Sgavinm default: 6575327Sgavinm cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - " 6585327Sgavinm "using default policy of AUTHAMD_SCRUB_MAX", 6595327Sgavinm authamd_scrub_policy); 6605327Sgavinm /*FALLTHRU*/ 6615327Sgavinm 6625327Sgavinm case AUTHAMD_SCRUB_MAX: 6635327Sgavinm if (osrate != 0 && biosrate != 0) 6645327Sgavinm rate = MIN(osrate, biosrate); /* small is fast */ 6655327Sgavinm else 6665327Sgavinm rate = osrate ? osrate : biosrate; 6675327Sgavinm } 6685327Sgavinm 6695327Sgavinm return (rate); 6705327Sgavinm } 6715327Sgavinm 6725327Sgavinm /* 6735254Sgavinm * cms_mca_init entry point. 6745254Sgavinm */ 6755254Sgavinm /*ARGSUSED*/ 6765254Sgavinm void 6775254Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks) 6785254Sgavinm { 6795254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 68011947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 68111947Ssrihari.venkatesan@oracle.com uint_t procnodeid = authamd->amd_shared->ans_procnodeid; 6825254Sgavinm 6835254Sgavinm /* 6845254Sgavinm * On chips with a NB online spare control register take control 6855254Sgavinm * and clear ECC counts. 6865254Sgavinm */ 6875254Sgavinm if (AUTHAMD_HAS_ONLINESPARECTL(rev) && 6885254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) { 6895254Sgavinm authamd_clear_ecccnt(authamd, B_TRUE); 6905254Sgavinm } 6915254Sgavinm 6925254Sgavinm /* 6935254Sgavinm * And since we are claiming the telemetry stop the BIOS receiving 6945254Sgavinm * an SMI on NB threshold overflow. 6955254Sgavinm */ 6965254Sgavinm if (AUTHAMD_NBMISC_NUM(rev) && 6975254Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) { 6985254Sgavinm union mcmsr_nbmisc nbm; 6995254Sgavinm int i; 7005254Sgavinm 7015254Sgavinm authamd_bankstatus_prewrite(hdl, authamd); 7025254Sgavinm 7035254Sgavinm for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) { 7045254Sgavinm if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i), 7055254Sgavinm (uint64_t *)&nbm) != CMI_SUCCESS) 7065254Sgavinm continue; 7075254Sgavinm 7085254Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && 7095254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) && 7105254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) { 7115254Sgavinm MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0; 7125254Sgavinm } else if (X86_CHIPREV_ATLEAST(rev, 7135254Sgavinm X86_CHIPREV_AMD_10_REV_A) && 7145254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) && 7155254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) { 7165254Sgavinm MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0; 7175254Sgavinm } 7185254Sgavinm 7195254Sgavinm (void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i), 7205254Sgavinm MCMSR_VAL(&nbm)); 7215254Sgavinm } 7225254Sgavinm 7235254Sgavinm authamd_bankstatus_postwrite(hdl, authamd); 7245254Sgavinm } 7255327Sgavinm 7265327Sgavinm /* 7275327Sgavinm * NB MCA Configuration Register. 7285327Sgavinm */ 7295327Sgavinm if (AUTHAMD_DO_NBMCACFG(rev) && 7305327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) { 73110947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7325327Sgavinm MC_CTL_REG_NBCFG); 7335327Sgavinm 7345327Sgavinm switch (authamd_nb_watchdog_policy) { 7355327Sgavinm case AUTHAMD_NB_WDOG_LEAVEALONE: 7365327Sgavinm break; 7375327Sgavinm 7385327Sgavinm case AUTHAMD_NB_WDOG_DISABLE: 7395327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7405327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK); 7415327Sgavinm val |= AMD_NB_CFG_WDOGTMRDIS; 7425327Sgavinm break; 7435327Sgavinm 7445327Sgavinm default: 7455327Sgavinm cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d " 7465327Sgavinm "unrecognised, using default policy", 7475327Sgavinm authamd_nb_watchdog_policy); 7485327Sgavinm /*FALLTHRU*/ 7495327Sgavinm 7505327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED: 7515327Sgavinm if (!(val & AMD_NB_CFG_WDOGTMRDIS)) 7525327Sgavinm break; /* if enabled leave rate intact */ 7535327Sgavinm /*FALLTHRU*/ 7545327Sgavinm 7555327Sgavinm case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE: 7565327Sgavinm val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK | 7575327Sgavinm AMD_NB_CFG_WDOGTMRCNTSEL_MASK | 7585327Sgavinm AMD_NB_CFG_WDOGTMRDIS); 7595327Sgavinm val |= authamd_nb_mcacfg_wdog; 7605327Sgavinm break; 7615327Sgavinm } 7625327Sgavinm 7635327Sgavinm /* 7645327Sgavinm * Bit 0 of the NB MCA Config register is reserved on family 7655327Sgavinm * 0x10. 7665327Sgavinm */ 7675327Sgavinm if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) 7685327Sgavinm authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN; 7695327Sgavinm 7705327Sgavinm val &= ~authamd_nb_mcacfg_remove; 7715327Sgavinm val |= authamd_nb_mcacfg_add; 7725327Sgavinm 77310947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 77410947SSrihari.Venkatesan@Sun.COM MC_CTL_REG_NBCFG, val); 7755327Sgavinm } 7765327Sgavinm 7775327Sgavinm /* 7785327Sgavinm * Cache scrubbing. We can't enable DRAM scrubbing since 7795327Sgavinm * we don't know the DRAM base for this node. 7805327Sgavinm */ 7815327Sgavinm if (AUTHAMD_HAS_CHIPSCRUB(rev) && 7825327Sgavinm authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT && 7835327Sgavinm authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) { 78410947SSrihari.Venkatesan@Sun.COM uint32_t val = authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 7855327Sgavinm MC_CTL_REG_SCRUBCTL); 7865327Sgavinm int l3cap = 0; 7875327Sgavinm 7885327Sgavinm if (AUTHAMD_L3CAPABLE(rev)) { 78910947SSrihari.Venkatesan@Sun.COM l3cap = (authamd_pcicfg_read(procnodeid, 79010947SSrihari.Venkatesan@Sun.COM MC_FUNC_MISCCTL, MC_CTL_REG_NBCAP) & 79110947SSrihari.Venkatesan@Sun.COM MC_NBCAP_L3CAPABLE) != 0; 7925327Sgavinm } 7935327Sgavinm 7945327Sgavinm authamd_scrub_rate_dcache = 7955327Sgavinm authamd_scrubrate(authamd_scrub_rate_dcache, 7965327Sgavinm (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT, 7975327Sgavinm "authamd_scrub_rate_dcache"); 7985327Sgavinm 7995327Sgavinm authamd_scrub_rate_l2cache = 8005327Sgavinm authamd_scrubrate(authamd_scrub_rate_l2cache, 8015327Sgavinm (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT, 8025327Sgavinm "authamd_scrub_rate_l2cache"); 8035327Sgavinm 8045327Sgavinm authamd_scrub_rate_l3cache = l3cap ? 8055327Sgavinm authamd_scrubrate(authamd_scrub_rate_l3cache, 8065327Sgavinm (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT, 8075327Sgavinm "authamd_scrub_rate_l3cache") : 0; 8085327Sgavinm 8095327Sgavinm val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache, 8105327Sgavinm authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache, 8115327Sgavinm val & AMD_NB_SCRUBCTL_DRAM_MASK); 8125327Sgavinm 81310947SSrihari.Venkatesan@Sun.COM authamd_pcicfg_write(procnodeid, MC_FUNC_MISCCTL, 8145327Sgavinm MC_CTL_REG_SCRUBCTL, val); 8155327Sgavinm } 8165327Sgavinm 81711947Ssrihari.venkatesan@oracle.com /* 81811947Ssrihari.venkatesan@oracle.com * ECC symbol size. Defaults to 4. 81911947Ssrihari.venkatesan@oracle.com * Set to 8 on systems that support x8 ECC and have it enabled. 82011947Ssrihari.venkatesan@oracle.com */ 82111947Ssrihari.venkatesan@oracle.com if (authamd_chip_once(authamd, AUTHAMD_CFGONCE_ECCSYMSZ)) { 82211947Ssrihari.venkatesan@oracle.com authamd->amd_shared->ans_eccsymsz = "C4"; 82311947Ssrihari.venkatesan@oracle.com if (AUTHAMD_SUPPORTS_X8ECC(rev) && 82411947Ssrihari.venkatesan@oracle.com (authamd_pcicfg_read(procnodeid, MC_FUNC_MISCCTL, 82511947Ssrihari.venkatesan@oracle.com MC_CTL_REG_EXTNBCFG) & MC_EXTNBCFG_ECCSYMSZ)) 82611947Ssrihari.venkatesan@oracle.com authamd->amd_shared->ans_eccsymsz = "C8"; 82711947Ssrihari.venkatesan@oracle.com } 8285327Sgavinm } 8295327Sgavinm 8305327Sgavinm /* 8315327Sgavinm * cms_poll_ownermask entry point. 8325327Sgavinm */ 8335327Sgavinm uint64_t 8345327Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl) 8355327Sgavinm { 8365327Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 83711947Ssrihari.venkatesan@oracle.com struct authamd_nodeshared *ansp = authamd->amd_shared; 8385327Sgavinm hrtime_t now = gethrtime_waitfree(); 83911947Ssrihari.venkatesan@oracle.com hrtime_t last = ansp->ans_poll_timestamp; 8405327Sgavinm int dopoll = 0; 8415327Sgavinm 8425327Sgavinm if (now - last > 2 * pintvl || last == 0) { 84311947Ssrihari.venkatesan@oracle.com ansp->ans_pollowner = hdl; 8445327Sgavinm dopoll = 1; 84511947Ssrihari.venkatesan@oracle.com } else if (ansp->ans_pollowner == hdl) { 8465327Sgavinm dopoll = 1; 8475327Sgavinm } 8485327Sgavinm 8495327Sgavinm if (dopoll) 85011947Ssrihari.venkatesan@oracle.com ansp->ans_poll_timestamp = now; 8515327Sgavinm 8525327Sgavinm return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB)); 8535327Sgavinm 8545254Sgavinm } 8555254Sgavinm 8565254Sgavinm /* 8575254Sgavinm * cms_bank_logout entry point. 8585254Sgavinm */ 8595254Sgavinm /*ARGSUSED*/ 8605254Sgavinm void 8615254Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status, 8625254Sgavinm uint64_t addr, uint64_t misc, void *mslogout) 8635254Sgavinm { 8645254Sgavinm authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); 8655254Sgavinm struct authamd_logout *msl = mslogout; 86611947Ssrihari.venkatesan@oracle.com uint32_t rev = authamd->amd_shared->ans_rev; 8675254Sgavinm 8685254Sgavinm if (msl == NULL) 8695254Sgavinm return; 8705254Sgavinm 8715254Sgavinm /* 8725254Sgavinm * For main memory ECC errors on revisions with an Online Spare 8735254Sgavinm * Control Register grab the ECC counts by channel and chip-select 8745254Sgavinm * and reset them to 0. 8755254Sgavinm */ 8765254Sgavinm if (AUTHAMD_MEMECC_RECOGNISED(rev) && 8775254Sgavinm AUTHAMD_IS_MEMECCERR(bank, status) && 8785254Sgavinm AUTHAMD_HAS_ONLINESPARECTL(rev)) { 8795254Sgavinm if (authamd_read_ecccnt(authamd, msl)) 8805254Sgavinm authamd_clear_ecccnt(authamd, B_FALSE); 8815254Sgavinm } 8825254Sgavinm } 8835254Sgavinm 8845254Sgavinm /* 8855254Sgavinm * cms_error_action entry point 8865254Sgavinm */ 8875254Sgavinm 8885254Sgavinm int authamd_forgive_uc = 0; /* For test/debug only */ 8895254Sgavinm int authamd_forgive_pcc = 0; /* For test/debug only */ 8905254Sgavinm int authamd_fake_poison = 0; /* For test/debug only */ 8915254Sgavinm 8925254Sgavinm /*ARGSUSED*/ 8935254Sgavinm uint32_t 8945254Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank, 8955254Sgavinm uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) 8965254Sgavinm { 8975254Sgavinm authamd_error_disp_t *disp; 8985254Sgavinm uint32_t rv = 0; 8995254Sgavinm 9005254Sgavinm if (authamd_forgive_uc) 9015254Sgavinm rv |= CMS_ERRSCOPE_CLEARED_UC; 9025254Sgavinm 9035254Sgavinm if (authamd_forgive_pcc) 9045254Sgavinm rv |= CMS_ERRSCOPE_CURCONTEXT_OK; 9055254Sgavinm 9065254Sgavinm if (authamd_fake_poison && status & MSR_MC_STATUS_UC) 9075254Sgavinm rv |= CMS_ERRSCOPE_POISONED; 9085254Sgavinm 9095254Sgavinm if (rv) 9105254Sgavinm return (rv); 9115254Sgavinm 912*12437SAdrian.Frost@Sun.COM disp = authamd_disp_match(hdl, ismc, bank, status, addr, misc, 913*12437SAdrian.Frost@Sun.COM 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 937*12437SAdrian.Frost@Sun.COM authamd_disp_match(cmi_hdl_t hdl, int ismc, 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); 94311947Ssrihari.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 */ 100411947Ssrihari.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, 103211947Ssrihari.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, 109411947Ssrihari.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 1123*12437SAdrian.Frost@Sun.COM cms_api_ver_t _cms_api_version = CMS_API_VERSION_2; 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