| /* $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: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ |
| * |
| **/ |
| /***************************************************************************** |
| * |
| * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of Advanced Micro Devices, Inc. nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * *************************************************************************** |
| * |
| */ |
| |
| /* |
| *---------------------------------------------------------------------------- |
| * MODULES USED |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| |
| |
| #include "AGESA.h" |
| #include "Ids.h" |
| #include "mm.h" |
| #include "mn.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 |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------- |
| * 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; |
| |
| ASSERT ((NBPtr->CsPerDelay == 1) || (NBPtr->CsPerDelay == 2)); |
| |
| 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) + |
| (MAX_DIMMS * MAX_NUMBER_LANES) |
| ) |
| ); |
| |
| if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) { |
| AllocHeapParams.RequestedBufferSize *= 2; |
| } |
| |
| 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].RdDqs2dDlys = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES); |
| 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); |
| if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) { |
| MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlysMemPs1 = (UINT16 *) AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2; |
| MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].WrDatDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].RdDqs2dDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES); |
| MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlysMemPs1 = AllocHeapParams.BufferPtr; |
| AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS); |
| MCTPtr->DctData[Dct].ChData[Channel].FailingBitMaskMemPs1 = 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); |
| if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) { |
| ASSERT (FALSE); |
| } |
| } |
| } |
| (*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 / TechPtr->NBPtr->CsPerDelay; |
| |
| 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 / TechPtr->NBPtr->CsPerDelay) * 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 ChipSelect; |
| UINT8 ByteLane; |
| UINT16 RcvEnDly; |
| UINT16 MaxDly; |
| UINT8 MaxDlyCs; |
| BOOLEAN RetVal; |
| |
| MEM_NB_BLOCK *NBPtr; |
| CH_DEF_STRUCT *ChannelPtr; |
| |
| NBPtr = TechPtr->NBPtr; |
| ChannelPtr = NBPtr->ChannelPtr; |
| |
| RetVal = FALSE; |
| MaxDly = 0; |
| MaxDlyCs = 0; |
| for (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) != 0) { |
| if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) == 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[ChipSelect / NBPtr->CsPerDelay * MAX_DELAYS + ByteLane]; |
| if (RcvEnDly > MaxDly) { |
| MaxDly = RcvEnDly; |
| MaxDlyCs = ChipSelect; |
| 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 (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) { |
| RcvEnDly = ChannelPtr->RcvEnDlys[ChipSelect / NBPtr->CsPerDelay * MAX_DELAYS + ByteLane]; |
| if (RcvEnDly > MaxDly) { |
| MaxDly = RcvEnDly; |
| MaxDlyCs = ChipSelect; |
| } |
| } |
| } |
| NBPtr->SwitchDCT (NBPtr, 0); |
| } |
| |
| TechPtr->MaxDlyForMaxRdLat = MaxDly; |
| *ChipSel = MaxDlyCs; |
| 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 ChipSelect; |
| UINT8 ByteLane; |
| UINT16 RcvEnDly; |
| UINT16 RdDqsDly; |
| UINT16 TotalDly; |
| UINT16 MaxDly; |
| UINT8 MaxDlyCs; |
| BOOLEAN RetVal; |
| |
| MEM_NB_BLOCK *NBPtr; |
| CH_DEF_STRUCT *ChannelPtr; |
| |
| NBPtr = TechPtr->NBPtr; |
| ChannelPtr = NBPtr->ChannelPtr; |
| |
| RetVal = FALSE; |
| MaxDly = 0; |
| MaxDlyCs = 0; |
| for (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) == 0) { |
| // Only choose the dimm that does not fail training |
| for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) { |
| RcvEnDly = ChannelPtr->RcvEnDlys[ChipSelect / NBPtr->CsPerDelay * 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[ChipSelect / NBPtr->CsPerDelay * MAX_DELAYS + ByteLane]; |
| TotalDly = RcvEnDly + (RdDqsDly >> 1); |
| if (TotalDly > MaxDly) { |
| MaxDly = TotalDly; |
| MaxDlyCs = ChipSelect; |
| RetVal = TRUE; |
| } |
| } |
| } |
| } |
| |
| TechPtr->MaxDlyForMaxRdLat = MaxDly; |
| *ChipSel = MaxDlyCs; |
| return RetVal; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function finds the DIMM that has the largest receiver enable delay + Read DQS Delay for UNB |
| * |
| * @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 |
| MemTFindMaxRcvrEnDlyRdDqsDlyByteUnb ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| OUT UINT8 *ChipSel |
| ) |
| { |
| UINT8 ChipSelect; |
| UINT8 ByteLane; |
| UINT16 RcvEnDly; |
| UINT16 RdDqsDly; |
| UINT16 TotalDly; |
| UINT16 MaxDly; |
| UINT8 MaxDlyCs; |
| BOOLEAN RetVal; |
| UINT16 *RcvEnDlyPtr; |
| UINT8 *RdDqsDlyPtr; |
| |
| MEM_NB_BLOCK *NBPtr; |
| CH_DEF_STRUCT *ChannelPtr; |
| |
| NBPtr = TechPtr->NBPtr; |
| ChannelPtr = NBPtr->ChannelPtr; |
| RcvEnDlyPtr = ChannelPtr->RcvEnDlys; |
| RdDqsDlyPtr = ChannelPtr->RdDqsDlys; |
| if (NBPtr->MemPstate == MEMORY_PSTATE1) { |
| RcvEnDlyPtr = ChannelPtr->RcvEnDlysMemPs1; |
| RdDqsDlyPtr = ChannelPtr->RdDqsDlysMemPs1; |
| } |
| |
| RetVal = FALSE; |
| MaxDly = 0; |
| MaxDlyCs = 0; |
| for (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) == 0) { |
| // Only choose the dimm that does not fail training |
| for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) { |
| RcvEnDly = RcvEnDlyPtr[ChipSelect / NBPtr->CsPerDelay * 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 = RdDqsDlyPtr[ChipSelect / NBPtr->CsPerDelay * MAX_DELAYS + ByteLane]; |
| TotalDly = RcvEnDly + ((NBPtr->IsSupported[SwitchRdDqsDlyForMaxRdLatency] && NBPtr->RdDqsDlyForMaxRdLat != 0) ? NBPtr->RdDqsDlyForMaxRdLat: RdDqsDly); |
| if (TotalDly > MaxDly) { |
| MaxDly = TotalDly; |
| MaxDlyCs = ChipSelect; |
| RetVal = TRUE; |
| } |
| } |
| } |
| } |
| |
| TechPtr->MaxDlyForMaxRdLat = MaxDly; |
| *ChipSel = MaxDlyCs; |
| 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 ChipSelect; |
| 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 (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| if ((CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) != 0) { |
| for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) { |
| TrnDly = (UINT8) (GetTrainDlyFromHeapNb (NBPtr, TrnDlyType, DIMM_BYTE_ACCESS (ChipSelect / NBPtr->CsPerDelay, 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)) { |
| ASSERT (FALSE); |
| 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]; |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * This function finds the DIMM that has the largest receiver enable delay that are trained by PMU |
| * |
| * @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 |
| MemTFindMaxRcvrEnDlyTrainedByPmuByte ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| OUT UINT8 *ChipSel |
| ) |
| { |
| UINT8 ChipSelect; |
| BOOLEAN RetVal; |
| UINT16 MaxDly; |
| UINT8 MaxDlyCs; |
| |
| MEM_NB_BLOCK *NBPtr; |
| |
| NBPtr = TechPtr->NBPtr; |
| |
| RetVal = FALSE; |
| MaxDly = 0; |
| MaxDlyCs = 0; |
| for (ChipSelect = 0; ChipSelect < NBPtr->CsPerChannel; ChipSelect = ChipSelect + NBPtr->CsPerDelay) { |
| /// @todo Fix this when BKDG has updated algorithm |
| if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSelect)) != 0) { |
| MaxDly = 0; |
| MaxDlyCs = ChipSelect; |
| RetVal = TRUE; |
| } |
| } |
| |
| TechPtr->MaxDlyForMaxRdLat = MaxDly; |
| *ChipSel = MaxDlyCs; |
| return RetVal; |
| } |