| /* $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_SL1_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; |
| } |