summaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mctwl.c')
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctwl.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
new file mode 100644
index 0000000000..d14bc0dd58
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
@@ -0,0 +1,382 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+static void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+
+
+static void AgesaDelay(u32 msec)
+{
+ mct_Wait(msec*10);
+}
+
+void PrepareC_MCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ pDCTstat->C_MCTPtr->AgesaDelay = AgesaDelay;
+ pDCTstat->C_MCTPtr->PlatMaxTotalDimms = mctGet_NVbits(NV_MAX_DIMMS);
+ pDCTstat->C_MCTPtr->PlatMaxDimmsDct = pDCTstat->C_MCTPtr->PlatMaxTotalDimms >> 1;
+}
+
+void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DimmValid;
+ u16 Dimmx8Present;
+
+ dct &= 1;
+
+ pDCTstat->C_DCTPtr[dct]->DctTrain = dct;
+
+ if (dct == 1) {
+ Dimmx8Present = pDCTstat->Dimmx8Present >> 1;
+ } else
+ Dimmx8Present = pDCTstat->Dimmx8Present;
+ Dimmx8Present &= 0x55;
+
+ pDCTstat->C_DCTPtr[dct]->MaxDimmsInstalled = pDCTstat->MAdimms[dct];
+ DimmValid = pDCTstat->DIMMValidDCT[dct];
+
+ pDCTstat->C_DCTPtr[dct]->NodeId = pDCTstat->Node_ID;
+ pDCTstat->C_DCTPtr[dct]->LogicalCPUID = pDCTstat->LogicalCPUID;
+
+ for (dimm = 0; dimm < MAX_DIMMS; dimm++) {
+ if (DimmValid & (1 << dimm))
+ pDCTstat->C_DCTPtr[dct]->DimmPresent[dimm] = 1;
+ if (Dimmx8Present & (1 << dimm))
+ pDCTstat->C_DCTPtr[dct]->DimmX8Present[dimm] = 1;
+ }
+
+ if (pDCTstat->GangedMode & (1 << 0))
+ pDCTstat->C_DCTPtr[dct]->CurrDct = 0;
+ else
+ pDCTstat->C_DCTPtr[dct]->CurrDct = dct;
+
+ pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[dct];
+ if (!(pDCTstat->GangedMode & (1 << 0)) && (dct == 1))
+ pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 1;
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 0;
+ } else {
+ if (pDCTstat->MirrPresU_NumRegR > 0)
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 1;
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0;
+ }
+
+ pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present;
+
+ for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) {
+ u8 DimmRanks;
+ if (DimmValid & (1 << dimm)) {
+ DimmRanks = 1;
+ if (pDCTstat->DimmDRPresent & (1 << (dimm+dct)))
+ DimmRanks = 2;
+ else if (pDCTstat->DimmQRPresent & (1 << (dimm+dct)))
+ DimmRanks = 4;
+ } else
+ DimmRanks = 0;
+ pDCTstat->C_DCTPtr[dct]->DimmRanks[dimm] = DimmRanks;
+ }
+}
+
+void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val |= 1 << 11;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val |= 1 << 11;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+}
+
+void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+}
+
+static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+ /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ val |= 1 << EnterSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ val |= 1 << EnterSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ } while (val & (1 <<EnterSelfRef));
+ if (DCT1Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ } while (val & (1 <<EnterSelfRef));
+}
+
+/*
+ * Change memclk for write levelization pass 2
+ */
+static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+ /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
+ if (DCT0Present) {
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
+ val |= 1 << DisAutoComp;
+ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
+ val |= 1 << DisAutoComp;
+ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val &= ~(1 << MemClkFreqVal);
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= ~(1 << MemClkFreqVal);
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val &= 0xFFFFFFF8;
+ val |= pDCTstat->TargetFreq - 1;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= 0xFFFFFFF8;
+ val |= pDCTstat->TargetFreq - 1;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreqVal] = 1. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val |= 1 << MemClkFreqVal;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val |= 1 << MemClkFreqVal;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ } while (val & (1 << FreqChgInProg));
+ if (DCT1Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ } while (val & (1 << FreqChgInProg));
+
+ /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
+ if (DCT0Present) {
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
+ val &= ~(1 << DisAutoComp);
+ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
+ val &= ~(1 << DisAutoComp);
+ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
+ }
+}
+
+/* Multiply the previously saved delay values in Pass 1, step #5 by
+ (target frequency)/400 to find the gross and fine delay initialization
+ values at the target frequency.
+ */
+void MultiplyDelay(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u16 index;
+ u8 Multiplier;
+ u8 gross, fine;
+ u16 total;
+
+ Multiplier = pDCTstat->TargetFreq;
+
+ for (index=0; index < MAX_BYTE_LANES*MAX_LDIMMS; index ++) {
+ gross = pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index];
+ fine = pDCTstat->C_DCTPtr[dct]->WLFineDelay[index];
+
+ total = gross << 5 | fine;
+ total *= Multiplier;
+ if (total % 3)
+ total = total / 3 + 1;
+ else
+ total = total / 3;
+ pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index] = (total & 0xFF) >> 5;
+ pDCTstat->C_DCTPtr[dct]->WLFineDelay[index] = total & 0x1F;
+ }
+}
+
+/*
+ * the DRAM controller to bring the DRAMs out of self refresh mode.
+ */
+static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+ /* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ val |= 1 << ExitSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ val |= 1 << ExitSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ } while (val & (1 << ExitSelfRef));
+ if (DCT1Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ } while (val & (1 << ExitSelfRef));
+}
+
+void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Program F2x[1,0]90[EnterSelfRefresh]=1.
+ * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
+ */
+ EnterSelfRefresh(pMCTstat, pDCTstat);
+
+ /*
+ * Program F2x[1,0]9C_x08[DisAutoComp]=1
+ * Program F2x[1,0]94[MemClkFreqVal] = 0.
+ * Program F2x[1,0]94[MemClkFreq] to specify the target MEMCLK frequency.
+ * Program F2x[1,0]94[MemClkFreqVal] = 1.
+ * Wait until F2x[1,0]94[FreqChgInProg]=0.
+ * Program F2x[1,0]9C_x08[DisAutoComp]=0
+ */
+ ChangeMemClk(pMCTstat, pDCTstat);
+
+ /* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
+ * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
+ */
+ ExitSelfRefresh(pMCTstat, pDCTstat);
+
+ /* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */
+ mct_Wait(250);
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ /* TODO: Assuming the dct==0. The agesa here is confusing. */
+ FreqChgCtrlWrd(pMCTstat, pDCTstat);
+ }
+}
+
+static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set)
+{
+ u32 val;
+ u32 reg_off = dct * 0x100 + 0x44;
+ while (reg_off < 0x60) {
+ val = Get_NB32(pDCTstat->dev_dct, reg_off);
+ if (val & (1 << CSEnable))
+ set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror));
+ Set_NB32(pDCTstat->dev_dct, reg_off, val);
+ reg_off += 8;
+ }
+}
+
+void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
+ if (pDCTstat->MirrPresU_NumRegR & 0x55)
+ Modify_OnDimmMirror(pDCTstat, 0, 1); /* dct=0, set */
+ if (pDCTstat->MirrPresU_NumRegR & 0xAA)
+ Modify_OnDimmMirror(pDCTstat, 1, 1); /* dct=1, set */
+ }
+}
+void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
+ if (pDCTstat->MirrPresU_NumRegR & 0x55)
+ Modify_OnDimmMirror(pDCTstat, 0, 0); /* dct=0, clear */
+ if (pDCTstat->MirrPresU_NumRegR & 0xAA)
+ Modify_OnDimmMirror(pDCTstat, 1, 0); /* dct=1, clear */
+ }
+}