Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2010 Advanced Micro Devices, Inc. |
Timothy Pearson | b8a355d | 2015-09-05 17:55:58 -0500 | [diff] [blame] | 5 | * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; version 2 of the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 17 | static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, |
| 18 | struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); |
| 19 | static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, |
| 20 | struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); |
| 21 | static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, |
| 22 | struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 23 | static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); |
| 24 | static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); |
| 25 | static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); |
| 26 | static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 27 | static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); |
| 28 | static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); |
| 29 | |
| 30 | static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat) |
| 31 | { |
| 32 | u8 ByteLane, DimmNum, OddByte, Addl_Index, Channel; |
| 33 | u8 EccRef1, EccRef2, EccDQSScale; |
| 34 | u32 val; |
| 35 | u16 word; |
| 36 | |
| 37 | for (Channel = 0; Channel < 2; Channel ++) { |
| 38 | for (DimmNum = 0; DimmNum < C_MAX_DIMMS; DimmNum ++) { /* we use DimmNum instead of DimmNumx3 */ |
| 39 | for (ByteLane = 0; ByteLane < 9; ByteLane ++) { |
| 40 | /* Get RxEn initial value from WrDqs */ |
| 41 | if (ByteLane & 1) |
| 42 | OddByte = 1; |
| 43 | else |
| 44 | OddByte = 0; |
| 45 | if (ByteLane < 2) |
| 46 | Addl_Index = 0x30; |
| 47 | else if (ByteLane < 4) |
| 48 | Addl_Index = 0x31; |
| 49 | else if (ByteLane < 6) |
| 50 | Addl_Index = 0x40; |
| 51 | else if (ByteLane < 8) |
| 52 | Addl_Index = 0x41; |
| 53 | else |
| 54 | Addl_Index = 0x32; |
| 55 | Addl_Index += DimmNum * 3; |
| 56 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 57 | val = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, Channel, 0x98, Addl_Index); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 58 | if (OddByte) |
| 59 | val >>= 16; |
| 60 | /* Save WrDqs to stack for later usage */ |
| 61 | pDCTstat->CH_D_B_TxDqs[Channel][DimmNum][ByteLane] = val & 0xFF; |
| 62 | EccDQSScale = pDCTstat->CH_EccDQSScale[Channel]; |
| 63 | word = pDCTstat->CH_EccDQSLike[Channel]; |
| 64 | if ((word & 0xFF) == ByteLane) EccRef1 = val & 0xFF; |
| 65 | if (((word >> 8) & 0xFF) == ByteLane) EccRef2 = val & 0xFF; |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat) |
| 72 | { |
| 73 | u32 val; |
| 74 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 75 | val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 76 | val &= ~(1 << DisAutoRefresh); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 77 | Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 78 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 79 | val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 80 | val &= ~(1 << DisAutoRefresh); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 81 | Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat, |
| 85 | struct DCTStatStruc *pDCTstat) |
| 86 | { |
| 87 | u32 val; |
| 88 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 89 | val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 90 | val |= 1 << DisAutoRefresh; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 91 | Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 92 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 93 | val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 94 | val |= 1 << DisAutoRefresh; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 95 | Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 96 | } |
| 97 | |
| 98 | |
| 99 | static void PhyWLPass1(struct MCTStatStruc *pMCTstat, |
| 100 | struct DCTStatStruc *pDCTstat, u8 dct) |
| 101 | { |
| 102 | u8 dimm; |
| 103 | u16 DIMMValid; |
| 104 | void *DCTPtr; |
| 105 | |
| 106 | dct &= 1; |
| 107 | |
| 108 | DCTPtr = (void *)(pDCTstat->C_DCTPtr[dct]); |
| 109 | pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct]; |
| 110 | pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct]; |
| 111 | |
| 112 | if (pDCTstat->GangedMode & 1) |
| 113 | pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0]; |
| 114 | |
| 115 | if (pDCTstat->DIMMValid) { |
| 116 | DIMMValid = pDCTstat->DIMMValid; |
| 117 | PrepareC_DCT(pMCTstat, pDCTstat, dct); |
| 118 | for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 119 | if (DIMMValid & (1 << (dimm << 1))) { |
| 120 | AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass); |
| 121 | AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass); |
| 122 | AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass); |
| 123 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | static void PhyWLPass2(struct MCTStatStruc *pMCTstat, |
| 129 | struct DCTStatStruc *pDCTstat, u8 dct) |
| 130 | { |
| 131 | u8 dimm; |
| 132 | u16 DIMMValid; |
| 133 | void *DCTPtr; |
| 134 | |
| 135 | dct &= 1; |
| 136 | |
| 137 | DCTPtr = (void *)&(pDCTstat->C_DCTPtr[dct]); /* todo: */ |
| 138 | pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct]; |
| 139 | pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct]; |
| 140 | |
| 141 | if (pDCTstat->GangedMode & 1) |
| 142 | pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0]; |
| 143 | |
| 144 | if (pDCTstat->DIMMValid) { |
| 145 | DIMMValid = pDCTstat->DIMMValid; |
| 146 | PrepareC_DCT(pMCTstat, pDCTstat, dct); |
| 147 | pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq; |
| 148 | pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL; |
| 149 | SPD2ndTiming(pMCTstat, pDCTstat, dct); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 150 | if (!is_fam15h()) { |
| 151 | ProgDramMRSReg_D(pMCTstat, pDCTstat, dct); |
| 152 | PlatformSpec_D(pMCTstat, pDCTstat, dct); |
| 153 | fenceDynTraining_D(pMCTstat, pDCTstat, dct); |
| 154 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 155 | Restore_OnDimmMirror(pMCTstat, pDCTstat); |
| 156 | StartupDCT_D(pMCTstat, pDCTstat, dct); |
| 157 | Clear_OnDimmMirror(pMCTstat, pDCTstat); |
| 158 | SetDllSpeedUp_D(pMCTstat, pDCTstat, dct); |
| 159 | DisableAutoRefresh_D(pMCTstat, pDCTstat); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 160 | for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 161 | if (DIMMValid & (1 << (dimm << 1))) { |
| 162 | AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass); |
| 163 | AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass); |
| 164 | AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass); |
| 165 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 166 | } |
| 167 | } |
| 168 | } |
| 169 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 170 | static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq) |
| 171 | { |
| 172 | uint16_t fam15h_next_highest_freq_tab[] = {0, 0, 0, 0, 0x6, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12, 0, 0, 0, 0x16, 0, 0, 0, 0x16}; |
| 173 | return fam15h_next_highest_freq_tab[memclk_freq]; |
| 174 | } |
| 175 | |
Timothy Pearson | b8a355d | 2015-09-05 17:55:58 -0500 | [diff] [blame] | 176 | /* Write Levelization Training |
| 177 | * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1 |
| 178 | */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 179 | static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat, |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 180 | struct DCTStatStruc *pDCTstat, uint8_t Pass) |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 181 | { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 182 | uint16_t final_target_freq; |
| 183 | |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 184 | pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr); |
| 185 | pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]); |
| 186 | pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]); |
| 187 | |
| 188 | /* Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1 */ |
| 189 | DisableAutoRefresh_D(pMCTstat, pDCTstat); |
| 190 | |
| 191 | /* Disable ZQ calibration short command by F2x[1,0]94[ZqcsInterval]=00b */ |
| 192 | DisableZQcalibration(pMCTstat, pDCTstat); |
| 193 | PrepareC_MCT(pMCTstat, pDCTstat); |
| 194 | |
| 195 | if (pDCTstat->GangedMode & (1 << 0)) { |
| 196 | pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0]; |
| 197 | } |
| 198 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 199 | if (Pass == FirstPass) { |
| 200 | PhyWLPass1(pMCTstat, pDCTstat, 0); |
| 201 | PhyWLPass1(pMCTstat, pDCTstat, 1); |
| 202 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 203 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 204 | if (Pass == SecondPass) { |
| 205 | if (pDCTstat->TargetFreq > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { |
| 206 | /* 8.Prepare the memory subsystem for the target MEMCLK frequency. |
| 207 | * NOTE: BIOS must program both DCTs to the same frequency. |
| 208 | * NOTE: Fam15h steps the frequency, Fam10h slams the frequency. |
| 209 | */ |
| 210 | final_target_freq = pDCTstat->TargetFreq; |
| 211 | |
| 212 | while (pDCTstat->Speed != final_target_freq) { |
| 213 | if (is_fam15h()) |
| 214 | pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed); |
| 215 | else |
| 216 | pDCTstat->TargetFreq = final_target_freq; |
| 217 | SetTargetFreq(pMCTstat, pDCTstat); |
| 218 | PhyWLPass2(pMCTstat, pDCTstat, 0); |
| 219 | PhyWLPass2(pMCTstat, pDCTstat, 1); |
| 220 | } |
| 221 | |
| 222 | pDCTstat->TargetFreq = final_target_freq; |
| 223 | |
| 224 | uint8_t dct; |
| 225 | for (dct = 0; dct < 2; dct++) { |
| 226 | sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; |
| 227 | memcpy(pDCTData->WLGrossDelayFinalPass, pDCTData->WLGrossDelayPrevPass, sizeof(pDCTData->WLGrossDelayPrevPass)); |
| 228 | memcpy(pDCTData->WLFineDelayFinalPass, pDCTData->WLFineDelayPrevPass, sizeof(pDCTData->WLFineDelayPrevPass)); |
| 229 | pDCTData->WLCriticalGrossDelayFinalPass = pDCTData->WLCriticalGrossDelayPrevPass; |
| 230 | } |
| 231 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | SetEccWrDQS_D(pMCTstat, pDCTstat); |
| 235 | EnableAutoRefresh_D(pMCTstat, pDCTstat); |
| 236 | EnableZQcalibration(pMCTstat, pDCTstat); |
| 237 | } |
| 238 | |
| 239 | void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 240 | struct DCTStatStruc *pDCTstatA, uint8_t Pass) |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 241 | { |
| 242 | u8 Node; |
| 243 | |
| 244 | for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| 245 | struct DCTStatStruc *pDCTstat; |
| 246 | pDCTstat = pDCTstatA + Node; |
| 247 | |
| 248 | if (pDCTstat->NodePresent) { |
| 249 | mctSMBhub_Init(Node); |
| 250 | Clear_OnDimmMirror(pMCTstat, pDCTstat); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 251 | WriteLevelization_HW(pMCTstat, pDCTstat, Pass); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 252 | Restore_OnDimmMirror(pMCTstat, pDCTstat); |
| 253 | } |
| 254 | } |
| 255 | } |