diff options
author | Timothy Pearson <tpearson@raptorengineeringinc.com> | 2015-07-26 00:55:43 -0500 |
---|---|---|
committer | Martin Roth <martinroth@google.com> | 2015-11-16 17:55:57 +0100 |
commit | f682d0028cb33fc4a085af83344f4a7b9c0e78f2 (patch) | |
tree | 84c2289b2ca976c728f2fb42ded31c9f79c7a275 /src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | |
parent | 474ff3dee54e81017587f53ce644307e4f655333 (diff) |
amd/amdmct/mct_ddr3: Partially fix up registered DIMMs on Fam10h
Sufficient support has been added to allow booting with registered
DIMMs on the KGPE-D16 in certain slots. ECC support needs additional
work; the ECC data lanes appear to cause boot failures in some slots.
Change-Id: Ieaf4cbf351908e5a89760be49a6667dc55dbc575
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
Reviewed-on: http://review.coreboot.org/12017
Tested-by: build bot (Jenkins)
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 72 |
1 files changed, 48 insertions, 24 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c index aec8e53653..fab7bc6a73 100644 --- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c +++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c @@ -926,7 +926,9 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui else if ((cs == 4) || (cs == 0)) WrLvOdt1 = (dword & 0xf); } else { - if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); + } else { if ((pDCTData->DctCSPresent & 0x05) == 0x05) { WrLvOdt1 = 0x03; } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) { @@ -934,14 +936,14 @@ void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui } else { WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm); } - } else { - WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); } } set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1); + printk(BIOS_SPEW, "Programmed DCT %d write levelling ODT pattern %08x\n", dct, WrLvOdt1); + } #ifdef UNUSED_CODE @@ -976,7 +978,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui u16 Addl_Data_Offset, Addl_Data_Port; sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; - u16 fam10h_freq_tab[] = {400, 533, 667, 800}; + uint16_t fam10h_freq_tab[] = {0, 0, 0, 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}; if (is_fam15h()) { @@ -1089,21 +1091,18 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; } } else { - if (pDCTData->Status[DCT_STATUS_REGISTERED]) - { - if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct)))) - { + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ + + /* The seed values below assume Pass 1 utilizes a 400MHz clock frequency (DDR3-800) */ + if (AddrCmdPrelaunch == 0) { Seed_Gross = 0x02; - Seed_Fine = 0x16; - } - else - { + Seed_Fine = 0x01; + } else { Seed_Gross = 0x02; - Seed_Fine = 0x00; + Seed_Fine = 0x11; } - } - else - { + } else { if (MemClkFreq == 6) { /* DDR-800 */ Seed_Gross = 0x00; @@ -1127,6 +1126,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui */ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + printk(BIOS_SPEW, "\tLane %02x initial seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f)); } } else { /* Pass 2 */ @@ -1178,21 +1178,30 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + + printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f)); } } else { - u32 RegisterDelay, SeedTotal; + uint32_t RegisterDelay; + uint32_t SeedTotalPreScaling; + uint32_t SeedTotal; + 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]) - RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */ - else + if (pDCTData->Status[DCT_STATUS_REGISTERED]) { + if (AddrCmdPrelaunch == 0) + RegisterDelay = 0x20; + else + RegisterDelay = 0x30; + } else { RegisterDelay = 0; - SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | - (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5); + } + SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | + (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5)) - RegisterDelay; /* 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))); + SeedTotal = (uint16_t) ((((uint64_t) SeedTotalPreScaling) * + fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100)); Seed_Gross = SeedTotal / 32; Seed_Fine = SeedTotal & 0x1f; if (Seed_Gross == 0) @@ -1201,8 +1210,20 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui Seed_Gross = 1; else Seed_Gross = 2; + + /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems + * due to the long register delays causing premature total delay wrap-around. + * Attempt to work around this... + */ + SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f); + SeedTotal += RegisterDelay; + Seed_Gross = SeedTotal / 32; + Seed_Fine = SeedTotal & 0x1f; + pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + + printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f)); } } } @@ -1379,6 +1400,8 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 gross = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc); + printk(BIOS_SPEW, "\tLane %02x raw readback: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f)); + if (!is_fam15h()) { /* Adjust seed gross delay overflow (greater than 3): * - Adjust the trained gross delay to the original seed gross delay. @@ -1402,4 +1425,5 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 } pDCTData->WLFineDelay[index+ByteLane] = (u8)fine; pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross; + printk(BIOS_SPEW, "\tLane %02x final adjusted value: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f)); } |