2 * This file is part of the coreboot project.
4 * Copyright (C) 2010 Advanced Micro Devices, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *-----------------------------------------------------------------------------
24 *-----------------------------------------------------------------------------
27 /*----------------------------------------------------------------------------
28 * PROTOTYPES OF LOCAL FUNCTIONS
30 *----------------------------------------------------------------------------
32 u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue);
33 u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue);
34 void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl);
35 void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm);
36 void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass);
37 void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr);
38 void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
40 *-----------------------------------------------------------------------------
43 *-----------------------------------------------------------------------------
46 /*-----------------------------------------------------------------------------
47 * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
51 * This function initialized Hardware based write levelization phase 1
54 * IN OUT *SPDData - Pointer to buffer with information about each DIMMs
56 * *MCTData - Pointer to buffer with runtime parameters,
57 * *DCTData - Pointer to buffer with information about each DCT
59 * IN DIMM - Logical DIMM number
60 * Pass - First or Second Pass
62 *-----------------------------------------------------------------------------
64 void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
69 u16 Addl_Data_Offset, Addl_Data_Port;
71 pDCTData->WLPass = pass;
72 /* 1. Specify the target DIMM that is to be trained by programming
73 * F2x[1, 0]9C_x08[TrDimmSel].
75 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
76 DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
77 TrDimmSelEnd,(u32)dimm);
78 /* 2. Prepare the DIMMs for write levelization using DDR3-defined
80 prepareDimms(pMCTData, pDCTData,dimm, TRUE);
81 /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
82 * satisfy DDR3-defined internal DRAM timing.
84 pMCTData->AgesaDelay(40);
85 /* 4. Configure the processor's DDR phy for write levelization training: */
86 procConifg(pMCTData,pDCTData, dimm, pass);
87 /* 5. Begin write levelization training:
88 * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */
89 if (pDCTData->LogicalCPUID & AMD_DR_Cx)
90 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
91 DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
94 /* Broadcast write to all D3Dbyte chiplet register offset 0xc
96 * Program bit 4 to nibble being trained (only matters for x4dimms)
97 * retain value of 3:2 (Trdimmsel)
100 if (pDCTData->DctTrain)
102 Addl_Data_Offset=0x198;
103 Addl_Data_Port=0x19C;
107 Addl_Data_Offset=0x98;
111 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
112 while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
113 DctAccessDone, DctAccessDone)) == 0);
114 AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
115 Value = bitTestSet(Value, 0); /* enable WL training */
116 Value = bitTestReset(Value, 4); /* for x8 only */
117 Value = bitTestReset(Value, 5); /* for harward WL training */
118 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
120 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
121 while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
122 DctAccessDone, DctAccessDone)) == 0);
125 /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
126 pMCTData->AgesaDelay(140);
127 /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
128 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
129 DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
130 /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
131 * to get the gross and fine delay settings
132 * for the target DIMM and save these values. */
134 while (ByteLane < MAX_BYTE_LANES)
136 getWLByteDelay(pDCTData,ByteLane, dimm);
137 setWLByteDelay(pDCTData,ByteLane, dimm, 1);
141 /* 6. Configure DRAM Phy Control Register so that the phy stops driving
142 * write levelization ODT. */
143 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
144 DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0);
146 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
147 pMCTData->AgesaDelay(10);
149 /* 7. Program the target DIMM back to normal operation by configuring
150 * the following (See section 2.8.5.4.1.1
151 * [Phy Assisted Write Levelization] on page 97 pass 1, step #2):
152 * Configure all ranks of the target DIMM for normal operation.
153 * Enable the output drivers of all ranks of the target DIMM.
154 * For a two DIMM system, program the Rtt value for the target DIMM
155 * to the normal operating termination:
157 prepareDimms(pMCTData, pDCTData,dimm,FALSE);
160 /*----------------------------------------------------------------------------
163 *----------------------------------------------------------------------------
166 /*-----------------------------------------------------------------------------
167 * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
170 * This function swaps the bits in MSR register value
173 * IN OUT *DCTData - Pointer to buffer with information about each DCT
175 * OUT u32: sWAPPED BANK BITS
177 * ----------------------------------------------------------------------------
179 u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
183 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
184 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
187 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
189 /* swap A3/A4,A5/A6,A7/A8 */
195 MRSValue |= (tempW<<1);
196 MRSValue |= (tempW1>>1);
202 /*-----------------------------------------------------------------------------
203 * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
206 * This function swaps the bits in MSR register value
209 * IN OUT *DCTData - Pointer to buffer with information about each DCT
211 * OUT u32: sWAPPED BANK BITS
213 * ----------------------------------------------------------------------------
215 u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
219 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
220 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
223 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
231 MRSValue |= (tempW<<1);
232 MRSValue |= (tempW1>>1);
238 /*-----------------------------------------------------------------------------
239 * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
242 * This function prepares DIMMS for training
245 * IN OUT *DCTData - Pointer to buffer with information about each DCT
246 * *SPDData - Pointer to buffer with information about each DIMMs
248 * *MCTData - Pointer to buffer with runtime parameters,
249 * IN Dimm - Logical DIMM number
250 * WL - indicates if the routine is used for Write levelization
255 * ----------------------------------------------------------------------------
257 void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
259 u32 tempW, tempW1, tempW2, MrsBank;
260 u8 rank, currDimm, MemClkFreq;
262 MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
263 FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
264 /* Configure the DCT to send initialization MR commands to the target DIMM
265 * ;by programming the F2x[1,0]7C register using the following steps.
268 while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2))
270 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */
271 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
272 DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, dimm*2+rank);
273 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
274 * ;register that defines the required DDR3-defined function for write
277 MrsBank = swapBankBits(pDCTData,1);
278 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
279 DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
280 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
281 * ; for write levelization.
283 tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */
285 /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
286 tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
287 FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
290 if (pDCTData->DimmX8Present[dimm])
294 /* determine Rtt_Nom for WL & Normal mode */
295 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
296 tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
300 if (pDCTData->MaxDimmsInstalled == 1)
302 if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0))
304 tempW1 = 0x00; /* Rtt_Nom=OFF */
308 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
311 else /* 2 Dimms or more per channel */
313 if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1))
315 tempW1 = 0x00; /* Rtt_Nom=OFF */
319 if (MemClkFreq == 6) {
320 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
322 tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */
327 else { /* 1 or 4 Dimms per channel */
328 if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4))
330 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
332 else /* 2 or 3 Dimms per channel */
334 if (MemClkFreq < 5) {
335 tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
337 tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
344 /* All ranks of the target DIMM are set to write levelization mode. */
347 tempW1 = bitTestSet(tempW, MRS_Level);
350 /* ?Enable the output driver of the first rank of the target DIMM. */
355 /* Disable the output drivers of all other ranks for
356 * the target DIMM. */
357 tempW = bitTestSet(tempW1, Qoff);
360 /* program MrsAddress[5,1]=output driver impedance control (DIC):
361 * based on F2x[1,0]84[DrvImpCtrl] */
362 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
363 FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
364 if (bitTest(tempW1,1))
365 {tempW = bitTestSet(tempW, 5);}
366 if (bitTest(tempW1,0))
367 {tempW = bitTestSet(tempW, 1);}
369 tempW = swapAddrBits_wl(pDCTData,tempW);
371 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
372 DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
373 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
374 * ;the specified DIMM.
376 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
377 DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
378 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
379 while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
380 FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
383 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
384 * ;register that defines the required DDR3-defined function for Rtt_WR.
386 MrsBank = swapBankBits(pDCTData,2);
387 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
388 DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
389 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
390 * ; for Rtt_WR (DRAMTermDyn).
392 tempW = 0;/* PASR = 0,*/
393 /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
394 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
395 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
396 FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
397 if (bitTest(tempW1,19))
398 {tempW = bitTestSet(tempW, 7);}
399 if (bitTest(tempW1,18))
400 {tempW = bitTestSet(tempW, 6);}
401 /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
402 tempW=tempW|((tempW1&0x00700000)>>17);
403 /* workaround for DR-B0 */
404 if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
406 /* determine Rtt_WR for WL & Normal mode */
407 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
408 tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
412 tempW1 = 0x00; /* Rtt_WR=off */
416 if (pDCTData->MaxDimmsInstalled == 1)
418 tempW1 = 0x00; /* Rtt_WR=off */
422 if (MemClkFreq == 6) {
423 tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
425 tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
431 tempW = swapAddrBits_wl(pDCTData,tempW);
432 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
433 DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
434 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
435 the specified DIMM.*/
436 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
437 DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
438 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
439 while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
440 FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
447 /* Configure the non-target DIMM normally. */
449 while (currDimm < MAX_LDIMMS)
451 if (pDCTData->DimmPresent[currDimm])
453 if (currDimm != dimm)
456 while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2))
459 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
462 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
463 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, currDimm*2+rank);
464 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal
465 * ;DRAM register that defines the required DDR3-defined function
466 * ; for write levelization.
468 MrsBank = swapBankBits(pDCTData,1);
469 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
470 FUN_DCT, DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
471 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required
472 * ;DDR3-defined function for write levelization.
474 tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */
476 /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
477 tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
478 FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
481 if (pDCTData->DimmX8Present[currDimm])
485 /* determine Rtt_Nom for WL & Normal mode */
486 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
487 tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
491 if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1))
493 tempW1 = 0x00; /* Rtt_Nom=OFF */
497 if (MemClkFreq < 5) {
498 tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */
500 tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */
504 else { /* 1 or 4 Dimms per channel */
505 if ((pDCTData->MaxDimmsInstalled == 4))
507 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
509 else { /* 2 or 3 Dimms per channel */
510 if (MemClkFreq < 5) {
511 tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
513 tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
519 /* program MrsAddress[5,1]=output driver impedance control (DIC):
520 * based on F2x[1,0]84[DrvImpCtrl] */
521 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
522 FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
523 if (bitTest(tempW1,1))
524 {tempW = bitTestSet(tempW, 5);}
525 if (bitTest(tempW1,0))
526 {tempW = bitTestSet(tempW, 1);}
527 tempW = swapAddrBits_wl(pDCTData,tempW);
528 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
529 FUN_DCT, DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
530 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command
531 * ; to the specified DIMM.
533 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
534 FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
535 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
536 while ((get_Bits(pDCTData, pDCTData->CurrDct,
537 pDCTData->NodeId, FUN_DCT, DRAM_INIT,
538 SendMrsCmd, SendMrsCmd)) == 1);
539 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
540 * ;register that defines the required DDR3-defined function for Rtt_WR.
542 MrsBank = swapBankBits(pDCTData,2);
543 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
544 DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
545 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
546 * ; for Rtt_WR (DRAMTermDyn).
548 tempW = 0;/* PASR = 0,*/
549 /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
550 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
551 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
552 FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
553 if (bitTest(tempW1,19))
554 {tempW = bitTestSet(tempW, 7);}
555 if (bitTest(tempW1,18))
556 {tempW = bitTestSet(tempW, 6);}
557 /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
558 tempW=tempW|((tempW1&0x00700000)>>17);
559 /* workaround for DR-B0 */
560 if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
562 /* determine Rtt_WR for WL & Normal mode */
563 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
564 tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
568 tempW1 = 0x00; /* Rtt_WR=off */
572 if (MemClkFreq == 6) {
573 tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
575 tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
580 tempW = swapAddrBits_wl(pDCTData,tempW);
581 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
582 DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
583 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
584 the specified DIMM.*/
585 set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
586 DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
587 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
588 while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
589 FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
600 /*-----------------------------------------------------------------------------
601 * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm)
604 * This function programs the ODT values for the NB
607 * IN OUT *DCTData - Pointer to buffer with information about each DCT
610 * ----------------------------------------------------------------------------
612 void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
616 if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
617 if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
619 } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
620 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
622 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
625 WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
628 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
629 DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
633 /*-----------------------------------------------------------------------------
634 * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
637 * This function programs the ODT values for the NB
640 * IN OUT *DCTData - Pointer to buffer with information about each DCT
641 * *MCTData - Pointer to buffer with runtime parameters,
642 * IN Dimm - Logical DIMM
643 * Pass - First of Second Pass
645 * ----------------------------------------------------------------------------
647 void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
649 u8 ByteLane, Seed_Gross, Seed_Fine;
651 u16 Addl_Data_Offset, Addl_Data_Port;
653 /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
654 * ;current memory subsystem configuration.
656 programODT(pMCTData, pDCTData, dimm);
658 /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
659 if (pDCTData->LogicalCPUID & AMD_DR_Cx)
660 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
661 DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn,(u32) 1);
664 /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B*/
665 if (pDCTData->DctTrain)
667 Addl_Data_Offset=0x198;
668 Addl_Data_Port=0x19C;
672 Addl_Data_Offset=0x98;
676 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
677 while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
678 DctAccessDone, DctAccessDone)) == 0);
679 AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
680 Value = bitTestSet(Value, 12);
681 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
683 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
684 while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
685 DctAccessDone, DctAccessDone)) == 0);
688 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
689 pMCTData->AgesaDelay(10);
693 if (pDCTData->Status[DCT_STATUS_REGISTERED])
695 if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
711 while(ByteLane < MAX_BYTE_LANES)
713 /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
714 * ;F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields
715 * ; If the target frequency is different than 400MHz, BIOS must
716 * execute two training passes for each DIMM.
717 * For pass 1 at a 400MHz MEMCLK frequency, use an initial total delay value
718 * ; of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
721 pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
722 pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
726 setWLByteDelay(pDCTData, ByteLane, dimm, 0);
729 /*-----------------------------------------------------------------------------
730 * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
733 * This function writes the write levelization byte delay for the Phase
734 * Recovery control registers
737 * IN OUT *DCTData - Pointer to buffer with information about each DCT
738 * IN Dimm - Dimm Number
739 * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for each
741 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each
743 * ByteLane - target byte lane to write
744 * targetAddr - 0: write to DRAM phase recovery control register
745 * 1: write to DQS write register
748 *-----------------------------------------------------------------------------
750 void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
752 u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
753 u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW;
757 index = (u8)(MAX_BYTE_LANES * dimm);
762 while (ByteLane < MAX_BYTE_LANES)
764 /* This subtract 0xC workaround might be temporary. */
765 if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain))))
767 tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
769 pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
770 pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
772 grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
773 /* Adjust seed gross delay overflow (greater than 3):
774 * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
775 * - Keep original seed gross delay for later reference.
777 if(grossDelayValue >= 3)
779 grossDelayValue = (grossDelayValue&1)? 1 : 2;
781 fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
783 ValueLow |= ((grossDelayValue << 5) | fineDelayValue) << 8*ByteLane;
784 else if(ByteLane < 8)
785 ValueHigh |= ((grossDelayValue << 5) | fineDelayValue) << 8*(ByteLane-4);
787 EccValue = ((grossDelayValue << 5) | fineDelayValue);
790 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
791 DRAM_CONT_ADD_PHASE_REC_CTRL_LOW, 0, 31, (u32)ValueLow);
792 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
793 DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh);
794 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
795 DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue);
799 index = (u8)(MAX_BYTE_LANES * dimm);
800 grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
801 fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
804 offsetAddr = (u8)(3 * dimm);
807 tempB = (u8)(16 * ByteLane);
808 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01;
810 else if (ByteLane <4)
812 tempB = (u8)(16 * ByteLane);
813 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 1;
815 else if (ByteLane <6)
817 tempB = (u8)(16 * ByteLane);
818 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45;
820 else if (ByteLane <8)
822 tempB = (u8)(16 * ByteLane);
823 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 + 1;
828 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2;
832 fineStartLoc = (u8)(tempB % 32);
833 fineEndLoc = (u8)(fineStartLoc + 4);
834 grossStartLoc = (u8)(fineEndLoc + 1);
835 grossEndLoc = (u8)(grossStartLoc + 1);
837 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
838 (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue);
839 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
840 (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue);
845 /*-----------------------------------------------------------------------------
846 * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
849 * This function reads the write levelization byte delay from the Phase
850 * Recovery control registers
853 * IN OUT *DCTData - Pointer to buffer with information about each DCT
854 * IN Dimm - Dimm Number
855 * ByteLane - target byte lane to read
857 * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for current
858 * byte for logical DIMM
859 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for current
860 * byte for logical DIMM
862 *-----------------------------------------------------------------------------
864 void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
866 u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
867 u32 addr, fine, gross;
869 index = (u8)(MAX_BYTE_LANES*dimm);
872 tempB = (u8)(8 * ByteLane);
873 addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW;
875 else if (ByteLane < 8)
877 tempB1 = (u8)(ByteLane - 4);
878 tempB = (u8)(8 * tempB1);
879 addr = DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH;
884 addr = DRAM_CONT_ADD_ECC_PHASE_REC_CTRL;
886 fineStartLoc = tempB;
887 fineEndLoc = (u8)(fineStartLoc + 4);
888 grossStartLoc = (u8)(fineEndLoc + 1);
889 grossEndLoc = (u8)(grossStartLoc + 1);
891 fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
892 FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc);
893 gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
894 FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc);
895 /* Adjust seed gross delay overflow (greater than 3):
896 * - Adjust the trained gross delay to the original seed gross delay.
898 if(pDCTData->WLGrossDelay[index+ByteLane] >= 3)
900 gross += pDCTData->WLGrossDelay[index+ByteLane];
901 if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
906 else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3))
908 /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
909 * We will then round the negative number to 0.
914 pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
915 pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;