blob: 076f44387605665d54e6892652afe6cf21e6dff5 [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mnphy.c
6 *
7 * Common Northbridge Phy support
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/NB)
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 *----------------------------------------------------------------------------
48 * MODULES USED
49 *
50 *----------------------------------------------------------------------------
51 */
52
53
54
55#include "AGESA.h"
56#include "amdlib.h"
57#include "Ids.h"
58#include "mport.h"
59#include "mm.h"
60#include "mn.h"
61#include "mt.h"
62#include "mu.h"
63#include "PlatformMemoryConfiguration.h"
64#include "heapManager.h"
65#include "merrhdl.h"
66#include "Filecode.h"
67CODE_GROUP (G1_PEICC)
68RDATA_GROUP (G1_PEICC)
69#define FILECODE PROC_MEM_NB_MNPHY_FILECODE
70/*----------------------------------------------------------------------------
71 * DEFINITIONS AND MACROS
72 *
73 *----------------------------------------------------------------------------
74 */
75#define UNUSED_CLK 4
76
77/*----------------------------------------------------------------------------
78 * TYPEDEFS AND STRUCTURES
79 *
80 *----------------------------------------------------------------------------
81 */
82/// Type of an entry for processing phy init compensation for client NB
83typedef struct {
84 BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided
85 BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified
86 BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified
87 UINT16 ExtraValue; ///< Extra value needed to be written to bit field
88 CONST UINT16 (*TxPrePN)[3][5]; ///< Pointer to slew rate table
89} PHY_COMP_INIT_CLIENTNB;
90
91/*----------------------------------------------------------------------------
92 * PROTOTYPES OF LOCAL FUNCTIONS
93 *
94 *----------------------------------------------------------------------------
95 */
96
97
98
99/*----------------------------------------------------------------------------
100 * EXPORTED FUNCTIONS
101 *
102 *----------------------------------------------------------------------------
103 */
104/* -----------------------------------------------------------------------------*/
105/**
106 *
107 *
108 * This function gets a delay value a PCI register during training
109 *
110 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
111 * @param[in] TrnDly - type of delay to be set
112 * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
113 * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
114 *
115 * @return Value read
116 */
117
118UINT32
119MemNGetTrainDlyNb (
120 IN OUT MEM_NB_BLOCK *NBPtr,
121 IN TRN_DLY_TYPE TrnDly,
122 IN DRBN DrbnVar
123 )
124{
125 return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0);
126}
127
128/* -----------------------------------------------------------------------------*/
129/**
130 *
131 *
132 * This function sets a delay value a PCI register during training
133 *
134 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
135 * @param[in] TrnDly - type of delay to be set
136 * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
137 * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
138 * @param[in] Field - Value to be programmed
139 *
140 */
141
142VOID
143MemNSetTrainDlyNb (
144 IN OUT MEM_NB_BLOCK *NBPtr,
145 IN TRN_DLY_TYPE TrnDly,
146 IN DRBN DrbnVar,
147 IN UINT16 Field
148 )
149{
150 NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field);
151}
152
153/* -----------------------------------------------------------------------------*/
154/**
155 *
156 *
157 * This function executes prototypical Phy fence training function.
158 *
159 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
160 *
161 */
162
163VOID
164MemNPhyFenceTrainingNb (
165 IN OUT MEM_NB_BLOCK *NBPtr
166 )
167{
168 NBPtr->MemPPhyFenceTrainingNb (NBPtr);
169}
170
171/* -----------------------------------------------------------------------------*/
172/**
173 *
174 *
175 * This function executes prototypical Phy fence training function.
176 *
177 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
178 *
179 */
180
181VOID
182MemNPhyFenceTrainingUnb (
183 IN OUT MEM_NB_BLOCK *NBPtr
184 )
185{
186 UINT8 FenceThresholdTxDll;
187 UINT8 FenceThresholdRxDll;
188 UINT8 FenceThresholdTxPad;
189 UINT16 Fence2Data;
190
191 MemNSetBitFieldNb (NBPtr, BFDataFence2, 0);
192 MemNSetBitFieldNb (NBPtr, BFFence2, 0);
193 // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b.
194 // 2. Perform phy fence training.
195 // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll].
196 MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2);
197 MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence);
198 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFenceThresholdTxDll\n");
199 MemNTrainPhyFenceNb (NBPtr);
200 FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
201 NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxDll);
202
203 // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b.
204 MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000);
205
206 // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b.
207 // 6. Perform phy fence training.
208 // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll].
209 MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1);
210 MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence);
211 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n");
212 MemNTrainPhyFenceNb (NBPtr);
213 FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
214 NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdRxDll);
215
216 // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b.
217 MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000);
218
219 // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b.
220 // 10. Perform phy fence training.
221 // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad].
222 MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3);
223 MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence);
224 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n");
225 MemNTrainPhyFenceNb (NBPtr);
226 FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence);
227 NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxPad);
228
229 // Program Fence2 threshold for Clk, Cmd, and Addr
230 if (FenceThresholdTxPad < 16) {
231 MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10);
232 MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10);
233 MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10);
234 } else {
235 MemNSetBitFieldNb (NBPtr, BFClkFence2, 0);
236 MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0);
237 MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0);
238 }
239
240 // Program Fence2 threshold for data
241 Fence2Data = 0;
242 if (FenceThresholdTxPad < 16) {
243 Fence2Data |= FenceThresholdTxPad | 0x10;
244 }
245 if (FenceThresholdRxDll < 16) {
246 Fence2Data |= (FenceThresholdRxDll | 0x10) << 10;
247 }
248 if (FenceThresholdTxDll < 16) {
249 Fence2Data |= (FenceThresholdTxDll | 0x10) << 5;
250 }
251 MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data);
252 NBPtr->FamilySpecificHook[ProgramFence2RxDll] (NBPtr, &Fence2Data);
253
254 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
255 // 18. If motherboard routing requires CS[7:6] to adopt address timings, e.g. 3 LRDIMMs/ch with CS[7:6]
256 // routed across all DIMM sockets, BIOS performs the following:
257 if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, NULL, NULL) != NULL) {
258 // A. Program D18F2xA8_dct[1:0][CSTimingMux67] = 1.
259 MemNSetBitFieldNb (NBPtr, BFCSTimingMux67, 1);
260 // B. Program D18F2x9C_x0D0F_8021_dct[1:0]:
261 // - DiffTimingEn = 1.
262 // - IF (D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay] >=
263 // D18F2x9C_x0D0F_E008_dct[1:0][FenceValue]) THEN Fence = 1 ELSE Fence = 0.
264 // - Delay = D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay].
265 //
266 MemNSetBitFieldNb (NBPtr, BFDiffTimingEn, 1);
267 MemNSetBitFieldNb (NBPtr, BFFence, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay) >= MemNGetBitFieldNb (NBPtr, BFFenceValue)) ? 1 : 0);
268 MemNSetBitFieldNb (NBPtr, BFDelay, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay)));
269 }
270 }
271
272 // 19. Reprogram F2x9C_04.
273 MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
274
275}
276
277/* -----------------------------------------------------------------------------*/
278/**
279 *
280 *
281 * This function executes Phy fence training
282 *
283 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
284 *
285 */
286
287VOID
288MemNTrainPhyFenceNb (
289 IN OUT MEM_NB_BLOCK *NBPtr
290 )
291{
292 UINT8 Byte;
293 INT16 Avg;
294 UINT8 PREvalue;
295
296 if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
297 return;
298 }
299
300 // 1. BIOS first programs a seed value to the phase recovery
301 // engine registers.
302 //
303 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
304 for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
305 // This includes ECC as byte 8
306 MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
307 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
308 }
309
310 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
311 // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
312 MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
313
314 if (!NBPtr->IsSupported[UnifiedNbFence]) {
315 // 3. Wait 200 MEMCLKs.
316 MemNWaitXMemClksNb (NBPtr, 200);
317 } else {
318 // 3. Wait 2000 MEMCLKs.
319 MemNWaitXMemClksNb (NBPtr, 2000);
320 }
321
322 // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
323 MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
324
325 // 5. BIOS reads the phase recovery engine registers
326 // F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
327 // 6. Calculate the average value of the fine delay and subtract 8.
328 //
329 Avg = 0;
330 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t PRE: ");
331 for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) {
332 //
333 // This includes ECC as byte 8. ECC Byte lane (8) is ignored by MemNGetTrainDlyNb function where
334 // ECC is not supported.
335 //
336 PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
337 Avg = Avg + ((INT16) PREvalue);
338 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", PREvalue);
339 }
340 Avg = ((Avg + 8) / 9); // round up
341
342 Avg -= 8;
343 NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg);
344
345 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFence: %02x\n", Avg);
346
347 // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
348 MemNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
349
350 // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
351 // Register delays for both channels. This forces the phy to recompute
352 // the fence.
353 //
354 MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl));
355}
356
357/* -----------------------------------------------------------------------------*/
358/**
359 *
360 *
361 * This function initializes the DDR phy compensation logic
362 *
363 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
364 *
365 */
366
367VOID
368MemNInitPhyCompNb (
369 IN OUT MEM_NB_BLOCK *NBPtr
370 )
371{
372 CONST UINT8 TableCompRiseSlew20x[] = {7, 3, 2, 2};
373 CONST UINT8 TableCompRiseSlew15x[] = {7, 7, 3, 2};
374 CONST UINT8 TableCompFallSlew20x[] = {7, 5, 3, 2};
375 CONST UINT8 TableCompFallSlew15x[] = {7, 7, 5, 3};
376 UINT8 i;
377 UINT8 j;
378 UINT8 CurrDct;
379 UINT8 CurrChannel;
380 BOOLEAN MarginImprv;
381 MarginImprv = FALSE;
382 CurrDct = NBPtr->Dct;
383 CurrChannel = NBPtr->Channel;
384 if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
385 if (NBPtr->MCTPtr->GangedMode == FALSE) {
386 for (i = 0; i < NBPtr->DctCount; i++) {
387 MemNSwitchDCTNb (NBPtr, i);
388 for (j = 0; j < NBPtr->ChannelCount; j++) {
389 NBPtr->SwitchChannel (NBPtr, j);
390 if ((NBPtr->ChannelPtr->Dimms == 4) && ((NBPtr->DCTPtr->Timings.Speed == DDR533_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY))) {
391 MarginImprv = TRUE;
392 }
393 }
394 }
395 MemNSwitchDCTNb (NBPtr, CurrDct);
396 NBPtr->SwitchChannel (NBPtr, CurrChannel);
397 }
398 }
399
400 // 1. BIOS disables the phy compensation register by programming F2x9C_x08[DisAutoComp]=1
401 // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
402 // DisAutoComp will be cleared after Dram init has completed
403 //
404 MemNSwitchDCTNb (NBPtr, 0);
405 MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
406 MemUWait10ns (500, NBPtr->MemPtr);
407 MemNSwitchDCTNb (NBPtr, CurrDct);
408
409 // 3. For each normalized driver strength code read from
410 // F2x[1, 0]9C_x00[AddrCmdDrvStren], program the
411 // corresponding 3 bit predriver code in F2x9C_x0A[D3Cmp1NCal, D3Cmp1PCal].
412 //
413 // 4. For each normalized driver strength code read from
414 // F2x[1, 0]9C_x00[DataDrvStren], program the corresponding
415 // 3 bit predriver code in F2x9C_x0A[D3Cmp0NCal, D3Cmp0PCal, D3Cmp2NCal,
416 // D3Cmp2PCal].
417 //
418 j = (UINT8) MemNGetBitFieldNb (NBPtr, BFAddrCmdDrvStren);
419 i = (UINT8) MemNGetBitFieldNb (NBPtr, BFDataDrvStren);
420
421 MemNSwitchDCTNb (NBPtr, 0);
422 MemNSetBitFieldNb (NBPtr, BFD3Cmp1NCal, TableCompRiseSlew20x[j]);
423 MemNSetBitFieldNb (NBPtr, BFD3Cmp1PCal, TableCompFallSlew20x[j]);
424
425 if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) {
426 MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
427 MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
428 MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]);
429 MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]);
430 }
431 if (NBPtr->IsSupported[CheckSlewWithoutMarginImprv]) {
432 ASSERT (i <= 3);
433 MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, TableCompRiseSlew15x[i]);
434 MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, TableCompFallSlew15x[i]);
435 MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, TableCompRiseSlew15x[i]);
436 MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, TableCompFallSlew15x[i]);
437 }
438 MemNSwitchDCTNb (NBPtr, CurrDct);
439}
440
441/* -----------------------------------------------------------------------------*/
442/**
443 *
444 *
445 * This is a general purpose function that executes before DRAM training
446 *
447 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
448 *
449 */
450
451VOID
452MemNBeforeDQSTrainingNb (
453 IN OUT MEM_NB_BLOCK *NBPtr
454 )
455{
456 UINT8 Dct;
457 UINT8 ChipSel;
458 UINT32 TestAddrRJ16;
459 UINT32 RealAddr;
460
461 MemTBeginTraining (NBPtr->TechPtr);
462
463 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
464 MemNSwitchDCTNb (NBPtr, Dct);
465 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
466 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) {
467 if (MemNGetMCTSysAddrNb (NBPtr, ChipSel, &TestAddrRJ16)) {
468
469 RealAddr = MemUSetUpperFSbase (TestAddrRJ16, NBPtr->MemPtr);
470
471 MemUDummyCLRead (RealAddr);
472
473 MemNSetBitFieldNb (NBPtr, BFErr350, 0x8000);
474 MemUWait10ns (60, NBPtr->MemPtr); // Wait 300ns
475 MemNSetBitFieldNb (NBPtr, BFErr350, 0x0000);
476 MemUWait10ns (400, NBPtr->MemPtr); // Wait 2us
477 MemUProcIOClFlush (TestAddrRJ16, 1, NBPtr->MemPtr);
478 break;
479 }
480 }
481 }
482 if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) {
483 if (!NBPtr->MCTPtr->Status[SbEccDimms]) {
484 MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010);
485 }
486 if (NBPtr->DCTPtr->Timings.Dimmx4Present == 0) {
487 MemNSetBitFieldNb (NBPtr, BFEccDLLConf, 0x0080);
488 }
489 }
490 }
491
492 MemTEndTraining (NBPtr->TechPtr);
493}
494
495/*-----------------------------------------------------------------------------*/
496/**
497 *
498 * Returns the parameters for a requested delay value to be used in training
499 * The correct Min, Max and Mask are determined based on the type of Delay,
500 * and the frequency
501 *
502 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
503 * @param[in] TrnDly - Type of delay
504 * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
505 *
506 */
507
508VOID
509MemNGetTrainDlyParmsNb (
510 IN OUT MEM_NB_BLOCK *NBPtr,
511 IN TRN_DLY_TYPE TrnDly,
512 IN OUT TRN_DLY_PARMS *Parms
513 )
514{
515 Parms->Min = 0;
516
517 if (TrnDly == AccessWrDatDly) {
518 Parms->Max = 0x1F;
519 Parms->Mask = 0x01F;
520 } else if (TrnDly == AccessRdDqsDly) {
521 if ( (NBPtr->IsSupported[CheckMaxRdDqsDlyPtr]) && (NBPtr->DCTPtr->Timings.Speed > DDR667_FREQUENCY) ) {
522 Parms->Max = 0x3E;
523 Parms->Mask = 0x03E;
524 } else {
525 Parms->Max = 0x1F;
526 Parms->Mask = 0x01F;
527 }
528 }
529}
530
531/*-----------------------------------------------------------------------------*/
532/**
533 *
534 * Returns the parameters for a requested delay value to be used in training
535 * The correct Min, Max and Mask are determined based on the type of Delay,
536 * and the frequency
537 *
538 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
539 * @param[in] TrnDly - Type of delay
540 * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
541 *
542 */
543
544VOID
545MemNGetTrainDlyParmsClientNb (
546 IN OUT MEM_NB_BLOCK *NBPtr,
547 IN TRN_DLY_TYPE TrnDly,
548 IN OUT TRN_DLY_PARMS *Parms
549 )
550{
551 Parms->Min = 0;
552
553 if (TrnDly == AccessWrDatDly) {
554 Parms->Max = 0x1F;
555 Parms->Mask = 0x01F;
556 } else if (TrnDly == AccessRdDqsDly) {
557 Parms->Max = 0x3E;
558 Parms->Mask = 0x03E;
559 }
560}
561/*-----------------------------------------------------------------------------*/
562/**
563 *
564 * Returns the parameters for a requested delay value to be used in training
565 * The correct Min, Max and Mask are determined based on the type of Delay,
566 * and the frequency
567 *
568 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
569 * @param[in] TrnDly - Type of delay
570 * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct
571 *
572 */
573
574VOID
575MemNGetTrainDlyParmsUnb (
576 IN OUT MEM_NB_BLOCK *NBPtr,
577 IN TRN_DLY_TYPE TrnDly,
578 IN OUT TRN_DLY_PARMS *Parms
579 )
580{
581 Parms->Min = 0;
582
583 if ((TrnDly == AccessWrDatDly) || (TrnDly == AccessRdDqsDly)) {
584 Parms->Max = 0x1F;
585 Parms->Mask = 0x01F;
586 }
587}
588/*----------------------------------------------------------------------------
589 * LOCAL FUNCTIONS
590 *
591 *----------------------------------------------------------------------------
592 */
593/* -----------------------------------------------------------------------------*/
594/**
595 *
596 *
597 * This function gets or set DQS timing during training.
598 *
599 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
600 * @param[in] TrnDly - type of delay to be set
601 * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
602 * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
603 * @param[in] Field - Value to be programmed
604 * @param[in] IsSet - Indicates if the function will set or get
605 *
606 * @return value read, if the function is used as a "get"
607 */
608
609UINT32
610MemNcmnGetSetTrainDlyNb (
611 IN OUT MEM_NB_BLOCK *NBPtr,
612 IN UINT8 IsSet,
613 IN TRN_DLY_TYPE TrnDly,
614 IN DRBN DrbnVar,
615 IN UINT16 Field
616 )
617{
618 UINT16 Index;
619 UINT16 Offset;
620 UINT32 Value;
621 UINT32 Address;
622 UINT8 Dimm;
623 UINT8 Rank;
624 UINT8 Byte;
625 UINT8 Nibble;
626
627 Dimm = DRBN_DIMM (DrbnVar);
628 Rank = DRBN_RANK (DrbnVar);
629 Byte = DRBN_BYTE (DrbnVar);
630 Nibble = DRBN_NBBL (DrbnVar);
631
632 ASSERT (Dimm < 4);
633 ASSERT (Byte <= ECC_DLY);
634
635 switch (TrnDly) {
636 case AccessRcvEnDly:
637 Index = 0x10;
638 break;
639 case AccessWrDqsDly:
640 Index = 0x30;
641 break;
642 case AccessWrDatDly:
643 Index = 0x01;
644 break;
645 case AccessRdDqsDly:
646 Index = 0x05;
647 break;
648 case AccessPhRecDly:
649 Index = 0x50;
650 break;
651 default:
652 Index = 0;
653 IDS_ERROR_TRAP;
654 }
655
656 switch (TrnDly) {
657 case AccessRcvEnDly:
658 case AccessWrDqsDly:
659 Index += (Dimm * 3);
660 if (Byte & 0x04) {
661 // if byte 4,5,6,7
662 Index += 0x10;
663 }
664 if (Byte & 0x02) {
665 // if byte 2,3,6,7
666 Index++;
667 }
668 if (Byte > 7) {
669 Index += 2;
670 }
671 Offset = 16 * (Byte % 2);
672 Index |= (Rank << 8);
673 Index |= (Nibble << 9);
674 break;
675
676 case AccessRdDqsDly:
677 case AccessWrDatDly:
678
679 if (NBPtr->IsSupported[DimmBasedOnSpeed]) {
680 if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) {
681 // if DDR speed is below 800, use DIMM 0 delays for all DIMMs.
682 Dimm = 0;
683 }
684 }
685
686 Index += (Dimm * 0x100);
687 if (Nibble) {
688 if (Rank) {
689 Index += 0xA0;
690 } else {
691 Index += 0x70;
692 }
693 } else if (Rank) {
694 Index += 0x60;
695 }
Jacob Garber4c33a3a2019-07-12 10:34:06 -0600696 // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence
zbao7d94cf92012-07-02 14:19:14 +0800697 case AccessPhRecDly:
698 Index += (Byte / 4);
699 Offset = 8 * (Byte % 4);
700 break;
701 default:
702 Offset = 0;
703 IDS_ERROR_TRAP;
704 }
705
706 Address = Index;
707 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
708 MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
709 Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
710
711 if (TrnDly == AccessRdDqsDly) {
712 NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset);
713 }
714
715 if (IsSet) {
716 if (TrnDly == AccessPhRecDly) {
717 Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
718 }
719
720 Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
721 MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
722 Address |= DCT_ACCESS_WRITE;
723 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
724 MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
725
726 if (TrnDly == AccessPhRecDly) {
727 NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
728 }
729 } else {
730 Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
731 }
732
733 return Value;
734}
735
736/* -----------------------------------------------------------------------------*/
737/**
738 *
739 * This function gets or set DQS timing during training.
740 *
741 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
742 * @param[in] IsSet - Indicates if the function will set or get
743 * @param[in] TrnDly - type of delay to be set
744 * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
745 * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
746 * @param[in] Field - Value to be programmed
747 *
748 * @return value read, if the function is used as a "get"
749 */
750UINT32
751MemNcmnGetSetTrainDlyClientNb (
752 IN OUT MEM_NB_BLOCK *NBPtr,
753 IN UINT8 IsSet,
754 IN TRN_DLY_TYPE TrnDly,
755 IN DRBN DrbnVar,
756 IN UINT16 Field
757 )
758{
759 UINT16 Index;
760 UINT16 Offset;
761 UINT32 Value;
762 UINT32 Address;
763 UINT8 Dimm;
764 UINT8 Byte;
765
766 Dimm = DRBN_DIMM (DrbnVar);
767 Byte = DRBN_BYTE (DrbnVar);
768
769 ASSERT (Dimm < 2);
770 ASSERT (Byte <= ECC_DLY);
771
772 if ((Byte > 7)) {
773 // Llano does not support ECC delay, so:
774 if (IsSet) {
775 // On write, ignore
776 return 0;
777 } else {
778 // On read, redirect to byte 0 to correct fence averaging
779 Byte = 0;
780 }
781 }
782
783 switch (TrnDly) {
784 case AccessRcvEnDly:
785 Index = 0x10;
786 break;
787 case AccessWrDqsDly:
788 Index = 0x30;
789 break;
790 case AccessWrDatDly:
791 Index = 0x01;
792 break;
793 case AccessRdDqsDly:
794 Index = 0x05;
795 break;
796 case AccessPhRecDly:
797 Index = 0x50;
798 break;
799 default:
800 Index = 0;
801 IDS_ERROR_TRAP;
802 }
803
804 switch (TrnDly) {
805 case AccessRcvEnDly:
806 case AccessWrDqsDly:
807 Index += (Dimm * 3);
808 if (Byte & 0x04) {
809 // if byte 4,5,6,7
810 Index += 0x10;
811 }
812 if (Byte & 0x02) {
813 // if byte 2,3,6,7
814 Index++;
815 }
816 Offset = 16 * (Byte % 2);
817 break;
818
819 case AccessRdDqsDly:
820 case AccessWrDatDly:
821 Index += (Dimm * 0x100);
Jacob Garber4c33a3a2019-07-12 10:34:06 -0600822 // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence
zbao7d94cf92012-07-02 14:19:14 +0800823 case AccessPhRecDly:
824 Index += (Byte / 4);
825 Offset = 8 * (Byte % 4);
826 break;
827 default:
828 Offset = 0;
829 IDS_ERROR_TRAP;
830 }
831
832 Address = Index;
833 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
834 Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
835
836 if (IsSet) {
837 if (TrnDly == AccessPhRecDly) {
838 Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
839 }
840
841 Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
842 MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
843 Address |= DCT_ACCESS_WRITE;
844 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
845
846 if (TrnDly == AccessPhRecDly) {
847 NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
848 }
849 // Gross WrDatDly and WrDqsDly cannot be larger than 4
850 ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
851 } else {
852 Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
853 }
854
855 return Value;
856}
857/* -----------------------------------------------------------------------------*/
858/**
859 *
860 *
861 * This function gets or set DQS timing during training.
862 *
863 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
864 * @param[in] TrnDly - type of delay to be set
865 * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed
866 * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding
867 * @param[in] Field - Value to be programmed
868 * @param[in] IsSet - Indicates if the function will set or get
869 *
870 * @return value read, if the function is used as a "get"
871 */
872
873UINT32
874MemNcmnGetSetTrainDlyUnb (
875 IN OUT MEM_NB_BLOCK *NBPtr,
876 IN UINT8 IsSet,
877 IN TRN_DLY_TYPE TrnDly,
878 IN DRBN DrbnVar,
879 IN UINT16 Field
880 )
881{
882 UINT16 Index;
883 UINT16 Offset;
884 UINT32 Value;
885 UINT32 Address;
886 UINT8 Dimm;
887 UINT8 Rank;
888 UINT8 Byte;
889 UINT8 Nibble;
890 UINT8 DimmNibble;
891
892 Dimm = DRBN_DIMM (DrbnVar);
893 Rank = DRBN_RANK (DrbnVar);
894 Byte = DRBN_BYTE (DrbnVar);
895 Nibble = DRBN_NBBL (DrbnVar);
896 DimmNibble = DRBN_DIMM_NBBL (DrbnVar);
897
898 ASSERT (Dimm < (NBPtr->CsPerChannel / NBPtr->CsPerDelay));
899 ASSERT (Byte <= ECC_DLY);
900 if ((Byte == ECC_DLY) && (!NBPtr->MCTPtr->Status[SbEccDimms] || !NBPtr->IsSupported[EccByteTraining])) {
901 // When ECC is not enabled
902 if (IsSet) {
903 // On write, ignore
904 return 0;
905 } else {
906 // On read, redirect to byte 0 to correct fence averaging
907 Byte = 0;
908 }
909 }
910
911 switch (TrnDly) {
912 case AccessRcvEnDly:
913 Index = 0x10;
914 break;
915 case AccessWrDqsDly:
916 Index = 0x30;
917 break;
918 case AccessWrDatDly:
919 Index = 0x01;
920 break;
921 case AccessRdDqsDly:
922 Index = 0x05;
923 break;
924 case excel845 :
925 Index = 0x00;
926 break;
927 case AccessPhRecDly:
928 Index = 0x50;
929 break;
930 default:
931 Index = 0;
932 IDS_ERROR_TRAP;
933 }
934
935 switch (TrnDly) {
936 case AccessRcvEnDly:
937 case AccessWrDqsDly:
938 Index += (Dimm * 3);
939 if (Byte & 0x04) {
940 // if byte 4,5,6,7
941 Index += 0x10;
942 }
943 if (Byte & 0x02) {
944 // if byte 2,3,6,7
945 Index++;
946 }
947 if (Byte > 7) {
948 Index += 2;
949 }
950 Offset = 16 * (Byte % 2);
951 Index |= (Rank << 8);
952 Index |= (Nibble << 9);
953 Address = Index;
954 break;
955
956 case AccessRdDqsDly:
957 case AccessWrDatDly:
958
959 if (NBPtr->IsSupported[DimmBasedOnSpeed]) {
960 if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) {
961 // if DDR speed is below 800, use DIMM 0 delays for all DIMMs.
962 Dimm = 0;
963 }
964 }
965
966 Index += (Dimm * 0x100);
967 if (Nibble) {
968 if (Rank) {
969 Index += 0xA0;
970 } else {
971 Index += 0x70;
972 }
973 } else if (Rank) {
974 Index += 0x60;
975 }
Jacob Garber4c33a3a2019-07-12 10:34:06 -0600976 // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence
zbao7d94cf92012-07-02 14:19:14 +0800977 case AccessPhRecDly:
978 Index += (Byte / 4);
979 Offset = 8 * (Byte % 4);
980 Address = Index;
981 break;
982 case excel845 :
983 Address = 0x0D0F0000;
984 Index += (DimmNibble >> 1) * 0x100;
985 Index += 0x20;
986 Index = Index + Dimm;
987 Offset = 4 * ((DimmNibble & 0x01) * 2);
988 Address += Index;
989 break;
990 default:
991 Offset = 0;
992 IDS_ERROR_TRAP;
993 Address = Index;
994 }
995 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
996 MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
997 Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
998 if (TrnDly == AccessRdDqsDly) {
999 NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset);
1000 }
1001
1002 if (IsSet) {
1003 if (TrnDly == AccessPhRecDly) {
1004 Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
1005 }
1006 if (TrnDly != excel845 ) {
1007 Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF) << Offset)));
1008 } else {
1009 Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) 0x1F << Offset)));
1010 }
1011 MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
1012 Address |= DCT_ACCESS_WRITE;
1013 MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1014 MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE);
1015 if (TrnDly == AccessPhRecDly) {
1016 NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
1017 }
1018 } else {
1019 if (TrnDly != excel845 ) {
1020 Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF);
1021 } else {
1022 Value = (Value >> Offset) & (UINT32) (0x1F);
1023 }
1024 }
1025 return Value;
1026}
1027/* -----------------------------------------------------------------------------*/
1028/**
1029 *
1030 * This function initializes the training pattern.
1031 *
1032 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1033 *
1034 * @return AGESA_STATUS - Result
1035 * AGESA_SUCCESS - Training pattern is ready to use
1036 * AGESA_ERROR - Unable to initialize the pattern.
1037 */
1038
1039AGESA_STATUS
1040MemNTrainingPatternInitNb (
1041 IN OUT MEM_NB_BLOCK *NBPtr
1042 )
1043{
1044 MEM_TECH_BLOCK *TechPtr;
1045 ALLOCATE_HEAP_PARAMS AllocHeapParams;
1046 TRAIN_PATTERN TrainPattern;
1047 AGESA_STATUS Status;
1048
1049 TechPtr = NBPtr->TechPtr;
1050 TrainPattern = 0;
1051 //
1052 // Check the training type
1053 //
1054 if (TechPtr->TrainingType == TRN_DQS_POSITION) {
1055 //
1056 // DQS Position Training
1057 //
1058 if (NBPtr->PosTrnPattern == POS_PATTERN_256B) {
1059 //
1060 // 256 Bit pattern
1061 //
1062 if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
1063 TrainPattern = TestPatternJD256B;
1064 TechPtr->PatternLength = 64;
1065 } else {
1066 TrainPattern = TestPatternJD256A;
1067 TechPtr->PatternLength = 32;
1068 }
1069 } else {
1070 //
1071 // 72 bit pattern will be used if PosTrnPattern is not specified
1072 //
1073 if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
1074 TrainPattern = TestPatternJD1B;
1075 TechPtr->PatternLength = 18;
1076 } else {
1077 TrainPattern = TestPatternJD1A;
1078 TechPtr->PatternLength = 9;
1079 }
1080 }
1081 } else if (TechPtr->TrainingType == TRN_MAX_READ_LATENCY) {
1082 //
1083 // Max Read Latency Training
1084 //
1085 TrainPattern = TestPatternML;
1086 TechPtr->PatternLength = (NBPtr->MCTPtr->Status[Sb128bitmode]) ? 6 : 3;
1087 } else {
1088 //
1089 // Error - TechPtr->Training Type must be set to one of the types handled in this function
1090 //
1091 ASSERT (FALSE);
1092 }
1093 //
1094 // Allocate training buffer
1095 //
1096 AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16;
1097 AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE;
1098 AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
1099 Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader);
1100 ASSERT (Status == AGESA_SUCCESS);
1101 if (Status != AGESA_SUCCESS) {
1102 return Status;
1103 }
1104 TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr;
1105 AlignPointerTo16Byte (&TechPtr->PatternBufPtr);
1106 TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64);
1107
1108 // Prepare training pattern
1109 MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
1110
1111 return Status;
1112}
1113
1114/* -----------------------------------------------------------------------------*/
1115/**
1116 *
1117 * This function determined the settings for the Reliable Read/Write engine
1118 * for each specific type of training
1119 *
1120 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1121 * @param[in] *OptParam - Pointer to an Enum of TRAINING_TYPE
1122 *
1123 * @return TRUE
1124 */
1125
1126BOOLEAN
1127MemNSetupHwTrainingEngineUnb (
1128 IN OUT MEM_NB_BLOCK *NBPtr,
1129 IN VOID *OptParam
1130 )
1131{
1132 TRAINING_TYPE TrnType;
1133 RRW_SETTINGS *Rrw;
1134
1135 TrnType = *(TRAINING_TYPE*) OptParam;
1136 Rrw = &NBPtr->RrwSettings;
1137 //
1138 // Common Settings
1139 //
1140 Rrw->TgtBankAddressA = CPG_BANK_ADDRESS_A;
1141 Rrw->TgtRowAddressA = CPG_ROW_ADDRESS_A;
1142 Rrw->TgtColAddressA = CPG_COL_ADDRESS_A;
1143 Rrw->TgtBankAddressB = CPG_BANK_ADDRESS_B;
1144 Rrw->TgtRowAddressB = CPG_ROW_ADDRESS_B;
1145 Rrw->TgtColAddressB = CPG_COL_ADDRESS_B;
1146 Rrw->CompareMaskHigh = CPG_COMPARE_MASK_HI;
1147 Rrw->CompareMaskLow = CPG_COMPARE_MASK_LOW;
1148 Rrw->CompareMaskEcc = CPG_COMPARE_MASK_ECC;
1149
1150 switch (TrnType) {
1151 case TRN_RCVR_ENABLE:
1152 //
1153 // Receiver Enable Training
1154 //
1155 NBPtr->TechPtr->PatternLength = 192;
1156 break;
1157 case TRN_MAX_READ_LATENCY:
1158 //
1159 // Max Read Latency Training
1160 //
1161 Rrw->CmdTgt = CMD_TGT_A;
1162 NBPtr->TechPtr->PatternLength = 32;
1163 Rrw->DataPrbsSeed = PRBS_SEED_32;
1164 break;
1165 case TRN_DQS_POSITION:
1166 //
1167 // Read/Write DQS Position training
1168 //
1169 Rrw->CmdTgt = CMD_TGT_AB;
1170 NBPtr->TechPtr->PatternLength = 256;
1171 Rrw->DataPrbsSeed = PRBS_SEED_256;
1172 break;
1173 default:
1174 ASSERT (FALSE);
1175 }
1176 return TRUE;
1177}
1178
1179/* -----------------------------------------------------------------------------*/
1180/**
1181 *
1182 * This function finalizes the training pattern.
1183 *
1184 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1185 * @param[in] Index - Index of Write Data Delay Value
1186 * @param[in,out] *Value - Write Data Delay Value
1187 * @return BOOLEAN - TRUE - Use the value returned.
1188 * FALSE - No more values in table.
1189 */
1190
1191BOOLEAN
1192MemNGetApproximateWriteDatDelayNb (
1193 IN OUT MEM_NB_BLOCK *NBPtr,
1194 IN UINT8 Index,
1195 IN OUT UINT8 *Value
1196 )
1197{
1198 CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F};
1199 if (Index < GET_SIZE_OF (WriteDatDelayValue)) {
1200 *Value = WriteDatDelayValue[Index];
1201 return TRUE;
1202 }
1203 return FALSE;
1204}
1205
1206
1207/* -----------------------------------------------------------------------------*/
1208/**
1209 *
1210 * This function finalizes the training pattern.
1211 *
1212 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1213 *
1214 * @return AGESA_STATUS - Result
1215 * AGESA_SUCCESS - Training pattern has been finalized.
1216 * AGESA_ERROR - Unable to initialize the pattern.
1217 */
1218
1219AGESA_STATUS
1220MemNTrainingPatternFinalizeNb (
1221 IN OUT MEM_NB_BLOCK *NBPtr
1222 )
1223{
1224 AGESA_STATUS Status;
1225 //
1226 // Deallocate training buffer
1227 //
1228 Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader);
1229 ASSERT (Status == AGESA_SUCCESS);
1230 return Status;
1231}
1232
1233/* -----------------------------------------------------------------------------*/
1234/**
1235 *
1236 * This function returns the number of Chipselects controlled by each set
1237 * of Delay registers under current conditions.
1238 *
1239 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1240 *
1241 * @return
1242 */
1243
1244UINT8
1245MemNCSPerDelayNb (
1246 IN OUT MEM_NB_BLOCK *NBPtr
1247 )
1248{
1249 if (MemNGetBitFieldNb (NBPtr, BFPerRankTimingEn) == 1) {
1250 return 1;
1251 } else {
1252 return 2;
1253 }
1254}
1255
1256/* -----------------------------------------------------------------------------*/
1257/**
1258 *
1259 * This function returns the minimum data eye width in 32nds of a UI for
1260 * the type of data eye(Rd/Wr) that is being trained. This value will
1261 * be the minimum number of consecutive delays that yield valid data.
1262 * Uses TechPtr->Direction to determine read or write.
1263 *
1264 *
1265 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1266 *
1267 * @return
1268 */
1269
1270UINT8
1271MemNMinDataEyeWidthNb (
1272 IN OUT MEM_NB_BLOCK *NBPtr
1273 )
1274{
1275 UINT8 MinRdDataeye;
1276 UINT8 MinWrDataeye;
1277 UINT8 *MinRdWrDataeyePtr;
1278
1279 MinRdWrDataeyePtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MIN_RD_WR_DATAEYE_WIDTH, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0,
1280 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
1281
1282 if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
1283 if (MinRdWrDataeyePtr != NULL) {
1284 MinRdDataeye = MinRdWrDataeyePtr[0];
1285 return MinRdDataeye;
1286 } else {
1287 return MIN_RD_DATAEYE_WIDTH_NB;
1288 }
1289 } else {
1290 if (MinRdWrDataeyePtr != NULL) {
1291 MinWrDataeye = MinRdWrDataeyePtr[1];
1292 return MinWrDataeye;
1293 } else {
1294 return MIN_WR_DATAEYE_WIDTH_NB;
1295 }
1296 }
1297}
1298
1299/* -----------------------------------------------------------------------------*/
1300/**
1301 *
1302 * This function programs the phy registers according to the desired phy VDDIO voltage level
1303 *
1304 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1305 *
1306 */
1307
1308VOID
1309MemNPhyVoltageLevelNb (
1310 IN OUT MEM_NB_BLOCK *NBPtr
1311 )
1312{
1313 BIT_FIELD_NAME BitField;
1314 BIT_FIELD_NAME BFEnd;
1315 UINT16 BFValue;
1316 UINT16 RegValue;
1317
1318 BFValue = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
1319 BFEnd = NBPtr->IsSupported[ProgramCsrComparator] ? BFCsrComparator : BFCmpVioLvl;
1320
1321 for (BitField = BFDataRxVioLvl; BitField <= BFEnd; BitField++) {
1322 RegValue = BFValue;
1323 if (BitField == BFCsrComparator) {
1324 RegValue >>= (3 - 2);
1325 // Setting this bit in DCT0 adjusts the comparator for DCT0 and DCT1. Setting this bit in DCT1 has no effect.
1326 NBPtr->SwitchDCT (NBPtr, 0);
1327 MemNSetBitFieldNb (NBPtr, BitField, RegValue);
1328 break;
1329 } else if (BitField == BFCmpVioLvl) {
1330 RegValue <<= (14 - 3);
1331 // Must set this bit on DCT0 even when DCT0 has no memory
1332 NBPtr->SwitchDCT (NBPtr, 0);
1333 MemNSetBitFieldNb (NBPtr, BitField, RegValue);
1334 }
1335 MemNBrdcstSetNb (NBPtr, BitField, RegValue);
1336 }
1337}
1338
1339/* -----------------------------------------------------------------------------*/
1340/**
1341 *
1342 *
1343 * This function adjusts Avg PRE value of Phy fence training according to specific CPU family.
1344 *
1345 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1346 * @param[in,out] *Value16 - Pointer to the value that we want to adjust
1347 *
1348 */
1349VOID
1350MemNPFenceAdjustUnb (
1351 IN OUT MEM_NB_BLOCK *NBPtr,
1352 IN OUT INT16 *Value16
1353 )
1354{
1355 *Value16 += 2; //The Avg PRE value is subtracted by 6 only.
1356}
1357
1358/* -----------------------------------------------------------------------------*/
1359/**
1360 *
1361 *
1362 * This function initializes the DDR phy compensation logic
1363 *
1364 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1365 *
1366 */
1367
1368VOID
1369MemNInitPhyCompClientNb (
1370 IN OUT MEM_NB_BLOCK *NBPtr
1371 )
1372{
1373 // Slew rate table array [x][y][z]
1374 // array[0]: slew rate for VDDIO 1.5V
1375 // array[1]: slew rate for VDDIO 1.35V
1376 // array[2]: slew rate for VDDIO 1.25V
1377 // array[x][y]: slew rate for a certain frequency
1378 // array[x][y][0]: frequency mask for current entry
1379 CONST STATIC UINT16 TxPrePNDataDqs[3][3][5] = {
1380 {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924},
1381 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1382 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1383 {{ (UINT16) DDR800, 0xFF6, 0xB6D, 0xB6D, 0x924},
1384 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1385 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1386 {{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924},
1387 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1388 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
1389 };
1390 CONST STATIC UINT16 TxPrePNCmdAddr[3][3][5] = {
1391 {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1392 { (UINT16) (DDR1066 + DDR1333), 0x6DB, 0x6DB, 0x6DB, 0x6DB},
1393 { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0xB6D, 0xB6D}},
1394 {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1395 { (UINT16) (DDR1066 + DDR1333), 0x924, 0x6DB, 0x6DB, 0x6DB},
1396 { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0x924, 0x924}},
1397 {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492},
1398 { (UINT16) (DDR1066 + DDR1333), 0xDAD, 0x924, 0x6DB, 0x492},
1399 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xDAD, 0xB64, 0xB64}}
1400 };
1401 CONST STATIC UINT16 TxPrePNClock[3][3][5] = {
1402 {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924},
1403 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xB6D},
1404 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}},
1405 {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
1406 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xDAD},
1407 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xDAD}},
1408 {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924},
1409 { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6},
1410 { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}
1411 };
1412
1413 CONST PHY_COMP_INIT_CLIENTNB PhyCompInitBitField[] = {
1414 // 3. Program TxPreP/TxPreN for Data and DQS according toTable 14 if VDDIO is 1.5V or Table 15 if 1.35V.
1415 // A. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0[A,6]={0000b, TxPreP, TxPreN}.
1416 // B. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]02={1000b, TxPreP, TxPreN}.
1417 {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqs},
1418 {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqs},
1419 {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqs},
1420 // 4. Program TxPreP/TxPreN for Cmd/Addr according toTable 16 if VDDIO is 1.5V or Table 17 if 1.35V.
1421 // A. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0][12,0E,0A,06]={0000b, TxPreP, TxPreN}.
1422 // B. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0]02={1000b, TxPreP, TxPreN}.
1423 {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddr},
1424 {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddr},
1425 {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1426 {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1427 {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddr},
1428 // 5. Program TxPreP/TxPreN for Clock according toTable 18 if VDDIO is 1.5V or Table 19 if 1.35V.
1429 // A. Program D18F2x[1,0]9C_x0D0F_2[1:0]02={1000b, TxPreP, TxPreN}.
1430 {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock1TxPreDriverCalPad0, 8, TxPrePNClock}
1431 };
1432
1433 BIT_FIELD_NAME CurrentBitField;
1434 UINT16 SpeedMask;
1435 CONST UINT16 (*TxPrePNArray)[5];
1436 UINT8 Voltage;
1437 UINT8 i;
1438 UINT8 j;
1439 UINT8 k;
1440 UINT8 Dct;
1441
1442 Dct = NBPtr->Dct;
1443 NBPtr->SwitchDCT (NBPtr, 0);
1444 // 1. Program D18F2x[1,0]9C_x0D0F_E003[DisAutoComp, DisablePreDriverCal] = {1b, 1b}.
1445 MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x6000);
1446 NBPtr->SwitchDCT (NBPtr, Dct);
1447
1448 SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66);
1449 Voltage = (UINT8) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage);
1450
1451 for (j = 0; j < GET_SIZE_OF (PhyCompInitBitField); j ++) {
1452 i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitField[j].IndexBitField);
1453 TxPrePNArray = PhyCompInitBitField[j].TxPrePN[Voltage];
1454 for (k = 0; k < 3; k ++) {
1455 if ((TxPrePNArray[k][0] & SpeedMask) != 0) {
1456 for (CurrentBitField = PhyCompInitBitField[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitField[j].EndTargetBitField; CurrentBitField ++) {
1457 MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitField[j].ExtraValue << 12) | TxPrePNArray[k][i + 1]));
1458 }
1459 break;
1460 }
1461 }
1462 ASSERT (k < 3);
1463 }
1464
1465 NBPtr->FamilySpecificHook[ForceAutoComp] (NBPtr, NBPtr);
1466}
1467
1468/*-----------------------------------------------------------------------------
1469 *
1470 *
1471 * This function re-enable phy compensation.
1472 *
1473 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1474 * @param[in,out] OptParam - Optional parameter
1475 *
1476 * @return TRUE
1477 * ----------------------------------------------------------------------------
1478 */
1479BOOLEAN
1480MemNReEnablePhyCompNb (
1481 IN OUT MEM_NB_BLOCK *NBPtr,
1482 IN OUT VOID *OptParam
1483 )
1484{
1485 UINT8 Dct;
1486
1487 Dct = NBPtr->Dct;
1488
1489 NBPtr->SwitchDCT (NBPtr, 0);
1490 // Clear DisableCal and set DisablePredriverCal
1491 MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x2000);
1492 NBPtr->SwitchDCT (NBPtr, Dct);
1493
1494 return TRUE;
1495}
1496
1497/*-----------------------------------------------------------------------------
1498 *
1499 *
1500 * This function calculates the value of WrDqDqsEarly and programs it into
1501 * the DCT and adds it to the WrDqsGrossDelay of each byte lane on each
1502 * DIMM of the channel.
1503 *
1504 *
1505 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1506 * @param[in,out] OptParam - Optional parameter
1507 *
1508 * @return TRUE
1509 * ----------------------------------------------------------------------------
1510 */
1511BOOLEAN
1512MemNCalcWrDqDqsEarlyUnb (
1513 IN OUT MEM_NB_BLOCK *NBPtr,
1514 IN OUT VOID *OptParam
1515 )
1516{
1517 MEM_TECH_BLOCK *TechPtr;
1518 DCT_STRUCT *DCTPtr;
1519 CH_DEF_STRUCT *ChannelPtr;
1520 UINT8 Dimm;
1521 UINT8 ByteLane;
1522 UINT8 *WrDqsDlysPtr;
1523 UINT8 WrDqDqsEarly;
1524 UINT8 ChipSel;
1525
1526 ASSERT ((NBPtr->IsSupported[WLSeedAdjust]) && (NBPtr->IsSupported[WLNegativeDelay]));
1527
1528 TechPtr = NBPtr->TechPtr;
1529 ChannelPtr = NBPtr->ChannelPtr;
1530 DCTPtr = NBPtr->DCTPtr;
1531
1532 ASSERT (NBPtr != NULL);
1533 ASSERT (ChannelPtr != NULL);
1534 ASSERT (DCTPtr != NULL);
1535 //
1536 // For each DIMM:
1537 // - The Critical Gross Delay (CGD) is the minimum GrossDly of all byte lanes and all DIMMs.
1538 // - If (CGD < 0) Then
1539 // - D18F2xA8_dct[1:0][WrDqDqsEarly] = ABS(CGD)
1540 // - WrDqsGrossDly = GrossDly + WrDqDqsEarly
1541 // - Else
1542 // - D18F2xA8_dct[1:0][WrDqDqsEarly] = 0.
1543 // - WrDqsGrossDly = GrossDly
1544 //
1545 WrDqDqsEarly = 0;
1546 if (TechPtr->WLCriticalDelay < 0) {
1547 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCalculating WrDqDqsEarly, adjusting WrDqs.\n");
1548 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMin. Critical Delay: %x\n", TechPtr->WLCriticalDelay);
1549 // We've saved the entire negative delay value, so take the ABS and convert to GrossDly.
1550 WrDqDqsEarly = (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20)));
1551 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n\n", WrDqDqsEarly);
1552 //
1553 // Loop through All WrDqsDlys on all DIMMs
1554 //
1555 for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay) {
1556 if ((NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
1557 ((DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSel)) != 0)) {
1558 //
1559 // If LRDIMMs, only include the physical dimms, not logical Dimms
1560 //
1561 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS %x:", ChipSel);
1562 Dimm = ChipSel / NBPtr->CsPerDelay;
1563
1564 WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
1565 for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
1566 WrDqsDlysPtr[ByteLane] += (WrDqDqsEarly << 5);
1567 NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]);
1568 IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]);
1569 }
1570 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
1571 }
1572 }
1573 }
1574 MemNSetBitFieldNb (NBPtr, BFWrDqDqsEarly, WrDqDqsEarly);
1575 return TRUE;
1576}
1577
1578/*-----------------------------------------------------------------------------
1579 *
1580 *
1581 * This function forces phy to M0 state
1582 *
1583 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1584 *
1585 * @return none
1586 * ----------------------------------------------------------------------------
1587 */
1588VOID
1589MemNForcePhyToM0Unb (
1590 IN OUT MEM_NB_BLOCK *NBPtr
1591 )
1592{
1593 IDS_SKIP_HOOK (IDS_FORCE_PHY_TO_M0, NBPtr, &(NBPtr->MemPtr->StdHeader)) {
1594 // 1. Program D18F2x9C_x0D0F_E013_dct[1:0] = 0118h.
1595 MemNBrdcstSetUnConditionalNb (NBPtr, BFPllRegWaitTime, 0x118);
1596 // 2. For each byte lane and each memory P-state: Program D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxSsbMntClkEn] = 0.
1597 MemNBrdcstSetUnConditionalNb (NBPtr, BFRxSsbMntClkEn, 0);
1598 // 3. D18F2xA8_dct[1:0][MemPhyPllPdMode] = 00b.
1599 MemNBrdcstSetUnConditionalNb (NBPtr, BFMemPhyPllPdMode, 0);
1600 // 4. Force the phy to M0 with the following sequence:
1601 // A. Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 190h. Restore the default PLL lock time.
1602 MemNBrdcstSetUnConditionalNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
1603 // B. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80800000h.
1604 MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80800000);
1605 NBPtr->SwitchDCT (NBPtr, 0);
1606 // C. Program D18F2x9C_x0D0F_E018_dct[0][PhyPSMasterChannel] = 0.
1607 MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0);
1608 // D. Program D18F2x9C_x0000_000B_dct[0] = 40000000h. CH0 only;
1609 MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x40000000);
1610 // E. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80000000h.
1611 MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
1612 }
1613}
1614
1615/*-----------------------------------------------------------------------------
1616 *
1617 *
1618 * This function sets SkewMemClk before enabling MemClk
1619 *
1620 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1621 * @param[in,out] *OptParam - Optional parameter
1622 *
1623 * @return TRUE - always
1624 * ----------------------------------------------------------------------------
1625 */
1626BOOLEAN
1627MemNSetSkewMemClkUnb (
1628 IN OUT MEM_NB_BLOCK *NBPtr,
1629 IN OUT VOID *OptParam
1630 )
1631{
1632 UINT8 Dct;
1633
1634 // SkewMemClk is set to 1 if all DCTs are enabled, else 0.
1635 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
1636 MemNSwitchDCTNb (NBPtr, Dct);
1637 if (NBPtr->DCTPtr->Timings.DctMemSize == 0) {
1638 break;
1639 }
1640 }
1641 MemNSwitchDCTNb (NBPtr, 0);
1642 if (Dct == NBPtr->DctCount) {
1643 MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0x10);
1644 } else {
1645 MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0);
1646 }
1647
1648 return TRUE;
1649}
1650
1651/* -----------------------------------------------------------------------------*/
1652/**
1653 *
1654 * This function masks the RdDqsDly Bit 0 before writing to register for UNB.
1655 *
1656 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1657 * @param[in,out] *Offset - Bit offset of the field to be programmed
1658 *
1659 * @return TRUE
1660 */
1661BOOLEAN
1662MemNAdjustRdDqsDlyOffsetUnb (
1663 IN OUT MEM_NB_BLOCK *NBPtr,
1664 IN OUT VOID *Offset
1665 )
1666{
1667 *(UINT16*) Offset = *(UINT16*) Offset + 1;
1668 return TRUE;
1669}
1670
1671/* -----------------------------------------------------------------------------*/
1672/**
1673 *
1674 *
1675 * This function delays MEMCLK to prevent WrDqs skew due to negative PRE result.
1676 *
1677 *
1678 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1679 * @param[in,out] OptParam - Optional parameter
1680 *
1681 * @return TRUE
1682 * ----------------------------------------------------------------------------
1683 */
1684BOOLEAN
1685MemNCalcWrDqDqsEarlyClientNb (
1686 IN OUT MEM_NB_BLOCK *NBPtr,
1687 IN OUT VOID *OptParam
1688 )
1689{
1690 MEM_TECH_BLOCK *TechPtr;
1691 DCT_STRUCT *DCTPtr;
1692 CH_DEF_STRUCT *ChannelPtr;
1693 UINT8 Dimm;
1694 UINT8 ByteLane;
1695 UINT8 *WrDqsDlysPtr;
1696 UINT8 NewClkDllDelay;
1697 UINT16 ClkDllFineDly;
1698 UINT32 AddrCmdTmg;
1699
1700 TechPtr = NBPtr->TechPtr;
1701 ChannelPtr = NBPtr->ChannelPtr;
1702 DCTPtr = NBPtr->DCTPtr;
1703
1704 ASSERT (NBPtr != NULL);
1705 ASSERT (ChannelPtr != NULL);
1706 ASSERT (DCTPtr != NULL);
1707
1708 if (NBPtr->IsSupported[WLNegativeDelay]) {
1709 if (TechPtr->WLCriticalDelay < 0) {
1710 NewClkDllDelay = (UINT8) ABS (TechPtr->WLCriticalDelay);
1711
1712 // Prepare new delay for MEMCLK
1713 ClkDllFineDly = (UINT16) ((MemNGetBitFieldNb (NBPtr, BFPhyClkDllFine0) & 0xBF60) | NewClkDllDelay);
1714
1715 // Program bit 7(FenceBit) = 1 if NewClkDllDelay >= > F2x9C[FenceThresholdTxPad], else 0.
1716 ClkDllFineDly |= (NewClkDllDelay >= MemNGetBitFieldNb (NBPtr, BFPhyFence)) ? 0x80 : 0;
1717
1718 // Apply new delay to both chiplets
1719 MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly | 0x4000);
1720 MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly);
1721 MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly | 0x4000);
1722 MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly);
1723 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tShift MemClk, AddrCmd, CsOdt, Cke by %d to eliminate negative WL\n", NewClkDllDelay);
1724
1725 //
1726 // Adjust AddrCmd/CsOdt/Cke timing by amount MemClk was delayed
1727 //
1728 AddrCmdTmg = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl);
1729 AddrCmdTmg += (NewClkDllDelay << 16) | (NewClkDllDelay << 8) | NewClkDllDelay;
1730 AddrCmdTmg &= 0x003F3F3F;
1731 MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrCmdTmg);
1732
1733 //
1734 // Adjust all WrDqsDlys on all DIMMs of the current channel
1735 //
1736 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
1737 if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) {
1738 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCS%d\n\t\t\tWrDqs:", Dimm << 1);
1739 WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]);
1740 for (ByteLane = 0; ByteLane < 8; ByteLane++) {
1741 WrDqsDlysPtr[ByteLane] = (UINT8) (WrDqsDlysPtr[ByteLane] + NewClkDllDelay);
1742 NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]);
1743 IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]);
1744 }
1745 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
1746 }
1747 }
1748 }
1749 }
1750
1751 return TRUE;
1752}
1753/* -----------------------------------------------------------------------------*/
1754/**
1755 *
1756 *
1757 * This function initializes RxEn Delays for RxEn seedless training
1758 *
1759 *
1760 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1761 * @param[in,out] OptParam - Optional parameter
1762 *
1763 * @return TRUE
1764 * ----------------------------------------------------------------------------
1765 */
1766BOOLEAN
1767MemNInitializeRxEnSeedlessTrainingUnb (
1768 IN OUT MEM_NB_BLOCK *NBPtr,
1769 IN OUT VOID *OptParam
1770 )
1771{
1772 UINT8 ByteLane;
1773 // Save original PRE based RxEnDly for RxEn Seedless training
1774 for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
1775 NBPtr->TechPtr->RxOrig[ByteLane] = NBPtr->ChannelPtr->RcvEnDlys[(NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) * NBPtr->TechPtr->DlyTableWidth () + ByteLane];
1776 }
1777 return TRUE;
1778}
1779/*-----------------------------------------------------------------------------
1780 *
1781 *
1782 * This function checks each bytelane for no window error.
1783 *
1784 *
1785 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1786 * @param[in,out] OptParam - Optional parameter
1787 *
1788 * @return TRUE
1789 * ----------------------------------------------------------------------------
1790 */
1791BOOLEAN
1792MemNTrackRxEnSeedlessRdWrNoWindBLErrorUnb (
1793 IN OUT MEM_NB_BLOCK *NBPtr,
1794 IN OUT VOID *OptParam
1795 )
1796{
1797 MemTTrackRxEnSeedlessRdWrNoWindBLError (NBPtr->TechPtr, OptParam);
1798 return TRUE;
1799}
1800/*-----------------------------------------------------------------------------
1801 *
1802 *
1803 * This function checks each bytelane for small window error.
1804 *
1805 *
1806 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1807 * @param[in,out] OptParam - Optional parameter
1808 *
1809 * @return TRUE
1810 * ----------------------------------------------------------------------------
1811 */
1812BOOLEAN
1813MemNTrackRxEnSeedlessRdWrSmallWindBLErrorUnb (
1814 IN OUT MEM_NB_BLOCK *NBPtr,
1815 IN OUT VOID *OptParam
1816 )
1817{
1818 MemTTrackRxEnSeedlessRdWrSmallWindBLError (NBPtr->TechPtr, OptParam);
1819 return TRUE;
1820}
1821/*-----------------------------------------------------------------------------
1822 *
1823 *
1824 * This function initializes a ByteLaneError error.
1825 *
1826 *
1827 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1828 * @param[in,out] OptParam - Optional parameter
1829 *
1830 * @return TRUE
1831 * ----------------------------------------------------------------------------
1832 */
1833BOOLEAN
1834MemNInitialzeRxEnSeedlessByteLaneErrorUnb (
1835 IN OUT MEM_NB_BLOCK *NBPtr,
1836 IN OUT VOID *OptParam
1837 )
1838{
1839 UINT8 ByteLane;
1840 for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) {
1841 NBPtr->TechPtr->ByteLaneError[ByteLane] = FALSE; // All Bytelanes have no errors
1842 }
1843 return TRUE;
1844}
1845/* -----------------------------------------------------------------------------*/
1846/**
1847 *
1848 *
1849 * This function sets phy power saving related settings in different MPstate context.
1850 *
1851 *
1852 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1853 *
1854 * @return none
1855 * ----------------------------------------------------------------------------
1856 */
1857VOID
1858MemNPhyPowerSavingMPstateUnb (
1859 IN OUT MEM_NB_BLOCK *NBPtr
1860 )
1861{
1862 STATIC UINT8 Sequence[] = {8, 4, 3, 5, 2, 6, 1, 7, 0};
1863 UINT16 DllPower[9];
1864 UINT8 NumLanes;
1865 UINT8 DllWakeTime;
1866 UINT8 MaxRxStggrDly;
1867 UINT8 MinRcvEnGrossDly;
1868 UINT8 MinWrDatGrossDly;
1869 UINT8 dRxStggrDly;
1870 UINT8 dTxStggrDly;
1871 UINT8 TempStggrDly;
1872 UINT8 MaxTxStggrDly;
1873 UINT8 Tcwl;
1874 UINT8 i;
1875
1876 IDS_HDT_CONSOLE (MEM_FLOW, "Start Phy power saving setting for memory Pstate %d\n", NBPtr->MemPstate);
1877 // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b.
1878 // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b.
1879 // 6. D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxDqsUDllPowerDown] = 1.
1880 MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x83);
1881 // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn].
1882 // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b}
1883 if (NBPtr->MemPstate == MEMORY_PSTATE0) {
1884 MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1);
1885 }
1886 // 9. IF (DimmsPopulated == 1)&& ((D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==010b) ||
1887 // (D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==011b)) THEN THEN
1888 // program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 1
1889 // ELSE program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 0 ENDIF.
1890 if ((NBPtr->ChannelPtr->Dimms == 1) && ((MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 2) || (MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 3))) {
1891 MemNSetBitFieldNb (NBPtr, BFLowPowerDrvStrengthEn, 0x100);
1892 }
1893 // 10. Program D18F2x9C_x0D0F_0[F,7:0][50,10]_dct[1:0][EnRxPadStandby] = IF
1894 // (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz) THEN 1 ELSE 0 ENDIF.
1895 MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0);
1896 // 11. Program D18F2x9C_x0000_000D_dct[1:0]_mp[1:0] as follows:
1897 // If (DDR rate < = 1600) TxMaxDurDllNoLock = RxMaxDurDllNoLock = 8h
1898 // else TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h.
1899 if (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) {
1900 MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 8);
1901 MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 8);
1902 } else {
1903 MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7);
1904 MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7);
1905 }
1906 // TxCPUpdPeriod = RxCPUpdPeriod = 011b.
1907 MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3);
1908 MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3);
1909 // TxDLLWakeupTime = RxDLLWakeupTime = 11b.
1910 MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3);
1911 MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3);
1912
1913 if (NBPtr->IsSupported[DllStaggerEn]) {
1914 // 12. Program D18F2x9C_x0D0F_0[F,7:0][5C,1C]_dct[1:0] as follows.
1915 // Let Numlanes = 8. = 9 with ECC.
1916 NumLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8;
1917 // RxDllStggrEn = TxDllStggrEn = 1.
1918 for (i = 0; i < 9; i ++) {
1919 DllPower[i] = 0x8080;
1920 }
1921 // If (DDR rate > = 1866) DllWakeTime = 1, Else DllWakeTime = 0.
1922 DllWakeTime = (NBPtr->DCTPtr->Timings.Speed >= DDR1866_FREQUENCY) ? 1 : 0;
1923 // Let MaxRxStggrDly = (Tcl*2) + MIN(DqsRcvEnGrossDelay for all byte lanes (see D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
1924 MinRcvEnGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessRcvEnDly, FALSE);
1925 ASSERT ((NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly) >= 4);
1926 MaxRxStggrDly = NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly - 4;
1927 // Let (real) dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (Numlanes - 1).
1928 ASSERT (MaxRxStggrDly >= DllWakeTime);
1929 dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (NumLanes - 1);
1930 IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum RcvEnGrossDly: 0x%02x MaxRxStggrDly: 0x%02x dRxStggrDly: 0x%02x\n", MinRcvEnGrossDly, MaxRxStggrDly, dRxStggrDly);
1931 // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program RxDllStggrDly[5:0] = an
1932 // increasing value, starting with 0 for the first byte lane in the sequence and increasing at a rate of dRxStggrDly
1933 // for each subsequent byte lane. Convert the real to integer by rounding down or using C (int) typecast after linearization.
1934 i = 9 - NumLanes;
1935 TempStggrDly = 0;
1936 for (; i < 9; i ++) {
1937 DllPower[Sequence[i]] |= ((TempStggrDly & 0x3F) << 8);
1938 TempStggrDly = TempStggrDly + dRxStggrDly;
1939 }
1940
1941 // Let MaxTxStggrDly = (Tcwl*2) + MIN(MIN (WrDatGrossDly for all byte lanes (see
1942 // D18F2x9C_x0000_0[3:0]0[2:1]_dct[1:0]_mp[1:0])), MIN(DqsRcvEnGrossDelay for all byte lanes (see
1943 // D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4.
1944 Tcwl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcwl);
1945 MinWrDatGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessWrDatDly, FALSE);
1946 MaxTxStggrDly = Tcwl * 2 + MIN (MinRcvEnGrossDly, MinWrDatGrossDly) - 4;
1947 // Let dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (Numlanes - 1).
1948 ASSERT (MaxTxStggrDly >= DllWakeTime);
1949 dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (NumLanes - 1);
1950 // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program TxDllStggrDly[5:0] = an
1951 // increasing integer value, starting with 0 for the first byte lane in the sequence and increasing at a rate of
1952 // dTxStggrDly for each subsequent byte lane.
1953 IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum WrDatGrossDly: 0x%02x MaxTxStggrDly: 0x%02x dTxStggrDly: 0x%02x\n", MinWrDatGrossDly, MaxTxStggrDly, dTxStggrDly);
1954 i = 9 - NumLanes;
1955 TempStggrDly = 0;
1956 for (; i < 9; i ++) {
1957 DllPower[Sequence[i]] |= (TempStggrDly & 0x3F);
1958 TempStggrDly = TempStggrDly + dTxStggrDly;
1959 }
1960
1961 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : ECC 07 06 05 04 03 02 01 00\n");
1962 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDll Power : %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",
1963 DllPower[8], DllPower[7], DllPower[6], DllPower[5], DllPower[4], DllPower[3], DllPower[2], DllPower[1], DllPower[0]);
1964
1965 for (i = 0; i < NumLanes; i ++) {
1966 MemNSetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i, (MemNGetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i) & 0x4040) | DllPower[i]);
1967 }
1968 }
1969 // 13. Program D18F2x248_dct[1:0]_mp[1:0] and then D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0] as follows:
1970 // For M1 context program RxChMntClkEn=RxSsbMntClkEn=0.
1971 // For M0 context program RxChMntClkEn=RxSsbMntClkEn=1.
1972 if (NBPtr->MemPstate == MEMORY_PSTATE1) {
1973 MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 0);
1974 MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0);
1975 } else {
1976 MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 1);
1977 MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0x100);
1978 }
1979
1980 IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader);
1981}
1982
1983/* -----------------------------------------------------------------------------*/
1984/**
1985 *
1986 * This function resets RxFifo pointer during Read DQS training
1987 *
1988 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1989 * @param[in,out] *OptParam - Optional parameter
1990 *
1991 * @return TRUE
1992 */
1993
1994BOOLEAN
1995MemNResetRxFifoPtrClientNb (
1996 IN OUT MEM_NB_BLOCK *NBPtr,
1997 IN OUT VOID *OptParam
1998 )
1999{
2000 if (NBPtr->TechPtr->Direction == DQS_READ_DIR) {
2001 MemNSetBitFieldNb (NBPtr, BFRxPtrInitReq, 1);
2002 MemNPollBitFieldNb (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, FALSE);
2003 }
2004 return TRUE;
2005}
2006
2007
2008/* -----------------------------------------------------------------------------*/
2009/**
2010 *
2011 * This function adjusts WrDqsBias before seed scaling
2012 *
2013 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2014 * @param[in,out] *WrDqsBias - Pointer to WrDqsBias
2015 *
2016 * @return FALSE - Supported
2017 * @return TRUE - Not supported
2018 */
2019
2020BOOLEAN
2021MemNAdjustWrDqsBeforeSeedScalingUnb (
2022 IN OUT MEM_NB_BLOCK *NBPtr,
2023 IN OUT VOID *WrDqsBias
2024 )
2025{
2026 // Subtract (0x20 * WrDqDqsEarly) since it is a non-scalable component
2027 * (INT16 *) WrDqsBias = (INT16) (0x20 * MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly));
2028 return TRUE;
2029}