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