diff options
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mctwl.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 382 |
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 */ + } +} |