aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c')
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
new file mode 100644
index 0000000000..bf9222339d
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
@@ -0,0 +1,217 @@
+/*
+ * 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 SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void AgesaHwWlPhase1(sMCTStruct *pMCTData,
+ sDCTStruct *pDCTData, u8 dimm, u8 pass);
+static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+static void MultiplyDelay(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u8 ByteLane, DimmNum, OddByte, Addl_Index, Channel;
+ u8 EccRef1, EccRef2, EccDQSScale;
+ u32 val;
+ u16 word;
+
+ for (Channel = 0; Channel < 2; Channel ++) {
+ for (DimmNum = 0; DimmNum < C_MAX_DIMMS; DimmNum ++) { /* we use DimmNum instead of DimmNumx3 */
+ for (ByteLane = 0; ByteLane < 9; ByteLane ++) {
+ /* Get RxEn initial value from WrDqs */
+ if (ByteLane & 1)
+ OddByte = 1;
+ else
+ OddByte = 0;
+ if (ByteLane < 2)
+ Addl_Index = 0x30;
+ else if (ByteLane < 4)
+ Addl_Index = 0x31;
+ else if (ByteLane < 6)
+ Addl_Index = 0x40;
+ else if (ByteLane < 8)
+ Addl_Index = 0x41;
+ else
+ Addl_Index = 0x32;
+ Addl_Index += DimmNum * 3;
+
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index);
+ if (OddByte)
+ val >>= 16;
+ /* Save WrDqs to stack for later usage */
+ pDCTstat->CH_D_B_TxDqs[Channel][DimmNum][ByteLane] = val & 0xFF;
+ EccDQSScale = pDCTstat->CH_EccDQSScale[Channel];
+ word = pDCTstat->CH_EccDQSLike[Channel];
+ if ((word & 0xFF) == ByteLane) EccRef1 = val & 0xFF;
+ if (((word >> 8) & 0xFF) == ByteLane) EccRef2 = val & 0xFF;
+ }
+ }
+ }
+}
+
+static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+ val &= ~(1 << DisAutoRefresh);
+ Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+ val &= ~(1 << DisAutoRefresh);
+ Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+}
+
+static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+ val |= 1 << DisAutoRefresh;
+ Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+ val |= 1 << DisAutoRefresh;
+ Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+}
+
+
+static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DIMMValid;
+ void *DCTPtr;
+
+ dct &= 1;
+
+ DCTPtr = (void *)(pDCTstat->C_DCTPtr[dct]);
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
+
+ if (pDCTstat->GangedMode & 1)
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->DIMMValid) {
+ DIMMValid = pDCTstat->DIMMValid;
+ PrepareC_DCT(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1)))
+ AgesaHwWlPhase1(pDCTstat->C_MCTPtr, DCTPtr, dimm, FirstPass);
+ }
+ }
+}
+
+static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DIMMValid;
+ void *DCTPtr;
+
+ dct &= 1;
+
+ DCTPtr = (void *)&(pDCTstat->C_DCTPtr[dct]); /* todo: */
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
+
+ if (pDCTstat->GangedMode & 1)
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->DIMMValid) {
+ DIMMValid = pDCTstat->DIMMValid;
+ PrepareC_DCT(pMCTstat, pDCTstat, dct);
+ pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
+ pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL;
+ SPD2ndTiming(pMCTstat, pDCTstat, dct);
+ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+ PlatformSpec_D(pMCTstat, pDCTstat, dct);
+ fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ StartupDCT_D(pMCTstat, pDCTstat, dct);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+ MultiplyDelay(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1)))
+ AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
+ }
+ }
+}
+
+static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
+ pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
+ pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
+
+ /* Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1 */
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+
+ /* Disable ZQ calibration short command by F2x[1,0]94[ZqcsInterval]=00b */
+ DisableZQcalibration(pMCTstat, pDCTstat);
+ PrepareC_MCT(pMCTstat, pDCTstat);
+
+ if (pDCTstat->GangedMode & (1 << 0)) {
+ pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0];
+ }
+
+ PhyWLPass1(pMCTstat, pDCTstat, 0);
+ PhyWLPass1(pMCTstat, pDCTstat, 1);
+
+ if (pDCTstat->TargetFreq > 4) {
+ /* 8.Prepare the memory subsystem for the target MEMCLK frequency.
+ * Note: BIOS must program both DCTs to the same frequency.
+ */
+ SetTargetFreq(pMCTstat, pDCTstat);
+ PhyWLPass2(pMCTstat, pDCTstat, 0);
+ PhyWLPass2(pMCTstat, pDCTstat, 1);
+
+ }
+
+ SetEccWrDQS_D(pMCTstat, pDCTstat);
+ EnableAutoRefresh_D(pMCTstat, pDCTstat);
+ EnableZQcalibration(pMCTstat, pDCTstat);
+}
+
+void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ mctSMBhub_Init(Node);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ WriteLevelization_HW(pMCTstat, pDCTstat);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ }
+ }
+}