/* $NoKeywords:$ */ /** * @file * * AMD Family_15 specific utility functions. * * Provides numerous utility functions specific to family 15h. * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: CPU/Family/0x15 * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $ * */ /* ****************************************************************************** * * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Advanced Micro Devices, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ****************************************************************************** */ /*---------------------------------------------------------------------------------------- * M O D U L E S U S E D *---------------------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "Ids.h" #include "cpuRegisters.h" #include "cpuFamilyTranslation.h" #include "cpuPstateTables.h" #include "cpuF15PowerMgmt.h" #include "cpuApicUtilities.h" #include "cpuServices.h" #include "GeneralServices.h" #include "cpuF15Utilities.h" #include "cpuEarlyInit.h" #include "cpuPostInit.h" #include "cpuFeatures.h" #include "OptionMultiSocket.h" #include "Filecode.h" CODE_GROUP (G2_PEI) RDATA_GROUP (G2_PEI) #define FILECODE PROC_CPU_FAMILY_0X15_CPUF15UTILITIES_FILECODE /*---------------------------------------------------------------------------------------- * D E F I N I T I O N S A N D M A C R O S *---------------------------------------------------------------------------------------- */ extern CPU_FAMILY_SUPPORT_TABLE PstateFamilyServiceTable; extern OPTION_MULTISOCKET_CONFIGURATION OptionMultiSocketConfiguration; // HT Phy registers used in code. #define HT_PHY_FUSE_PROC_DLL_PROCESS_COMP_RD_SL0 0x4011 #define HT_PHY_FUSE_PROC_DLL_PROCESS_COMP_RD_SL1 0x4411 #define HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_RD 0x400F #define HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_SL0 0x520F #define HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_SL1 0x530F /*---------------------------------------------------------------------------------------- * T Y P E D E F S A N D S T R U C T U R E S *---------------------------------------------------------------------------------------- */ /** * HT PHY DLL Process Compensation Lookup Table. * * If the hardware provides compensation values, the value is provided by accessing the bitfield * [HiBit:LoBit]. Otherwise, a default value will be used. * */ typedef struct { UINT32 DefaultComp; ///< The default compensation value if not provided by hardware. UINT8 CtlIndexLoBit; ///< The low bit position of the compensation value. UINT8 CtlIndexHiBit; ///< The high bit position of the compensation value. } HT_PHY_DLL_COMP_LOOKUP_TABLE; /** * Process Compensation Fuses for HT PHY, Link Phy Receiver Process Fuse Control Register. */ typedef struct { UINT32 :11; UINT32 DllProcessComp10:2; ///< [12:11] DLL Process Comp bits [1:0], this phy's adjustment. UINT32 DllProcessComp2:1; ///< [13] DLL Process Comp bit 2, Increment or Decrement. UINT32 : (31 - 13); } LINK_PHY_RECEIVER_PROCESS_FUSE_CONTROL_FIELDS; /// Access register as fields or uint32 value. typedef union { UINT32 Value; ///< 32 bit value for register access LINK_PHY_RECEIVER_PROCESS_FUSE_CONTROL_FIELDS Fields; ///< The register bit fields } LINK_PHY_RECEIVER_PROCESS_FUSE_CONTROL; /** * Link Phy Receiver Process DLL Control Register. */ typedef struct { UINT32 DllProcessFreqCtlIndex2:4; ///< [3:0] The DLL Compensation override. UINT32 : (12 - 4); UINT32 DllProcessFreqCtlOverride:1; ///< [12] Enable DLL Compensation overriding. UINT32 : (31 - 12); } LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_FIELDS; /// Access register as fields or uint32 value. typedef union { UINT32 Value; ///< 32 bit value for register access LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_FIELDS Fields; ///< The register bit fields } LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL; /** * Provide the HT PHY DLL compensation value for each HT Link frequency. * * The HT Frequency enum is not contiguous, there are skipped values. Rather than complicate * index calculations, add Invalid entries here marked with an invalid compensation value (invalid * because real compensation values are 0 .. 15). */ CONST STATIC HT_PHY_DLL_COMP_LOOKUP_TABLE ROMDATA HtPhyDllCompLookupTable[] = { {0xAul, 0, 3}, // HT_FREQUENCY_1200M {0xAul, 0, 3}, // HT_FREQUENCY_1400M {0x7ul, 4, 7}, // HT_FREQUENCY_1600M {0x7ul, 4, 7}, // HT_FREQUENCY_1800M {0x5ul, 8, 11}, // HT_FREQUENCY_2000M {0x5ul, 8, 11}, // HT_FREQUENCY_2200M {0x4ul, 12, 15}, // HT_FREQUENCY_2400M {0x3ul, 16, 19}, // HT_FREQUENCY_2600M {0xFFFFFFFFul, 0, 0}, // Invalid {0xFFFFFFFFul, 0, 0}, // Invalid {0x3ul, 20, 23}, // HT_FREQUENCY_2800M {0x2ul, 24, 27}, // HT_FREQUENCY_3000M {0x2ul, 28, 31} // HT_FREQUENCY_3200M }; /*---------------------------------------------------------------------------------------- * P R O T O T Y P E S O F L O C A L F U N C T I O N S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------- * E X P O R T E D F U N C T I O N S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------*/ /** * Disables the desired P-state. * * @CpuServiceMethod{::F_CPU_DISABLE_PSTATE}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] StateNumber The P-State to disable. * @param[in] StdHeader Header for library and services * * @retval AGESA_SUCCESS Always succeeds. */ AGESA_STATUS F15DisablePstate ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN UINT8 StateNumber, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT64 LocalMsrRegister; ASSERT (StateNumber < NM_PS_REG); LibAmdMsrRead (PS_REG_BASE + (UINT32) StateNumber, &LocalMsrRegister, StdHeader); ((F15_PSTATE_MSR *) &LocalMsrRegister)->PsEnable = 0; LibAmdMsrWrite (PS_REG_BASE + (UINT32) StateNumber, &LocalMsrRegister, StdHeader); return (AGESA_SUCCESS); } /*---------------------------------------------------------------------------------------*/ /** * Transitions the executing core to the desired P-state. * * @CpuServiceMethod{::F_CPU_TRANSITION_PSTATE}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] StateNumber The new P-State to make effective. * @param[in] WaitForTransition True if the caller wants the transition completed upon return. * @param[in] StdHeader Header for library and services * * @retval AGESA_SUCCESS Always Succeeds */ AGESA_STATUS F15TransitionPstate ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN UINT8 StateNumber, IN BOOLEAN WaitForTransition, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT64 LocalMsrRegister; LibAmdMsrRead (MSR_PSTATE_CURRENT_LIMIT, &LocalMsrRegister, StdHeader); ASSERT (((PSTATE_CURLIM_MSR *) &LocalMsrRegister)->PstateMaxVal >= StateNumber); LibAmdMsrRead (MSR_PSTATE_CTL, &LocalMsrRegister, StdHeader); ((PSTATE_CTRL_MSR *) &LocalMsrRegister)->PstateCmd = (UINT64) StateNumber; LibAmdMsrWrite (MSR_PSTATE_CTL, &LocalMsrRegister, StdHeader); if (WaitForTransition) { do { LibAmdMsrRead (MSR_PSTATE_STS, &LocalMsrRegister, StdHeader); } while (((PSTATE_STS_MSR *) &LocalMsrRegister)->CurPstate != (UINT64) StateNumber); } return (AGESA_SUCCESS); } /*---------------------------------------------------------------------------------------*/ /** * Determines the rate at which the executing core's time stamp counter is * incrementing. * * @CpuServiceMethod{::F_CPU_GET_TSC_RATE}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[out] FrequencyInMHz TSC actual frequency. * @param[in] StdHeader Header for library and services. * * @return The most severe status of all called services */ AGESA_STATUS F15GetTscRate ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, OUT UINT32 *FrequencyInMHz, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT8 NumBoostStates; UINT32 LocalPciRegister; UINT64 LocalMsrRegister; PCI_ADDR PciAddress; PSTATE_CPU_FAMILY_SERVICES *FamilyServices; LibAmdMsrRead (0xC0010015, &LocalMsrRegister, StdHeader); if ((LocalMsrRegister & 0x01000000) != 0) { FamilyServices = NULL; GetFeatureServicesOfCurrentCore (&PstateFamilyServiceTable, (CONST VOID **)&FamilyServices, StdHeader); ASSERT (FamilyServices != NULL); OptionMultiSocketConfiguration.GetCurrPciAddr (&PciAddress, StdHeader); PciAddress.Address.Function = FUNC_4; PciAddress.Address.Register = CPB_CTRL_REG; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); NumBoostStates = (UINT8) ((F15_CPB_CTRL_REGISTER *) &LocalPciRegister)->NumBoostStates; return (FamilyServices->GetPstateFrequency (FamilyServices, NumBoostStates, FrequencyInMHz, StdHeader)); } else { return (FamilySpecificServices->GetCurrentNbFrequency (FamilySpecificServices, FrequencyInMHz, StdHeader)); } } /*---------------------------------------------------------------------------------------*/ /** * Initially launches the desired core to run from the reset vector. * * @CpuServiceMethod{::F_CPU_AP_INITIAL_LAUNCH}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] SocketNum The Processor on which the core is to be launched * @param[in] ModuleNum The Module in that processor containing that core * @param[in] CoreNum The Core to launch * @param[in] PrimaryCoreNum The id of the module's primary core. * @param[in] StdHeader Header for library and services * * @retval TRUE The core was launched * @retval FALSE The core was previously launched */ BOOLEAN F15LaunchApCore ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN UINT32 SocketNum, IN UINT32 ModuleNum, IN UINT32 CoreNum, IN UINT32 PrimaryCoreNum, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 NodeRelativeCoreNum; UINT32 LocalPciRegister; PCI_ADDR PciAddress; BOOLEAN LaunchFlag; AGESA_STATUS Ignored; // Code Start LaunchFlag = FALSE; NodeRelativeCoreNum = CoreNum - PrimaryCoreNum; GetPciAddress (StdHeader, SocketNum, ModuleNum, &PciAddress, &Ignored); PciAddress.Address.Function = FUNC_0; switch (NodeRelativeCoreNum) { case 0: PciAddress.Address.Register = HT_INIT_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & HT_INIT_CTRL_REQ_DIS) != 0) { LocalPciRegister &= ~HT_INIT_CTRL_REQ_DIS; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 1: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE1_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE1_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 2: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE2_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE2_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 3: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE3_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE3_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 4: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE4_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE4_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 5: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE5_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE5_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 6: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE6_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE6_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 7: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE7_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE7_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 8: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE8_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE8_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; case 9: PciAddress.Address.Register = CORE_CTRL; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((LocalPciRegister & CORE_CTRL_CORE9_EN) == 0) { LocalPciRegister |= CORE_CTRL_CORE9_EN; LibAmdPciWrite (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); LaunchFlag = TRUE; } else { LaunchFlag = FALSE; } break; default: break; } return (LaunchFlag); } /*---------------------------------------------------------------------------------------*/ /** * Provide the features of the next HT link. * * @CpuServiceMethod{::F_GET_NEXT_HT_LINK_FEATURES}. * * This method is different than the HT Phy Features method, because for the phy registers * sublink 1 matches and should be programmed if the link is ganged but for PCI config * registers sublink 1 is reserved if the link is ganged. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in,out] Link Initially zero, each call returns the link number; * caller passes it back unmodified each call. * @param[in,out] LinkBase Initially the PCI bus, device, function=0, offset=0; * Each call returns the HT Host Capability function and offset; * Caller may use it to access registers, but must @b not modify it; * Each new call passes the previous value as input. * @param[out] HtHostFeats The link's features. * @param[in] StdHeader Standard Head Pointer * * @retval TRUE Valid link and features found. * @retval FALSE No more links. */ BOOLEAN F15GetNextHtLinkFeatures ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN OUT UINTN *Link, IN OUT PCI_ADDR *LinkBase, OUT HT_HOST_FEATS *HtHostFeats, IN AMD_CONFIG_PARAMS *StdHeader ) { PCI_ADDR PciAddress; UINT32 RegValue; UINT32 ExtendedFreq; UINTN LinkOffset; BOOLEAN Result; ASSERT (FamilySpecificServices != NULL); // No features present unless link is good and connected. HtHostFeats->HtHostValue = 0; Result = TRUE; // Find next link. if (LinkBase->Address.Register == 0) { // Beginning iteration now. LinkBase->Address.Register = HT_CAPABILITIES_POINTER; LibAmdPciReadBits (*LinkBase, 7, 0, &RegValue, StdHeader); } else { // Get next link offset. LibAmdPciReadBits (*LinkBase, 15, 8, &RegValue, StdHeader); } if (RegValue == 0) { // Are we at the end? Check if we can move to another function. if (LinkBase->Address.Function == 0) { LinkBase->Address.Function = 4; LinkBase->Address.Register = HT_CAPABILITIES_POINTER; LibAmdPciReadBits (*LinkBase, 7, 0, &RegValue, StdHeader); } } if (RegValue != 0) { // Not at end, process the found link. LinkBase->Address.Register = RegValue; // Compute link number *Link = (((LinkBase->Address.Function == 4) ? 4 : 0) + ((LinkBase->Address.Register - 0x80) >> 5)); // Handle pending link power off, check End of Chain, Xmit Off. PciAddress = *LinkBase; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_CONTROL_REG_OFFSET; LibAmdPciReadBits (PciAddress, 7, 6, &RegValue, StdHeader); if (RegValue == 0) { // Check coherency (HTHOST_LINK_TYPE_REG = 0x18) PciAddress = *LinkBase; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_TYPE_REG_OFFSET; LibAmdPciReadBits (PciAddress, 4, 0, &RegValue, StdHeader); if (RegValue == 3) { HtHostFeats->HtHostFeatures.Coherent = 1; } else if (RegValue == 7) { HtHostFeats->HtHostFeatures.NonCoherent = 1; } } // If link was not connected, don't check other attributes, make sure // to return zero, no match. if ((HtHostFeats->HtHostFeatures.Coherent == 1) || (HtHostFeats->HtHostFeatures.NonCoherent == 1)) { // Check gen3 PciAddress = *LinkBase; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_EXTENDED_FREQ; LibAmdPciRead (AccessWidth32, PciAddress, &ExtendedFreq, StdHeader); PciAddress = *LinkBase; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_FREQ_OFFSET; LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); RegValue = (((ExtendedFreq & 0x1) << 4) | ((RegValue & 0x00000F00) >> 8)); if (RegValue > 6) { HtHostFeats->HtHostFeatures.Ht3 = 1; } else { HtHostFeats->HtHostFeatures.Ht1 = 1; } // Check ganged. Must check the bit for sublink 0. LinkOffset = (*Link > 3) ? ((*Link - 4) * 4) : (*Link * 4); PciAddress = *LinkBase; PciAddress.Address.Function = 0; PciAddress.Address.Register = ((UINT32)LinkOffset + 0x170); LibAmdPciReadBits (PciAddress, 0, 0, &RegValue, StdHeader); if (RegValue == 0) { HtHostFeats->HtHostFeatures.UnGanged = 1; } else { if (*Link < 4) { HtHostFeats->HtHostFeatures.Ganged = 1; } else { // If this is a sublink 1 but it will be ganged, clear all features. HtHostFeats->HtHostValue = 0; } } } } else { // end of links. Result = FALSE; } return Result; } /*---------------------------------------------------------------------------------------*/ /** * Checks to see if the HT phy register table entry should be applied * * @CpuServiceMethod{::F_NEXT_LINK_HAS_HTFPY_FEATS}. * * Find the next link which matches, if any. * This method will match for sublink 1 if the link is ganged and sublink 0 matches. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in,out] HtHostCapability Initially the PCI bus, device, function=0, offset=0; * Each call returns the HT Host Capability function and offset; * Caller may use it to access registers, but must @b not modify it; * Each new call passes the previous value as input. * @param[in,out] Link Initially zero, each call returns the link number; caller passes it back unmodified each call. * @param[in] HtPhyLinkType Link type field from a register table entry to compare against * @param[out] MatchedSublink1 TRUE: It is actually just sublink 1 that matches, FALSE: any other condition. * @param[out] Frequency0 The frequency of sublink0 (200 MHz if not connected). * @param[out] Frequency1 The frequency of sublink1 (200 MHz if not connected). * @param[in] StdHeader Standard Head Pointer * * @retval TRUE Link matches * @retval FALSE No more links * */ BOOLEAN F15NextLinkHasHtPhyFeats ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN OUT PCI_ADDR *HtHostCapability, IN OUT UINT32 *Link, IN HT_PHY_LINK_FEATS *HtPhyLinkType, OUT BOOLEAN *MatchedSublink1, OUT HT_FREQUENCIES *Frequency0, OUT HT_FREQUENCIES *Frequency1, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 RegValue; UINT32 ExtendedFreq; UINT32 InternalLinks; UINT32 Width; PCI_ADDR PciAddress; PCI_ADDR SubLink1Address; HT_PHY_LINK_FEATS LinkType; BOOLEAN IsReallyCheckingBoth; BOOLEAN IsFound; BOOLEAN Result; ASSERT (*Link < 4); ASSERT (HtPhyLinkType != NULL); // error checks: No unknown link type bits set and not a "match none" ASSERT ((HtPhyLinkType->HtPhyLinkValue & ~(HTPHY_LINKTYPE_ALL | HTPHY_LINKTYPE_SL0_AND | HTPHY_LINKTYPE_SL1_AND)) == 0); ASSERT (HtPhyLinkType->HtPhyLinkValue != 0); Result = FALSE; IsFound = FALSE; while (!IsFound) { *Frequency0 = 0; *Frequency1 = 0; IsReallyCheckingBoth = FALSE; *MatchedSublink1 = FALSE; LinkType.HtPhyLinkValue = 0; // Find next link. PciAddress = *HtHostCapability; if (PciAddress.Address.Register == 0) { // Beginning iteration now. PciAddress.Address.Register = HT_CAPABILITIES_POINTER; LibAmdPciReadBits (PciAddress, 7, 0, &RegValue, StdHeader); } else { // Get next link offset. LibAmdPciReadBits (PciAddress, 15, 8, &RegValue, StdHeader); } if (RegValue != 0) { HtHostCapability->Address.Register = RegValue; // Compute link number of this sublink pair (so we don't need to account for function). *Link = ((HtHostCapability->Address.Register - 0x80) >> 5); // Set the link indicators. This assumes each sublink set is contiguous, that is, links 3, 2, 1, 0 and 7, 6, 5, 4. LinkType.HtPhyLinkValue |= (HTPHY_LINKTYPE_SL0_LINK0 << *Link); LinkType.HtPhyLinkValue |= (HTPHY_LINKTYPE_SL1_LINK4 << *Link); // Read IntLnkRoute from the Link Initialization Status register. PciAddress = *HtHostCapability; PciAddress.Address.Function = 0; PciAddress.Address.Register = 0x1A0; LibAmdPciReadBits (PciAddress, 23, 16, &InternalLinks, StdHeader); // if ganged, don't read sublink 1, but use sublink 0 to check. SubLink1Address = *HtHostCapability; // Check ganged. Since we got called for sublink 0, sublink 1 is implemented also, // but only access it if it is also unganged. PciAddress = *HtHostCapability; PciAddress.Address.Function = 0; PciAddress.Address.Register = ((*Link * 4) + 0x170); LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); RegValue = (RegValue & 0x01); if (RegValue == 0) { // Then really read sublink1, rather than using sublink0 SubLink1Address.Address.Function = 4; IsReallyCheckingBoth = TRUE; } // Checks for Sublink 0 // Handle pending link power off, check End of Chain, Xmit Off. PciAddress = *HtHostCapability; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_CONTROL_REG_OFFSET; LibAmdPciReadBits (PciAddress, 7, 6, &RegValue, StdHeader); if (RegValue == 0) { // Check coherency (HTHOST_LINK_TYPE_REG = 0x18) PciAddress = *HtHostCapability; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_TYPE_REG_OFFSET; LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); if ((RegValue & 0x1F) == 3) { LinkType.HtPhyLinkFeatures.HtPhySL0Coh = 1; } else if ((RegValue & 0x1F) == 7) { LinkType.HtPhyLinkFeatures.HtPhySL0NonCoh = 1; } } // If link was not connected, don't check other attributes, make sure // to return zero, no match. (Phy may be powered off.) if ((LinkType.HtPhyLinkFeatures.HtPhySL0Coh) || (LinkType.HtPhyLinkFeatures.HtPhySL0NonCoh)) { // Check gen3 PciAddress = *HtHostCapability; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_EXTENDED_FREQ; LibAmdPciRead (AccessWidth32, PciAddress, &ExtendedFreq, StdHeader); PciAddress = *HtHostCapability; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_FREQ_OFFSET; LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); RegValue = (((ExtendedFreq & 0x1) << 4) | ((RegValue & 0x00000F00) >> 8)); *Frequency0 = RegValue; if (RegValue > 6) { LinkType.HtPhyLinkFeatures.HtPhySL0Ht3 = 1; } else { LinkType.HtPhyLinkFeatures.HtPhySL0Ht1 = 1; } // Check internal / external if ((InternalLinks & (1 << *Link)) == 0) { // External LinkType.HtPhyLinkFeatures.HtPhySL0External = 1; } else { // Internal LinkType.HtPhyLinkFeatures.HtPhySL0Internal = 1; } } else { LinkType.HtPhyLinkValue &= ~(HTPHY_LINKTYPE_SL0_ALL); } // Checks for Sublink 1 // Handle pending link power off, check End of Chain, Xmit Off. // Also, if the links are ganged but the width is not 16 bits, treat it is an inactive lane. PciAddress = SubLink1Address; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_CONTROL_REG_OFFSET; LibAmdPciReadBits (PciAddress, 7, 6, &RegValue, StdHeader); LibAmdPciReadBits (PciAddress, 31, 24, &Width, StdHeader); if ((RegValue == 0) && (IsReallyCheckingBoth || (Width == 0x11))) { // Check coherency (HTHOST_LINK_TYPE_REG = 0x18) PciAddress = SubLink1Address; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_TYPE_REG_OFFSET; LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); if ((RegValue & 0x1F) == 3) { LinkType.HtPhyLinkFeatures.HtPhySL1Coh = 1; } else if ((RegValue & 0x1F) == 7) { LinkType.HtPhyLinkFeatures.HtPhySL1NonCoh = 1; } } if ((LinkType.HtPhyLinkFeatures.HtPhySL1Coh) || (LinkType.HtPhyLinkFeatures.HtPhySL1NonCoh)) { // Check gen3 PciAddress = SubLink1Address; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_EXTENDED_FREQ; LibAmdPciRead (AccessWidth32, PciAddress, &ExtendedFreq, StdHeader); PciAddress = SubLink1Address; PciAddress.Address.Register = PciAddress.Address.Register + HT_LINK_FREQ_OFFSET; LibAmdPciRead (AccessWidth32, PciAddress, &RegValue, StdHeader); RegValue = (((ExtendedFreq & 0x1) << 4) | ((RegValue & 0x00000F00) >> 8)); *Frequency1 = RegValue; if (RegValue > 6) { LinkType.HtPhyLinkFeatures.HtPhySL1Ht3 = 1; } else { LinkType.HtPhyLinkFeatures.HtPhySL1Ht1 = 1; } // Check internal / external. Note that we do really check sublink 1 regardless of ganging. if ((InternalLinks & (1 << (*Link + 4))) == 0) { // External LinkType.HtPhyLinkFeatures.HtPhySL1External = 1; } else { // Internal LinkType.HtPhyLinkFeatures.HtPhySL1Internal = 1; } } else { LinkType.HtPhyLinkValue &= ~(HTPHY_LINKTYPE_SL1_ALL); } // Determine if the link matches the entry criteria. // For Deemphasis checking, indicate whether it was actually sublink 1 that matched. // If the link is ganged or only sublink 0 matched, or the link features didn't match, this is false. if (((HtPhyLinkType->HtPhyLinkValue & HTPHY_LINKTYPE_SL0_AND) == 0) && ((HtPhyLinkType->HtPhyLinkValue & HTPHY_LINKTYPE_SL1_AND) == 0)) { // Match if any feature matches (OR) Result = (BOOLEAN) ((LinkType.HtPhyLinkValue & HtPhyLinkType->HtPhyLinkValue) != 0); } else { // Match if all features match (AND) Result = (BOOLEAN) ((HtPhyLinkType->HtPhyLinkValue & ~(HTPHY_LINKTYPE_SL0_AND | HTPHY_LINKTYPE_SL1_AND)) == (LinkType.HtPhyLinkValue & HtPhyLinkType->HtPhyLinkValue)); } if (Result) { if (IsReallyCheckingBoth && (((LinkType.HtPhyLinkValue & HtPhyLinkType->HtPhyLinkValue) & (HTPHY_LINKTYPE_SL1_ALL)) != 0)) { *MatchedSublink1 = TRUE; } IsFound = TRUE; } else { // Go to next link } } else { // No more links IsFound = TRUE; } } return Result; } /*---------------------------------------------------------------------------------------*/ /** * Applies an HT Phy read-modify-write based on an HT Phy register table entry. * * @CpuServiceMethod{::F_SET_HT_PHY_REGISTER}. * * This function performs the necessary sequence of PCI reads, writes, and waits * necessary to program an HT Phy register. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] HtPhyEntry HT Phy register table entry to apply * @param[in] CapabilitySet The link's HT Host base address. * @param[in] Link Zero based, node, link number (not package link). * @param[in] StdHeader Config handle for library and services * */ VOID F15SetHtPhyRegister ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN HT_PHY_TYPE_ENTRY_DATA *HtPhyEntry, IN PCI_ADDR CapabilitySet, IN UINT32 Link, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Temp; UINT32 PhyReg; PCI_ADDR PhyBase; // Determine the PCI config address of the HT Phy portal PhyBase = CapabilitySet; PhyBase.Address.Function = FUNC_4; PhyBase.Address.Register = ((Link << 3) + REG_HT4_PHY_OFFSET_BASE_4X180); LibAmdPciRead (AccessWidth32, PhyBase, &PhyReg, StdHeader); // Handle direct map registers if needed PhyReg &= ~(HTPHY_DIRECT_OFFSET_MASK); if ((HtPhyEntry->Address > 0x3FF) || ((HtPhyEntry->Address >= 0xE) && (HtPhyEntry->Address <= 0x11))) { PhyReg |= HTPHY_DIRECT_MAP; } PhyReg |= (HtPhyEntry->Address); // Ask the portal to read the HT Phy Register contents LibAmdPciWrite (AccessWidth32, PhyBase, &PhyReg, StdHeader); do { LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); } while (!(Temp & HTPHY_IS_COMPLETE_MASK)); // Get the current register contents and do the update requested by the table PhyBase.AddressValue += 4; LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); Temp &= ~(HtPhyEntry->Mask); Temp |= (HtPhyEntry->Data); LibAmdPciWrite (AccessWidth32, PhyBase, &Temp, StdHeader); PhyBase.AddressValue -= 4; // Ask the portal to write our updated value to the HT Phy PhyReg |= HTPHY_WRITE_CMD; LibAmdPciWrite (AccessWidth32, PhyBase, &PhyReg, StdHeader); do { LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); } while (!(Temp & HTPHY_IS_COMPLETE_MASK)); } /*---------------------------------------------------------------------------------------*/ /** * Applies an HT Phy write to a specified Phy register. * * @CpuServiceMethod{::F_SET_HT_PHY_REGISTER}. * * The caller is responsible for performing any read and modify steps. * This function performs the necessary sequence of PCI reads, writes, and waits * necessary to program an HT Phy register. * * @param[in] CapabilitySet The link's HT Host base address. * @param[in] Link Zero based, node, link number (not package link). * @param[in] Address The HT Phy register address * @param[in] Data The data to write to the register * @param[in] StdHeader Config handle for library and services * */ VOID STATIC F15WriteOnlyHtPhyRegister ( IN PCI_ADDR CapabilitySet, IN UINT32 Link, IN UINT32 Address, IN UINT32 Data, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Temp; UINT32 PhyReg; PCI_ADDR PhyBase; // Determine the PCI config address of the HT Phy portal PhyBase = CapabilitySet; PhyBase.Address.Function = FUNC_4; PhyBase.Address.Register = ((Link << 3) + REG_HT4_PHY_OFFSET_BASE_4X180); LibAmdPciRead (AccessWidth32, PhyBase, &PhyReg, StdHeader); // Handle direct map registers if needed PhyReg &= ~(HTPHY_DIRECT_OFFSET_MASK); if ((Address > 0x3FF) || ((Address >= 0xE) && (Address <= 0x11))) { PhyReg |= HTPHY_DIRECT_MAP; } PhyReg |= (Address); // Get the current register contents and do the update requested by the table PhyBase.AddressValue += 4; LibAmdPciWrite (AccessWidth32, PhyBase, &Data, StdHeader); PhyBase.AddressValue -= 4; // Ask the portal to write our updated value to the HT Phy PhyReg |= HTPHY_WRITE_CMD; LibAmdPciWrite (AccessWidth32, PhyBase, &PhyReg, StdHeader); do { LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); } while (!(Temp & HTPHY_IS_COMPLETE_MASK)); } /*---------------------------------------------------------------------------------------*/ /** * Get the value of an HT PHY register. * * Reading HT Phy registers is not generally useful, because they return the effective value, * not the currently written value. So be warned, this function is dangerous if used to read * a register that will be udpated subsequently elsewhere. * * This routine is useful for reading hardware status from the HT Phy that can be used to set * other phy registers. * * @param[in] CapabilitySet The link's HT Host base address. * @param[in] Link Zero based, node link number (not package link). * @param[in] Address The HT Phy register address to read * @param[in] StdHeader Config handle for library and services * * @return The register content (in most cases, the effective content not the pending content) * */ UINT32 STATIC F15GetHtPhyRegister ( IN PCI_ADDR CapabilitySet, IN UINT32 Link, IN UINT32 Address, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 Temp; UINT32 PhyReg; PCI_ADDR PhyBase; // Determine the PCI config address of the HT Phy portal PhyBase = CapabilitySet; PhyBase.Address.Function = FUNC_4; PhyBase.Address.Register = ((Link << 3) + REG_HT4_PHY_OFFSET_BASE_4X180); LibAmdPciRead (AccessWidth32, PhyBase, &PhyReg, StdHeader); // Handle direct map registers if needed PhyReg &= ~(HTPHY_DIRECT_OFFSET_MASK); if ((Address > 0x3FF) || ((Address >= 0xE) && (Address <= 0x11))) { PhyReg |= HTPHY_DIRECT_MAP; } PhyReg |= Address; // Ask the portal to read the HT Phy Register contents LibAmdPciWrite (AccessWidth32, PhyBase, &PhyReg, StdHeader); do { LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); } while (!(Temp & HTPHY_IS_COMPLETE_MASK)); // Get the current register contents PhyBase.AddressValue += 4; LibAmdPciRead (AccessWidth32, PhyBase, &Temp, StdHeader); return Temp; } /*---------------------------------------------------------------------------------------*/ /** * A Family Specific Workaround method, to override HT DLL Compensation. * * \@TableTypeFamSpecificInstances. * * The Link Product Information register can be fused to contain an HT PHY DLL Compensation Override table. * Based on link frequency, a compensation override can be selected from the value. * To accomodate individual link differences in the package, each link can also have a DLL process compensation * value set. This value can apply an adjustment to the compensation value. * * @param[in] Data The table data value, for example to indicate which CPU and Platform types matched. * @param[in] StdHeader Config params for library, services. */ VOID F15HtPhyOverrideDllCompensation ( IN UINT32 Data, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 ProductLinkInfo; UINT32 Link; CPU_LOGICAL_ID CpuFamilyRevision; PCI_ADDR StartingCapabilitySet; PCI_ADDR CapabilitySet; PCI_ADDR PciAddress; CPU_SPECIFIC_SERVICES *FamilySpecificServices; BOOLEAN MatchedSublink1; HT_FREQUENCIES Freq0; HT_FREQUENCIES Freq1; UINTN Sublink; HT_PHY_LINK_FEATS DesiredLinkFeats; BOOLEAN IsEarlyRevProcessor; BOOLEAN IsHardwareReportingComp; UINTN LinkFrequency; UINT32 Compensation; UINT32 Adjustment; BOOLEAN IsIncrementAdjust; LINK_PHY_RECEIVER_PROCESS_FUSE_CONTROL LinkPhyReceiverProcessFuseControl; LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL LinkPhyReceiverProcessDllControl; OptionMultiSocketConfiguration.GetCurrPciAddr (&StartingCapabilitySet, StdHeader); GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader); GetCpuServicesFromLogicalId (&CpuFamilyRevision, (CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader); // Check if the hardware reported any compensation values. IsEarlyRevProcessor = (BOOLEAN) ((Data == 0) ? TRUE : FALSE); PciAddress = StartingCapabilitySet; PciAddress.Address.Function = FUNC_5; PciAddress.Address.Register = 0x190; LibAmdPciRead (AccessWidth32, PciAddress, &ProductLinkInfo, StdHeader); IsHardwareReportingComp = (BOOLEAN) (ProductLinkInfo != 0); if (!IsEarlyRevProcessor || IsHardwareReportingComp) { // Process all the sublink 0's and then all the sublink 1's that are at HT3 frequency. for (Sublink = 0; Sublink < 2; Sublink++) { CapabilitySet = StartingCapabilitySet; Link = 0; DesiredLinkFeats.HtPhyLinkValue = ((Sublink == 0) ? HTPHY_LINKTYPE_SL0_HT3 : HTPHY_LINKTYPE_SL0_HT3); while (FamilySpecificServices->NextLinkHasHtPhyFeats ( FamilySpecificServices, &CapabilitySet, &Link, &DesiredLinkFeats, &MatchedSublink1, &Freq0, &Freq1, StdHeader)) { // Look up compensation value. Remember that we matched links which are at HT3 frequency, so Freq[1,0] // should safely be greater than or equal to 1.2 GHz. if (Sublink == 0) { LinkFrequency = Freq0 - HT_FREQUENCY_1200M; } else { LinkFrequency = (MatchedSublink1 ? Freq1 : Freq0) - HT_FREQUENCY_1200M; } // This assert would catch frequencies higher than we know how to support, or any table overrun bug. ASSERT (LinkFrequency < (sizeof (HtPhyDllCompLookupTable) / sizeof (HT_PHY_DLL_COMP_LOOKUP_TABLE))); // Since there are invalid entries in the table, for frequency enum skipped values, ensure we did not // pick one of those entries. This should be impossible from real hardware. ASSERT (HtPhyDllCompLookupTable[LinkFrequency].DefaultComp != 0xFFFFFFFFul); if (IsHardwareReportingComp) { LibAmdPciReadBits ( PciAddress, HtPhyDllCompLookupTable[LinkFrequency].CtlIndexHiBit, HtPhyDllCompLookupTable[LinkFrequency].CtlIndexLoBit, &Compensation, StdHeader); } else { Compensation = HtPhyDllCompLookupTable[LinkFrequency].DefaultComp; } // Apply any per PHY adjustment LinkPhyReceiverProcessFuseControl.Value = F15GetHtPhyRegister ( CapabilitySet, Link, ((Sublink == 0) ? HT_PHY_FUSE_PROC_DLL_PROCESS_COMP_RD_SL0 : HT_PHY_FUSE_PROC_DLL_PROCESS_COMP_RD_SL1), StdHeader); Adjustment = LinkPhyReceiverProcessFuseControl.Fields.DllProcessComp10; IsIncrementAdjust = (BOOLEAN) ((LinkPhyReceiverProcessFuseControl.Fields.DllProcessComp2 == 0) ? TRUE : FALSE); if (IsIncrementAdjust) { Compensation = (((Compensation + Adjustment) > 0x000F) ? 0x000F : (Compensation + Adjustment)); } else { // decrement adjustment Compensation = ((Compensation < Adjustment) ? 0 : (Compensation - Adjustment)); } // Update the DLL Compensation LinkPhyReceiverProcessDllControl.Value = F15GetHtPhyRegister ( CapabilitySet, Link, HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_RD, StdHeader); LinkPhyReceiverProcessDllControl.Fields.DllProcessFreqCtlOverride = 1; LinkPhyReceiverProcessDllControl.Fields.DllProcessFreqCtlIndex2 = Compensation; F15WriteOnlyHtPhyRegister ( CapabilitySet, Link, ((Sublink == 0) ? HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_SL0 : HT_PHY_LINK_PHY_RECEIVER_PROCESS_DLL_CONTROL_SL1), LinkPhyReceiverProcessDllControl.Value, StdHeader); } } } } /*---------------------------------------------------------------------------------------*/ /** * Returns whether or not BIOS is responsible for configuring the NB COFVID. * * @CpuServiceMethod{::F_CPU_IS_NBCOF_INIT_NEEDED}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] PciAddress The northbridge to query by pci base address. * @param[out] NbVidUpdateAll Do all NbVids need to be updated * @param[in] StdHeader Header for library and services * * @retval TRUE Perform northbridge frequency and voltage config. * @retval FALSE Do not configure them. */ BOOLEAN F15CommonGetNbCofVidUpdate ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN PCI_ADDR *PciAddress, OUT BOOLEAN *NbVidUpdateAll, IN AMD_CONFIG_PARAMS *StdHeader ) { *NbVidUpdateAll = FALSE; return FALSE; } /*---------------------------------------------------------------------------------------*/ /** * Is the Northbridge PState feature enabled? * * @CpuServiceMethod{::F_IS_NB_PSTATE_ENABLED}. * * @param[in] FamilySpecificServices The current Family Specific Services. * @param[in] PlatformConfig Platform profile/build option config structure. * @param[in] StdHeader Handle of Header for calling lib functions and services. * * @retval TRUE The NB PState feature is enabled. * @retval FALSE The NB PState feature is not enabled. */ BOOLEAN F15IsNbPstateEnabled ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN PLATFORM_CONFIGURATION *PlatformConfig, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 LocalPciRegister; PCI_ADDR PciAddress; BOOLEAN PowerMode; BOOLEAN SkipHwCfg; SkipHwCfg = FALSE; IDS_OPTION_HOOK (IDS_NBPSDIS_OVERRIDE, &SkipHwCfg, StdHeader); // Defaults to Power Optimized Mode PowerMode = TRUE; // If system is optimized for performance, disable NB P-States if (PlatformConfig->PlatformProfile.PlatformPowerPolicy == Performance) { PowerMode = FALSE; } PciAddress.AddressValue = F15_NB_PSTATE_CTRL_PCI_ADDR; LibAmdPciRead (AccessWidth32, PciAddress, &LocalPciRegister, StdHeader); if ((((((F15_NB_PSTATE_CTRL_REGISTER *) &LocalPciRegister)->NbPstateMaxVal != 0) && (!IsNonCoherentHt1 (StdHeader))) || SkipHwCfg) && (PowerMode)) { return TRUE; } return FALSE; }