/* $NoKeywords:$ */ /** * @file * * mnphy.c * * Common Northbridge Phy support * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: (Mem/NB) * @e \$Revision: 87494 $ @e \$Date: 2013-02-04 12:06:47 -0600 (Mon, 04 Feb 2013) $ * **/ /***************************************************************************** * * Copyright (c) 2008 - 2013, 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. * *************************************************************************** * */ /* *---------------------------------------------------------------------------- * MODULES USED * *---------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "Ids.h" #include "mport.h" #include "mm.h" #include "mn.h" #include "mt.h" #include "mu.h" #include "PlatformMemoryConfiguration.h" #include "heapManager.h" #include "merrhdl.h" #include "Filecode.h" CODE_GROUP (G1_PEICC) RDATA_GROUP (G1_PEICC) #define FILECODE PROC_MEM_NB_MNPHY_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ #define UNUSED_CLK 4 /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /// Type of an entry for processing phy init compensation for client NB typedef struct { BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified UINT16 ExtraValue; ///< Extra value needed to be written to bit field CONST UINT16 (*TxPrePN)[3][5]; ///< Pointer to slew rate table } PHY_COMP_INIT_CLIENTNB; /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * * This function gets a delay value a PCI register during training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * * @return Value read */ UINT32 MemNGetTrainDlyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar ) { return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0); } /* -----------------------------------------------------------------------------*/ /** * * * This function sets a delay value a PCI register during training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * @param[in] Field - Value to be programmed * */ VOID MemNSetTrainDlyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar, IN UINT16 Field ) { NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field); } /* -----------------------------------------------------------------------------*/ /** * * * This function executes prototypical Phy fence training function. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNPhyFenceTrainingUnb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 FenceThresholdTxDll; UINT8 FenceThresholdRxDll; UINT8 FenceThresholdTxPad; UINT16 Fence2Data; MemNSetBitFieldNb (NBPtr, BFDataFence2, 0); MemNSetBitFieldNb (NBPtr, BFFence2, 0); // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b. // 2. Perform phy fence training. // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFenceThresholdTxDll\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxDll); // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b. MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000); // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b. // 6. Perform phy fence training. // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence); IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdRxDll); // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b. MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000); // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b. // 10. Perform phy fence training. // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence); IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxPad); // Program Fence2 threshold for Clk, Cmd, and Addr if (FenceThresholdTxPad < 16) { MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10); MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10); MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10); } else { MemNSetBitFieldNb (NBPtr, BFClkFence2, 0); MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0); MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0); } // Program Fence2 threshold for data Fence2Data = 0; if (FenceThresholdTxPad < 16) { Fence2Data |= FenceThresholdTxPad | 0x10; } if (FenceThresholdRxDll < 16) { Fence2Data |= (FenceThresholdRxDll | 0x10) << 10; } if (FenceThresholdTxDll < 16) { Fence2Data |= (FenceThresholdTxDll | 0x10) << 5; } MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data); NBPtr->FamilySpecificHook[ProgramFence2RxDll] (NBPtr, &Fence2Data); if (NBPtr->MCTPtr->Status[SbLrdimms]) { // 18. If motherboard routing requires CS[7:6] to adopt address timings, e.g. 3 LRDIMMs/ch with CS[7:6] // routed across all DIMM sockets, BIOS performs the following: if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, NULL, NULL) != NULL) { // A. Program D18F2xA8_dct[1:0][CSTimingMux67] = 1. MemNSetBitFieldNb (NBPtr, BFCSTimingMux67, 1); // B. Program D18F2x9C_x0D0F_8021_dct[1:0]: // - DiffTimingEn = 1. // - IF (D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay] >= // D18F2x9C_x0D0F_E008_dct[1:0][FenceValue]) THEN Fence = 1 ELSE Fence = 0. // - Delay = D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay]. // MemNSetBitFieldNb (NBPtr, BFDiffTimingEn, 1); MemNSetBitFieldNb (NBPtr, BFFence, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay) >= MemNGetBitFieldNb (NBPtr, BFFenceValue)) ? 1 : 0); MemNSetBitFieldNb (NBPtr, BFDelay, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay))); } } // 19. Reprogram F2x9C_04. MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); } /* -----------------------------------------------------------------------------*/ /** * * * This function executes Phy fence training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNTrainPhyFenceNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 Byte; INT16 Avg; UINT8 PREvalue; if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) { return; } // 1. BIOS first programs a seed value to the phase recovery // engine registers. // IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: "); for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) { // This includes ECC as byte 8 MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19); IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19); } IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1"); // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1. MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1); if (!NBPtr->IsSupported[UnifiedNbFence]) { // 3. Wait 200 MEMCLKs. MemNWaitXMemClksNb (NBPtr, 200); } else { // 3. Wait 2000 MEMCLKs. MemNWaitXMemClksNb (NBPtr, 2000); } // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0. MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0); // 5. BIOS reads the phase recovery engine registers // F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52. // 6. Calculate the average value of the fine delay and subtract 8. // Avg = 0; IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t PRE: "); for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) { // // This includes ECC as byte 8. ECC Byte lane (8) is ignored by MemNGetTrainDlyNb function where // ECC is not supported. // PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte))); Avg = Avg + ((INT16) PREvalue); IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", PREvalue); } Avg = ((Avg + 8) / 9); // round up NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg); IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFence: %02x\n", Avg); // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence]. MemNSetBitFieldNb (NBPtr, BFPhyFence, Avg); // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control // Register delays for both channels. This forces the phy to recompute // the fence. // MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); } /*-----------------------------------------------------------------------------*/ /** * * Returns the parameters for a requested delay value to be used in training * The correct Min, Max and Mask are determined based on the type of Delay, * and the frequency * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - Type of delay * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct * */ VOID MemNGetTrainDlyParmsUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN OUT TRN_DLY_PARMS *Parms ) { Parms->Min = 0; if ((TrnDly == AccessWrDatDly) || (TrnDly == AccessRdDqsDly)) { Parms->Max = 0x1F; Parms->Mask = 0x01F; } } /*---------------------------------------------------------------------------- * LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /* -----------------------------------------------------------------------------*/ /** * * * This function gets or set DQS timing during training. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * @param[in] Field - Value to be programmed * @param[in] IsSet - Indicates if the function will set or get * * @return value read, if the function is used as a "get" */ UINT32 MemNcmnGetSetTrainDlyUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT8 IsSet, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar, IN UINT16 Field ) { UINT16 Index; UINT16 Offset; UINT32 Value; UINT32 Address; UINT8 Dimm; UINT8 Rank; UINT8 Byte; UINT8 Nibble; UINT8 DimmNibble; Dimm = DRBN_DIMM (DrbnVar); Rank = DRBN_RANK (DrbnVar); Byte = DRBN_BYTE (DrbnVar); Nibble = DRBN_NBBL (DrbnVar); DimmNibble = DRBN_DIMM_NBBL (DrbnVar); ASSERT (Dimm < (NBPtr->CsPerChannel / NBPtr->CsPerDelay)); ASSERT (Byte <= ECC_DLY); if ((Byte == ECC_DLY) && (!NBPtr->MCTPtr->Status[SbEccDimms] || !NBPtr->IsSupported[EccByteTraining])) { // When ECC is not enabled if (IsSet) { // On write, ignore return 0; } else { // On read, redirect to byte 0 to correct fence averaging Byte = 0; } } switch (TrnDly) { case AccessRcvEnDly: Index = 0x10; break; case AccessWrDqsDly: Index = 0x30; break; case AccessWrDatDly: Index = 0x01; break; case AccessRdDqsDly: Index = 0x05; break; case AccessRdDqs2dDly: Index = 0x00; break; case AccessPhRecDly: Index = 0x50; break; default: Index = 0; IDS_ERROR_TRAP; } switch (TrnDly) { case AccessRcvEnDly: case AccessWrDqsDly: Index += (Dimm * 3); if (Byte & 0x04) { // if byte 4,5,6,7 Index += 0x10; } if (Byte & 0x02) { // if byte 2,3,6,7 Index++; } if (Byte > 7) { Index += 2; } Offset = 16 * (Byte % 2); Index |= (Rank << 8); Index |= (Nibble << 9); Address = Index; break; case AccessRdDqsDly: case AccessWrDatDly: if (NBPtr->IsSupported[DimmBasedOnSpeed]) { if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) { // if DDR speed is below 800, use DIMM 0 delays for all DIMMs. Dimm = 0; } } Index += (Dimm * 0x100); if (Nibble) { if (Rank) { Index += 0xA0; } else { Index += 0x70; } } else if (Rank) { Index += 0x60; } // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need // to run AccessPhRecDly sequence. case AccessPhRecDly: Index += (Byte / 4); Offset = 8 * (Byte % 4); Address = Index; break; case AccessRdDqs2dDly: Address = 0x0D0F0000; Index += (DimmNibble >> 1) * 0x100; Index += 0x20; Index = Index + Dimm; Offset = 4 * ((DimmNibble & 0x01) * 2); Address += Index; break; default: Offset = 0; IDS_ERROR_TRAP; Address = Index; } MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); if (TrnDly == AccessRdDqsDly) { NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset); } if (IsSet) { if (TrnDly == AccessPhRecDly) { Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; } if (TrnDly != AccessRdDqs2dDly) { Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF) << Offset))); } else { Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) 0x1F << Offset))); } ASSERT (!NBPtr->IsSupported[ScrubberEn]); // Phy CSR write is not allowed after scrubber is enabled MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); Address |= DCT_ACCESS_WRITE; MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); if (TrnDly == AccessPhRecDly) { NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; } } else { if (TrnDly != AccessRdDqs2dDly) { Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF); } else { Value = (Value >> Offset) & (UINT32) (0x1F); } } return Value; } /* -----------------------------------------------------------------------------*/ /** * * This function initializes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return AGESA_STATUS - Result * AGESA_SUCCESS - Training pattern is ready to use * AGESA_ERROR - Unable to initialize the pattern. */ AGESA_STATUS MemNTrainingPatternInitNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { MEM_TECH_BLOCK *TechPtr; ALLOCATE_HEAP_PARAMS AllocHeapParams; TRAIN_PATTERN TrainPattern; AGESA_STATUS Status; TechPtr = NBPtr->TechPtr; TrainPattern = 0; // // Check the training type // if (TechPtr->TrainingType == TRN_DQS_POSITION) { // // DQS Position Training // if (NBPtr->PosTrnPattern == POS_PATTERN_256B) { // // 256 Bit pattern // if (NBPtr->MCTPtr->Status[Sb128bitmode]) { TrainPattern = TestPatternJD256B; TechPtr->PatternLength = 64; } else { TrainPattern = TestPatternJD256A; TechPtr->PatternLength = 32; } } else { // // 72 bit pattern will be used if PosTrnPattern is not specified // if (NBPtr->MCTPtr->Status[Sb128bitmode]) { TrainPattern = TestPatternJD1B; TechPtr->PatternLength = 18; } else { TrainPattern = TestPatternJD1A; TechPtr->PatternLength = 9; } } } else if (TechPtr->TrainingType == TRN_MAX_READ_LATENCY) { // // Max Read Latency Training // TrainPattern = TestPatternML; TechPtr->PatternLength = (NBPtr->MCTPtr->Status[Sb128bitmode]) ? 6 : 3; } else { // // Error - TechPtr->Training Type must be set to one of the types handled in this function // ASSERT (FALSE); } // // Allocate training buffer // AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16; AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE; AllocHeapParams.Persist = HEAP_LOCAL_CACHE; Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader); ASSERT (Status == AGESA_SUCCESS); if (Status != AGESA_SUCCESS) { return Status; } TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr; AlignPointerTo16Byte (&TechPtr->PatternBufPtr); TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64); // Prepare training pattern MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64); return Status; } /* -----------------------------------------------------------------------------*/ /** * * This function determined the settings for the Reliable Read/Write engine * for each specific type of training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] *OptParam - Pointer to an Enum of TRAINING_TYPE * * @return TRUE */ BOOLEAN MemNSetupHwTrainingEngineUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN VOID *OptParam ) { TRAINING_TYPE TrnType; RRW_SETTINGS *Rrw; TrnType = *(TRAINING_TYPE*) OptParam; Rrw = &NBPtr->RrwSettings; // // Common Settings // Rrw->TgtBankAddressA = CPG_BANK_ADDRESS_A; Rrw->TgtRowAddressA = CPG_ROW_ADDRESS_A; Rrw->TgtColAddressA = CPG_COL_ADDRESS_A; Rrw->TgtBankAddressB = CPG_BANK_ADDRESS_B; Rrw->TgtRowAddressB = CPG_ROW_ADDRESS_B; Rrw->TgtColAddressB = CPG_COL_ADDRESS_B; Rrw->CompareMaskHigh = CPG_COMPARE_MASK_HI; Rrw->CompareMaskLow = CPG_COMPARE_MASK_LOW; Rrw->CompareMaskEcc = CPG_COMPARE_MASK_ECC; switch (TrnType) { case TRN_RCVR_ENABLE: // // Receiver Enable Training // NBPtr->TechPtr->PatternLength = 192; break; case TRN_MAX_READ_LATENCY: // // Max Read Latency Training // Rrw->CmdTgt = CMD_TGT_A; NBPtr->TechPtr->PatternLength = 32; Rrw->DataPrbsSeed = PRBS_SEED_32; break; case TRN_DQS_POSITION: // // Read/Write DQS Position training // Rrw->CmdTgt = CMD_TGT_AB; NBPtr->TechPtr->PatternLength = 256; Rrw->DataPrbsSeed = PRBS_SEED_256; break; default: ASSERT (FALSE); } return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * This function finalizes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] Index - Index of Write Data Delay Value * @param[in,out] *Value - Write Data Delay Value * @return BOOLEAN - TRUE - Use the value returned. * FALSE - No more values in table. */ BOOLEAN MemNGetApproximateWriteDatDelayNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT8 Index, IN OUT UINT8 *Value ) { CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F}; if (Index < GET_SIZE_OF (WriteDatDelayValue)) { *Value = WriteDatDelayValue[Index]; return TRUE; } return FALSE; } /* -----------------------------------------------------------------------------*/ /** * * This function finalizes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return AGESA_STATUS - Result * AGESA_SUCCESS - Training pattern has been finalized. * AGESA_ERROR - Unable to initialize the pattern. */ AGESA_STATUS MemNTrainingPatternFinalizeNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { AGESA_STATUS Status; // // Deallocate training buffer // Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader); ASSERT (Status == AGESA_SUCCESS); return Status; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the number of Chipselects controlled by each set * of Delay registers under current conditions. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return */ UINT8 MemNCSPerDelayNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { if (MemNGetBitFieldNb (NBPtr, BFPerRankTimingEn) == 1) { return 1; } else { return 2; } } /* -----------------------------------------------------------------------------*/ /** * * This function returns the minimum data eye width in 32nds of a UI for * the type of data eye(Rd/Wr) that is being trained. This value will * be the minimum number of consecutive delays that yield valid data. * Uses TechPtr->Direction to determine read or write. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return */ UINT8 MemNMinDataEyeWidthNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 MinRdDataeye; UINT8 MinWrDataeye; UINT8 *MinRdWrDataeyePtr; MinRdWrDataeyePtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MIN_RD_WR_DATAEYE_WIDTH, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { if (MinRdWrDataeyePtr != NULL) { MinRdDataeye = MinRdWrDataeyePtr[0]; return MinRdDataeye; } else { return MIN_RD_DATAEYE_WIDTH_NB; } } else { if (MinRdWrDataeyePtr != NULL) { MinWrDataeye = MinRdWrDataeyePtr[1]; return MinWrDataeye; } else { return MIN_WR_DATAEYE_WIDTH_NB; } } } /* -----------------------------------------------------------------------------*/ /** * * This function programs the phy registers according to the desired phy VDDIO voltage level * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNPhyVoltageLevelNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { BIT_FIELD_NAME BitField; BIT_FIELD_NAME BFEnd; UINT16 BFValue; UINT16 RegValue; BFValue = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3; BFEnd = NBPtr->IsSupported[ProgramCsrComparator] ? BFCsrComparator : BFCmpVioLvl; for (BitField = BFDataRxVioLvl; BitField <= BFEnd; BitField++) { RegValue = BFValue; if (BitField == BFCsrComparator) { RegValue >>= (3 - 2); // Setting this bit in DCT0 adjusts the comparator for DCT0 and DCT1. Setting this bit in DCT1 has no effect. NBPtr->SwitchDCT (NBPtr, 0); MemNSetBitFieldNb (NBPtr, BitField, RegValue); break; } else if (BitField == BFCmpVioLvl) { RegValue <<= (14 - 3); // Must set this bit on DCT0 even when DCT0 has no memory NBPtr->SwitchDCT (NBPtr, 0); MemNSetBitFieldNb (NBPtr, BitField, RegValue); } MemNBrdcstSetNb (NBPtr, BitField, RegValue); } // Program D18F2x9C_x0D0F_4006_dct[0][VrefSel] = 0. MemNSetBitFieldNb (NBPtr, BFObsrvVrefSel, 0); // Program D18F2x9C_x0D0F_4007_dct[0] per platform requirements. NBPtr->FamilySpecificHook[PhyInitVref] (NBPtr, NULL); } /*----------------------------------------------------------------------------- * * * This function calculates the value of WrDqDqsEarly and programs it into * the DCT and adds it to the WrDqsGrossDelay of each byte lane on each * DIMM of the channel. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNCalcWrDqDqsEarlyUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { MEM_TECH_BLOCK *TechPtr; DCT_STRUCT *DCTPtr; CH_DEF_STRUCT *ChannelPtr; UINT8 Dimm; UINT8 ByteLane; UINT8 *WrDqsDlysPtr; UINT8 WrDqDqsEarly; UINT8 ChipSel; ASSERT ((NBPtr->IsSupported[WLSeedAdjust]) && (NBPtr->IsSupported[WLNegativeDelay])); TechPtr = NBPtr->TechPtr; ChannelPtr = NBPtr->ChannelPtr; DCTPtr = NBPtr->DCTPtr; ASSERT (NBPtr != NULL); ASSERT (ChannelPtr != NULL); ASSERT (DCTPtr != NULL); // // For each DIMM: // - The Critical Gross Delay (CGD) is the minimum GrossDly of all byte lanes and all DIMMs. // - If (CGD < 0) Then // - D18F2xA8_dct[1:0][WrDqDqsEarly] = ABS(CGD) // - WrDqsGrossDly = GrossDly + WrDqDqsEarly // - Else // - D18F2xA8_dct[1:0][WrDqDqsEarly] = 0. // - WrDqsGrossDly = GrossDly // WrDqDqsEarly = 0; if (TechPtr->WLCriticalDelay < 0) { IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCalculating WrDqDqsEarly, adjusting WrDqs.\n"); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMin. Critical Delay: %x\n", TechPtr->WLCriticalDelay); // We've saved the entire negative delay value, so take the ABS and convert to GrossDly. WrDqDqsEarly = (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20))); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n\n", WrDqDqsEarly); // // Loop through All WrDqsDlys on all DIMMs // for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay) { if ((NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : ((DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSel)) != 0)) { // // If LRDIMMs, only include the physical dimms, not logical Dimms // IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS %x:", ChipSel); Dimm = ChipSel / NBPtr->CsPerDelay; WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]); for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { WrDqsDlysPtr[ByteLane] += (WrDqDqsEarly << 5); NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]); IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]); } IDS_HDT_CONSOLE (MEM_FLOW, "\n"); } } } MemNSetBitFieldNb (NBPtr, BFWrDqDqsEarly, WrDqDqsEarly); return TRUE; } /*----------------------------------------------------------------------------- * * * This function forces phy to M0 state * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return none * ---------------------------------------------------------------------------- */ VOID MemNForcePhyToM0Unb ( IN OUT MEM_NB_BLOCK *NBPtr ) { IDS_SKIP_HOOK (IDS_FORCE_PHY_TO_M0, NBPtr, &(NBPtr->MemPtr->StdHeader)) { // 1. Program D18F2x9C_x0D0F_E013_dct[1:0] = 0118h. MemNBrdcstSetUnConditionalNb (NBPtr, BFPllRegWaitTime, 0x118); // 2. For each byte lane and each memory P-state: Program D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxSsbMntClkEn] = 0. MemNBrdcstSetUnConditionalNb (NBPtr, BFRxSsbMntClkEn, 0); // 3. D18F2xA8_dct[1:0][MemPhyPllPdMode] = 00b. MemNBrdcstSetUnConditionalNb (NBPtr, BFMemPhyPllPdMode, 0); // 4. Force the phy to M0 with the following sequence: // A. Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 190h. Restore the default PLL lock time. MemNBrdcstSetUnConditionalNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault); // B. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80800000h. MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80800000); NBPtr->SwitchDCT (NBPtr, 0); // C. Program D18F2x9C_x0D0F_E018_dct[0][PhyPSMasterChannel] = 0. MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0); // D. Program D18F2x9C_x0000_000B_dct[0] = 40000000h. CH0 only; MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x40000000); // E. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80000000h. MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80000000); } } /*----------------------------------------------------------------------------- * * * This function sets SkewMemClk before enabling MemClk * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *OptParam - Optional parameter * * @return TRUE - always * ---------------------------------------------------------------------------- */ BOOLEAN MemNSetSkewMemClkUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { UINT8 Dct; // SkewMemClk is set to 1 if all DCTs are enabled, else 0. for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { MemNSwitchDCTNb (NBPtr, Dct); if (NBPtr->DCTPtr->Timings.DctMemSize == 0) { break; } } MemNSwitchDCTNb (NBPtr, 0); if (Dct == NBPtr->DctCount) { MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0x10); } else { MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0); } return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * This function masks the RdDqsDly Bit 0 before writing to register for UNB. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *Offset - Bit offset of the field to be programmed * * @return TRUE */ BOOLEAN MemNAdjustRdDqsDlyOffsetUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *Offset ) { *(UINT16*) Offset = *(UINT16*) Offset + 1; return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * * This function initializes RxEn Delays for RxEn seedless training * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNInitializeRxEnSeedlessTrainingUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { UINT8 ByteLane; // Save original PRE based RxEnDly for RxEn Seedless training for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { NBPtr->TechPtr->RxOrig[ByteLane] = NBPtr->ChannelPtr->RcvEnDlys[(NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) * NBPtr->TechPtr->DlyTableWidth () + ByteLane]; } return TRUE; } /*----------------------------------------------------------------------------- * * * This function checks each bytelane for no window error. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNTrackRxEnSeedlessRdWrNoWindBLErrorUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { MemTTrackRxEnSeedlessRdWrNoWindBLError (NBPtr->TechPtr, OptParam); return TRUE; } /*----------------------------------------------------------------------------- * * * This function checks each bytelane for small window error. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNTrackRxEnSeedlessRdWrSmallWindBLErrorUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { MemTTrackRxEnSeedlessRdWrSmallWindBLError (NBPtr->TechPtr, OptParam); return TRUE; } /*----------------------------------------------------------------------------- * * * This function initializes a ByteLaneError error. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNInitialzeRxEnSeedlessByteLaneErrorUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { UINT8 ByteLane; for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { NBPtr->TechPtr->ByteLaneError[ByteLane] = FALSE; // All Bytelanes have no errors } return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * * This function sets phy power saving related settings in different MPstate context. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return none * ---------------------------------------------------------------------------- */ VOID MemNPhyPowerSavingMPstateUnb ( IN OUT MEM_NB_BLOCK *NBPtr ) { STATIC UINT8 Sequence[] = {8, 4, 3, 5, 2, 6, 1, 7, 0}; UINT16 DllPower[9]; UINT8 NumLanes; UINT8 DllWakeTime; UINT8 MaxRxStggrDly; UINT8 MinRcvEnGrossDly; UINT8 MinWrDatGrossDly; UINT8 dRxStggrDly; UINT8 dTxStggrDly; UINT8 TempStggrDly; UINT8 MaxTxStggrDly; UINT8 Tcwl; UINT8 i; UINT16 MemClkSpeed; MemClkSpeed = ( (NBPtr->MemPstate == MEMORY_PSTATE0) ? NBPtr->DCTPtr->Timings.Speed : MemNGetMemClkFreqUnb (NBPtr, (UINT8) MemNGetBitFieldNb (NBPtr, BFM1MemClkFreq)) ); IDS_HDT_CONSOLE (MEM_FLOW, "Start Phy power saving setting for memory Pstate %d\n", NBPtr->MemPstate); // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b. // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b. // 6. D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxDqsUDllPowerDown] = 1. MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x83); // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn]. // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b} if (NBPtr->MemPstate == MEMORY_PSTATE0) { MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1); } // 9. IF (DimmsPopulated == 1)&& ((D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==010b) || // (D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==011b)) THEN THEN // program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 1 // ELSE program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 0 ENDIF. if ((NBPtr->ChannelPtr->Dimms == 1) && ((MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 2) || (MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 3))) { MemNSetBitFieldNb (NBPtr, BFLowPowerDrvStrengthEn, 0x100); } // 10. Program D18F2x9C_x0D0F_0[F,7:0][50,10]_dct[1:0][EnRxPadStandby] = IF // (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz) THEN 1 ELSE 0 ENDIF. MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (MemClkSpeed <= DDR1600_FREQUENCY) ? 0x1000 : 0); // 11. Program D18F2x9C_x0000_000D_dct[1:0]_mp[1:0] as follows: // If (DDR rate < = 1600) TxMaxDurDllNoLock = RxMaxDurDllNoLock = 8h // else TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h. if (MemClkSpeed <= DDR1600_FREQUENCY) { MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 8); MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 8); } else { MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7); MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7); } // TxCPUpdPeriod = RxCPUpdPeriod = 011b. MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3); MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3); // TxDLLWakeupTime = RxDLLWakeupTime = 11b. MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3); MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3); if (NBPtr->IsSupported[DllStaggerEn]) { // 12. Program D18F2x9C_x0D0F_0[F,7:0][5C,1C]_dct[1:0] as follows. // Let Numlanes = 8. = 9 with ECC. NumLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8; // RxDllStggrEn = TxDllStggrEn = 1. for (i = 0; i < 9; i ++) { DllPower[i] = 0x8080; } // If (DDR rate > = 1866) DllWakeTime = 1, Else DllWakeTime = 0. DllWakeTime = (MemClkSpeed >= DDR1866_FREQUENCY) ? 1 : 0; // Let MaxRxStggrDly = (Tcl*2) + MIN(DqsRcvEnGrossDelay for all byte lanes (see D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4. MinRcvEnGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessRcvEnDly, FALSE); ASSERT ((NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly) >= 4); MaxRxStggrDly = NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly - 4; // Let (real) dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (Numlanes - 1). ASSERT (MaxRxStggrDly >= DllWakeTime); dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (NumLanes - 1); IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum RcvEnGrossDly: 0x%02x MaxRxStggrDly: 0x%02x dRxStggrDly: 0x%02x\n", MinRcvEnGrossDly, MaxRxStggrDly, dRxStggrDly); // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program RxDllStggrDly[5:0] = an // increasing value, starting with 0 for the first byte lane in the sequence and increasing at a rate of dRxStggrDly // for each subsequent byte lane. Convert the real to integer by rounding down or using C (int) typecast after linearization. i = 9 - NumLanes; TempStggrDly = 0; for (; i < 9; i ++) { DllPower[Sequence[i]] |= ((TempStggrDly & 0x3F) << 8); TempStggrDly = TempStggrDly + dRxStggrDly; } // Let MaxTxStggrDly = (Tcwl*2) + MIN(MIN (WrDatGrossDly for all byte lanes (see // D18F2x9C_x0000_0[3:0]0[2:1]_dct[1:0]_mp[1:0])), MIN(DqsRcvEnGrossDelay for all byte lanes (see // D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4. Tcwl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcwl); MinWrDatGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessWrDatDly, FALSE); MaxTxStggrDly = Tcwl * 2 + MIN (MinRcvEnGrossDly, MinWrDatGrossDly) - 4; // Let dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (Numlanes - 1). ASSERT (MaxTxStggrDly >= DllWakeTime); dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (NumLanes - 1); // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program TxDllStggrDly[5:0] = an // increasing integer value, starting with 0 for the first byte lane in the sequence and increasing at a rate of // dTxStggrDly for each subsequent byte lane. IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum WrDatGrossDly: 0x%02x MaxTxStggrDly: 0x%02x dTxStggrDly: 0x%02x\n", MinWrDatGrossDly, MaxTxStggrDly, dTxStggrDly); i = 9 - NumLanes; TempStggrDly = 0; for (; i < 9; i ++) { DllPower[Sequence[i]] |= (TempStggrDly & 0x3F); TempStggrDly = TempStggrDly + dTxStggrDly; } IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : ECC 07 06 05 04 03 02 01 00\n"); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDll Power : %04x %04x %04x %04x %04x %04x %04x %04x %04x\n", DllPower[8], DllPower[7], DllPower[6], DllPower[5], DllPower[4], DllPower[3], DllPower[2], DllPower[1], DllPower[0]); for (i = 0; i < NumLanes; i ++) { MemNSetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i, (MemNGetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i) & 0x4040) | DllPower[i]); } } // 13. Program D18F2x248_dct[1:0]_mp[1:0] and then D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0] as follows: // For M1 context program RxChMntClkEn=RxSsbMntClkEn=0. // For M0 context program RxChMntClkEn=RxSsbMntClkEn=1. if (NBPtr->MemPstate == MEMORY_PSTATE1 || NBPtr->IsSupported[RxChnMntClksEn] == FALSE) { MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 0); MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0); } else { MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 1); MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0x100); } IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader); } /* -----------------------------------------------------------------------------*/ /** * This function adjusts the Phase Mask based on ECC. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] OptParam - Optional parameter * * @return TRUE * ---------------------------------------------------------------------------- */ BOOLEAN MemNAdjust2DPhaseMaskBasedOnEccUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { NBPtr->PhaseLaneMask = NBPtr->PhaseLaneMask & (UINT32) (NBPtr->MCTPtr->Status[SbEccDimms] ? 0x0FFFF : 0x3FFFF); return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * This function adjusts WrDqsBias before seed scaling * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *WrDqsBias - Pointer to WrDqsBias * * @return FALSE - Supported * @return TRUE - Not supported */ BOOLEAN MemNAdjustWrDqsBeforeSeedScalingUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *WrDqsBias ) { // Subtract (0x20 * WrDqDqsEarly) since it is a non-scalable component * (INT16 *) WrDqsBias = (INT16) (0x20 * MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly)); return TRUE; } /* -----------------------------------------------------------------------------*/ /** * * This function adjust RdDqsDly used for MaxRdLatency calculation * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *OptParam - Optional parameter * * @return FALSE - Supported * @return TRUE - Not supported */ BOOLEAN MemNAdjustRdDqsDlyForMaxRdLatUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { // Set RdDqsDlyForMaxRdLat to 0 so that actual RdDqsDly is used for MaxRdLatency calculation NBPtr->RdDqsDlyForMaxRdLat = 0; return TRUE; } /* -----------------------------------------------------------------------------*/ /** * This function collects data for Eye Rim Search * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return TRUE - No Errors occurred * @return FALSE - Errors occurred */ BOOLEAN MemN2DRdDQSEyeRimSearchUnb ( IN OUT MEM_NB_BLOCK *NBPtr ) { return MemT2DRdDQSEyeRimSearch (NBPtr->TechPtr); } /*----------------------------------------------------------------------------- * * * Hook for some families which need an intermediate Mem Clk Frequency Valid * before changing to the initial startup training frequency. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *OptParam - Optional parameter * * @return TRUE - always * ---------------------------------------------------------------------------- */ BOOLEAN MemNIntermediateMemclkFreqValUnb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT VOID *OptParam ) { // 1. Program MemClkFreq = 200MHz MemNSetBitFieldNb (NBPtr, BFMemClkFreq, MemNGetMemClkFreqIdUnb (NBPtr, DDR400_FREQUENCY)); // 2. MemClkFreqVal = 1; MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1); // 3. Wait for FreqChgInPrg == 0 MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, FALSE); // 4. Program MemClkFreqVal = 0 MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 0); // 5. Retore MemClkFreq to startup value. MemNSetBitFieldNb (NBPtr, BFMemClkFreq, MemNGetMemClkFreqIdUnb (NBPtr, NBPtr->DCTPtr->Timings.Speed)); return TRUE; }