aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch')
-rw-r--r--resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch407
1 files changed, 407 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch b/resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch
new file mode 100644
index 00000000..4b7392b9
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0069-northbridge-amd-amdmct-mct_ddr3-Fix-lockups-and-wast.patch
@@ -0,0 +1,407 @@
+From c942d174cfc1a0ba6e91e0131c0a105addddbbd4 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <tpearson@raptorengineeringinc.com>
+Date: Wed, 24 Jun 2015 19:15:09 -0500
+Subject: [PATCH 069/139] northbridge/amd/amdmct/mct_ddr3: Fix lockups and
+ wasted time during ECC init
+
+Change-Id: I09a8ea83024186b7ece7d78a4bef1201ab34ff8a
+Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
+---
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 142 +++++++++++++++----------
+ src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 39 ++++++-
+ src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 22 +++-
+ src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 3 +-
+ 4 files changed, 145 insertions(+), 61 deletions(-)
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index d8a09f0..68957f5 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -1498,11 +1498,12 @@ restartinit:
+ InterleaveChannels_D(pMCTstat, pDCTstatA);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+- ECCInit_D(pMCTstat, pDCTstatA); /* Setup ECC control and ECC check-bits*/
+-
+- /* mctDoWarmResetMemClr_D(); */
+- printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+- MCTMemClr_D(pMCTstat,pDCTstatA);
++ if (!ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
++ /* Memory was not cleared during ECC setup */
++ /* mctDoWarmResetMemClr_D(); */
++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
++ MCTMemClr_D(pMCTstat,pDCTstatA);
++ }
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+@@ -1695,7 +1696,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ uint8_t x8_present = 0;
+ uint8_t memclk_index;
+ uint8_t interleave_channels = 0;
+- uint8_t redirect_ecc_scrub = 0;
+ uint16_t trdrdsddc;
+ uint16_t trdrddd;
+ uint16_t cdd_trdrddd;
+@@ -1733,9 +1733,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged))
+ interleave_channels = 1;
+
+- if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+- redirect_ecc_scrub = 1;
+-
+ dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf;
+ if (dword > 6)
+ read_odt_delay = dword - 6;
+@@ -1927,21 +1924,10 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword |= (interleave_channels & 0x1) << 2;
+ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */
+
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */
+- dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
+- dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+- dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
+- dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
+- dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
+- dword |= redirect_ecc_scrub & 0x1;
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
+-
+- dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
+- dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
+- Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++ /* NOTE
++ * ECC-related setup is performed as part of ECCInit_D and must not be located here,
++ * otherwise semi-random lockups will occur due to misconfigured scrubbing hardware!
++ */
+
+ /* FIXME
+ * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
+@@ -1983,11 +1969,17 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+ dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
+ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */
+
++ /* Configure partial power down delay */
++ dword = Get_NB32(dev, 0x244); /* DRAM Controller Miscellaneous 3 */
++ dword &= ~0xf; /* PrtlChPDDynDly = 0x2 */
++ dword |= 0x2;
++ Set_NB32(dev, 0x244, dword); /* DRAM Controller Miscellaneous 3 */
++
+ /* Enable prefetchers */
+- dword = Get_NB32_DCT(dev, dct, 0x110); /* Memory Controller Configuration High */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
+ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
+ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
+- Set_NB32_DCT(dev, dct, 0x110, dword); /* Memory Controller Configuration High */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
+ }
+ }
+
+@@ -2091,6 +2083,19 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
+ }
+
++ if (is_fam15h()) {
++ uint8_t Node;
++ struct DCTStatStruc *pDCTstat;
++
++ /* Switch DCT control register to DCT 0 per Erratum 505 */
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ pDCTstat = pDCTstatA + Node;
++ if (pDCTstat->NodePresent) {
++ fam15h_switch_dct(pDCTstat->dev_map, 0);
++ }
++ }
++ }
++
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+ mctHookAfterAnyTraining();
+ }
+@@ -2337,6 +2342,7 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ * status are checked to ensure that memclr has completed.
+ */
+ u8 Node;
++ uint32_t dword;
+ struct DCTStatStruc *pDCTstat;
+
+ if (!mctGet_NVbits(NV_DQSTrainCTL)){
+@@ -2357,6 +2363,16 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ }
+ }
+ }
++
++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
++ /* Configure and enable prefetchers */
++ if (is_fam15h())
++ dword = 0x0ce00f41; /* BKDG recommended */
++ else
++ dword = 0x0fe40fc0; /* BKDG recommended */
++ dword |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
++ Set_NB32(pDCTstat->dev_dct, 0x11c, dword);
++ }
+ }
+
+ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+@@ -2364,48 +2380,59 @@ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ {
+ u32 val;
+ u32 dev;
+- u32 reg;
++ uint32_t dword;
+
+ /* Initiates a memory clear operation on one node */
+ if (pDCTstat->DCTSysLimit) {
+ dev = pDCTstat->dev_dct;
+- reg = 0x110;
++
++ /* Disable prefetchers */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
++ dword |= 0x1 << 13; /* PrefIoDis = 1 */
++ dword |= 0x1 << 12; /* PrefCpuDis = 1 */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
+
+ do {
+- val = Get_NB32(dev, reg);
++ val = Get_NB32(dev, 0x110);
+ } while (val & (1 << MemClrBusy));
+
+ val |= (1 << MemClrInit);
+- Set_NB32(dev, reg, val);
++ Set_NB32(dev, 0x110, val);
+ }
+ }
+
+ static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+ {
+- u32 val;
+- u32 dev = pDCTstat->dev_dct;
+- u32 reg;
++ uint32_t dword;
++ uint32_t dev = pDCTstat->dev_dct;
++
++ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+ /* Ensure that a memory clear operation has completed on one node */
+ if (pDCTstat->DCTSysLimit){
+- reg = 0x110;
+-
++ printk(BIOS_DEBUG, "%s: Waiting for memory clear to complete", __func__);
+ do {
+- val = Get_NB32(dev, reg);
+- } while (val & (1 << MemClrBusy));
++ dword = Get_NB32(dev, 0x110);
+
++ printk(BIOS_DEBUG, ".");
++ } while (dword & (1 << MemClrBusy));
++
++ printk(BIOS_DEBUG, "\n");
+ do {
+- val = Get_NB32(dev, reg);
+- } while (!(val & (1 << Dr_MemClrStatus)));
++ printk(BIOS_DEBUG, ".");
++ dword = Get_NB32(dev, 0x110);
++ } while (!(dword & (1 << Dr_MemClrStatus)));
++ printk(BIOS_DEBUG, "\n");
+ }
+
+- if (is_fam15h())
+- val = 0x0ce00f41; /* BKDG recommended */
+- else
+- val = 0x0fe40fc0; /* BKDG recommended */
+- val |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
+- Set_NB32(dev, 0x11c, val);
++ /* Enable prefetchers */
++ dword = Get_NB32(dev, 0x11c); /* Memory Controller Configuration High */
++ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */
++ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */
++ Set_NB32(dev, 0x11c, dword); /* Memory Controller Configuration High */
++
++ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ }
+
+ static u8 NodePresent_D(u8 Node)
+@@ -3346,8 +3373,8 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ dev = pDCTstat->dev_dct;
+
+ /* Build Dram Control Register Value */
+- DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8); /* Dram Control*/
+- DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control*/
++ DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xa8); /* Dram Miscellaneous 2 */
++ DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control */
+
+ /* FIXME: Skip mct_checkForDxSupport */
+ /* REV_CALL mct_DoRdPtrInit if not Dx */
+@@ -3402,9 +3429,15 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ /* set only if x8 Registered DIMMs in System*/
+ DramConfigHi |= 1 << RDqsEn;
+
+- if (mctGet_NVbits(NV_CKE_CTL))
+- /*Chip Select control of CKE*/
+- DramConfigHi |= 1 << 16;
++ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
++ DramConfigLo |= 1 << 25; /* PendRefPaybackS3En = 1 */
++ DramConfigLo |= 1 << 24; /* StagRefEn = 1 */
++ DramConfigHi |= 1 << 16; /* PowerDownMode = 1 */
++ } else {
++ if (mctGet_NVbits(NV_CKE_CTL))
++ /*Chip Select control of CKE*/
++ DramConfigHi |= 1 << 16;
++ }
+
+ /* Control Bank Swizzle */
+ if (0) /* call back not needed mctBankSwizzleControl_D()) */
+@@ -4112,8 +4145,7 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+
+ if (load_spd_hashes_from_nvram(pMCTstat, pDCTstat) < 0) {
+ pDCTstat->spd_data.nvram_spd_match = 0;
+- }
+- else {
++ } else {
+ compare_nvram_spd_hashes(pMCTstat, pDCTstat);
+ }
+ #else
+@@ -4311,8 +4343,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ }
+ for (i=i_start; i<i_end; i++) {
+ index_reg = 0x98;
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
+- Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A/B Output Driver Compensation Control */
++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A/B Output Driver Compensation Control */
+ }
+
+ return pDCTstat->ErrCode;
+@@ -6102,11 +6134,11 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ DramMRS |= 1 << 1;
+
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
++ dword |= DramMRS;
+ if (is_fam15h())
+ dword &= ~0x00800003;
+ else
+ dword &= ~0x00fc2f8f;
+- dword |= DramMRS;
+ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
+ }
+
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+index 918e91e..a0482e8 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+@@ -92,8 +92,13 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
+ uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
+
++ uint8_t redirect_ecc_scrub = 0;
++
+ mctHookBeforeECC();
+
++ if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
++ redirect_ecc_scrub = 1;
++
+ /* Construct these booleans, based on setup options, for easy handling
+ later in this procedure */
+ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
+@@ -230,12 +235,12 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+- if(OB_ECCRedir) {
+- val |= (1<<0); /* enable redirection */
++ if (OB_ECCRedir) {
++ val |= (1<<0); /* enable redirection */
+ }
+- Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
++ Set_NB32(dev, 0x5c, val); /* Dram Scrub Addr Low */
+ val = curBase>>24;
+- Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
++ Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+ Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
+
+ if (!is_fam15h()) {
+@@ -252,6 +257,32 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+ }
+ }
+ }
++
++ if (is_fam15h()) {
++ uint8_t dct;
++
++ /* Disable training mode
++ * See fam15EnableTrainingMode for the non-ECC training mode tear-down code
++ */
++ for (dct = 0; dct < 2; dct++) {
++ /* NOTE: Reads use DCT 0 and writes use the current DCT per Erratum 505 */
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, 0, 0x58); /* Scrub Rate Control */
++ dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */
++ dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
++ dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */
++ dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */
++ dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */
++ dword |= redirect_ecc_scrub & 0x1;
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */
++
++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */
++ dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */
++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */
++ }
++ }
+ } /* this node has ECC enabled dram */
+ } /*Node has Dram */
+ } /*if Node present */
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+index 5ef4a2c..32b447f 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+@@ -23,7 +23,27 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+ {
+ u32 val;
+
+- if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
++ /* FIXME
++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
++ * For now assume a maximum of 2 DIMMs per channel can be installed
++ */
++ uint8_t MaxDimmsInstallable = 2;
++
++ if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
++ uint8_t cs_mux_45;
++ uint8_t cs_mux_67;
++
++ /* BKDG v3.14 Table 200 / Table 201 */
++ if (MaxDimmsInstallable < 3) {
++ cs_mux_45 = 1;
++ cs_mux_67 = 1;
++ } else {
++ cs_mux_45 = 0;
++ cs_mux_67 = 0;
++ }
++ misc2 |= (cs_mux_45 & 0x1) << 26;
++ misc2 |= (cs_mux_67 & 0x1) << 27;
++ } else if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ misc2 |= 1 << SubMemclkRegDly;
+ if (mctGet_NVbits(NV_MAX_DIMMS) == 8)
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+index 7ea7901..c760bac 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+@@ -209,7 +209,8 @@ void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
+ 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__);
++ printk(BIOS_DEBUG, "%s: overriding faulty phy value (seed: %04x phy: %04x step: %04x)\n", __func__,
++ total_delay_seed, total_delay_phy, abs(total_delay_phy - total_delay_seed));
+ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
+ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
+ }
+--
+1.9.1
+