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