/* $NoKeywords:$ */ /** * @file * * AMD Family_14 specific utility functions. * * Provides numerous utility functions specific to family 10h. * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: CPU/F14 * @e \$Revision: 39275 $ @e \$Date: 2010-10-09 08:22:05 +0800 (Sat, 09 Oct 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 "cpuRegisters.h" #include "cpuFamilyTranslation.h" #include "cpuCommonF14Utilities.h" #include "cpuF14PowerMgmt.h" #include "OptionFamily14hEarlySample.h" #include "NbSmuLib.h" #include "GnbRegistersON.h" #include "Filecode.h" #define FILECODE PROC_CPU_FAMILY_0X14_CPUCOMMONF14UTILITIES_FILECODE /*---------------------------------------------------------------------------------------- * D E F I N I T I O N S A N D M A C R O S *---------------------------------------------------------------------------------------- */ extern F14_ES_CORE_SUPPORT F14EarlySampleCoreSupport; /*---------------------------------------------------------------------------------------- * T Y P E D E F S A N D S T R U C T U R E S *---------------------------------------------------------------------------------------- */ CONST UINT16 ROMDATA F14MaxNbFreqAtMinVidFreqTable[] = { 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 F14GetApCoreNumber ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN AMD_CONFIG_PARAMS *StdHeader ); CORE_ID_POSITION F14CpuAmdCoreIdPositionInInitialApicId ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, IN AMD_CONFIG_PARAMS *StdHeader ); UINT32 STATIC RoundedDivision ( IN UINT32 Dividend, IN UINT32 Divisor ); /*---------------------------------------------------------------------------------------- * 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 F14SetAgesaWarmResetFlag ( 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 F14GetAgesaWarmResetFlag ( 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 F14GetApMailboxFromHardware ( IN CPU_SPECIFIC_SERVICES *FamilySpecificServices, OUT AP_MAILBOXES *ApMailboxInfo, IN AMD_CONFIG_PARAMS *StdHeader ) { // For Family 14h, 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 14h, 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 F14GetApCoreNumber ( 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 F14CpuAmdCoreIdPositionInInitialApicId ( 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[in] 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 F14NbPstateInit ( 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; BOOLEAN ReturnStatus; BOOLEAN WaitForTransition; 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; // F14 only supports NB P0 and NB P1 ASSERT (TargetNbPstate < 2); WaitForTransition = FALSE; ReturnStatus = TRUE; // 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; 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); F14EarlySampleCoreSupport.F14NbPstateInitHook (&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 <= F14MaxNbFreqAtMinVidFreqTable[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); // 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. 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 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; }