diff options
author | Zheng Bao <zheng.bao@amd.com> | 2010-04-23 17:32:48 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2010-04-23 17:32:48 +0000 |
commit | eb75f652d392d2f4f257194e112f3f0db7479145 (patch) | |
tree | aa972907734abcba4ca52f2a3a71f8d81d4bdce0 /src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | |
parent | fe6c2cda6e6977894d9b668af9509b983c850f68 (diff) | |
download | coreboot-eb75f652d392d2f4f257194e112f3f0db7479145.tar.xz |
DDR3 support for AMD Fam10.
Signed-off-by: Zheng Bao <zheng.bao@amd.com>
Acked-by: Stefan Reinauer <stepan@coresystems.de>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5481 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c new file mode 100644 index 0000000000..fdc35e3bfb --- /dev/null +++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c @@ -0,0 +1,916 @@ +/* + * 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 + */ + +/* + *----------------------------------------------------------------------------- + * MODULES USED + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue); +u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue); +void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl); +void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm); +void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass); +void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr); +void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm); +/* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData, + * u8 Dimm, u8 Pass) + * + * Description: + * This function initialized Hardware based write levelization phase 1 + * + * Parameters: + * IN OUT *SPDData - Pointer to buffer with information about each DIMMs + * SPD information + * *MCTData - Pointer to buffer with runtime parameters, + * *DCTData - Pointer to buffer with information about each DCT + * + * IN DIMM - Logical DIMM number + * Pass - First or Second Pass + * OUT + *----------------------------------------------------------------------------- + */ +void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + u8 dimm, u8 pass) +{ + u8 ByteLane; + u32 Value, Addr; + u16 Addl_Data_Offset, Addl_Data_Port; + + pDCTData->WLPass = pass; + /* 1. Specify the target DIMM that is to be trained by programming + * F2x[1, 0]9C_x08[TrDimmSel]. + */ + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart, + TrDimmSelEnd,(u32)dimm); + /* 2. Prepare the DIMMs for write levelization using DDR3-defined + * MR commands. */ + prepareDimms(pMCTData, pDCTData,dimm, TRUE); + /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to + * satisfy DDR3-defined internal DRAM timing. + */ + pMCTData->AgesaDelay(40); + /* 4. Configure the processor's DDR phy for write levelization training: */ + procConifg(pMCTData,pDCTData, dimm, pass); + /* 5. Begin write levelization training: + * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */ + if (pDCTData->LogicalCPUID & AMD_DR_Cx) + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1); + else + { + /* Broadcast write to all D3Dbyte chiplet register offset 0xc + * Set bit 0 (wrTrain) + * Program bit 4 to nibble being trained (only matters for x4dimms) + * retain value of 3:2 (Trdimmsel) + * reset bit 5 (FrzPR) + */ + if (pDCTData->DctTrain) + { + Addl_Data_Offset=0x198; + Addl_Data_Port=0x19C; + } + else + { + Addl_Data_Offset=0x98; + Addl_Data_Port=0x9C; + } + Addr=0x0D00000C; + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr); + while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset, + DctAccessDone, DctAccessDone)) == 0); + AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value); + Value = bitTestSet(Value, 0); /* enable WL training */ + Value = bitTestReset(Value, 4); /* for x8 only */ + Value = bitTestReset(Value, 5); /* for harward WL training */ + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value); + Addr=0x4D030F0C; + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr); + while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset, + DctAccessDone, DctAccessDone)) == 0); + } + + /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */ + pMCTData->AgesaDelay(140); + /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */ + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0); + /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 + * to get the gross and fine delay settings + * for the target DIMM and save these values. */ + ByteLane = 0; + while (ByteLane < MAX_BYTE_LANES) + { + getWLByteDelay(pDCTData,ByteLane, dimm); + setWLByteDelay(pDCTData,ByteLane, dimm, 1); + ByteLane++; + } + + /* 6. Configure DRAM Phy Control Register so that the phy stops driving + * write levelization ODT. */ + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0); + + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ + pMCTData->AgesaDelay(10); + + /* 7. Program the target DIMM back to normal operation by configuring + * the following (See section 2.8.5.4.1.1 + * [Phy Assisted Write Levelization] on page 97 pass 1, step #2): + * Configure all ranks of the target DIMM for normal operation. + * Enable the output drivers of all ranks of the target DIMM. + * For a two DIMM system, program the Rtt value for the target DIMM + * to the normal operating termination: + */ + prepareDimms(pMCTData, pDCTData,dimm,FALSE); +} + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) + * + * Description: + * This function swaps the bits in MSR register value + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * IN u32: MRS value + * OUT u32: sWAPPED BANK BITS + * + * ---------------------------------------------------------------------------- + */ +u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) +{ + u32 tempW, tempW1; + + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); + if (tempW1 & 1) + { + if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) + { + /* swap A3/A4,A5/A6,A7/A8 */ + tempW = MRSValue; + tempW1 = MRSValue; + tempW &= 0x0A8; + tempW1 &= 0x0150; + MRSValue &= 0xFE07; + MRSValue |= (tempW<<1); + MRSValue |= (tempW1>>1); + } + } + return MRSValue; +} + +/*----------------------------------------------------------------------------- + * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) + * + * Description: + * This function swaps the bits in MSR register value + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * IN u32: MRS value + * OUT u32: sWAPPED BANK BITS + * + * ---------------------------------------------------------------------------- + */ +u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) +{ + u32 tempW, tempW1; + + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); + if (tempW1 & 1) + { + if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) + { + /* swap BA0/BA1 */ + tempW = MRSValue; + tempW1 = MRSValue; + tempW &= 0x01; + tempW1 &= 0x02; + MRSValue = 0; + MRSValue |= (tempW<<1); + MRSValue |= (tempW1>>1); + } + } + return MRSValue; +} + +/*----------------------------------------------------------------------------- + * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL) + * + * Description: + * This function prepares DIMMS for training + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * *SPDData - Pointer to buffer with information about each DIMMs + * SPD information + * *MCTData - Pointer to buffer with runtime parameters, + * IN Dimm - Logical DIMM number + * WL - indicates if the routine is used for Write levelization + * training + * + * OUT + * + * ---------------------------------------------------------------------------- + */ +void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) +{ + u32 tempW, tempW1, tempW2, MrsBank; + u8 rank, currDimm, MemClkFreq; + + MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); + /* Configure the DCT to send initialization MR commands to the target DIMM + * ;by programming the F2x[1,0]7C register using the following steps. + */ + rank = 0; + while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2)) + { + /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, dimm*2+rank); + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * ;register that defines the required DDR3-defined function for write + * ; levelization. + */ + MrsBank = swapBankBits(pDCTData,1); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * ; for write levelization. + */ + tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */ + + /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ + tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); + if (tempW2) + { + if (pDCTData->DimmX8Present[dimm]) + tempW |= 0x800; + } + + /* determine Rtt_Nom for WL & Normal mode */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); + } else { + if (wl) + { + if (pDCTData->MaxDimmsInstalled == 1) + { + if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0)) + { + tempW1 = 0x00; /* Rtt_Nom=OFF */ + } + else + { + tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */ + } + } + else /* 2 Dimms or more per channel */ + { + if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1)) + { + tempW1 = 0x00; /* Rtt_Nom=OFF */ + } + else + { + if (MemClkFreq == 6) { + tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */ + } else { + tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */ + } + } + } + } + else { /* 1 or 4 Dimms per channel */ + if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4)) + { + tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */ + } + else /* 2 or 3 Dimms per channel */ + { + if (MemClkFreq < 5) { + tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */ + } else { + tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */ + } + } + } + } + tempW=tempW|tempW1; + + /* All ranks of the target DIMM are set to write levelization mode. */ + if (wl) + { + tempW1 = bitTestSet(tempW, MRS_Level); + if (rank == 0) + { + /* ?Enable the output driver of the first rank of the target DIMM. */ + tempW = tempW1; + } + else + { + /* Disable the output drivers of all other ranks for + * the target DIMM. */ + tempW = bitTestSet(tempW1, Qoff); + } + } + /* program MrsAddress[5,1]=output driver impedance control (DIC): + * based on F2x[1,0]84[DrvImpCtrl] */ + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); + if (bitTest(tempW1,1)) + {tempW = bitTestSet(tempW, 5);} + if (bitTest(tempW1,0)) + {tempW = bitTestSet(tempW, 1);} + + tempW = swapAddrBits_wl(pDCTData,tempW); + + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + * ;the specified DIMM. + */ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ + while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * ;register that defines the required DDR3-defined function for Rtt_WR. + */ + MrsBank = swapBankBits(pDCTData,2); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * ; for Rtt_WR (DRAMTermDyn). + */ + tempW = 0;/* PASR = 0,*/ + /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, + * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); + if (bitTest(tempW1,19)) + {tempW = bitTestSet(tempW, 7);} + if (bitTest(tempW1,18)) + {tempW = bitTestSet(tempW, 6);} + /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ + tempW=tempW|((tempW1&0x00700000)>>17); + /* workaround for DR-B0 */ + if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) + tempW+=0x8; + /* determine Rtt_WR for WL & Normal mode */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); + } else { + if (wl) + { + tempW1 = 0x00; /* Rtt_WR=off */ + } + else + { + if (pDCTData->MaxDimmsInstalled == 1) + { + tempW1 = 0x00; /* Rtt_WR=off */ + } + else + { + if (MemClkFreq == 6) { + tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */ + } else { + tempW1 = 0x400; /* Rtt_WR=RZQ/2 */ + } + } + } + } + tempW=tempW|tempW1; + tempW = swapAddrBits_wl(pDCTData,tempW); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + the specified DIMM.*/ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ + while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } + + rank++; + } + + /* Configure the non-target DIMM normally. */ + currDimm = 0; + while (currDimm < MAX_LDIMMS) + { + if (pDCTData->DimmPresent[currDimm]) + { + if (currDimm != dimm) + { + rank = 0; + while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2)) + { + + /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank + * ;to be trained. + */ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, currDimm*2+rank); + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal + * ;DRAM register that defines the required DDR3-defined function + * ; for write levelization. + */ + MrsBank = swapBankBits(pDCTData,1); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required + * ;DDR3-defined function for write levelization. + */ + tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */ + + /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ + tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); + if (tempW2) + { + if (pDCTData->DimmX8Present[currDimm]) + tempW |= 0x800; + } + + /* determine Rtt_Nom for WL & Normal mode */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); + } else { + if (wl) + { + if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1)) + { + tempW1 = 0x00; /* Rtt_Nom=OFF */ + } + else + { + if (MemClkFreq < 5) { + tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */ + } else { + tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */ + } + } + } + else { /* 1 or 4 Dimms per channel */ + if ((pDCTData->MaxDimmsInstalled == 4)) + { + tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */ + } + else { /* 2 or 3 Dimms per channel */ + if (MemClkFreq < 5) { + tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */ + } else { + tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */ + } + } + } + } + tempW=tempW|tempW1; + /* program MrsAddress[5,1]=output driver impedance control (DIC): + * based on F2x[1,0]84[DrvImpCtrl] */ + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); + if (bitTest(tempW1,1)) + {tempW = bitTestSet(tempW, 5);} + if (bitTest(tempW1,0)) + {tempW = bitTestSet(tempW, 1);} + tempW = swapAddrBits_wl(pDCTData,tempW); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command + * ; to the specified DIMM. + */ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ + while ((get_Bits(pDCTData, pDCTData->CurrDct, + pDCTData->NodeId, FUN_DCT, DRAM_INIT, + SendMrsCmd, SendMrsCmd)) == 1); + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * ;register that defines the required DDR3-defined function for Rtt_WR. + */ + MrsBank = swapBankBits(pDCTData,2); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * ; for Rtt_WR (DRAMTermDyn). + */ + tempW = 0;/* PASR = 0,*/ + /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, + * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ + tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); + if (bitTest(tempW1,19)) + {tempW = bitTestSet(tempW, 7);} + if (bitTest(tempW1,18)) + {tempW = bitTestSet(tempW, 6);} + /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ + tempW=tempW|((tempW1&0x00700000)>>17); + /* workaround for DR-B0 */ + if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) + tempW+=0x8; + /* determine Rtt_WR for WL & Normal mode */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); + } else { + if (wl) + { + tempW1 = 0x00; /* Rtt_WR=off */ + } + else + { + if (MemClkFreq == 6) { + tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */ + } else { + tempW1 = 0x400; /* Rtt_WR=RZQ/2 */ + } + } + } + tempW=tempW|tempW1; + tempW = swapAddrBits_wl(pDCTData,tempW); + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + the specified DIMM.*/ + set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ + while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } + rank++; + } + } + } + currDimm++; + } +} + +/*----------------------------------------------------------------------------- + * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm) + * + * Description: + * This function programs the ODT values for the NB + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * IN + * OUT + * ---------------------------------------------------------------------------- + */ +void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm) +{ + u8 WrLvOdt1=0; + + if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) { + if ((pDCTData->DctCSPresent & 0x05) == 0x05) { + WrLvOdt1 = 0x03; + } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) { + WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2); + } else { + WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm); + } + } else { + WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); + } + + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1); + +} + +/*----------------------------------------------------------------------------- + * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass) + * + * Description: + * This function programs the ODT values for the NB + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * *MCTData - Pointer to buffer with runtime parameters, + * IN Dimm - Logical DIMM + * Pass - First of Second Pass + * OUT + * ---------------------------------------------------------------------------- + */ +void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) +{ + u8 ByteLane, Seed_Gross, Seed_Fine; + u32 Value, Addr; + u16 Addl_Data_Offset, Addl_Data_Port; + + /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the + * ;current memory subsystem configuration. + */ + programODT(pMCTData, pDCTData, dimm); + + /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */ + if (pDCTData->LogicalCPUID & AMD_DR_Cx) + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn,(u32) 1); + else + { + /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B*/ + if (pDCTData->DctTrain) + { + Addl_Data_Offset=0x198; + Addl_Data_Port=0x19C; + } + else + { + Addl_Data_Offset=0x98; + Addl_Data_Port=0x9C; + } + Addr=0x0D008000; + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr); + while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset, + DctAccessDone, DctAccessDone)) == 0); + AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value); + Value = bitTestSet(Value, 12); + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value); + Addr=0x4D088F00; + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr); + while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset, + DctAccessDone, DctAccessDone)) == 0); + } + + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ + pMCTData->AgesaDelay(10); + ByteLane = 0; + if (pass == 1) + { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + { + if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain)))) + { + Seed_Gross = 0x02; + Seed_Fine = 0x16; + } + else + { + Seed_Gross = 0x02; + Seed_Fine = 0x00; + } + } + else + { + Seed_Gross = 0x00; + Seed_Fine = 0x1A; + } + while(ByteLane < MAX_BYTE_LANES) + { + /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and + * ;F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields + * ; If the target frequency is different than 400MHz, BIOS must + * execute two training passes for each DIMM. + * For pass 1 at a 400MHz MEMCLK frequency, use an initial total delay value + * ; of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined + * ;by design. + */ + pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; + pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + ByteLane++; + } + } + setWLByteDelay(pDCTData, ByteLane, dimm, 0); +} + +/*----------------------------------------------------------------------------- + * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){ + * + * Description: + * This function writes the write levelization byte delay for the Phase + * Recovery control registers + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * IN Dimm - Dimm Number + * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for each + * logical DIMM + * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each + * logical DIMM + * ByteLane - target byte lane to write + * targetAddr - 0: write to DRAM phase recovery control register + * 1: write to DQS write register + * OUT + * + *----------------------------------------------------------------------------- + */ +void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) +{ + u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr; + u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW; + + if (targetAddr == 0) + { + index = (u8)(MAX_BYTE_LANES * dimm); + ValueLow = 0; + ValueHigh = 0; + ByteLane = 0; + EccValue = 0; + while (ByteLane < MAX_BYTE_LANES) + { + /* This subtract 0xC workaround might be temporary. */ + if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain)))) + { + tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane]; + tempW -= 0xC; + pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5); + pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F); + } + grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; + /* Adjust seed gross delay overflow (greater than 3): + * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5). + * - Keep original seed gross delay for later reference. + */ + if(grossDelayValue >= 3) + { + grossDelayValue = (grossDelayValue&1)? 1 : 2; + } + fineDelayValue = pDCTData->WLFineDelay[index+ByteLane]; + if (ByteLane < 4) + ValueLow |= ((grossDelayValue << 5) | fineDelayValue) << 8*ByteLane; + else if(ByteLane < 8) + ValueHigh |= ((grossDelayValue << 5) | fineDelayValue) << 8*(ByteLane-4); + else + EccValue = ((grossDelayValue << 5) | fineDelayValue); + ByteLane++; + } + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_PHASE_REC_CTRL_LOW, 0, 31, (u32)ValueLow); + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh); + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue); + } + else + { + index = (u8)(MAX_BYTE_LANES * dimm); + grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; + fineDelayValue = pDCTData->WLFineDelay[index+ByteLane]; + + tempB = 0; + offsetAddr = (u8)(3 * dimm); + if (ByteLane < 2) + { + tempB = (u8)(16 * ByteLane); + addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01; + } + else if (ByteLane <4) + { + tempB = (u8)(16 * ByteLane); + addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 1; + } + else if (ByteLane <6) + { + tempB = (u8)(16 * ByteLane); + addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45; + } + else if (ByteLane <8) + { + tempB = (u8)(16 * ByteLane); + addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 + 1; + } + else + { + tempB = 0; + addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2; + } + addr += offsetAddr; + + fineStartLoc = (u8)(tempB % 32); + fineEndLoc = (u8)(fineStartLoc + 4); + grossStartLoc = (u8)(fineEndLoc + 1); + grossEndLoc = (u8)(grossStartLoc + 1); + + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue); + set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue); + } + +} + +/*----------------------------------------------------------------------------- + * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm) + * + * Description: + * This function reads the write levelization byte delay from the Phase + * Recovery control registers + * + * Parameters: + * IN OUT *DCTData - Pointer to buffer with information about each DCT + * IN Dimm - Dimm Number + * ByteLane - target byte lane to read + * OUT + * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for current + * byte for logical DIMM + * DCTData->WLFineDelay[index+ByteLane] - fine write delay for current + * byte for logical DIMM + * + *----------------------------------------------------------------------------- + */ +void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm) +{ + u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index; + u32 addr, fine, gross; + tempB = 0; + index = (u8)(MAX_BYTE_LANES*dimm); + if (ByteLane < 4) + { + tempB = (u8)(8 * ByteLane); + addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW; + } + else if (ByteLane < 8) + { + tempB1 = (u8)(ByteLane - 4); + tempB = (u8)(8 * tempB1); + addr = DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH; + } + else + { + tempB = 0; + addr = DRAM_CONT_ADD_ECC_PHASE_REC_CTRL; + } + fineStartLoc = tempB; + fineEndLoc = (u8)(fineStartLoc + 4); + grossStartLoc = (u8)(fineEndLoc + 1); + grossEndLoc = (u8)(grossStartLoc + 1); + + fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, + FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc); + gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, + FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc); + /* Adjust seed gross delay overflow (greater than 3): + * - Adjust the trained gross delay to the original seed gross delay. + */ + if(pDCTData->WLGrossDelay[index+ByteLane] >= 3) + { + gross += pDCTData->WLGrossDelay[index+ByteLane]; + if(pDCTData->WLGrossDelay[index+ByteLane] & 1) + gross -= 1; + else + gross -= 2; + } + else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) + { + /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative. + * We will then round the negative number to 0. + */ + gross = 0; + fine = 0; + } + pDCTData->WLFineDelay[index+ByteLane] = (u8)fine; + pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross; +} |