| /* $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; |
| } |