AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / Tech / mttdimbt.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mttdimmbt.c
6  *
7  * Technology Dimm Based Training
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Mem/Tech)
12  * @e \$Revision: 56279 $ @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
13  *
14  **/
15 /*****************************************************************************
16 *
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 *     * Redistributions of source code must retain the above copyright
23 *       notice, this list of conditions and the following disclaimer.
24 *     * Redistributions in binary form must reproduce the above copyright
25 *       notice, this list of conditions and the following disclaimer in the
26 *       documentation and/or other materials provided with the distribution.
27 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 *       its contributors may be used to endorse or promote products derived
29 *       from this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * ***************************************************************************
43 *
44 */
45
46 /*
47  *----------------------------------------------------------------------------
48  *                                MODULES USED
49  *
50  *----------------------------------------------------------------------------
51  */
52
53
54
55 #include "AGESA.h"
56 #include "Ids.h"
57 #include "mm.h"
58 #include "mn.h"
59 #include "mt.h"
60 #include "GeneralServices.h"
61 #include "heapManager.h"
62 #include "Filecode.h"
63 CODE_GROUP (G1_PEICC)
64 RDATA_GROUP (G2_PEI)
65
66 #define FILECODE PROC_MEM_TECH_MTTDIMBT_FILECODE
67
68 /*----------------------------------------------------------------------------
69  *                          DEFINITIONS AND MACROS
70  *
71  *----------------------------------------------------------------------------
72  */
73
74 /*----------------------------------------------------------------------------
75  *                           TYPEDEFS AND STRUCTURES
76  *
77  *----------------------------------------------------------------------------
78  */
79
80 /*----------------------------------------------------------------------------
81  *                        PROTOTYPES OF LOCAL FUNCTIONS
82  *
83  *----------------------------------------------------------------------------
84  */
85
86 VOID
87 STATIC
88 MemTInitDqsPos4RcvrEnByte (
89   IN OUT   MEM_TECH_BLOCK *TechPtr
90   );
91
92 VOID
93 STATIC
94 MemTSetRcvrEnDlyByte (
95   IN OUT   MEM_TECH_BLOCK *TechPtr,
96   IN       UINT8 Receiver,
97   IN       UINT16 RcvEnDly
98   );
99
100 VOID
101 STATIC
102 MemTLoadRcvrEnDlyByte (
103   IN OUT   MEM_TECH_BLOCK *TechPtr,
104   IN       UINT8 Receiver
105   );
106
107 BOOLEAN
108 STATIC
109 MemTSaveRcvrEnDlyByte (
110   IN OUT   MEM_TECH_BLOCK *TechPtr,
111   IN       UINT8 Receiver,
112   IN       UINT16 RcvEnDly,
113   IN       UINT16 CmpResultRank0,
114   IN       UINT16 CmpResultRank1
115   );
116
117 VOID
118 STATIC
119 MemTResetDctWrPtrByte (
120   IN OUT   MEM_TECH_BLOCK *TechPtr,
121   IN       UINT8 Receiver
122   );
123
124 UINT16
125 STATIC
126 MemTCompare1ClPatternByte (
127   IN OUT   MEM_TECH_BLOCK *TechPtr,
128   IN       UINT8 Buffer[],
129   IN       UINT8 Pattern[]
130   );
131
132 VOID
133 STATIC
134 MemTSkipChipSelPass1Byte (
135   IN OUT   MEM_TECH_BLOCK *TechPtr,
136   IN OUT   UINT8 *ChipSelPtr
137   );
138
139 VOID
140 STATIC
141 MemTSkipChipSelPass2Byte (
142   IN OUT   MEM_TECH_BLOCK *TechPtr,
143   IN OUT   UINT8 *ChipSelPtr
144   );
145
146 UINT8
147 STATIC
148 MemTMaxByteLanesByte ( VOID );
149
150 UINT8
151 STATIC
152 MemTDlyTableWidthByte ( VOID );
153
154 VOID
155 STATIC
156 MemTSetDqsDelayCsrByte (
157   IN OUT   MEM_TECH_BLOCK *TechPtr,
158   IN       UINT8 ByteLane,
159   IN       UINT8 Dly
160   );
161
162 VOID
163 STATIC
164 MemTDqsWindowSaveByte (
165   IN OUT   MEM_TECH_BLOCK *TechPtr,
166   IN       UINT8 ByteLane,
167   IN       UINT8 DlyMin,
168   IN       UINT8 DlyMax
169   );
170
171 BOOLEAN
172 STATIC
173 MemTFindMaxRcvrEnDlyByte (
174   IN OUT   MEM_TECH_BLOCK *TechPtr,
175      OUT   UINT8 *ChipSel
176   );
177
178 UINT16
179 STATIC
180 MemTCompare1ClPatternOptByte (
181   IN OUT   MEM_TECH_BLOCK *TechPtr,
182   IN       UINT8 Buffer[],
183   IN       UINT8 Pattern[],
184   IN       UINT8 Side,
185   IN       UINT8 Receiver,
186   IN       BOOLEAN Side1En
187   );
188
189 VOID
190 STATIC
191 MemTLoadRcvrEnDlyOptByte (
192   IN OUT   MEM_TECH_BLOCK *TechPtr,
193   IN       UINT8 Receiver
194   );
195
196 VOID
197 STATIC
198 MemTSetRcvrEnDlyOptByte (
199   IN OUT   MEM_TECH_BLOCK *TechPtr,
200   IN       UINT8 Receiver,
201   IN       UINT16 RcvEnDly
202   );
203
204 VOID
205 STATIC
206 MemTLoadInitialRcvEnDlyOptByte (
207   IN OUT   MEM_TECH_BLOCK *TechPtr,
208   IN       UINT8 Receiver
209   );
210
211 UINT8
212 STATIC
213 MemTFindMinMaxGrossDlyByte (
214   IN OUT   MEM_TECH_BLOCK *TechPtr,
215   IN       TRN_DLY_TYPE TrnDlyType,
216   IN       BOOLEAN IfMax
217   );
218 /*----------------------------------------------------------------------------
219  *                            EXPORTED FUNCTIONS
220  *
221  *----------------------------------------------------------------------------
222  */
223
224
225 /* -----------------------------------------------------------------------------*/
226 /**
227  *
228  *   This function enables byte based training if called
229  *
230  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
231  *
232  */
233
234 VOID
235 MemTDimmByteTrainInit (
236   IN OUT   MEM_TECH_BLOCK *TechPtr
237   )
238 {
239   UINT8 Dct;
240   UINT8 Channel;
241   UINT8 DctCount;
242   UINT8 ChannelCount;
243   DIE_STRUCT *MCTPtr;
244   ALLOCATE_HEAP_PARAMS AllocHeapParams;
245   MEM_NB_BLOCK  *NBPtr;
246
247   NBPtr = TechPtr->NBPtr;
248   MCTPtr = NBPtr->MCTPtr;
249
250   TechPtr->InitDQSPos4RcvrEn = MemTInitDqsPos4RcvrEnByte;
251   TechPtr->SetRcvrEnDly = MemTSetRcvrEnDlyByte;
252   TechPtr->LoadRcvrEnDly = MemTLoadRcvrEnDlyByte;
253   TechPtr->SaveRcvrEnDly = MemTSaveRcvrEnDlyByte;
254   TechPtr->SaveRcvrEnDlyFilter = MemTSaveRcvrEnDlyByteFilterOpt;
255   TechPtr->ResetDCTWrPtr = MemTResetDctWrPtrByte;
256   TechPtr->Compare1ClPattern = MemTCompare1ClPatternByte;
257   TechPtr->SkipChipSelPass1 = MemTSkipChipSelPass1Byte;
258   TechPtr->SkipChipSelPass2 = MemTSkipChipSelPass2Byte;
259   TechPtr->MaxByteLanes = MemTMaxByteLanesByte;
260   TechPtr->DlyTableWidth = MemTDlyTableWidthByte;
261   TechPtr->SetDQSDelayCSR = MemTSetDqsDelayCsrByte;
262   TechPtr->DQSWindowSave = MemTDqsWindowSaveByte;
263   TechPtr->FindMaxDlyForMaxRdLat = MemTFindMaxRcvrEnDlyByte;
264   TechPtr->Compare1ClPatternOpt = MemTCompare1ClPatternOptByte;
265   TechPtr->LoadRcvrEnDlyOpt = MemTLoadRcvrEnDlyOptByte;
266   TechPtr->SetRcvrEnDlyOpt = MemTSetRcvrEnDlyOptByte;
267   TechPtr->InitializeVariablesOpt = MemTInitializeVariablesOptByte;
268   TechPtr->GetMaxValueOpt = MemTGetMaxValueOptByte;
269   TechPtr->SetSweepErrorOpt = MemTSetSweepErrorOptByte;
270   TechPtr->CheckRcvrEnDlyLimitOpt = MemTCheckRcvrEnDlyLimitOptByte;
271   TechPtr->LoadInitialRcvrEnDlyOpt = MemTLoadInitialRcvEnDlyOptByte;
272   TechPtr->GetMinMaxGrossDly = MemTFindMinMaxGrossDlyByte;
273   // Dynamically allocate buffers for storing trained timings.
274   DctCount = MCTPtr->DctCount;
275   ChannelCount = MCTPtr->DctData[0].ChannelCount;
276   AllocHeapParams.RequestedBufferSize = ((DctCount * ChannelCount) *
277                                          ((MAX_DIMMS * MAX_DELAYS * NUMBER_OF_DELAY_TABLES) +
278                                           (MAX_DELAYS * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) +
279                                            (MAX_DIMMS * MAX_NUMBER_LANES)
280                                           )
281                                          );
282
283   if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) {
284     AllocHeapParams.RequestedBufferSize *= 2;
285   }
286
287   AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_TRN_DATA_HANDLE, MCTPtr->NodeId, 0, 0);
288   AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
289   if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) {
290     for (Dct = 0; Dct < DctCount; Dct++) {
291       for (Channel = 0; Channel < ChannelCount; Channel++) {
292         MCTPtr->DctData[Dct].ChData[Channel].RowCount = MAX_DIMMS;
293         MCTPtr->DctData[Dct].ChData[Channel].ColumnCount = MAX_DELAYS;
294
295         MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlys = (UINT16 *) AllocHeapParams.BufferPtr;
296         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2;
297         MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlys = AllocHeapParams.BufferPtr;
298         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
299         MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = AllocHeapParams.BufferPtr;
300         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
301         MCTPtr->DctData[Dct].ChData[Channel].WrDatDlys = AllocHeapParams.BufferPtr;
302         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
303         MCTPtr->DctData[Dct].ChData[Channel].RdDqs__Dlys = AllocHeapParams.BufferPtr;
304         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES);
305         MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlys = AllocHeapParams.BufferPtr;
306         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
307         MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlys = AllocHeapParams.BufferPtr;
308         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
309         MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlys = AllocHeapParams.BufferPtr;
310         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
311         MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlys = AllocHeapParams.BufferPtr;
312         AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
313         MCTPtr->DctData[Dct].ChData[Channel].FailingBitMask = AllocHeapParams.BufferPtr;
314         AllocHeapParams.BufferPtr += (MAX_CS_PER_CHANNEL * MAX_DELAYS);
315         if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) {
316           MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlysMemPs1 = (UINT16 *) AllocHeapParams.BufferPtr;
317           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2;
318           MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlysMemPs1 = AllocHeapParams.BufferPtr;
319           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
320           MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlysMemPs1 = AllocHeapParams.BufferPtr;
321           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
322           MCTPtr->DctData[Dct].ChData[Channel].WrDatDlysMemPs1 = AllocHeapParams.BufferPtr;
323           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
324           MCTPtr->DctData[Dct].ChData[Channel].RdDqs__DlysMemPs1 = AllocHeapParams.BufferPtr;
325           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES);
326           MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlysMemPs1 = AllocHeapParams.BufferPtr;
327           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
328           MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlysMemPs1 = AllocHeapParams.BufferPtr;
329           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
330           MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlysMemPs1 = AllocHeapParams.BufferPtr;
331           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
332           MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlysMemPs1 = AllocHeapParams.BufferPtr;
333           AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
334           MCTPtr->DctData[Dct].ChData[Channel].FailingBitMaskMemPs1 = AllocHeapParams.BufferPtr;
335           AllocHeapParams.BufferPtr += (MAX_CS_PER_CHANNEL * MAX_DELAYS);
336
337         }
338       }
339     }
340   } else {
341     PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_DYN_STORING_OF_TRAINED_TIMINGS, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
342     SetMemError (AGESA_FATAL, MCTPtr);
343     ASSERT(FALSE);  // Could not dynamically allocate buffers for storing trained timings
344   }
345 }
346
347
348 /* -----------------------------------------------------------------------------*/
349 /**
350  *
351  *  This function initializes the DQS Positions in preparation for Receiver Enable Training.
352  *  Write Position is no delay, Read Position is 1/2 Memclock delay
353  *
354  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
355  *
356  */
357
358 VOID
359 STATIC
360 MemTInitDqsPos4RcvrEnByte (
361   IN OUT   MEM_TECH_BLOCK *TechPtr
362   )
363 {
364   UINT8 Dimm;
365   UINT8 ByteLane;
366   UINT8 WrDqs;
367
368   for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
369     for (ByteLane = 0; ByteLane < MAX_DELAYS; ByteLane++) {
370       WrDqs = TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane];
371       TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqs);
372       TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), 0x3F);
373     }
374   }
375 }
376
377 /* -----------------------------------------------------------------------------*/
378 /**
379  *
380  *  This function programs DqsRcvEnDly to additional index for DQS receiver enabled training
381  *
382  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
383  *     @param[in]       Receiver  - Current Chip select value
384  *     @param[in]       RcvEnDly  - receiver enable delay to be saved
385  */
386
387 VOID
388 STATIC
389 MemTSetRcvrEnDlyByte (
390   IN OUT   MEM_TECH_BLOCK *TechPtr,
391   IN       UINT8 Receiver,
392   IN       UINT16 RcvEnDly
393   )
394 {
395   UINT8 ByteLane;
396
397   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
398   for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
399     TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), RcvEnDly);
400   }
401 }
402
403 /* -----------------------------------------------------------------------------*/
404 /**
405  *
406  *  This function loads the DqsRcvEnDly from saved data and program to additional index
407  *  for DQS receiver enabled training
408  *
409  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
410  *     @param[in]       Receiver  - Current Chip select value
411  *
412  */
413
414 VOID
415 STATIC
416 MemTLoadRcvrEnDlyByte (
417   IN OUT   MEM_TECH_BLOCK *TechPtr,
418   IN       UINT8 Receiver
419   )
420 {
421   UINT8 i;
422   UINT8 Dimm;
423   UINT16 Saved;
424   CH_DEF_STRUCT *ChannelPtr;
425
426   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
427   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
428
429   Dimm = Receiver >> 1;
430   Saved = TechPtr->DqsRcvEnSaved;
431   for (i = 0; i < MAX_BYTELANES; i++) {
432     if (Saved & 1) {
433       TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i),
434       ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]);
435     }
436     Saved >>= 1;
437   }
438 }
439
440 /* -----------------------------------------------------------------------------*/
441 /**
442  *
443  *  This function saves passing DqsRcvEnDly values to the stack
444  *
445  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
446  *     @param[in]       Receiver  - Current Chip select value
447  *     @param[in]       RcvEnDly  - receiver enable delay to be saved
448  *     @param[in]       CmpResultRank0 - compare result for Rank 0
449  *     @param[in]       CmpResultRank1 - compare result for Rank 1
450  *
451  *     @return  TRUE - All bytelanes pass
452  *     @return  FALSE - Some bytelanes fail
453  */
454
455 BOOLEAN
456 STATIC
457 MemTSaveRcvrEnDlyByte (
458   IN OUT   MEM_TECH_BLOCK *TechPtr,
459   IN       UINT8 Receiver,
460   IN       UINT16 RcvEnDly,
461   IN       UINT16 CmpResultRank0,
462   IN       UINT16 CmpResultRank1
463   )
464 {
465   UINT8 i;
466   UINT8 Passed;
467   UINT8 Saved;
468   UINT8 Mask;
469   UINT8 Dimm;
470   CH_DEF_STRUCT *ChannelPtr;
471
472   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
473   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
474
475   Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF);
476
477   Saved = (UINT8) (TechPtr->DqsRcvEnSaved & Passed); //@attention - false passes filter (subject to be replaced with a better solution)
478   Dimm = Receiver >> 1;
479   Mask = 1;
480   for (i = 0; i < MAX_BYTELANES; i++) {
481     if (Passed & Mask) {
482       if (!(Saved & Mask)) {
483         ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20;     // @attention -1 pass only
484         IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tBL %d = %02x", i, RcvEnDly + 0x20);
485       }
486       Saved |= Mask;
487     }
488     Mask <<= 1;
489   }
490   TechPtr->DqsRcvEnSaved = Saved;
491
492   if (Saved == 0xFF) {
493     return TRUE;
494   } else {
495     return FALSE;
496   }
497 }
498
499 /* -----------------------------------------------------------------------------*/
500 /**
501  *
502  *  This function performs a filtering functionality and saves passing DqsRcvEnDly
503  *  values to the stack
504  *
505  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
506  *     @param[in]       Receiver  - Current Chip select value
507  *     @param[in]       RcvEnDly  - receiver enable delay to be saved
508  *     @param[in]       CmpResultRank0 - compare result for Rank 0
509  *     @param[in]       CmpResultRank1 - compare result for Rank 1
510  *
511  *     @return  TRUE - All bytelanes pass
512  *     @return  FALSE - Some bytelanes fail
513  */
514
515 BOOLEAN
516 MemTSaveRcvrEnDlyByteFilter (
517   IN OUT   MEM_TECH_BLOCK *TechPtr,
518   IN       UINT8 Receiver,
519   IN       UINT16 RcvEnDly,
520   IN       UINT16 CmpResultRank0,
521   IN       UINT16 CmpResultRank1
522   )
523 {
524   UINT8 i;
525   UINT8 Passed;
526   UINT8 Saved;
527   UINT8 Mask;
528   UINT8 Dimm;
529   UINT8 MaxFilterDly;
530   CH_DEF_STRUCT *ChannelPtr;
531   MEM_DCT_CACHE *DctCachePtr;
532
533   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
534   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
535   DctCachePtr = TechPtr->NBPtr->DctCachePtr;
536
537   MaxFilterDly = TechPtr->MaxFilterDly;
538   Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF);
539
540   Dimm = Receiver >> 1;
541   Saved = (UINT8) TechPtr->DqsRcvEnSaved;
542   Mask = 1;
543   for (i = 0; i < MAX_BYTELANES; i++) {
544     if ((Passed & Mask) != 0) {
545       DctCachePtr->RcvEnDlyCounts [i] += 1;
546       if ((Saved & Mask) == 0) {
547         ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20;
548         Saved |= Mask;
549         IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tBL %d = %02x", i, RcvEnDly + 0x20);
550       }
551     } else {
552       if (DctCachePtr->RcvEnDlyCounts [i] <= MaxFilterDly) {
553         DctCachePtr->RcvEnDlyCounts [i] = 0;
554         Saved &= ~Mask;
555       }
556     }
557     Mask <<= 1;
558   }
559
560   //-----------------------
561   TechPtr->DqsRcvEnSaved = (UINT16) Saved;
562
563   Saved = 0;
564   for (i = 0; i < MAX_BYTELANES; i++) {
565     if (DctCachePtr->RcvEnDlyCounts [i] >= MaxFilterDly) {
566       Saved |= (UINT8) 1 << i;
567     }
568   }
569
570   if (Saved == 0xFF) {
571     return TRUE;
572   } else {
573     return FALSE;
574   }
575 }
576
577 /* -----------------------------------------------------------------------------*/
578 /**
579  *
580  *       This function compares test pattern with data in buffer and return a pass/fail bitmap
581  *       for 8 Bytes
582  *
583  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
584  *     @param[in]       Buffer[]  -  Buffer data from DRAM (Measured data from DRAM) to compare
585  *     @param[in]       Pattern[]  - Pattern (Expected data in ROM/CACHE) to compare against
586  *
587  *     @return  PASS - Bit map of results of comparison
588  */
589
590 UINT16
591 STATIC
592 MemTCompare1ClPatternByte (
593   IN OUT   MEM_TECH_BLOCK *TechPtr,
594   IN       UINT8 Buffer[],
595   IN       UINT8 Pattern[]
596   )
597 {
598   UINT16 i;
599   UINT16 j;
600   UINT16 Pass;
601   DIE_STRUCT *MCTPtr;
602
603   MCTPtr = TechPtr->NBPtr->MCTPtr;
604   if (MCTPtr->GangedMode && MCTPtr->Dct) {
605     j = 8;
606   } else {
607     j = 0;
608   }
609
610   Pass = 0xFFFF;
611   IDS_HDT_CONSOLE (MEM_FLOW, " -");
612   for (i = 0; i < 8; i++) {
613     if (Buffer[j] != Pattern[j]) {
614       // if bytelane n fails
615       Pass &= ~((UINT16)1 << (j % 8));    // clear bit n
616     }
617     IDS_HDT_CONSOLE (MEM_FLOW, "  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
618     j++;
619   }
620
621   IDS_HDT_CONSOLE_DEBUG_CODE (
622     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t        -");
623     for (i = 0, j -= 8; i < 8; i++, j++) {
624       IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]);
625     }
626     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t        -");
627     for (i = 0, j -= 8; i < 8; i++, j++) {
628       IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]);
629     }
630     IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
631   );
632
633   return Pass;
634 }
635
636 /* -----------------------------------------------------------------------------*/
637 /**
638  *
639  *       The function resets the DCT input buffer write pointer.
640  *
641  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
642  *     @param[in]       Receiver - Chip select
643  *
644  */
645
646 VOID
647 STATIC
648 MemTResetDctWrPtrByte (
649   IN OUT   MEM_TECH_BLOCK *TechPtr,
650   IN       UINT8 Receiver
651   )
652 {
653   UINT8 i;
654   UINT16 RcvEnDly;
655
656   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
657   for (i = 0; i < MAX_BYTELANES; i++) {
658     RcvEnDly = (UINT16) TechPtr->NBPtr->GetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i));
659     TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i), RcvEnDly);
660   }
661 }
662
663 /* -----------------------------------------------------------------------------*/
664 /**
665  *
666  *       This function skips odd chip select if training at 800MT or above.
667  *
668  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
669  *     @param[in]       *ChipSelPtr - Pointer to variable contains Chip select index
670  *
671  */
672
673 VOID
674 STATIC
675 MemTSkipChipSelPass1Byte (
676   IN OUT   MEM_TECH_BLOCK *TechPtr,
677   IN OUT   UINT8 *ChipSelPtr
678   )
679 {
680   MEM_NB_BLOCK *NBPtr;
681
682   NBPtr = TechPtr->NBPtr;
683   // if the even chip select failed training, need to set CsTrainFail for odd chip select if present.
684   if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ((*ChipSelPtr) + 1))) {
685     if (NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16)1 << *ChipSelPtr)) {
686       NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ((*ChipSelPtr) + 1);
687       if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
688         ASSERT (FALSE);
689       }
690     }
691   }
692   (*ChipSelPtr)++;
693 }
694
695 /* -----------------------------------------------------------------------------*/
696 /**
697  *
698  *  MemTSkipChipSelPass2Byte:
699  *
700  *       This function skips odd chip select if training at 800MT or above.
701  *
702  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
703  *     @param[in,out]   *ChipSelPtr - Pointer to variable contains Chip select index
704  *
705  */
706
707 VOID
708 STATIC
709 MemTSkipChipSelPass2Byte (
710   IN OUT   MEM_TECH_BLOCK *TechPtr,
711   IN OUT   UINT8 *ChipSelPtr
712   )
713 {
714   if (*ChipSelPtr & 1) {
715     *ChipSelPtr = MAX_CS_PER_CHANNEL;    // skip all successions
716   }
717 }
718
719 /* -----------------------------------------------------------------------------*/
720 /**
721  *
722  *       This function determines the maximum number of byte lanes
723  *
724  *     @return  Max number of Bytelanes
725  */
726
727 UINT8
728 STATIC
729 MemTMaxByteLanesByte ( VOID )
730 {
731   return MAX_BYTELANES;
732 }
733
734 /* -----------------------------------------------------------------------------*/
735 /**
736  *
737  *       This function determines the width of the delay tables (eg. RcvEnDlys, WrDqsDlys,...)
738  *
739  *     @return  Delay table width in bytes
740  */
741
742 UINT8
743 STATIC
744 MemTDlyTableWidthByte ( VOID )
745 {
746   return MAX_DELAYS;
747 }
748
749 /* -----------------------------------------------------------------------------*/
750 /**
751  *
752  *       This function writes the Delay value to a certain byte lane
753  *
754  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
755  *     @param[in]       ByteLane  -  Bytelane number being targeted
756  *     @param[in]       Dly - Delay value
757  *
758  */
759
760 VOID
761 STATIC
762 MemTSetDqsDelayCsrByte (
763   IN OUT   MEM_TECH_BLOCK *TechPtr,
764   IN       UINT8 ByteLane,
765   IN       UINT8 Dly
766   )
767 {
768   UINT8 Reg;
769   UINT8 Dimm;
770
771   ASSERT (ByteLane <= MAX_BYTELANES);
772
773   if (!(TechPtr->DqsRdWrPosSaved & ((UINT8)1 << ByteLane))) {
774     Dimm = (TechPtr->ChipSel >> 1);
775
776     if (TechPtr->Direction == DQS_WRITE_DIR) {
777       Dly = Dly + ((UINT8) TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane]);
778       Reg = AccessWrDatDly;
779     } else {
780       Reg = AccessRdDqsDly;
781     }
782
783     TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, Reg, DIMM_BYTE_ACCESS (Dimm, ByteLane), Dly);
784   }
785 }
786
787 /* -----------------------------------------------------------------------------*/
788 /**
789  *
790  *       This function programs the trained DQS delay for the specified byte lane
791  *       and stores its DQS window for reference.
792  *
793  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
794  *     @param[in]       ByteLane  -  Bytelane number being targeted
795  *     @param[in]       DlyMin - Minimum delay value
796  *     @param[in]       DlyMax- Maximum delay value
797  *
798  */
799
800 VOID
801 STATIC
802 MemTDqsWindowSaveByte (
803   IN OUT   MEM_TECH_BLOCK *TechPtr,
804   IN       UINT8 ByteLane,
805   IN       UINT8 DlyMin,
806   IN       UINT8 DlyMax
807   )
808 {
809   UINT8 DqsDelay;
810   UINT8 Dimm;
811   CH_DEF_STRUCT *ChanPtr;
812
813   ASSERT (ByteLane <= MAX_BYTELANES);
814   ChanPtr = TechPtr->NBPtr->ChannelPtr;
815
816   DqsDelay = ((DlyMin + DlyMax + 1) / 2) & 0x3F;
817   MemTSetDqsDelayCsrByte (TechPtr, ByteLane, DqsDelay);
818   TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
819   TechPtr->DqsRdWrPosSaved |= 0xFF00;
820
821   Dimm = (TechPtr->ChipSel / 2) * MAX_DELAYS + ByteLane;
822   if (TechPtr->Direction == DQS_READ_DIR) {
823     ChanPtr->RdDqsDlys[Dimm] = DqsDelay;
824   } else {
825     ChanPtr->WrDatDlys[Dimm] = DqsDelay + ChanPtr->WrDqsDlys[Dimm];
826   }
827
828   if (TechPtr->Direction == DQS_READ_DIR) {
829     ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
830     ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
831   } else {
832     ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
833     ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
834   }
835
836 }
837
838
839 /* -----------------------------------------------------------------------------*/
840 /**
841  *
842  *      This function finds the DIMM that has the largest receiver enable delay.
843  *
844  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
845  *     @param[out]      *ChipSel   - Pointer to the Chip select that has the largest receiver enable delay.
846  *
847  *     @return   TRUE - A chip select can be found.
848  *     @return   FALSE - A chip select cannot be found.
849  */
850
851 BOOLEAN
852 STATIC
853 MemTFindMaxRcvrEnDlyByte (
854   IN OUT   MEM_TECH_BLOCK *TechPtr,
855      OUT   UINT8 *ChipSel
856   )
857 {
858   UINT8  Dimm;
859   UINT8  ByteLane;
860   UINT16 RcvEnDly;
861   UINT16 MaxDly;
862   UINT8  MaxDlyDimm;
863   BOOLEAN RetVal;
864
865   MEM_NB_BLOCK  *NBPtr;
866   CH_DEF_STRUCT *ChannelPtr;
867
868   NBPtr = TechPtr->NBPtr;
869   ChannelPtr = NBPtr->ChannelPtr;
870
871   RetVal = FALSE;
872   MaxDly = 0;
873   MaxDlyDimm = 0;
874   for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
875     if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << (Dimm << 1))) != 0) {
876       if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
877         // Only choose the dimm that does not fail training
878         for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
879           RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
880           if (RcvEnDly > MaxDly) {
881             MaxDly = RcvEnDly;
882             MaxDlyDimm = Dimm;
883             RetVal = TRUE;
884           }
885         }
886       }
887     }
888   }
889
890   if (NBPtr->MCTPtr->Status[Sb128bitmode] != 0) {
891     //The RcvrEnDlys of DCT1 DIMMs should also be considered while ganging.
892     NBPtr->SwitchDCT (NBPtr, 1);
893     ChannelPtr = NBPtr->ChannelPtr;
894     for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
895       for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
896         RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
897         if (RcvEnDly > MaxDly) {
898           MaxDly = RcvEnDly;
899           MaxDlyDimm = Dimm;
900         }
901       }
902     }
903     NBPtr->SwitchDCT (NBPtr, 0);
904   }
905
906   TechPtr->MaxDlyForMaxRdLat = MaxDly;
907   *ChipSel = (MaxDlyDimm * 2);
908   return RetVal;
909 }
910
911 /* -----------------------------------------------------------------------------*/
912 /**
913  *
914  *      This function finds the DIMM that has the largest receiver enable delay + Read DQS Delay.
915  *
916  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
917  *     @param[out]      *ChipSel   - Pointer to the Chip select that has the largest receiver enable delay
918  *                                   + Read DQS Delay.
919  *
920  *     @return   TRUE - A chip select can be found.
921  *     @return   FALSE - A chip select cannot be found.
922  */
923
924 BOOLEAN
925 MemTFindMaxRcvrEnDlyRdDqsDlyByte (
926   IN OUT   MEM_TECH_BLOCK *TechPtr,
927      OUT   UINT8 *ChipSel
928   )
929 {
930   UINT8  Dimm;
931   UINT8  ByteLane;
932   UINT16 RcvEnDly;
933   UINT16 RdDqsDly;
934   UINT16 TotalDly;
935   UINT16 MaxDly;
936   UINT8  MaxDlyDimm;
937   BOOLEAN RetVal;
938
939   MEM_NB_BLOCK  *NBPtr;
940   CH_DEF_STRUCT *ChannelPtr;
941
942   NBPtr = TechPtr->NBPtr;
943   ChannelPtr = NBPtr->ChannelPtr;
944
945   RetVal = FALSE;
946   MaxDly = 0;
947   MaxDlyDimm = 0;
948   for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
949     if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
950       // Only choose the dimm that does not fail training
951       for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
952         RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
953         // Before Dqs Position Training, this value is 0. So the maximum value for
954         // RdDqsDly needs to be added later when calculating the MaxRdLatency value
955         // after RcvEnDly training but before DQS Position Training.
956         RdDqsDly = ChannelPtr->RdDqsDlys[Dimm * MAX_DELAYS + ByteLane];
957         TotalDly = RcvEnDly + (RdDqsDly >> 1);
958         if (TotalDly > MaxDly) {
959           MaxDly = TotalDly;
960           MaxDlyDimm = Dimm;
961           RetVal = TRUE;
962         }
963       }
964     }
965   }
966
967   TechPtr->MaxDlyForMaxRdLat = MaxDly;
968   *ChipSel = (MaxDlyDimm * 2);
969   return RetVal;
970 }
971
972 /* -----------------------------------------------------------------------------*/
973 /**
974  *
975  *      This function finds the DIMM that has the largest receiver enable delay + Read DQS Delay for UNB
976  *
977  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
978  *     @param[out]      *ChipSel   - Pointer to the Chip select that has the largest receiver enable delay
979  *                                   + Read DQS Delay.
980  *
981  *     @return   TRUE - A chip select can be found.
982  *     @return   FALSE - A chip select cannot be found.
983  */
984
985 BOOLEAN
986 MemTFindMaxRcvrEnDlyRdDqsDlyByteUnb (
987   IN OUT   MEM_TECH_BLOCK *TechPtr,
988      OUT   UINT8 *ChipSel
989   )
990 {
991   UINT8  Dimm;
992   UINT8  ByteLane;
993   UINT16 RcvEnDly;
994   UINT16 RdDqsDly;
995   UINT16 TotalDly;
996   UINT16 MaxDly;
997   UINT8  MaxDlyDimm;
998   BOOLEAN RetVal;
999
1000   MEM_NB_BLOCK  *NBPtr;
1001   CH_DEF_STRUCT *ChannelPtr;
1002
1003   NBPtr = TechPtr->NBPtr;
1004   ChannelPtr = NBPtr->ChannelPtr;
1005
1006   RetVal = FALSE;
1007   MaxDly = 0;
1008   MaxDlyDimm = 0;
1009   for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1010     if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
1011       // Only choose the dimm that does not fail training
1012       for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
1013         RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
1014         // Before Dqs Position Training, this value is 0. So the maximum value for
1015         // RdDqsDly needs to be added later when calculating the MaxRdLatency value
1016         // after RcvEnDly training but before DQS Position Training.
1017         RdDqsDly = ChannelPtr->RdDqsDlys[Dimm * MAX_DELAYS + ByteLane];
1018         TotalDly = RcvEnDly + RdDqsDly;
1019         if (TotalDly > MaxDly) {
1020           MaxDly = TotalDly;
1021           MaxDlyDimm = Dimm;
1022           RetVal = TRUE;
1023         }
1024       }
1025     }
1026   }
1027
1028   TechPtr->MaxDlyForMaxRdLat = MaxDly;
1029   *ChipSel = (MaxDlyDimm * 2);
1030   return RetVal;
1031 }
1032
1033 /* -----------------------------------------------------------------------------*/
1034 /**
1035  *
1036  *      This function finds the minimum or maximum gross dly among all the bytes.
1037  *
1038  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1039  *     @param[in]       TrnDlyType - Target Dly type
1040  *     @param[in]       IfMax - If this is for maximum value or minimum
1041  *
1042  *     @return   minimum gross dly
1043  */
1044 UINT8
1045 STATIC
1046 MemTFindMinMaxGrossDlyByte (
1047   IN OUT   MEM_TECH_BLOCK *TechPtr,
1048   IN       TRN_DLY_TYPE TrnDlyType,
1049   IN       BOOLEAN IfMax
1050   )
1051 {
1052   UINT8 i;
1053   UINT8 ByteLane;
1054   UINT16 CsEnabled;
1055   UINT8 MinMaxGrossDly;
1056   UINT8 TrnDly;
1057   MEM_NB_BLOCK *NBPtr;
1058
1059   NBPtr = TechPtr->NBPtr;
1060   CsEnabled = NBPtr->DCTPtr->Timings.CsEnabled;
1061   MinMaxGrossDly = IfMax ? 0 : 0xFF;
1062
1063   for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
1064     if ((CsEnabled & (UINT16) (3 << (i << 1))) != 0) {
1065       for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
1066         TrnDly = (UINT8) (GetTrainDlyFromHeapNb (NBPtr, TrnDlyType, DIMM_BYTE_ACCESS (i, ByteLane)) >> 5);
1067         if ((IfMax && (TrnDly > MinMaxGrossDly)) || (!IfMax && (TrnDly < MinMaxGrossDly))) {
1068           MinMaxGrossDly = TrnDly;
1069         }
1070       }
1071     }
1072   }
1073
1074   return MinMaxGrossDly;
1075 }
1076
1077 /* -----------------------------------------------------------------------------*/
1078 /**
1079  *
1080  *       This function compares test pattern with data in buffer and return a pass/fail bitmap
1081  *       for 8 Bytes for optimized receiver enable training
1082  *
1083  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1084  *     @param[in]       Buffer[]  -  Buffer data from DRAM (Measured data from DRAM) to compare
1085  *     @param[in]       Pattern[]  - Pattern (Expected data in ROM/CACHE) to compare against
1086  *     @param[in]       Side - current side being targeted
1087  *     @param[in]       Receiver - Current receiver value
1088  *     @param[in]       Side1En - Indicates if the second side of the DIMM is being used
1089  *     @return  PASS - Bit map of results of comparison
1090  */
1091
1092 UINT16
1093 STATIC
1094 MemTCompare1ClPatternOptByte (
1095   IN OUT   MEM_TECH_BLOCK *TechPtr,
1096   IN       UINT8 Buffer[],
1097   IN       UINT8 Pattern[],
1098   IN       UINT8 Side,
1099   IN       UINT8 Receiver,
1100   IN       BOOLEAN  Side1En
1101   )
1102 {
1103   UINT16 i;
1104   UINT16 j;
1105   UINT16 Pass;
1106   DIE_STRUCT *MCTPtr;
1107   CH_DEF_STRUCT *ChannelPtr;
1108
1109   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
1110   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
1111   MCTPtr = TechPtr->NBPtr->MCTPtr;
1112
1113   if (MCTPtr->GangedMode && MCTPtr->Dct) {
1114     j = 8;
1115   } else {
1116     j = 0;
1117   }
1118
1119   Pass = 0xFFFF;
1120   IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDelay[BL] -");
1121   for (i = 0; i < 8; i++) {
1122     IDS_HDT_CONSOLE (MEM_FLOW, " %02x", TechPtr->RcvrEnDlyOpt[i] & 0xFF);
1123     if (Buffer[j] != Pattern[j]) {
1124       // if bytelane n fails
1125       Pass &= ~((UINT16)1 << (j % 8));    // clear bit n
1126       TechPtr->DqsRcvEnFirstPassValOpt[i] = 0;
1127       TechPtr->GetFirstPassValOpt[i] = FALSE;
1128       TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
1129       TechPtr->DqsRcvEnSavedOpt[i] = FALSE;
1130       if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) {
1131         if (Side == ((Side1En ? 4 : 2) - 1)) {
1132           TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT;
1133         }
1134       }
1135     } else {
1136       if (TechPtr->FilterSidePassCountOpt[i] == ((Side1En ? 4 : 2) - 1)) {
1137         //Only apply filter if all sides have passed
1138         if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) {
1139           if (TechPtr->GetFirstPassValOpt[i] == FALSE) {
1140             // This is the first Pass, mark the start of filter check
1141             TechPtr->DqsRcvEnFirstPassValOpt[i] = TechPtr->RcvrEnDlyOpt[i];
1142             TechPtr->GetFirstPassValOpt[i] = TRUE;
1143             TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
1144             TechPtr->RcvrEnDlyOpt[i]++;
1145           } else {
1146             if ((TechPtr->RcvrEnDlyOpt[i] - TechPtr->DqsRcvEnFirstPassValOpt[i]) < FILTER_WINDOW_SIZE) {
1147               if (TechPtr->IncBy1ForNextCountOpt[i] == FALSE) {
1148                 TechPtr->RcvrEnDlyOpt[i] += FILTER_SECOND_STAGE_COUNT;
1149                 TechPtr->IncBy1ForNextCountOpt[i] = TRUE;
1150               } else {
1151                 TechPtr->RcvrEnDlyOpt[i]++;
1152                 TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
1153               }
1154             } else {
1155               // End sweep and add offset to first pass
1156               TechPtr->MaxRcvrEnDlyBlOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i];
1157               TechPtr->RcvrEnDlyOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i] + FILTER_OFFSET_VALUE;
1158               TechPtr->FilterStatusOpt[i] = DONE_FILTER;
1159               TechPtr->FilterCountOpt++;
1160             }
1161           }
1162         } else {
1163           TechPtr->FilterSidePassCountOpt[i]++;
1164         }
1165       } else {
1166         if (TechPtr->GetFirstPassValOpt[i] == FALSE) {
1167           if (Side == ((Side1En ? 4 : 2) - 1)) {
1168             TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT;
1169           }
1170         }
1171         TechPtr->FilterSidePassCountOpt[i]++;
1172       }
1173       TechPtr->DqsRcvEnSavedOpt[i] = TRUE;
1174       ChannelPtr->RcvEnDlys[(Receiver >> 1) * MAX_DELAYS + i] = TechPtr->RcvrEnDlyOpt[i];
1175     }
1176     if (Side == ((Side1En ? 4 : 2) - 1)) {
1177       TechPtr->FilterSidePassCountOpt[i] = 0;
1178     }
1179     if (TechPtr->RcvrEnDlyOpt[i] >= TechPtr->RcvrEnDlyLimitOpt[i]) {
1180       TechPtr->FilterCountOpt++;
1181     }
1182
1183     j++;
1184   }
1185
1186   IDS_HDT_CONSOLE_DEBUG_CODE (
1187     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tPass/Fail -");
1188     for (i = 0, j -= 8; i < 8; i++, j++) {
1189       IDS_HDT_CONSOLE (MEM_FLOW, "  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
1190     }
1191     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Measured -");
1192     for (i = 0, j -= 8; i < 8; i++, j++) {
1193       IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]);
1194     }
1195     IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Expected -");
1196     for (i = 0, j -= 8; i < 8; i++, j++) {
1197       IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]);
1198     }
1199     IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
1200   );
1201
1202   return Pass;
1203 }
1204 /*-----------------------------------------------------------------------------
1205  *
1206  *  This function initializes variables for optimized training.
1207  *
1208  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1209  *
1210  * ----------------------------------------------------------------------------
1211  */
1212 VOID
1213 MemTInitializeVariablesOptByte (
1214   IN OUT   MEM_TECH_BLOCK *TechPtr
1215   )
1216 {
1217   UINT8 ByteLane;
1218   for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
1219     TechPtr->RcvrEnDlyLimitOpt[ByteLane] = FILTER_MAX_REC_EN_DLY_VALUE;      // @attention - limit depends on proc type
1220     TechPtr->DqsRcvEnSavedOpt[ByteLane] = FALSE;
1221     TechPtr->RcvrEnDlyOpt[ByteLane] = FILTER_NEW_RECEIVER_START_VALUE;
1222     TechPtr->GetFirstPassValOpt[ByteLane] = FALSE;
1223     TechPtr->DqsRcvEnFirstPassValOpt[ByteLane] = 0;
1224     TechPtr->RevertPassValOpt[ByteLane] = FALSE;
1225     TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] = 0;
1226     TechPtr->FilterStatusOpt[ByteLane] = START_FILTER;
1227     TechPtr->FilterCountOpt = 0;
1228     TechPtr->FilterSidePassCountOpt[ByteLane] = 0;
1229     TechPtr->IncBy1ForNextCountOpt[ByteLane] = FALSE;
1230   }
1231 }
1232
1233 /* -----------------------------------------------------------------------------*/
1234 /**
1235  *
1236  *  This function loads the DqsRcvEnDly from saved data and program to additional index
1237  *  for optimized DQS receiver enabled training
1238  *
1239  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1240  *     @param[in]       Receiver  - Current Chip select value
1241  *
1242  */
1243
1244 VOID
1245 STATIC
1246 MemTLoadRcvrEnDlyOptByte (
1247   IN OUT   MEM_TECH_BLOCK *TechPtr,
1248   IN       UINT8 Receiver
1249   )
1250 {
1251   UINT8 i;
1252   UINT8 Dimm;
1253   CH_DEF_STRUCT *ChannelPtr;
1254
1255   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
1256   ChannelPtr = TechPtr->NBPtr->ChannelPtr;
1257
1258   Dimm = Receiver >> 1;
1259   for (i = 0; i < 8; i++) {
1260     if (TechPtr->DqsRcvEnSavedOpt[i]) {
1261       TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i),
1262       ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]);
1263     }
1264   }
1265 }
1266
1267 /* -----------------------------------------------------------------------------*/
1268 /**
1269  *
1270  *  This function programs DqsRcvEnDly to additional index for DQS receiver enabled training
1271  *
1272  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1273  *     @param[in]       Receiver  - Current Chip select value
1274  *     @param[in]       RcvEnDly  - receiver enable delay to be saved
1275  */
1276
1277 VOID
1278 STATIC
1279 MemTSetRcvrEnDlyOptByte (
1280   IN OUT   MEM_TECH_BLOCK *TechPtr,
1281   IN       UINT8 Receiver,
1282   IN       UINT16 RcvEnDly
1283   )
1284 {
1285   UINT8 ByteLane;
1286
1287   ASSERT (Receiver < MAX_CS_PER_CHANNEL);
1288
1289   for (ByteLane = 0; ByteLane < 8; ByteLane++) {
1290     if (TechPtr->FilterStatusOpt[ByteLane] != DONE_FILTER) {
1291       TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), TechPtr->RcvrEnDlyOpt[ByteLane]);
1292     }
1293   }
1294 }
1295 /*-----------------------------------------------------------------------------
1296  *
1297  *  This sets any Errors generated from Dly sweep
1298  *
1299  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1300  *     @param[in]   DCT   - current DCT
1301  *     @param[in]   Receiver   - current receiver
1302  *
1303  *     @return     FALSE - Fatal error occurs.
1304  *     @return     TRUE  - No fatal error occurs.
1305  * ----------------------------------------------------------------------------
1306  */
1307 BOOLEAN
1308 MemTSetSweepErrorOptByte (
1309   IN OUT   MEM_TECH_BLOCK *TechPtr,
1310   IN       UINT8 Receiver,
1311   IN       UINT8 Dct,
1312   IN       BOOLEAN ErrorCheck
1313   )
1314 {
1315   UINT8 ByteLane;
1316   MEM_DATA_STRUCT *MemPtr;
1317   DIE_STRUCT *MCTPtr;
1318   DCT_STRUCT *DCTPtr;
1319   MEM_NB_BLOCK  *NBPtr;
1320
1321   NBPtr = TechPtr->NBPtr;
1322   MemPtr = NBPtr->MemPtr;
1323   MCTPtr = NBPtr->MCTPtr;
1324   DCTPtr = NBPtr->DCTPtr;
1325   for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
1326     if (TechPtr->RcvrEnDlyOpt[ByteLane] == TechPtr->RcvrEnDlyLimitOpt[ByteLane]) {
1327       // no passing window
1328       if (ErrorCheck) {
1329         return FALSE;
1330       }
1331       PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_NO_PASSING_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader);
1332       SetMemError (AGESA_ERROR, MCTPtr);
1333     }
1334     if (TechPtr->RcvrEnDlyOpt[ByteLane] > (TechPtr->RcvrEnDlyLimitOpt[ByteLane] - 1)) {
1335       // passing window too narrow, too far delayed
1336       if (ErrorCheck) {
1337         return FALSE;
1338       }
1339       PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_VALUE_TOO_LARGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader);
1340       SetMemError (AGESA_ERROR, MCTPtr);
1341       DCTPtr->Timings.CsTrainFail |= (UINT16) (3 << Receiver) & DCTPtr->Timings.CsPresent;
1342       MCTPtr->ChannelTrainFail |= (UINT32)1 << Dct;
1343       if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, DCTPtr->Timings.CsTrainFail, &MemPtr->StdHeader)) {
1344         ASSERT (FALSE);
1345         return FALSE;
1346       }
1347     }
1348   }
1349   return TRUE;
1350 }
1351
1352 /*-----------------------------------------------------------------------------
1353  *
1354  *  This function determines the maximum receiver delay value
1355  *
1356  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1357  *
1358  *     @retval  MaxRcvrValue - Maximum receiver delay value for all bytelanes
1359  * ----------------------------------------------------------------------------
1360  */
1361
1362 UINT16
1363 MemTGetMaxValueOptByte (
1364   IN OUT   MEM_TECH_BLOCK *TechPtr
1365   )
1366 {
1367   UINT8 ByteLane;
1368   UINT16 MaxRcvrValue;
1369   MaxRcvrValue = 0;
1370   for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
1371     if (TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] > MaxRcvrValue) {
1372       MaxRcvrValue = TechPtr->MaxRcvrEnDlyBlOpt[ByteLane];
1373     }
1374   }
1375   MaxRcvrValue += FILTER_OFFSET_VALUE;
1376   return MaxRcvrValue;
1377 }
1378 /*-----------------------------------------------------------------------------
1379  *
1380  *  This function determines if the sweep loop should complete.
1381  *
1382  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1383  *
1384  *     @retval  TRUE - All bytelanes pass
1385  *              FALSE - Some bytelanes fail
1386  * ----------------------------------------------------------------------------
1387  */
1388
1389 BOOLEAN
1390 MemTCheckRcvrEnDlyLimitOptByte (
1391   IN OUT   MEM_TECH_BLOCK *TechPtr
1392   )
1393 {
1394   if (TechPtr->FilterCountOpt >= (UINT16)MAX_CS_PER_CHANNEL) {
1395     return TRUE;
1396   } else {
1397     return FALSE;
1398   }
1399 }
1400
1401 /* -----------------------------------------------------------------------------*/
1402 /**
1403  *
1404  *  This function load the result of write levelization training into RcvrEnDlyOpt,
1405  *  using it as the initial value for Receiver DQS training.
1406  *
1407  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
1408  *     @param[in]       Receiver  - Current Chip select value
1409  */
1410 VOID
1411 STATIC
1412 MemTLoadInitialRcvEnDlyOptByte (
1413   IN OUT   MEM_TECH_BLOCK *TechPtr,
1414   IN       UINT8 Receiver
1415   )
1416 {
1417   UINT8 ByteLane;
1418   MEM_NB_BLOCK *NBPtr;
1419
1420   NBPtr = TechPtr->NBPtr;
1421   for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
1422     TechPtr->RcvrEnDlyOpt[ByteLane] = NBPtr->ChannelPtr->WrDqsDlys[((Receiver >> 1) * TechPtr->DlyTableWidth ()) + ByteLane];
1423   }
1424 }