blob: 1ee1735f5bea0b6e2a94abd69e2b16b0f67fd960 [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mttwl3.c
6 *
7 * Technology Phy assisted write levelization for DDR3
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/Tech/DDR3)
12 * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $
13 *
14 **/
15/*****************************************************************************
16*
Siyuan Wang641f00c2013-06-08 11:50:55 +080017 * Copyright (c) 2008 - 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.
zbao7d94cf92012-07-02 14:19:14 +080041* ***************************************************************************
42*
43*/
44
45/*
46 *----------------------------------------------------------------------------
47 * MODULES USED
48 *
49 *----------------------------------------------------------------------------
50 */
51
52
53
54#include "AGESA.h"
55#include "Ids.h"
56#include "mm.h"
57#include "mn.h"
58#include "mu.h"
59#include "mt.h"
60#include "mp.h"
61#include "mtsdi3.h"
62#include "mtlrdimm3.h"
63#include "merrhdl.h"
64#include "OptionMemory.h"
65#include "PlatformMemoryConfiguration.h"
66#include "GeneralServices.h"
67#include "Filecode.h"
68CODE_GROUP (G1_PEICC)
69RDATA_GROUP (G1_PEICC)
70
71#define FILECODE PROC_MEM_TECH_DDR3_MTTWL3_FILECODE
72/*----------------------------------------------------------------------------
73 * DEFINITIONS AND MACROS
74 *
75 *----------------------------------------------------------------------------
76 */
77extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[];
78
79/*----------------------------------------------------------------------------
80 * TYPEDEFS AND STRUCTURES
81 *
82 *----------------------------------------------------------------------------
83 */
84
85/*----------------------------------------------------------------------------
86 * PROTOTYPES OF LOCAL FUNCTIONS
87 *
88 *----------------------------------------------------------------------------
89 */
90
91BOOLEAN
92STATIC
93MemTWriteLevelizationHw3 (
94 IN OUT MEM_TECH_BLOCK *TechPtr,
95 IN UINT8 Pass
96 );
97
98VOID
99STATIC
100MemTWLPerDimmHw3 (
101 IN OUT MEM_TECH_BLOCK *TechPtr,
102 IN UINT8 Dimm,
103 IN UINT8 Pass
104 );
105
106VOID
107STATIC
108MemTPrepareDIMMs3 (
109 IN OUT MEM_TECH_BLOCK *TechPtr,
110 IN UINT8 TargetDIMM,
111 IN BOOLEAN Wl
112 );
113
114VOID
115STATIC
116MemTProcConfig3 (
117 IN OUT MEM_TECH_BLOCK *TechPtr,
118 IN UINT8 Dimm,
119 IN UINT8 Pass
120 );
121
122VOID
123STATIC
124MemTBeginWLTrain3 (
125 IN OUT MEM_TECH_BLOCK *TechPtr,
126 IN UINT8 Dimm
127 );
128
129/*----------------------------------------------------------------------------
130 * EXPORTED FUNCTIONS
131 *
132 *----------------------------------------------------------------------------
133 */
134
135
136/* -----------------------------------------------------------------------------*/
137/**
138 *
139 * This function executes first pass of Phy assisted write levelization
140 * for a specific node (DDR800).
141 *
142 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
143 *
144 * @return TRUE - No fatal error occurs.
145 * @return FALSE - Fatal error occurs.
146 */
147
148BOOLEAN
149MemTWriteLevelizationHw3Pass1 (
150 IN OUT MEM_TECH_BLOCK *TechPtr
151 )
152{
153 return MemTWriteLevelizationHw3 (TechPtr, 1);
154}
155
156/* -----------------------------------------------------------------------------*/
157/**
158 *
159 * This function executes second pass of Phy assisted write levelization
160 * for a specific node (DDR1066 and above).
161 *
162 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
163 *
164 * @return TRUE - No fatal error occurs.
165 * @return FALSE - Fatal error occurs.
166 */
167
168BOOLEAN
169MemTWriteLevelizationHw3Pass2 (
170 IN OUT MEM_TECH_BLOCK *TechPtr
171 )
172{
173 // If current speed is higher than start-up speed, do second pass of WL
174 if (TechPtr->NBPtr->DCTPtr->Timings.Speed > TechPtr->NBPtr->StartupSpeed) {
175 return MemTWriteLevelizationHw3 (TechPtr, 2);
176 }
177 return TRUE;
178}
179
180/* -----------------------------------------------------------------------------*/
181/**
182 *
183 * This function prepares for Phy assisted training.
184 *
185 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
186 *
187 * @return TRUE - No fatal error occurs.
188 * @return FALSE - Fatal error occurs.
189 */
190
191BOOLEAN
192MemTPreparePhyAssistedTraining (
193 IN OUT MEM_TECH_BLOCK *TechPtr
194 )
195{
196 // Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1.
197 TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFDisAutoRefresh, 1);
198 // Disable ZQ calibration short command by configuring F2x[1, 0]94[ZqcsInterval] = 00b.
199 TechPtr->NBPtr->BrdcstSet (TechPtr->NBPtr, BFZqcsInterval, 0);
200 // Attempt to get the seeds value from PSC tables for WL and RxEn pass1 training if applicable.
201 if (!TechPtr->NBPtr->PsPtr->MemPGetPass1Seeds (TechPtr->NBPtr)) {
202 ASSERT (FALSE);
203 }
204
205 return (BOOLEAN) (TechPtr->NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
206}
207
208/* -----------------------------------------------------------------------------*/
209/**
210 *
211 * This function revert to normal settings when exiting from Phy assisted training.
212 *
213 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
214 *
215 * @return TRUE - No fatal error occurs.
216 * @return FALSE - Fatal error occurs.
217 */
218
219BOOLEAN
220MemTExitPhyAssistedTraining (
221 IN OUT MEM_TECH_BLOCK *TechPtr
222 )
223{
224 MEM_NB_BLOCK *NBPtr;
225 NBPtr = TechPtr->NBPtr;
226
227 // 13.Program F2x[1, 0]8C[DisAutoRefresh] = 0.
228 NBPtr->BrdcstSet (NBPtr, BFDisAutoRefresh, 0);
229 // 14.Program F2x[1, 0]94[ZqcsInterval] to the proper interval for the current memory configuration.
230 NBPtr->BrdcstSet (NBPtr, BFZqcsInterval, 2);
231 NBPtr->FamilySpecificHook[ExitPhyAssistedTraining] (NBPtr, NBPtr);
232
233 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
234}
235
236/*----------------------------------------------------------------------------
237 * LOCAL FUNCTIONS
238 *
239 *----------------------------------------------------------------------------
240 */
241
242/* -----------------------------------------------------------------------------*/
243/**
244 *
245 * This function executed hardware based write levelization for a specific die
246 *
247 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
248 * @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
249 *
250 * @pre Auto refresh and ZQCL must be disabled
251 *
252 * @return TRUE - No fatal error occurs.
253 * @return FALSE - Fatal error occurs.
254 */
255
256BOOLEAN
257STATIC
258MemTWriteLevelizationHw3 (
259 IN OUT MEM_TECH_BLOCK *TechPtr,
260 IN UINT8 Pass
261 )
262{
263 MEM_NB_BLOCK *NBPtr;
264 DCT_STRUCT *DCTPtr;
265 UINT8 Dct;
266 UINT8 ChipSel;
267
268 NBPtr = TechPtr->NBPtr;
269
270 IDS_HDT_CONSOLE (MEM_STATUS, "\nStart write leveling\n");
271 AGESA_TESTPOINT (TpProcMemWriteLevelizationTraining, &(NBPtr->MemPtr->StdHeader));
272 // Begin DQS Write timing training
273 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
274 NBPtr->SwitchDCT (NBPtr, Dct);
275 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
276 DCTPtr = NBPtr->DCTPtr;
277
278 TechPtr->WLCriticalDelay = 0x00;
279
280 //training for each Dimm/CS
281 for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay) {
282 if ((DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSel)) != 0) {
283 if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
284 IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
285 MemTWLPerDimmHw3 (TechPtr, (ChipSel / NBPtr->CsPerDelay), Pass);
286 }
287 }
288 }
289
290 NBPtr->FamilySpecificHook[CalcWrDqDqsEarly] (NBPtr, NULL);
291 }
292 IDS_HDT_CONSOLE (MEM_FLOW, "End write leveling\n\n");
293 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
294}
295
296/* -----------------------------------------------------------------------------*/
297/**
298 *
299 * This function initializes per DIMM write levelization
300 *
301 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
302 * @param[in] Dimm - DIMM to be trained
303 * @param[in] Pass - Pass number (1 (400Mhz) or 2 (>400Mhz))
304 *
305 */
306
307VOID
308STATIC
309MemTWLPerDimmHw3 (
310 IN OUT MEM_TECH_BLOCK *TechPtr,
311 IN UINT8 Dimm,
312 IN UINT8 Pass
313 )
314{
315 MEM_DATA_STRUCT *MemPtr;
316 MEM_NB_BLOCK *NBPtr;
317
318 NBPtr = TechPtr->NBPtr;
319 MemPtr = NBPtr->MemPtr;
320
321 ASSERT (Dimm < (NBPtr->CsPerChannel / NBPtr->CsPerDelay));
322
323 // 1. A. Specify the target Dimm that is to be trained by programming
324 // F2x[1, 0]9C_x08[TrDimmSel].
325 NBPtr->SetBitField (NBPtr, BFTrDimmSel, Dimm);
326
327 TechPtr->TargetDIMM = Dimm;
328 NBPtr->FamilySpecificHook[InitPerNibbleTrn] (NBPtr, NULL);
329 for (TechPtr->TrnNibble = NIBBLE_0; TechPtr->TrnNibble <= (NBPtr->FamilySpecificHook[TrainWlPerNibble] (NBPtr, &Dimm)? NIBBLE_0 : NIBBLE_1); TechPtr->TrnNibble++) {
330 // 2. Prepare the DIMMs for write levelization using DDR3-defined
331 // MR commands.
332 MemTPrepareDIMMs3 (TechPtr, Dimm, TRUE);
333
334 // 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
335 // satisfy DDR3-defined internal DRAM timing.
336 NBPtr->WaitXMemClks (NBPtr, 40);
337
338 // 4. Configure the processor's DDR phy for write levelization training:
339 MemTProcConfig3 (TechPtr, Dimm, Pass);
340
341 // 5. Begin write levelization training
342 MemTBeginWLTrain3 (TechPtr, Dimm);
343 }
344 // 7. Program the target Dimm back to normal operation
345 MemTPrepareDIMMs3 (TechPtr, Dimm, FALSE);
346}
347
348/* -----------------------------------------------------------------------------*/
349/**
350 *
351 * This function prepares the DIMMS for Write Levelization
352 *
353 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
354 * @param[in] TargetDIMM - DIMM to be trained
355 * @param[in] Wl - Indicates if WL mode should be enabled
356 *
357 */
358
359VOID
360STATIC
361MemTPrepareDIMMs3 (
362 IN OUT MEM_TECH_BLOCK *TechPtr,
363 IN UINT8 TargetDIMM,
364 IN BOOLEAN Wl
365 )
366{
367 MEM_NB_BLOCK *NBPtr;
368 UINT8 ChipSel;
369
370 NBPtr = TechPtr->NBPtr;
371
372 AGESA_TESTPOINT (TpProcMemWlPrepDimms, &(NBPtr->MemPtr->StdHeader));
373 ASSERT (TargetDIMM < (NBPtr->CsPerChannel / NBPtr->CsPerDelay));
374 TechPtr->TargetDIMM = TargetDIMM;
375 if (!(TechPtr->TechnologySpecificHook[WlTrainingPrepareLrdimm] (TechPtr, &Wl))) {
376 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
377 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
378 NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel);
379 // Set MR1 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
380 MemTEMRS13 (TechPtr, Wl, TargetDIMM);
381 NBPtr->SendMrsCmd (NBPtr);
382 // Set MR2 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
383 MemTEMRS23 (TechPtr);
384 // Send command
385 NBPtr->SendMrsCmd (NBPtr);
386 }
387 }
388 if (Wl) {
389 // Program WrLvOdt for the Target DIMM (or CS)
390 NBPtr->SetBitField (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetDIMM]);
391 }
392 }
393}
394
395/* -----------------------------------------------------------------------------*/
396/**
397 *
398 * This function programs seed values for Write Levelization
399 *
400 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
401 * @param[in] Dimm - DIMM to be trained
402 * @param[in] Pass - Pass for WL training (1 - 400Mhz or 2 - >400Mhz)
403 *
404 */
405
406VOID
407STATIC
408MemTProcConfig3 (
409 IN OUT MEM_TECH_BLOCK *TechPtr,
410 IN UINT8 Dimm,
411 IN UINT8 Pass
412 )
413{
414 DIE_STRUCT *MCTPtr;
415 CH_DEF_STRUCT *ChannelPtr;
416 MEM_NB_BLOCK *NBPtr;
417 UINT16 WrDqsDly;
418 // Memclk Delay incurred by register.
419 UINT8 MemClkRegDly;
420 UINT8 ByteLane;
421 UINT8 DefaultSeed;
422 UINT8 CurrentSeed;
423 UINT8 *Seed;
424 UINT8 RCW2;
425 UINT16 Speed;
426 INT16 WrDqsBias;
427
428 NBPtr = TechPtr->NBPtr;
429 MCTPtr = NBPtr->MCTPtr;
430 ChannelPtr = TechPtr->NBPtr->ChannelPtr;
431
432 AGESA_TESTPOINT (TpProcMemWlConfigDimms, &(NBPtr->MemPtr->StdHeader));
433 RCW2 = ChannelPtr->CtrlWrd02[Dimm];
434 Speed = TechPtr->NBPtr->DCTPtr->Timings.Speed;
435
436 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Byte: 00 01 02 03 04 05 06 07 ECC\n");
437 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSeeds: ");
438 // Program an initialization Value to registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to set
439 // the gross and fine delay for all the byte lane fields. If the target frequency is different than 400MHz,
440 // BIOS must execute two training passes for each Dimm. For pass 1 at a 400MHz MEMCLK frequency,
441 // use an initial total delay value.
442 if (Pass == 1) {
443 //
444 // Get the default value of seed
445 //
446 if (MCTPtr->Status[SbRegistered]) {
447 //
448 // RDIMM
449 //
450 if (Speed == DDR667_FREQUENCY) {
451 DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x3B : 0x4B;
452 } else {
453 DefaultSeed = ((RCW2 & BIT0) == 0) ? 0x41 : 0x51;
454 }
455 } else if (ChannelPtr->SODimmPresent != 0) {
456 //
457 // SODIMMM
458 //
459 DefaultSeed = 0x12;
460 } else if (MCTPtr->Status[SbLrdimms]) {
461 //
462 // LRDIMM
463 //
464 DefaultSeed = 0xF7;
465 } else {
466 //
467 // UDIMMM
468 //
469 DefaultSeed = 0x1A;
470 }
471
472 NBPtr->FamilySpecificHook[OverrideWLSeed] (NBPtr, &DefaultSeed);
473 ASSERT (Speed >= DDR667_FREQUENCY);
474
475 // Get platform override seed
476 Seed = (UINT8 *) FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_WL_SEED, MCTPtr->SocketId, ChannelPtr->ChannelID, Dimm,
477 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
478 for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
479 // This includes ECC as byte 8
480 CurrentSeed = ((Seed != NULL) ? Seed[ByteLane] : DefaultSeed);
481 ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = CurrentSeed;
482
483 if (NBPtr->IsSupported[WLSeedAdjust]) {
484 if ((CurrentSeed & 0x20) != 0) {
485 // If (SeedGross is odd) then SeedPreGross = 1
486 CurrentSeed = (CurrentSeed & 0x1F) | 0x20;
487 } else {
488 // If (SeedGross is even) then SeedPreGross = 2
489 CurrentSeed = (CurrentSeed & 0x1F) | 0x40;
490 }
491 }
492
493 NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), CurrentSeed);
494 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", CurrentSeed);
495 }
496 } else {
497 //10.Multiply the previously saved delay values in Pass 1, step #5 by (target frequency)/400 to find
498 //the gross and fine delay initialization values at the target frequency. Use these values as the initial
499 //seed values when executing Pass 2, step #4.
500 for (ByteLane = 0; ByteLane < TechPtr->DlyTableWidth (); ByteLane++) {
501 // This includes ECC as byte 8
502 WrDqsDly = ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane];
503 TechPtr->Bytelane = ByteLane;
504 NBPtr->FamilySpecificHook[TrainWlPerNibbleSeed] (NBPtr, &WrDqsDly);
505
506 if (MCTPtr->Status[SbRegistered]) {
507 //
508 // For Registered Dimms
509 //
510 MemClkRegDly = ((RCW2 & BIT0) == 0) ? 0x20 : 0x30;
511 } else {
512 //
513 // Unbuffered Dimms and LRDIMMs
514 //
515 MemClkRegDly = 0;
516 }
517 //
518 // Recover any adjustmen to delay for WrDqDqsEarly
519 //
520 WrDqsBias = 0;
521 NBPtr->FamilySpecificHook[AdjustWrDqsBeforeSeedScaling] (NBPtr, &WrDqsBias);
522
523 // Scale WrDqsDly to the next speed
524 WrDqsDly = (UINT16) (MemClkRegDly + ((((INT32) WrDqsDly - MemClkRegDly - WrDqsBias) * Speed) / TechPtr->PrevSpeed));
525
526 ChannelPtr->WrDqsDlys[Dimm * TechPtr->DlyTableWidth () + ByteLane] = (UINT8) WrDqsDly;
527
528 if (NBPtr->IsSupported[WLSeedAdjust]) {
529 if ((WrDqsDly & 0x20) != 0) {
530 // If (SeedGross is odd) then SeedPreGross = 1
531 WrDqsDly = (WrDqsDly & 0x1F) | 0x20;
532 } else {
533 // If (SeedGross is even) then SeedPreGross = 2
534 WrDqsDly = (WrDqsDly & 0x1F) | 0x40;
535 }
536 }
537 NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDly);
538 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", WrDqsDly);
539 }
540 }
541 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
542}
543
544/* -----------------------------------------------------------------------------*/
545/**
546 *
547 * This function begins WL training for a specific DIMM
548 *
549 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
550 * @param[in] Dimm - DIMM to be trained
551 *
552 */
553
554VOID
555STATIC
556MemTBeginWLTrain3 (
557 IN OUT MEM_TECH_BLOCK *TechPtr,
558 IN UINT8 Dimm
559 )
560{
561 MEM_DATA_STRUCT *MemPtr;
562 DIE_STRUCT *MCTPtr;
563 MEM_NB_BLOCK *NBPtr;
564 UINT8 ByteLane;
565 UINT8 Seed;
566 UINT8 Delay;
567 INT16 Delay16;
568
569 NBPtr = TechPtr->NBPtr;
570 MemPtr = NBPtr->MemPtr;
571 MCTPtr = NBPtr->MCTPtr;
572
573 AGESA_TESTPOINT (TpProcMemWlTrainTargetDimm, &(MemPtr->StdHeader));
574 // Assert ODT pins for write leveling
575 NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 1);
576
577 // Wait 10 MEMCLKs to allow for ODT signal settling.
578 NBPtr->WaitXMemClks (NBPtr, 10);
579
580 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrtLvTrEn = 1\n");
581 // Program F2x[1, 0]9C_x08[WrtLlTrEn]=1.
582 NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 1);
583
584 // Wait 200 MEMCLKs.
585 NBPtr->WaitXMemClks (NBPtr, 200);
586
587 // Program F2x[1, 0]9C_x08[WrtLlTrEn]=0.
588 NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 0);
589
590 // Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to get the gross and fine Delay settings
591 // for the target Dimm and save these values.
592 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t PRE: ");
593 for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
594 // This includes ECC as byte 8
595 Seed = NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane];
596 Delay = (UINT8)NBPtr->GetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane));
597 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Delay);
598
599 TechPtr->Bytelane = ByteLane;
600 TechPtr->TargetDIMM = Dimm;
601 NBPtr->FamilySpecificHook[TrainWlPerNibbleAdjustWLDly] (NBPtr, &Delay);
602
603 if (NBPtr->IsSupported[WLSeedAdjust]) {
604 // Recover WrDqsGrossDly:
605 // WrDqsGrossDly = SeedGross + PhRecGrossDlyByte - SeedPreGross
606 if ((Seed & 0x20) != 0) {
607 // If (SeedGross is odd) then SeedPreGross = 1
608 if ((NBPtr->IsSupported[WLNegativeDelay]) && ((Seed & 0x80) != 0)) {
609 // If the seed was negative, save the most negative delay in WLCriticalDelay
610 TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
611 Delay -= 0x40;
612 } else {
613 Delay += (Seed & 0xE0) - 0x20;
614 }
615 } else {
616 // If (SeedGross is even) then SeedPreGross = 2
617 if (((Seed & 0xE0) == 0) && (Delay < 0x40)) {
618 // If SeedGross is 0 and PhRecGrossDlyByte is less than SeedPreGross,
619 // we have a negative result and need to program the delay to 0
620 if (NBPtr->IsSupported[WLNegativeDelay]) {
621 //
622 // Save the lowest negative delay value across all Dimms and Bytelanes
623 //
624 TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, (INT16)Delay - 0x40);
625 Delay -= 0x40;
626 } else {
627 Delay = 0;
628 }
629 } else {
630 if (NBPtr->GetBitField (NBPtr, BFWrDqDqsEarly) != 0) {
631 Delay = Delay + (Seed & 0xE0);
632 Delay16 = Delay - 0x40;
633 Delay = (UINT8)Delay16;
634 TechPtr->WLCriticalDelay = MIN (TechPtr->WLCriticalDelay, Delay16);
635 } else {
636 Delay += (Seed & 0xE0) - 0x40;
637 }
638 }
639 }
640 } else if (((Seed >> 5) == 0) && ((Delay >> 5) == 3)) {
641 IDS_OPTION_HOOK (IDS_CHECK_NEGATIVE_WL, &Delay, &(TechPtr->NBPtr->MemPtr->StdHeader));
642 // If seed has gross delay of 0 and PRE has gross delay of 3,
643 // then round the total delay of TxDqs to 0.
644 Delay = 0;
645 }
646
647 if ((!NBPtr->IsSupported[WLNegativeDelay]) && ((Delay > (Seed + 0x20)) || (Seed > (Delay + 0x20)))) {
648 //
649 // If PRE comes back with more than Seed +/- 0x20, then this is an
650 // unexpected condition. Log the condition.
651 //
652 PutEventLog (AGESA_ERROR, MEM_ERROR_WL_PRE_OUT_OF_RANGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ((Seed << 8) + Delay), &NBPtr->MemPtr->StdHeader);
653 }
654 NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), Delay);
655 NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane] = Delay;
656 }
657
658 IDS_HDT_CONSOLE_DEBUG_CODE (
659 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrDqs: ");
660 for (ByteLane = 0; ByteLane < (MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
661 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ()) + ByteLane]);
662 }
663 IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
664 );
665
666 // Disable write leveling ODT pins
667 NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 0);
668
669 // Wait 10 MEMCLKs to allow for ODT signal settling.
670 NBPtr->WaitXMemClks (NBPtr, 10);
671
672}
673
674/* -----------------------------------------------------------------------------*/
675/**
676 *
677 * This function programs register after Phy assisted training is finish.
678 *
679 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
680 *
681 * @return TRUE - No fatal error occurs.
682 * @return FALSE - Fatal error occurs.
683 */
684
685BOOLEAN
686MemTExitPhyAssistedTrainingClient3 (
687 IN OUT MEM_TECH_BLOCK *TechPtr
688 )
689{
690 MEM_NB_BLOCK *NBPtr;
691 UINT8 Dct;
692 UINT8 ChipSel;
693 NBPtr = TechPtr->NBPtr;
694
695 NBPtr->FamilySpecificHook[ReEnablePhyComp] (NBPtr, NBPtr);
696 NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 1);
697 NBPtr->BrdcstSet (NBPtr, BFEnterSelfRef, 1);
698 NBPtr->PollBitField (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
699 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign = 2\n");
700 NBPtr->BrdcstSet (NBPtr, BFExitSelfRef, 1);
701 NBPtr->PollBitField (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
702 if (NBPtr->IsSupported[SetDllShutDown]) {
703 NBPtr->BrdcstSet (NBPtr, BFDisDllShutdownSR, 0);
704 }
705
706 // Calculate Max Latency for both channels to prepare for position training
707 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
708 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
709 NBPtr->SwitchDCT (NBPtr, Dct);
710 if (TechPtr->FindMaxDlyForMaxRdLat (TechPtr, &ChipSel)) {
711 NBPtr->SetMaxLatency (NBPtr, TechPtr->MaxDlyForMaxRdLat);
712 }
713 }
714 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
715}