blob: 59fb1d94f4c06d1b4a02c678ce9cfecf441fad1f [file] [log] [blame]
/* $NoKeywords:$ */
/**
* @file
*
* AMD Family_12 specific utility functions.
*
* Provides numerous utility functions specific to family 12h.
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: AGESA
* @e sub-project: CPU/FAMILY/0x12
* @e \$Revision: 49553 $ @e \$Date: 2011-03-25 08:55:17 +0800 (Fri, 25 Mar 2011) $
*
*/
/*
*****************************************************************************
*
* 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 "cpuRegisters.h"
#include "cpuServices.h"
#include "cpuFamilyTranslation.h"
#include "cpuCommonF12Utilities.h"
#include "cpuF12PowerMgmt.h"
#include "OptionFamily12hEarlySample.h"
#include "NbSmuLib.h"
#include "GnbRegistersLN.h"
#include "F12PackageType.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G1_PEICC)
#define FILECODE PROC_CPU_FAMILY_0X12_CPUCOMMONF12UTILITIES_FILECODE
/*----------------------------------------------------------------------------------------
* D E F I N I T I O N S A N D M A C R O S
*----------------------------------------------------------------------------------------
*/
extern F12_ES_CORE_SUPPORT F12EarlySampleCoreSupport;
#define F12_DDR1333_ENCODED_MEMCLK (0xE)
/*----------------------------------------------------------------------------------------
* T Y P E D E F S A N D S T R U C T U R E S
*----------------------------------------------------------------------------------------
*/
CONST UINT16 ROMDATA F12MaxNbFreqAtMinVidFreqTable[] =
{
25, // 00000b
50, // 00001b
100, // 00010b
150, // 00011b
167, // 00100b
183, // 00101b
200, // 00110b
217, // 00111b
233, // 01000b
250, // 01001b
267, // 01010b
283, // 01011b
300, // 01100b
317, // 01101b
333, // 01110b
350, // 01111b
366, // 10000b
383, // 10001b
400, // 10010b
417, // 10011b
433, // 10100b
450, // 10101b
467, // 10110b
483, // 10111b
500, // 11000b
517, // 11001b
533, // 11010b
550, // 11011b
563, // 11100b
575, // 11101b
588, // 11110b
600 // 11111b
};
/*----------------------------------------------------------------------------------------
* 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
*----------------------------------------------------------------------------------------
*/
UINT32
STATIC
RoundedDivision (
IN UINT32 Dividend,
IN UINT32 Divisor
);
UINT32
F12GetApCoreNumber (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
);
CORE_ID_POSITION
F12CpuAmdCoreIdPositionInInitialApicId (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
);
/*----------------------------------------------------------------------------------------
* E X P O R T E D F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
/*---------------------------------------------------------------------------------------*/
/**
* Set warm reset status and count
*
* @CpuServiceMethod{::F_CPU_SET_WARM_RESET_FLAG}.
*
* This function will use bit9, and bit 10 of register F0x6C as a warm reset status and count.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[in] StdHeader Handle of Header for calling lib functions and services.
* @param[in] Request Indicate warm reset status
*
*/
VOID
F12SetAgesaWarmResetFlag (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader,
IN WARM_RESET_REQUEST *Request
)
{
PCI_ADDR PciAddress;
UINT32 PciData;
PciAddress.AddressValue = MAKE_SBDFO (0, 0 , PCI_DEV_BASE, FUNC_0, HT_INIT_CTRL);
LibAmdPciRead (AccessWidth32, PciAddress, &PciData, StdHeader);
// bit[5] - indicate a warm reset is or is not required
PciData &= ~(HT_INIT_BIOS_RST_DET_0);
PciData = PciData | (Request->RequestBit << 5);
// bit[10,9] - indicate warm reset status and count
PciData &= ~(HT_INIT_BIOS_RST_DET_1 | HT_INIT_BIOS_RST_DET_2);
PciData |= Request->StateBits << 9;
LibAmdPciWrite (AccessWidth32, PciAddress, &PciData, StdHeader);
}
/*---------------------------------------------------------------------------------------*/
/**
* Get warm reset status and count
*
* @CpuServiceMethod{::F_CPU_GET_WARM_RESET_FLAG}.
*
* This function will bit9, and bit 10 of register F0x6C as a warm reset status and count.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[in] StdHeader Config handle for library and services
* @param[out] Request Indicate warm reset status
*
*/
VOID
F12GetAgesaWarmResetFlag (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader,
OUT WARM_RESET_REQUEST *Request
)
{
PCI_ADDR PciAddress;
UINT32 PciData;
PciAddress.AddressValue = MAKE_SBDFO (0, 0 , PCI_DEV_BASE, FUNC_0, HT_INIT_CTRL);
LibAmdPciRead (AccessWidth32, PciAddress, &PciData, StdHeader);
// bit[5] - indicate a warm reset is or is not required
Request->RequestBit = (UINT8) ((PciData & HT_INIT_BIOS_RST_DET_0) >> 5);
// bit[10,9] - indicate warm reset status and count
Request->StateBits = (UINT8) ((PciData & (HT_INIT_BIOS_RST_DET_1 | HT_INIT_BIOS_RST_DET_2)) >> 9);
}
/*---------------------------------------------------------------------------------------*/
/**
* Use the Mailbox Register to get the Ap Mailbox info for the current core.
*
* @CpuServiceMethod{::F_CPU_AMD_GET_AP_MAILBOX_FROM_HARDWARE}.
*
* Access the mailbox register used with this NB family. This is valid until the
* point that some init code initializes the mailbox register for its normal use.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[out] ApMailboxInfo The AP Mailbox info
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
*/
VOID
F12GetApMailboxFromHardware (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
OUT AP_MAILBOXES *ApMailboxInfo,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
// For Family 12h, we will return socket 0, node 0, module 0, module type 0, and 0 for
// the system degree
ApMailboxInfo->ApMailInfo.Info = (UINT32) 0x00000000;
ApMailboxInfo->ApMailExtInfo.Info = (UINT32) 0x00000000;
}
/*---------------------------------------------------------------------------------------*/
/**
* Get this AP's system core number from hardware.
*
* @CpuServiceMethod{::F_CPU_GET_AP_CORE_NUMBER}.
*
* Returns the system core number. For family 12h, this is simply the
* initial APIC ID.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
* @return The AP's unique core number
*/
UINT32
F12GetApCoreNumber (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
CPUID_DATA Cpuid;
LibAmdCpuidRead (0x1, &Cpuid, StdHeader);
return ((Cpuid.EBX_Reg >> 24) & 0xFF);
}
/*---------------------------------------------------------------------------------------*/
/**
* Return a number zero or one, based on the Core ID position in the initial APIC Id.
*
* @CpuServiceMethod{::F_CORE_ID_POSITION_IN_INITIAL_APIC_ID}.
*
* @param[in] FamilySpecificServices The current Family Specific Services.
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
* @retval CoreIdPositionZero Core Id is not low
* @retval CoreIdPositionOne Core Id is low
*/
CORE_ID_POSITION
F12CpuAmdCoreIdPositionInInitialApicId (
IN CPU_SPECIFIC_SERVICES *FamilySpecificServices,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
return (CoreIdPositionOne);
}
/*---------------------------------------------------------------------------------------*/
/**
* Sets up a valid set of NB P-states based on the value of MEMCLK, transitions
* to the desired NB P-state, and returns the current NB frequency in megahertz.
*
* @param[in] TargetMemclk The target MEMCLK in megahertz, or zero to
* indicate NB P-state change only.
* @param[in] TargetMemclkEncoded The target MEMCLK's register encoding.
* @param[in] TargetNbPstate The NB P-state to exit in.
* @param[out] CurrentNbFreq Current NB operating frequency in megahertz.
* @param[in] StdHeader Handle of Header for calling lib functions and services.
*
* @retval TRUE Transition to TargetNbPstate was successful.
* @retval FALSE Transition to TargetNbPstate was unsuccessful.
*/
BOOLEAN
F12NbPstateInit (
IN UINT32 TargetMemclk,
IN UINT32 TargetMemclkEncoded,
IN UINT32 TargetNbPstate,
OUT UINT32 *CurrentNbFreq,
IN AMD_CONFIG_PARAMS *StdHeader
)
{
UINT32 EncodedNbPs1Vid;
UINT32 EncodedNbPs0NclkDiv;
UINT32 EncodedNbPs1NclkDiv;
UINT32 NbP0Cof;
UINT32 NbP1Cof;
UINT32 NbPstateNumerator;
UINT32 TargetNumerator;
UINT32 TargetDenominator;
UINT32 PkgType;
BOOLEAN ReturnStatus;
BOOLEAN WaitForTransition;
BOOLEAN EnableAltVddNb;
PCI_ADDR PciAddress;
D18F3xD4_STRUCT Cptc0;
D18F3xDC_STRUCT Cptc2;
D18F6x90_STRUCT NbPsCfgLow;
D18F6x98_STRUCT NbPsCtrlSts;
FCRxFE00_6000_STRUCT FCRxFE00_6000;
FCRxFE00_6002_STRUCT FCRxFE00_6002;
FCRxFE00_7006_STRUCT FCRxFE00_7006;
FCRxFE00_7009_STRUCT FCRxFE00_7009;
FCRxFE00_705F_STRUCT FCRxFE00_705F;
// F12 only supports NB P0 and NB P1
ASSERT (TargetNbPstate < 2);
WaitForTransition = FALSE;
ReturnStatus = TRUE;
EnableAltVddNb = FALSE;
// Get D18F3xD4[MainPllOpFreqId] frequency
PciAddress.AddressValue = CPTC0_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &Cptc0.Value, StdHeader);
// Calculate the numerator to be used for NB P-state calculations
NbPstateNumerator = (UINT32) (4 * ((Cptc0.Field.MainPllOpFreqId + 0x10) * 100));
if (TargetMemclk != 0) {
// Determine the appropriate numerator / denominator of the target memclk
switch (TargetMemclk) {
case DDR800_FREQUENCY:
TargetNumerator = 400;
TargetDenominator = 1;
break;
case DDR1066_FREQUENCY:
TargetNumerator = 1600;
TargetDenominator = 3;
break;
case DDR1333_FREQUENCY:
TargetNumerator = 2000;
TargetDenominator = 3;
break;
case DDR1600_FREQUENCY:
TargetNumerator = 800;
TargetDenominator = 1;
break;
case DDR1866_FREQUENCY:
TargetNumerator = 2800;
TargetDenominator = 3;
break;
default:
// An invalid memclk has been passed in.
ASSERT (FALSE);
TargetNumerator = TargetMemclk;
TargetDenominator = 1;
break;
}
FCRxFE00_6000.Value = NbSmuReadEfuse (FCRxFE00_6000_ADDRESS, StdHeader);
FCRxFE00_6002.Value = NbSmuReadEfuse (FCRxFE00_6002_ADDRESS, StdHeader);
FCRxFE00_7006.Value = NbSmuReadEfuse (FCRxFE00_7006_ADDRESS, StdHeader);
FCRxFE00_7009.Value = NbSmuReadEfuse (FCRxFE00_7009_ADDRESS, StdHeader);
F12EarlySampleCoreSupport.F12NbPstateInitHook (&FCRxFE00_6000,
&FCRxFE00_6002,
&FCRxFE00_7006,
&FCRxFE00_7009,
NbPstateNumerator,
StdHeader);
// Determine NB P0 settings
if ((TargetNumerator * FCRxFE00_7009.Field.NbPs0NclkDiv) < (NbPstateNumerator * TargetDenominator)) {
// Program D18F3xDC[NbPs0NclkDiv] to the minimum divisor where
// (target memclk frequency >= (D18F3xD4[MainPllOpFreqId] freq) / divisor)
EncodedNbPs0NclkDiv = ((NbPstateNumerator * TargetDenominator) / TargetNumerator);
if (((NbPstateNumerator * TargetDenominator) % TargetNumerator) != 0) {
EncodedNbPs0NclkDiv++;
}
// Ensure that the encoded divisor is even to give 50% duty cycle
EncodedNbPs0NclkDiv = ((EncodedNbPs0NclkDiv + 1) & 0xFFFFFFFE);
ASSERT (EncodedNbPs0NclkDiv >= 8);
ASSERT (EncodedNbPs0NclkDiv <= 0x3F);
} else {
EncodedNbPs0NclkDiv = FCRxFE00_7009.Field.NbPs0NclkDiv;
}
// Check to see if the DIMMs are too fast for the CPU (NB P0 COF < (Memclk / 2))
if ((TargetNumerator * EncodedNbPs0NclkDiv) > (NbPstateNumerator * TargetDenominator * 2)) {
// Indicate the error to the memory code so the DIMMs can be derated.
ReturnStatus = FALSE;
}
// Apply the appropriate P0 frequency
PciAddress.AddressValue = CPTC2_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &Cptc2.Value, StdHeader);
if (Cptc2.Field.NbPs0NclkDiv != EncodedNbPs0NclkDiv) {
WaitForTransition = TRUE;
Cptc2.Field.NbPs0NclkDiv = EncodedNbPs0NclkDiv;
LibAmdPciWrite (AccessWidth32, PciAddress, &Cptc2.Value, StdHeader);
}
NbP0Cof = RoundedDivision (NbPstateNumerator, EncodedNbPs0NclkDiv);
// Determine NB P1 settings if necessary
PciAddress.AddressValue = NB_PSTATE_CFG_LOW_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
if (NbPsCfgLow.Field.NbPsCap == 1) {
if ((TargetNumerator * FCRxFE00_7006.Field.NbPs1NclkDiv) > (NbPstateNumerator * TargetDenominator * 2)) {
// Program D18F6x90[NbPs1NclkDiv] to the maximum divisor where
// (target memclk frequency / 2 <= (D18F3xD4[MainPllOpFreqId] freq) / divisor)
EncodedNbPs1NclkDiv = ((NbPstateNumerator * TargetDenominator * 2) / TargetNumerator);
// Ensure that the encoded divisor is even to give 50% duty cycle
EncodedNbPs1NclkDiv &= 0xFFFFFFFE;
ASSERT (EncodedNbPs1NclkDiv >= 8);
ASSERT (EncodedNbPs1NclkDiv <= 0x3F);
// Calculate the new effective P1 frequency to determine the voltage
NbP1Cof = RoundedDivision (NbPstateNumerator, EncodedNbPs1NclkDiv);
if (NbP1Cof <= F12MaxNbFreqAtMinVidFreqTable[FCRxFE00_7006.Field.MaxNbFreqAtMinVid]) {
// Program D18F6x90[NbPs1Vid] = FCRxFE00_6002[NbPs1VidAddl]
EncodedNbPs1Vid = FCRxFE00_6002.Field.NbPs1VidAddl;
} else {
// Program D18F6x90[NbPs1Vid] = FCRxFE00_6002[NbPs1VidHigh]
EncodedNbPs1Vid = FCRxFE00_6002.Field.NbPs1VidHigh;
}
} else {
// Fused frequency and voltage are legal
EncodedNbPs1Vid = FCRxFE00_6000.Field.NbPs1Vid;
EncodedNbPs1NclkDiv = FCRxFE00_7006.Field.NbPs1NclkDiv;
NbP1Cof = RoundedDivision (NbPstateNumerator, EncodedNbPs1NclkDiv);
}
if (NbP0Cof < NbP1Cof) {
// NB P1 frequency is faster than NB P0. Fix it up by slowing
// P1 to match P0.
EncodedNbPs1NclkDiv = EncodedNbPs0NclkDiv;
NbP1Cof = NbP0Cof;
}
// Program the new NB P1 settings
NbPsCfgLow.Field.NbPs1NclkDiv = EncodedNbPs1NclkDiv;
NbPsCfgLow.Field.NbPs1Vid = EncodedNbPs1Vid;
LibAmdPciWrite (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
} else {
// NB P-states are not enabled
NbP1Cof = 0;
}
*CurrentNbFreq = NbP0Cof;
if (WaitForTransition) {
// Ensure that the frequency has settled before returning to memory code.
PciAddress.AddressValue = CPTC2_PCI_ADDR;
do {
LibAmdPciRead (AccessWidth32, PciAddress, &Cptc2.Value, StdHeader);
} while (Cptc2.Field.NclkFreqDone != 1);
}
} else {
// Get NB P0 COF
PciAddress.AddressValue = CPTC2_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &Cptc2.Value, StdHeader);
NbP0Cof = RoundedDivision (NbPstateNumerator, Cptc2.Field.NbPs0NclkDiv);
// Read NB P-state status
PciAddress.AddressValue = NB_PSTATE_CTRL_STS_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &NbPsCtrlSts.Value, StdHeader);
FCRxFE00_705F.Value = NbSmuReadEfuse (FCRxFE00_705F_ADDRESS, StdHeader);
if (FCRxFE00_705F.Field.GnbIdleAdjustVid != 0) {
PkgType = LibAmdGetPackageType (StdHeader);
if ((PkgType == PACKAGE_TYPE_FP1) || ((PkgType == PACKAGE_TYPE_FS1) && (TargetMemclkEncoded <= F12_DDR1333_ENCODED_MEMCLK))) {
EnableAltVddNb = TRUE;
}
}
// Read low config register
PciAddress.AddressValue = NB_PSTATE_CFG_LOW_PCI_ADDR;
LibAmdPciRead (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
if (TargetNbPstate == 1) {
// If target is P1, the CPU MUST be in P0, otherwise the P1 settings
// cannot be realized. This is a programming error.
ASSERT (NbPsCtrlSts.Field.NbPs1Act == 0);
if (NbPsCfgLow.Field.NbPsCap == 1) {
// The part is capable of NB P-states. Transition to P1.
if (EnableAltVddNb) {
NbPsCfgLow.Field.NbPs1Vid += FCRxFE00_705F.Field.GnbIdleAdjustVid;
LibAmdPciWrite (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
}
NbPsCfgLow.Field.NbPsForceSel = 1;
LibAmdPciWrite (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
WaitForTransition = TRUE;
*CurrentNbFreq = RoundedDivision (NbPstateNumerator, NbPsCfgLow.Field.NbPs1NclkDiv);
} else {
// No NB P-states. Return FALSE, and set current frequency to P0.
*CurrentNbFreq = NbP0Cof;
ReturnStatus = FALSE;
}
} else {
// Target P0
*CurrentNbFreq = NbP0Cof;
if (NbPsCtrlSts.Field.NbPs1Act != 0) {
// Request transition to P0
if (EnableAltVddNb) {
NbPsCfgLow.Field.NbPs1Vid -= FCRxFE00_705F.Field.GnbIdleAdjustVid;
}
NbPsCfgLow.Field.NbPsForceSel = 0;
LibAmdPciWrite (AccessWidth32, PciAddress, &NbPsCfgLow.Value, StdHeader);
WaitForTransition = TRUE;
}
}
if (WaitForTransition) {
// Ensure that the frequency has settled before returning to memory code.
PciAddress.AddressValue = NB_PSTATE_CTRL_STS_PCI_ADDR;
do {
LibAmdPciRead (AccessWidth32, PciAddress, &NbPsCtrlSts.Value, StdHeader);
} while (NbPsCtrlSts.Field.NbPs1Act != TargetNbPstate);
}
}
return ReturnStatus;
}
/*---------------------------------------------------------------------------------------*/
/**
* Performs integer division, and rounds the quotient up if the remainder is greater
* than or equal to 50% of the divisor.
*
* @param[in] Dividend The target MEMCLK in megahertz.
* @param[in] Divisor The target MEMCLK's register encoding.
*
* @return The rounded quotient
*/
UINT32
STATIC
RoundedDivision (
IN UINT32 Dividend,
IN UINT32 Divisor
)
{
UINT32 Quotient;
ASSERT (Divisor != 0);
Quotient = Dividend / Divisor;
if (((Dividend % Divisor) * 2) >= Divisor) {
Quotient++;
}
return Quotient;
}