blob: f7801b43de99c2b0f72a2c9273a3f252707ea826 [file] [log] [blame]
/* $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;
}