/* * 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); } } }