2 * This file is part of the coreboot project.
4 * Copyright (C) 2010 Advanced Micro Devices, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 static void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
21 struct DCTStatStruc *pDCTstat);
24 static void AgesaDelay(u32 msec)
29 void PrepareC_MCT(struct MCTStatStruc *pMCTstat,
30 struct DCTStatStruc *pDCTstat)
32 pDCTstat->C_MCTPtr->AgesaDelay = AgesaDelay;
33 pDCTstat->C_MCTPtr->PlatMaxTotalDimms = mctGet_NVbits(NV_MAX_DIMMS);
34 pDCTstat->C_MCTPtr->PlatMaxDimmsDct = pDCTstat->C_MCTPtr->PlatMaxTotalDimms >> 1;
37 void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
38 struct DCTStatStruc *pDCTstat, u8 dct)
46 pDCTstat->C_DCTPtr[dct]->DctTrain = dct;
49 Dimmx8Present = pDCTstat->Dimmx8Present >> 1;
51 Dimmx8Present = pDCTstat->Dimmx8Present;
52 Dimmx8Present &= 0x55;
54 pDCTstat->C_DCTPtr[dct]->MaxDimmsInstalled = pDCTstat->MAdimms[dct];
55 DimmValid = pDCTstat->DIMMValidDCT[dct];
57 pDCTstat->C_DCTPtr[dct]->NodeId = pDCTstat->Node_ID;
58 pDCTstat->C_DCTPtr[dct]->LogicalCPUID = pDCTstat->LogicalCPUID;
60 for (dimm = 0; dimm < MAX_DIMMS; dimm++) {
61 if (DimmValid & (1 << dimm))
62 pDCTstat->C_DCTPtr[dct]->DimmPresent[dimm] = 1;
63 if (Dimmx8Present & (1 << dimm))
64 pDCTstat->C_DCTPtr[dct]->DimmX8Present[dimm] = 1;
67 if (pDCTstat->GangedMode & (1 << 0))
68 pDCTstat->C_DCTPtr[dct]->CurrDct = 0;
70 pDCTstat->C_DCTPtr[dct]->CurrDct = dct;
72 pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[dct];
73 if (!(pDCTstat->GangedMode & (1 << 0)) && (dct == 1))
74 pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[0];
76 if (pDCTstat->Status & (1 << SB_Registered)) {
77 pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 1;
78 pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 0;
80 if (pDCTstat->MirrPresU_NumRegR > 0)
81 pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 1;
82 pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0;
85 pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present;
87 for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) {
89 if (DimmValid & (1 << (dimm << 1))) {
91 if (pDCTstat->DimmDRPresent & (1 << (dimm+dct)))
93 else if (pDCTstat->DimmQRPresent & (1 << (dimm+dct)))
97 pDCTstat->C_DCTPtr[dct]->DimmRanks[dimm] = DimmRanks;
101 void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
105 val = Get_NB32(pDCTstat->dev_dct, 0x94);
107 Set_NB32(pDCTstat->dev_dct, 0x94, val);
109 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
111 Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
114 void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
115 struct DCTStatStruc *pDCTstat)
119 val = Get_NB32(pDCTstat->dev_dct, 0x94);
122 Set_NB32(pDCTstat->dev_dct, 0x94, val);
124 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
127 Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
130 static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
131 struct DCTStatStruc *pDCTstat)
133 u8 DCT0Present, DCT1Present;
136 DCT0Present = pDCTstat->DIMMValidDCT[0];
137 if (pDCTstat->GangedMode)
140 DCT1Present = pDCTstat->DIMMValidDCT[1];
142 /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
144 val = Get_NB32(pDCTstat->dev_dct, 0x90);
145 val |= 1 << EnterSelfRef;
146 Set_NB32(pDCTstat->dev_dct, 0x90, val);
149 val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
150 val |= 1 << EnterSelfRef;
151 Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
153 /* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */
156 val = Get_NB32(pDCTstat->dev_dct, 0x90);
157 } while (val & (1 <<EnterSelfRef));
160 val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
161 } while (val & (1 <<EnterSelfRef));
165 * Change memclk for write levelization pass 2
167 static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
168 struct DCTStatStruc *pDCTstat)
170 u8 DCT0Present, DCT1Present;
173 DCT0Present = pDCTstat->DIMMValidDCT[0];
174 if (pDCTstat->GangedMode)
177 DCT1Present = pDCTstat->DIMMValidDCT[1];
179 /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
181 val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
182 val |= 1 << DisAutoComp;
183 Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
186 val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
187 val |= 1 << DisAutoComp;
188 Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
191 /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
193 val = Get_NB32(pDCTstat->dev_dct, 0x94);
194 val &= ~(1 << MemClkFreqVal);
195 Set_NB32(pDCTstat->dev_dct, 0x94, val);
198 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
199 val &= ~(1 << MemClkFreqVal);
200 Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
203 /* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */
205 val = Get_NB32(pDCTstat->dev_dct, 0x94);
207 val |= pDCTstat->TargetFreq - 1;
208 Set_NB32(pDCTstat->dev_dct, 0x94, val);
211 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
213 val |= pDCTstat->TargetFreq - 1;
214 Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
217 /* Program F2x[1, 0]94[MemClkFreqVal] = 1. */
219 val = Get_NB32(pDCTstat->dev_dct, 0x94);
220 val |= 1 << MemClkFreqVal;
221 Set_NB32(pDCTstat->dev_dct, 0x94, val);
224 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
225 val |= 1 << MemClkFreqVal;
226 Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
229 /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
232 val = Get_NB32(pDCTstat->dev_dct, 0x94);
233 } while (val & (1 << FreqChgInProg));
236 val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
237 } while (val & (1 << FreqChgInProg));
239 /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
241 val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
242 val &= ~(1 << DisAutoComp);
243 Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
246 val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
247 val &= ~(1 << DisAutoComp);
248 Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
252 /* Multiply the previously saved delay values in Pass 1, step #5 by
253 (target frequency)/400 to find the gross and fine delay initialization
254 values at the target frequency.
256 void MultiplyDelay(struct MCTStatStruc *pMCTstat,
257 struct DCTStatStruc *pDCTstat, u8 dct)
264 Multiplier = pDCTstat->TargetFreq;
266 for (index=0; index < MAX_BYTE_LANES*MAX_LDIMMS; index ++) {
267 gross = pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index];
268 fine = pDCTstat->C_DCTPtr[dct]->WLFineDelay[index];
270 total = gross << 5 | fine;
273 total = total / 3 + 1;
276 pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index] = (total & 0xFF) >> 5;
277 pDCTstat->C_DCTPtr[dct]->WLFineDelay[index] = total & 0x1F;
282 * the DRAM controller to bring the DRAMs out of self refresh mode.
284 static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
285 struct DCTStatStruc *pDCTstat)
287 u8 DCT0Present, DCT1Present;
290 DCT0Present = pDCTstat->DIMMValidDCT[0];
291 if (pDCTstat->GangedMode)
294 DCT1Present = pDCTstat->DIMMValidDCT[1];
296 /* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */
298 val = Get_NB32(pDCTstat->dev_dct, 0x90);
299 val |= 1 << ExitSelfRef;
300 Set_NB32(pDCTstat->dev_dct, 0x90, val);
303 val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
304 val |= 1 << ExitSelfRef;
305 Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
307 /* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */
310 val = Get_NB32(pDCTstat->dev_dct, 0x90);
311 } while (val & (1 << ExitSelfRef));
314 val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
315 } while (val & (1 << ExitSelfRef));
318 void SetTargetFreq(struct MCTStatStruc *pMCTstat,
319 struct DCTStatStruc *pDCTstat)
321 /* Program F2x[1,0]90[EnterSelfRefresh]=1.
322 * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
324 EnterSelfRefresh(pMCTstat, pDCTstat);
327 * Program F2x[1,0]9C_x08[DisAutoComp]=1
328 * Program F2x[1,0]94[MemClkFreqVal] = 0.
329 * Program F2x[1,0]94[MemClkFreq] to specify the target MEMCLK frequency.
330 * Program F2x[1,0]94[MemClkFreqVal] = 1.
331 * Wait until F2x[1,0]94[FreqChgInProg]=0.
332 * Program F2x[1,0]9C_x08[DisAutoComp]=0
334 ChangeMemClk(pMCTstat, pDCTstat);
336 /* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
337 * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
339 ExitSelfRefresh(pMCTstat, pDCTstat);
341 /* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */
344 if (pDCTstat->Status & (1 << SB_Registered)) {
345 /* TODO: Assuming the dct==0. The agesa here is confusing. */
346 FreqChgCtrlWrd(pMCTstat, pDCTstat);
350 static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set)
353 u32 reg_off = dct * 0x100 + 0x44;
354 while (reg_off < (dct * 0x100 + 0x60)) {
355 val = Get_NB32(pDCTstat->dev_dct, reg_off);
356 if (val & (1 << CSEnable))
357 set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror));
358 Set_NB32(pDCTstat->dev_dct, reg_off, val);
363 void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat,
364 struct DCTStatStruc *pDCTstat)
366 if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
367 if (pDCTstat->MirrPresU_NumRegR & 0x55)
368 Modify_OnDimmMirror(pDCTstat, 0, 1); /* dct=0, set */
369 if (pDCTstat->MirrPresU_NumRegR & 0xAA)
370 Modify_OnDimmMirror(pDCTstat, 1, 1); /* dct=1, set */
373 void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat,
374 struct DCTStatStruc *pDCTstat)
376 if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
377 if (pDCTstat->MirrPresU_NumRegR & 0x55)
378 Modify_OnDimmMirror(pDCTstat, 0, 0); /* dct=0, clear */
379 if (pDCTstat->MirrPresU_NumRegR & 0xAA)
380 Modify_OnDimmMirror(pDCTstat, 1, 0); /* dct=1, clear */