blob: 79725ccc3adfeb1afec59433f7e890fc2194a911 [file] [log] [blame]
/* $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: 45101 $ @e \$Date: 2011-01-13 00:59:16 +0800 (Thu, 13 Jan 2011) $
*
**/
/*****************************************************************************
*
* 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
);
UINT8
STATIC
MemTFindMinMaxGrossDlyByte (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN TRN_DLY_TYPE TrnDlyType,
IN BOOLEAN IfMax
);
/*----------------------------------------------------------------------------
* 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;
TechPtr->GetMinMaxGrossDly = MemTFindMinMaxGrossDlyByte;
// 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 finds the minimum or maximum gross dly among all the bytes.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in] TrnDlyType - Target Dly type
* @param[in] IfMax - If this is for maximum value or minimum
*
* @return minimum gross dly
*/
UINT8
STATIC
MemTFindMinMaxGrossDlyByte (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN TRN_DLY_TYPE TrnDlyType,
IN BOOLEAN IfMax
)
{
UINT8 i;
UINT8 ByteLane;
UINT16 CsEnabled;
UINT8 MinMaxGrossDly;
UINT8 TrnDly;
MEM_NB_BLOCK *NBPtr;
NBPtr = TechPtr->NBPtr;
CsEnabled = NBPtr->DCTPtr->Timings.CsEnabled;
MinMaxGrossDly = IfMax ? 0 : 0xFF;
for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
if ((CsEnabled & (UINT16) (3 << (i << 1))) != 0) {
for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
TrnDly = (UINT8) (GetTrainDlyFromHeapNb (NBPtr, TrnDlyType, DIMM_BYTE_ACCESS (i, ByteLane)) >> 5);
if ((IfMax && (TrnDly > MinMaxGrossDly)) || (!IfMax && (TrnDly < MinMaxGrossDly))) {
MinMaxGrossDly = TrnDly;
}
}
}
}
return MinMaxGrossDly;
}
/* -----------------------------------------------------------------------------*/
/**
*
* 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];
}
}