diff options
Diffstat (limited to 'src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/mttdimbt.c')
-rw-r--r-- | src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/mttdimbt.c | 1279 |
1 files changed, 1279 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/mttdimbt.c b/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/mttdimbt.c new file mode 100644 index 0000000000..ced3d938da --- /dev/null +++ b/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/mttdimbt.c @@ -0,0 +1,1279 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mttdimmbt.c + * + * Technology Dimm Based Training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Tech) + * @e \$Revision: 38442 $ @e \$Date: 2010-09-24 06:39:57 +0800 (Fri, 24 Sep 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. + * + * *************************************************************************** + * + */ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + +#include "AGESA.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "GeneralServices.h" +#include "heapManager.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_TECH_MTTDIMBT_FILECODE + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ +#define MAX_BYTELANES 8 /* 8 byte lanes */ +#define MAX_DELAYS 9 /* 8 data bytes + 1 ECC byte */ +#define MAX_DIMMS 4 /* 4 DIMMs per channel */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +VOID +STATIC +MemTInitDqsPos4RcvrEnByte ( + IN OUT MEM_TECH_BLOCK *TechPtr + ); + +VOID +STATIC +MemTSetRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly + ); + +VOID +STATIC +MemTLoadRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ); + +BOOLEAN +STATIC +MemTSaveRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly, + IN UINT16 CmpResultRank0, + IN UINT16 CmpResultRank1 + ); + +VOID +STATIC +MemTResetDctWrPtrByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ); + +UINT16 +STATIC +MemTCompare1ClPatternByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Buffer[], + IN UINT8 Pattern[] + ); + +VOID +STATIC +MemTSkipChipSelPass1Byte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN OUT UINT8 *ChipSelPtr + ); + +VOID +STATIC +MemTSkipChipSelPass2Byte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN OUT UINT8 *ChipSelPtr + ); + +UINT8 +STATIC +MemTMaxByteLanesByte (VOID); + +UINT8 +STATIC +MemTDlyTableWidthByte (VOID); + +VOID +STATIC +MemTSetDqsDelayCsrByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 ByteLane, + IN UINT8 Dly + ); + +VOID +STATIC +MemTDqsWindowSaveByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 ByteLane, + IN UINT8 DlyMin, + IN UINT8 DlyMax + ); + +BOOLEAN +STATIC +MemTFindMaxRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + OUT UINT8 *ChipSel + ); + +UINT16 +STATIC +MemTCompare1ClPatternOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Buffer[], + IN UINT8 Pattern[], + IN UINT8 Side, + IN UINT8 Receiver, + IN BOOLEAN Side1En + ); + +VOID +STATIC +MemTLoadRcvrEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ); + +VOID +STATIC +MemTSetRcvrEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly + ); + +VOID +STATIC +MemTLoadInitialRcvEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ); +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function enables byte based training if called + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + */ + +VOID +MemTDimmByteTrainInit ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 Dct; + UINT8 Channel; + UINT8 DctCount; + UINT8 ChannelCount; + DIE_STRUCT *MCTPtr; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + MEM_NB_BLOCK *NBPtr; + + NBPtr = TechPtr->NBPtr; + MCTPtr = NBPtr->MCTPtr; + + TechPtr->InitDQSPos4RcvrEn = MemTInitDqsPos4RcvrEnByte; + TechPtr->SetRcvrEnDly = MemTSetRcvrEnDlyByte; + TechPtr->LoadRcvrEnDly = MemTLoadRcvrEnDlyByte; + TechPtr->SaveRcvrEnDly = MemTSaveRcvrEnDlyByte; + TechPtr->SaveRcvrEnDlyFilter = MemTSaveRcvrEnDlyByteFilterOpt; + TechPtr->ResetDCTWrPtr = MemTResetDctWrPtrByte; + TechPtr->Compare1ClPattern = MemTCompare1ClPatternByte; + TechPtr->SkipChipSelPass1 = MemTSkipChipSelPass1Byte; + TechPtr->SkipChipSelPass2 = MemTSkipChipSelPass2Byte; + TechPtr->MaxByteLanes = MemTMaxByteLanesByte; + TechPtr->DlyTableWidth = MemTDlyTableWidthByte; + TechPtr->SetDQSDelayCSR = MemTSetDqsDelayCsrByte; + TechPtr->DQSWindowSave = MemTDqsWindowSaveByte; + TechPtr->FindMaxDlyForMaxRdLat = MemTFindMaxRcvrEnDlyByte; + TechPtr->Compare1ClPatternOpt = MemTCompare1ClPatternOptByte; + TechPtr->LoadRcvrEnDlyOpt = MemTLoadRcvrEnDlyOptByte; + TechPtr->SetRcvrEnDlyOpt = MemTSetRcvrEnDlyOptByte; + TechPtr->InitializeVariablesOpt = MemTInitializeVariablesOptByte; + TechPtr->GetMaxValueOpt = MemTGetMaxValueOptByte; + TechPtr->SetSweepErrorOpt = MemTSetSweepErrorOptByte; + TechPtr->CheckRcvrEnDlyLimitOpt = MemTCheckRcvrEnDlyLimitOptByte; + TechPtr->LoadInitialRcvrEnDlyOpt = MemTLoadInitialRcvEnDlyOptByte; + // Dynamically allocate buffers for storing trained timings. + DctCount = MCTPtr->DctCount; + ChannelCount = MCTPtr->DctData[0].ChannelCount; + AllocHeapParams.RequestedBufferSize = ((DctCount * ChannelCount) * + ((MAX_DIMMS * MAX_DELAYS * NUMBER_OF_DELAY_TABLES) + + (MAX_DELAYS * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) + ) + ); + AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_TRN_DATA_HANDLE, MCTPtr->NodeId, 0, 0); + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) { + for (Dct = 0; Dct < DctCount; Dct++) { + for (Channel = 0; Channel < ChannelCount; Channel++) { + MCTPtr->DctData[Dct].ChData[Channel].RowCount = MAX_DIMMS; + MCTPtr->DctData[Dct].ChData[Channel].ColumnCount = MAX_DELAYS; + + MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlys = (UINT16 *) AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2; + MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].WrDatDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlys = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); + MCTPtr->DctData[Dct].ChData[Channel].FailingBitMask = AllocHeapParams.BufferPtr; + AllocHeapParams.BufferPtr += (MAX_CS_PER_CHANNEL * MAX_DELAYS); + } + } + } else { + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_DYN_STORING_OF_TRAINED_TIMINGS, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + SetMemError (AGESA_FATAL, MCTPtr); + ASSERT(FALSE); // Could not dynamically allocate buffers for storing trained timings + } +} + + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function initializes the DQS Positions in preparation for Receiver Enable Training. + * Write Position is no delay, Read Position is 1/2 Memclock delay + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + */ + +VOID +STATIC +MemTInitDqsPos4RcvrEnByte ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 Dimm; + UINT8 ByteLane; + UINT8 WrDqs; + + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { + for (ByteLane = 0; ByteLane < MAX_DELAYS; ByteLane++) { + WrDqs = TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane]; + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqs); + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), 0x3F); + } + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs DqsRcvEnDly to additional index for DQS receiver enabled training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * @param[in] RcvEnDly - receiver enable delay to be saved + */ + +VOID +STATIC +MemTSetRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly + ) +{ + UINT8 ByteLane; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) { + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), RcvEnDly); + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function loads the DqsRcvEnDly from saved data and program to additional index + * for DQS receiver enabled training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * + */ + +VOID +STATIC +MemTLoadRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ) +{ + UINT8 i; + UINT8 Dimm; + UINT16 Saved; + CH_DEF_STRUCT *ChannelPtr; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + ChannelPtr = TechPtr->NBPtr->ChannelPtr; + + Dimm = Receiver >> 1; + Saved = TechPtr->DqsRcvEnSaved; + for (i = 0; i < MAX_BYTELANES; i++) { + if (Saved & 1) { + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i), + ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]); + } + Saved >>= 1; + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function saves passing DqsRcvEnDly values to the stack + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * @param[in] RcvEnDly - receiver enable delay to be saved + * @param[in] CmpResultRank0 - compare result for Rank 0 + * @param[in] CmpResultRank1 - compare result for Rank 1 + * + * @return TRUE - All bytelanes pass + * @return FALSE - Some bytelanes fail + */ + +BOOLEAN +STATIC +MemTSaveRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly, + IN UINT16 CmpResultRank0, + IN UINT16 CmpResultRank1 + ) +{ + UINT8 i; + UINT8 Passed; + UINT8 Saved; + UINT8 Mask; + UINT8 Dimm; + CH_DEF_STRUCT *ChannelPtr; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + ChannelPtr = TechPtr->NBPtr->ChannelPtr; + + Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF); + + Saved = (UINT8) (TechPtr->DqsRcvEnSaved & Passed); //@attention - false passes filter (subject to be replaced with a better solution) + Dimm = Receiver >> 1; + Mask = 1; + for (i = 0; i < MAX_BYTELANES; i++) { + if (Passed & Mask) { + if (!(Saved & Mask)) { + ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20; // @attention -1 pass only + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tBL %d = %02x", i, RcvEnDly + 0x20); + } + Saved |= Mask; + } + Mask <<= 1; + } + TechPtr->DqsRcvEnSaved = Saved; + + if (Saved == 0xFF) { + return TRUE; + } else { + return FALSE; + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function performs a filtering functionality and saves passing DqsRcvEnDly + * values to the stack + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * @param[in] RcvEnDly - receiver enable delay to be saved + * @param[in] CmpResultRank0 - compare result for Rank 0 + * @param[in] CmpResultRank1 - compare result for Rank 1 + * + * @return TRUE - All bytelanes pass + * @return FALSE - Some bytelanes fail + */ + +BOOLEAN +MemTSaveRcvrEnDlyByteFilter ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly, + IN UINT16 CmpResultRank0, + IN UINT16 CmpResultRank1 + ) +{ + UINT8 i; + UINT8 Passed; + UINT8 Saved; + UINT8 Mask; + UINT8 Dimm; + UINT8 MaxFilterDly; + CH_DEF_STRUCT *ChannelPtr; + MEM_DCT_CACHE *DctCachePtr; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + ChannelPtr = TechPtr->NBPtr->ChannelPtr; + DctCachePtr = TechPtr->NBPtr->DctCachePtr; + + MaxFilterDly = TechPtr->MaxFilterDly; + Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF); + + Dimm = Receiver >> 1; + Saved = (UINT8) TechPtr->DqsRcvEnSaved; + Mask = 1; + for (i = 0; i < MAX_BYTELANES; i++) { + if ((Passed & Mask) != 0) { + DctCachePtr->RcvEnDlyCounts [i] += 1; + if ((Saved & Mask) == 0) { + ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20; + Saved |= Mask; + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tBL %d = %02x", i, RcvEnDly + 0x20); + } + } else { + if (DctCachePtr->RcvEnDlyCounts [i] <= MaxFilterDly) { + DctCachePtr->RcvEnDlyCounts [i] = 0; + Saved &= ~Mask; + } + } + Mask <<= 1; + } + + //----------------------- + TechPtr->DqsRcvEnSaved = (UINT16) Saved; + + Saved = 0; + for (i = 0; i < MAX_BYTELANES; i++) { + if (DctCachePtr->RcvEnDlyCounts [i] >= MaxFilterDly) { + Saved |= (UINT8) 1 << i; + } + } + + if (Saved == 0xFF) { + return TRUE; + } else { + return FALSE; + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function compares test pattern with data in buffer and return a pass/fail bitmap + * for 8 Bytes + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Buffer[] - Buffer data from DRAM (Measured data from DRAM) to compare + * @param[in] Pattern[] - Pattern (Expected data in ROM/CACHE) to compare against + * + * @return PASS - Bit map of results of comparison + */ + +UINT16 +STATIC +MemTCompare1ClPatternByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Buffer[], + IN UINT8 Pattern[] + ) +{ + UINT16 i; + UINT16 j; + UINT16 Pass; + DIE_STRUCT *MCTPtr; + + MCTPtr = TechPtr->NBPtr->MCTPtr; + if (MCTPtr->GangedMode && MCTPtr->Dct) { + j = 8; + } else { + j = 0; + } + + Pass = 0xFFFF; + IDS_HDT_CONSOLE (MEM_FLOW, " -"); + for (i = 0; i < 8; i++) { + if (Buffer[j] != Pattern[j]) { + // if bytelane n fails + Pass &= ~((UINT16)1 << (j % 8)); // clear bit n + } + IDS_HDT_CONSOLE (MEM_FLOW, " %c", (Buffer[j] == Pattern[j]) ? 'P' : '.'); + j++; + } + + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t -"); + for (i = 0, j -= 8; i < 8; i++, j++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t -"); + for (i = 0, j -= 8; i < 8; i++, j++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\n"); + ); + + return Pass; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * The function resets the DCT input buffer write pointer. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Chip select + * + */ + +VOID +STATIC +MemTResetDctWrPtrByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ) +{ + UINT8 i; + UINT16 RcvEnDly; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + for (i = 0; i < MAX_BYTELANES; i++) { + RcvEnDly = (UINT16) TechPtr->NBPtr->GetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i)); + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i), RcvEnDly); + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function skips odd chip select if training at 800MT or above. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *ChipSelPtr - Pointer to variable contains Chip select index + * + */ + +VOID +STATIC +MemTSkipChipSelPass1Byte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN OUT UINT8 *ChipSelPtr + ) +{ + MEM_NB_BLOCK *NBPtr; + + NBPtr = TechPtr->NBPtr; + // if the even chip select failed training, need to set CsTrainFail for odd chip select if present. + if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ((*ChipSelPtr) + 1))) { + if (NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16)1 << *ChipSelPtr)) { + NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ((*ChipSelPtr) + 1); + NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader); + } + } + (*ChipSelPtr)++; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * MemTSkipChipSelPass2Byte: + * + * This function skips odd chip select if training at 800MT or above. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in,out] *ChipSelPtr - Pointer to variable contains Chip select index + * + */ + +VOID +STATIC +MemTSkipChipSelPass2Byte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN OUT UINT8 *ChipSelPtr + ) +{ + if (*ChipSelPtr & 1) { + *ChipSelPtr = MAX_CS_PER_CHANNEL; // skip all successions + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the maximum number of byte lanes + * + * @return Max number of Bytelanes + */ + +UINT8 +STATIC +MemTMaxByteLanesByte (VOID) +{ + return MAX_BYTELANES; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the width of the delay tables (eg. RcvEnDlys, WrDqsDlys,...) + * + * @return Delay table width in bytes + */ + +UINT8 +STATIC +MemTDlyTableWidthByte (VOID) +{ + return MAX_DELAYS; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function writes the Delay value to a certain byte lane + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] ByteLane - Bytelane number being targeted + * @param[in] Dly - Delay value + * + */ + +VOID +STATIC +MemTSetDqsDelayCsrByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 ByteLane, + IN UINT8 Dly + ) +{ + UINT8 Reg; + UINT8 Dimm; + + ASSERT (ByteLane <= MAX_BYTELANES); + + if (!(TechPtr->DqsRdWrPosSaved & ((UINT8)1 << ByteLane))) { + Dimm = (TechPtr->ChipSel >> 1); + + if (TechPtr->Direction == DQS_WRITE_DIR) { + Dly = Dly + ((UINT8) TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane]); + Reg = AccessWrDatDly; + } else { + Reg = AccessRdDqsDly; + } + + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, Reg, DIMM_BYTE_ACCESS (Dimm, ByteLane), Dly); + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the trained DQS delay for the specified byte lane + * and stores its DQS window for reference. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] ByteLane - Bytelane number being targeted + * @param[in] DlyMin - Minimum delay value + * @param[in] DlyMax- Maximum delay value + * + */ + +VOID +STATIC +MemTDqsWindowSaveByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 ByteLane, + IN UINT8 DlyMin, + IN UINT8 DlyMax + ) +{ + UINT8 DqsDelay; + UINT8 Dimm; + CH_DEF_STRUCT *ChanPtr; + + ASSERT (ByteLane <= MAX_BYTELANES); + ChanPtr = TechPtr->NBPtr->ChannelPtr; + + DqsDelay = ((DlyMin + DlyMax + 1) / 2) & 0x3F; + MemTSetDqsDelayCsrByte (TechPtr, ByteLane, DqsDelay); + TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane; + TechPtr->DqsRdWrPosSaved |= 0xFF00; + + Dimm = (TechPtr->ChipSel / 2) * MAX_DELAYS + ByteLane; + if (TechPtr->Direction == DQS_READ_DIR) { + ChanPtr->RdDqsDlys[Dimm] = DqsDelay; + } else { + ChanPtr->WrDatDlys[Dimm] = DqsDelay + ChanPtr->WrDqsDlys[Dimm]; + } + + if (TechPtr->Direction == DQS_READ_DIR) { + ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin; + ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax; + } else { + ChanPtr->WrDatMinDlys[ByteLane] = DlyMin; + ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax; + } + +} + + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the DIMM that has the largest receiver enable delay. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[out] *ChipSel - Pointer to the Chip select that has the largest receiver enable delay. + * + * @return TRUE - A chip select can be found. + * @return FALSE - A chip select cannot be found. + */ + +BOOLEAN +STATIC +MemTFindMaxRcvrEnDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + OUT UINT8 *ChipSel + ) +{ + UINT8 Dimm; + UINT8 ByteLane; + UINT16 RcvEnDly; + UINT16 MaxDly; + UINT8 MaxDlyDimm; + BOOLEAN RetVal; + + MEM_NB_BLOCK *NBPtr; + CH_DEF_STRUCT *ChannelPtr; + + NBPtr = TechPtr->NBPtr; + ChannelPtr = NBPtr->ChannelPtr; + + RetVal = FALSE; + MaxDly = 0; + MaxDlyDimm = 0; + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { + if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) { + // Only choose the dimm that does not fail training + for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) { + RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane]; + if (RcvEnDly > MaxDly) { + MaxDly = RcvEnDly; + MaxDlyDimm = Dimm; + RetVal = TRUE; + } + } + } + } + + if (NBPtr->MCTPtr->Status[Sb128bitmode] != 0) { + //The RcvrEnDlys of DCT1 DIMMs should also be considered while ganging. + NBPtr->SwitchDCT (NBPtr, 1); + ChannelPtr = NBPtr->ChannelPtr; + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { + for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) { + RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane]; + if (RcvEnDly > MaxDly) { + MaxDly = RcvEnDly; + MaxDlyDimm = Dimm; + } + } + } + NBPtr->SwitchDCT (NBPtr, 0); + } + + TechPtr->MaxDlyForMaxRdLat = MaxDly; + *ChipSel = (MaxDlyDimm * 2); + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the DIMM that has the largest receiver enable delay + Read DQS Delay. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[out] *ChipSel - Pointer to the Chip select that has the largest receiver enable delay + * + Read DQS Delay. + * + * @return TRUE - A chip select can be found. + * @return FALSE - A chip select cannot be found. + */ + +BOOLEAN +MemTFindMaxRcvrEnDlyRdDqsDlyByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + OUT UINT8 *ChipSel + ) +{ + UINT8 Dimm; + UINT8 ByteLane; + UINT16 RcvEnDly; + UINT16 RdDqsDly; + UINT16 TotalDly; + UINT16 MaxDly; + UINT8 MaxDlyDimm; + BOOLEAN RetVal; + + MEM_NB_BLOCK *NBPtr; + CH_DEF_STRUCT *ChannelPtr; + + NBPtr = TechPtr->NBPtr; + ChannelPtr = NBPtr->ChannelPtr; + + RetVal = FALSE; + MaxDly = 0; + MaxDlyDimm = 0; + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { + if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) { + // Only choose the dimm that does not fail training + for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) { + RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane]; + // Before Dqs Position Training, this value is 0. So the maximum value for + // RdDqsDly needs to be added later when calculating the MaxRdLatency value + // after RcvEnDly training but before DQS Position Training. + RdDqsDly = ChannelPtr->RdDqsDlys[Dimm * MAX_DELAYS + ByteLane]; + TotalDly = RcvEnDly + (RdDqsDly >> 1); + if (TotalDly > MaxDly) { + MaxDly = TotalDly; + MaxDlyDimm = Dimm; + RetVal = TRUE; + } + } + } + } + + TechPtr->MaxDlyForMaxRdLat = MaxDly; + *ChipSel = (MaxDlyDimm * 2); + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function compares test pattern with data in buffer and return a pass/fail bitmap + * for 8 Bytes for optimized receiver enable training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Buffer[] - Buffer data from DRAM (Measured data from DRAM) to compare + * @param[in] Pattern[] - Pattern (Expected data in ROM/CACHE) to compare against + * @param[in] Side - current side being targeted + * @param[in] Receiver - Current receiver value + * @param[in] Side1En - Indicates if the second side of the DIMM is being used + * @return PASS - Bit map of results of comparison + */ + +UINT16 +STATIC +MemTCompare1ClPatternOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Buffer[], + IN UINT8 Pattern[], + IN UINT8 Side, + IN UINT8 Receiver, + IN BOOLEAN Side1En + ) +{ + UINT16 i; + UINT16 j; + UINT16 Pass; + DIE_STRUCT *MCTPtr; + CH_DEF_STRUCT *ChannelPtr; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + ChannelPtr = TechPtr->NBPtr->ChannelPtr; + MCTPtr = TechPtr->NBPtr->MCTPtr; + + if (MCTPtr->GangedMode && MCTPtr->Dct) { + j = 8; + } else { + j = 0; + } + + Pass = 0xFFFF; + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDelay[BL] -"); + for (i = 0; i < 8; i++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x", TechPtr->RcvrEnDlyOpt[i] & 0xFF); + if (Buffer[j] != Pattern[j]) { + // if bytelane n fails + Pass &= ~((UINT16)1 << (j % 8)); // clear bit n + TechPtr->DqsRcvEnFirstPassValOpt[i] = 0; + TechPtr->GetFirstPassValOpt[i] = FALSE; + TechPtr->IncBy1ForNextCountOpt[i] = FALSE; + TechPtr->DqsRcvEnSavedOpt[i] = FALSE; + if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) { + if (Side == ((Side1En ? 4 : 2) - 1)) { + TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT; + } + } + } else { + if (TechPtr->FilterSidePassCountOpt[i] == ((Side1En ? 4 : 2) - 1)) { + //Only apply filter if all sides have passed + if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) { + if (TechPtr->GetFirstPassValOpt[i] == FALSE) { + // This is the first Pass, mark the start of filter check + TechPtr->DqsRcvEnFirstPassValOpt[i] = TechPtr->RcvrEnDlyOpt[i]; + TechPtr->GetFirstPassValOpt[i] = TRUE; + TechPtr->IncBy1ForNextCountOpt[i] = FALSE; + TechPtr->RcvrEnDlyOpt[i]++; + } else { + if ((TechPtr->RcvrEnDlyOpt[i] - TechPtr->DqsRcvEnFirstPassValOpt[i]) < FILTER_WINDOW_SIZE) { + if (TechPtr->IncBy1ForNextCountOpt[i] == FALSE) { + TechPtr->RcvrEnDlyOpt[i] += FILTER_SECOND_STAGE_COUNT; + TechPtr->IncBy1ForNextCountOpt[i] = TRUE; + } else { + TechPtr->RcvrEnDlyOpt[i]++; + TechPtr->IncBy1ForNextCountOpt[i] = FALSE; + } + } else { + // End sweep and add offset to first pass + TechPtr->MaxRcvrEnDlyBlOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i]; + TechPtr->RcvrEnDlyOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i] + FILTER_OFFSET_VALUE; + TechPtr->FilterStatusOpt[i] = DONE_FILTER; + TechPtr->FilterCountOpt++; + } + } + } else { + TechPtr->FilterSidePassCountOpt[i]++; + } + } else { + if (TechPtr->GetFirstPassValOpt[i] == FALSE) { + if (Side == ((Side1En ? 4 : 2) - 1)) { + TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT; + } + } + TechPtr->FilterSidePassCountOpt[i]++; + } + TechPtr->DqsRcvEnSavedOpt[i] = TRUE; + ChannelPtr->RcvEnDlys[(Receiver >> 1) * MAX_DELAYS + i] = TechPtr->RcvrEnDlyOpt[i]; + } + if (Side == ((Side1En ? 4 : 2) - 1)) { + TechPtr->FilterSidePassCountOpt[i] = 0; + } + if (TechPtr->RcvrEnDlyOpt[i] >= TechPtr->RcvrEnDlyLimitOpt[i]) { + TechPtr->FilterCountOpt++; + } + + j++; + } + + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tPass/Fail -"); + for (i = 0, j -= 8; i < 8; i++, j++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %c", (Buffer[j] == Pattern[j]) ? 'P' : '.'); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Measured -"); + for (i = 0, j -= 8; i < 8; i++, j++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Expected -"); + for (i = 0, j -= 8; i < 8; i++, j++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\n"); + ); + + return Pass; +} +/*----------------------------------------------------------------------------- + * + * This function initializes variables for optimized training. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * ---------------------------------------------------------------------------- + */ +VOID +MemTInitializeVariablesOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 ByteLane; + for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) { + TechPtr->RcvrEnDlyLimitOpt[ByteLane] = FILTER_MAX_REC_EN_DLY_VALUE; // @attention - limit depends on proc type + TechPtr->DqsRcvEnSavedOpt[ByteLane] = FALSE; + TechPtr->RcvrEnDlyOpt[ByteLane] = FILTER_NEW_RECEIVER_START_VALUE; + TechPtr->GetFirstPassValOpt[ByteLane] = FALSE; + TechPtr->DqsRcvEnFirstPassValOpt[ByteLane] = 0; + TechPtr->RevertPassValOpt[ByteLane] = FALSE; + TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] = 0; + TechPtr->FilterStatusOpt[ByteLane] = START_FILTER; + TechPtr->FilterCountOpt = 0; + TechPtr->FilterSidePassCountOpt[ByteLane] = 0; + TechPtr->IncBy1ForNextCountOpt[ByteLane] = FALSE; + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function loads the DqsRcvEnDly from saved data and program to additional index + * for optimized DQS receiver enabled training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * + */ + +VOID +STATIC +MemTLoadRcvrEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ) +{ + UINT8 i; + UINT8 Dimm; + CH_DEF_STRUCT *ChannelPtr; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + ChannelPtr = TechPtr->NBPtr->ChannelPtr; + + Dimm = Receiver >> 1; + for (i = 0; i < 8; i++) { + if (TechPtr->DqsRcvEnSavedOpt[i]) { + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i), + ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]); + } + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs DqsRcvEnDly to additional index for DQS receiver enabled training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + * @param[in] RcvEnDly - receiver enable delay to be saved + */ + +VOID +STATIC +MemTSetRcvrEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT16 RcvEnDly + ) +{ + UINT8 ByteLane; + + ASSERT (Receiver < MAX_CS_PER_CHANNEL); + + for (ByteLane = 0; ByteLane < 8; ByteLane++) { + if (TechPtr->FilterStatusOpt[ByteLane] != DONE_FILTER) { + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), TechPtr->RcvrEnDlyOpt[ByteLane]); + } + } +} +/*----------------------------------------------------------------------------- + * + * This sets any Errors generated from Dly sweep + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] DCT - current DCT + * @param[in] Receiver - current receiver + * + * @return FALSE - Fatal error occurs. + * @return TRUE - No fatal error occurs. + * ---------------------------------------------------------------------------- + */ +BOOLEAN +MemTSetSweepErrorOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver, + IN UINT8 Dct, + IN BOOLEAN ErrorCheck + ) +{ + UINT8 ByteLane; + MEM_DATA_STRUCT *MemPtr; + DIE_STRUCT *MCTPtr; + DCT_STRUCT *DCTPtr; + MEM_NB_BLOCK *NBPtr; + + NBPtr = TechPtr->NBPtr; + MemPtr = NBPtr->MemPtr; + MCTPtr = NBPtr->MCTPtr; + DCTPtr = NBPtr->DCTPtr; + for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) { + if (TechPtr->RcvrEnDlyOpt[ByteLane] == TechPtr->RcvrEnDlyLimitOpt[ByteLane]) { + // no passing window + if (ErrorCheck) { + return FALSE; + } + PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_NO_PASSING_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader); + SetMemError (AGESA_ERROR, MCTPtr); + } + if (TechPtr->RcvrEnDlyOpt[ByteLane] > (TechPtr->RcvrEnDlyLimitOpt[ByteLane] - 1)) { + // passing window too narrow, too far delayed + if (ErrorCheck) { + return FALSE; + } + PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_VALUE_TOO_LARGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader); + SetMemError (AGESA_ERROR, MCTPtr); + DCTPtr->Timings.CsTrainFail |= (UINT16) (3 << Receiver) & DCTPtr->Timings.CsPresent; + MCTPtr->ChannelTrainFail |= (UINT32)1 << Dct; + if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, DCTPtr->Timings.CsTrainFail, &MemPtr->StdHeader)) { + return FALSE; + } + } + } + return TRUE; +} + +/*----------------------------------------------------------------------------- + * + * This function determines the maximum receiver delay value + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @retval MaxRcvrValue - Maximum receiver delay value for all bytelanes + * ---------------------------------------------------------------------------- + */ + +UINT16 +MemTGetMaxValueOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 ByteLane; + UINT16 MaxRcvrValue; + MaxRcvrValue = 0; + for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) { + if (TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] > MaxRcvrValue) { + MaxRcvrValue = TechPtr->MaxRcvrEnDlyBlOpt[ByteLane]; + } + } + MaxRcvrValue += FILTER_OFFSET_VALUE; + return MaxRcvrValue; +} +/*----------------------------------------------------------------------------- + * + * This function determines if the sweep loop should complete. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @retval TRUE - All bytelanes pass + * FALSE - Some bytelanes fail + * ---------------------------------------------------------------------------- + */ + +BOOLEAN +MemTCheckRcvrEnDlyLimitOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + if (TechPtr->FilterCountOpt >= (UINT16)MAX_CS_PER_CHANNEL) { + return TRUE; + } else { + return FALSE; + } +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function load the result of write levelization training into RcvrEnDlyOpt, + * using it as the initial value for Receiver DQS training. + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Receiver - Current Chip select value + */ +VOID +STATIC +MemTLoadInitialRcvEnDlyOptByte ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Receiver + ) +{ + UINT8 ByteLane; + MEM_NB_BLOCK *NBPtr; + + NBPtr = TechPtr->NBPtr; + for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) { + TechPtr->RcvrEnDlyOpt[ByteLane] = NBPtr->ChannelPtr->WrDqsDlys[((Receiver >> 1) * TechPtr->DlyTableWidth ()) + ByteLane]; + } +} |