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