AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / Tech / mttEdgeDetect.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * mttEdgeDetect.c
6  *
7  * DQS R/W position training utilizing Data Eye Edge Detection for optimization
8  *
9  * @xrefitem bom "File Content Label" "Release Content"
10  * @e project: AGESA
11  * @e sub-project: (Mem/Tech)
12  * @e \$Revision: 57884 $ @e \$Date: 2011-08-15 10:42:12 -0600 (Mon, 15 Aug 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
56
57 #include "AGESA.h"
58 #include "amdlib.h"
59 #include "AdvancedApi.h"
60 #include "GeneralServices.h"
61 #include "Ids.h"
62 #include "heapManager.h"
63 #include "mm.h"
64 #include "mn.h"
65 #include "mu.h"
66 #include "mt.h"
67 #include "mport.h"
68 #include "mttEdgeDetect.h"
69 #include "OptionMemory.h"
70 #include "merrhdl.h"
71 #include "Filecode.h"
72 CODE_GROUP (G1_PEICC)
73 RDATA_GROUP (G1_PEICC)
74
75 #define FILECODE PROC_MEM_TECH_MTTEDGEDETECT_FILECODE
76 /*----------------------------------------------------------------------------
77  *                          DEFINITIONS AND MACROS
78  *
79  *----------------------------------------------------------------------------
80  */
81
82
83 #define LAST_DELAY    (-128)
84 #define INC_DELAY     1
85 #define DEC_DELAY     0
86
87
88
89 /*----------------------------------------------------------------------------
90  *                           TYPEDEFS AND STRUCTURES
91  *
92  *----------------------------------------------------------------------------
93  */
94
95 /*----------------------------------------------------------------------------
96  *                        PROTOTYPES OF LOCAL FUNCTIONS
97  *
98  *----------------------------------------------------------------------------
99  */
100
101 /**
102  * Sweep Table For Byte Training without insertion delay
103  *
104 */
105 DQS_POS_SWEEP_TABLE SweepTableByte[] =
106 {
107   //     Begin  End   Inc/Dec  Step  EndResult Edge
108   {      0x00,  0x1F, INC_DELAY,  4, 0xFFFF,   LEFT_EDGE},  /// For Left Edge, start from 0 and Increment to 0x1F by 4 until all PASS
109   { LAST_DELAY, 0x00, DEC_DELAY, -1, 0xFE00,   LEFT_EDGE},  ///  Then go back down to 0x00 by 1 until all FAIL
110   {      0x1F,  0x00, DEC_DELAY, -4, 0xFFFF,   RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
111   { LAST_DELAY, 0x1F, INC_DELAY,  1, 0xFE00,   RIGHT_EDGE}  ///  Then go back up by 1 until all FAIL.
112 };
113 /**
114  * Sweep Table For Byte Training with insertion delay
115  *
116 */
117 DQS_POS_SWEEP_TABLE InsSweepTableByte[] =
118 {
119   //     Begin  End   Inc/Dec  Step  EndResult Edge
120   {      0x00, -0x20, DEC_DELAY, -4, 0xFE00, LEFT_EDGE},  /// For Left Edge, start from 0 and Decrement to -0x20 by -4 until all FAIL
121   { LAST_DELAY, 0x1F, INC_DELAY,  1, 0xFFFF, LEFT_EDGE},  ///  Then go back up to 0x1F by 1 until all PASS
122   {      0x1F,  0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
123   { LAST_DELAY, 0x1F, INC_DELAY,  1, 0xFE00, RIGHT_EDGE}  ///  Then go back up by 1 until all FAIL.
124 };
125
126 BOOLEAN
127 STATIC
128 MemTTrainDQSRdWrEdgeDetect (
129   IN OUT   MEM_TECH_BLOCK *TechPtr
130   );
131
132 BOOLEAN
133 STATIC
134 MemTInitTestPatternAddress (
135   IN OUT   MEM_TECH_BLOCK *TechPtr,
136   IN OUT   SWEEP_INFO *SweepPtr
137   );
138
139 BOOLEAN
140 STATIC
141 MemTContinueSweep (
142   IN OUT   MEM_TECH_BLOCK *TechPtr,
143   IN OUT   SWEEP_INFO *SweepPtr
144   );
145
146 BOOLEAN
147 STATIC
148 MemTSetNextDelay (
149   IN OUT   MEM_TECH_BLOCK *TechPtr,
150   IN OUT   SWEEP_INFO *SweepPtr
151   );
152
153 UINT8
154 STATIC
155 MemTScaleDelayVal (
156   IN OUT MEM_TECH_BLOCK *TechPtr,
157   IN INT8 Delay
158   );
159
160 BOOLEAN
161 STATIC
162 MemTDataEyeSave (
163   IN OUT   MEM_TECH_BLOCK *TechPtr,
164   IN OUT   SWEEP_INFO *SweepPtr,
165   IN       UINT8 ByteLane
166   );
167
168 /*----------------------------------------------------------------------------
169  *                            EXPORTED FUNCTIONS
170  *
171  *----------------------------------------------------------------------------
172  */
173 extern MEM_FEAT_TRAIN_SEQ memTrainSequenceDDR3[];
174 /* -----------------------------------------------------------------------------*/
175 /**
176  *
177  *     This function executes DQS position training for all a Memory channel using
178  *     the Edge Detection algorithm.
179  *
180  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
181  *
182  */
183
184 BOOLEAN
185 MemTTrainDQSEdgeDetectSw (
186   IN OUT   MEM_TECH_BLOCK *TechPtr
187   )
188 {
189   MEM_NB_BLOCK *NBPtr;
190   BOOLEAN Status;
191
192   Status = FALSE;
193   NBPtr = TechPtr->NBPtr;
194   TechPtr->TrainingType = TRN_DQS_POSITION;
195   //
196   // Initialize the Pattern
197   //
198   if (AGESA_SUCCESS == NBPtr->TrainingPatternInit (NBPtr)) {
199     //
200     // Setup hardware training engine (if applicable)
201     //
202     NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType);
203     //
204     // Start Edge Detection
205     //
206     Status |= MemTTrainDQSRdWrEdgeDetect (TechPtr);
207     //
208     // Finalize the Pattern
209     //
210     Status &= (AGESA_SUCCESS == NBPtr->TrainingPatternFinalize (NBPtr));
211   }
212   return Status;
213 }
214
215 /*----------------------------------------------------------------------------
216  *                              LOCAL FUNCTIONS
217  *
218  *----------------------------------------------------------------------------
219  */
220
221 /* -----------------------------------------------------------------------------*/
222 /**
223  *
224  *      This Executes Read DQS and Write Data Position training on a chip select pair
225  *  using the Edge Detection algorithm.
226  *
227  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
228  *
229  *     @return  TRUE - No Errors occurred
230  *     @return  FALSE - Errors occurred
231
232  */
233
234 BOOLEAN
235 STATIC
236 MemTTrainDQSRdWrEdgeDetect (
237   IN OUT   MEM_TECH_BLOCK *TechPtr
238   )
239 {
240   MEM_DATA_STRUCT *MemPtr;
241   MEM_NB_BLOCK  *NBPtr;
242   UINT8 WrDqDelay;
243   UINT8 Dct;
244   UINT8 CSPerChannel;
245   UINT8 CsPerDelay;
246   UINT8 ChipSel;
247   UINT8 i;
248   BOOLEAN Status;
249   UINT8 TimesFail;
250   UINT8 TimesRetrain;
251
252   NBPtr = TechPtr->NBPtr;
253   MemPtr = NBPtr->MemPtr;
254   TimesRetrain = DEFAULT_TRAINING_TIMES;
255   IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader);
256   //
257   // Set environment settings before training
258   //
259   IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Read/Write Data Eye Edge Detection.\n");
260   MemTBeginTraining (TechPtr);
261   //
262   // Do Rd DQS /Wr Data Position training for all Dcts/Chipselects
263   //
264   for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
265     IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
266     NBPtr->SwitchDCT (NBPtr, Dct);
267     //
268     // Chip Select Loop
269     //
270     CSPerChannel = NBPtr->CSPerChannel (NBPtr);
271     CsPerDelay = NBPtr->CSPerDelay (NBPtr);
272     for (ChipSel = 0; ChipSel < CSPerChannel; ChipSel = ChipSel + CsPerDelay ) {
273       //
274       // Init Bit Error Masks
275       //
276       LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
277         0xFF,
278         (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
279         &MemPtr->StdHeader);
280       if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
281            ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
282
283         TechPtr->ChipSel = ChipSel;
284         IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
285         IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tIncrease WrDat, Train RdDqs:\n");
286
287         TechPtr->DqsRdWrPosSaved = 0;
288         //
289         // Use a list of Approximate Write Data delay values and train Read DQS Position for
290         // each until a valid Data eye is found.
291         //
292         Status = FALSE;
293         TimesFail = 0;
294         NBPtr->FamilySpecificHook[InitializeRxEnSeedlessTraining] (NBPtr, NBPtr);
295         ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) {
296           i = 0;
297           while (NBPtr->GetApproximateWriteDatDelay (NBPtr, i, &WrDqDelay)) {
298             TechPtr->SmallDqsPosWindow = FALSE;
299             //
300             // Set Write Delay approximation
301             //
302             TechPtr->Direction = DQS_WRITE_DIR;
303             IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrite Delay: %02x", WrDqDelay);
304             MemTSetDQSDelayAllCSR (TechPtr, WrDqDelay);
305             //
306             // Attempt Read Training
307             //
308             TechPtr->Direction = DQS_READ_DIR;
309             Status = memTrainSequenceDDR3[NBPtr->TrainingSequenceIndex].MemTechFeatBlock->RdPosTraining (TechPtr);
310             if (Status) {
311               //
312               // If Read DQS Training was successful, Train Write Data (DQ) Position
313               //
314               TechPtr->DqsRdWrPosSaved = 0;
315               IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tTrain WrDat:\n\n");
316               TechPtr->Direction = DQS_WRITE_DIR;
317               if (NBPtr->FamilySpecificHook[BeforeWrDatTrn] (NBPtr, &ChipSel)) {
318                 Status = MemTTrainDQSEdgeDetect (TechPtr);
319               }
320               break;
321             }
322             i++;
323           }
324         ERROR_HANDLE_RETRAIN_END ((Status == FALSE), TimesFail)
325         }
326
327         //
328         // If we went through the table, Fail.
329         //
330         if (Status == FALSE) {
331           // On training failure, check and record whether training fails due to small window or no window
332           if (TechPtr->SmallDqsPosWindow) {
333             NBPtr->MCTPtr->ErrStatus[EsbSmallDqs] = TRUE;
334           } else {
335             NBPtr->MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
336           }
337
338           SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
339           if (TechPtr->Direction == DQS_READ_DIR) {
340             PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_RD_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
341           } else {
342             PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_WR_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
343           }
344           NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ChipSel;
345           // If the even chip select failed training always fail the odd, if present.
346           if ((ChipSel & 0x01) == 0) {
347             if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << (ChipSel + 1))) {
348               NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << (ChipSel + 1);
349             }
350           }
351           if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
352             ASSERT (FALSE);
353           }
354         }
355       } else {
356       //
357       // Clear Bit Error Masks if these CS will not be trained.
358       //
359       LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
360         0x00,
361         (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
362         &NBPtr->MemPtr->StdHeader);
363       }
364     }
365   }
366   //
367   // Restore environment settings after training
368   //
369   MemTEndTraining (TechPtr);
370   IDS_HDT_CONSOLE (MEM_FLOW, "End Read/Write Data Eye Edge Detection\n\n");
371   return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
372 }
373
374 /* -----------------------------------------------------------------------------*/
375 /**
376  *
377  *     This function executes DQS position training for both read and write, using
378  *     the Edge Detection Algorithm.  This method searches for the beginning and end
379  *     of the Data Eye with out scanning every DSQ delay value. The following is a
380  *     detailed description of the algorithm:
381  *
382  *     Four-Stage Data Eye Sweep
383  *
384  *     -Search starts at Delay value of 0.
385  *     -Search left in steps of 4/32UI looking for all Byte lanes Passing. Left from zero rolls over to a negative value.
386  *     -Negative values are translated to the high end of the delay range, but using Insertion delay comparison.
387  *     -For each passing byte lane, freeze delay at first passing value, but set mask so next steps will not compare for byte lanes that previously passed
388  *     -Switch to search right in steps of 1/32UI looking for fail.
389  *     -For each lane, starting delay for 1/32 sweep right is first passing delay from 4/32 sweep left.
390  *     -For each failing byte lane, freeze delay at first failing value, but set mask so next steps will not compare for byte lanes that previously failed
391  *     -Search right until all byte lanes have failed
392  *     -For each lane, right edge used by BIOS will be first failing delay value minus 1/32
393
394  *
395  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
396  *
397  *     @return  TRUE - All bytelanes pass
398  *     @return  FALSE - Some bytelanes fail
399 */
400 BOOLEAN
401 MemTTrainDQSEdgeDetect (
402   IN OUT   MEM_TECH_BLOCK *TechPtr
403   )
404 {
405   MEM_NB_BLOCK         *NBPtr;
406   DIE_STRUCT           *MCTPtr;
407   DQS_POS_SWEEP_TABLE  *SweepTablePtr;
408   UINT8                SweepTableSize;
409   SWEEP_INFO           SweepData;
410   BOOLEAN              Status;
411   UINT16               CurrentResult;
412   UINT16               AlignedResult;
413   UINT16               OffsetResult;
414   UINT8                StageIndex;
415   UINT8                CsIndex;
416   UINT8                CsPerDelay;
417   UINT8                i;
418
419   Status = TRUE;
420   //
421   // Initialize Object Pointers
422   //
423   NBPtr = TechPtr->NBPtr;
424   MCTPtr = NBPtr->MCTPtr;
425   //
426   // Initialize stack variables
427   //
428   LibAmdMemFill (&SweepData, 0, sizeof (SWEEP_INFO), &NBPtr->MemPtr->StdHeader);
429   //
430   /// Get Pointer to Sweep Table
431   //
432   if (TechPtr->Direction == DQS_READ_DIR) {
433     SweepTablePtr = InsSweepTableByte;
434     SweepTableSize = GET_SIZE_OF (InsSweepTableByte);
435   } else {
436     SweepTablePtr = SweepTableByte;
437     SweepTableSize = GET_SIZE_OF (SweepTableByte);
438   }
439   //
440   // Get number of CS to train
441   //
442   CsPerDelay = NBPtr->CSPerDelay (NBPtr);
443   //
444   /// Set up the test Pattern, exit if no Memory
445   //
446   if (MemTInitTestPatternAddress (TechPtr, &SweepData) == FALSE) {
447     LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (TechPtr->ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
448       0,
449       (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
450       &NBPtr->MemPtr->StdHeader);
451     return FALSE;
452   }
453   //
454   // Clear Error Flag
455   //
456   SweepData.Error = FALSE;
457   NBPtr->FamilySpecificHook[InitialzeRxEnSeedlessByteLaneError] (NBPtr, NBPtr);
458   //
459   /// Process Sweep table, using entries from the table to determine Starting and Ending Delays
460   /// as well as the Step size and criteria for evaluating whether the correct result is found.
461   ///
462   /// Delay values at this level are an abstract range of values which gets scaled to the actual value
463   /// before it is written to the hardware. This allows NB specific code to handle the scaling as a
464   ///  function of frequency or other conditions.
465   //
466   for (StageIndex = 0; (StageIndex < SweepTableSize) && (SweepData.Error == FALSE); StageIndex++) {
467
468     IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSTAGE: %d\t", StageIndex);
469     //
470     /// Initialize SweepData variables
471     //
472     SweepData.BeginDelay = SweepTablePtr->BeginDelay;
473     SweepData.EndDelay = SweepTablePtr->EndDelay;
474     SweepData.Step = 0;                                    /// Step Value will be 0 to start.
475     SweepData.EndResult = SweepTablePtr->EndResult;
476     if (!(MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining])) {
477       SweepData.EndResult |= 0x0100;
478     }
479     SweepData.Edge = SweepTablePtr->MinMax;
480     SweepData.InsertionDelayMsk = 0;
481     SweepData.ResultFound = 0x0000;
482     //
483     // Set Training Delays Pointer.
484     //
485     if (TechPtr->Direction == DQS_READ_DIR) {
486       SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->RdDqsMaxDlys : NBPtr->ChannelPtr->RdDqsMinDlys);
487     } else {
488       SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->WrDatMaxDlys : NBPtr->ChannelPtr->WrDatMinDlys);
489     };
490     //
491     /// Set initial TrnDelay Values if necessary
492     //
493     IDS_HDT_CONSOLE (MEM_FLOW, "Sweeping %s DQS, %s from ", (TechPtr->Direction == DQS_READ_DIR) ?"Read":"Write", (SweepTablePtr->ScanDir == INC_DELAY) ? "incrementing":"decrementing");
494     if (SweepData.BeginDelay != LAST_DELAY) {
495       IDS_HDT_CONSOLE (MEM_FLOW, "%02x", (UINT16) MemTScaleDelayVal (TechPtr, SweepData.BeginDelay));
496       for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); i++) {
497         SweepData.TrnDelays[i] = SweepData.BeginDelay;
498       }
499     } else {
500       IDS_HDT_CONSOLE (MEM_FLOW, "Current Delay");
501       SweepData.Step = SweepTablePtr->Step;
502     }
503     IDS_HDT_CONSOLE (MEM_FLOW, " by %02x, until all bytelanes %s.\n\n", (UINT16) MemTScaleDelayVal (TechPtr, ABS (SweepTablePtr->Step)), (SweepData.EndResult == 0xFFFF)?"PASS":"FAIL");
504
505     //-------------------------------------------------------------------
506     //   Sweep DQS Delays
507     //   MemTContinueSweep function returns false to break out of loop.
508     //   There are no other breaks out of this loop.
509     //-------------------------------------------------------------------
510     while (MemTContinueSweep (TechPtr, &SweepData)) {
511       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane    : 08 07 06 05 04 03 02 01 00\n");
512       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDQS Delays   : %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
513       (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[8]),
514       (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[7]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[6]),
515       (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[5]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[4]),
516       (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[3]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[2]),
517       (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[1]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[0])
518        );
519       //
520       /// Set Step Value
521       //
522       SweepData.Step = SweepTablePtr->Step;
523       CurrentResult = 0xFFFF;
524       //
525       /// Chip Select Loop: Test the Pattern for all populated CS that are controlled by the current delay registers
526       //
527       for (CsIndex = 0; CsIndex < CsPerDelay ; CsIndex++, TechPtr->ChipSel++) {
528         ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
529         ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
530         if (SweepData.CsAddrValid[CsIndex] == TRUE) {
531           //
532           /// If this is a Write Dqs sweep, Write the pattern now.
533           //
534           if (TechPtr->Direction == DQS_WRITE_DIR) {
535             NBPtr->WritePattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
536           }
537           //
538           /// Read the Pattern Back
539           //
540           NBPtr->ReadPattern (NBPtr, TechPtr->TestBufPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
541           //
542           /// Compare the Pattern and Merge the results using InsertionDelayMsk
543           //
544           AlignedResult = NBPtr->CompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
545           CurrentResult &= AlignedResult | SweepData.InsertionDelayMsk;
546           if (SweepData.InsertionDelayMsk != 0) {
547             OffsetResult = NBPtr->InsDlyCompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
548             CurrentResult &= (OffsetResult | (~SweepData.InsertionDelayMsk));
549           }
550           //
551           /// Flush the Test Pattern
552           //
553           NBPtr->FlushPattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
554           NBPtr->FamilySpecificHook[ResetRxFifoPtr] (NBPtr, NBPtr);
555         }
556       } /// End Chip Select Loop
557       TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
558       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResult       :  %c  %c  %c  %c  %c  %c  %c  %c  %c \n",
559         (SweepData.ResultFound & ((UINT16) 1 << (8))) ? ' ':(CurrentResult & ((UINT16) 1 << (8))) ? 'P':'.',
560         (SweepData.ResultFound & ((UINT16) 1 << (7))) ? ' ':(CurrentResult & ((UINT16) 1 << (7))) ? 'P':'.',
561         (SweepData.ResultFound & ((UINT16) 1 << (6))) ? ' ':(CurrentResult & ((UINT16) 1 << (6))) ? 'P':'.',
562         (SweepData.ResultFound & ((UINT16) 1 << (5))) ? ' ':(CurrentResult & ((UINT16) 1 << (5))) ? 'P':'.',
563         (SweepData.ResultFound & ((UINT16) 1 << (4))) ? ' ':(CurrentResult & ((UINT16) 1 << (4))) ? 'P':'.',
564         (SweepData.ResultFound & ((UINT16) 1 << (3))) ? ' ':(CurrentResult & ((UINT16) 1 << (3))) ? 'P':'.',
565         (SweepData.ResultFound & ((UINT16) 1 << (2))) ? ' ':(CurrentResult & ((UINT16) 1 << (2))) ? 'P':'.',
566         (SweepData.ResultFound & ((UINT16) 1 << (1))) ? ' ':(CurrentResult & ((UINT16) 1 << (1))) ? 'P':'.',
567         (SweepData.ResultFound & ((UINT16) 1 << (0))) ? ' ':(CurrentResult & ((UINT16) 1 << (0))) ? 'P':'.'
568         );
569       //
570       /// Merge current result into cumulative result and make it positive.
571       //
572       SweepData.ResultFound |= ~(CurrentResult ^ SweepData.EndResult);
573
574       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResultFound  :  %c  %c  %c  %c  %c  %c  %c  %c  %c \n\n",
575         (SweepData.ResultFound & ((UINT16) 1 << (8))) ? 'Y':' ',
576         (SweepData.ResultFound & ((UINT16) 1 << (7))) ? 'Y':' ',
577         (SweepData.ResultFound & ((UINT16) 1 << (6))) ? 'Y':' ',
578         (SweepData.ResultFound & ((UINT16) 1 << (5))) ? 'Y':' ',
579         (SweepData.ResultFound & ((UINT16) 1 << (4))) ? 'Y':' ',
580         (SweepData.ResultFound & ((UINT16) 1 << (3))) ? 'Y':' ',
581         (SweepData.ResultFound & ((UINT16) 1 << (2))) ? 'Y':' ',
582         (SweepData.ResultFound & ((UINT16) 1 << (1))) ? 'Y':' ',
583         (SweepData.ResultFound & ((UINT16) 1 << (0))) ? 'Y':' '
584         );
585     } /// End of Delay Sweep
586     //
587     /// Place Final delay values at last passing delay.
588     //
589     if (SweepData.ResultFound == 0xFFFF) {
590       if ( ABS (SweepData.Step) == 1) {
591         for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
592           if ((SweepData.EndResult & ((UINT16) (1 << i))) == 0) {
593             SweepData.TrnDelays[i] = SweepData.TrnDelays[i] - SweepData.Step;
594           }
595         }
596       }
597     }
598     //
599     // Update Pointer to Sweep Table
600     //
601     SweepTablePtr++;
602   }///End of Edge Detect loop
603   //
604   /// If No Errors are detected, Calculate Data Eye Width and Center
605   //
606   if (SweepData.Error == FALSE) {
607     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tData Eye Results:\n\n");
608     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tByte     Left     Right\n");
609     IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane     Edge     Edge     Width     Center\n");
610     for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
611       IDS_HDT_CONSOLE (MEM_FLOW, "\t\t %0d", i);
612       TechPtr->Bytelane = i;
613       if (!MemTDataEyeSave (TechPtr, &SweepData, i)) {
614         break;
615       }
616       IDS_HDT_CONSOLE (MEM_FLOW, "\n");
617       if (SweepData.Error == TRUE) {
618         Status = FALSE;
619       }
620     }
621   } else {
622     Status = FALSE;
623     IDS_HDT_CONSOLE (MEM_FLOW, "\t\t--DATA EYE NOT FOUND--\n\n");
624     NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrNoWindBLError] (NBPtr, &SweepData);
625   }
626   return Status;
627 }
628
629 /* -----------------------------------------------------------------------------*/
630 /**
631  *
632  *     Initialize the Test Pattern Address for two chip selects and, if this
633  *     is a Write Data Eye, write the initial test pattern.
634  *
635  *     Test Address is stored in the Sweep info struct.  If Memory is not present
636  *     then return with False.
637  *
638  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
639  *     @param[in,out]   *SweepPtr -  Pointer to SWEEP_INFO structure.
640  *
641  *     @return  BOOLEAN
642  *              TRUE  - Memory is present
643  *              FALSE - No memory present on this Chip Select pair.
644  *
645 **
646  */
647 BOOLEAN
648 STATIC
649 MemTInitTestPatternAddress (
650   IN OUT   MEM_TECH_BLOCK *TechPtr,
651   IN OUT   SWEEP_INFO *SweepPtr
652   )
653 {
654   MEM_NB_BLOCK  *NBPtr;
655   UINT8 ChipSel;
656   UINT8 CsPerDelay;
657   UINT8 CsIndex;
658   BOOLEAN BanksPresent;
659
660   NBPtr = TechPtr->NBPtr;
661   BanksPresent = FALSE;
662   CsPerDelay = NBPtr->CSPerDelay (NBPtr);
663   ChipSel = TechPtr->ChipSel;
664   for (CsIndex = 0; CsIndex < CsPerDelay; ChipSel++, CsIndex++, TechPtr->ChipSel++) {
665     ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
666     ASSERT (ChipSel < MAX_CS_PER_CHANNEL);
667     ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
668     //
669     /// If memory is present on this cs, get the test addr
670     //
671     if (NBPtr->GetSysAddr (NBPtr, ChipSel, &(SweepPtr->TestAddrRJ16[CsIndex]))) {
672       if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
673         BanksPresent = TRUE;
674         SweepPtr->CsAddrValid[CsIndex] = TRUE;
675         //
676         /// If this is a Read Dqs sweep, Write the pattern now.
677         //
678         if (TechPtr->Direction == DQS_READ_DIR) {
679           IDS_HDT_CONSOLE (MEM_FLOW, "\tTestAddr: %x0000\n", SweepPtr->TestAddrRJ16[CsIndex]);
680           NBPtr->WritePattern (NBPtr, SweepPtr->TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
681         }
682       }
683     } else {
684       SweepPtr->CsAddrValid[CsIndex] = FALSE;
685     }
686   } /// End Chip Select Loop
687   TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
688   //
689   /// return FALSE if no ChipSelects present.
690   //
691   return BanksPresent;
692 }
693
694 /* -----------------------------------------------------------------------------*/
695 /**
696  *     Test Conditions for exiting the training loop, set the next delay value,
697  *     and return status
698  *
699  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
700  *     @param[in,out]   *SweepPtr -  Pointer to SWEEP_INFO structure.
701  *
702  *     @return  BOOLEAN
703  *              TRUE  - Continue to test with next delay setting
704  *              FALSE - Exit training loop.  Either the result has been found or
705  *                      end of delay range has been reached.
706 */
707 BOOLEAN
708 STATIC
709 MemTContinueSweep (
710   IN OUT   MEM_TECH_BLOCK *TechPtr,
711   IN OUT   SWEEP_INFO *SweepPtr
712   )
713 {
714   BOOLEAN Status;
715   Status = FALSE;
716   if (SweepPtr->ResultFound != 0xFFFF) {
717     Status = MemTSetNextDelay (TechPtr, SweepPtr);
718   }
719   TechPtr->NBPtr->FamilySpecificHook[RegAccessFence] (TechPtr->NBPtr, NULL);
720   return Status;
721 }
722
723 /* -----------------------------------------------------------------------------*/
724 /**
725  *
726  *      This function sets the next delay value for each bytelane that needs to
727  *      be advanced.  It checks the bounds of the delay to see if we are at the
728  *      end of the range.  If we are to close to advance a whole step value, but
729  *      not at the boundary, then we set the delay to the boundary.
730  *
731  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
732  *     @param[in,out]   *SweepPtr -  Pointer to SWEEP_INFO structure.
733  *
734  */
735
736 BOOLEAN
737 STATIC
738 MemTSetNextDelay (
739   IN OUT   MEM_TECH_BLOCK *TechPtr,
740   IN OUT   SWEEP_INFO *SweepPtr
741   )
742 {
743   DIE_STRUCT           *MCTPtr;
744   UINT8 i;
745
746   MCTPtr =  TechPtr->NBPtr->MCTPtr;
747   //
748   ///< Loop through bytelanes
749   //
750   for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && TechPtr->NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
751     //
752     /// Skip Bytelanes that have already reached the desired result
753     //
754     if ( (SweepPtr->ResultFound  & ((UINT16)1 << i)) == 0) {
755       //
756       /// If a bytelane has reached the end, flag an error and exit
757       //
758       if (SweepPtr->TrnDelays[i] == SweepPtr->EndDelay) {
759         if ((SweepPtr->EndResult & ((UINT16) (1 << i))) != 0) {
760           MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
761           SweepPtr->Error = TRUE;
762         }
763         return FALSE;
764       }
765       //
766       /// If the Current delay value is less than a step away from EndDelay,
767       //
768       if ( ABS (SweepPtr->EndDelay - SweepPtr->TrnDelays[i]) < ABS (SweepPtr->Step)) {
769         /// set to EndDelay.
770         //
771         SweepPtr->TrnDelays[i] = SweepPtr->EndDelay;
772       } else {
773         //
774         /// Otherwise, add the step value to it
775         SweepPtr->TrnDelays[i] = SweepPtr->TrnDelays[i] + SweepPtr->Step;
776       }
777       //
778       /// Set InsertionDelayMsk bit if Delay < 0 for this bytelane
779       //
780       if (SweepPtr->TrnDelays[i] < 0) {
781         SweepPtr->InsertionDelayMsk |= ((UINT16) 1 << i);
782       } else {
783         SweepPtr->InsertionDelayMsk &= ~((UINT16) 1 << i);
784       }
785       //
786       /// Write the scaled value to the Delay Register
787       //
788       TechPtr->SetDQSDelayCSR (TechPtr, i, MemTScaleDelayVal (TechPtr, SweepPtr->TrnDelays[i]));
789     }
790   }
791   return TRUE;
792 }
793 /* -----------------------------------------------------------------------------*/
794 /**
795  *
796  *     This function accepts a delay value in 32nd of a UI and converts it to an
797  *     actual register value, taking into consideration NB type, rd/wr,
798  *     and frequency.
799  *
800  *     Delay = (Min + (Delay * ( (Max - Min) / TRN_DELAY_MAX) )) & Mask
801  *
802  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
803  *     @param[in]       *Delay     - INT8 of delay value;
804  *
805  *     @return  UINT8 of the adjusted delay value
806 */
807 UINT8
808 STATIC
809 MemTScaleDelayVal (
810   IN OUT   MEM_TECH_BLOCK *TechPtr,
811   IN       INT8 Delay
812   )
813 {
814   MEM_NB_BLOCK  *NBPtr;
815   TRN_DLY_PARMS Parms;
816   TRN_DLY_TYPE DelayType;
817   UINT8 NewDelay;
818   INT8 Factor;
819   INT8 ScaledDelay;
820
821   NBPtr = TechPtr->NBPtr;
822   //
823   // Determine Delay Type, Get Delay Parameters, and return scaled Delay value
824   //
825   DelayType = (TechPtr->Direction == DQS_WRITE_DIR) ? AccessWrDatDly : AccessRdDqsDly;
826   NBPtr->GetTrainDlyParms (NBPtr, DelayType, &Parms);
827   Factor = ((Parms.Max - Parms.Min) / TRN_DELAY_MAX);
828   ScaledDelay = Delay * Factor;
829   NewDelay = (Parms.Min + ScaledDelay) & Parms.Mask;
830   return NewDelay;
831 }
832
833
834
835
836
837 /* -----------------------------------------------------------------------------*/
838 /**
839  *
840  *       This function calculates the Center of the Data eye for the specified byte lane
841  *       and stores its DQS Delay value for reference.
842  *
843  *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
844  *     @param[in,out]   *SweepPtr -  Pointer to SWEEP_INFO structure.
845  *     @param[in]       ByteLane  -  Bytelane number being targeted
846  *
847  */
848 BOOLEAN
849 STATIC
850 MemTDataEyeSave (
851   IN OUT   MEM_TECH_BLOCK *TechPtr,
852   IN OUT   SWEEP_INFO *SweepPtr,
853   IN       UINT8 ByteLane
854   )
855 {
856   MEM_NB_BLOCK *NBPtr;
857   UINT8 EyeCenter;
858   UINT8 DlyMin;
859   UINT8 DlyMax;
860   UINT8 EyeWidth;
861   UINT8 Dimm;
862   CH_DEF_STRUCT *ChanPtr;
863
864   NBPtr = TechPtr->NBPtr;
865   ChanPtr = NBPtr->ChannelPtr;
866
867   ASSERT (ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8));
868   //
869   // Calculate Data Eye edges, Width, and Center in real terms.
870   //
871   if (TechPtr->Direction == DQS_READ_DIR) {
872     DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMinDlys[ByteLane]);
873     DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMaxDlys[ByteLane]);
874     EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->RdDqsMaxDlys[ByteLane] - ChanPtr->RdDqsMinDlys[ByteLane]));
875     EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->RdDqsMinDlys[ByteLane] + ChanPtr->RdDqsMaxDlys[ByteLane] + 1) / 2));
876     if (!NBPtr->FamilySpecificHook[RdDqsDlyRestartChk] (NBPtr, &EyeCenter)) {
877       return FALSE;
878     }
879     ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
880     ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
881     NBPtr->FamilySpecificHook[ForceRdDqsPhaseB] (NBPtr, &EyeCenter);
882   } else {
883     DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMinDlys[ByteLane]);
884     DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMaxDlys[ByteLane]);
885     EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->WrDatMaxDlys[ByteLane] - ChanPtr->WrDatMinDlys[ByteLane]));
886     EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->WrDatMinDlys[ByteLane] + ChanPtr->WrDatMaxDlys[ByteLane] + 1) / 2));
887     ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
888     ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
889   }
890   //
891   // Flag error for small window.
892   //
893   if (EyeWidth < MemTScaleDelayVal (TechPtr, NBPtr->MinDataEyeWidth (NBPtr))) {
894     TechPtr->SmallDqsPosWindow = TRUE;
895     SweepPtr->Error = TRUE;
896     NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrSmallWindBLError] (NBPtr, NULL);
897   }
898
899   IDS_HDT_CONSOLE (MEM_FLOW, "        %02x       %02x       %02x         %02x", DlyMin, DlyMax, EyeWidth, EyeCenter);
900
901   TechPtr->SetDQSDelayCSR (TechPtr, ByteLane, EyeCenter);
902   if (!SweepPtr->Error) {
903     TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
904   }
905   TechPtr->DqsRdWrPosSaved |= 0xFE00;
906
907   Dimm = (TechPtr->ChipSel / 2) * TechPtr->DlyTableWidth () + ByteLane;
908   if (TechPtr->Direction == DQS_READ_DIR) {
909     ChanPtr->RdDqsDlys[Dimm] = EyeCenter;
910   } else {
911     ChanPtr->WrDatDlys[Dimm] = EyeCenter + ChanPtr->WrDqsDlys[Dimm];
912   }
913
914   return TRUE;
915 }
916