/* $NoKeywords:$ */ /** * @file * * HyperTransport features and sequence implementation. * * Implements the external AmdHtInitialize entry point. * Contains routines for directing the sequence of available features. * Mostly, but not exclusively, AGESA_TESTPOINT invocations should be * contained in this file, and not in the feature code. * * From a build option perspective, it may be that a few lines could be removed * from compilation in this file for certain options. It is considered that * the code savings from this are too small to be of concern and this file * should not have any explicit build option implementation. * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: HyperTransport * @e \$Revision: 44324 $ @e \$Date: 2010-12-22 17:16:51 +0800 (Wed, 22 Dec 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 "AdvancedApi.h" #include "amdlib.h" #include "Ids.h" #include "Topology.h" #include "htFeat.h" #include "htInterface.h" #include "htNb.h" #include "heapManager.h" #include "cpuRegisters.h" #include "cpuServices.h" #include "OptionsHt.h" #include "Filecode.h" CODE_GROUP (G1_PEICC) RDATA_GROUP (G1_PEICC) #define FILECODE PROC_HT_HTMAIN_FILECODE #define APIC_Base_BSP 8 #define APIC_Base 0x1b extern OPTION_HT_CONFIGURATION OptionHtConfiguration; BOOLEAN STATIC IsBootCore ( IN STATE_DATA *State ); /*----------------------------------------------------------------------------------------*/ /** * Update maps with the core range for each module. * * Cores are numbered relative to a Processor, but sometimes there is a need to know the * starting and ending core ids on a particular node. This same info is also useful for * supporting the Core count on a node other than the one currently executing. * * For each Processor, get the core count of each node using the family specific PCI core count * interface. The order of cores in a processor, and whether it is special for the BSP is family * specific. But whether the processor orders core ids by module or node, iterate in the right * order and use the counts to determine each start and end range. * * Update compute unit status for each node. * * @param[in] State number of Nodes discovered. */ VOID STATIC UpdateCoreRanges ( IN STATE_DATA *State ) { UINT8 Node; UINT8 ProcessorCores; UINT8 ModuleCoreCount[MAX_DIES]; UINT8 Socket; UINT8 Module; ASSERT (State->SocketDieToNodeMap != NULL); ASSERT (State->NodeToSocketDieMap != NULL); for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { // Is a Processor present in Socket? if ((*State->SocketDieToNodeMap)[Socket][0].Node != HT_LIST_TERMINAL) { // Get all the Module core counts for this processor // Note that the core counts are 1 based counts. // Since Compute Unit info is not module ordering dependent, write it now. for (Module = 0; Module < MAX_DIES; Module++) { if ((*State->SocketDieToNodeMap)[Socket][Module].Node != HT_LIST_TERMINAL) { ModuleCoreCount[Module] = State->Nb->GetNumCoresOnNode ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb); (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits = State->Nb->GetEnabledComputeUnits ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb); (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits = State->Nb->GetDualCoreComputeUnits ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb); } else { ModuleCoreCount[Module] = 0; } } // Determine the core ordering rule for this processor. if ((((*State->NodeToSocketDieMap)[0].Socket == Socket) && State->Nb->IsOrderBSPCoresByNode) || (!State->Nb->IsOrderCoresByModule)) { // Order core ranges on this processor by Node Id. ProcessorCores = 0; for (Node = 0; Node < State->Nb->GetNodeCount (State->Nb); Node++) { // Is this node a module in this processor? if ((*State->NodeToSocketDieMap)[Node].Socket == Socket) { Module = (*State->NodeToSocketDieMap)[Node].Die; if (ModuleCoreCount[Module] != 0) { (*State->SocketDieToNodeMap)[Socket][Module].LowCore = ProcessorCores; (*State->SocketDieToNodeMap)[Socket][Module].HighCore = ProcessorCores + (ModuleCoreCount[Module] - 1); IDS_HDT_CONSOLE ( HT_TRACE, (IsBootCore (State) ? "Topology: Socket %d, Die %d, is Node %d, with Cores %d thru %d. Compute Unit status (0x%x,0x%x).\n" : ""), Socket, Module, Node, (*State->SocketDieToNodeMap)[Socket][Module].LowCore, (*State->SocketDieToNodeMap)[Socket][Module].HighCore, (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits, (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits ); ProcessorCores = ProcessorCores + ModuleCoreCount[Module]; } } } } else { // Order core ranges in this processor by Module Id. ProcessorCores = 0; for (Module = 0; Module < MAX_DIES; Module++) { if (ModuleCoreCount[Module] != 0) { (*State->SocketDieToNodeMap)[Socket][Module].LowCore = ProcessorCores; (*State->SocketDieToNodeMap)[Socket][Module].HighCore = ProcessorCores + (ModuleCoreCount[Module] - 1); IDS_HDT_CONSOLE ( HT_TRACE, (IsBootCore (State) ? "Topology: Socket %d, Die %d, is Node %d, with Cores %d thru %d. Compute Unit status (0x%x,0x%x).\n" : ""), Socket, Module, (*State->SocketDieToNodeMap)[Socket][Module].Node, (*State->SocketDieToNodeMap)[Socket][Module].LowCore, (*State->SocketDieToNodeMap)[Socket][Module].HighCore, (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits, (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits ); ProcessorCores = ProcessorCores + ModuleCoreCount[Module]; } } } } } } /*----------------------------------------------------------------------------------------*/ /** * Complete the coherent init with any system level initialization. * * Find the total number of cores and update the number of Nodes and cores in all cpus. * Limit cpu config access to installed cpus. * * @param[in] State number of Nodes discovered. */ VOID STATIC FinalizeCoherentInit ( IN STATE_DATA *State ) { UINT8 Node; UINT8 TotalCores; TotalCores = 0; for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { TotalCores = TotalCores + State->Nb->GetNumCoresOnNode (Node, State->Nb); } for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { State->Nb->SetTotalNodesAndCores (Node, State->NodesDiscovered + 1, TotalCores, State->Nb); } // Set all nodes to limit config space based on node count, after all nodes have a valid count. // (just being cautious, probably we could combine the loops.) for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { State->Nb->LimitNodes (Node, State->Nb); } } /*----------------------------------------------------------------------------------------*/ /** * Initialize the coherent fabric. * * Perform discovery and initialization of the coherent fabric, for builds including * support for multiple coherent nodes. * * @param[in] State global state */ VOID STATIC CoherentInit ( IN OUT STATE_DATA *State ) { UINT8 i; UINT8 j; UINT8 ModuleType; UINT8 Module; UINT8 HardwareSocket; COHERENT_FABRIC Fabric; // Because Node 0, the BSP, is not discovered, initialize info about it specially here. // Allocate Socket Die Map. // While the BSP is always capable of being the only processor in the system, call the // IsExceededCapable method to make sure the BSP's capability is included in the aggregate system // capability. We don't care to check the return value. // State->Fabric = &Fabric; State->NodesDiscovered = 0; State->TotalLinks = 0; State->SysMpCap = MAX_NODES; State->Nb->IsExceededCapable (0, State, State->Nb); HardwareSocket = State->Nb->GetSocket (0, 0, State->Nb); ModuleType = 0; Module = 0; State->Nb->GetModuleInfo (0, &ModuleType, &Module, State->Nb); // No predecessor info for BSP, so pass 0xFF for those parameters. State->HtInterface->SetNodeToSocketMap (0xFF, 0xFF, 0xFF, 0, HardwareSocket, Module, State); // Initialize system state data structures for (i = 0; i < MAX_NODES; i++) { State->Fabric->SysDegree[i] = 0; for (j = 0; j < MAX_NODES; j++) { State->Fabric->SysMatrix[i][j] = 0; } } // // Call the coherent init features // // Discovery State->HtFeatures->CoherentDiscovery (State); State->HtInterface->PostMapToAp (State); // Topology matching and Routing AGESA_TESTPOINT (TpProcHtTopology, State->ConfigHandle); State->HtFeatures->LookupComputeAndLoadRoutingTables (State); State->HtFeatures->MakeHopCountTable (State); // UpdateCoreRanges requires the other maps to be initialized, and the node count set. FinalizeCoherentInit (State); UpdateCoreRanges (State); State->Fabric = NULL; } /*************************************************************************** *** Non-coherent init code *** *** Algorithms *** ***************************************************************************/ /*----------------------------------------------------------------------------------------*/ /** * Initialize the non-coherent fabric. * * Begin with the Compat Link on the BSP, then find and initialize all other * non-coherent chains. * * @param[in] State our global state */ VOID STATIC NcInit ( IN STATE_DATA *State ) { UINT8 Node; UINT8 Link; UINT8 CompatLink; FINAL_LINK_STATE FinalLinkState; // Initialize the southbridge chain. State->AutoBusCurrent = State->HtBlock->AutoBusStart; State->UsedCfgMapEntries = 0; CompatLink = State->Nb->ReadSouthbridgeLink (State->Nb); State->HtFeatures->ProcessLink (0, CompatLink, TRUE, State); // Find and initialize all other non-coherent chains. for (Node = 0; Node <= State->NodesDiscovered; Node++) { for (Link = 0; Link < State->Nb->MaxLinks; Link++) { // Skip the Link, if any of these tests indicate FinalLinkState = State->HtInterface->GetIgnoreLink (Node, Link, State->Nb->DefaultIgnoreLinkList, State); if (FinalLinkState == UNMATCHED) { if ( !((Node == 0) && (Link == CompatLink))) { if ( !(State->Nb->ReadTrueLinkFailStatus (Node, Link, State, State->Nb))) { if (State->Nb->VerifyLinkIsNonCoherent (Node, Link, State->Nb)) { State->HtFeatures->ProcessLink (Node, Link, FALSE, State); } } } } } } } /*************************************************************************** *** Link Optimization *** ***************************************************************************/ /*----------------------------------------------------------------------------------------*/ /** * Optimize Link Features. * * Based on Link capabilities, apply optimization rules to come up with the best * settings, including several external limit decision from the interface. This includes * handling of subLinks. Finally, after the port list data is updated, set the hardware * state for all Links. * * @param[in] State our global state */ VOID STATIC LinkOptimization ( IN STATE_DATA *State ) { AGESA_TESTPOINT (TpProcHtOptGather, State->ConfigHandle); State->HtFeatures->GatherLinkData (State); AGESA_TESTPOINT (TpProcHtOptRegang, State->ConfigHandle); State->HtFeatures->RegangLinks (State); AGESA_TESTPOINT (TpProcHtOptLinks, State->ConfigHandle); State->HtFeatures->SelectOptimalWidthAndFrequency (State); // A likely cause of mixed Retry settings on coherent links is sublink ratio balancing // so check this after doing the sublinks. AGESA_TESTPOINT (TpProcHtOptSubLinks, State->ConfigHandle); State->HtFeatures->SubLinkRatioFixup (State); if (State->HtFeatures->IsCoherentRetryFixup (State)) { // Fix sublinks again within HT1 only frequencies, as ratios may be invalid again. State->HtFeatures->SubLinkRatioFixup (State); } AGESA_TESTPOINT (TpProcHtOptFinish, State->ConfigHandle); State->HtFeatures->SetLinkData (State); } /*----------------------------------------------------------------------------------------*/ /** * Handle system and performance tunings. * * Including traffic distribution, fifo and * buffer tuning that can't be placed in the register table, * and special config tunings. * * @param[in] State Total Nodes, port list data */ VOID STATIC Tuning ( IN STATE_DATA *State ) { UINT8 Node; // See if traffic distribution can be done and do it if so. // AGESA_TESTPOINT (TpProcHtTrafficDist, State->ConfigHandle); State->HtFeatures->TrafficDistribution (State); // For each Node, invoke northbridge specific buffer tunings that can not be done in reg table. // AGESA_TESTPOINT (TpProcHtTuning, State->ConfigHandle); for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { State->Nb->BufferOptimizations (Node, State, State->Nb); } } /*----------------------------------------------------------------------------------------*/ /** * Initialize the Node and Socket maps for an AP Core. * * In each core's local heap, create a Node to Socket map and a Socket/Module to Node map. * The mapping is filled in by reading the AP Mailboxes from PCI config on each node. * * @param[in] State global state, input data * */ VOID STATIC InitApMaps ( IN STATE_DATA *State ) { UINT8 Node; AP_MAIL_INFO NodeApMailBox; // There is no option to not have socket - node maps, if they aren't allocated that is a fatal bug. ASSERT (State->SocketDieToNodeMap != NULL); ASSERT (State->NodeToSocketDieMap != NULL); for (Node = 0; Node < State->Nb->GetNodeCount (State->Nb); Node++) { NodeApMailBox = State->Nb->RetrieveMailbox (Node, State->Nb); (*State->SocketDieToNodeMap)[NodeApMailBox.Fields.Socket][NodeApMailBox.Fields.Module].Node = Node; (*State->NodeToSocketDieMap)[Node].Socket = (UINT8)NodeApMailBox.Fields.Socket; (*State->NodeToSocketDieMap)[Node].Die = (UINT8)NodeApMailBox.Fields.Module; } // This requires the other maps to be initialized. UpdateCoreRanges (State); } /*----------------------------------------------------------------------------------------*/ /** * Is the currently running core the BSC? * * Determine whether the init steps for BSC or AP core should be run. * * @param[in] State global state, input data * * @retval TRUE This is the boot core. * @retval FALSE This is not the boot core. */ BOOLEAN STATIC IsBootCore ( IN STATE_DATA *State ) { UINT64 Value; LibAmdMsrRead (APIC_Base, &Value, State->ConfigHandle); return ((BOOLEAN) (((UINT32) (Value & 0xFFFFFFFF) & ((UINT32)1 << APIC_Base_BSP)) != 0)); } /*************************************************************************** *** HT Initialize *** ***************************************************************************/ /*----------------------------------------------------------------------------------------*/ /** * The top level external interface for Hypertransport Initialization. * * Create our initial internal state, initialize the coherent fabric, * initialize the non-coherent chains, and perform any required fabric tuning or * optimization. * * @param[in] StdHeader Opaque handle to standard config header * @param[in] PlatformConfiguration The platform configuration options. * @param[in] AmdHtInterface HT Interface structure. * * @retval AGESA_SUCCESS Only information events logged. * @retval AGESA_ALERT Sync Flood or CRC error logged. * @retval AGESA_WARNING Example: expected capability not found * @retval AGESA_ERROR logged events indicating some devices may not be available * @retval AGESA_FATAL Mixed Family or MP capability mismatch * */ AGESA_STATUS AmdHtInitialize ( IN AMD_CONFIG_PARAMS *StdHeader, IN PLATFORM_CONFIGURATION *PlatformConfiguration, IN AMD_HT_INTERFACE *AmdHtInterface ) { STATE_DATA State; NORTHBRIDGE Nb; HT_FEATURES HtFeatures; HT_INTERFACE HtInterface; AGESA_STATUS DeallocateStatus; AP_MAIL_INFO ApMailboxInfo; UINT8 ApNode; ALLOCATE_HEAP_PARAMS AllocHeapParams; State.HtBlock = AmdHtInterface; State.ConfigHandle = StdHeader; State.PlatformConfiguration = PlatformConfiguration; // Get the current HT internal interface (to HtBlock data) NewHtInterface (&HtInterface, State.ConfigHandle); State.HtInterface = &HtInterface; // Get the current HT Feature Set NewHtFeatures (&HtFeatures, State.ConfigHandle); State.HtFeatures = &HtFeatures; // Initialize from static options State.IsUsingRecoveryHt = OptionHtConfiguration.IsUsingRecoveryHt; State.IsSetHtCrcFlood = OptionHtConfiguration.IsSetHtCrcFlood; State.IsUsingUnitIdClumping = OptionHtConfiguration.IsUsingUnitIdClumping; // Initialize for status and event output State.MaxEventClass = AGESA_SUCCESS; // Allocate permanent heap structs that are interfaces to other AGESA services. State.HtInterface->NewNodeAndSocketTables (&State); if (IsBootCore (&State)) { AGESA_TESTPOINT (TpProcHtEntry, State.ConfigHandle); // Allocate Bsp only interface heap structs. State.HtInterface->NewHopCountTable (&State); // Allocate heap for our temporary working space. AllocHeapParams.RequestedBufferSize = (sizeof (PORT_DESCRIPTOR) * (MAX_PLATFORM_LINKS * 2)); AllocHeapParams.BufferHandle = HT_STATE_DATA_HANDLE; AllocHeapParams.Persist = HEAP_LOCAL_CACHE; if (HeapAllocateBuffer (&AllocHeapParams, State.ConfigHandle) == AGESA_SUCCESS) { State.PortList = (PORT_LIST)AllocHeapParams.BufferPtr; // Create the BSP's northbridge. NewNorthBridge (0, &State, &Nb); State.Nb = &Nb; CoherentInit (&State); NcInit (&State); LinkOptimization (&State); Tuning (&State); DeallocateStatus = HeapDeallocateBuffer (HT_STATE_DATA_HANDLE, State.ConfigHandle); ASSERT (DeallocateStatus == AGESA_SUCCESS); AGESA_TESTPOINT (TpProcHtDone, State.ConfigHandle); } else { ASSERT (FALSE); State.MaxEventClass = AGESA_ERROR; // Cannot Log entry due to heap allocate failed. } } else { // Do the AP HT Init, which produces Node and Socket Maps for the AP's use. AGESA_TESTPOINT (TpProcHtApMapEntry, State.ConfigHandle); GetApMailbox (&ApMailboxInfo.Info, State.ConfigHandle); ASSERT (ApMailboxInfo.Fields.Node < MAX_NODES); ApNode = (UINT8)ApMailboxInfo.Fields.Node; NewNorthBridge (ApNode, &State, &Nb); State.Nb = &Nb; InitApMaps (&State); AGESA_TESTPOINT (TpProcHtApMapDone, State.ConfigHandle); } return State.MaxEventClass; }