diff options
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 1007 |
1 files changed, 734 insertions, 273 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c index 9ea854f186..f3915a2438 100644 --- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c +++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c @@ -26,13 +26,22 @@ * *---------------------------------------------------------------------------- */ -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); +u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue); +u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue); +void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, + u8 dct, u8 dimm, BOOL wl); +void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm); +void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass); +void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass); +void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass); + +static int32_t abs(int32_t val) { + if (val < 0) + val *= -1; + + return val; +} + /* *----------------------------------------------------------------------------- * EXPORTED FUNCTIONS @@ -58,34 +67,55 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm); * OUT *----------------------------------------------------------------------------- */ -void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, - u8 dimm, u8 pass) +void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, + u8 dct, u8 dimm, u8 pass) { u8 ByteLane; u32 Value, Addr; u16 Addl_Data_Offset, Addl_Data_Port; + sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; 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, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart, - TrDimmSelEnd,(u32)dimm); + TrDimmSelEnd, (u32)dimm); + + if (is_fam15h()) { + /* Set TrNibbleSel = 0 + * + * TODO: Add support for x4 DIMMs + */ + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, 2, + 2, (u32)0); + } + /* 2. Prepare the DIMMs for write levelization using DDR3-defined * MR commands. */ - prepareDimms(pMCTData, pDCTData,dimm, TRUE); + prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE); + /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to * satisfy DDR3-defined internal DRAM timing. */ - pMCTData->AgesaDelay(40); + if (is_fam15h()) + precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40); + else + pMCTData->AgesaDelay(40); + /* 4. Configure the processor's DDR phy for write levelization training: */ - procConifg(pMCTData,pDCTData, dimm, pass); + procConfig(pMCTstat, pDCTstat, dct, dimm, pass); + /* 5. Begin write levelization training: - * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */ - if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */ + if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) + { + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1); + } else { /* Broadcast write to all D3Dbyte chipset register offset 0xc @@ -94,7 +124,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, * retain value of 3:2 (Trdimmsel) * reset bit 5 (FrzPR) */ - if (pDCTData->DctTrain) + if (dct) { Addl_Data_Offset=0x198; Addl_Data_Port=0x19C; @@ -119,29 +149,127 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, DctAccessDone, DctAccessDone)) == 0); } + if (is_fam15h()) + proc_MFENCE(); + /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */ - pMCTData->AgesaDelay(140); + if (is_fam15h()) + precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200); + else + pMCTData->AgesaDelay(140); + /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */ - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + set_DCT_ADDR_Bits(pDCTData, dct, 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++; + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass); + } + + pDCTData->WLCriticalGrossDelayPrevPass = 0x1f; +} + +void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, + u8 dct, u8 dimm, u8 pass) +{ + u8 ByteLane; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + + if (is_fam15h()) { + int32_t gross_diff[MAX_BYTE_LANES]; + int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass; + uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm); + + /* Calculate the Critical Gross Delay */ + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + /* Calculate the gross delay differential for this lane */ + gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane]; + gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane]; + + /* WrDqDqsEarly values greater than 2 are reserved */ + if (gross_diff[ByteLane] < -2) + gross_diff[ByteLane] = -2; + + /* Update the Critical Gross Delay */ + if (gross_diff[ByteLane] < cgd) + cgd = gross_diff[ByteLane]; + } + + pDCTData->WLCriticalGrossDelayPrevPass = cgd; + + /* Compensate for occasional noise/instability causing sporadic training failure */ + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f); + uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f); + if (abs(total_delay_phy - total_delay_seed) > 0x20) { + printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__); + pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane]; + pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane]; + } + } + } +} + +void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, + u8 dct, u8 dimm, u8 pass) +{ + u8 ByteLane; + sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + + if (is_fam15h()) { + uint32_t dword; + int32_t gross_diff[MAX_BYTE_LANES]; + int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass; + uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm); + + /* Apply offset(s) if needed */ + if (cgd < 0) { + dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8); + dword &= ~(0x3 << 24); /* WrDqDqsEarly = abs(cgd) */ + dword |= ((abs(cgd) & 0x3) << 24); + Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword); + + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + /* Calculate the gross delay differential for this lane */ + gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane]; + gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane]; + + /* Prevent underflow in the presence of noise / instability*/ + if (gross_diff[ByteLane] < cgd) + gross_diff[ByteLane] = cgd; + + pDCTData->WLGrossDelay[index+ByteLane] = (gross_diff[ByteLane] + (abs(cgd) & 0x3)); + } + } else { + dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8); + dword &= ~(0x3 << 24); /* WrDqDqsEarly = 0 */ + Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword); + } + } + + /* Write the adjusted gross and fine delay settings + * to the target DIMM. */ + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass); } /* 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, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0); + if (is_fam15h()) + proc_MFENCE(); + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ - pMCTData->AgesaDelay(10); + if (is_fam15h()) + precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10); + else + pMCTData->AgesaDelay(10); /* 7. Program the target DIMM back to normal operation by configuring * the following (See section 2.8.5.4.1.1 @@ -151,7 +279,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, * For a two DIMM system, program the Rtt value for the target DIMM * to the normal operating termination: */ - prepareDimms(pMCTData, pDCTData,dimm,FALSE); + prepareDimms(pMCTstat, pDCTstat, dct, dimm, FALSE); } /*---------------------------------------------------------------------------- @@ -161,7 +289,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, */ /*----------------------------------------------------------------------------- - * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) + * u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) * * Description: * This function swaps the bits in MSR register value @@ -173,12 +301,17 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, * * ---------------------------------------------------------------------------- */ -u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) +u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue) { + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; u32 tempW, tempW1; - tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, - FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); + if (is_fam15h()) + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15); + else + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10); if (tempW1 & 1) { if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) @@ -197,7 +330,7 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) } /*----------------------------------------------------------------------------- - * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) + * u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) * * Description: * This function swaps the bits in MSR register value @@ -209,12 +342,17 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) * * ---------------------------------------------------------------------------- */ -u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) +u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) { + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; u32 tempW, tempW1; - tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, - FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); + if (is_fam15h()) + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15); + else + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10); if (tempW1 & 1) { if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) @@ -265,7 +403,7 @@ static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms return term; } -static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank) +static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count) { uint16_t term; @@ -296,27 +434,27 @@ static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms * * 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 - * + * Fam10h: BKDG Rev. 3.62 section 2.8.9.9.1 + * Fam15h: BKDG Rev. 3.14 section 2.10.5.8.1 * ---------------------------------------------------------------------------- */ -void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) +void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, + u8 dct, u8 dimm, BOOL wl) { u32 tempW, tempW1, tempW2, MrsBank; u8 rank, currDimm, MemClkFreq; + sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); + uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; - MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + if (is_fam15h()) { + MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, 0, 4); + } else { + MemClkFreq = get_Bits(pDCTData, dct, 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. */ @@ -324,52 +462,95 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) 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); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, dimm*2+rank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, 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); + MrsBank = swapBankBits(pDCTstat, dct, 1); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, 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; + /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */ + if (is_fam15h()) { + tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; + tempW &= ~(0x0244); + } else { + /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ + tempW2 = get_Bits(pDCTData, dct, 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 (is_fam15h()) { if (wl) { - if (rank == 0) { - /* Get Rtt_WR for the current DIMM and rank */ - uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); - - /* Convert dynamic termination code to corresponding nominal termination code */ - if (dynamic_term == 0x200) - tempW1 = 0x04; - else if (dynamic_term == 0x400) - tempW1 = 0x40; - else - tempW1 = 0x0; + if (number_of_dimms > 1) { + if (rank == 0) { + /* Get Rtt_WR for the current DIMM and rank */ + tempW2 = fam15_rttwr(pDCTstat, dct, dimm, rank, package_type); + } else { + tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); + } } else { - tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); } } else { - tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); + } + tempW1 = 0; + tempW1 |= ((tempW2 & 0x4) >> 2) << 9; + tempW1 |= ((tempW2 & 0x2) >> 1) << 6; + tempW1 |= ((tempW2 & 0x1) >> 0) << 2; + } else { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); + } else { + if (wl) { + if (number_of_dimms > 1) { + if (rank == 0) { + /* Get Rtt_WR for the current DIMM and rank */ + uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]); + + /* Convert dynamic termination code to corresponding nominal termination code */ + if (dynamic_term == 0x200) + tempW1 = 0x04; + else if (dynamic_term == 0x400) + tempW1 = 0x40; + else + tempW1 = 0x0; + } else { + tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + } + } else { + tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + } + } else { + tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + } } } + + /* Apply Rtt_Nom to the MRS control word */ tempW=tempW|tempW1; /* All ranks of the target DIMM are set to write levelization mode. */ @@ -389,68 +570,105 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) 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); + + /* Program MrsAddress[5,1]=output driver impedance control (DIC) */ + if (is_fam15h()) { + tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); + } else { + /* Read DIC from F2x[1,0]84[DrvImpCtrl] */ + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); + } + + /* Apply DIC to the MRS control word */ if (bitTest(tempW1, 1)) tempW = bitTestSet(tempW, 5); if (bitTest(tempW1, 0)) tempW = bitTestSet(tempW, 1); - tempW = swapAddrBits_wl(pDCTData, tempW); + tempW = swapAddrBits_wl(pDCTstat, dct, tempW); + + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, 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, + set_Bits(pDCTData, dct, 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, + while ((get_Bits(pDCTData, dct, 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); + MrsBank = swapBankBits(pDCTstat, dct, 2); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, 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; + + /* Retrieve normal settings of the MRS control word and clear Rtt_WR */ + if (is_fam15h()) { + tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; + tempW &= ~(0x0600); + } else { + /* 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, dct, 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 - tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); + if (is_fam15h()) { + tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); + } else { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); + else + tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]); + } + + /* Apply Rtt_WR to the MRS control word */ tempW=tempW|tempW1; - tempW = swapAddrBits_wl(pDCTData,tempW); - set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, - DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + tempW = swapAddrBits_wl(pDCTstat, dct, tempW); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, 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, + set_Bits(pDCTData, dct, 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, + while ((get_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) { } @@ -469,97 +687,163 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) 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); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, currDimm*2+rank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, 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); + MrsBank = swapBankBits(pDCTstat, dct, 1); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, 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; + /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */ + if (is_fam15h()) { + tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; + tempW &= ~(0x0244); + } else { + /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ + tempW2 = get_Bits(pDCTData, dct, 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 - tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); + if (is_fam15h()) { + tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); + tempW1 = 0; + tempW1 |= ((tempW2 & 0x4) >> 2) << 9; + tempW1 |= ((tempW2 & 0x2) >> 1) << 6; + tempW1 |= ((tempW2 & 0x1) >> 0) << 2; + } else { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); + else + tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); + } + + /* Apply Rtt_Nom to the MRS control word */ 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); + + /* Program MrsAddress[5,1]=output driver impedance control (DIC) */ + if (is_fam15h()) { + tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); + } else { + /* Read DIC from F2x[1,0]84[DrvImpCtrl] */ + tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); + } + + /* Apply DIC to the MRS control word */ 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); + + tempW = swapAddrBits_wl(pDCTstat, dct, tempW); + + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command * to the specified DIMM. */ - set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, + set_Bits(pDCTData, dct, 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, + while ((get_Bits(pDCTData, dct, 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); + MrsBank = swapBankBits(pDCTstat, dct, 2); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, 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; + + /* Retrieve normal settings of the MRS control word and clear Rtt_WR */ + if (is_fam15h()) { + tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; + tempW &= ~(0x0600); + } else { + /* 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, dct, 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 - tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); + if (is_fam15h()) { + tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); + } else { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); + else + tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]); + } + + /* Apply Rtt_WR to the MRS control word */ tempW=tempW|tempW1; - tempW = swapAddrBits_wl(pDCTData,tempW); - set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, - DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + tempW = swapAddrBits_wl(pDCTstat, dct, tempW); + if (is_fam15h()) + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); + else + set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, 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, + set_Bits(pDCTData, dct, 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, + while ((get_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) { } @@ -583,29 +867,60 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) * OUT * ---------------------------------------------------------------------------- */ -void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm) +void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm) { + sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + 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); + if (is_fam15h()) { + /* Convert DIMM number to CS */ + uint32_t dword; + uint8_t cs; + uint8_t rank = 0; + + cs = (dimm * 2) + rank; + + /* Fetch preprogammed ODT pattern from configuration registers */ + dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, ((cs>3)?0x23c:0x238)); + if ((cs == 7) || (cs == 3)) + WrLvOdt1 = ((dword >> 24) & 0xf); + else if ((cs == 6) || (cs == 2)) + WrLvOdt1 = ((dword >> 16) & 0xf); + else if ((cs == 5) || (cs == 1)) + WrLvOdt1 = ((dword >> 8) & 0xf); + else if ((cs == 4) || (cs == 0)) + WrLvOdt1 = (dword & 0xf); + } else { + 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 = (u8)bitTestSet(WrLvOdt1, dimm); + WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); } - } else { - WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); } - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1); } +#ifdef UNUSED_CODE +static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq) +{ + uint16_t fam15h_next_lowest_freq_tab[] = {0, 0, 0, 0, 0x4, 0, 0x4, 0, 0, 0, 0x6, 0, 0, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12}; + return fam15h_next_lowest_freq_tab[memclk_freq]; +} +#endif + /*----------------------------------------------------------------------------- - * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass) + * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass) * * Description: * This function programs the ODT values for the NB @@ -618,31 +933,43 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm) * OUT * ---------------------------------------------------------------------------- */ -void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) +void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass) { - u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq; + u8 ByteLane, MemClkFreq; + int32_t Seed_Gross; + int32_t Seed_Fine; + uint8_t Seed_PreGross; u32 Value, Addr; u16 Addl_Data_Offset, Addl_Data_Port; - u16 freq_tab[] = {400, 533, 667, 800}; + sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + u16 fam10h_freq_tab[] = {400, 533, 667, 800}; + uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; - /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */ - MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, - FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); + if (is_fam15h()) { + /* MemClkFreq: 0x4: 333MHz; 0x6: 400MHz; 0xa: 533MHz; 0xe: 667MHz; 0x12: 800MHz; 0x16: 933MHz */ + MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, 0, 4); + } else { + /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */ + MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); + } /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the * current memory subsystem configuration. */ - programODT(pMCTData, pDCTData, dimm); + programODT(pMCTstat, pDCTstat, dct, dimm); /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */ - if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) { - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) { + set_DCT_ADDR_Bits(pDCTData, dct, 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) + if (dct) { Addl_Data_Offset=0x198; Addl_Data_Port=0x19C; @@ -665,33 +992,94 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) DctAccessDone, DctAccessDone)) == 0); } + if (is_fam15h()) + proc_MFENCE(); + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ - pMCTData->AgesaDelay(10); + if (is_fam15h()) + precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10); + else + pMCTData->AgesaDelay(10); + + /* Program write levelling seed values */ if (pass == 1) { - if (pDCTData->Status[DCT_STATUS_REGISTERED]) - { - if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain)))) + /* Pass 1 */ + if (is_fam15h()) { + uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ + uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); + uint16_t Seed_Total = 0; + if (package_type == PT_GR) { + /* Socket G34: Fam15h BKDG v3.14 Table 96 */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + Seed_Total = 0x41; + } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) { + Seed_Total = 0x0; + } else { + Seed_Total = 0xf; + } + } else if (package_type == PT_C3) { + /* Socket C32: Fam15h BKDG v3.14 Table 97 */ + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + Seed_Total = 0x3e; + } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) { + Seed_Total = 0x0; + } else { + Seed_Total = 0x12; + } + } else if (package_type == PT_M2) { + /* Socket AM3: Fam15h BKDG v3.14 Table 98 */ + Seed_Total = 0xf; + } + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + Seed_Total += ((AddrCmdPrelaunch)?0x10:0x0); + + /* Adjust seed for the minimum platform supported frequency */ + Seed_Total = (int32_t) (((((int64_t) Seed_Total) * + fam15h_freq_tab[MemClkFreq] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100))); + + Seed_Gross = (Seed_Total >> 5) & 0x1f; + Seed_Fine = Seed_Total & 0x1f; + + /* Save seed values for later use */ + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; + pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + + if (Seed_Gross == 0) + Seed_PreGross = 0; + else if (Seed_Gross & 0x1) + Seed_PreGross = 1; + else + Seed_PreGross = 2; + + pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; + } + } else { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { - Seed_Gross = 0x02; - Seed_Fine = 0x16; + if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct)))) + { + Seed_Gross = 0x02; + Seed_Fine = 0x16; + } + else + { + Seed_Gross = 0x02; + Seed_Fine = 0x00; + } } else { - Seed_Gross = 0x02; - Seed_Fine = 0x00; - } - } - else - { - if (MemClkFreq == 6) { - /* DDR-800 */ - Seed_Gross = 0x00; - Seed_Fine = 0x1a; - } else { - /* Use settings for DDR-400 (interpolated from BKDG) */ - Seed_Gross = 0x00; - Seed_Fine = 0x0d; + if (MemClkFreq == 6) { + /* DDR-800 */ + Seed_Gross = 0x00; + Seed_Fine = 0x1a; + } else { + /* Use settings for DDR-400 (interpolated from BKDG) */ + Seed_Gross = 0x00; + Seed_Fine = 0x0d; + } } } for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) @@ -707,39 +1095,91 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; } - } else { /* Pass 2 */ + } else { + /* Pass 2 */ /* From BKDG, Write Leveling Seed Value. */ - u32 RegisterDelay, SeedTotal; - for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) - { - if (pDCTData->Status[DCT_STATUS_REGISTERED]) - RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */ - else - RegisterDelay = 0; - SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | - (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5); - /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization - training) - RegisterDelay. */ - SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) * - freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 100))); - Seed_Gross = SeedTotal / 32; - Seed_Fine = SeedTotal & 0x1f; - if (Seed_Gross == 0) - Seed_Gross = 0; - else if (Seed_Gross & 0x1) - Seed_Gross = 1; - else - Seed_Gross = 2; - pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; - pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + if (is_fam15h()) { + uint32_t RegisterDelay; + int32_t SeedTotal; + int32_t SeedTotalPreScaling; + uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ + + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + if (AddrCmdPrelaunch) + RegisterDelay = 0x30; + else + RegisterDelay = 0x20; + } else { + RegisterDelay = 0; + } + /* Retrieve WrDqDqsEarly */ + AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value); + + /* Calculate adjusted seed values */ + SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | + ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5); + SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value)); + SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) * + fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100))); + + if (SeedTotal >= 0) { + Seed_Gross = SeedTotal / 32; + Seed_Fine = SeedTotal % 32; + } else { + Seed_Gross = (SeedTotal / 32) - 1; + Seed_Fine = (SeedTotal % 32) + 32; + } + + if (Seed_Gross == 0) + Seed_PreGross = 0; + else if (Seed_Gross & 0x1) + Seed_PreGross = 1; + else + Seed_PreGross = 2; + + /* Save seed values for later use */ + pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; + pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; + + pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; + pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + } + } else { + u32 RegisterDelay, SeedTotal; + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) + { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) + RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */ + else + RegisterDelay = 0; + SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | + (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5); + /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization + training) - RegisterDelay. */ + SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) * + fam10h_freq_tab[MemClkFreq-3] * 100) / (fam10h_freq_tab[0] * 100))); + Seed_Gross = SeedTotal / 32; + Seed_Fine = SeedTotal & 0x1f; + if (Seed_Gross == 0) + Seed_Gross = 0; + else if (Seed_Gross & 0x1) + Seed_Gross = 1; + else + Seed_Gross = 2; + pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; + pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + } } } - setWLByteDelay(pDCTData, ByteLane, dimm, 0); + pDCTData->WLPrevMemclkFreq = MemClkFreq; + setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass); } /*----------------------------------------------------------------------------- - * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){ + * void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm){ * * Description: * This function writes the write levelization byte delay for the Phase @@ -759,8 +1199,9 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) * *----------------------------------------------------------------------------- */ -void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) +void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass) { + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr; u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW; @@ -773,22 +1214,26 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) 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; + if (is_fam15h()) { + grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; + } else { + /* This subtract 0xC workaround might be temporary. */ + if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+dct)))) + { + 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) @@ -799,15 +1244,16 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) EccValue = ((grossDelayValue << 5) | fineDelayValue); ByteLane++; } - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + set_DCT_ADDR_Bits(pDCTData, dct, 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, + set_DCT_ADDR_Bits(pDCTData, dct, 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, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue); } else { + /* Fam10h BKDG Rev. 3.62 2.8.9.9.1 (6) */ index = (u8)(MAX_BYTE_LANES * dimm); grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; fineDelayValue = pDCTData->WLFineDelay[index+ByteLane]; @@ -837,16 +1283,24 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) grossStartLoc = (u8)(fineEndLoc + 1); grossEndLoc = (u8)(grossStartLoc + 1); - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue); - set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, + set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue); + + pDCTData->WLFineDelayPrevPass[index+ByteLane] = fineDelayValue; + pDCTData->WLGrossDelayPrevPass[index+ByteLane] = grossDelayValue; + if (pass == FirstPass) { + pDCTData->WLFineDelayFirstPass[index+ByteLane] = fineDelayValue; + pDCTData->WLGrossDelayFirstPass[index+ByteLane] = grossDelayValue; + pDCTData->WLCriticalGrossDelayFirstPass = pDCTData->WLCriticalGrossDelayPrevPass; + } } } /*----------------------------------------------------------------------------- - * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm) + * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm) * * Description: * This function reads the write levelization byte delay from the Phase @@ -864,8 +1318,9 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) * *----------------------------------------------------------------------------- */ -void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm) +void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass) { + sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index; u32 addr, fine, gross; tempB = 0; @@ -886,25 +1341,31 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm) grossStartLoc = (u8)(fineEndLoc + 1); grossEndLoc = (u8)(grossStartLoc + 1); - fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, + fine = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc); - gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, + gross = get_ADD_DCT_Bits(pDCTData, dct, 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. + + if (!is_fam15h()) { + /* Adjust seed gross delay overflow (greater than 3): + * - Adjust the trained gross delay to the original seed gross delay. */ - gross = 0; - fine = 0; + 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; |