blob: d46a4c619982c2ff87959b219cd203948c79c7f3 [file] [log] [blame]
Zheng Baoeb75f652010-04-23 17:32:48 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2010 Advanced Micro Devices, Inc.
5 *
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.
9 *
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.
14 *
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
18 */
19
20/*
21 *-----------------------------------------------------------------------------
22 * MODULES USED
23 *
24 *-----------------------------------------------------------------------------
25 */
26
27/*----------------------------------------------------------------------------
28 * PROTOTYPES OF LOCAL FUNCTIONS
29 *
30 *----------------------------------------------------------------------------
31 */
32u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue);
33u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue);
34void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl);
35void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm);
36void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass);
37void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr);
38void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
39/*
40 *-----------------------------------------------------------------------------
41 * EXPORTED FUNCTIONS
42 *
43 *-----------------------------------------------------------------------------
44 */
45
46/*-----------------------------------------------------------------------------
47 * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
48 * u8 Dimm, u8 Pass)
49 *
50 * Description:
51 * This function initialized Hardware based write levelization phase 1
52 *
53 * Parameters:
54 * IN OUT *SPDData - Pointer to buffer with information about each DIMMs
55 * SPD information
56 * *MCTData - Pointer to buffer with runtime parameters,
57 * *DCTData - Pointer to buffer with information about each DCT
58 *
59 * IN DIMM - Logical DIMM number
60 * Pass - First or Second Pass
61 * OUT
62 *-----------------------------------------------------------------------------
63 */
64void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
65 u8 dimm, u8 pass)
66{
67 u8 ByteLane;
68 u32 Value, Addr;
69 u16 Addl_Data_Offset, Addl_Data_Port;
70
71 pDCTData->WLPass = pass;
72 /* 1. Specify the target DIMM that is to be trained by programming
73 * F2x[1, 0]9C_x08[TrDimmSel].
74 */
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
79 * MR commands. */
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.
83 */
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);
92 else
93 {
Zheng Bao53b52f32010-10-09 07:18:50 +000094 /* Broadcast write to all D3Dbyte chipset register offset 0xc
Zheng Baoeb75f652010-04-23 17:32:48 +000095 * Set bit 0 (wrTrain)
96 * Program bit 4 to nibble being trained (only matters for x4dimms)
97 * retain value of 3:2 (Trdimmsel)
98 * reset bit 5 (FrzPR)
99 */
100 if (pDCTData->DctTrain)
101 {
102 Addl_Data_Offset=0x198;
103 Addl_Data_Port=0x19C;
104 }
105 else
106 {
107 Addl_Data_Offset=0x98;
108 Addl_Data_Port=0x9C;
109 }
110 Addr=0x0D00000C;
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 */
Zheng Bao53b52f32010-10-09 07:18:50 +0000117 Value = bitTestReset(Value, 5); /* for hardware WL training */
Zheng Baoeb75f652010-04-23 17:32:48 +0000118 AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
119 Addr=0x4D030F0C;
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);
123 }
124
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. */
133 ByteLane = 0;
134 while (ByteLane < MAX_BYTE_LANES)
135 {
136 getWLByteDelay(pDCTData,ByteLane, dimm);
137 setWLByteDelay(pDCTData,ByteLane, dimm, 1);
138 ByteLane++;
139 }
140
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);
145
146 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
147 pMCTData->AgesaDelay(10);
148
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:
156 */
157 prepareDimms(pMCTData, pDCTData,dimm,FALSE);
158}
159
160/*----------------------------------------------------------------------------
161 * LOCAL FUNCTIONS
162 *
163 *----------------------------------------------------------------------------
164 */
165
166/*-----------------------------------------------------------------------------
167 * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
168 *
169 * Description:
170 * This function swaps the bits in MSR register value
171 *
172 * Parameters:
173 * IN OUT *DCTData - Pointer to buffer with information about each DCT
174 * IN u32: MRS value
Zheng Bao53b52f32010-10-09 07:18:50 +0000175 * OUT u32: Swapped BANK BITS
Zheng Baoeb75f652010-04-23 17:32:48 +0000176 *
177 * ----------------------------------------------------------------------------
178 */
179u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
180{
181 u32 tempW, tempW1;
182
183 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
184 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
185 if (tempW1 & 1)
186 {
187 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
188 {
189 /* swap A3/A4,A5/A6,A7/A8 */
190 tempW = MRSValue;
191 tempW1 = MRSValue;
192 tempW &= 0x0A8;
193 tempW1 &= 0x0150;
194 MRSValue &= 0xFE07;
195 MRSValue |= (tempW<<1);
196 MRSValue |= (tempW1>>1);
197 }
198 }
199 return MRSValue;
200}
201
202/*-----------------------------------------------------------------------------
203 * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
204 *
205 * Description:
206 * This function swaps the bits in MSR register value
207 *
208 * Parameters:
209 * IN OUT *DCTData - Pointer to buffer with information about each DCT
210 * IN u32: MRS value
Zheng Bao53b52f32010-10-09 07:18:50 +0000211 * OUT u32: Swapped BANK BITS
Zheng Baoeb75f652010-04-23 17:32:48 +0000212 *
213 * ----------------------------------------------------------------------------
214 */
215u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
216{
217 u32 tempW, tempW1;
218
219 tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
220 FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
221 if (tempW1 & 1)
222 {
223 if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
224 {
225 /* swap BA0/BA1 */
226 tempW = MRSValue;
227 tempW1 = MRSValue;
228 tempW &= 0x01;
229 tempW1 &= 0x02;
230 MRSValue = 0;
231 MRSValue |= (tempW<<1);
232 MRSValue |= (tempW1>>1);
233 }
234 }
235 return MRSValue;
236}
237
238/*-----------------------------------------------------------------------------
239 * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
240 *
241 * Description:
242 * This function prepares DIMMS for training
243 *
244 * Parameters:
245 * IN OUT *DCTData - Pointer to buffer with information about each DCT
246 * *SPDData - Pointer to buffer with information about each DIMMs
247 * SPD information
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
251 * training
252 *
253 * OUT
254 *
255 * ----------------------------------------------------------------------------
256 */
257void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
258{
259 u32 tempW, tempW1, tempW2, MrsBank;
260 u8 rank, currDimm, MemClkFreq;
261
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000265 * by programming the F2x[1,0]7C register using the following steps.
Zheng Baoeb75f652010-04-23 17:32:48 +0000266 */
267 rank = 0;
268 while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2))
269 {
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000274 * register that defines the required DDR3-defined function for write
275 * levelization.
Zheng Baoeb75f652010-04-23 17:32:48 +0000276 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000281 * for write levelization.
Zheng Baoeb75f652010-04-23 17:32:48 +0000282 */
283 tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */
284
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);
288 if (tempW2)
289 {
290 if (pDCTData->DimmX8Present[dimm])
291 tempW |= 0x800;
292 }
293
294 /* determine Rtt_Nom for WL & Normal mode */
295 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
296 tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
297 } else {
298 if (wl)
299 {
300 if (pDCTData->MaxDimmsInstalled == 1)
301 {
302 if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0))
303 {
304 tempW1 = 0x00; /* Rtt_Nom=OFF */
305 }
306 else
307 {
308 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
309 }
310 }
311 else /* 2 Dimms or more per channel */
312 {
313 if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1))
314 {
315 tempW1 = 0x00; /* Rtt_Nom=OFF */
316 }
317 else
318 {
319 if (MemClkFreq == 6) {
320 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
321 } else {
322 tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */
323 }
324 }
325 }
326 }
327 else { /* 1 or 4 Dimms per channel */
328 if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4))
329 {
330 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
331 }
332 else /* 2 or 3 Dimms per channel */
333 {
334 if (MemClkFreq < 5) {
335 tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
336 } else {
337 tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
338 }
339 }
340 }
341 }
342 tempW=tempW|tempW1;
343
344 /* All ranks of the target DIMM are set to write levelization mode. */
345 if (wl)
346 {
347 tempW1 = bitTestSet(tempW, MRS_Level);
348 if (rank == 0)
349 {
Zheng Bao53b52f32010-10-09 07:18:50 +0000350 /* Enable the output driver of the first rank of the target DIMM. */
Zheng Baoeb75f652010-04-23 17:32:48 +0000351 tempW = tempW1;
352 }
353 else
354 {
355 /* Disable the output drivers of all other ranks for
356 * the target DIMM. */
357 tempW = bitTestSet(tempW1, Qoff);
358 }
359 }
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);}
368
369 tempW = swapAddrBits_wl(pDCTData,tempW);
370
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000374 * the specified DIMM.
Zheng Baoeb75f652010-04-23 17:32:48 +0000375 */
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)
381 {
382 }
383 /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
Zheng Bao53b52f32010-10-09 07:18:50 +0000384 * register that defines the required DDR3-defined function for Rtt_WR.
Zheng Baoeb75f652010-04-23 17:32:48 +0000385 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000390 * for Rtt_WR (DRAMTermDyn).
Zheng Baoeb75f652010-04-23 17:32:48 +0000391 */
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]))
405 tempW+=0x8;
406 /* determine Rtt_WR for WL & Normal mode */
407 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
408 tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
409 } else {
410 if (wl)
411 {
412 tempW1 = 0x00; /* Rtt_WR=off */
413 }
414 else
415 {
416 if (pDCTData->MaxDimmsInstalled == 1)
417 {
418 tempW1 = 0x00; /* Rtt_WR=off */
419 }
420 else
421 {
422 if (MemClkFreq == 6) {
423 tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
424 } else {
425 tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
426 }
427 }
428 }
429 }
430 tempW=tempW|tempW1;
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)
441 {
442 }
443
444 rank++;
445 }
446
447 /* Configure the non-target DIMM normally. */
448 currDimm = 0;
449 while (currDimm < MAX_LDIMMS)
450 {
451 if (pDCTData->DimmPresent[currDimm])
452 {
453 if (currDimm != dimm)
454 {
455 rank = 0;
456 while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2))
457 {
458
459 /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
Zheng Bao53b52f32010-10-09 07:18:50 +0000460 * to be trained.
Zheng Baoeb75f652010-04-23 17:32:48 +0000461 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000465 * DRAM register that defines the required DDR3-defined function
466 * for write levelization.
Zheng Baoeb75f652010-04-23 17:32:48 +0000467 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000472 * DDR3-defined function for write levelization.
Zheng Baoeb75f652010-04-23 17:32:48 +0000473 */
474 tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */
475
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);
479 if (tempW2)
480 {
481 if (pDCTData->DimmX8Present[currDimm])
482 tempW |= 0x800;
483 }
484
485 /* determine Rtt_Nom for WL & Normal mode */
486 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
487 tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
488 } else {
489 if (wl)
490 {
491 if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1))
492 {
493 tempW1 = 0x00; /* Rtt_Nom=OFF */
494 }
495 else
496 {
497 if (MemClkFreq < 5) {
498 tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */
499 } else {
500 tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */
501 }
502 }
503 }
504 else { /* 1 or 4 Dimms per channel */
505 if ((pDCTData->MaxDimmsInstalled == 4))
506 {
507 tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
508 }
509 else { /* 2 or 3 Dimms per channel */
510 if (MemClkFreq < 5) {
511 tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
512 } else {
513 tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
514 }
515 }
516 }
517 }
518 tempW=tempW|tempW1;
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000531 * to the specified DIMM.
Zheng Baoeb75f652010-04-23 17:32:48 +0000532 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000540 * register that defines the required DDR3-defined function for Rtt_WR.
Zheng Baoeb75f652010-04-23 17:32:48 +0000541 */
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
Zheng Bao53b52f32010-10-09 07:18:50 +0000546 * for Rtt_WR (DRAMTermDyn).
Zheng Baoeb75f652010-04-23 17:32:48 +0000547 */
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]))
561 tempW+=0x8;
562 /* determine Rtt_WR for WL & Normal mode */
563 if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
564 tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
565 } else {
566 if (wl)
567 {
568 tempW1 = 0x00; /* Rtt_WR=off */
569 }
570 else
571 {
572 if (MemClkFreq == 6) {
573 tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
574 } else {
575 tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
576 }
577 }
578 }
579 tempW=tempW|tempW1;
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)
590 {
591 }
592 rank++;
593 }
594 }
595 }
596 currDimm++;
597 }
598}
599
600/*-----------------------------------------------------------------------------
601 * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm)
602 *
603 * Description:
604 * This function programs the ODT values for the NB
605 *
606 * Parameters:
607 * IN OUT *DCTData - Pointer to buffer with information about each DCT
608 * IN
609 * OUT
610 * ----------------------------------------------------------------------------
611 */
612void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
613{
614 u8 WrLvOdt1=0;
615
616 if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
617 if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
618 WrLvOdt1 = 0x03;
619 } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
620 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
621 } else {
622 WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
623 }
624 } else {
625 WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
626 }
627
628 set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
629 DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
630
631}
632
633/*-----------------------------------------------------------------------------
634 * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
635 *
636 * Description:
637 * This function programs the ODT values for the NB
638 *
639 * Parameters:
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
644 * OUT
645 * ----------------------------------------------------------------------------
646 */
647void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
648{
649 u8 ByteLane, Seed_Gross, Seed_Fine;
650 u32 Value, Addr;
651 u16 Addl_Data_Offset, Addl_Data_Port;
652
653 /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
Zheng Bao53b52f32010-10-09 07:18:50 +0000654 * current memory subsystem configuration.
Zheng Baoeb75f652010-04-23 17:32:48 +0000655 */
656 programODT(pMCTData, pDCTData, dimm);
657
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);
662 else
663 {
664 /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B*/
665 if (pDCTData->DctTrain)
666 {
667 Addl_Data_Offset=0x198;
668 Addl_Data_Port=0x19C;
669 }
670 else
671 {
672 Addl_Data_Offset=0x98;
673 Addl_Data_Port=0x9C;
674 }
675 Addr=0x0D008000;
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);
682 Addr=0x4D088F00;
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);
686 }
687
688 /* Wait 10 MEMCLKs to allow for ODT signal settling. */
689 pMCTData->AgesaDelay(10);
690 ByteLane = 0;
691 if (pass == 1)
692 {
693 if (pDCTData->Status[DCT_STATUS_REGISTERED])
694 {
695 if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
696 {
697 Seed_Gross = 0x02;
698 Seed_Fine = 0x16;
699 }
700 else
701 {
702 Seed_Gross = 0x02;
703 Seed_Fine = 0x00;
704 }
705 }
706 else
707 {
708 Seed_Gross = 0x00;
709 Seed_Fine = 0x1A;
710 }
711 while(ByteLane < MAX_BYTE_LANES)
712 {
713 /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
Zheng Bao53b52f32010-10-09 07:18:50 +0000714 * 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
Zheng Baoeb75f652010-04-23 17:32:48 +0000716 * execute two training passes for each DIMM.
717 * For pass 1 at a 400MHz MEMCLK frequency, use an initial total delay value
Zheng Bao53b52f32010-10-09 07:18:50 +0000718 * of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
719 * by design.
Zheng Baoeb75f652010-04-23 17:32:48 +0000720 */
721 pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
722 pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
723 ByteLane++;
724 }
725 }
726 setWLByteDelay(pDCTData, ByteLane, dimm, 0);
727}
728
729/*-----------------------------------------------------------------------------
730 * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
731 *
732 * Description:
733 * This function writes the write levelization byte delay for the Phase
734 * Recovery control registers
735 *
736 * Parameters:
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
740 * logical DIMM
741 * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each
742 * logical DIMM
743 * ByteLane - target byte lane to write
744 * targetAddr - 0: write to DRAM phase recovery control register
745 * 1: write to DQS write register
746 * OUT
747 *
748 *-----------------------------------------------------------------------------
749 */
750void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
751{
752 u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
753 u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW;
754
755 if (targetAddr == 0)
756 {
757 index = (u8)(MAX_BYTE_LANES * dimm);
758 ValueLow = 0;
759 ValueHigh = 0;
760 ByteLane = 0;
761 EccValue = 0;
762 while (ByteLane < MAX_BYTE_LANES)
763 {
764 /* This subtract 0xC workaround might be temporary. */
765 if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain))))
766 {
767 tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
768 tempW -= 0xC;
769 pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
770 pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
771 }
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.
776 */
777 if(grossDelayValue >= 3)
778 {
779 grossDelayValue = (grossDelayValue&1)? 1 : 2;
780 }
781 fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
782 if (ByteLane < 4)
783 ValueLow |= ((grossDelayValue << 5) | fineDelayValue) << 8*ByteLane;
784 else if(ByteLane < 8)
785 ValueHigh |= ((grossDelayValue << 5) | fineDelayValue) << 8*(ByteLane-4);
786 else
787 EccValue = ((grossDelayValue << 5) | fineDelayValue);
788 ByteLane++;
789 }
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);
796 }
797 else
798 {
799 index = (u8)(MAX_BYTE_LANES * dimm);
800 grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
801 fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
802
803 tempB = 0;
804 offsetAddr = (u8)(3 * dimm);
805 if (ByteLane < 2)
806 {
807 tempB = (u8)(16 * ByteLane);
808 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01;
809 }
810 else if (ByteLane <4)
811 {
812 tempB = (u8)(16 * ByteLane);
813 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 1;
814 }
815 else if (ByteLane <6)
816 {
817 tempB = (u8)(16 * ByteLane);
818 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45;
819 }
820 else if (ByteLane <8)
821 {
822 tempB = (u8)(16 * ByteLane);
823 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 + 1;
824 }
825 else
826 {
827 tempB = 0;
828 addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2;
829 }
830 addr += offsetAddr;
831
832 fineStartLoc = (u8)(tempB % 32);
833 fineEndLoc = (u8)(fineStartLoc + 4);
834 grossStartLoc = (u8)(fineEndLoc + 1);
835 grossEndLoc = (u8)(grossStartLoc + 1);
836
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);
841 }
842
843}
844
845/*-----------------------------------------------------------------------------
846 * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
847 *
848 * Description:
849 * This function reads the write levelization byte delay from the Phase
850 * Recovery control registers
851 *
852 * Parameters:
853 * IN OUT *DCTData - Pointer to buffer with information about each DCT
854 * IN Dimm - Dimm Number
855 * ByteLane - target byte lane to read
856 * OUT
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
861 *
862 *-----------------------------------------------------------------------------
863 */
864void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
865{
866 u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
867 u32 addr, fine, gross;
868 tempB = 0;
869 index = (u8)(MAX_BYTE_LANES*dimm);
870 if (ByteLane < 4)
871 {
872 tempB = (u8)(8 * ByteLane);
873 addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW;
874 }
875 else if (ByteLane < 8)
876 {
877 tempB1 = (u8)(ByteLane - 4);
878 tempB = (u8)(8 * tempB1);
879 addr = DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH;
880 }
881 else
882 {
883 tempB = 0;
884 addr = DRAM_CONT_ADD_ECC_PHASE_REC_CTRL;
885 }
886 fineStartLoc = tempB;
887 fineEndLoc = (u8)(fineStartLoc + 4);
888 grossStartLoc = (u8)(fineEndLoc + 1);
889 grossEndLoc = (u8)(grossStartLoc + 1);
890
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.
897 */
898 if(pDCTData->WLGrossDelay[index+ByteLane] >= 3)
899 {
900 gross += pDCTData->WLGrossDelay[index+ByteLane];
901 if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
902 gross -= 1;
903 else
904 gross -= 2;
905 }
906 else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3))
907 {
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.
910 */
911 gross = 0;
912 fine = 0;
913 }
914 pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
915 pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
916}