tree: drop last paragraph of GPL copyright header
[coreboot.git] / src / northbridge / amd / amdmct / mct_ddr3 / mhwlc_d.c
blob9ea854f186e331f9723d2b6818f1b8fb68394dd1
1 /*
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 *-----------------------------------------------------------------------------
19 * MODULES USED
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 *-----------------------------------------------------------------------------
38 * EXPORTED FUNCTIONS
40 *-----------------------------------------------------------------------------
43 /*-----------------------------------------------------------------------------
44 * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
45 * u8 Dimm, u8 Pass)
47 * Description:
48 * This function initialized Hardware based write levelization phase 1
50 * Parameters:
51 * IN OUT *SPDData - Pointer to buffer with information about each DIMMs
52 * SPD information
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
58 * OUT
59 *-----------------------------------------------------------------------------
61 void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
62 u8 dimm, u8 pass)
64 u8 ByteLane;
65 u32 Value, Addr;
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
76 * MR commands. */
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);
89 else
91 /* Broadcast write to all D3Dbyte chipset register offset 0xc
92 * Set bit 0 (wrTrain)
93 * Program bit 4 to nibble being trained (only matters for x4dimms)
94 * retain value of 3:2 (Trdimmsel)
95 * reset bit 5 (FrzPR)
97 if (pDCTData->DctTrain)
99 Addl_Data_Offset=0x198;
100 Addl_Data_Port=0x19C;
102 else
104 Addl_Data_Offset=0x98;
105 Addl_Data_Port=0x9C;
107 Addr=0x0D00000C;
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);
116 Addr=0x4D030F0C;
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. */
130 ByteLane = 0;
131 while (ByteLane < MAX_BYTE_LANES)
133 getWLByteDelay(pDCTData,ByteLane, dimm);
134 setWLByteDelay(pDCTData,ByteLane, dimm, 1);
135 ByteLane++;
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 /*----------------------------------------------------------------------------
158 * LOCAL FUNCTIONS
160 *----------------------------------------------------------------------------
163 /*-----------------------------------------------------------------------------
164 * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
166 * Description:
167 * This function swaps the bits in MSR register value
169 * Parameters:
170 * IN OUT *DCTData - Pointer to buffer with information about each DCT
171 * IN u32: MRS value
172 * OUT u32: Swapped BANK BITS
174 * ----------------------------------------------------------------------------
176 u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
178 u32 tempW, tempW1;
180 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
181 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
182 if (tempW1 & 1)
184 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
186 /* swap A3/A4,A5/A6,A7/A8 */
187 tempW = MRSValue;
188 tempW1 = MRSValue;
189 tempW &= 0x0A8;
190 tempW1 &= 0x0150;
191 MRSValue &= 0xFE07;
192 MRSValue |= (tempW<<1);
193 MRSValue |= (tempW1>>1);
196 return MRSValue;
199 /*-----------------------------------------------------------------------------
200 * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
202 * Description:
203 * This function swaps the bits in MSR register value
205 * Parameters:
206 * IN OUT *DCTData - Pointer to buffer with information about each DCT
207 * IN u32: MRS value
208 * OUT u32: Swapped BANK BITS
210 * ----------------------------------------------------------------------------
212 u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
214 u32 tempW, tempW1;
216 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
217 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
218 if (tempW1 & 1)
220 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
222 /* swap BA0/BA1 */
223 tempW = MRSValue;
224 tempW1 = MRSValue;
225 tempW &= 0x01;
226 tempW1 &= 0x02;
227 MRSValue = 0;
228 MRSValue |= (tempW<<1);
229 MRSValue |= (tempW1>>1);
232 return MRSValue;
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)
237 uint16_t term;
239 /* FIXME
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 */
248 } else {
249 if (rank_count == 1) {
250 term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
251 } else {
252 if (rank == 0)
253 term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
254 else
255 term = 0x00; /* Rtt_Nom=OFF */
258 } else {
259 if (frequency_index < 5)
260 term = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
261 else
262 term = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
265 return term;
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)
270 uint16_t term;
272 /* FIXME
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 */
281 } else {
282 if (rank_count == 1)
283 term = 0x00; /* Rtt_WR=off */
284 else
285 term = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
287 } else {
288 term = 0x400; /* Rtt_WR=RZQ/2=120 Ohm */
291 return term;
294 /*-----------------------------------------------------------------------------
295 * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
297 * Description:
298 * This function prepares DIMMS for training
300 * Parameters:
301 * IN OUT *DCTData - Pointer to buffer with information about each DCT
302 * *SPDData - Pointer to buffer with information about each DIMMs
303 * SPD information
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
307 * training
309 * OUT
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.
323 rank = 0;
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
331 * levelization.
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);
344 if (tempW2)
346 if (pDCTData->DimmX8Present[dimm])
347 tempW |= 0x800;
350 /* determine Rtt_Nom for WL & Normal mode */
351 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
352 tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
353 } else {
354 if (wl) {
355 if (rank == 0) {
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)
361 tempW1 = 0x04;
362 else if (dynamic_term == 0x400)
363 tempW1 = 0x40;
364 else
365 tempW1 = 0x0;
366 } else {
367 tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
369 } else {
370 tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
373 tempW=tempW|tempW1;
375 /* All ranks of the target DIMM are set to write levelization mode. */
376 if (wl)
378 tempW1 = bitTestSet(tempW, MRS_Level);
379 if (rank == 0)
381 /* Enable the output driver of the first rank of the target DIMM. */
382 tempW = tempW1;
384 else
386 /* Disable the output drivers of all other ranks for
387 * the target DIMM.
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]))
438 tempW+=0x8;
439 /* determine Rtt_WR for WL & Normal mode */
440 if (pDCTData->Status[DCT_STATUS_REGISTERED])
441 tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
442 else
443 tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
444 tempW=tempW|tempW1;
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)
458 rank++;
461 /* Configure the non-target DIMM normally. */
462 currDimm = 0;
463 while (currDimm < MAX_LDIMMS)
465 if (pDCTData->DimmPresent[currDimm])
467 if (currDimm != dimm)
469 rank = 0;
470 while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2))
473 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
474 * to be trained.
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);
493 if (tempW2)
495 if (pDCTData->DimmX8Present[currDimm])
496 tempW |= 0x800;
499 /* determine Rtt_Nom for WL & Normal mode */
500 if (pDCTData->Status[DCT_STATUS_REGISTERED])
501 tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
502 else
503 tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
504 tempW=tempW|tempW1;
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]))
547 tempW+=0x8;
548 /* determine Rtt_WR for WL & Normal mode */
549 if (pDCTData->Status[DCT_STATUS_REGISTERED])
550 tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
551 else
552 tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
553 tempW=tempW|tempW1;
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)
566 rank++;
570 currDimm++;
574 /*-----------------------------------------------------------------------------
575 * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm)
577 * Description:
578 * This function programs the ODT values for the NB
580 * Parameters:
581 * IN OUT *DCTData - Pointer to buffer with information about each DCT
582 * IN
583 * OUT
584 * ----------------------------------------------------------------------------
586 void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
588 u8 WrLvOdt1=0;
590 if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
591 if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
592 WrLvOdt1 = 0x03;
593 } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
594 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
595 } else {
596 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
598 } else {
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)
610 * Description:
611 * This function programs the ODT values for the NB
613 * Parameters:
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
618 * OUT
619 * ----------------------------------------------------------------------------
621 void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
623 u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq;
624 u32 Value, Addr;
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);
642 else
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;
650 else
652 Addl_Data_Offset=0x98;
653 Addl_Data_Port=0x9C;
655 Addr=0x0D008000;
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);
662 Addr=0x4D088F00;
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);
670 if (pass == 1)
672 if (pDCTData->Status[DCT_STATUS_REGISTERED])
674 if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
676 Seed_Gross = 0x02;
677 Seed_Fine = 0x16;
679 else
681 Seed_Gross = 0x02;
682 Seed_Fine = 0x00;
685 else
687 if (MemClkFreq == 6) {
688 /* DDR-800 */
689 Seed_Gross = 0x00;
690 Seed_Fine = 0x1a;
691 } else {
692 /* Use settings for DDR-400 (interpolated from BKDG) */
693 Seed_Gross = 0x00;
694 Seed_Fine = 0x0d;
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
705 * by design.
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; */
717 else
718 RegisterDelay = 0;
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;
727 if (Seed_Gross == 0)
728 Seed_Gross = 0;
729 else if (Seed_Gross & 0x1)
730 Seed_Gross = 1;
731 else
732 Seed_Gross = 2;
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){
744 * Description:
745 * This function writes the write levelization byte delay for the Phase
746 * Recovery control registers
748 * Parameters:
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
752 * logical DIMM
753 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each
754 * logical DIMM
755 * ByteLane - target byte lane to write
756 * targetAddr - 0: write to DRAM phase recovery control register
757 * 1: write to DQS write register
758 * OUT
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;
767 if (targetAddr == 0)
769 index = (u8)(MAX_BYTE_LANES * dimm);
770 ValueLow = 0;
771 ValueHigh = 0;
772 ByteLane = 0;
773 EccValue = 0;
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];
780 tempW -= 0xC;
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];
794 if (ByteLane < 4)
795 ValueLow |= ((grossDelayValue << 5) | fineDelayValue) << 8*ByteLane;
796 else if(ByteLane < 8)
797 ValueHigh |= ((grossDelayValue << 5) | fineDelayValue) << 8*(ByteLane-4);
798 else
799 EccValue = ((grossDelayValue << 5) | fineDelayValue);
800 ByteLane++;
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);
809 else
811 index = (u8)(MAX_BYTE_LANES * dimm);
812 grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
813 fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
815 tempB = 0;
816 offsetAddr = (u8)(3 * dimm);
817 if (ByteLane < 2) {
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;
829 } else {
830 tempB = 0;
831 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2;
833 addr += offsetAddr;
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)
851 * Description:
852 * This function reads the write levelization byte delay from the Phase
853 * Recovery control registers
855 * Parameters:
856 * IN OUT *DCTData - Pointer to buffer with information about each DCT
857 * IN Dimm - Dimm Number
858 * ByteLane - target byte lane to read
859 * OUT
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;
871 tempB = 0;
872 index = (u8)(MAX_BYTE_LANES*dimm);
873 if (ByteLane < 4) {
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;
880 } else {
881 tempB = 0;
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)
899 gross -= 1;
900 else
901 gross -= 2;
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.
906 gross = 0;
907 fine = 0;
909 pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
910 pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;