blob: 4f767b8f279900b6ce496fa828f7e07fbdddc523 [file] [log] [blame]
/**
* @file
*
* AMD CPU Register Table Related Functions
*
* Set registers according to a set of register tables
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: AGESA
* @e sub-project: CPU
* @e \$Revision: 44323 $ @e \$Date: 2010-12-22 01:24:58 -0700 (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.
*
******************************************************************************
*/
/*----------------------------------------------------------------------------------------
* M O D U L E S U S E D
*----------------------------------------------------------------------------------------
*/
#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "Topology.h"
#include "OptionMultiSocket.h"
#include "cpuRegisters.h"
#include "cpuFamilyTranslation.h"
#include "Table.h"
#include "GeneralServices.h"
#include "cpuServices.h"
#include "cpuFeatures.h"
#include "CommonReturns.h"
#include "Filecode.h"
#define FILECODE PROC_CPU_TABLE_FILECODE
extern OPTION_MULTISOCKET_CONFIGURATION OptionMultiSocketConfiguration;
/*----------------------------------------------------------------------------------------
* D E F I N I T I O N S A N D M A C R O S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* T Y P E D E F S A N D S T R U C T U R E S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* 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
*----------------------------------------------------------------------------------------
*/
extern BUILD_OPT_CFG UserOptions;
VOID
SetRegistersFromTablesAtEarly (
IN CPU_SPECIFIC_SERVICES *FamilyServices,
IN AMD_CPU_EARLY_PARAMS *EarlyParams,
IN AMD_CONFIG_PARAMS *StdHeader
);
/*---------------------------------------------------------------------------------------*/
/**
* An iterator for all the Family and Model Register Tables.
*
* RegisterTableHandle should be set to NULL to begin iteration, the first time the method is
* invoked. Register tables can be processed, until this method returns NULL. RegisterTableHandle
* should simply be passed back to the method without modification or use by the caller.
* The table selector allows the relevant tables for different cores to be iterated, if the family separates
* tables. For example, MSRs can be in a table processed by all cores and PCI registers in a table processed by
* primary cores.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[in] Selector Select whether to iterate over tables for either all cores, primary cores, bsp, ....
* @param[in,out] RegisterTableHandle IN: The handle of the current register table, or NULL if Begin.
* OUT: The handle of the next register table, if not End.
* @param[out] NumberOfEntries The number of entries in the table returned, if not End.
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
* @return The pointer to the next Register Table, or NULL if End.
*/
TABLE_ENTRY_FIELDS
STATIC
*GetNextRegisterTable (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN TABLE_CORE_SELECTOR Selector,
IN OUT REGISTER_TABLE ***RegisterTableHandle,
OUT UINTN *NumberOfEntries,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
REGISTER_TABLE **NextTable;
TABLE_ENTRY_FIELDS *Entries;
ASSERT ((FamilySpecificServices != NULL) && (StdHeader != NULL));
ASSERT (Selector < (TABLE_CORE_SELECTOR)TableEntryTypeMax);
NextTable = *RegisterTableHandle;
if (NextTable == NULL) {
// Begin
NextTable = FamilySpecificServices->RegisterTableList;
} else {
NextTable++;
}
// skip if not selected
while ((*NextTable != NULL) && (*NextTable)->Selector != Selector) {
NextTable++;
}
if (*NextTable == NULL) {
// End
*RegisterTableHandle = NULL;
Entries = NULL;
} else {
// Iterate next table
*RegisterTableHandle = NextTable;
*NumberOfEntries = (*NextTable)->NumberOfEntries;
Entries = (TABLE_ENTRY_FIELDS *) (*NextTable)->Table;
}
return Entries;
}
/*---------------------------------------------------------------------------------------*/
/**
* Compare counts to a pair of ranges.
*
* @param[in] FirstCount The actual count to be compared to the first range.
* @param[in] SecondCount The actual count to be compared to the second range.
* @param[in] Ranges The ranges which the counts are compared to.
*
* @retval TRUE Either one, or both, of the counts is in the range given.
* @retval FALSE Neither count is in the range given.
*/
BOOLEAN
IsEitherCountInRange (
IN UINTN FirstCount,
IN UINTN SecondCount,
IN COUNT_RANGE_FEATURE Ranges
)
{
// Errors: Entire Range value is zero, Min and Max reversed or not <=, ranges overlap (OK if first range is all),
// the real counts are too big.
ASSERT ((Ranges.Range0Min <= Ranges.Range0Max) &&
(Ranges.Range1Min <= Ranges.Range1Max) &&
(Ranges.Range0Max != 0) &&
(Ranges.Range1Max != 0) &&
((Ranges.Range0Max == COUNT_RANGE_HIGH) || (Ranges.Range0Max < Ranges.Range1Min)) &&
((FirstCount < COUNT_RANGE_HIGH) && (SecondCount < COUNT_RANGE_HIGH)));
return (BOOLEAN) (((FirstCount <= Ranges.Range0Max) && (FirstCount >= Ranges.Range0Min)) ||
((SecondCount <= Ranges.Range1Max) && (SecondCount >= Ranges.Range1Min)));
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the performance profile features list of the currently running processor core.
*
* @param[out] Features The performance profile features supported by this platform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Header for library and services
*
*/
VOID
GetPerformanceFeatures (
OUT PERFORMANCE_PROFILE_FEATS *Features,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
CPUID_DATA CpuidDataStruct;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
Features->PerformanceProfileValue = 0;
// Reflect Probe Filter Configuration.
Features->PerformanceProfileFeatures.ProbeFilter = 0;
if (IsFeatureEnabled (HtAssist, PlatformConfig, StdHeader)) {
Features->PerformanceProfileFeatures.ProbeFilter = 1;
}
// Reflect Display Refresh Requests use 32 bytes Configuration.
Features->PerformanceProfileFeatures.RefreshRequest32Byte = 0;
if (PlatformConfig->PlatformProfile.Use32ByteRefresh) {
Features->PerformanceProfileFeatures.RefreshRequest32Byte = 1;
}
// Reflect Mct Isoc Read Priority set to variable Configuration.
Features->PerformanceProfileFeatures.MctIsocVariable = 0;
if (PlatformConfig->PlatformProfile.UseVariableMctIsocPriority) {
Features->PerformanceProfileFeatures.MctIsocVariable = 1;
}
// Indicate if this boot is a warm reset.
Features->PerformanceProfileFeatures.IsWarmReset = 0;
if (IsWarmReset (StdHeader)) {
Features->PerformanceProfileFeatures.IsWarmReset = 1;
}
// Get L3 Cache present as indicated by CPUID
Features->PerformanceProfileFeatures.L3Cache = 0;
Features->PerformanceProfileFeatures.NoL3Cache = 1;
LibAmdCpuidRead (AMD_CPUID_L2L3Cache_L2TLB, &CpuidDataStruct, StdHeader);
if (((CpuidDataStruct.EDX_Reg & 0xFFFC0000) >> 18) != 0) {
Features->PerformanceProfileFeatures.L3Cache = 1;
Features->PerformanceProfileFeatures.NoL3Cache = 0;
}
// Get VRM select high speed from build option.
Features->PerformanceProfileFeatures.VrmHighSpeed = 0;
if (PlatformConfig->VrmProperties.HiSpeedEnable) {
Features->PerformanceProfileFeatures.VrmHighSpeed = 1;
}
// Get some family, model specific performance type info.
GetCpuServicesOfCurrentCore (&FamilySpecificServices, StdHeader);
ASSERT (FamilySpecificServices != NULL);
// Is the Northbridge P-State feature enabled
Features->PerformanceProfileFeatures.NbPstates = 0;
if (FamilySpecificServices->IsNbPstateEnabled (FamilySpecificServices, StdHeader)) {
Features->PerformanceProfileFeatures.NbPstates = 1;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the MSR Register Entry.
*
* @TableEntryTypeMethod{::MsrRegister}.
*
* Read - Modify - Write the MSR, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The MSR register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForMsrEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT64 MsrData;
// Even for only single bit fields, use those in the mask. "Mask nothing" is a bug, even if just by policy.
ASSERT (Entry->MsrEntry.Mask != 0);
LibAmdMsrRead (Entry->MsrEntry.Address, &MsrData, StdHeader);
MsrData = MsrData & (~(Entry->MsrEntry.Mask));
MsrData = MsrData | Entry->MsrEntry.Data;
LibAmdMsrWrite (Entry->MsrEntry.Address, &MsrData, StdHeader);
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the PCI Register Entry.
*
* @TableEntryTypeMethod{::PciRegister}.
*
* Make the current core's PCI address with the function and register for the entry.
* Read - Modify - Write the PCI register, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForPciEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 TempVar32_a;
UINT32 MySocket;
UINT32 MyModule;
UINT32 Ignored;
PCI_ADDR MyPciAddress;
AGESA_STATUS IgnoredSts;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
// Even for only single bit fields, use those in the mask. "Mask nothing" is a bug, even if just by policy.
ASSERT ((Entry->InitialValues[4] == 0) &&
(Entry->InitialValues[3] == 0) &&
(Entry->PciEntry.Mask != 0));
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredSts);
GetPciAddress (StdHeader, MySocket, MyModule, &MyPciAddress, &IgnoredSts);
MyPciAddress.Address.Function = Entry->PciEntry.Address.Address.Function;
MyPciAddress.Address.Register = Entry->PciEntry.Address.Address.Register;
LibAmdPciRead (AccessWidth32, MyPciAddress, &TempVar32_a, StdHeader);
TempVar32_a = TempVar32_a & (~(Entry->PciEntry.Mask));
TempVar32_a = TempVar32_a | Entry->PciEntry.Data;
LibAmdPciWrite (AccessWidth32, MyPciAddress, &TempVar32_a, StdHeader);
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the Errata Workaround Register Entry.
*
* @TableEntryTypeMethod{::ErrataWorkaround}.
*
* Call the function, passing the data.
*
* See if you can use the other entries or make an entry that covers the fix.
* After all, the purpose of having a table entry is to @b NOT have code which
* isn't generic feature code, but is family/model code specific to one case.
*
* @param[in] Entry The Errata Workaround register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForErrataWorkaroundEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
ASSERT (Entry->ErrataEntry.DoAction != NULL);
Entry->ErrataEntry.DoAction (Entry->ErrataEntry.Data, StdHeader);
}
/*---------------------------------------------------------------------------------------*/
/**
* Finds the HT link capability set for a particular node/link.
*
* This function traverses the desired node's PCI capabilities searching
* for the HT capabilities associated with the desired HT link. If found,
* the PCI address of the capability block will be returned.
*
* @param[in] Link Zero based link number on Node
* @param[in,out] CapabilitySet PCI address of the link's capability block
* @param[in] StdHeader Config handle for library and services
*
* @retval TRUE Node/link capabilities found
* @retval FALSE Node/link capabilities not found
*
*/
BOOLEAN
FindHtHostCapability (
IN UINTN Link,
IN OUT PCI_ADDR *CapabilitySet,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 CapabilityReg;
PCI_ADDR PciAddress;
BOOLEAN Result;
Result = FALSE;
PciAddress = *CapabilitySet;
PciAddress.Address.Register = 0;
// Convert link from zero base to one base and adjust for sub link 0/1 split.
Link++;
PciAddress.Address.Function = ((Link > 4) ? 4 : 0);
Link = ((Link > 4) ? (Link - 4) : Link);
// Until either all capabilities are done or until the desired link is found,
// keep looking for HT Host Capabilities.
while (Link != 0) {
LibAmdPciFindNextCap (&PciAddress, StdHeader);
if (PciAddress.AddressValue != ILLEGAL_SBDFO) {
LibAmdPciRead (AccessWidth32, PciAddress, &CapabilityReg, StdHeader);
if ((CapabilityReg & 0xE00000FF) == 0x20000008) {
Link--;
}
// A capability other than an HT capability, keep looking.
} else {
// end of capabilities
break;
}
}
if (Link == 0) {
// If we found the link, update the caller's pointer and success.
*CapabilitySet = PciAddress;
Result = TRUE;
}
return Result ;
}
/*---------------------------------------------------------------------------------------*/
/**
* Program HT Phy PCI registers using BKDG values.
*
* @TableEntryTypeMethod{::HtPhyRegister}.
*
*
* @param[in] Entry The type specific entry data to be implemented (that is written).
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config params for library, services.
*
*/
VOID
SetRegisterForHtPhyEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 Link;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
PCI_ADDR CapabilitySet;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
BOOLEAN MatchedSublink1;
HT_FREQUENCIES Freq0;
HT_FREQUENCIES Freq1;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT ((Entry->InitialValues[4] == 0) &&
((Entry->HtPhyEntry.TypeFeats.HtPhyLinkValue & ~(HTPHY_LINKTYPE_ALL)) == 0) &&
(Entry->HtPhyEntry.Address < HTPHY_REGISTER_MAX));
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
Link = 0;
while (Link < 4) {
if (FindHtHostCapability (Link, &CapabilitySet, StdHeader)) {
if (FamilySpecificServices->DoesLinkHaveHtPhyFeats (
FamilySpecificServices,
CapabilitySet,
Link,
&Entry->HtPhyEntry.TypeFeats,
&MatchedSublink1,
&Freq0,
&Freq1,
StdHeader)) {
FamilySpecificServices->SetHtPhyRegister (FamilySpecificServices, &Entry->HtPhyEntry, CapabilitySet, Link, StdHeader);
}
} else {
// No more Capabilities, no more links present
break;
}
Link ++;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Program a range of HT Phy PCI registers using BKDG values.
*
* @TableEntryTypeMethod{::HtPhyRangeRegister}.
*
*
* @param[in] Entry The type specific entry data to be implemented (that is written).
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config params for library, services.
*
*/
VOID
SetRegisterForHtPhyRangeEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 Link;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
PCI_ADDR CapabilitySet;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
HT_PHY_TYPE_ENTRY_DATA CurrentHtPhyRegister;
BOOLEAN MatchedSublink1;
HT_FREQUENCIES Freq0;
HT_FREQUENCIES Freq1;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->HtPhyRangeEntry.TypeFeats.HtPhyLinkValue & ~(HTPHY_LINKTYPE_ALL)) == 0) &&
(Entry->HtPhyRangeEntry.LowAddress <= Entry->HtPhyRangeEntry.HighAddress) &&
(Entry->HtPhyRangeEntry.HighAddress < HTPHY_REGISTER_MAX) &&
(Entry->HtPhyRangeEntry.HighAddress != 0));
CurrentHtPhyRegister.Mask = Entry->HtPhyRangeEntry.Mask;
CurrentHtPhyRegister.Data = Entry->HtPhyRangeEntry.Data;
CurrentHtPhyRegister.TypeFeats = Entry->HtPhyRangeEntry.TypeFeats;
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
Link = 0;
while (Link < 4) {
if (FindHtHostCapability (Link, &CapabilitySet, StdHeader)) {
if (FamilySpecificServices->DoesLinkHaveHtPhyFeats (
FamilySpecificServices,
CapabilitySet,
Link,
&Entry->HtPhyRangeEntry.TypeFeats,
&MatchedSublink1,
&Freq0,
&Freq1,
StdHeader)) {
for (CurrentHtPhyRegister.Address = Entry->HtPhyRangeEntry.LowAddress;
CurrentHtPhyRegister.Address <= Entry->HtPhyRangeEntry.HighAddress;
CurrentHtPhyRegister.Address++) {
FamilySpecificServices->SetHtPhyRegister (FamilySpecificServices, &CurrentHtPhyRegister, CapabilitySet, Link, StdHeader);
}
}
} else {
// No more Capabilities, no more links present
break;
}
Link ++;
}
}
/*----------------------------------------------------------------------------------------*/
/**
* Is PackageLink an Internal Link?
*
* This is a test for the logical link match codes in the user interface, not a test for
* the actual northbridge links.
*
* @param[in] PackageLink The link
*
* @retval TRUE This is an internal link
* @retval FALSE This is not an internal link
*/
BOOLEAN
STATIC
IsDeemphasisLinkInternal (
IN UINT32 PackageLink
)
{
return (BOOLEAN) ((PackageLink <= HT_LIST_MATCH_INTERNAL_LINK_2) && (PackageLink >= HT_LIST_MATCH_INTERNAL_LINK_0));
}
/*----------------------------------------------------------------------------------------*/
/**
* Get the Package Link number, for the current node and real link number.
*
* Based on the link to package link mapping from BKDG, look up package link for
* the input link on the internal node number corresponding to the current core's node.
* For single module processors, the northbridge link and package link are the same.
*
* @param[in] Link the link on the current node.
* @param[in] FamilySpecificServices CPU specific support interface.
* @param[in] StdHeader Config params for library, services.
*
* @return the Package Link, HT_LIST_TERMINAL Not connected in package, HT_LIST_MATCH_INTERNAL_LINK package internal link.
*
*/
UINT32
STATIC
LookupPackageLink (
IN UINT32 Link,
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 PackageLinkMapItem;
UINT32 PackageLink;
AP_MAIL_INFO ApMailbox;
PackageLink = HT_LIST_TERMINAL;
GetApMailbox (&ApMailbox.Info, StdHeader);
if (ApMailbox.Fields.ModuleType != 0) {
ASSERT (FamilySpecificServices->PackageLinkMap != NULL);
// Use table to find this module's package link
PackageLinkMapItem = 0;
while ((*FamilySpecificServices->PackageLinkMap)[PackageLinkMapItem].Link != HT_LIST_TERMINAL) {
if (((*FamilySpecificServices->PackageLinkMap)[PackageLinkMapItem].Module == ApMailbox.Fields.Module) &&
((*FamilySpecificServices->PackageLinkMap)[PackageLinkMapItem].Link == Link)) {
PackageLink = (*FamilySpecificServices->PackageLinkMap)[PackageLinkMapItem].PackageLink;
break;
}
PackageLinkMapItem++;
}
} else {
PackageLink = Link;
}
return PackageLink;
}
/*---------------------------------------------------------------------------------------*/
/**
* Get the platform's specified deemphasis levels for the current link.
*
* Search the platform's list for a match to the current link and also matching frequency.
* If a match is found, use the specified deemphasis levels.
*
* @param[in] Socket The current Socket.
* @param[in] Link The link on that socket.
* @param[in] Frequency The frequency the link is set to.
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] FamilySpecificServices CPU specific support interface.
* @param[in] StdHeader Config params for library, services.
*
* @return The Deemphasis values for the link.
*/
UINT32
STATIC
GetLinkDeemphasis (
IN UINT32 Socket,
IN UINT32 Link,
IN HT_FREQUENCIES Frequency,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 Result;
CPU_HT_DEEMPHASIS_LEVEL *Match;
UINT32 PackageLink;
PackageLink = LookupPackageLink (Link, FamilySpecificServices, StdHeader);
// All External and Internal links have deemphasis level none as the default.
// However, it is expected that the platform BIOS will provide deemphasis levels for the external links.
Result = ((DCV_LEVEL_NONE) | (DEEMPHASIS_LEVEL_NONE));
if (PlatformConfig->PlatformDeemphasisList != NULL) {
Match = PlatformConfig->PlatformDeemphasisList;
while (Match->Socket != HT_LIST_TERMINAL) {
if (((Match->Socket == Socket) || (Match->Socket == HT_LIST_MATCH_ANY)) &&
((Match->Link == PackageLink) ||
((Match->Link == HT_LIST_MATCH_ANY) && (!IsDeemphasisLinkInternal (PackageLink))) ||
((Match->Link == HT_LIST_MATCH_INTERNAL_LINK) && (IsDeemphasisLinkInternal (PackageLink)))) &&
((Match->LoFreq <= Frequency) && (Match->HighFreq >= Frequency))) {
// Found a match, get the deemphasis value.
ASSERT ((MaxPlatformDeemphasisLevel > Match->DcvDeemphasis) | (MaxPlatformDeemphasisLevel > Match->ReceiverDeemphasis));
Result = ((1 << Match->DcvDeemphasis) | (1 << Match->ReceiverDeemphasis));
break;
} else {
Match++;
}
}
}
return Result;
}
/*---------------------------------------------------------------------------------------*/
/**
* Program Deemphasis registers using BKDG values, for the platform specified levels.
*
* @TableEntryTypeMethod{::DeemphasisRegister}.
*
*
* @param[in] Entry The type specific entry data to be implemented (that is written).
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config params for library, services.
*
*/
VOID
SetRegisterForDeemphasisEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 Link;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
PCI_ADDR CapabilitySet;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
BOOLEAN MatchedSublink1;
HT_FREQUENCIES Freq0;
HT_FREQUENCIES Freq1;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->DeemphasisEntry.Levels.DeemphasisValues & ~(VALID_DEEMPHASIS_LEVELS)) == 0) &&
((Entry->DeemphasisEntry.HtPhyEntry.TypeFeats.HtPhyLinkValue & ~(HTPHY_LINKTYPE_ALL)) == 0) &&
(Entry->DeemphasisEntry.HtPhyEntry.Address < HTPHY_REGISTER_MAX));
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
Link = 0;
while (Link < 4) {
if (FindHtHostCapability (Link, &CapabilitySet, StdHeader)) {
if (FamilySpecificServices->DoesLinkHaveHtPhyFeats (
FamilySpecificServices,
CapabilitySet,
Link,
&Entry->DeemphasisEntry.HtPhyEntry.TypeFeats,
&MatchedSublink1,
&Freq0,
&Freq1,
StdHeader)) {
if (DoesEntryTypeSpecificInfoMatch (
GetLinkDeemphasis (
MySocket,
(MatchedSublink1 ? (Link + 4) : Link),
(MatchedSublink1 ? Freq1 : Freq0),
PlatformConfig,
FamilySpecificServices,
StdHeader),
Entry->DeemphasisEntry.Levels.DeemphasisValues)) {
FamilySpecificServices->SetHtPhyRegister (
FamilySpecificServices,
&Entry->DeemphasisEntry.HtPhyEntry,
CapabilitySet,
Link,
StdHeader
);
}
}
} else {
// No more Capabilities, no more links present
break;
}
Link ++;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Program HT Phy PCI registers which have complex frequency dependencies.
*
* @TableEntryTypeMethod{::HtPhyFreqRegister}.
*
*
* @param[in] Entry The type specific entry data to be implemented (that is written).
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config params for library, services.
*
*/
VOID
SetRegisterForHtPhyFreqEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 Link;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
PCI_ADDR CapabilitySet;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
BOOLEAN MatchedSublink1;
HT_FREQUENCIES Freq0;
HT_FREQUENCIES Freq1;
BOOLEAN Temp;
UINT32 NbFreq;
// Errors: extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->HtPhyFreqEntry.HtPhyEntry.TypeFeats.HtPhyLinkValue & ~(HTPHY_LINKTYPE_ALL)) == 0) &&
(Entry->HtPhyFreqEntry.HtPhyEntry.Address < HTPHY_REGISTER_MAX));
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
Link = 0;
while (Link < 4) {
if (FindHtHostCapability (Link, &CapabilitySet, StdHeader)) {
if (FamilySpecificServices->DoesLinkHaveHtPhyFeats (
FamilySpecificServices,
CapabilitySet,
Link,
&Entry->HtPhyFreqEntry.HtPhyEntry.TypeFeats,
&MatchedSublink1,
&Freq0,
&Freq1,
StdHeader)) {
// Check the HT Frequency for match to the range.
if (IsEitherCountInRange (
(MatchedSublink1 ? Freq1 : Freq0),
(MatchedSublink1 ? Freq1 : Freq0),
Entry->HtPhyFreqEntry.HtFreqCounts.HtFreqCountRanges)) {
// Get the NB Frequency, convert to 100's of MHz, then convert to equivalent HT encoding. This supports
// NB frequencies from 800 MHz to 2600 MHz, which is currently greater than any processor supports.
OptionMultiSocketConfiguration.GetSystemNbCof (&NbFreq, &Temp, StdHeader);
NbFreq = (NbFreq / 100);
NbFreq = (NbFreq / 2) + 1;
if (IsEitherCountInRange (NbFreq, NbFreq, Entry->HtPhyFreqEntry.NbFreqCounts.HtFreqCountRanges)) {
FamilySpecificServices->SetHtPhyRegister (FamilySpecificServices, &Entry->HtPhyFreqEntry.HtPhyEntry, CapabilitySet, Link, StdHeader);
}
}
}
} else {
// No more Capabilities, no more links present
break;
}
Link ++;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the Performance Profile PCI Register Entry.
*
* @TableEntryTypeMethod{::ProfileFixup}.
*
* Check the entry's performance profile features to the platform's and do the
* PCI register entry if they match.
*
* @param[in] Entry The Performance Profile register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForPerformanceProfileEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PERFORMANCE_PROFILE_FEATS PlatformProfile;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->TokenPciEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0) &&
(Entry->InitialValues[4] == 0));
GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue,
Entry->FixupEntry.TypeFeats.PerformanceProfileValue)) {
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->FixupEntry.PciEntry;
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the HT Phy Performance Profile Register Entry.
*
* @TableEntryTypeMethod{::HtPhyProfileRegister}.
*
* @param[in] Entry The HT Phy register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForHtPhyProfileEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PERFORMANCE_PROFILE_FEATS PlatformProfile;
TABLE_ENTRY_DATA HtPhyEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->HtPhyProfileEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0) &&
(Entry->InitialValues[5] == 0));
GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue,
Entry->HtPhyProfileEntry.TypeFeats.PerformanceProfileValue)) {
LibAmdMemFill (&HtPhyEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
HtPhyEntry.HtPhyEntry = Entry->HtPhyProfileEntry.HtPhyEntry;
SetRegisterForHtPhyEntry (&HtPhyEntry, PlatformConfig, StdHeader);
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the HT Host PCI Register Entry.
*
* @TableEntryTypeMethod{::HtHostPciRegister}.
*
* Make the current core's PCI address with the function and register for the entry.
* For all HT links, check the link's feature set for a match to the entry.
* Read - Modify - Write the PCI register, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForHtHostEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINTN Link;
UINTN LinkCount;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
PCI_ADDR CapabilitySet;
HT_HOST_FEATS HtHostFeats;
UINT32 RegisterData;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT ((Entry->InitialValues[4] == 0) &&
((Entry->HtHostEntry.TypeFeats.HtHostValue & ~((HT_HOST_FEATURES_ALL) | (HT_HOST_AND))) == 0) &&
(Entry->HtHostEntry.Address.Address.Register < HT_LINK_HOST_CAP_MAX));
HtHostFeats.HtHostValue = 0;
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
LinkCount = 0;
while (LinkCount < 8) {
if (FindHtHostCapability (LinkCount, &CapabilitySet, StdHeader)) {
FamilySpecificServices->GetHtLinkFeatures (FamilySpecificServices, &Link, &CapabilitySet, &HtHostFeats, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (HtHostFeats.HtHostValue, Entry->HtHostEntry.TypeFeats.HtHostValue)) {
// Do the HT Host PCI register update.
CapabilitySet.Address.Register += Entry->HtHostEntry.Address.Address.Register;
LibAmdPciRead (AccessWidth32, CapabilitySet, &RegisterData, StdHeader);
RegisterData = RegisterData & (~(Entry->HtHostEntry.Mask));
RegisterData = RegisterData | Entry->HtHostEntry.Data;
LibAmdPciWrite (AccessWidth32, CapabilitySet, &RegisterData, StdHeader);
}
} else {
// No more Capabilities. Switch to sublink 1's if we are just done with sublink 0's.
// (This case handles less than 4 links are implemented in the processor.)
if (CapabilitySet.Address.Function == 0) {
CapabilitySet.Address.Function = 4;
LinkCount = 3;
} else {
break;
}
}
LinkCount ++;
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the Core Counts Performance PCI Register Entry.
*
* @TableEntryTypeMethod{::CoreCountsPciRegister}.
*
* Make the current core's PCI address with the function and register for the entry.
* Read - Modify - Write the PCI register, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForCoreCountsPerformanceEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PERFORMANCE_PROFILE_FEATS PlatformProfile;
UINTN ActualCoreCount;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->CoreCountEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));
GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->CoreCountEntry.TypeFeats.PerformanceProfileValue)) {
ActualCoreCount = GetActiveCoresInCurrentModule (StdHeader);
// Check if the actual core count is in either range.
if (IsEitherCountInRange (ActualCoreCount, ActualCoreCount, Entry->CoreCountEntry.CoreCounts.CoreRanges)) {
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->CoreCountEntry.PciEntry;
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the Processor Counts PCI Register Entry.
*
* @TableEntryTypeMethod{::ProcCountsPciRegister}.
*
* Make the current core's PCI address with the function and register for the entry.
* Read - Modify - Write the PCI register, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForProcessorCountsEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PERFORMANCE_PROFILE_FEATS PlatformProfile;
UINTN ProcessorCount;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->ProcCountEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));
GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->ProcCountEntry.TypeFeats.PerformanceProfileValue)) {
ProcessorCount = GetNumberOfProcessors (StdHeader);
// Check if the actual processor count is in either range.
if (IsEitherCountInRange (ProcessorCount, ProcessorCount, Entry->ProcCountEntry.ProcessorCounts.ProcessorCountRanges)) {
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->ProcCountEntry.PciEntry;
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the Processor Token Counts PCI Register Entry.
*
* @TableEntryTypeMethod{::TokenPciRegister}.
*
* The table criteria then translate as:
* - 2 Socket, half populated == Degree 1
* - 4 Socket, half populated == Degree 2
* - 2 Socket, fully populated == Degree 3
* - 4 Socket, fully populated == Degree > 3. (4 or 5 if 3P, 6 if 4P)
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForTokenPciEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PERFORMANCE_PROFILE_FEATS PlatformProfile;
UINTN SystemDegree;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT (((Entry->TokenPciEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));
GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->TokenPciEntry.TypeFeats.PerformanceProfileValue)) {
SystemDegree = GetSystemDegree (StdHeader);
// Check if the system degree is in the range.
if (IsEitherCountInRange (SystemDegree, SystemDegree, Entry->TokenPciEntry.ConnectivityCount.ConnectivityCountRanges)) {
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->TokenPciEntry.PciEntry;
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the HT Link Feature PCI Register Entry.
*
* @TableEntryTypeMethod{::HtFeatPciRegister}.
*
* Set a single field (that is, the register field is not in HT Host capability or a
* set of per link registers) in PCI config, based on HT link features and package type.
* This code is used for two cases: single link processors and multilink processors.
* For single link cases, the link will be tested for a match to the HT Features for the link.
* For multilink processors, the entry will match if @b any link is found which matches.
* For example, a setting can be applied based on coherent HT3 by matching coherent AND HT3.
*
* Make the core's PCI address. Check the package type (currently more important to the single link case),
* and if matching, iterate through all links checking for an HT feature match until found or exhausted.
* If a match was found, pass the PCI entry data to the implementer for writing for the current core.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForHtFeaturePciEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINTN Link;
UINTN LinkCount;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
PCI_ADDR CapabilitySet;
HT_HOST_FEATS HtHostFeats;
UINT32 ProcessorPackageType;
BOOLEAN IsMatch;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT ((Entry->HtFeatPciEntry.PciEntry.Mask != 0) &&
((Entry->HtFeatPciEntry.LinkFeats.HtHostValue & ~((HT_HOST_FEATURES_ALL) | (HT_HOST_AND))) == 0));
HtHostFeats.HtHostValue = 0;
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->HtFeatPciEntry.PciEntry;
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
ASSERT ((Entry->HtFeatPciEntry.PackageType.PackageTypeValue & ~(PACKAGE_TYPE_ALL)) == 0);
ProcessorPackageType = LibAmdGetPackageType (StdHeader);
if (DoesEntryTypeSpecificInfoMatch (ProcessorPackageType, Entry->HtFeatPciEntry.PackageType.PackageTypeValue)) {
IsMatch = FALSE;
for (LinkCount = 0; LinkCount < 8; LinkCount++) {
if (FindHtHostCapability (LinkCount, &CapabilitySet, StdHeader)) {
FamilySpecificServices->GetHtLinkFeatures (FamilySpecificServices, &Link, &CapabilitySet, &HtHostFeats, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (HtHostFeats.HtHostValue, Entry->HtFeatPciEntry.LinkFeats.HtHostValue)) {
IsMatch = TRUE;
break;
}
} else {
break;
}
}
if (IsMatch) {
// Do the PCI register update.
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Perform the HT Link PCI Register Entry.
*
* @TableEntryTypeMethod{::HtLinkPciRegister}.
*
* Make the current core's PCI address with the function and register for the entry.
* Registers are processed for match per link, assuming sequential PCI address per link.
* Read - Modify - Write each matching link's PCI register, clearing masked bits, and setting the data bits.
*
* @param[in] Entry The PCI register entry to perform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegisterForHtLinkPciEntry (
IN TABLE_ENTRY_DATA *Entry,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINTN Link;
UINTN LinkCount;
UINT32 MySocket;
UINT32 MyModule;
AGESA_STATUS IgnoredStatus;
UINT32 Ignored;
CPU_LOGICAL_ID CpuFamilyRevision;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
PCI_ADDR CapabilitySet;
HT_HOST_FEATS HtHostFeats;
TABLE_ENTRY_DATA PciEntry;
// Errors: Possible values in unused entry space, extra type features, value range checks.
// Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
ASSERT ((Entry->HtLinkPciEntry.PciEntry.Mask != 0) &&
((Entry->HtLinkPciEntry.LinkFeats.HtHostValue & ~((HT_HOST_FEATURES_ALL) | (HT_HOST_AND))) == 0));
HtHostFeats.HtHostValue = 0;
LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
PciEntry.PciEntry = Entry->HtLinkPciEntry.PciEntry;
IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredStatus);
GetPciAddress (StdHeader, MySocket, MyModule, &CapabilitySet, &IgnoredStatus);
GetLogicalIdOfCurrentCore (&CpuFamilyRevision, StdHeader);
GetCpuServicesFromLogicalId (&CpuFamilyRevision, &FamilySpecificServices, StdHeader);
LinkCount = 0;
while (LinkCount < 8) {
if (FindHtHostCapability (LinkCount, &CapabilitySet, StdHeader)) {
FamilySpecificServices->GetHtLinkFeatures (FamilySpecificServices, &Link, &CapabilitySet, &HtHostFeats, StdHeader);
if (DoesEntryTypeSpecificInfoMatch (HtHostFeats.HtHostValue, Entry->HtLinkPciEntry.LinkFeats.HtHostValue)) {
// Do the update to the link's non-Host PCI register, based on the entry address.
PciEntry.PciEntry.Address = Entry->HtLinkPciEntry.PciEntry.Address;
PciEntry.PciEntry.Address.Address.Register = PciEntry.PciEntry.Address.Address.Register + ((UINT32)Link * 4);
SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
}
} else {
// No more Capabilities. Switch to sublink 1's if we are just done with sublink 0's.
// (This case handles less than 4 links are implemented in the processor.)
if (CapabilitySet.Address.Function == 0) {
CapabilitySet.Address.Function = 4;
LinkCount = 3;
} else {
break;
}
}
LinkCount ++;
}
}
/* -----------------------------------------------------------------------------*/
/**
* Returns the platform features list of the currently running processor core.
*
* @param[out] Features The Features supported by this platform
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Header for library and services
*
*/
VOID
GetPlatformFeatures (
OUT PLATFORM_FEATS *Features,
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
PCI_ADDR PciAddress;
UINT32 CapabilityReg;
UINT32 Link;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
UINT32 CoreCount;
// Start with none.
Features->PlatformValue = 0;
switch (PlatformConfig->PlatformProfile.PlatformControlFlowMode) {
case Nfcm:
Features->PlatformFeatures.PlatformNfcm = 1;
break;
case UmaDr:
Features->PlatformFeatures.PlatformUma = 1;
break;
case UmaIfcm:
Features->PlatformFeatures.PlatformUmaIfcm = 1;
break;
case Ifcm:
Features->PlatformFeatures.PlatformIfcm = 1;
break;
case Iommu:
Features->PlatformFeatures.PlatformIommu = 1;
break;
default:
ASSERT (FALSE);
}
// Check - Single Link?
// This is based on the implemented links on the package regardless of their
// connection status. All processors must match the BSP, so we only check it and
// not the current node. We don't care exactly how many links there are, as soon
// as we find more than one we are done.
Link = 0;
PciAddress.AddressValue = MAKE_SBDFO (0, 0, PCI_DEV_BASE, FUNC_0, 0);
// Until either all capabilities are done or until the desired link is found,
// keep looking for HT Host Capabilities.
while (Link < 2) {
LibAmdPciFindNextCap (&PciAddress, StdHeader);
if (PciAddress.AddressValue != ILLEGAL_SBDFO) {
LibAmdPciRead (AccessWidth32, PciAddress, &CapabilityReg, StdHeader);
if ((CapabilityReg & 0xE00000FF) == 0x20000008) {
Link++;
}
// A capability other than an HT capability, keep looking.
} else {
// end of capabilities
break;
}
}
if (Link < 2) {
Features->PlatformFeatures.PlatformSingleLink = 1;
} else {
Features->PlatformFeatures.PlatformMultiLink = 1;
}
// Set the legacy core count bits.
GetActiveCoresInCurrentSocket (&CoreCount, StdHeader);
switch (CoreCount) {
case 1:
Features->PlatformFeatures.PlatformSingleCore = 1;
break;
case 2:
Features->PlatformFeatures.PlatformDualCore = 1;
break;
default:
Features->PlatformFeatures.PlatformMultiCore = 1;
}
//
// Get some specific platform type info, VC...etc.
//
GetCpuServicesOfCurrentCore (&FamilySpecificServices, StdHeader);
ASSERT (FamilySpecificServices != NULL);
FamilySpecificServices->GetPlatformTypeSpecificInfo (FamilySpecificServices, Features, StdHeader);
}
/*---------------------------------------------------------------------------------------*/
/**
* Checks if a register table entry applies to the executing core.
*
* This function uses a combination of logical ID and platform features to
* determine whether or not a register table entry applies to the executing core.
*
* @param[in] CoreCpuRevision The current core's logical ID
* @param[in] EntryCpuRevision The entry's desired logical IDs
* @param[in] PlatformFeatures The platform features
* @param[in] EntryFeatures The entry's desired platform features
*
* @retval TRUE This entry should be applied
* @retval FALSE This entry does not apply
*
*/
BOOLEAN
STATIC
DoesEntryMatchPlatform (
IN CPU_LOGICAL_ID CoreCpuRevision,
IN CPU_LOGICAL_ID EntryCpuRevision,
IN PLATFORM_FEATS PlatformFeatures,
IN PLATFORM_FEATS EntryFeatures
)
{
BOOLEAN Result;
Result = FALSE;
if (((CoreCpuRevision.Family & EntryCpuRevision.Family) != 0) &&
((CoreCpuRevision.Revision & EntryCpuRevision.Revision) != 0)) {
if (EntryFeatures.PlatformFeatures.AndPlatformFeats == 0) {
// Match if ANY entry feats match a platform feat (an OR test)
if ((EntryFeatures.PlatformValue & PlatformFeatures.PlatformValue) != 0) {
Result = TRUE;
}
} else {
// Match if ALL entry feats match a platform feat (an AND test)
if ((EntryFeatures.PlatformValue & ~(AMD_PF_AND)) ==
(EntryFeatures.PlatformValue & PlatformFeatures.PlatformValue)) {
Result = TRUE;
}
}
}
return Result;
}
/*---------------------------------------------------------------------------------------*/
/**
* Checks register table entry type specific criteria to the platform.
*
* Entry Data Type implementer methods can use this generically to check their own
* specific criteria. The method collects the actual platform characteristics and
* provides them along with the table entry's criteria to this service.
*
* There are a couple considerations for any implementer method using this service.
* The criteria value has to be representable as a UINT32. The MSB, Bit 31, has to
* be used as a AND test request if set in the entry. (The platform value should never
* have that bit set.)
*
* @param[in] PlatformTypeSpecificFeatures The platform features
* @param[in] EntryTypeFeatures The entry's desired platform features
*
* @retval TRUE This entry should be applied
* @retval FALSE This entry does not apply
*
*/
BOOLEAN
DoesEntryTypeSpecificInfoMatch (
IN UINT32 PlatformTypeSpecificFeatures,
IN UINT32 EntryTypeFeatures
)
{
BOOLEAN Result;
Result = FALSE;
if ((EntryTypeFeatures & BIT31) == 0) {
// Match if ANY entry feats match a platform feat (an OR test)
if ((EntryTypeFeatures & PlatformTypeSpecificFeatures) != 0) {
Result = TRUE;
}
} else {
// Match if ALL entry feats match a platform feat (an AND test)
if ((EntryTypeFeatures & ~(BIT31)) == (EntryTypeFeatures & PlatformTypeSpecificFeatures)) {
Result = TRUE;
}
}
return Result;
}
/*---------------------------------------------------------------------------------------*/
/**
* Determine this core's Selector matches.
*
* @param[in] Selector Is the current core this selector type?
* @param[in] StdHeader Config handle for library and services.
*
* @retval TRUE Yes, it is.
* @retval FALSE No, it is not.
*/
BOOLEAN
STATIC
IsCoreSelector (
IN TABLE_CORE_SELECTOR Selector,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
BOOLEAN Result;
AGESA_STATUS CalledStatus;
Result = TRUE;
ASSERT (Selector < TableCoreSelectorMax);
if ((Selector == PrimaryCores) && !IsCurrentCorePrimary (StdHeader)) {
Result = FALSE;
}
if ((Selector == BSCCORE) && (!IsBsp (StdHeader, &CalledStatus))) {
Result = FALSE;
}
return Result;
}
/*---------------------------------------------------------------------------------------*/
/**
* Set the registers for this core based on entries in a list of Register Tables.
*
* Determine the platform features and this core's logical id. Get the specific table
* entry type implementations for the logical model, which may be either generic (the ones
* in this file) or specific.
*
* Scan the tables starting the with ones for all cores and progressively narrowing the selection
* based on this core's role (ex. primary core). For a selected table, check for each entry
* matching the current core and platform, and call the implementer method to perform the
* register set operation if it matches.
*
* @param[in] PlatformConfig Config handle for platform specific information
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegistersFromTables (
IN PLATFORM_CONFIGURATION *PlatformConfig,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
CPU_LOGICAL_ID CpuLogicalId;
PLATFORM_FEATS PlatformFeatures;
CPU_SPECIFIC_SERVICES *FamilySpecificServices;
TABLE_ENTRY_FIELDS *Entries;
TABLE_CORE_SELECTOR Selector;
TABLE_ENTRY_TYPE EntryType;
REGISTER_TABLE **TableHandle;
UINTN NumberOfEntries;
UINTN CurrentEntryCount;
TABLE_ENTRY_TYPE_DESCRIPTOR *TypeImplementer;
PF_DO_TABLE_ENTRY DoTableEntry[TableEntryTypeMax];
// Did you really mean to increase the size of ALL table entries??!!
// While it is not necessarily a bug to increase the size of table entries:
// - Is this warning a surprise? Please fix it.
// - If expected, is this really a feature which is worth the increase? Then let other entries also use the space.
ASSERT (sizeof (TABLE_ENTRY_DATA) == (MAX_ENTRY_TYPE_ITEMS32 * sizeof (UINT32)));
PlatformFeatures.PlatformValue = 0;
GetLogicalIdOfCurrentCore (&CpuLogicalId, StdHeader);
GetPlatformFeatures (&PlatformFeatures, PlatformConfig, StdHeader);
GetCpuServicesFromLogicalId (&CpuLogicalId, &FamilySpecificServices, StdHeader);
// Build a non-sparse table of implementer methods, so we don't have to keep searching.
// It is a bug to not include a descriptor for a type that is in the table (but the
// descriptor can point to a non-assert stub).
// Also, it is not a bug to have no register table implementations, but it is a bug to have none and call this routine.
for (EntryType = MSRREGISTER; EntryType < TableEntryTypeMax; EntryType++) {
DoTableEntry[EntryType] = (PF_DO_TABLE_ENTRY)CommonAssert;
}
TypeImplementer = FamilySpecificServices->TableEntryTypeDescriptors;
ASSERT (TypeImplementer != NULL);
while (TypeImplementer->EntryType < TableEntryTypeMax) {
DoTableEntry[TypeImplementer->EntryType] = TypeImplementer->DoTableEntry;
TypeImplementer++;
}
for (Selector = AllCores; Selector < TableCoreSelectorMax; Selector++) {
if (IsCoreSelector (Selector, StdHeader)) {
// If the current core is the selected type of core, work the table list for tables for that type of core.
TableHandle = NULL;
Entries = GetNextRegisterTable (FamilySpecificServices, Selector, &TableHandle, &NumberOfEntries, StdHeader);
while (Entries != NULL) {
for (CurrentEntryCount = 0; CurrentEntryCount < NumberOfEntries; CurrentEntryCount++, Entries++) {
if (DoesEntryMatchPlatform (CpuLogicalId, Entries->CpuRevision, PlatformFeatures, Entries->Features)) {
// The entry matches this config, Do It!
// Find the implementer for this entry type and pass the entry data to it.
ASSERT (Entries->EntryType < TableEntryTypeMax);
DoTableEntry[Entries->EntryType] (&Entries->Entry, PlatformConfig, StdHeader);
}
}
Entries = GetNextRegisterTable (FamilySpecificServices, Selector, &TableHandle, &NumberOfEntries, StdHeader);
}
} else {
// Once a selector does not match the current core, quit looking.
break;
}
}
}
/*---------------------------------------------------------------------------------------*/
/**
* Set the registers for this core based on entries in a list of Register Tables.
*
* This function acts as a wrapper for calling the SetRegistersFromTables
* routine at AmdInitEarly.
*
* @param[in] FamilyServices The current Family Specific Services.
* @param[in] EarlyParams Service parameters.
* @param[in] StdHeader Config handle for library and services.
*
*/
VOID
SetRegistersFromTablesAtEarly (
IN CPU_SPECIFIC_SERVICES *FamilyServices,
IN AMD_CPU_EARLY_PARAMS *EarlyParams,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
AGESA_TESTPOINT (TpProcCpuProcessRegisterTables, StdHeader);
SetRegistersFromTables (&EarlyParams->PlatformConfig, StdHeader);
}