2 * This file is part of the coreboot project.
4 * Copyright (C) 2010 Advanced Micro Devices, Inc.
5 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 *-----------------------------------------------------------------------------
21 *-----------------------------------------------------------------------------
24 /*----------------------------------------------------------------------------
25 * PROTOTYPES OF LOCAL FUNCTIONS
27 *----------------------------------------------------------------------------
29 u32
swapAddrBits_wl(sDCTStruct
*pDCTData
, u32 MRSValue
);
30 u32
swapBankBits(sDCTStruct
*pDCTData
, u32 MRSValue
);
31 void prepareDimms(sMCTStruct
*pMCTData
, sDCTStruct
*pDCTData
, u8 dimm
, BOOL wl
);
32 void programODT(sMCTStruct
*pMCTData
, sDCTStruct
*pDCTData
, u8 dimm
);
33 void procConifg(sMCTStruct
*pMCTData
,sDCTStruct
*pDCTData
, u8 dimm
, u8 pass
);
34 void setWLByteDelay(sDCTStruct
*pDCTData
, u8 ByteLane
, u8 dimm
, u8 targetAddr
);
35 void getWLByteDelay(sDCTStruct
*pDCTData
, u8 ByteLane
, u8 dimm
);
37 *-----------------------------------------------------------------------------
40 *-----------------------------------------------------------------------------
43 /*-----------------------------------------------------------------------------
44 * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
48 * This function initialized Hardware based write levelization phase 1
51 * IN OUT *SPDData - Pointer to buffer with information about each DIMMs
53 * *MCTData - Pointer to buffer with runtime parameters,
54 * *DCTData - Pointer to buffer with information about each DCT
56 * IN DIMM - Logical DIMM number
57 * Pass - First or Second Pass
59 *-----------------------------------------------------------------------------
61 void AgesaHwWlPhase1(sMCTStruct
*pMCTData
, sDCTStruct
*pDCTData
,
66 u16 Addl_Data_Offset
, Addl_Data_Port
;
68 pDCTData
->WLPass
= pass
;
69 /* 1. Specify the target DIMM that is to be trained by programming
70 * F2x[1, 0]9C_x08[TrDimmSel].
72 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
73 DRAM_ADD_DCT_PHY_CONTROL_REG
, TrDimmSelStart
,
74 TrDimmSelEnd
,(u32
)dimm
);
75 /* 2. Prepare the DIMMs for write levelization using DDR3-defined
77 prepareDimms(pMCTData
, pDCTData
,dimm
, TRUE
);
78 /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
79 * satisfy DDR3-defined internal DRAM timing.
81 pMCTData
->AgesaDelay(40);
82 /* 4. Configure the processor's DDR phy for write levelization training: */
83 procConifg(pMCTData
,pDCTData
, dimm
, pass
);
84 /* 5. Begin write levelization training:
85 * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */
86 if (pDCTData
->LogicalCPUID
& (AMD_DR_Cx
| AMD_DR_Dx
))
87 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
88 DRAM_ADD_DCT_PHY_CONTROL_REG
, WrtLvTrEn
, WrtLvTrEn
, 1);
91 /* Broadcast write to all D3Dbyte chipset register offset 0xc
93 * Program bit 4 to nibble being trained (only matters for x4dimms)
94 * retain value of 3:2 (Trdimmsel)
97 if (pDCTData
->DctTrain
)
99 Addl_Data_Offset
=0x198;
100 Addl_Data_Port
=0x19C;
104 Addl_Data_Offset
=0x98;
108 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Offset
), 31, 0, &Addr
);
109 while ((get_Bits(pDCTData
,FUN_DCT
,pDCTData
->NodeId
, FUN_DCT
, Addl_Data_Offset
,
110 DctAccessDone
, DctAccessDone
)) == 0);
111 AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Port
), 31, 0, &Value
);
112 Value
= bitTestSet(Value
, 0); /* enable WL training */
113 Value
= bitTestReset(Value
, 4); /* for x8 only */
114 Value
= bitTestReset(Value
, 5); /* for hardware WL training */
115 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Port
), 31, 0, &Value
);
117 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Offset
), 31, 0, &Addr
);
118 while ((get_Bits(pDCTData
,FUN_DCT
,pDCTData
->NodeId
, FUN_DCT
, Addl_Data_Offset
,
119 DctAccessDone
, DctAccessDone
)) == 0);
122 /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
123 pMCTData
->AgesaDelay(140);
124 /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
125 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
126 DRAM_ADD_DCT_PHY_CONTROL_REG
, WrtLvTrEn
, WrtLvTrEn
, 0);
127 /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
128 * to get the gross and fine delay settings
129 * for the target DIMM and save these values. */
131 while (ByteLane
< MAX_BYTE_LANES
)
133 getWLByteDelay(pDCTData
,ByteLane
, dimm
);
134 setWLByteDelay(pDCTData
,ByteLane
, dimm
, 1);
138 /* 6. Configure DRAM Phy Control Register so that the phy stops driving
139 * write levelization ODT. */
140 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
141 DRAM_ADD_DCT_PHY_CONTROL_REG
, WrLvOdtEn
, WrLvOdtEn
, 0);
143 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
144 pMCTData
->AgesaDelay(10);
146 /* 7. Program the target DIMM back to normal operation by configuring
147 * the following (See section 2.8.5.4.1.1
148 * [Phy Assisted Write Levelization] on page 97 pass 1, step #2):
149 * Configure all ranks of the target DIMM for normal operation.
150 * Enable the output drivers of all ranks of the target DIMM.
151 * For a two DIMM system, program the Rtt value for the target DIMM
152 * to the normal operating termination:
154 prepareDimms(pMCTData
, pDCTData
,dimm
,FALSE
);
157 /*----------------------------------------------------------------------------
160 *----------------------------------------------------------------------------
163 /*-----------------------------------------------------------------------------
164 * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
167 * This function swaps the bits in MSR register value
170 * IN OUT *DCTData - Pointer to buffer with information about each DCT
172 * OUT u32: Swapped BANK BITS
174 * ----------------------------------------------------------------------------
176 u32
swapAddrBits_wl(sDCTStruct
*pDCTData
, u32 MRSValue
)
180 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
181 FUN_DCT
, DRAM_INIT
, MrsChipSelStart
, MrsChipSelEnd
);
184 if ((pDCTData
->Status
[DCT_STATUS_OnDimmMirror
]))
186 /* swap A3/A4,A5/A6,A7/A8 */
192 MRSValue
|= (tempW
<<1);
193 MRSValue
|= (tempW1
>>1);
199 /*-----------------------------------------------------------------------------
200 * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
203 * This function swaps the bits in MSR register value
206 * IN OUT *DCTData - Pointer to buffer with information about each DCT
208 * OUT u32: Swapped BANK BITS
210 * ----------------------------------------------------------------------------
212 u32
swapBankBits(sDCTStruct
*pDCTData
, u32 MRSValue
)
216 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
217 FUN_DCT
, DRAM_INIT
, MrsChipSelStart
, MrsChipSelEnd
);
220 if ((pDCTData
->Status
[DCT_STATUS_OnDimmMirror
]))
228 MRSValue
|= (tempW
<<1);
229 MRSValue
|= (tempW1
>>1);
235 static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms
, uint8_t frequency_index
, uint8_t rank_count
, uint8_t rank
)
240 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
241 * For now assume a maximum of 2 DIMMs per channel can be installed
243 uint8_t MaxDimmsInstallable
= 2;
245 if (number_of_dimms
== 1) {
246 if (MaxDimmsInstallable
< 3) {
247 term
= 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
249 if (rank_count
== 1) {
250 term
= 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
253 term
= 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
255 term
= 0x00; /* Rtt_Nom=OFF */
259 if (frequency_index
< 5)
260 term
= 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
262 term
= 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
268 static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms
, uint8_t frequency_index
, uint8_t rank_count
, uint8_t rank
)
273 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
274 * For now assume a maximum of 2 DIMMs per channel can be installed
276 uint8_t MaxDimmsInstallable
= 2;
278 if (number_of_dimms
== 1) {
279 if (MaxDimmsInstallable
< 3) {
280 term
= 0x00; /* Rtt_WR=off */
283 term
= 0x00; /* Rtt_WR=off */
285 term
= 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
288 term
= 0x400; /* Rtt_WR=RZQ/2=120 Ohm */
294 /*-----------------------------------------------------------------------------
295 * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
298 * This function prepares DIMMS for training
301 * IN OUT *DCTData - Pointer to buffer with information about each DCT
302 * *SPDData - Pointer to buffer with information about each DIMMs
304 * *MCTData - Pointer to buffer with runtime parameters,
305 * IN Dimm - Logical DIMM number
306 * WL - indicates if the routine is used for Write levelization
311 * ----------------------------------------------------------------------------
313 void prepareDimms(sMCTStruct
*pMCTData
, sDCTStruct
*pDCTData
, u8 dimm
, BOOL wl
)
315 u32 tempW
, tempW1
, tempW2
, MrsBank
;
316 u8 rank
, currDimm
, MemClkFreq
;
318 MemClkFreq
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
319 FUN_DCT
, DRAM_CONFIG_HIGH
, 0, 2);
320 /* Configure the DCT to send initialization MR commands to the target DIMM
321 * by programming the F2x[1,0]7C register using the following steps.
324 while ((rank
< pDCTData
->DimmRanks
[dimm
]) && (rank
< 2))
326 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */
327 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
328 DRAM_INIT
, MrsChipSelStart
, MrsChipSelEnd
, dimm
*2+rank
);
329 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
330 * register that defines the required DDR3-defined function for write
333 MrsBank
= swapBankBits(pDCTData
,1);
334 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
335 DRAM_INIT
, MrsBankStart
, MrsBankEnd
, MrsBank
);
336 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
337 * for write levelization.
339 tempW
= 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */
341 /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
342 tempW2
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
343 FUN_DCT
, DRAM_CONFIG_HIGH
, RDqsEn
, RDqsEn
);
346 if (pDCTData
->DimmX8Present
[dimm
])
350 /* determine Rtt_Nom for WL & Normal mode */
351 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
]) {
352 tempW1
= RttNomTargetRegDimm(pMCTData
, pDCTData
, dimm
, wl
, MemClkFreq
, rank
);
356 /* Get Rtt_WR for the current DIMM and rank */
357 uint16_t dynamic_term
= unbuffered_dimm_dynamic_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[dimm
], rank
);
359 /* Convert dynamic termination code to corresponding nominal termination code */
360 if (dynamic_term
== 0x200)
362 else if (dynamic_term
== 0x400)
367 tempW1
= unbuffered_dimm_nominal_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[dimm
], rank
);
370 tempW1
= unbuffered_dimm_nominal_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[dimm
], rank
);
375 /* All ranks of the target DIMM are set to write levelization mode. */
378 tempW1
= bitTestSet(tempW
, MRS_Level
);
381 /* Enable the output driver of the first rank of the target DIMM. */
386 /* Disable the output drivers of all other ranks for
389 tempW
= bitTestSet(tempW1
, Qoff
);
392 /* Program MrsAddress[5,1]=output driver impedance control (DIC):
393 * based on F2x[1,0]84[DrvImpCtrl]
395 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
396 FUN_DCT
, DRAM_MRS_REGISTER
, DrvImpCtrlStart
, DrvImpCtrlEnd
);
397 if (bitTest(tempW1
, 1))
398 tempW
= bitTestSet(tempW
, 5);
399 if (bitTest(tempW1
, 0))
400 tempW
= bitTestSet(tempW
, 1);
402 tempW
= swapAddrBits_wl(pDCTData
, tempW
);
404 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
405 DRAM_INIT
, MrsAddressStart
, MrsAddressEnd
, tempW
);
406 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
407 * the specified DIMM.
409 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
410 DRAM_INIT
, SendMrsCmd
, SendMrsCmd
, 1);
411 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
412 while ((get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
413 FUN_DCT
, DRAM_INIT
, SendMrsCmd
, SendMrsCmd
)) == 0x1)
416 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
417 * register that defines the required DDR3-defined function for Rtt_WR.
419 MrsBank
= swapBankBits(pDCTData
,2);
420 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
421 DRAM_INIT
, MrsBankStart
, MrsBankEnd
, MrsBank
);
422 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
423 * for Rtt_WR (DRAMTermDyn).
425 tempW
= 0;/* PASR = 0,*/
426 /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
427 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
428 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
429 FUN_DCT
, DRAM_MRS_REGISTER
, PCI_MIN_LOW
, PCI_MAX_HIGH
);
430 if (bitTest(tempW1
,19))
431 {tempW
= bitTestSet(tempW
, 7);}
432 if (bitTest(tempW1
,18))
433 {tempW
= bitTestSet(tempW
, 6);}
434 /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
435 tempW
=tempW
|((tempW1
&0x00700000)>>17);
436 /* workaround for DR-B0 */
437 if ((pDCTData
->LogicalCPUID
& AMD_DR_Bx
) && (pDCTData
->Status
[DCT_STATUS_REGISTERED
]))
439 /* determine Rtt_WR for WL & Normal mode */
440 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
])
441 tempW1
= RttWrRegDimm(pMCTData
, pDCTData
, dimm
, wl
, MemClkFreq
, rank
);
443 tempW1
= unbuffered_dimm_dynamic_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[dimm
], rank
);
445 tempW
= swapAddrBits_wl(pDCTData
,tempW
);
446 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
447 DRAM_INIT
, MrsAddressStart
, MrsAddressEnd
, tempW
);
448 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
449 the specified DIMM.*/
450 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
451 DRAM_INIT
, SendMrsCmd
, SendMrsCmd
, 1);
452 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
453 while ((get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
454 FUN_DCT
, DRAM_INIT
, SendMrsCmd
, SendMrsCmd
)) == 0x1)
461 /* Configure the non-target DIMM normally. */
463 while (currDimm
< MAX_LDIMMS
)
465 if (pDCTData
->DimmPresent
[currDimm
])
467 if (currDimm
!= dimm
)
470 while ((rank
< pDCTData
->DimmRanks
[currDimm
]) && (rank
< 2))
473 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
476 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
477 FUN_DCT
, DRAM_INIT
, MrsChipSelStart
, MrsChipSelEnd
, currDimm
*2+rank
);
478 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal
479 * DRAM register that defines the required DDR3-defined function
480 * for write levelization.
482 MrsBank
= swapBankBits(pDCTData
,1);
483 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
484 FUN_DCT
, DRAM_INIT
, MrsBankStart
, MrsBankEnd
, MrsBank
);
485 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required
486 * DDR3-defined function for write levelization.
488 tempW
= 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */
490 /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
491 tempW2
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
492 FUN_DCT
, DRAM_CONFIG_HIGH
, RDqsEn
, RDqsEn
);
495 if (pDCTData
->DimmX8Present
[currDimm
])
499 /* determine Rtt_Nom for WL & Normal mode */
500 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
])
501 tempW1
= RttNomNonTargetRegDimm(pMCTData
, pDCTData
, currDimm
, wl
, MemClkFreq
, rank
);
503 tempW1
= unbuffered_dimm_nominal_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[currDimm
], rank
);
505 /* program MrsAddress[5,1]=output driver impedance control (DIC):
506 * based on F2x[1,0]84[DrvImpCtrl] */
507 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
508 FUN_DCT
, DRAM_MRS_REGISTER
, DrvImpCtrlStart
, DrvImpCtrlEnd
);
509 if (bitTest(tempW1
,1))
510 {tempW
= bitTestSet(tempW
, 5);}
511 if (bitTest(tempW1
,0))
512 {tempW
= bitTestSet(tempW
, 1);}
513 tempW
= swapAddrBits_wl(pDCTData
,tempW
);
514 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
515 FUN_DCT
, DRAM_INIT
, MrsAddressStart
, MrsAddressEnd
, tempW
);
516 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command
517 * to the specified DIMM.
519 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
520 FUN_DCT
, DRAM_INIT
, SendMrsCmd
, SendMrsCmd
, 1);
521 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
522 while ((get_Bits(pDCTData
, pDCTData
->CurrDct
,
523 pDCTData
->NodeId
, FUN_DCT
, DRAM_INIT
,
524 SendMrsCmd
, SendMrsCmd
)) == 1);
525 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
526 * register that defines the required DDR3-defined function for Rtt_WR.
528 MrsBank
= swapBankBits(pDCTData
,2);
529 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
530 DRAM_INIT
, MrsBankStart
, MrsBankEnd
, MrsBank
);
531 /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
532 * for Rtt_WR (DRAMTermDyn).
534 tempW
= 0;/* PASR = 0,*/
535 /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
536 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
537 tempW1
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
538 FUN_DCT
, DRAM_MRS_REGISTER
, PCI_MIN_LOW
, PCI_MAX_HIGH
);
539 if (bitTest(tempW1
,19))
540 {tempW
= bitTestSet(tempW
, 7);}
541 if (bitTest(tempW1
,18))
542 {tempW
= bitTestSet(tempW
, 6);}
543 /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
544 tempW
=tempW
|((tempW1
&0x00700000)>>17);
545 /* workaround for DR-B0 */
546 if ((pDCTData
->LogicalCPUID
& AMD_DR_Bx
) && (pDCTData
->Status
[DCT_STATUS_REGISTERED
]))
548 /* determine Rtt_WR for WL & Normal mode */
549 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
])
550 tempW1
= RttWrRegDimm(pMCTData
, pDCTData
, currDimm
, wl
, MemClkFreq
, rank
);
552 tempW1
= unbuffered_dimm_dynamic_termination_emrs(pDCTData
->MaxDimmsInstalled
, MemClkFreq
, pDCTData
->DimmRanks
[currDimm
], rank
);
554 tempW
= swapAddrBits_wl(pDCTData
,tempW
);
555 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
556 DRAM_INIT
, MrsAddressStart
, MrsAddressEnd
, tempW
);
557 /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
558 the specified DIMM.*/
559 set_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
, FUN_DCT
,
560 DRAM_INIT
, SendMrsCmd
, SendMrsCmd
, 1);
561 /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
562 while ((get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
563 FUN_DCT
, DRAM_INIT
, SendMrsCmd
, SendMrsCmd
)) == 0x1)
574 /*-----------------------------------------------------------------------------
575 * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm)
578 * This function programs the ODT values for the NB
581 * IN OUT *DCTData - Pointer to buffer with information about each DCT
584 * ----------------------------------------------------------------------------
586 void programODT(sMCTStruct
*pMCTData
, sDCTStruct
*pDCTData
, u8 dimm
)
590 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
] == 0) {
591 if ((pDCTData
->DctCSPresent
& 0x05) == 0x05) {
593 } else if (bitTest((u32
)pDCTData
->DctCSPresent
,(u8
)(dimm
*2+1))) {
594 WrLvOdt1
= (u8
)bitTestSet(WrLvOdt1
, dimm
+2);
596 WrLvOdt1
= (u8
)bitTestSet(WrLvOdt1
, dimm
);
599 WrLvOdt1
= WrLvOdtRegDimm(pMCTData
, pDCTData
, dimm
);
602 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
603 DRAM_ADD_DCT_PHY_CONTROL_REG
, 8, 11, (u32
)WrLvOdt1
);
607 /*-----------------------------------------------------------------------------
608 * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
611 * This function programs the ODT values for the NB
614 * IN OUT *DCTData - Pointer to buffer with information about each DCT
615 * *MCTData - Pointer to buffer with runtime parameters,
616 * IN Dimm - Logical DIMM
617 * Pass - First of Second Pass
619 * ----------------------------------------------------------------------------
621 void procConifg(sMCTStruct
*pMCTData
,sDCTStruct
*pDCTData
, u8 dimm
, u8 pass
)
623 u8 ByteLane
, Seed_Gross
, Seed_Fine
, MemClkFreq
;
625 u16 Addl_Data_Offset
, Addl_Data_Port
;
626 u16 freq_tab
[] = {400, 533, 667, 800};
628 /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
629 MemClkFreq
= get_Bits(pDCTData
, pDCTData
->CurrDct
, pDCTData
->NodeId
,
630 FUN_DCT
, DRAM_CONFIG_HIGH
, 0, 2);
632 /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
633 * current memory subsystem configuration.
635 programODT(pMCTData
, pDCTData
, dimm
);
637 /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
638 if (pDCTData
->LogicalCPUID
& (AMD_DR_Cx
| AMD_DR_Dx
)) {
639 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
640 DRAM_ADD_DCT_PHY_CONTROL_REG
, WrLvOdtEn
, WrLvOdtEn
, (u32
)1);
644 /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */
645 if (pDCTData
->DctTrain
)
647 Addl_Data_Offset
=0x198;
648 Addl_Data_Port
=0x19C;
652 Addl_Data_Offset
=0x98;
656 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Offset
), 31, 0, &Addr
);
657 while ((get_Bits(pDCTData
,FUN_DCT
,pDCTData
->NodeId
, FUN_DCT
, Addl_Data_Offset
,
658 DctAccessDone
, DctAccessDone
)) == 0);
659 AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Port
), 31, 0, &Value
);
660 Value
= bitTestSet(Value
, 12);
661 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Port
), 31, 0, &Value
);
663 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData
->NodeId
),FUN_DCT
,Addl_Data_Offset
), 31, 0, &Addr
);
664 while ((get_Bits(pDCTData
,FUN_DCT
,pDCTData
->NodeId
, FUN_DCT
, Addl_Data_Offset
,
665 DctAccessDone
, DctAccessDone
)) == 0);
668 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
669 pMCTData
->AgesaDelay(10);
672 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
])
674 if(pDCTData
->RegMan1Present
& ((1<<(dimm
*2+pDCTData
->DctTrain
))))
687 if (MemClkFreq
== 6) {
692 /* Use settings for DDR-400 (interpolated from BKDG) */
697 for (ByteLane
= 0; ByteLane
< MAX_BYTE_LANES
; ByteLane
++)
699 /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
700 * F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields
701 * If the target frequency is different than 400MHz, BIOS must
702 * execute two training passes for each DIMM.
703 * For pass 1 at a 400MHz MEMCLK frequency, use an initial total delay value
704 * of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
707 pDCTData
->WLGrossDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] = Seed_Gross
;
708 pDCTData
->WLFineDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] = Seed_Fine
;
710 } else { /* Pass 2 */
711 /* From BKDG, Write Leveling Seed Value. */
712 u32 RegisterDelay
, SeedTotal
;
713 for (ByteLane
= 0; ByteLane
< MAX_BYTE_LANES
; ByteLane
++)
715 if (pDCTData
->Status
[DCT_STATUS_REGISTERED
])
716 RegisterDelay
= 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
719 SeedTotal
= (pDCTData
->WLFineDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] & 0x1f) |
720 (pDCTData
->WLGrossDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] << 5);
721 /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
722 training) - RegisterDelay. */
723 SeedTotal
= (uint16_t) (RegisterDelay
+ ((((uint64_t) SeedTotal
- RegisterDelay
) *
724 freq_tab
[MemClkFreq
-3] * 100) / (freq_tab
[0] * 100)));
725 Seed_Gross
= SeedTotal
/ 32;
726 Seed_Fine
= SeedTotal
& 0x1f;
729 else if (Seed_Gross
& 0x1)
733 pDCTData
->WLGrossDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] = Seed_Gross
;
734 pDCTData
->WLFineDelay
[MAX_BYTE_LANES
*dimm
+ByteLane
] = Seed_Fine
;
738 setWLByteDelay(pDCTData
, ByteLane
, dimm
, 0);
741 /*-----------------------------------------------------------------------------
742 * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
745 * This function writes the write levelization byte delay for the Phase
746 * Recovery control registers
749 * IN OUT *DCTData - Pointer to buffer with information about each DCT
750 * IN Dimm - Dimm Number
751 * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for each
753 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each
755 * ByteLane - target byte lane to write
756 * targetAddr - 0: write to DRAM phase recovery control register
757 * 1: write to DQS write register
760 *-----------------------------------------------------------------------------
762 void setWLByteDelay(sDCTStruct
*pDCTData
, u8 ByteLane
, u8 dimm
, u8 targetAddr
)
764 u8 fineStartLoc
, fineEndLoc
, grossStartLoc
, grossEndLoc
, tempB
, index
, offsetAddr
;
765 u32 addr
, fineDelayValue
, grossDelayValue
, ValueLow
, ValueHigh
, EccValue
, tempW
;
769 index
= (u8
)(MAX_BYTE_LANES
* dimm
);
774 while (ByteLane
< MAX_BYTE_LANES
)
776 /* This subtract 0xC workaround might be temporary. */
777 if ((pDCTData
->WLPass
==2) && (pDCTData
->RegMan1Present
& (1<<(dimm
*2+pDCTData
->DctTrain
))))
779 tempW
= (pDCTData
->WLGrossDelay
[index
+ByteLane
] << 5) | pDCTData
->WLFineDelay
[index
+ByteLane
];
781 pDCTData
->WLGrossDelay
[index
+ByteLane
] = (u8
)(tempW
>> 5);
782 pDCTData
->WLFineDelay
[index
+ByteLane
] = (u8
)(tempW
& 0x1F);
784 grossDelayValue
= pDCTData
->WLGrossDelay
[index
+ByteLane
];
785 /* Adjust seed gross delay overflow (greater than 3):
786 * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
787 * - Keep original seed gross delay for later reference.
789 if(grossDelayValue
>= 3)
791 grossDelayValue
= (grossDelayValue
&1)? 1 : 2;
793 fineDelayValue
= pDCTData
->WLFineDelay
[index
+ByteLane
];
795 ValueLow
|= ((grossDelayValue
<< 5) | fineDelayValue
) << 8*ByteLane
;
796 else if(ByteLane
< 8)
797 ValueHigh
|= ((grossDelayValue
<< 5) | fineDelayValue
) << 8*(ByteLane
-4);
799 EccValue
= ((grossDelayValue
<< 5) | fineDelayValue
);
802 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
803 DRAM_CONT_ADD_PHASE_REC_CTRL_LOW
, 0, 31, (u32
)ValueLow
);
804 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
805 DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH
, 0, 31, (u32
)ValueHigh
);
806 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
807 DRAM_CONT_ADD_ECC_PHASE_REC_CTRL
, 0, 31, (u32
)EccValue
);
811 index
= (u8
)(MAX_BYTE_LANES
* dimm
);
812 grossDelayValue
= pDCTData
->WLGrossDelay
[index
+ByteLane
];
813 fineDelayValue
= pDCTData
->WLFineDelay
[index
+ByteLane
];
816 offsetAddr
= (u8
)(3 * dimm
);
818 tempB
= (u8
)(16 * ByteLane
);
819 addr
= DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01
;
820 } else if (ByteLane
<4) {
821 tempB
= (u8
)(16 * ByteLane
);
822 addr
= DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01
+ 1;
823 } else if (ByteLane
<6) {
824 tempB
= (u8
)(16 * ByteLane
);
825 addr
= DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45
;
826 } else if (ByteLane
<8) {
827 tempB
= (u8
)(16 * ByteLane
);
828 addr
= DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45
+ 1;
831 addr
= DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01
+ 2;
835 fineStartLoc
= (u8
)(tempB
% 32);
836 fineEndLoc
= (u8
)(fineStartLoc
+ 4);
837 grossStartLoc
= (u8
)(fineEndLoc
+ 1);
838 grossEndLoc
= (u8
)(grossStartLoc
+ 1);
840 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
841 (u16
)addr
, fineStartLoc
, fineEndLoc
,(u32
)fineDelayValue
);
842 set_DCT_ADDR_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
, FUN_DCT
,
843 (u16
)addr
, grossStartLoc
, grossEndLoc
, (u32
)grossDelayValue
);
848 /*-----------------------------------------------------------------------------
849 * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
852 * This function reads the write levelization byte delay from the Phase
853 * Recovery control registers
856 * IN OUT *DCTData - Pointer to buffer with information about each DCT
857 * IN Dimm - Dimm Number
858 * ByteLane - target byte lane to read
860 * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for current
861 * byte for logical DIMM
862 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for current
863 * byte for logical DIMM
865 *-----------------------------------------------------------------------------
867 void getWLByteDelay(sDCTStruct
*pDCTData
, u8 ByteLane
, u8 dimm
)
869 u8 fineStartLoc
, fineEndLoc
, grossStartLoc
, grossEndLoc
, tempB
, tempB1
, index
;
870 u32 addr
, fine
, gross
;
872 index
= (u8
)(MAX_BYTE_LANES
*dimm
);
874 tempB
= (u8
)(8 * ByteLane
);
875 addr
= DRAM_CONT_ADD_PHASE_REC_CTRL_LOW
;
876 } else if (ByteLane
< 8) {
877 tempB1
= (u8
)(ByteLane
- 4);
878 tempB
= (u8
)(8 * tempB1
);
879 addr
= DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH
;
882 addr
= DRAM_CONT_ADD_ECC_PHASE_REC_CTRL
;
884 fineStartLoc
= tempB
;
885 fineEndLoc
= (u8
)(fineStartLoc
+ 4);
886 grossStartLoc
= (u8
)(fineEndLoc
+ 1);
887 grossEndLoc
= (u8
)(grossStartLoc
+ 1);
889 fine
= get_ADD_DCT_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
,
890 FUN_DCT
, (u16
)addr
, fineStartLoc
, fineEndLoc
);
891 gross
= get_ADD_DCT_Bits(pDCTData
, pDCTData
->DctTrain
, pDCTData
->NodeId
,
892 FUN_DCT
, (u16
)addr
, grossStartLoc
, grossEndLoc
);
893 /* Adjust seed gross delay overflow (greater than 3):
894 * - Adjust the trained gross delay to the original seed gross delay.
896 if (pDCTData
->WLGrossDelay
[index
+ByteLane
] >= 3) {
897 gross
+= pDCTData
->WLGrossDelay
[index
+ByteLane
];
898 if(pDCTData
->WLGrossDelay
[index
+ByteLane
] & 1)
902 } else if ((pDCTData
->WLGrossDelay
[index
+ByteLane
] == 0) && (gross
== 3)) {
903 /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
904 * We will then round the negative number to 0.
909 pDCTData
->WLFineDelay
[index
+ByteLane
] = (u8
)fine
;
910 pDCTData
->WLGrossDelay
[index
+ByteLane
] = (u8
)gross
;