/* $NoKeywords:$ */ /** * @file * * Link Optimization Routines. * * Contains routines for determining width, frequency, and other * Link features * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: HyperTransport * @e \$Revision: 35136 $ @e \$Date: 2010-07-16 11:29:48 +0800 (Fri, 16 Jul 2010) $ * */ /* ***************************************************************************** * * Copyright (c) 2011, 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. * * *************************************************************************** * */ /* *---------------------------------------------------------------------------- * MODULES USED * *---------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "Ids.h" #include "Topology.h" #include "htFeat.h" #include "IdsHt.h" #include "htInterface.h" #include "htNb.h" #include "htFeatOptimization.h" #include "htNotify.h" #include "Filecode.h" CODE_GROUP (G1_PEICC) RDATA_GROUP (G1_PEICC) #define FILECODE PROC_HT_FEATURES_HTFEATOPTIMIZATION_FILECODE extern CONST PF_HtIdsGetPortOverride ROMDATA pf_HtIdsGetPortOverride; /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ #define PCI_CONFIG_COMMAND_REG04 4 #define PCI_CONFIG_REVISION_REG08 8 /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*************************************************************************** *** Link Optimization *** ***************************************************************************/ /*----------------------------------------------------------------------------------------*/ /** * Given the bits set in the register field, return the width it represents. * * As invalid width values or encodings are rare except during debug, catch those using * ASSERT(). This means theoretically we are returning an incorrect result if that * happens. The default chosen for the result is arbitrarily 8 bits. This is likely * not to be the actual correct width and may cause a crash, hang, or incorrect operation. * Hardware often ignores writes of invalid width encodings. * * @note This routine is used for CPUs as well as IO devices, as all comply to the * "HyperTransport I/O Link Specification ". * * @param[in] Value The bits for the register * * @return The width */ UINT8 STATIC ConvertBitsToWidth ( IN UINT8 Value ) { UINT8 Result; Result = 0; switch (Value) { case 1: Result = 16; break; case 0: Result = 8; break; case 3: Result = 32; break; case 5: Result = 4; break; case 4: Result = 2; break; default: ASSERT (FALSE); } return Result; } /*----------------------------------------------------------------------------------------*/ /** * Translate a desired width setting to the bits to set in the register field. * * As invalid width values or encodings are rare except during debug, catch those using * ASSERT(). This means theoretically we are returning an incorrect result if that * happens. The default chosen for the result is arbitrarily 8 bits. This is likely * not to be the actual correct width and may cause a crash, hang, or incorrect operation. * Hardware often ignores writes of invalid width encodings. * * @note This routine is used for CPUs as well as IO devices, as all comply to the * "HyperTransport I/O Link Specification ". * * @param[in] Value the width Value * * @return The bits for the register */ UINT8 ConvertWidthToBits ( IN UINT8 Value ) { UINT8 Result; Result = 8; switch (Value) { case 16: Result = 1; break; case 8: Result = 0; break; case 32: Result = 3; break; case 4: Result = 5; break; case 2: Result = 4; break; default: ASSERT (FALSE); } return Result; } /*----------------------------------------------------------------------------------------*/ /** * Access HT Link Control Register. * * @HtFeatMethod{::F_SET_HT_CONTROL_REGISTER_BITS} * * Provide a common routine for accessing the HT Link Control registers (84, a4, c4, * e4), to enforce not clearing the HT CRC error bits. Replaces direct use of * AmdPCIWriteBits(). * * @note: This routine is called for CPUs as well as IO Devices! All comply to the * "HyperTransport I/O Link Specification ". * * @param[in] Reg the PCI config address the control register * @param[in] HiBit the high bit number * @param[in] LoBit the low bit number * @param[in] Value the value to write to that bit range. Bit 0 => loBit. * @param[in] State Our state, config handle for lib */ VOID SetHtControlRegisterBits ( IN PCI_ADDR Reg, IN UINT8 HiBit, IN UINT8 LoBit, IN UINT32 *Value, IN STATE_DATA *State ) { UINT32 Temp; UINT32 mask; ASSERT ((HiBit < 32) && (LoBit < 32) && (HiBit >= LoBit) && ((Reg.AddressValue & 0x3) == 0)); ASSERT ((HiBit < 8) || (LoBit > 9)); // A 1 << 32 == 1 << 0 due to x86 SHL instruction, so skip if that is the case if ((HiBit - LoBit) != 31) { mask = (((UINT32)1 << (HiBit - LoBit + 1)) - 1); } else { mask = (UINT32)0xFFFFFFFF; } LibAmdPciRead (AccessWidth32, Reg, &Temp, State->ConfigHandle); Temp &= ~(mask << LoBit); Temp |= (*Value & mask) << LoBit; Temp &= (UINT32)HT_CONTROL_CLEAR_CRC; LibAmdPciWrite (AccessWidth32, Reg, &Temp, State->ConfigHandle); } /*----------------------------------------------------------------------------------------*/ /** * Set HT Frequency register for IO Devices * * Provide a common routine for accessing the HT Link Frequency registers at offset 8 * and 0x10, to enforce not clearing the HT Link error bits. Replaces direct use of * AmdPCIWriteBits(). * * @note This routine is called for IO Devices only!! All comply to the * "HyperTransport I/O Link Specification ". * * @param[in] Reg the PCI config address the control register * @param[in] Hibit the high bit number * @param[in] Lobit the low bit number * @param[in] Value the value to write to that bit range. Bit 0 => loBit. * @param[in] State Our state, config handle for lib */ VOID STATIC SetHtIoFrequencyRegisterBits ( IN PCI_ADDR Reg, IN UINT8 Hibit, IN UINT8 Lobit, IN UINT32 *Value, IN STATE_DATA *State ) { UINT32 Mask; UINT32 Temp; ASSERT ((Hibit < 32) && (Lobit < 32) && (Hibit >= Lobit) && ((Reg.AddressValue & 0x3) == 0)); ASSERT ((Hibit < 12) || (Lobit > 14)); // A 1<<32 == 1<<0 due to x86 SHL instruction, so skip if that is the case if ((Hibit - Lobit) != 31) { Mask = (((UINT32)1 << ((Hibit - Lobit) + 1)) - 1); } else { Mask = (UINT32)0xFFFFFFFF; } LibAmdPciRead (AccessWidth32, Reg, &Temp, State->ConfigHandle); Temp &= ~(Mask << Lobit); Temp |= (*Value & Mask) << Lobit; Temp &= (UINT32)HT_FREQUENCY_CLEAR_LINK_ERRORS; LibAmdPciWrite (AccessWidth32, Reg, &Temp, State->ConfigHandle); } /*----------------------------------------------------------------------------------------*/ /** * Get Link features into system data structure. * * @HtFeatMethod{::F_GATHER_LINK_DATA} * * For all discovered Links, populate the port list with the frequency and width * capabilities. Gather support data for: * - Unit ID Clumping * * @param[in] State our global state, port list */ VOID GatherLinkData ( IN STATE_DATA *State ) { UINT8 i; PCI_ADDR LinkBase; PCI_ADDR Reg; UINT32 Bits; UINT8 Revision; // Get the capability base for whatever device type the link port is on for (i = 0; i < (State->TotalLinks * 2); i++) { if ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) { LinkBase = State->Nb->MakeLinkBase ((*State->PortList)[i].NodeID, (*State->PortList)[i].Link, State->Nb); (*State->PortList)[i].Pointer = LinkBase; } else { LinkBase = (*State->PortList)[i].Pointer; if ((*State->PortList)[i].Link == 1) { LinkBase.Address.Register += HTSLAVE_LINK01_OFFSET; } } // Getting the Width is standard across device types Reg = LinkBase; Reg.Address.Register += HTSLAVE_LINK_CONTROL_0_REG; LibAmdPciReadBits (Reg, 22, 20, &Bits, State->ConfigHandle); (*State->PortList)[i].PrvWidthOutCap = ConvertBitsToWidth ((UINT8)Bits); LibAmdPciReadBits (Reg, 18, 16, &Bits, State->ConfigHandle); (*State->PortList)[i].PrvWidthInCap = ConvertBitsToWidth ((UINT8)Bits); // Get Frequency and other device type specific features if ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) { State->Nb->GatherLinkFeatures (&(*State->PortList)[i], State->HtInterface, State->PlatformConfiguration, State->Nb); } else { Reg = LinkBase; Reg.Address.Register += HTSLAVE_FREQ_REV_0_REG; LibAmdPciReadBits (Reg, 31, 16, &Bits, State->ConfigHandle); (*State->PortList)[i].PrvFrequencyCap = Bits; // Unit ID Clumping Support if (State->IsUsingUnitIdClumping) { if (DoesDeviceHaveHtSubtypeCap (LinkBase, HT_UNITID_CAPABILITY, &Reg, State)) { Reg.Address.Register += HTUNIT_SUPPORT_REG; LibAmdPciReadBits (Reg, 31, 0, &Bits, State->ConfigHandle); } else { // Not there, that's ok, we don't know that it should have one. // Check for Passive support. (Bit 0 won't be set if full support is implemented, // so we can use it to indicate passive support in our portlist struct). Reg = LinkBase; Reg.Address.Register += HTSLAVE_FEATURECAP_REG; Bits = 1; LibAmdPciWriteBits (Reg, 5, 5, &Bits, State->ConfigHandle); LibAmdPciReadBits (Reg, 5, 5, &Bits, State->ConfigHandle); } (*State->PortList)[i].ClumpingSupport = Bits; } else { (*State->PortList)[i].ClumpingSupport = HT_CLUMPING_DISABLE; } Reg = LinkBase; Reg.Address.Register = PCI_CONFIG_REVISION_REG08; LibAmdPciReadBits ( LinkBase, 7, 0, &Bits, State->ConfigHandle); Revision = (UINT8) Bits; LinkBase.Address.Register = 0; LibAmdPciRead (AccessWidth32, LinkBase, &Bits, State->ConfigHandle); State->HtInterface->GetDeviceCapOverride ((*State->PortList)[i].NodeID, (*State->PortList)[i].HostLink, (*State->PortList)[i].HostDepth, (*State->PortList)[i].Pointer, Bits, Revision, (*State->PortList)[i].Link, &((*State->PortList)[i].PrvWidthInCap), &((*State->PortList)[i].PrvWidthOutCap), &((*State->PortList)[i].PrvFrequencyCap), &((*State->PortList)[i].ClumpingSupport), State); } } } /*----------------------------------------------------------------------------------------*/ /** * Optimize Links. * * @HtFeatMethod{::F_SELECT_OPTIMAL_WIDTH_AND_FREQUENCY} * * For all Links: * Examine both sides of a Link and determine the optimal frequency and width, * taking into account externally provided limits and enforcing any other limit * or matching rules as applicable except subLink balancing. Update the port * list data with the optimal settings. * * @note no hardware state changes in this routine. * * @param[in,out] State Process and update portlist */ VOID SelectOptimalWidthAndFrequency ( IN OUT STATE_DATA *State ) { UINT8 i; UINT8 j; UINT8 Freq; UINT32 Temp; UINT32 CbPcbFreqLimit; UINT8 CbPcbABDownstreamWidth; UINT8 CbPcbBAUpstreamWidth; for (i = 0; i < (State->TotalLinks * 2); i += 2) { CbPcbFreqLimit = HT_FREQUENCY_NO_LIMIT; CbPcbABDownstreamWidth = HT_WIDTH_16_BITS; CbPcbBAUpstreamWidth = HT_WIDTH_16_BITS; if (((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) && ((*State->PortList)[i + 1].Type == PORTLIST_TYPE_CPU)) { State->HtInterface->GetCpu2CpuPcbLimits ((*State->PortList)[i].NodeID, (*State->PortList)[i].Link, (*State->PortList)[i + 1].NodeID, (*State->PortList)[i + 1].Link, &CbPcbABDownstreamWidth, &CbPcbBAUpstreamWidth, &CbPcbFreqLimit, State ); } else { State->HtInterface->GetIoPcbLimits ((*State->PortList)[i + 1].NodeID, (*State->PortList)[i + 1].HostLink, (*State->PortList)[i + 1].HostDepth, &CbPcbABDownstreamWidth, &CbPcbBAUpstreamWidth, &CbPcbFreqLimit, State ); } Temp = (*State->PortList)[i].PrvFrequencyCap; Temp &= (*State->PortList)[i + 1].PrvFrequencyCap; Temp &= CbPcbFreqLimit; (*State->PortList)[i].CompositeFrequencyCap = (UINT32)Temp; (*State->PortList)[i + 1].CompositeFrequencyCap = (UINT32)Temp; ASSERT (Temp != 0); Freq = LibAmdBitScanReverse (Temp); (*State->PortList)[i].SelFrequency = Freq; (*State->PortList)[i + 1].SelFrequency = Freq; Temp = (*State->PortList)[i].PrvWidthOutCap; if ((*State->PortList)[i + 1].PrvWidthInCap < Temp) { Temp = (*State->PortList)[i + 1].PrvWidthInCap; } if (CbPcbABDownstreamWidth < Temp) { Temp = CbPcbABDownstreamWidth; } (*State->PortList)[i].SelWidthOut = (UINT8)Temp; (*State->PortList)[i + 1].SelWidthIn = (UINT8)Temp; Temp = (*State->PortList)[i].PrvWidthInCap; if ((*State->PortList)[i + 1].PrvWidthOutCap < Temp) { Temp = (*State->PortList)[i + 1].PrvWidthOutCap; } if (CbPcbBAUpstreamWidth < Temp) { Temp = CbPcbBAUpstreamWidth; } (*State->PortList)[i].SelWidthIn = (UINT8)Temp; (*State->PortList)[i + 1].SelWidthOut = (UINT8)Temp; } // Calculate unit id clumping // // Find the root of each IO Chain, process the chain for clumping support. // The root is always the first link of the chain in the port list. // Clumping is not device link specific, so we can just look at the upstream ports (j+1). Use ASSERTs to sanity // check the downstream ports (j). If any device on the chain does not support clumping, the entire chain will be // disabled for clumping. // After analyzing the clumping support on the chain the CPU's portlist has the enable mask. Update all the // IO Devices on the chain with the enable mask. If any device's only have passive support, that is already enabled. // if (State->IsUsingUnitIdClumping) { for (i = 0; i < (State->TotalLinks * 2); i += 2) { if (((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) && ((*State->PortList)[i + 1].Type == PORTLIST_TYPE_IO)) { (*State->PortList)[i].ClumpingSupport = HT_CLUMPING_DISABLE; if ((*State->PortList)[i + 1].ClumpingSupport != HT_CLUMPING_DISABLE) { (*State->PortList)[i].ClumpingSupport |= (*State->PortList)[i + 1].ClumpingSupport; for (j = i + 2; j < (State->TotalLinks * 2); j += 2) { if (((*State->PortList)[j].Type == PORTLIST_TYPE_IO) && ((*State->PortList)[j + 1].Type == PORTLIST_TYPE_IO)) { if (((*State->PortList)[i].NodeID == (*State->PortList)[j + 1].NodeID) && ((*State->PortList)[i].Link == (*State->PortList)[j + 1].HostLink)) { ASSERT (((*State->PortList)[i].NodeID == (*State->PortList)[j + 1].NodeID) && ((*State->PortList)[i].Link == (*State->PortList)[j].HostLink)); if ((*State->PortList)[j + 1].ClumpingSupport != HT_CLUMPING_DISABLE) { ASSERT ((((*State->PortList)[j + 1].ClumpingSupport & HT_CLUMPING_PASSIVE) == 0) || (((*State->PortList)[j + 1].ClumpingSupport & ~(HT_CLUMPING_PASSIVE)) == 0)); (*State->PortList)[i].ClumpingSupport |= (*State->PortList)[j + 1].ClumpingSupport; } else { (*State->PortList)[i].ClumpingSupport = HT_CLUMPING_DISABLE; break; } } } } if ((*State->PortList)[i + 1].ClumpingSupport != HT_CLUMPING_PASSIVE) { (*State->PortList)[i + 1].ClumpingSupport = (*State->PortList)[i].ClumpingSupport; } for (j = i + 2; j < (State->TotalLinks * 2); j += 2) { if (((*State->PortList)[j].Type == PORTLIST_TYPE_IO) && ((*State->PortList)[j + 1].Type == PORTLIST_TYPE_IO)) { if (((*State->PortList)[i].NodeID == (*State->PortList)[j + 1].NodeID) && ((*State->PortList)[i].Link == (*State->PortList)[j + 1].HostLink)) { if ((*State->PortList)[j + 1].ClumpingSupport != HT_CLUMPING_PASSIVE) { (*State->PortList)[j + 1].ClumpingSupport = (*State->PortList)[i].ClumpingSupport; // The downstream isn't really passive, just mark it so in order to write the device only once. (*State->PortList)[j].ClumpingSupport = HT_CLUMPING_PASSIVE; } } } } } } } } } /*----------------------------------------------------------------------------------------*/ /** * Change the hardware state for all Links according to the now optimized data in the * port list data structure. * * @HtFeatMethod{::F_SET_LINK_DATA} * * @param[in] State our global state, port list */ VOID SetLinkData ( IN STATE_DATA *State ) { UINT8 i; PCI_ADDR LinkBase; PCI_ADDR Reg; UINT32 Temp; UINT32 Widthin; UINT32 Widthout; UINT32 Bits; PCI_ADDR CurrentPtr; HTIDS_PORT_OVERRIDE_LIST PortOverrides; PortOverrides = NULL; for (i = 0; i < (State->TotalLinks * 2); i++) { ASSERT ((*State->PortList)[i & 0xFE].SelWidthOut == (*State->PortList)[ (i & 0xFE) + 1].SelWidthIn); ASSERT ((*State->PortList)[i & 0xFE].SelWidthIn == (*State->PortList)[ (i & 0xFE) + 1].SelWidthOut); ASSERT ((*State->PortList)[i & 0xFE].SelFrequency == (*State->PortList)[ (i & 0xFE) + 1].SelFrequency); if ((*State->PortList)[i].SelRegang) { ASSERT ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU); ASSERT ((*State->PortList)[i].Link < 4); State->Nb->SetLinkRegang ( (*State->PortList)[i].NodeID, (*State->PortList)[i].Link, State->Nb ); } // // IDS port override for CPUs and IO Devices // pf_HtIdsGetPortOverride ((BOOLEAN) ((i & 1) == 0), &(*State->PortList)[i], &(*State->PortList)[i + 1], &PortOverrides, State); LinkBase = (*State->PortList)[i].Pointer; if (((*State->PortList)[i].Type == PORTLIST_TYPE_IO) && ((*State->PortList)[i].Link == 1)) { LinkBase.Address.Register += HTSLAVE_LINK01_OFFSET; } // HT CRC Feature, set if configured. The default is not to set it, because with some chipsets it // will lock up if done here. if (State->IsSetHtCrcFlood) { Temp = 1; Reg = LinkBase; Reg.Address.Register += HTHOST_LINK_CONTROL_REG; State->HtFeatures->SetHtControlRegisterBits (Reg, 1, 1, &Temp, State); if ((*State->PortList)[i].Type == PORTLIST_TYPE_IO) { // IO Devices also need to have SERR enabled. Reg = LinkBase; Reg.Address.Register = PCI_CONFIG_COMMAND_REG04; LibAmdPciWriteBits (Reg, 8, 8, &Temp, State->ConfigHandle); } } // Some IO devices don't work properly when setting widths, so write them in a single operation, // rather than individually. // Widthout = ConvertWidthToBits ((*State->PortList)[i].SelWidthOut); ASSERT (Widthout == 1 || Widthout == 0 || Widthout == 5 || Widthout == 4); Widthin = ConvertWidthToBits ((*State->PortList)[i].SelWidthIn); ASSERT (Widthin == 1 || Widthin == 0 || Widthin == 5 || Widthin == 4); Temp = (Widthin & 7) | ((Widthout & 7) << 4); Reg = LinkBase; Reg.Address.Register += HTHOST_LINK_CONTROL_REG; State->HtFeatures->SetHtControlRegisterBits (Reg, 31, 24, &Temp, State); Temp = (*State->PortList)[i].SelFrequency; if ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) { State->Nb->SetLinkFrequency ( (*State->PortList)[i].NodeID, (*State->PortList)[i].Link, (UINT8)Temp, State->Nb ); } else { ASSERT (Temp <= HT_FREQUENCY_2600M); // Write the frequency setting Reg = LinkBase; Reg.Address.Register += HTSLAVE_FREQ_REV_0_REG; SetHtIoFrequencyRegisterBits (Reg, 11, 8, &Temp, State); // Handle additional HT3 frequency requirements, if needed, // or clear them if switching down to ht1 on a warm reset. // Gen1 = 200Mhz -> 1000MHz, Gen3 = 1200MHz -> 2600MHz // // Even though we assert if debugging, we need to check that the capability was // found always, since this is an unknown hardware device, also we are taking // unqualified frequency from the external interface (could be trying to do ht3 // on an ht1 IO device). // if (Temp > HT_FREQUENCY_1000M) { // Enabling features if gen 3 Bits = 1; } else { // Disabling features if gen 1 Bits = 0; } // Retry Enable if (DoesDeviceHaveHtSubtypeCap (LinkBase, HT_RETRY_CAPABILITY, &CurrentPtr, State)) { ASSERT ((*State->PortList)[i].Link < 2); CurrentPtr.Address.Register += HTRETRY_CONTROL_REG; LibAmdPciWriteBits (CurrentPtr, ((*State->PortList)[i].Link * 16), ((*State->PortList)[i].Link * 16), &Bits, State->ConfigHandle); } else { // If we are turning it off, that may mean the device was only ht1 capable, // so don't complain that we can't do it. // if (Bits != 0) { NotifyWarningOptRequiredCapRetry ((*State->PortList)[i].NodeID, (*State->PortList)[i].HostLink, (*State->PortList)[i].HostDepth, State); } } // Scrambling enable if (DoesDeviceHaveHtSubtypeCap (LinkBase, HT_GEN3_CAPABILITY, &CurrentPtr, State)) { ASSERT ((*State->PortList)[i].Link < 2); CurrentPtr.Address.Register = CurrentPtr.Address.Register + HTGEN3_LINK_TRAINING_0_REG + ((*State->PortList)[i].Link * HTGEN3_LINK01_OFFSET); LibAmdPciWriteBits (CurrentPtr, 3, 3, &Bits, State->ConfigHandle); } else { // If we are turning it off, that may mean the device was only ht1 capable, // so don't complain that we can't do it. // if (Bits != 0) { NotifyWarningOptRequiredCapGen3 ((*State->PortList)[i].NodeID, (*State->PortList)[i].HostLink, (*State->PortList)[i].HostDepth, State); } } } // Enable Unit ID Clumping if supported. if (State->IsUsingUnitIdClumping) { if (((*State->PortList)[i].ClumpingSupport != HT_CLUMPING_PASSIVE) && ((*State->PortList)[i].ClumpingSupport != HT_CLUMPING_DISABLE)) { Bits = (*State->PortList)[i].ClumpingSupport; if ((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) { State->Nb->SetLinkUnitIdClumping ( (*State->PortList)[i].NodeID, (*State->PortList)[i].Link, (*State->PortList)[i].ClumpingSupport, State->Nb ); } else { if (DoesDeviceHaveHtSubtypeCap (LinkBase, HT_UNITID_CAPABILITY, &Reg, State)) { Reg.Address.Register += HTUNIT_ENABLE_REG; LibAmdPciWriteBits (Reg, 31, 0, &Bits, State->ConfigHandle); } else { // If we found one when gathering support, we have to find one now. ASSERT (FALSE); } } } } } } /*------------------------------------------------------------------------------------------*/ /** * Find a specific HT capability type. * * Search all the PCI Config space capabilities on any type of device for an * HT capability of the specific subtype. * * @param[in] DevicePointer A PCI Config address somewhere in the device config space * @param[in] CapSubType The HT capability subtype to find * @param[out] CapabilityBase The Config space base address of the capability, if found. * @param[in] State Our State * * @retval TRUE the capability was found * @retval FALSE the capability was not found */ BOOLEAN DoesDeviceHaveHtSubtypeCap ( IN PCI_ADDR DevicePointer, IN UINT8 CapSubType, OUT PCI_ADDR *CapabilityBase, IN STATE_DATA *State ) { BOOLEAN IsFound; BOOLEAN IsDone; PCI_ADDR Reg; UINT32 Temp; UINT32 RegSubType; UINT32 RegSubTypeMask; // Set the PCI Config Space base and the match value. IsFound = FALSE; IsDone = FALSE; Reg = DevicePointer; Reg.Address.Register = 0; if (CapSubType < (HT_HOST_CAPABILITY + 1)) { // HT Interface sub type RegSubType = ((UINT32) (CapSubType << 29) | (UINT32)8); RegSubTypeMask = HT_INTERFACE_CAP_SUBTYPE_MASK; } else { // Other HT capability subtype RegSubType = ((UINT32) (CapSubType << 27) | (UINT32)8); RegSubTypeMask = HT_CAP_SUBTYPE_MASK; } (*CapabilityBase).AddressValue = (UINT32)ILLEGAL_SBDFO; // Find it do { LibAmdPciFindNextCap (&Reg, State->ConfigHandle); if (Reg.AddressValue != (UINT32)ILLEGAL_SBDFO) { LibAmdPciRead (AccessWidth32, Reg, &Temp, State->ConfigHandle); // HyperTransport and subtype capability ? if ((Temp & RegSubTypeMask) == RegSubType) { *CapabilityBase = Reg; IsFound = TRUE; } // Some other capability, keep looking } else { // Not there IsDone = TRUE; } } while (!IsFound && !IsDone); return IsFound; } /*----------------------------------------------------------------------------------------*/ /** * Retry must be enabled on all coherent links if it is enabled on any coherent links. * * @HtFeatMethod{::F_SET_LINK_DATA} * * Effectively, this means HT3 on some links cannot be mixed with HT1 on others. * Scan the CPU to CPU links for this condition and limit those frequencies to HT1 * if it is detected. * (Non-coherent links are independent.) * * @param[in,out] State global state, port frequency settings. * * @retval TRUE Fixup occurred, all coherent links HT1 * @retval FALSE No changes */ BOOLEAN IsCoherentRetryFixup ( IN STATE_DATA *State ) { UINT8 Freq; UINT8 i; UINT8 DetectedFrequencyState; BOOLEAN IsMixed; UINT32 Temp; // // detectedFrequencyState: // 0 - initial state // 1 - HT1 Frequencies detected // 2 - HT3 Frequencies detected // IsMixed = FALSE; DetectedFrequencyState = 0; // Scan coherent links for a mix of HT3 / HT1 for (i = 0; i < (State->TotalLinks * 2); i += 2) { if (((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) && ((*State->PortList)[i + 1].Type == PORTLIST_TYPE_CPU)) { // At this point, Frequency of port [i+1] must equal [i], so just check one of them. switch (DetectedFrequencyState) { case 0: // Set current state to indicate what link frequency we found first if ((*State->PortList)[i].SelFrequency > HT_FREQUENCY_1000M) { // HT3 frequencies DetectedFrequencyState = 2; } else { // HT1 frequencies DetectedFrequencyState = 1; } break; case 1: // If HT1 frequency detected, fail any HT3 frequency if ((*State->PortList)[i].SelFrequency > HT_FREQUENCY_1000M) { IsMixed = TRUE; } break; case 2: // If HT3 frequency detected, fail any HT1 frequency if ((*State->PortList)[i].SelFrequency <= HT_FREQUENCY_1000M) { IsMixed = TRUE; } break; default: ASSERT (FALSE); } if (IsMixed) { // Don't need to keep checking after we find a mix. break; } } } if (IsMixed) { for (i = 0; i < (State->TotalLinks * 2); i += 2) { if (((*State->PortList)[i].Type == PORTLIST_TYPE_CPU) && ((*State->PortList)[i + 1].Type == PORTLIST_TYPE_CPU)) { // Limit coherent links to HT 1 frequencies. Temp = (*State->PortList)[i].CompositeFrequencyCap & (*State->PortList)[i + 1].CompositeFrequencyCap; Temp &= HT_FREQUENCY_LIMIT_HT1_ONLY; ASSERT (Temp != 0); (*State->PortList)[i].CompositeFrequencyCap = Temp; (*State->PortList)[i + 1].CompositeFrequencyCap = Temp; Freq = LibAmdBitScanReverse (Temp); (*State->PortList)[i].SelFrequency = Freq; (*State->PortList)[i + 1].SelFrequency = Freq; } } } return (IsMixed); }