summaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c')
-rw-r--r--src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c b/src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c
new file mode 100644
index 0000000000..226e951ec3
--- /dev/null
+++ b/src/vendorcode/amd/agesa/f14/Proc/CPU/Family/0x14/cpuCommonF14Utilities.c
@@ -0,0 +1,518 @@
+/* $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
+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;
+ 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);
+
+ 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);
+ 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;
+ } 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);
+
+ // Wait for the transition to complete.
+ PciAddress.AddressValue = NB_PSTATE_CTRL_STS_PCI_ADDR;
+ do {
+ LibAmdPciRead (AccessWidth32, PciAddress, &NbPsCtrlSts.Value, StdHeader);
+ } while (NbPsCtrlSts.Field.NbPs1Act != 1);
+
+ *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);
+ }
+ }
+ }
+
+ // 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);
+
+ 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;
+}