summaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c')
-rw-r--r--src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c b/src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c
new file mode 100644
index 0000000000..c319a817de
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f15tn/Proc/CPU/Family/0x15/cpuF15Utilities.c
@@ -0,0 +1,1204 @@
+/* $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 2008 - 2012 ADVANCED MICRO DEVICES, INC. All Rights Reserved.
+ *
+ * AMD is granting you permission to use this software (the Materials)
+ * pursuant to the terms and conditions of your Software License Agreement
+ * with AMD. This header does *NOT* give you permission to use the Materials
+ * or any rights under AMD's intellectual property. Your use of any portion
+ * of these Materials shall constitute your acceptance of those terms and
+ * conditions. If you do not agree to the terms and conditions of the Software
+ * License Agreement, please do not use any portion of these Materials.
+ *
+ * CONFIDENTIALITY: The Materials and all other information, identified as
+ * confidential and provided to you by AMD shall be kept confidential in
+ * accordance with the terms and conditions of the Software License Agreement.
+ *
+ * LIMITATION OF LIABILITY: THE MATERIALS AND ANY OTHER RELATED INFORMATION
+ * PROVIDED TO YOU BY AMD ARE PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
+ * MERCHANTABILITY, NONINFRINGEMENT, TITLE, FITNESS FOR ANY PARTICULAR PURPOSE,
+ * OR WARRANTIES ARISING FROM CONDUCT, COURSE OF DEALING, OR USAGE OF TRADE.
+ * IN NO EVENT SHALL AMD OR ITS LICENSORS BE LIABLE FOR ANY DAMAGES WHATSOEVER
+ * (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS
+ * INTERRUPTION, OR LOSS OF INFORMATION) ARISING OUT OF AMD'S NEGLIGENCE,
+ * GROSS NEGLIGENCE, THE USE OF OR INABILITY TO USE THE MATERIALS OR ANY OTHER
+ * RELATED INFORMATION PROVIDED TO YOU BY AMD, EVEN IF AMD HAS BEEN ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES. BECAUSE SOME JURISDICTIONS PROHIBIT THE
+ * EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES,
+ * THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
+ *
+ * AMD does not assume any responsibility for any errors which may appear in
+ * the Materials or any other related information provided to you by AMD, or
+ * result from use of the Materials or any related information.
+ *
+ * You agree that you will not reverse engineer or decompile the Materials.
+ *
+ * NO SUPPORT OBLIGATION: AMD is not obligated to furnish, support, or make any
+ * further information, software, technical information, know-how, or show-how
+ * available to you. Additionally, AMD retains the right to modify the
+ * Materials at any time, without notice, and is not obligated to provide such
+ * modified Materials to you.
+ *
+ * U.S. GOVERNMENT RESTRICTED RIGHTS: The Materials are provided with
+ * "RESTRICTED RIGHTS." Use, duplication, or disclosure by the Government is
+ * subject to the restrictions as set forth in FAR 52.227-14 and
+ * DFAR252.227-7013, et seq., or its successor. Use of the Materials by the
+ * Government constitutes acknowledgement of AMD's proprietary rights in them.
+ *
+ * EXPORT ASSURANCE: You agree and certify that neither the Materials, nor any
+ * direct product thereof will be exported directly or indirectly, into any
+ * country prohibited by the United States Export Administration Act and the
+ * regulations thereunder, without the required authorization from the U.S.
+ * government nor will be used for any purpose prohibited by the same.
+ ******************************************************************************
+ */
+
+/*----------------------------------------------------------------------------------------
+ * 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;
+}