xref: /onnv-gate/usr/src/uts/i86pc/cpu/authenticamd/authamd_main.c (revision 6000:0b49d51041b9)
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*6000Sgavinm  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245254Sgavinm  * Use is subject to license terms.
255254Sgavinm  */
265254Sgavinm 
275254Sgavinm #pragma ident	"%Z%%M%	%I%	%E% SMI"
285254Sgavinm 
295254Sgavinm /*
305254Sgavinm  * "Generic AMD" model-specific support.  If no more-specific support can
315254Sgavinm  * be found, or such modules declines to initialize, then for AuthenticAMD
325254Sgavinm  * cpus this module can have a crack at providing some AMD model-specific
335254Sgavinm  * support that at least goes beyond common MCA architectural features
345254Sgavinm  * if not down to the nitty-gritty level for a particular model.  We
355254Sgavinm  * are layered on top of a cpu module, likely cpu.generic, so there is no
365254Sgavinm  * need for us to perform common architecturally-accessible functions.
375254Sgavinm  */
385254Sgavinm 
395254Sgavinm #include <sys/types.h>
405254Sgavinm #include <sys/cmn_err.h>
415254Sgavinm #include <sys/modctl.h>
425254Sgavinm #include <sys/cpu_module.h>
435254Sgavinm #include <sys/mca_x86.h>
445254Sgavinm #include <sys/pci_cfgspace.h>
455254Sgavinm #include <sys/x86_archext.h>
465254Sgavinm #include <sys/mc_amd.h>
475254Sgavinm #include <sys/fm/protocol.h>
485254Sgavinm #include <sys/fm/cpu/GENAMD.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 
585254Sgavinm int authamd_ms_support_disable = 0;
595254Sgavinm 
605254Sgavinm #define	AUTHAMD_F_REVS_BCDE \
615254Sgavinm 	(X86_CHIPREV_AMD_F_REV_B | X86_CHIPREV_AMD_F_REV_C0 | \
625254Sgavinm 	X86_CHIPREV_AMD_F_REV_CG | X86_CHIPREV_AMD_F_REV_D | \
635254Sgavinm 	X86_CHIPREV_AMD_F_REV_E)
645254Sgavinm 
655254Sgavinm #define	AUTHAMD_F_REVS_FG \
665254Sgavinm 	(X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G)
675254Sgavinm 
685254Sgavinm #define	AUTHAMD_10_REVS_AB \
695254Sgavinm 	(X86_CHIPREV_AMD_10_REV_A | X86_CHIPREV_AMD_10_REV_B)
705254Sgavinm 
715254Sgavinm /*
725254Sgavinm  * Bitmasks of support for various features.  Try to enable features
735254Sgavinm  * via inclusion in one of these bitmasks and check that at the
745254Sgavinm  * feature imlementation - that way new family support may often simply
755254Sgavinm  * simply need to update these bitmasks.
765254Sgavinm  */
775254Sgavinm 
785254Sgavinm /*
795254Sgavinm  * Families that this module will provide some model-specific
805254Sgavinm  * support for (if no more-specific module claims it first).
815254Sgavinm  * We try to support whole families rather than differentiate down
825254Sgavinm  * to revision.
835254Sgavinm  */
845254Sgavinm #define	AUTHAMD_SUPPORTED(fam) \
855254Sgavinm 	((fam) == AUTHAMD_FAMILY_6 || (fam) == AUTHAMD_FAMILY_F || \
865254Sgavinm 	(fam) == AUTHAMD_FAMILY_10)
875254Sgavinm 
885254Sgavinm /*
895639Sgavinm  * Models that include an on-chip NorthBridge.
905639Sgavinm  */
915639Sgavinm #define	AUTHAMD_NBONCHIP(rev) \
925639Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \
935639Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
945639Sgavinm 
955639Sgavinm /*
965254Sgavinm  * Families/revisions for which we can recognise main memory ECC errors.
975254Sgavinm  */
985254Sgavinm #define	AUTHAMD_MEMECC_RECOGNISED(rev) \
995254Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \
1005254Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1015254Sgavinm 
1025254Sgavinm /*
1035254Sgavinm  * Families/revisions that have an Online Spare Control Register
1045254Sgavinm  */
1055254Sgavinm #define	AUTHAMD_HAS_ONLINESPARECTL(rev) \
1065254Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) || \
1075254Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1085254Sgavinm 
1095254Sgavinm /*
1105327Sgavinm  * Families/revisions for which we will perform NB MCA Config changes
1115327Sgavinm  */
1125327Sgavinm #define	AUTHAMD_DO_NBMCACFG(rev) \
1135639Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \
1145327Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1155327Sgavinm 
1165327Sgavinm /*
1175327Sgavinm  * Families/revisions that have chip cache scrubbers.
1185327Sgavinm  */
1195327Sgavinm #define	AUTHAMD_HAS_CHIPSCRUB(rev) \
1205639Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \
1215327Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1225327Sgavinm 
1235327Sgavinm /*
1245254Sgavinm  * Families/revisions that have a NB misc register or registers -
1255254Sgavinm  * evaluates to 0 if no support, otherwise the number of MC4_MISCj.
1265254Sgavinm  */
1275254Sgavinm #define	AUTHAMD_NBMISC_NUM(rev) \
1285254Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F)? 1 : \
1295254Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A) ? 3 : 0))
1305254Sgavinm 
1315254Sgavinm /*
1325254Sgavinm  * Families/revision for which we wish not to machine check for GART
1335254Sgavinm  * table walk errors - bit 10 of NB CTL.
1345254Sgavinm  */
1355254Sgavinm #define	AUTHAMD_NOGARTTBLWLK_MC(rev) \
1365254Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \
1375254Sgavinm 	X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1385254Sgavinm 
1395254Sgavinm /*
1405327Sgavinm  * Families/revisions that are potentially L3 capable
1415327Sgavinm  */
1425327Sgavinm #define	AUTHAMD_L3CAPABLE(rev) \
1435327Sgavinm 	(X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
1445327Sgavinm 
1455327Sgavinm /*
1465254Sgavinm  * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED
1475254Sgavinm  * revisions as:
1485254Sgavinm  *
1495254Sgavinm  *	- being reported by the NB
1505254Sgavinm  *	- being a compound bus/interconnect error (external to chip)
1515254Sgavinm  *	- having LL of LG
1525254Sgavinm  *	- having II of MEM (but could still be a master/target abort)
1535254Sgavinm  *	- having CECC or UECC set
1545254Sgavinm  *
1555254Sgavinm  * We do not check the extended error code (first nibble of the
1565254Sgavinm  * model-specific error code on AMD) since this has changed from
1575254Sgavinm  * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10).
1585254Sgavinm  * Instead we use CECC/UECC to separate off the master/target
1595254Sgavinm  * abort cases.
1605254Sgavinm  *
1615254Sgavinm  * We insist that the detector be the NorthBridge bank;  although
1625254Sgavinm  * IC/DC can report some main memory errors, they do not capture
1635254Sgavinm  * an address at sufficient resolution to be useful and the NB will
1645254Sgavinm  * report most errors.
1655254Sgavinm  */
1665254Sgavinm #define	AUTHAMD_IS_MEMECCERR(bank, status) \
1675254Sgavinm 	((bank) == AMD_MCA_BANK_NB && \
1685254Sgavinm 	MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \
1695254Sgavinm 	MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \
1705254Sgavinm 	MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \
1715254Sgavinm 	((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC)))
1725254Sgavinm 
1735254Sgavinm static authamd_error_disp_t authamd_memce_disp = {
1745254Sgavinm 	FM_EREPORT_CPU_GENAMD,
1755254Sgavinm 	FM_EREPORT_CPU_GENAMD_MEM_CE,
1765254Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE
1775254Sgavinm };
1785254Sgavinm 
1795254Sgavinm static authamd_error_disp_t authamd_memue_disp = {
1805254Sgavinm 	FM_EREPORT_CPU_GENAMD,
1815254Sgavinm 	FM_EREPORT_CPU_GENAMD_MEM_UE,
1825254Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE
1835254Sgavinm };
1845254Sgavinm 
1855254Sgavinm static authamd_error_disp_t authamd_ckmemce_disp = {
1865254Sgavinm 	FM_EREPORT_CPU_GENAMD,
1875254Sgavinm 	FM_EREPORT_CPU_GENAMD_CKMEM_CE,
1885254Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE
1895254Sgavinm };
1905254Sgavinm 
1915254Sgavinm static authamd_error_disp_t authamd_ckmemue_disp = {
1925254Sgavinm 	FM_EREPORT_CPU_GENAMD,
1935254Sgavinm 	FM_EREPORT_CPU_GENAMD_CKMEM_UE,
1945254Sgavinm 	FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE
1955254Sgavinm };
1965254Sgavinm 
1975254Sgavinm /*
1985254Sgavinm  * We recognise GART walk errors as:
1995254Sgavinm  *
2005254Sgavinm  *	- being reported by the NB
2015254Sgavinm  *	- being a compound TLB error
2025254Sgavinm  *	- having LL of LG and TT of GEN
2035254Sgavinm  *	- having UC set
2045254Sgavinm  *	- possibly having PCC set (if source CPU)
2055254Sgavinm  */
2065254Sgavinm #define	AUTHAMD_IS_GARTERR(bank, status) \
2075254Sgavinm 	((bank) == AMD_MCA_BANK_NB && \
2085254Sgavinm 	MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \
2095254Sgavinm 	MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \
2105254Sgavinm 	MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \
2115254Sgavinm 	(status) & MSR_MC_STATUS_UC)
2125254Sgavinm 
2135254Sgavinm static authamd_error_disp_t authamd_gart_disp = {
2145254Sgavinm 	FM_EREPORT_CPU_GENAMD,			/* use generic subclass */
2155254Sgavinm 	FM_EREPORT_CPU_GENADM_GARTTBLWLK,	/* use generic leafclass */
2165254Sgavinm 	0					/* no additional payload */
2175254Sgavinm };
2185254Sgavinm 
2195254Sgavinm 
2205254Sgavinm static struct authamd_chipshared *authamd_shared[AUTHAMD_MAX_CHIPS];
2215254Sgavinm 
2225254Sgavinm static int
2235254Sgavinm authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what)
2245254Sgavinm {
2255254Sgavinm 	return (atomic_set_long_excl(&authamd->amd_shared->acs_cfgonce,
2265254Sgavinm 	    what) == 0 ?  B_TRUE : B_FALSE);
2275254Sgavinm }
2285254Sgavinm 
2295254Sgavinm static void
2305254Sgavinm authamd_pcicfg_write(uint_t chipid, uint_t func, uint_t reg, uint32_t val)
2315254Sgavinm {
2325254Sgavinm 	ASSERT(chipid + 24 <= 31);
2335254Sgavinm 	ASSERT((func & 7) == func);
2345254Sgavinm 	ASSERT((reg & 3) == 0 && reg < 256);
2355254Sgavinm 
2365254Sgavinm 	cmi_pci_putl(0, chipid + 24, func, reg, 0, val);
2375254Sgavinm }
2385254Sgavinm 
2395254Sgavinm static uint32_t
2405254Sgavinm authamd_pcicfg_read(uint_t chipid, uint_t func, uint_t reg)
2415254Sgavinm {
2425254Sgavinm 	ASSERT(chipid + 24 <= 31);
2435254Sgavinm 	ASSERT((func & 7) == func);
2445254Sgavinm 	ASSERT((reg & 3) == 0 && reg < 256);
2455254Sgavinm 
2465254Sgavinm 	return (cmi_pci_getl(0, chipid + 24, func, reg, 0, 0));
2475254Sgavinm }
2485254Sgavinm 
2495254Sgavinm void
2505254Sgavinm authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd)
2515254Sgavinm {
2525254Sgavinm 	uint64_t hwcr;
2535254Sgavinm 
2545254Sgavinm 	if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS)
2555254Sgavinm 		return;
2565254Sgavinm 
2575254Sgavinm 	authamd->amd_hwcr = hwcr;
2585254Sgavinm 
2595254Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
2605254Sgavinm 		hwcr |= AMD_HWCR_MCI_STATUS_WREN;
2615254Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
2625254Sgavinm 	}
2635254Sgavinm }
2645254Sgavinm 
2655254Sgavinm void
2665254Sgavinm authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd)
2675254Sgavinm {
2685254Sgavinm 	uint64_t hwcr = authamd->amd_hwcr;
2695254Sgavinm 
2705254Sgavinm 	if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) {
2715254Sgavinm 		hwcr &= ~AMD_HWCR_MCI_STATUS_WREN;
2725254Sgavinm 		(void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr);
2735254Sgavinm 	}
2745254Sgavinm }
2755254Sgavinm 
2765254Sgavinm /*
2775254Sgavinm  * Read EccCnt repeatedly for all possible channel/chip-select combos:
2785254Sgavinm  *
2795254Sgavinm  *	- read sparectl register
2805254Sgavinm  *	- if EccErrCntWrEn is set, clear that bit in the just-read value
2815254Sgavinm  *	  and write it back to sparectl;  this *may* clobber the EccCnt
2825254Sgavinm  *	  for the channel/chip-select combination currently selected, so
2835254Sgavinm  *	  we leave this bit clear if we had to clear it
2845254Sgavinm  *	- cycle through all channel/chip-select combinations writing each
2855254Sgavinm  *	  combination to sparectl before reading the register back for
2865254Sgavinm  *	  EccCnt for that combination;  since EccErrCntWrEn is clear
2875254Sgavinm  *	  the writes to select what count to read will not themselves
2885254Sgavinm  *	  zero any counts
2895254Sgavinm  */
2905254Sgavinm static int
2915254Sgavinm authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl)
2925254Sgavinm {
2935254Sgavinm 	union mcreg_sparectl sparectl;
2945254Sgavinm 	uint_t chipid = authamd->amd_shared->acs_chipid;
2955254Sgavinm 	uint_t family = authamd->amd_shared->acs_family;
2965254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
2975254Sgavinm 	int chan, cs;
2985254Sgavinm 
2995254Sgavinm 	/*
3005254Sgavinm 	 * Check for feature support;  this macro will test down to the
3015254Sgavinm 	 * family revision number, whereafter we'll switch on family
3025254Sgavinm 	 * assuming that future revisions will use the same register
3035254Sgavinm 	 * format.
3045254Sgavinm 	 */
3055254Sgavinm 	if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) {
3065254Sgavinm 		bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt));
3075254Sgavinm 		return (0);
3085254Sgavinm 	}
3095254Sgavinm 
3105254Sgavinm 	MCREG_VAL32(&sparectl) =
3115254Sgavinm 	    authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
3125254Sgavinm 
3135254Sgavinm 	switch (family) {
3145254Sgavinm 	case AUTHAMD_FAMILY_F:
3155254Sgavinm 		MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0;
3165254Sgavinm 		break;
3175254Sgavinm 
3185254Sgavinm 	case AUTHAMD_FAMILY_10:
3195254Sgavinm 		MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0;
3205254Sgavinm 		break;
3215254Sgavinm 	}
3225254Sgavinm 
3235254Sgavinm 	for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) {
3245254Sgavinm 		switch (family) {
3255254Sgavinm 		case AUTHAMD_FAMILY_F:
3265254Sgavinm 			MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) =
3275254Sgavinm 			    chan;
3285254Sgavinm 			break;
3295254Sgavinm 
3305254Sgavinm 		case AUTHAMD_FAMILY_10:
3315254Sgavinm 			MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) =
3325254Sgavinm 			    chan;
3335254Sgavinm 			break;
3345254Sgavinm 		}
3355254Sgavinm 
3365254Sgavinm 		for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) {
3375254Sgavinm 			switch (family) {
3385254Sgavinm 			case AUTHAMD_FAMILY_F:
3395254Sgavinm 				MCREG_FIELD_F_revFG(&sparectl,
3405254Sgavinm 				    EccErrCntDramCs) = cs;
3415254Sgavinm 				break;
3425254Sgavinm 
3435254Sgavinm 			case AUTHAMD_FAMILY_10:
3445254Sgavinm 				MCREG_FIELD_10_revAB(&sparectl,
3455254Sgavinm 				    EccErrCntDramCs) = cs;
3465254Sgavinm 				break;
3475254Sgavinm 			}
3485254Sgavinm 
3495254Sgavinm 			authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL,
3505254Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
3515254Sgavinm 
3525254Sgavinm 			MCREG_VAL32(&sparectl) = authamd_pcicfg_read(chipid,
3535254Sgavinm 			    MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL);
3545254Sgavinm 
3555254Sgavinm 			switch (family) {
3565254Sgavinm 			case AUTHAMD_FAMILY_F:
3575254Sgavinm 				msl->aal_eccerrcnt[chan][cs] =
3585254Sgavinm 				    MCREG_FIELD_F_revFG(&sparectl, EccErrCnt);
3595254Sgavinm 				break;
3605254Sgavinm 			case AUTHAMD_FAMILY_10:
3615254Sgavinm 				msl->aal_eccerrcnt[chan][cs] =
3625254Sgavinm 				    MCREG_FIELD_10_revAB(&sparectl, EccErrCnt);
3635254Sgavinm 				break;
3645254Sgavinm 			}
3655254Sgavinm 		}
3665254Sgavinm 	}
3675254Sgavinm 
3685254Sgavinm 	return (1);
3695254Sgavinm }
3705254Sgavinm 
3715254Sgavinm /*
3725254Sgavinm  * Clear EccCnt for all possible channel/chip-select combos:
3735254Sgavinm  *
3745254Sgavinm  *	- set EccErrCntWrEn in sparectl, if necessary
3755254Sgavinm  *	- write 0 to EccCnt for all channel/chip-select combinations
3765254Sgavinm  *	- clear EccErrCntWrEn
3775254Sgavinm  *
3785254Sgavinm  * If requested also disable the interrupts taken on counter overflow
3795254Sgavinm  * and on swap done.
3805254Sgavinm  */
3815254Sgavinm static void
3825254Sgavinm authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint)
3835254Sgavinm {
3845254Sgavinm 	union mcreg_sparectl sparectl;
3855254Sgavinm 	uint_t chipid = authamd->amd_shared->acs_chipid;
3865254Sgavinm 	uint_t family = authamd->amd_shared->acs_family;
3875254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
3885254Sgavinm 	int chan, cs;
3895254Sgavinm 
3905254Sgavinm 	if (!AUTHAMD_HAS_ONLINESPARECTL(rev))
3915254Sgavinm 		return;
3925254Sgavinm 
3935254Sgavinm 	MCREG_VAL32(&sparectl) =
3945254Sgavinm 	    authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, 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 
4145254Sgavinm 	authamd_pcicfg_write(chipid, 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 
4475254Sgavinm 			authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL,
4485254Sgavinm 			    MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl));
4495254Sgavinm 		}
4505254Sgavinm 	}
4515254Sgavinm }
4525254Sgavinm 
4535254Sgavinm /*
4545254Sgavinm  * cms_init entry point.
4555254Sgavinm  *
4565254Sgavinm  * This module provides broad model-specific support for AMD families
4575254Sgavinm  * 0x6, 0xf and 0x10.  Future families will have to be evaluated once their
4585254Sgavinm  * documentation is available.
4595254Sgavinm  */
4605254Sgavinm int
4615254Sgavinm authamd_init(cmi_hdl_t hdl, void **datap)
4625254Sgavinm {
4635254Sgavinm 	uint_t chipid = cmi_hdl_chipid(hdl);
4645254Sgavinm 	struct authamd_chipshared *sp, *osp;
4655254Sgavinm 	uint_t family = cmi_hdl_family(hdl);
4665254Sgavinm 	authamd_data_t *authamd;
4675254Sgavinm 	uint64_t cap;
4685254Sgavinm 
4695254Sgavinm 	if (authamd_ms_support_disable || !AUTHAMD_SUPPORTED(family))
4705254Sgavinm 		return (ENOTSUP);
4715254Sgavinm 
4725254Sgavinm 	if (!(x86_feature & X86_MCA))
4735254Sgavinm 		return (ENOTSUP);
4745254Sgavinm 
4755254Sgavinm 	if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS)
4765254Sgavinm 		return (ENOTSUP);
4775254Sgavinm 
4785254Sgavinm 	if (!(cap & MCG_CAP_CTL_P))
4795254Sgavinm 		return (ENOTSUP);
4805254Sgavinm 
4815254Sgavinm 	authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP);
4825254Sgavinm 	cmi_hdl_hold(hdl);	/* release in fini */
4835254Sgavinm 	authamd->amd_hdl = hdl;
4845254Sgavinm 
4855254Sgavinm 	if ((sp = authamd_shared[chipid]) == NULL) {
4865254Sgavinm 		sp = kmem_zalloc(sizeof (struct authamd_chipshared), KM_SLEEP);
487*6000Sgavinm 		sp->acs_chipid = chipid;
488*6000Sgavinm 		sp->acs_family = family;
489*6000Sgavinm 		sp->acs_rev = cmi_hdl_chiprev(hdl);
490*6000Sgavinm 		membar_producer();
491*6000Sgavinm 
4925254Sgavinm 		osp = atomic_cas_ptr(&authamd_shared[chipid], NULL, sp);
4935254Sgavinm 		if (osp != NULL) {
4945254Sgavinm 			kmem_free(sp, sizeof (struct authamd_chipshared));
4955254Sgavinm 			sp = osp;
4965254Sgavinm 		}
4975254Sgavinm 	}
4985254Sgavinm 	authamd->amd_shared = sp;
4995254Sgavinm 
5005254Sgavinm 	return (0);
5015254Sgavinm }
5025254Sgavinm 
5035254Sgavinm /*
5045254Sgavinm  * cms_logout_size entry point.
5055254Sgavinm  */
5065254Sgavinm /*ARGSUSED*/
5075254Sgavinm size_t
5085254Sgavinm authamd_logout_size(cmi_hdl_t hdl)
5095254Sgavinm {
5105254Sgavinm 	return (sizeof (struct authamd_logout));
5115254Sgavinm }
5125254Sgavinm 
5135254Sgavinm /*
5145254Sgavinm  * cms_mcgctl_val entry point
5155254Sgavinm  *
5165254Sgavinm  * Instead of setting all bits to 1 we can set just those for the
5175254Sgavinm  * error detector banks known to exist.
5185254Sgavinm  */
5195254Sgavinm /*ARGSUSED*/
5205254Sgavinm uint64_t
5215254Sgavinm authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed)
5225254Sgavinm {
5235254Sgavinm 	return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed);
5245254Sgavinm }
5255254Sgavinm 
5265254Sgavinm /*
5275254Sgavinm  * cms_bankctl_skipinit entry point
5285254Sgavinm  *
5295254Sgavinm  * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC)
5305254Sgavinm  * may produce spurious machine checks.
5315639Sgavinm  *
5325639Sgavinm  * Only allow a single core to setup the NorthBridge MCi_CTL register.
5335254Sgavinm  */
5345254Sgavinm /*ARGSUSED*/
5355254Sgavinm boolean_t
5365254Sgavinm authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank)
5375254Sgavinm {
5385254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
5395639Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
5405254Sgavinm 
5415639Sgavinm 	if (authamd->amd_shared->acs_family == AUTHAMD_FAMILY_6)
5425639Sgavinm 		return (bank == 0 ?  B_TRUE : B_FALSE);
5435639Sgavinm 
5445639Sgavinm 	if (AUTHAMD_NBONCHIP(rev) && bank == AMD_MCA_BANK_NB) {
5455639Sgavinm 		return (authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCA) ==
5465639Sgavinm 		    B_TRUE ? B_FALSE : B_TRUE);
5475639Sgavinm 	}
5485639Sgavinm 
5495639Sgavinm 	return (B_FALSE);
5505254Sgavinm }
5515254Sgavinm 
5525254Sgavinm /*
5535254Sgavinm  * cms_bankctl_val entry point
5545254Sgavinm  */
5555254Sgavinm uint64_t
5565254Sgavinm authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed)
5575254Sgavinm {
5585254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
5595254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
5605254Sgavinm 	uint64_t val = proposed;
5615254Sgavinm 
5625254Sgavinm 	/*
5635254Sgavinm 	 * The Intel MCA says we can write all 1's to enable #MC for
5645254Sgavinm 	 * all errors, and AMD docs say much the same.  But, depending
5655254Sgavinm 	 * perhaps on other config registers, taking machine checks
5665254Sgavinm 	 * for some errors such as GART TLB errors and master/target
5675254Sgavinm 	 * aborts may be bad - they set UC and sometime also PCC, but
5685254Sgavinm 	 * we should not always panic for these error types.
5695254Sgavinm 	 *
5705254Sgavinm 	 * Our cms_error_action entry point can suppress such panics,
5715254Sgavinm 	 * however we can also use the cms_bankctl_val entry point to
5725254Sgavinm 	 * veto enabling of some of the known villains in the first place.
5735254Sgavinm 	 */
5745254Sgavinm 	if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev))
5755254Sgavinm 		val &= ~AMD_NB_EN_GARTTBLWK;
5765254Sgavinm 
5775254Sgavinm 	return (val);
5785254Sgavinm }
5795254Sgavinm 
5805254Sgavinm /*
5815327Sgavinm  * Bits to add to NB MCA config (after watchdog config).
5825327Sgavinm  */
5835327Sgavinm uint32_t authamd_nb_mcacfg_add = AMD_NB_CFG_ADD_CMN;
5845327Sgavinm 
5855327Sgavinm /*
5865327Sgavinm  * Bits to remove from NB MCA config (after watchdog config)
5875327Sgavinm  */
5885327Sgavinm uint32_t authamd_nb_mcacfg_remove = AMD_NB_CFG_REMOVE_CMN;
5895327Sgavinm 
5905327Sgavinm /*
5915327Sgavinm  * NB Watchdog policy, and rate we use if enabling.
5925327Sgavinm  */
5935327Sgavinm enum {
5945327Sgavinm 	AUTHAMD_NB_WDOG_LEAVEALONE,
5955327Sgavinm 	AUTHAMD_NB_WDOG_DISABLE,
5965327Sgavinm 	AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED,
5975327Sgavinm 	AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE
5985327Sgavinm } authamd_nb_watchdog_policy = AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED;
5995327Sgavinm 
6005327Sgavinm uint32_t authamd_nb_mcacfg_wdog = AMD_NB_CFG_WDOGTMRCNTSEL_4095 |
6015327Sgavinm     AMD_NB_CFG_WDOGTMRBASESEL_1MS;
6025327Sgavinm 
6035327Sgavinm /*
6045327Sgavinm  * Per-core cache scrubbing policy and rates.
6055327Sgavinm  */
6065327Sgavinm enum {
6075327Sgavinm 	AUTHAMD_SCRUB_BIOSDEFAULT,	/* leave as BIOS configured */
6085327Sgavinm 	AUTHAMD_SCRUB_FIXED,		/* assign our chosen rate */
6095327Sgavinm 	AUTHAMD_SCRUB_MAX		/* use higher of ours and BIOS rate */
6105327Sgavinm } authamd_scrub_policy = AUTHAMD_SCRUB_MAX;
6115327Sgavinm 
6125327Sgavinm uint32_t authamd_scrub_rate_dcache = 0xf;	/* 64K per 0.67 seconds */
6135327Sgavinm uint32_t authamd_scrub_rate_l2cache = 0xe;	/* 1MB per 5.3 seconds */
6145327Sgavinm uint32_t authamd_scrub_rate_l3cache = 0xd;	/* 1MB per 2.7 seconds */
6155327Sgavinm 
6165327Sgavinm static uint32_t
6175327Sgavinm authamd_scrubrate(uint32_t osrate, uint32_t biosrate, const char *varnm)
6185327Sgavinm {
6195327Sgavinm 	uint32_t rate;
6205327Sgavinm 
6215327Sgavinm 	if (osrate > AMD_NB_SCRUBCTL_RATE_MAX) {
6225327Sgavinm 		cmn_err(CE_WARN, "%s is too large, resetting to 0x%x\n",
6235327Sgavinm 		    varnm, AMD_NB_SCRUBCTL_RATE_MAX);
6245327Sgavinm 		osrate = AMD_NB_SCRUBCTL_RATE_MAX;
6255327Sgavinm 	}
6265327Sgavinm 
6275327Sgavinm 	switch (authamd_scrub_policy) {
6285327Sgavinm 	case AUTHAMD_SCRUB_FIXED:
6295327Sgavinm 		rate = osrate;
6305327Sgavinm 		break;
6315327Sgavinm 
6325327Sgavinm 	default:
6335327Sgavinm 		cmn_err(CE_WARN, "Unknown authamd_scrub_policy %d - "
6345327Sgavinm 		    "using default policy of AUTHAMD_SCRUB_MAX",
6355327Sgavinm 		    authamd_scrub_policy);
6365327Sgavinm 		/*FALLTHRU*/
6375327Sgavinm 
6385327Sgavinm 	case AUTHAMD_SCRUB_MAX:
6395327Sgavinm 		if (osrate != 0 && biosrate != 0)
6405327Sgavinm 			rate = MIN(osrate, biosrate);	/* small is fast */
6415327Sgavinm 		else
6425327Sgavinm 			rate = osrate ? osrate : biosrate;
6435327Sgavinm 	}
6445327Sgavinm 
6455327Sgavinm 	return (rate);
6465327Sgavinm }
6475327Sgavinm 
6485327Sgavinm /*
6495254Sgavinm  * cms_mca_init entry point.
6505254Sgavinm  */
6515254Sgavinm /*ARGSUSED*/
6525254Sgavinm void
6535254Sgavinm authamd_mca_init(cmi_hdl_t hdl, int nbanks)
6545254Sgavinm {
6555254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
6565254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
6575327Sgavinm 	uint_t chipid = authamd->amd_shared->acs_chipid;
6585254Sgavinm 
6595254Sgavinm 	/*
6605254Sgavinm 	 * On chips with a NB online spare control register take control
6615254Sgavinm 	 * and clear ECC counts.
6625254Sgavinm 	 */
6635254Sgavinm 	if (AUTHAMD_HAS_ONLINESPARECTL(rev) &&
6645254Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) {
6655254Sgavinm 		authamd_clear_ecccnt(authamd, B_TRUE);
6665254Sgavinm 	}
6675254Sgavinm 
6685254Sgavinm 	/*
6695254Sgavinm 	 * And since we are claiming the telemetry stop the BIOS receiving
6705254Sgavinm 	 * an SMI on NB threshold overflow.
6715254Sgavinm 	 */
6725254Sgavinm 	if (AUTHAMD_NBMISC_NUM(rev) &&
6735254Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) {
6745254Sgavinm 		union mcmsr_nbmisc nbm;
6755254Sgavinm 		int i;
6765254Sgavinm 
6775254Sgavinm 		authamd_bankstatus_prewrite(hdl, authamd);
6785254Sgavinm 
6795254Sgavinm 		for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) {
6805254Sgavinm 			if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i),
6815254Sgavinm 			    (uint64_t *)&nbm) != CMI_SUCCESS)
6825254Sgavinm 				continue;
6835254Sgavinm 
6845254Sgavinm 			if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) &&
6855254Sgavinm 			    MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) &&
6865254Sgavinm 			    MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) {
6875254Sgavinm 				MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0;
6885254Sgavinm 			} else if (X86_CHIPREV_ATLEAST(rev,
6895254Sgavinm 			    X86_CHIPREV_AMD_10_REV_A) &&
6905254Sgavinm 			    MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) &&
6915254Sgavinm 			    MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) {
6925254Sgavinm 				MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0;
6935254Sgavinm 			}
6945254Sgavinm 
6955254Sgavinm 			(void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i),
6965254Sgavinm 			    MCMSR_VAL(&nbm));
6975254Sgavinm 		}
6985254Sgavinm 
6995254Sgavinm 		authamd_bankstatus_postwrite(hdl, authamd);
7005254Sgavinm 	}
7015327Sgavinm 
7025327Sgavinm 	/*
7035327Sgavinm 	 * NB MCA Configuration Register.
7045327Sgavinm 	 */
7055327Sgavinm 	if (AUTHAMD_DO_NBMCACFG(rev) &&
7065327Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBMCACFG)) {
7075327Sgavinm 		uint32_t val = authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL,
7085327Sgavinm 		    MC_CTL_REG_NBCFG);
7095327Sgavinm 
7105327Sgavinm 		switch (authamd_nb_watchdog_policy) {
7115327Sgavinm 		case AUTHAMD_NB_WDOG_LEAVEALONE:
7125327Sgavinm 			break;
7135327Sgavinm 
7145327Sgavinm 		case AUTHAMD_NB_WDOG_DISABLE:
7155327Sgavinm 			val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK |
7165327Sgavinm 			    AMD_NB_CFG_WDOGTMRCNTSEL_MASK);
7175327Sgavinm 			val |= AMD_NB_CFG_WDOGTMRDIS;
7185327Sgavinm 			break;
7195327Sgavinm 
7205327Sgavinm 		default:
7215327Sgavinm 			cmn_err(CE_NOTE, "authamd_nb_watchdog_policy=%d "
7225327Sgavinm 			    "unrecognised, using default policy",
7235327Sgavinm 			    authamd_nb_watchdog_policy);
7245327Sgavinm 			/*FALLTHRU*/
7255327Sgavinm 
7265327Sgavinm 		case AUTHAMD_NB_WDOG_ENABLE_IF_DISABLED:
7275327Sgavinm 			if (!(val & AMD_NB_CFG_WDOGTMRDIS))
7285327Sgavinm 				break;	/* if enabled leave rate intact */
7295327Sgavinm 			/*FALLTHRU*/
7305327Sgavinm 
7315327Sgavinm 		case AUTHAMD_NB_WDOG_ENABLE_FORCE_RATE:
7325327Sgavinm 			val &= ~(AMD_NB_CFG_WDOGTMRBASESEL_MASK |
7335327Sgavinm 			    AMD_NB_CFG_WDOGTMRCNTSEL_MASK |
7345327Sgavinm 			    AMD_NB_CFG_WDOGTMRDIS);
7355327Sgavinm 			val |= authamd_nb_mcacfg_wdog;
7365327Sgavinm 			break;
7375327Sgavinm 		}
7385327Sgavinm 
7395327Sgavinm 		/*
7405327Sgavinm 		 * Bit 0 of the NB MCA Config register is reserved on family
7415327Sgavinm 		 * 0x10.
7425327Sgavinm 		 */
7435327Sgavinm 		if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A))
7445327Sgavinm 			authamd_nb_mcacfg_add &= ~AMD_NB_CFG_CPUECCERREN;
7455327Sgavinm 
7465327Sgavinm 		val &= ~authamd_nb_mcacfg_remove;
7475327Sgavinm 		val |= authamd_nb_mcacfg_add;
7485327Sgavinm 
7495327Sgavinm 		authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG,
7505327Sgavinm 		    val);
7515327Sgavinm 	}
7525327Sgavinm 
7535327Sgavinm 	/*
7545327Sgavinm 	 * Cache scrubbing.  We can't enable DRAM scrubbing since
7555327Sgavinm 	 * we don't know the DRAM base for this node.
7565327Sgavinm 	 */
7575327Sgavinm 	if (AUTHAMD_HAS_CHIPSCRUB(rev) &&
7585327Sgavinm 	    authamd_scrub_policy != AUTHAMD_SCRUB_BIOSDEFAULT &&
7595327Sgavinm 	    authamd_chip_once(authamd, AUTHAMD_CFGONCE_CACHESCRUB)) {
7605327Sgavinm 		uint32_t val = authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL,
7615327Sgavinm 		    MC_CTL_REG_SCRUBCTL);
7625327Sgavinm 		int l3cap = 0;
7635327Sgavinm 
7645327Sgavinm 		if (AUTHAMD_L3CAPABLE(rev)) {
7655327Sgavinm 			l3cap = (authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL,
7665327Sgavinm 			    MC_CTL_REG_NBCAP) & MC_NBCAP_L3CAPABLE) != 0;
7675327Sgavinm 		}
7685327Sgavinm 
7695327Sgavinm 		authamd_scrub_rate_dcache =
7705327Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_dcache,
7715327Sgavinm 		    (val & AMD_NB_SCRUBCTL_DC_MASK) >> AMD_NB_SCRUBCTL_DC_SHIFT,
7725327Sgavinm 		    "authamd_scrub_rate_dcache");
7735327Sgavinm 
7745327Sgavinm 		authamd_scrub_rate_l2cache =
7755327Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_l2cache,
7765327Sgavinm 		    (val & AMD_NB_SCRUBCTL_L2_MASK) >> AMD_NB_SCRUBCTL_L2_SHIFT,
7775327Sgavinm 		    "authamd_scrub_rate_l2cache");
7785327Sgavinm 
7795327Sgavinm 		authamd_scrub_rate_l3cache = l3cap ?
7805327Sgavinm 		    authamd_scrubrate(authamd_scrub_rate_l3cache,
7815327Sgavinm 		    (val & AMD_NB_SCRUBCTL_L3_MASK) >> AMD_NB_SCRUBCTL_L3_SHIFT,
7825327Sgavinm 		    "authamd_scrub_rate_l3cache") : 0;
7835327Sgavinm 
7845327Sgavinm 		val = AMD_NB_MKSCRUBCTL(authamd_scrub_rate_l3cache,
7855327Sgavinm 		    authamd_scrub_rate_dcache, authamd_scrub_rate_l2cache,
7865327Sgavinm 		    val & AMD_NB_SCRUBCTL_DRAM_MASK);
7875327Sgavinm 
7885327Sgavinm 		authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL,
7895327Sgavinm 		    MC_CTL_REG_SCRUBCTL, val);
7905327Sgavinm 	}
7915327Sgavinm 
7925327Sgavinm }
7935327Sgavinm 
7945327Sgavinm /*
7955327Sgavinm  * cms_poll_ownermask entry point.
7965327Sgavinm  */
7975327Sgavinm uint64_t
7985327Sgavinm authamd_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl)
7995327Sgavinm {
8005327Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
8015327Sgavinm 	struct authamd_chipshared *acsp = authamd->amd_shared;
8025327Sgavinm 	hrtime_t now = gethrtime_waitfree();
8035327Sgavinm 	hrtime_t last = acsp->acs_poll_timestamp;
8045327Sgavinm 	int dopoll = 0;
8055327Sgavinm 
8065327Sgavinm 	if (now - last > 2 * pintvl || last == 0) {
8075327Sgavinm 		acsp->acs_pollowner = hdl;
8085327Sgavinm 		dopoll = 1;
8095327Sgavinm 	} else if (acsp->acs_pollowner == hdl) {
8105327Sgavinm 		dopoll = 1;
8115327Sgavinm 	}
8125327Sgavinm 
8135327Sgavinm 	if (dopoll)
8145327Sgavinm 		acsp->acs_poll_timestamp = now;
8155327Sgavinm 
8165327Sgavinm 	return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB));
8175327Sgavinm 
8185254Sgavinm }
8195254Sgavinm 
8205254Sgavinm /*
8215254Sgavinm  * cms_bank_logout entry point.
8225254Sgavinm  */
8235254Sgavinm /*ARGSUSED*/
8245254Sgavinm void
8255254Sgavinm authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status,
8265254Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
8275254Sgavinm {
8285254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
8295254Sgavinm 	struct authamd_logout *msl = mslogout;
8305254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
8315254Sgavinm 
8325254Sgavinm 	if (msl == NULL)
8335254Sgavinm 		return;
8345254Sgavinm 
8355254Sgavinm 	/*
8365254Sgavinm 	 * For main memory ECC errors on revisions with an Online Spare
8375254Sgavinm 	 * Control Register grab the ECC counts by channel and chip-select
8385254Sgavinm 	 * and reset them to 0.
8395254Sgavinm 	 */
8405254Sgavinm 	if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
8415254Sgavinm 	    AUTHAMD_IS_MEMECCERR(bank, status) &&
8425254Sgavinm 	    AUTHAMD_HAS_ONLINESPARECTL(rev)) {
8435254Sgavinm 		if (authamd_read_ecccnt(authamd, msl))
8445254Sgavinm 			authamd_clear_ecccnt(authamd, B_FALSE);
8455254Sgavinm 	}
8465254Sgavinm }
8475254Sgavinm 
8485254Sgavinm /*
8495254Sgavinm  * cms_error_action entry point
8505254Sgavinm  */
8515254Sgavinm 
8525254Sgavinm int authamd_forgive_uc = 0;	/* For test/debug only */
8535254Sgavinm int authamd_forgive_pcc = 0;	/* For test/debug only */
8545254Sgavinm int authamd_fake_poison = 0;	/* For test/debug only */
8555254Sgavinm 
8565254Sgavinm /*ARGSUSED*/
8575254Sgavinm uint32_t
8585254Sgavinm authamd_error_action(cmi_hdl_t hdl, int ismc, int bank,
8595254Sgavinm     uint64_t status, uint64_t addr, uint64_t misc, void *mslogout)
8605254Sgavinm {
8615254Sgavinm 	authamd_error_disp_t *disp;
8625254Sgavinm 	uint32_t rv = 0;
8635254Sgavinm 
8645254Sgavinm 	if (authamd_forgive_uc)
8655254Sgavinm 		rv |= CMS_ERRSCOPE_CLEARED_UC;
8665254Sgavinm 
8675254Sgavinm 	if (authamd_forgive_pcc)
8685254Sgavinm 		rv |= CMS_ERRSCOPE_CURCONTEXT_OK;
8695254Sgavinm 
8705254Sgavinm 	if (authamd_fake_poison && status & MSR_MC_STATUS_UC)
8715254Sgavinm 		rv |= CMS_ERRSCOPE_POISONED;
8725254Sgavinm 
8735254Sgavinm 	if (rv)
8745254Sgavinm 		return (rv);
8755254Sgavinm 
8765254Sgavinm 	disp = authamd_disp_match(hdl, bank, status, addr, misc, mslogout);
8775254Sgavinm 
8785254Sgavinm 	if (disp == &authamd_gart_disp) {
8795254Sgavinm 		/*
8805254Sgavinm 		 * GART walk errors set UC and possibly PCC (if source CPU)
8815254Sgavinm 		 * but should not be regarded as terminal.
8825254Sgavinm 		 */
8835254Sgavinm 		return (CMS_ERRSCOPE_IGNORE_ERR);
8845254Sgavinm 	}
8855254Sgavinm 
8865254Sgavinm 	/*
8875254Sgavinm 	 * May also want to consider master abort and target abort.  These
8885254Sgavinm 	 * also set UC and PCC (if src CPU) but the requester gets -1
8895254Sgavinm 	 * and I believe the IO stuff in Solaris will handle that.
8905254Sgavinm 	 */
8915254Sgavinm 
8925254Sgavinm 	return (rv);
8935254Sgavinm }
8945254Sgavinm 
8955254Sgavinm /*
8965254Sgavinm  * cms_disp_match entry point
8975254Sgavinm  */
8985254Sgavinm /*ARGSUSED*/
8995254Sgavinm cms_cookie_t
9005254Sgavinm authamd_disp_match(cmi_hdl_t hdl, int bank, uint64_t status,
9015254Sgavinm     uint64_t addr, uint64_t misc, void *mslogout)
9025254Sgavinm {
9035254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
9045254Sgavinm 	/* uint16_t errcode = MCAX86_ERRCODE(status); */
9055254Sgavinm 	uint16_t exterrcode = AMD_EXT_ERRCODE(status);
9065254Sgavinm 	uint32_t rev = authamd->amd_shared->acs_rev;
9075254Sgavinm 
9085254Sgavinm 	/*
9095254Sgavinm 	 * Recognise main memory ECC errors
9105254Sgavinm 	 */
9115254Sgavinm 	if (AUTHAMD_MEMECC_RECOGNISED(rev) &&
9125254Sgavinm 	    AUTHAMD_IS_MEMECCERR(bank, status)) {
9135254Sgavinm 		if (status & AMD_BANK_STAT_CECC) {
9145254Sgavinm 			return (exterrcode == 0 ? &authamd_memce_disp :
9155254Sgavinm 			    &authamd_ckmemce_disp);
9165254Sgavinm 		} else if (status & AMD_BANK_STAT_UECC) {
9175254Sgavinm 			return (exterrcode == 0 ? &authamd_memue_disp :
9185254Sgavinm 			    &authamd_ckmemue_disp);
9195254Sgavinm 		}
9205254Sgavinm 	}
9215254Sgavinm 
9225254Sgavinm 	/*
9235254Sgavinm 	 * Recognise GART walk errors
9245254Sgavinm 	 */
9255254Sgavinm 	if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status))
9265254Sgavinm 		return (&authamd_gart_disp);
9275254Sgavinm 
9285254Sgavinm 	return (NULL);
9295254Sgavinm }
9305254Sgavinm 
9315254Sgavinm /*
9325254Sgavinm  * cms_ereport_class entry point
9335254Sgavinm  */
9345254Sgavinm /*ARGSUSED*/
9355254Sgavinm void
9365254Sgavinm authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie,
9375254Sgavinm     const char **cpuclsp, const char **leafclsp)
9385254Sgavinm {
9395254Sgavinm 	const authamd_error_disp_t *aed = mscookie;
9405254Sgavinm 
9415254Sgavinm 	if (aed == NULL)
9425254Sgavinm 		return;
9435254Sgavinm 
9445254Sgavinm 	if (aed->aad_subclass != NULL)
9455254Sgavinm 		*cpuclsp = aed->aad_subclass;
9465254Sgavinm 	if (aed->aad_leafclass != NULL)
9475254Sgavinm 		*leafclsp = aed->aad_leafclass;
9485254Sgavinm }
9495254Sgavinm 
9505254Sgavinm /*ARGSUSED*/
9515254Sgavinm static void
9525254Sgavinm authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd,
9535254Sgavinm     nvlist_t *ereport, nv_alloc_t *nva, void *mslogout)
9545254Sgavinm {
9555254Sgavinm 	nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS];
9565254Sgavinm 	uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS];
9575254Sgavinm 	authamd_logout_t *msl;
9585254Sgavinm 	nvlist_t *nvl;
9595254Sgavinm 	int nelems = 0;
9605254Sgavinm 	int i, chan, cs;
9615254Sgavinm 
9625254Sgavinm 	if ((msl = mslogout) == NULL)
9635254Sgavinm 		return;
9645254Sgavinm 
9655254Sgavinm 	for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) {
9665254Sgavinm 		for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) {
9675254Sgavinm 			if (msl->aal_eccerrcnt[chan][cs] == 0)
9685254Sgavinm 				continue;
9695254Sgavinm 
9705254Sgavinm 			if ((nvl = fm_nvlist_create(nva)) == NULL)
9715254Sgavinm 				continue;
9725254Sgavinm 
9735254Sgavinm 			elems[nelems] = nvl;
9745254Sgavinm 			counts[nelems++] = msl->aal_eccerrcnt[chan][cs];
9755254Sgavinm 
9765254Sgavinm 			fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 5,
9775254Sgavinm 			    "motherboard", 0,
9785254Sgavinm 			    "chip", authamd->amd_shared->acs_chipid,
9795254Sgavinm 			    "memory-controller", 0,
9805254Sgavinm 			    "dram-channel", chan,
9815254Sgavinm 			    "chip-select", cs);
9825254Sgavinm 		}
9835254Sgavinm 	}
9845254Sgavinm 
9855254Sgavinm 	if (nelems == 0)
9865254Sgavinm 		return;
9875254Sgavinm 
9885254Sgavinm 	fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE,
9895254Sgavinm 	    DATA_TYPE_NVLIST_ARRAY, nelems, elems,
9905254Sgavinm 	    NULL);
9915254Sgavinm 
9925254Sgavinm 	fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT,
9935254Sgavinm 	    DATA_TYPE_UINT8_ARRAY, nelems, &counts[0],
9945254Sgavinm 	    NULL);
9955254Sgavinm 
9965254Sgavinm 	for (i = 0; i < nelems; i++)
9975254Sgavinm 		fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE);
9985254Sgavinm }
9995254Sgavinm 
10005254Sgavinm /*
10015254Sgavinm  * cms_ereport_add_logout entry point
10025254Sgavinm  */
10035254Sgavinm /*ARGSUSED*/
10045254Sgavinm void
10055254Sgavinm authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva,
10065254Sgavinm     int bank, uint64_t status, uint64_t addr, uint64_t misc,
10075254Sgavinm     void *mslogout, cms_cookie_t mscookie)
10085254Sgavinm {
10095254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
10105254Sgavinm 	const authamd_error_disp_t *aed = mscookie;
10115254Sgavinm 	uint64_t members;
10125254Sgavinm 
10135254Sgavinm 	if (aed == NULL)
10145254Sgavinm 		return;
10155254Sgavinm 
10165254Sgavinm 	members = aed->aad_ereport_members;
10175254Sgavinm 
10185254Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) {
10195254Sgavinm 		fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND,
10205254Sgavinm 		    DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status),
10215254Sgavinm 		    NULL);
10225254Sgavinm 
10235254Sgavinm 		if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) {
10245254Sgavinm 			fm_payload_set(ereport,
10255254Sgavinm 			    FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE,
10265254Sgavinm 			    DATA_TYPE_STRING, "E",
10275254Sgavinm 			    NULL);
10285254Sgavinm 		}
10295254Sgavinm 	}
10305254Sgavinm 
10315254Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) {
10325254Sgavinm 		fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND,
10335254Sgavinm 		    DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status),
10345254Sgavinm 		    NULL);
10355254Sgavinm 
10365254Sgavinm 		if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) {
10375254Sgavinm 			fm_payload_set(ereport,
10385254Sgavinm 			    FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE,
10395254Sgavinm 			    DATA_TYPE_STRING, "C",
10405254Sgavinm 			    NULL);
10415254Sgavinm 		}
10425254Sgavinm 	}
10435254Sgavinm 
10445254Sgavinm 	if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE &&
10455254Sgavinm 	    status & MSR_MC_STATUS_ADDRV) {
10465254Sgavinm 		authamd_ereport_add_resource(hdl, authamd, ereport, nva,
10475254Sgavinm 		    mslogout);
10485254Sgavinm 	}
10495254Sgavinm }
10505254Sgavinm 
10515254Sgavinm /*
10525254Sgavinm  * cms_msrinject entry point
10535254Sgavinm  */
10545254Sgavinm cms_errno_t
10555254Sgavinm authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val)
10565254Sgavinm {
10575254Sgavinm 	authamd_data_t *authamd = cms_hdl_getcmsdata(hdl);
10585254Sgavinm 	cms_errno_t rv = CMSERR_BADMSRWRITE;
10595254Sgavinm 
10605254Sgavinm 	authamd_bankstatus_prewrite(hdl, authamd);
10615254Sgavinm 	if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS)
10625254Sgavinm 		rv = CMS_SUCCESS;
10635254Sgavinm 	authamd_bankstatus_postwrite(hdl, authamd);
10645254Sgavinm 
10655254Sgavinm 	return (rv);
10665254Sgavinm }
10675254Sgavinm 
10685254Sgavinm cms_api_ver_t _cms_api_version = CMS_API_VERSION_0;
10695254Sgavinm 
10705254Sgavinm const cms_ops_t _cms_ops = {
10715254Sgavinm 	authamd_init,			/* cms_init */
10725254Sgavinm 	NULL,				/* cms_post_startup */
10735254Sgavinm 	NULL,				/* cms_post_mpstartup */
10745254Sgavinm 	authamd_logout_size,		/* cms_logout_size */
10755254Sgavinm 	authamd_mcgctl_val,		/* cms_mcgctl_val */
10765254Sgavinm 	authamd_bankctl_skipinit,	/* cms_bankctl_skipinit */
10775254Sgavinm 	authamd_bankctl_val,		/* cms_bankctl_val */
10785254Sgavinm 	NULL,				/* cms_bankstatus_skipinit */
10795254Sgavinm 	NULL,				/* cms_bankstatus_val */
10805254Sgavinm 	authamd_mca_init,		/* cms_mca_init */
10815327Sgavinm 	authamd_poll_ownermask,		/* cms_poll_ownermask */
10825254Sgavinm 	authamd_bank_logout,		/* cms_bank_logout */
10835254Sgavinm 	authamd_error_action,		/* cms_error_action */
10845254Sgavinm 	authamd_disp_match,		/* cms_disp_match */
10855254Sgavinm 	authamd_ereport_class,		/* cms_ereport_class */
10865254Sgavinm 	NULL,				/* cms_ereport_detector */
10875254Sgavinm 	NULL,				/* cms_ereport_includestack */
10885254Sgavinm 	authamd_ereport_add_logout,	/* cms_ereport_add_logout */
10895254Sgavinm 	authamd_msrinject,		/* cms_msrinject */
10905254Sgavinm 	NULL,				/* cms_fini */
10915254Sgavinm };
10925254Sgavinm 
10935254Sgavinm static struct modlcpu modlcpu = {
10945254Sgavinm 	&mod_cpuops,
10955254Sgavinm 	"Generic AMD model-specific MCA"
10965254Sgavinm };
10975254Sgavinm 
10985254Sgavinm static struct modlinkage modlinkage = {
10995254Sgavinm 	MODREV_1,
11005254Sgavinm 	(void *)&modlcpu,
11015254Sgavinm 	NULL
11025254Sgavinm };
11035254Sgavinm 
11045254Sgavinm int
11055254Sgavinm _init(void)
11065254Sgavinm {
11075254Sgavinm 	return (mod_install(&modlinkage));
11085254Sgavinm }
11095254Sgavinm 
11105254Sgavinm int
11115254Sgavinm _info(struct modinfo *modinfop)
11125254Sgavinm {
11135254Sgavinm 	return (mod_info(&modlinkage, modinfop));
11145254Sgavinm }
11155254Sgavinm 
11165254Sgavinm int
11175254Sgavinm _fini(void)
11185254Sgavinm {
11195254Sgavinm 	return (mod_remove(&modlinkage));
11205254Sgavinm }
1121