| /* $NoKeywords:$ */ |
| /** |
| * @file |
| * |
| * mnphytn.c |
| * |
| * Northbridge Phy support for TN |
| * |
| * @xrefitem bom "File Content Label" "Release Content" |
| * @e project: AGESA |
| * @e sub-project: (Mem/NB/TN) |
| * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $ |
| * |
| **/ |
| /***************************************************************************** |
| * |
| * Copyright (c) 2008 - 2012, 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 "ma.h" |
| #include "mm.h" |
| #include "mn.h" |
| #include "mt.h" |
| #include "mu.h" |
| #include "OptionMemory.h" // need def for MEM_FEAT_BLOCK_NB |
| #include "mntn.h" |
| #include "PlatformMemoryConfiguration.h" |
| #include "Filecode.h" |
| CODE_GROUP (G3_DXE) |
| RDATA_GROUP (G3_DXE) |
| |
| |
| #define FILECODE PROC_MEM_NB_TN_MNPHYTN_FILECODE |
| /*---------------------------------------------------------------------------- |
| * DEFINITIONS AND MACROS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| #define UNUSED_CLK 4 |
| |
| |
| /// The structure of TxPrePN tables |
| typedef struct { |
| UINT32 Speed; ///< Applied memory speed |
| UINT16 TxPrePNVal[4]; ///< Table values |
| } TXPREPN_STRUCT; |
| |
| /// The entry of individual TxPrePN tables |
| typedef struct { |
| UINT8 TxPrePNTblSize; ///< Total Table size |
| CONST TXPREPN_STRUCT *TxPrePNTblPtr; ///< Pointer to the table |
| } TXPREPN_ENTRY; |
| |
| /// Type of an entry for processing phy init compensation for TN |
| 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 TXPREPN_ENTRY *TxPrePN; ///< Pointer to slew rate table |
| } PHY_COMP_INIT_NB; |
| /*---------------------------------------------------------------------------- |
| * TYPEDEFS AND STRUCTURES |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------- |
| * PROTOTYPES OF LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| BOOLEAN |
| MemNRdPosTrnTN ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ); |
| |
| /*---------------------------------------------------------------------------- |
| * EXPORTED FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| extern MEM_FEAT_TRAIN_SEQ memTrainSequenceDDR3[]; |
| /* -----------------------------------------------------------------------------*/ |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function initializes the DDR phy compensation logic |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNInitPhyCompTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| // |
| // Phy Predriver Calibration Codes for Data/DQS |
| // |
| CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV15TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V |
| {DDR667 + DDR800, {0x924, 0x924, 0x924, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV135TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V |
| {DDR667 + DDR800, {0xFF6, 0xB6D, 0xB6D, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNDataDqsV125TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V |
| {DDR667 + DDR800, {0xFF6, 0xDAD, 0xDAD, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| }; |
| CONST STATIC TXPREPN_ENTRY TxPrePNDataDqsTN[] = { |
| {GET_SIZE_OF (TxPrePNDataDqsV15TN), (TXPREPN_STRUCT *)&TxPrePNDataDqsV15TN}, |
| {GET_SIZE_OF (TxPrePNDataDqsV135TN), (TXPREPN_STRUCT *)&TxPrePNDataDqsV135TN}, |
| {GET_SIZE_OF (TxPrePNDataDqsV125TN), (TXPREPN_STRUCT *)&TxPrePNDataDqsV125TN} |
| }; |
| |
| // |
| // Phy Predriver Calibration Codes for Cmd/Addr |
| // |
| CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV15TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V |
| {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}}, |
| {DDR1066 + DDR1333, {0x6DB, 0x6DB, 0x6DB, 0x6DB}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xB6D, 0xB6D, 0xB6D, 0xB6D}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV135TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V |
| {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}}, |
| {DDR1066 + DDR1333, {0x924, 0x6DB, 0x6DB, 0x6DB}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xB6D, 0xB6D, 0x924, 0x924}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNCmdAddrV125TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V |
| {DDR667 + DDR800, {0x492, 0x492, 0x492, 0x492}}, |
| {DDR1066 + DDR1333, {0xDAD, 0x924, 0x6DB, 0x492}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xDAD, 0xB64, 0xB64}} |
| }; |
| CONST STATIC TXPREPN_ENTRY TxPrePNCmdAddrTN[] = { |
| {GET_SIZE_OF (TxPrePNCmdAddrV15TN), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV15TN}, |
| {GET_SIZE_OF (TxPrePNCmdAddrV135TN), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV135TN}, |
| {GET_SIZE_OF (TxPrePNCmdAddrV125TN), (TXPREPN_STRUCT *)&TxPrePNCmdAddrV125TN} |
| }; |
| |
| // |
| // Phy Predriver Calibration Codes for Clock |
| // |
| CONST STATIC TXPREPN_STRUCT TxPrePNClockV15TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.5V |
| {DDR667 + DDR800, {0x924, 0x924, 0x924, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xB6D}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNClockV135TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.35V |
| {DDR667 + DDR800, {0xDAD, 0xDAD, 0x924, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xDAD}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xDAD}} |
| }; |
| CONST STATIC TXPREPN_STRUCT TxPrePNClockV125TN[] = { |
| //{TxPreP, TxPreN}[Speed][Drive Strength] at 1.25V |
| {DDR667 + DDR800, {0xDAD, 0xDAD, 0x924, 0x924}}, |
| {DDR1066 + DDR1333, {0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| {DDR1600 + DDR1866 + DDR2133, {0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| }; |
| CONST STATIC TXPREPN_ENTRY TxPrePNClockTN[] = { |
| {GET_SIZE_OF (TxPrePNClockV15TN), (TXPREPN_STRUCT *)&TxPrePNClockV15TN}, |
| {GET_SIZE_OF (TxPrePNClockV135TN), (TXPREPN_STRUCT *)&TxPrePNClockV135TN}, |
| {GET_SIZE_OF (TxPrePNClockV125TN), (TXPREPN_STRUCT *)&TxPrePNClockV125TN} |
| }; |
| |
| // |
| // Tables to describe the relationship between drive strength bit fields, PreDriver Calibration bit fields and also |
| // the extra value that needs to be written to specific PreDriver bit fields |
| // |
| CONST PHY_COMP_INIT_NB PhyCompInitBitFieldTN[] = { |
| // 3. Program TxPreP/TxPreN for Data and DQS according toTable 25 if VDDIO is 1.5V or Table 26 if 1.35V. |
| // A. Program D18F2x9C_x0D0F_0[F,7:0]0[A,6]_dct[1:0]={0000b, TxPreP, TxPreN}. |
| // B. Program D18F2x9C_x0D0F_0[F,7:0]02_dct[1:0]={0000b, TxPreP, TxPreN}. |
| {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqsTN}, |
| {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqsTN}, |
| {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqsTN}, |
| // 4. Program TxPreP/TxPreN for Cmd/Addr according to Table 28 if VDDIO is 1.5V or Table 29 if 1.35V. |
| // A. Program D18F2x9C_x0D0F_[C,8][1:0][12,0E,0A,06]_dct[1:0]={0000b, TxPreP, TxPreN}. |
| // B. Program D18F2x9C_x0D0F_[C,8][1:0]02_dct[1:0]={1000b, TxPreP, TxPreN}. |
| {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddrTN}, |
| {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddrTN}, |
| {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddrTN}, |
| {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddrTN}, |
| {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddrTN}, |
| // 5. Program TxPreP/TxPreN for Clock according to Table 31 if VDDIO is 1.5V or Table 32 if 1.35V. |
| // A. Program D18F2x9C_x0D0F_2[2:0]02_dct[1:0]={1000b, TxPreP, TxPreN}. |
| {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock2TxPreDriverCalPad0, 8, TxPrePNClockTN} |
| }; |
| |
| BIT_FIELD_NAME CurrentBitField; |
| UINT32 SpeedMask; |
| UINT8 SizeOfTable; |
| UINT8 Voltage; |
| UINT8 i; |
| UINT8 j; |
| UINT8 k; |
| UINT8 Dct; |
| CONST TXPREPN_STRUCT *TblPtr; |
| |
| Dct = NBPtr->Dct; |
| NBPtr->SwitchDCT (NBPtr, 0); |
| // 1. Program D18F2x[1,0]9C_x0000_0008[DisAutoComp, DisablePreDriverCal] = {1b, 1b}. |
| MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 3); |
| NBPtr->SwitchDCT (NBPtr, Dct); |
| |
| SpeedMask = (UINT32) 1 << (NBPtr->DCTPtr->Timings.Speed / 66); |
| Voltage = (UINT8) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage); |
| |
| for (j = 0; j < GET_SIZE_OF (PhyCompInitBitFieldTN); j ++) { |
| i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitFieldTN[j].IndexBitField); |
| TblPtr = (PhyCompInitBitFieldTN[j].TxPrePN[Voltage]).TxPrePNTblPtr; |
| SizeOfTable = (PhyCompInitBitFieldTN[j].TxPrePN[Voltage]).TxPrePNTblSize; |
| for (k = 0; k < SizeOfTable; k++, TblPtr++) { |
| if ((TblPtr->Speed & SpeedMask) != 0) { |
| for (CurrentBitField = PhyCompInitBitFieldTN[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitFieldTN[j].EndTargetBitField; CurrentBitField ++) { |
| MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitFieldTN[j].ExtraValue << 12) | TblPtr->TxPrePNVal[i])); |
| } |
| break; |
| } |
| } |
| // Asserting if no table is found corresponding to current memory speed. |
| ASSERT (k < SizeOfTable); |
| } |
| NBPtr->SwitchDCT (NBPtr, 0); |
| // 6. Program D18F2x9C_x0000_0008_dct[1:0]_mp[1:0][DisAutoComp] = 0. |
| MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 1); |
| NBPtr->SwitchDCT (NBPtr, Dct); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This is a general purpose function that executes before DRAM training |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNBeforeDQSTrainingTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 Dct; |
| |
| for (Dct = 0; Dct < MAX_DCTS_PER_NODE_TN; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| MemNSetBitFieldNb (NBPtr, BFTrNibbleSel, 0); |
| // |
| // 2.10.6.9.2 - BIOS should program D18F2x210_dct[1:0]_nbp[3:0][MaxRdLatency] to 55h. |
| // |
| MemNSetBitFieldNb (NBPtr, BFMaxLatency, 0x55); |
| NBPtr->CsPerDelay = MemNCSPerDelayNb (NBPtr); |
| } |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This is a function that executes after DRAM training for TN |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * |
| */ |
| |
| VOID |
| MemNAfterDQSTrainingTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr |
| ) |
| { |
| UINT8 Dct; |
| BOOLEAN DllShutDownEn; |
| UINT8 Dimm; |
| UINT8 Byte; |
| UINT16 Dly; |
| |
| DllShutDownEn = TRUE; |
| IDS_OPTION_HOOK (IDS_DLL_SHUT_DOWN, &DllShutDownEn, &(NBPtr->MemPtr->StdHeader)); |
| |
| MemNBrdcstSetNb (NBPtr, BFMemPhyPllPdMode, 2); |
| MemNBrdcstSetNb (NBPtr, BFPllLockTime, 0x190); |
| |
| for (Dct = 0; Dct < MAX_DCTS_PER_NODE_TN; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| // |
| // 2.10.6.7 DCT Training Specific Configuration |
| // |
| MemNSetBitFieldNb (NBPtr, BFAddrCmdTriEn, 1); |
| MemNSetBitFieldNb (NBPtr, BFDisAutoRefresh, 0); |
| if (DllShutDownEn && NBPtr->IsSupported[SetDllShutDown]) { |
| MemNSetBitFieldNb (NBPtr, BFDisDllShutdownSR, 0); |
| } |
| MemNSetBitFieldNb (NBPtr , BFForceAutoPchg, 0); |
| MemNSetBitFieldNb (NBPtr , BFDynPageCloseEn, 0); |
| if (NBPtr->RefPtr->EnableBankSwizzle) { |
| MemNSetBitFieldNb (NBPtr, BFBankSwizzleMode, 1); |
| } |
| MemNSetBitFieldNb (NBPtr, BFDcqBypassMax, 0x01F); |
| MemNPowerDownCtlTN (NBPtr); |
| MemNSetBitFieldNb (NBPtr, BFZqcsInterval, 2); |
| MemNSetBitFieldNb (NBPtr, BFBankSwap, 1); |
| // |
| // Post Training values for BFRxMaxDurDllNoLock, BFTxMaxDurDllNoLock, |
| // and BFEnRxPadStandby are handled by Power savings code |
| // |
| // BFBwCapEn and BFODTSEn are handled by OnDimmThermal Code |
| // |
| // BFDctSelIntLvEn is programmed by Interleaving feature |
| // |
| } |
| } |
| // |
| // |
| for (Dct = 0; Dct < MAX_DCTS_PER_NODE_TN; Dct++) { |
| MemNSwitchDCTNb (NBPtr, Dct); |
| if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| if (!(NBPtr->DctCachePtr->excel846 )) { |
| for (Dimm = 0; Dimm < 4; Dimm++) { |
| for (Byte = 0; Byte < 9; Byte++) { |
| Dly = (UINT16) MemNGetTrainDlyNb (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, Byte)); |
| MemNSetTrainDlyNb (NBPtr, excel845 , DIMM_NBBL_ACCESS (Dimm, Byte * 2), Dly); |
| MemNSetTrainDlyNb (NBPtr, excel845 , DIMM_NBBL_ACCESS (Dimm, (Byte * 2) + 1), Dly); |
| } |
| } |
| } |
| } |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function overrides the seed for hardware based RcvEn training of TN. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *SeedPtr - Pointer to the seed value. |
| * |
| * @return TRUE |
| */ |
| |
| BOOLEAN |
| MemNOverrideRcvEnSeedTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *SeedPtr |
| ) |
| { |
| *(UINT16 *)SeedPtr = 0x32 - (0x20 * (UINT16) MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly)); |
| |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function choose the correct PllLockTime for TN |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *PllLockTime - Bit offset of the field to be programmed |
| * |
| * @return TRUE |
| */ |
| BOOLEAN |
| MemNAdjustPllLockTimeTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *PllLockTime |
| ) |
| { |
| if (MemNGetBitFieldNb (NBPtr, BFMemPhyPllPdMode) == 2) { |
| *(UINT16*) PllLockTime = 0x190; |
| } |
| |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function overrides the seed for hardware based WL for TN. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *SeedPtr - Pointer to the seed value. |
| * |
| * @return TRUE |
| */ |
| |
| BOOLEAN |
| MemNOverrideWLSeedTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *SeedPtr |
| ) |
| { |
| if (NBPtr->ChannelPtr->SODimmPresent != 0) { |
| *(UINT8*) SeedPtr = 0xE; |
| } else { |
| // Unbuffered dimm |
| *(UINT8*) SeedPtr = 0x15; |
| } |
| |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function adjusts Avg PRE value of Phy fence training for TN. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *Value16 - Pointer to the value that we want to adjust |
| * |
| */ |
| VOID |
| MemNPFenceAdjustTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT INT16 *Value16 |
| ) |
| { |
| if (*Value16 < 0) { |
| *Value16 = 0; |
| } |
| |
| // This makes sure the phy fence value will be written to M1 context as well. |
| MULTI_MPSTATE_COPY_TSEFO (NBPtr->NBRegTable, BFPhyFence); |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * |
| * This function programs Fence2RxDll for TN. |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *Fence2Data - Pointer to the value of fence2 data |
| * |
| */ |
| BOOLEAN |
| MemNProgramFence2RxDllTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *Fence2Data |
| ) |
| { |
| UINT8 Dct; |
| UINT16 Fence2RxDllTxPad; |
| UINT16 Fence2Value; |
| UINT16 Fence1; |
| BIT_FIELD_NAME BitField; |
| |
| Fence2Value = (UINT16) MemNGetBitFieldNb (NBPtr, BFFence2); |
| Fence2RxDllTxPad = (*(UINT16*) Fence2Data & 0x1F) | (((*(UINT16*) Fence2Data >> 5) & 0x1F) << 10); |
| |
| Fence2Value &= ~(UINT16) ((0x1F << 10) | 0x1F); |
| Fence2Value |= Fence2RxDllTxPad; |
| MemNSetBitFieldNb (NBPtr, BFFence2, Fence2Value); |
| |
| if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) { |
| MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 16, BFPhyFence); |
| BitField = (NBPtr->Dct == 0) ? BFChAM1FenceSave : BFChBM1FenceSave; |
| |
| Fence1 = (UINT16) MemNGetBitFieldNb (NBPtr, BFPhyFence); |
| Dct = NBPtr->Dct; |
| MemNSwitchDCTNb (NBPtr, 1); |
| MemNSetBitFieldNb (NBPtr, BitField, Fence1); |
| MemNSwitchDCTNb (NBPtr, Dct); |
| } |
| |
| return TRUE; |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function checks if RdDqsDly needs to be restarted for Trinity |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *Center - Center of the data eye |
| * |
| * @return TRUE |
| */ |
| |
| BOOLEAN |
| MemNRdDqsDlyRestartChkTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *Center |
| ) |
| { |
| INT8 EyeCenter; |
| UINT8 ByteLane; |
| BOOLEAN RetVal; |
| MEM_TECH_BLOCK *TechPtr; |
| CH_DEF_STRUCT *ChanPtr; |
| |
| TechPtr = NBPtr->TechPtr; |
| ChanPtr = NBPtr->ChannelPtr; |
| ByteLane = NBPtr->TechPtr->Bytelane; |
| RetVal = TRUE; |
| |
| // If the average value of passing read DQS delay for the lane is negative, then adjust the input receiver |
| // DQ delay in D18F2x9C_x0D0F_0[F,7:0][5F,1F]_dct[1:0] for the lane as follows: |
| |
| EyeCenter = ((INT8) ChanPtr->RdDqsMinDlys[ByteLane] + (INT8) ChanPtr->RdDqsMaxDlys[ByteLane] + 1) / 2; |
| |
| if ((EyeCenter < 0) && (NBPtr->RdDqsDlyRetrnStat != RDDQSDLY_RTN_SUSPEND)) { |
| IDS_HDT_CONSOLE (MEM_FLOW, " Negative data eye center.\n"); |
| |
| if (MemNGetBitFieldNb (NBPtr, BFRxBypass3rd4thStg) == 4) { |
| // IF (RxBypass3rd4thStg == 1) program RxBypass3rd4thStg=0 and repeat step 3 above for all |
| // ranks and lanes |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tRxByPass3rd4thStg is 1, clear it and restart RdDqsDly training on current Dct.\n"); |
| |
| MemNSetBitFieldNb (NBPtr, BFRxBypass3rd4thStg, 0); |
| NBPtr->RdDqsDlyRetrnStat = RDDQSDLY_RTN_ONGOING; |
| |
| // When Retrain condition is first detected, record the current chipsel at which the retrain starts |
| // so we don't need to retrain RcvEnDly and WrDatDly on the chipsels that are already done with these steps. |
| TechPtr->RestartChipSel = (INT8) TechPtr->ChipSel; |
| |
| RetVal = FALSE; |
| } else if (MemNGetBitFieldNb (NBPtr, BFRx4thStgEn) == 0) { |
| // ELSEIF (Rx4thStgEn == 0) program Rx4thStgEn=1 and repeat step 3 above for all ranks and lanes |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tRx4thStg is 0, set it and restart RdDqsDly training on current Dct.\n"); |
| |
| MemNSetBitFieldNb (NBPtr, BFRx4thStgEn, 0x100); |
| NBPtr->RdDqsDlyRetrnStat = RDDQSDLY_RTN_ONGOING; |
| |
| // If the second retrain starts beyond the chip selects that are previously trained, update the record so |
| // we don't need to retrain RcvEnDly and WrDatDly |
| if (TechPtr->RestartChipSel < ((INT8) TechPtr->ChipSel)) { |
| TechPtr->RestartChipSel = (INT8) TechPtr->ChipSel; |
| } |
| |
| RetVal = FALSE; |
| } else { |
| // ELSE program the read DQS delay for the lane with a value of zero |
| IDS_HDT_CONSOLE (MEM_FLOW, " "); |
| IDS_HDT_CONSOLE (MEM_FLOW, "Center of data eye is still negative after 2 retires. Do not restart training, just use 0\n"); |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\t "); |
| *(UINT8 *) Center = 0; |
| } |
| } |
| |
| return RetVal; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function executes RdDQS training |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return TRUE - All bytelanes pass |
| * @return FALSE - Some bytelanes fail |
| */ |
| BOOLEAN |
| MemNRdPosTrnTN ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| BOOLEAN RetVal; |
| |
| if (((INT8) TechPtr->ChipSel) > TechPtr->RestartChipSel) { |
| RetVal = MemTRdPosWithRxEnDlySeeds3 (TechPtr); |
| } else { |
| // Skip RcvEnDly cycle training when current chip select has already gone through that step. |
| // Because a retrain condition can only be detected on a chip select after RcvEnDly cycle training |
| // So when current chip select is equal to RestartChipSel, we don't need to redo RcvEnDly cycle training. |
| // Only redo DQS position training. |
| IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tSkip RcvEnDly Cycle Training on Current Chip Select.\n\n"); |
| RetVal = MemTTrainDQSEdgeDetect (TechPtr); |
| } |
| return RetVal; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function skips WrDatDly training when a retrain condition is just detected |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *ChipSel - Pointer to ChipSel |
| * |
| * @return TRUE |
| */ |
| |
| BOOLEAN |
| MemNHookBfWrDatTrnTN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *ChipSel |
| ) |
| { |
| BOOLEAN RetVal; |
| |
| RetVal = TRUE; |
| if (NBPtr->RdDqsDlyRetrnStat == RDDQSDLY_RTN_ONGOING) { |
| NBPtr->RdDqsDlyRetrnStat = RDDQSDLY_RTN_NEEDED; |
| // Clear chipsel value to force a restart of Rd Dqs Training |
| if (NBPtr->CsPerDelay == 1) { |
| *(UINT8 *) ChipSel = 0xFF; |
| } else { |
| *(UINT8 *) ChipSel = 0xFE; |
| } |
| |
| RetVal = FALSE; |
| } else if (((INT8) NBPtr->TechPtr->ChipSel) < NBPtr->TechPtr->RestartChipSel) { |
| IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSkip WrDatDly Training on Current Chip Select.\n\n"); |
| // Skip WrDatDly training when current chip select has gone through WrDatDly procedure |
| // A retrain is detected during RdDqsDly training, so if RestartChipSel is equal to current |
| // chip select, then WrDatDly has not been started for current chip select in the previous cycle. |
| // However, RcvEnDly cycle training has been done for current chip select. |
| // So we don't need to do RcvEnDly cycle training when current chip select is equal to RestartChipSel |
| // but we need to do WrDatDly training for current chip select. |
| RetVal = FALSE; |
| } |
| |
| // when return is FALSE, WrDatDly training will be skipped |
| |
| return RetVal; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function sets up output driver and write leveling mode in MR1 during WL |
| * |
| * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| * @param[in,out] *Value - MR1 value |
| * |
| * @return TRUE |
| */ |
| |
| BOOLEAN |
| MemNWLMR1TN ( |
| IN OUT MEM_NB_BLOCK *NBPtr, |
| IN OUT VOID *Value |
| ) |
| { |
| BOOLEAN Target; |
| |
| // For the target rank of the target DIMM, enable write leveling mode and enable the output driver. |
| // For all other ranks of the target DIMM, enable write leveling mode and disable the output driver. |
| Target = (BOOLEAN) (*(UINT16 *) Value >> 7) & 1; |
| |
| if (NBPtr->CsPerDelay == 1) { |
| // Clear Qoff and reset it based on TN requirement |
| *(UINT16 *) Value &= ~((UINT16) 1 << 12); |
| |
| if (!Target) { |
| *(UINT16 *) Value |= (((UINT16) 1 << 7) | ((UINT16) 1 << 12)); |
| } |
| } |
| |
| return TRUE; |
| } |