zbao | 7d94cf9 | 2012-07-02 14:19:14 +0800 | [diff] [blame] | 1 | /* $NoKeywords:$ */ |
| 2 | /** |
| 3 | * @file |
| 4 | * |
| 5 | * mnphy.c |
| 6 | * |
| 7 | * Common Northbridge Phy support |
| 8 | * |
| 9 | * @xrefitem bom "File Content Label" "Release Content" |
| 10 | * @e project: AGESA |
| 11 | * @e sub-project: (Mem/NB) |
| 12 | * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $ |
| 13 | * |
| 14 | **/ |
| 15 | /***************************************************************************** |
| 16 | * |
Siyuan Wang | 641f00c | 2013-06-08 11:50:55 +0800 | [diff] [blame] | 17 | * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc. |
| 18 | * All rights reserved. |
| 19 | * |
| 20 | * Redistribution and use in source and binary forms, with or without |
| 21 | * modification, are permitted provided that the following conditions are met: |
| 22 | * * Redistributions of source code must retain the above copyright |
| 23 | * notice, this list of conditions and the following disclaimer. |
| 24 | * * Redistributions in binary form must reproduce the above copyright |
| 25 | * notice, this list of conditions and the following disclaimer in the |
| 26 | * documentation and/or other materials provided with the distribution. |
| 27 | * * Neither the name of Advanced Micro Devices, Inc. nor the names of |
| 28 | * its contributors may be used to endorse or promote products derived |
| 29 | * from this software without specific prior written permission. |
| 30 | * |
| 31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| 32 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 33 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 34 | * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY |
| 35 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 36 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 37 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 38 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 39 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 40 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
zbao | 7d94cf9 | 2012-07-02 14:19:14 +0800 | [diff] [blame] | 41 | * *************************************************************************** |
| 42 | * |
| 43 | */ |
| 44 | |
| 45 | |
| 46 | /* |
| 47 | *---------------------------------------------------------------------------- |
| 48 | * MODULES USED |
| 49 | * |
| 50 | *---------------------------------------------------------------------------- |
| 51 | */ |
| 52 | |
| 53 | |
| 54 | |
| 55 | #include "AGESA.h" |
| 56 | #include "amdlib.h" |
| 57 | #include "Ids.h" |
| 58 | #include "mport.h" |
| 59 | #include "mm.h" |
| 60 | #include "mn.h" |
| 61 | #include "mt.h" |
| 62 | #include "mu.h" |
| 63 | #include "PlatformMemoryConfiguration.h" |
| 64 | #include "heapManager.h" |
| 65 | #include "merrhdl.h" |
| 66 | #include "Filecode.h" |
| 67 | CODE_GROUP (G1_PEICC) |
| 68 | RDATA_GROUP (G1_PEICC) |
| 69 | #define FILECODE PROC_MEM_NB_MNPHY_FILECODE |
| 70 | /*---------------------------------------------------------------------------- |
| 71 | * DEFINITIONS AND MACROS |
| 72 | * |
| 73 | *---------------------------------------------------------------------------- |
| 74 | */ |
| 75 | #define UNUSED_CLK 4 |
| 76 | |
| 77 | /*---------------------------------------------------------------------------- |
| 78 | * TYPEDEFS AND STRUCTURES |
| 79 | * |
| 80 | *---------------------------------------------------------------------------- |
| 81 | */ |
| 82 | /// Type of an entry for processing phy init compensation for client NB |
| 83 | typedef struct { |
| 84 | BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided |
| 85 | BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified |
| 86 | BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified |
| 87 | UINT16 ExtraValue; ///< Extra value needed to be written to bit field |
| 88 | CONST UINT16 (*TxPrePN)[3][5]; ///< Pointer to slew rate table |
| 89 | } PHY_COMP_INIT_CLIENTNB; |
| 90 | |
| 91 | /*---------------------------------------------------------------------------- |
| 92 | * PROTOTYPES OF LOCAL FUNCTIONS |
| 93 | * |
| 94 | *---------------------------------------------------------------------------- |
| 95 | */ |
| 96 | |
| 97 | |
| 98 | |
| 99 | /*---------------------------------------------------------------------------- |
| 100 | * EXPORTED FUNCTIONS |
| 101 | * |
| 102 | *---------------------------------------------------------------------------- |
| 103 | */ |
| 104 | /* -----------------------------------------------------------------------------*/ |
| 105 | /** |
| 106 | * |
| 107 | * |
| 108 | * This function gets a delay value a PCI register during training |
| 109 | * |
| 110 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 111 | * @param[in] TrnDly - type of delay to be set |
| 112 | * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed |
| 113 | * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding |
| 114 | * |
| 115 | * @return Value read |
| 116 | */ |
| 117 | |
| 118 | UINT32 |
| 119 | MemNGetTrainDlyNb ( |
| 120 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 121 | IN TRN_DLY_TYPE TrnDly, |
| 122 | IN DRBN DrbnVar |
| 123 | ) |
| 124 | { |
| 125 | return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0); |
| 126 | } |
| 127 | |
| 128 | /* -----------------------------------------------------------------------------*/ |
| 129 | /** |
| 130 | * |
| 131 | * |
| 132 | * This function sets a delay value a PCI register during training |
| 133 | * |
| 134 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 135 | * @param[in] TrnDly - type of delay to be set |
| 136 | * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed |
| 137 | * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding |
| 138 | * @param[in] Field - Value to be programmed |
| 139 | * |
| 140 | */ |
| 141 | |
| 142 | VOID |
| 143 | MemNSetTrainDlyNb ( |
| 144 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 145 | IN TRN_DLY_TYPE TrnDly, |
| 146 | IN DRBN DrbnVar, |
| 147 | IN UINT16 Field |
| 148 | ) |
| 149 | { |
| 150 | NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field); |
| 151 | } |
| 152 | |
| 153 | /* -----------------------------------------------------------------------------*/ |
| 154 | /** |
| 155 | * |
| 156 | * |
| 157 | * This function executes prototypical Phy fence training function. |
| 158 | * |
| 159 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 160 | * |
| 161 | */ |
| 162 | |
| 163 | VOID |
| 164 | MemNPhyFenceTrainingNb ( |
| 165 | IN OUT MEM_NB_BLOCK *NBPtr |
| 166 | ) |
| 167 | { |
| 168 | NBPtr->MemPPhyFenceTrainingNb (NBPtr); |
| 169 | } |
| 170 | |
| 171 | /* -----------------------------------------------------------------------------*/ |
| 172 | /** |
| 173 | * |
| 174 | * |
| 175 | * This function executes prototypical Phy fence training function. |
| 176 | * |
| 177 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 178 | * |
| 179 | */ |
| 180 | |
| 181 | VOID |
| 182 | MemNPhyFenceTrainingUnb ( |
| 183 | IN OUT MEM_NB_BLOCK *NBPtr |
| 184 | ) |
| 185 | { |
| 186 | UINT8 FenceThresholdTxDll; |
| 187 | UINT8 FenceThresholdRxDll; |
| 188 | UINT8 FenceThresholdTxPad; |
| 189 | UINT16 Fence2Data; |
| 190 | |
| 191 | MemNSetBitFieldNb (NBPtr, BFDataFence2, 0); |
| 192 | MemNSetBitFieldNb (NBPtr, BFFence2, 0); |
| 193 | // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b. |
| 194 | // 2. Perform phy fence training. |
| 195 | // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll]. |
| 196 | MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2); |
| 197 | MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence); |
| 198 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFenceThresholdTxDll\n"); |
| 199 | MemNTrainPhyFenceNb (NBPtr); |
| 200 | FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); |
| 201 | NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxDll); |
| 202 | |
| 203 | // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b. |
| 204 | MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000); |
| 205 | |
| 206 | // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b. |
| 207 | // 6. Perform phy fence training. |
| 208 | // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll]. |
| 209 | MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1); |
| 210 | MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence); |
| 211 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdRxDll\n"); |
| 212 | MemNTrainPhyFenceNb (NBPtr); |
| 213 | FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); |
| 214 | NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdRxDll); |
| 215 | |
| 216 | // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b. |
| 217 | MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000); |
| 218 | |
| 219 | // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b. |
| 220 | // 10. Perform phy fence training. |
| 221 | // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad]. |
| 222 | MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3); |
| 223 | MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence); |
| 224 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFenceThresholdTxPad\n"); |
| 225 | MemNTrainPhyFenceNb (NBPtr); |
| 226 | FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); |
| 227 | NBPtr->FamilySpecificHook[DetectMemPllError] (NBPtr, &FenceThresholdTxPad); |
| 228 | |
| 229 | // Program Fence2 threshold for Clk, Cmd, and Addr |
| 230 | if (FenceThresholdTxPad < 16) { |
| 231 | MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10); |
| 232 | MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10); |
| 233 | MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10); |
| 234 | } else { |
| 235 | MemNSetBitFieldNb (NBPtr, BFClkFence2, 0); |
| 236 | MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0); |
| 237 | MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0); |
| 238 | } |
| 239 | |
| 240 | // Program Fence2 threshold for data |
| 241 | Fence2Data = 0; |
| 242 | if (FenceThresholdTxPad < 16) { |
| 243 | Fence2Data |= FenceThresholdTxPad | 0x10; |
| 244 | } |
| 245 | if (FenceThresholdRxDll < 16) { |
| 246 | Fence2Data |= (FenceThresholdRxDll | 0x10) << 10; |
| 247 | } |
| 248 | if (FenceThresholdTxDll < 16) { |
| 249 | Fence2Data |= (FenceThresholdTxDll | 0x10) << 5; |
| 250 | } |
| 251 | MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data); |
| 252 | NBPtr->FamilySpecificHook[ProgramFence2RxDll] (NBPtr, &Fence2Data); |
| 253 | |
| 254 | if (NBPtr->MCTPtr->Status[SbLrdimms]) { |
| 255 | // 18. If motherboard routing requires CS[7:6] to adopt address timings, e.g. 3 LRDIMMs/ch with CS[7:6] |
| 256 | // routed across all DIMM sockets, BIOS performs the following: |
| 257 | if (FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_NO_LRDIMM_CS67_ROUTING, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, NULL, NULL) != NULL) { |
| 258 | // A. Program D18F2xA8_dct[1:0][CSTimingMux67] = 1. |
| 259 | MemNSetBitFieldNb (NBPtr, BFCSTimingMux67, 1); |
| 260 | // B. Program D18F2x9C_x0D0F_8021_dct[1:0]: |
| 261 | // - DiffTimingEn = 1. |
| 262 | // - IF (D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay] >= |
| 263 | // D18F2x9C_x0D0F_E008_dct[1:0][FenceValue]) THEN Fence = 1 ELSE Fence = 0. |
| 264 | // - Delay = D18F2x9C_x0000_0004_dct[1:0][AddrCmdFineDelay]. |
| 265 | // |
| 266 | MemNSetBitFieldNb (NBPtr, BFDiffTimingEn, 1); |
| 267 | MemNSetBitFieldNb (NBPtr, BFFence, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay) >= MemNGetBitFieldNb (NBPtr, BFFenceValue)) ? 1 : 0); |
| 268 | MemNSetBitFieldNb (NBPtr, BFDelay, (MemNGetBitFieldNb (NBPtr, BFAddrCmdFineDelay))); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | // 19. Reprogram F2x9C_04. |
| 273 | MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); |
| 274 | |
| 275 | } |
| 276 | |
| 277 | /* -----------------------------------------------------------------------------*/ |
| 278 | /** |
| 279 | * |
| 280 | * |
| 281 | * This function executes Phy fence training |
| 282 | * |
| 283 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 284 | * |
| 285 | */ |
| 286 | |
| 287 | VOID |
| 288 | MemNTrainPhyFenceNb ( |
| 289 | IN OUT MEM_NB_BLOCK *NBPtr |
| 290 | ) |
| 291 | { |
| 292 | UINT8 Byte; |
| 293 | INT16 Avg; |
| 294 | UINT8 PREvalue; |
| 295 | |
| 296 | if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) { |
| 297 | return; |
| 298 | } |
| 299 | |
| 300 | // 1. BIOS first programs a seed value to the phase recovery |
| 301 | // engine registers. |
| 302 | // |
| 303 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: "); |
| 304 | for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) { |
| 305 | // This includes ECC as byte 8 |
| 306 | MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19); |
| 307 | IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19); |
| 308 | } |
| 309 | |
| 310 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1"); |
| 311 | // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1. |
| 312 | MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1); |
| 313 | |
| 314 | if (!NBPtr->IsSupported[UnifiedNbFence]) { |
| 315 | // 3. Wait 200 MEMCLKs. |
| 316 | MemNWaitXMemClksNb (NBPtr, 200); |
| 317 | } else { |
| 318 | // 3. Wait 2000 MEMCLKs. |
| 319 | MemNWaitXMemClksNb (NBPtr, 2000); |
| 320 | } |
| 321 | |
| 322 | // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0. |
| 323 | MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0); |
| 324 | |
| 325 | // 5. BIOS reads the phase recovery engine registers |
| 326 | // F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52. |
| 327 | // 6. Calculate the average value of the fine delay and subtract 8. |
| 328 | // |
| 329 | Avg = 0; |
| 330 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t PRE: "); |
| 331 | for (Byte = 0; Byte < MAX_BYTELANES_PER_CHANNEL; Byte++) { |
| 332 | // |
| 333 | // This includes ECC as byte 8. ECC Byte lane (8) is ignored by MemNGetTrainDlyNb function where |
| 334 | // ECC is not supported. |
| 335 | // |
| 336 | PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte))); |
| 337 | Avg = Avg + ((INT16) PREvalue); |
| 338 | IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", PREvalue); |
| 339 | } |
| 340 | Avg = ((Avg + 8) / 9); // round up |
| 341 | |
| 342 | Avg -= 8; |
| 343 | NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg); |
| 344 | |
| 345 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFence: %02x\n", Avg); |
| 346 | |
| 347 | // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence]. |
| 348 | MemNSetBitFieldNb (NBPtr, BFPhyFence, Avg); |
| 349 | |
| 350 | // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control |
| 351 | // Register delays for both channels. This forces the phy to recompute |
| 352 | // the fence. |
| 353 | // |
| 354 | MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); |
| 355 | } |
| 356 | |
| 357 | /* -----------------------------------------------------------------------------*/ |
| 358 | /** |
| 359 | * |
| 360 | * |
| 361 | * This function initializes the DDR phy compensation logic |
| 362 | * |
| 363 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 364 | * |
| 365 | */ |
| 366 | |
| 367 | VOID |
| 368 | MemNInitPhyCompNb ( |
| 369 | IN OUT MEM_NB_BLOCK *NBPtr |
| 370 | ) |
| 371 | { |
| 372 | CONST UINT8 TableCompRiseSlew20x[] = {7, 3, 2, 2}; |
| 373 | CONST UINT8 TableCompRiseSlew15x[] = {7, 7, 3, 2}; |
| 374 | CONST UINT8 TableCompFallSlew20x[] = {7, 5, 3, 2}; |
| 375 | CONST UINT8 TableCompFallSlew15x[] = {7, 7, 5, 3}; |
| 376 | UINT8 i; |
| 377 | UINT8 j; |
| 378 | UINT8 CurrDct; |
| 379 | UINT8 CurrChannel; |
| 380 | BOOLEAN MarginImprv; |
| 381 | MarginImprv = FALSE; |
| 382 | CurrDct = NBPtr->Dct; |
| 383 | CurrChannel = NBPtr->Channel; |
| 384 | if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) { |
| 385 | if (NBPtr->MCTPtr->GangedMode == FALSE) { |
| 386 | for (i = 0; i < NBPtr->DctCount; i++) { |
| 387 | MemNSwitchDCTNb (NBPtr, i); |
| 388 | for (j = 0; j < NBPtr->ChannelCount; j++) { |
| 389 | NBPtr->SwitchChannel (NBPtr, j); |
| 390 | if ((NBPtr->ChannelPtr->Dimms == 4) && ((NBPtr->DCTPtr->Timings.Speed == DDR533_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY))) { |
| 391 | MarginImprv = TRUE; |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | MemNSwitchDCTNb (NBPtr, CurrDct); |
| 396 | NBPtr->SwitchChannel (NBPtr, CurrChannel); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | // 1. BIOS disables the phy compensation register by programming F2x9C_x08[DisAutoComp]=1 |
| 401 | // 2. BIOS waits 5 us for the disabling of the compensation engine to complete. |
| 402 | // DisAutoComp will be cleared after Dram init has completed |
| 403 | // |
| 404 | MemNSwitchDCTNb (NBPtr, 0); |
| 405 | MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1); |
| 406 | MemUWait10ns (500, NBPtr->MemPtr); |
| 407 | MemNSwitchDCTNb (NBPtr, CurrDct); |
| 408 | |
| 409 | // 3. For each normalized driver strength code read from |
| 410 | // F2x[1, 0]9C_x00[AddrCmdDrvStren], program the |
| 411 | // corresponding 3 bit predriver code in F2x9C_x0A[D3Cmp1NCal, D3Cmp1PCal]. |
| 412 | // |
| 413 | // 4. For each normalized driver strength code read from |
| 414 | // F2x[1, 0]9C_x00[DataDrvStren], program the corresponding |
| 415 | // 3 bit predriver code in F2x9C_x0A[D3Cmp0NCal, D3Cmp0PCal, D3Cmp2NCal, |
| 416 | // D3Cmp2PCal]. |
| 417 | // |
| 418 | j = (UINT8) MemNGetBitFieldNb (NBPtr, BFAddrCmdDrvStren); |
| 419 | i = (UINT8) MemNGetBitFieldNb (NBPtr, BFDataDrvStren); |
| 420 | |
| 421 | MemNSwitchDCTNb (NBPtr, 0); |
| 422 | MemNSetBitFieldNb (NBPtr, BFD3Cmp1NCal, TableCompRiseSlew20x[j]); |
| 423 | MemNSetBitFieldNb (NBPtr, BFD3Cmp1PCal, TableCompFallSlew20x[j]); |
| 424 | |
| 425 | if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) { |
| 426 | MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]); |
| 427 | MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]); |
| 428 | MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]); |
| 429 | MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]); |
| 430 | } |
| 431 | if (NBPtr->IsSupported[CheckSlewWithoutMarginImprv]) { |
| 432 | ASSERT (i <= 3); |
| 433 | MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, TableCompRiseSlew15x[i]); |
| 434 | MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, TableCompFallSlew15x[i]); |
| 435 | MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, TableCompRiseSlew15x[i]); |
| 436 | MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, TableCompFallSlew15x[i]); |
| 437 | } |
| 438 | MemNSwitchDCTNb (NBPtr, CurrDct); |
| 439 | } |
| 440 | |
| 441 | /* -----------------------------------------------------------------------------*/ |
| 442 | /** |
| 443 | * |
| 444 | * |
| 445 | * This is a general purpose function that executes before DRAM training |
| 446 | * |
| 447 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 448 | * |
| 449 | */ |
| 450 | |
| 451 | VOID |
| 452 | MemNBeforeDQSTrainingNb ( |
| 453 | IN OUT MEM_NB_BLOCK *NBPtr |
| 454 | ) |
| 455 | { |
| 456 | UINT8 Dct; |
| 457 | UINT8 ChipSel; |
| 458 | UINT32 TestAddrRJ16; |
| 459 | UINT32 RealAddr; |
| 460 | |
| 461 | MemTBeginTraining (NBPtr->TechPtr); |
| 462 | |
| 463 | for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| 464 | MemNSwitchDCTNb (NBPtr, Dct); |
| 465 | if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { |
| 466 | for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) { |
| 467 | if (MemNGetMCTSysAddrNb (NBPtr, ChipSel, &TestAddrRJ16)) { |
| 468 | |
| 469 | RealAddr = MemUSetUpperFSbase (TestAddrRJ16, NBPtr->MemPtr); |
| 470 | |
| 471 | MemUDummyCLRead (RealAddr); |
| 472 | |
| 473 | MemNSetBitFieldNb (NBPtr, BFErr350, 0x8000); |
| 474 | MemUWait10ns (60, NBPtr->MemPtr); // Wait 300ns |
| 475 | MemNSetBitFieldNb (NBPtr, BFErr350, 0x0000); |
| 476 | MemUWait10ns (400, NBPtr->MemPtr); // Wait 2us |
| 477 | MemUProcIOClFlush (TestAddrRJ16, 1, NBPtr->MemPtr); |
| 478 | break; |
| 479 | } |
| 480 | } |
| 481 | } |
| 482 | if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) { |
| 483 | if (!NBPtr->MCTPtr->Status[SbEccDimms]) { |
| 484 | MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010); |
| 485 | } |
| 486 | if (NBPtr->DCTPtr->Timings.Dimmx4Present == 0) { |
| 487 | MemNSetBitFieldNb (NBPtr, BFEccDLLConf, 0x0080); |
| 488 | } |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | MemTEndTraining (NBPtr->TechPtr); |
| 493 | } |
| 494 | |
| 495 | /*-----------------------------------------------------------------------------*/ |
| 496 | /** |
| 497 | * |
| 498 | * Returns the parameters for a requested delay value to be used in training |
| 499 | * The correct Min, Max and Mask are determined based on the type of Delay, |
| 500 | * and the frequency |
| 501 | * |
| 502 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 503 | * @param[in] TrnDly - Type of delay |
| 504 | * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct |
| 505 | * |
| 506 | */ |
| 507 | |
| 508 | VOID |
| 509 | MemNGetTrainDlyParmsNb ( |
| 510 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 511 | IN TRN_DLY_TYPE TrnDly, |
| 512 | IN OUT TRN_DLY_PARMS *Parms |
| 513 | ) |
| 514 | { |
| 515 | Parms->Min = 0; |
| 516 | |
| 517 | if (TrnDly == AccessWrDatDly) { |
| 518 | Parms->Max = 0x1F; |
| 519 | Parms->Mask = 0x01F; |
| 520 | } else if (TrnDly == AccessRdDqsDly) { |
| 521 | if ( (NBPtr->IsSupported[CheckMaxRdDqsDlyPtr]) && (NBPtr->DCTPtr->Timings.Speed > DDR667_FREQUENCY) ) { |
| 522 | Parms->Max = 0x3E; |
| 523 | Parms->Mask = 0x03E; |
| 524 | } else { |
| 525 | Parms->Max = 0x1F; |
| 526 | Parms->Mask = 0x01F; |
| 527 | } |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | /*-----------------------------------------------------------------------------*/ |
| 532 | /** |
| 533 | * |
| 534 | * Returns the parameters for a requested delay value to be used in training |
| 535 | * The correct Min, Max and Mask are determined based on the type of Delay, |
| 536 | * and the frequency |
| 537 | * |
| 538 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 539 | * @param[in] TrnDly - Type of delay |
| 540 | * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct |
| 541 | * |
| 542 | */ |
| 543 | |
| 544 | VOID |
| 545 | MemNGetTrainDlyParmsClientNb ( |
| 546 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 547 | IN TRN_DLY_TYPE TrnDly, |
| 548 | IN OUT TRN_DLY_PARMS *Parms |
| 549 | ) |
| 550 | { |
| 551 | Parms->Min = 0; |
| 552 | |
| 553 | if (TrnDly == AccessWrDatDly) { |
| 554 | Parms->Max = 0x1F; |
| 555 | Parms->Mask = 0x01F; |
| 556 | } else if (TrnDly == AccessRdDqsDly) { |
| 557 | Parms->Max = 0x3E; |
| 558 | Parms->Mask = 0x03E; |
| 559 | } |
| 560 | } |
| 561 | /*-----------------------------------------------------------------------------*/ |
| 562 | /** |
| 563 | * |
| 564 | * Returns the parameters for a requested delay value to be used in training |
| 565 | * The correct Min, Max and Mask are determined based on the type of Delay, |
| 566 | * and the frequency |
| 567 | * |
| 568 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 569 | * @param[in] TrnDly - Type of delay |
| 570 | * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct |
| 571 | * |
| 572 | */ |
| 573 | |
| 574 | VOID |
| 575 | MemNGetTrainDlyParmsUnb ( |
| 576 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 577 | IN TRN_DLY_TYPE TrnDly, |
| 578 | IN OUT TRN_DLY_PARMS *Parms |
| 579 | ) |
| 580 | { |
| 581 | Parms->Min = 0; |
| 582 | |
| 583 | if ((TrnDly == AccessWrDatDly) || (TrnDly == AccessRdDqsDly)) { |
| 584 | Parms->Max = 0x1F; |
| 585 | Parms->Mask = 0x01F; |
| 586 | } |
| 587 | } |
| 588 | /*---------------------------------------------------------------------------- |
| 589 | * LOCAL FUNCTIONS |
| 590 | * |
| 591 | *---------------------------------------------------------------------------- |
| 592 | */ |
| 593 | /* -----------------------------------------------------------------------------*/ |
| 594 | /** |
| 595 | * |
| 596 | * |
| 597 | * This function gets or set DQS timing during training. |
| 598 | * |
| 599 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 600 | * @param[in] TrnDly - type of delay to be set |
| 601 | * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed |
| 602 | * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding |
| 603 | * @param[in] Field - Value to be programmed |
| 604 | * @param[in] IsSet - Indicates if the function will set or get |
| 605 | * |
| 606 | * @return value read, if the function is used as a "get" |
| 607 | */ |
| 608 | |
| 609 | UINT32 |
| 610 | MemNcmnGetSetTrainDlyNb ( |
| 611 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 612 | IN UINT8 IsSet, |
| 613 | IN TRN_DLY_TYPE TrnDly, |
| 614 | IN DRBN DrbnVar, |
| 615 | IN UINT16 Field |
| 616 | ) |
| 617 | { |
| 618 | UINT16 Index; |
| 619 | UINT16 Offset; |
| 620 | UINT32 Value; |
| 621 | UINT32 Address; |
| 622 | UINT8 Dimm; |
| 623 | UINT8 Rank; |
| 624 | UINT8 Byte; |
| 625 | UINT8 Nibble; |
| 626 | |
| 627 | Dimm = DRBN_DIMM (DrbnVar); |
| 628 | Rank = DRBN_RANK (DrbnVar); |
| 629 | Byte = DRBN_BYTE (DrbnVar); |
| 630 | Nibble = DRBN_NBBL (DrbnVar); |
| 631 | |
| 632 | ASSERT (Dimm < 4); |
| 633 | ASSERT (Byte <= ECC_DLY); |
| 634 | |
| 635 | switch (TrnDly) { |
| 636 | case AccessRcvEnDly: |
| 637 | Index = 0x10; |
| 638 | break; |
| 639 | case AccessWrDqsDly: |
| 640 | Index = 0x30; |
| 641 | break; |
| 642 | case AccessWrDatDly: |
| 643 | Index = 0x01; |
| 644 | break; |
| 645 | case AccessRdDqsDly: |
| 646 | Index = 0x05; |
| 647 | break; |
| 648 | case AccessPhRecDly: |
| 649 | Index = 0x50; |
| 650 | break; |
| 651 | default: |
| 652 | Index = 0; |
| 653 | IDS_ERROR_TRAP; |
| 654 | } |
| 655 | |
| 656 | switch (TrnDly) { |
| 657 | case AccessRcvEnDly: |
| 658 | case AccessWrDqsDly: |
| 659 | Index += (Dimm * 3); |
| 660 | if (Byte & 0x04) { |
| 661 | // if byte 4,5,6,7 |
| 662 | Index += 0x10; |
| 663 | } |
| 664 | if (Byte & 0x02) { |
| 665 | // if byte 2,3,6,7 |
| 666 | Index++; |
| 667 | } |
| 668 | if (Byte > 7) { |
| 669 | Index += 2; |
| 670 | } |
| 671 | Offset = 16 * (Byte % 2); |
| 672 | Index |= (Rank << 8); |
| 673 | Index |= (Nibble << 9); |
| 674 | break; |
| 675 | |
| 676 | case AccessRdDqsDly: |
| 677 | case AccessWrDatDly: |
| 678 | |
| 679 | if (NBPtr->IsSupported[DimmBasedOnSpeed]) { |
| 680 | if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) { |
| 681 | // if DDR speed is below 800, use DIMM 0 delays for all DIMMs. |
| 682 | Dimm = 0; |
| 683 | } |
| 684 | } |
| 685 | |
| 686 | Index += (Dimm * 0x100); |
| 687 | if (Nibble) { |
| 688 | if (Rank) { |
| 689 | Index += 0xA0; |
| 690 | } else { |
| 691 | Index += 0x70; |
| 692 | } |
| 693 | } else if (Rank) { |
| 694 | Index += 0x60; |
| 695 | } |
Jacob Garber | 4c33a3a | 2019-07-12 10:34:06 -0600 | [diff] [blame] | 696 | // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence |
zbao | 7d94cf9 | 2012-07-02 14:19:14 +0800 | [diff] [blame] | 697 | case AccessPhRecDly: |
| 698 | Index += (Byte / 4); |
| 699 | Offset = 8 * (Byte % 4); |
| 700 | break; |
| 701 | default: |
| 702 | Offset = 0; |
| 703 | IDS_ERROR_TRAP; |
| 704 | } |
| 705 | |
| 706 | Address = Index; |
| 707 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 708 | MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); |
| 709 | Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); |
| 710 | |
| 711 | if (TrnDly == AccessRdDqsDly) { |
| 712 | NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset); |
| 713 | } |
| 714 | |
| 715 | if (IsSet) { |
| 716 | if (TrnDly == AccessPhRecDly) { |
| 717 | Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; |
| 718 | } |
| 719 | |
| 720 | Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset))); |
| 721 | MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); |
| 722 | Address |= DCT_ACCESS_WRITE; |
| 723 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 724 | MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); |
| 725 | |
| 726 | if (TrnDly == AccessPhRecDly) { |
| 727 | NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; |
| 728 | } |
| 729 | } else { |
| 730 | Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF); |
| 731 | } |
| 732 | |
| 733 | return Value; |
| 734 | } |
| 735 | |
| 736 | /* -----------------------------------------------------------------------------*/ |
| 737 | /** |
| 738 | * |
| 739 | * This function gets or set DQS timing during training. |
| 740 | * |
| 741 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 742 | * @param[in] IsSet - Indicates if the function will set or get |
| 743 | * @param[in] TrnDly - type of delay to be set |
| 744 | * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed |
| 745 | * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding |
| 746 | * @param[in] Field - Value to be programmed |
| 747 | * |
| 748 | * @return value read, if the function is used as a "get" |
| 749 | */ |
| 750 | UINT32 |
| 751 | MemNcmnGetSetTrainDlyClientNb ( |
| 752 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 753 | IN UINT8 IsSet, |
| 754 | IN TRN_DLY_TYPE TrnDly, |
| 755 | IN DRBN DrbnVar, |
| 756 | IN UINT16 Field |
| 757 | ) |
| 758 | { |
| 759 | UINT16 Index; |
| 760 | UINT16 Offset; |
| 761 | UINT32 Value; |
| 762 | UINT32 Address; |
| 763 | UINT8 Dimm; |
| 764 | UINT8 Byte; |
| 765 | |
| 766 | Dimm = DRBN_DIMM (DrbnVar); |
| 767 | Byte = DRBN_BYTE (DrbnVar); |
| 768 | |
| 769 | ASSERT (Dimm < 2); |
| 770 | ASSERT (Byte <= ECC_DLY); |
| 771 | |
| 772 | if ((Byte > 7)) { |
| 773 | // Llano does not support ECC delay, so: |
| 774 | if (IsSet) { |
| 775 | // On write, ignore |
| 776 | return 0; |
| 777 | } else { |
| 778 | // On read, redirect to byte 0 to correct fence averaging |
| 779 | Byte = 0; |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | switch (TrnDly) { |
| 784 | case AccessRcvEnDly: |
| 785 | Index = 0x10; |
| 786 | break; |
| 787 | case AccessWrDqsDly: |
| 788 | Index = 0x30; |
| 789 | break; |
| 790 | case AccessWrDatDly: |
| 791 | Index = 0x01; |
| 792 | break; |
| 793 | case AccessRdDqsDly: |
| 794 | Index = 0x05; |
| 795 | break; |
| 796 | case AccessPhRecDly: |
| 797 | Index = 0x50; |
| 798 | break; |
| 799 | default: |
| 800 | Index = 0; |
| 801 | IDS_ERROR_TRAP; |
| 802 | } |
| 803 | |
| 804 | switch (TrnDly) { |
| 805 | case AccessRcvEnDly: |
| 806 | case AccessWrDqsDly: |
| 807 | Index += (Dimm * 3); |
| 808 | if (Byte & 0x04) { |
| 809 | // if byte 4,5,6,7 |
| 810 | Index += 0x10; |
| 811 | } |
| 812 | if (Byte & 0x02) { |
| 813 | // if byte 2,3,6,7 |
| 814 | Index++; |
| 815 | } |
| 816 | Offset = 16 * (Byte % 2); |
| 817 | break; |
| 818 | |
| 819 | case AccessRdDqsDly: |
| 820 | case AccessWrDatDly: |
| 821 | Index += (Dimm * 0x100); |
Jacob Garber | 4c33a3a | 2019-07-12 10:34:06 -0600 | [diff] [blame] | 822 | // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence |
zbao | 7d94cf9 | 2012-07-02 14:19:14 +0800 | [diff] [blame] | 823 | case AccessPhRecDly: |
| 824 | Index += (Byte / 4); |
| 825 | Offset = 8 * (Byte % 4); |
| 826 | break; |
| 827 | default: |
| 828 | Offset = 0; |
| 829 | IDS_ERROR_TRAP; |
| 830 | } |
| 831 | |
| 832 | Address = Index; |
| 833 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 834 | Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); |
| 835 | |
| 836 | if (IsSet) { |
| 837 | if (TrnDly == AccessPhRecDly) { |
| 838 | Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; |
| 839 | } |
| 840 | |
| 841 | Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset))); |
| 842 | MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); |
| 843 | Address |= DCT_ACCESS_WRITE; |
| 844 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 845 | |
| 846 | if (TrnDly == AccessPhRecDly) { |
| 847 | NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; |
| 848 | } |
| 849 | // Gross WrDatDly and WrDqsDly cannot be larger than 4 |
| 850 | ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE); |
| 851 | } else { |
| 852 | Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF); |
| 853 | } |
| 854 | |
| 855 | return Value; |
| 856 | } |
| 857 | /* -----------------------------------------------------------------------------*/ |
| 858 | /** |
| 859 | * |
| 860 | * |
| 861 | * This function gets or set DQS timing during training. |
| 862 | * |
| 863 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 864 | * @param[in] TrnDly - type of delay to be set |
| 865 | * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed |
| 866 | * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding |
| 867 | * @param[in] Field - Value to be programmed |
| 868 | * @param[in] IsSet - Indicates if the function will set or get |
| 869 | * |
| 870 | * @return value read, if the function is used as a "get" |
| 871 | */ |
| 872 | |
| 873 | UINT32 |
| 874 | MemNcmnGetSetTrainDlyUnb ( |
| 875 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 876 | IN UINT8 IsSet, |
| 877 | IN TRN_DLY_TYPE TrnDly, |
| 878 | IN DRBN DrbnVar, |
| 879 | IN UINT16 Field |
| 880 | ) |
| 881 | { |
| 882 | UINT16 Index; |
| 883 | UINT16 Offset; |
| 884 | UINT32 Value; |
| 885 | UINT32 Address; |
| 886 | UINT8 Dimm; |
| 887 | UINT8 Rank; |
| 888 | UINT8 Byte; |
| 889 | UINT8 Nibble; |
| 890 | UINT8 DimmNibble; |
| 891 | |
| 892 | Dimm = DRBN_DIMM (DrbnVar); |
| 893 | Rank = DRBN_RANK (DrbnVar); |
| 894 | Byte = DRBN_BYTE (DrbnVar); |
| 895 | Nibble = DRBN_NBBL (DrbnVar); |
| 896 | DimmNibble = DRBN_DIMM_NBBL (DrbnVar); |
| 897 | |
| 898 | ASSERT (Dimm < (NBPtr->CsPerChannel / NBPtr->CsPerDelay)); |
| 899 | ASSERT (Byte <= ECC_DLY); |
| 900 | if ((Byte == ECC_DLY) && (!NBPtr->MCTPtr->Status[SbEccDimms] || !NBPtr->IsSupported[EccByteTraining])) { |
| 901 | // When ECC is not enabled |
| 902 | if (IsSet) { |
| 903 | // On write, ignore |
| 904 | return 0; |
| 905 | } else { |
| 906 | // On read, redirect to byte 0 to correct fence averaging |
| 907 | Byte = 0; |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | switch (TrnDly) { |
| 912 | case AccessRcvEnDly: |
| 913 | Index = 0x10; |
| 914 | break; |
| 915 | case AccessWrDqsDly: |
| 916 | Index = 0x30; |
| 917 | break; |
| 918 | case AccessWrDatDly: |
| 919 | Index = 0x01; |
| 920 | break; |
| 921 | case AccessRdDqsDly: |
| 922 | Index = 0x05; |
| 923 | break; |
| 924 | case excel845 : |
| 925 | Index = 0x00; |
| 926 | break; |
| 927 | case AccessPhRecDly: |
| 928 | Index = 0x50; |
| 929 | break; |
| 930 | default: |
| 931 | Index = 0; |
| 932 | IDS_ERROR_TRAP; |
| 933 | } |
| 934 | |
| 935 | switch (TrnDly) { |
| 936 | case AccessRcvEnDly: |
| 937 | case AccessWrDqsDly: |
| 938 | Index += (Dimm * 3); |
| 939 | if (Byte & 0x04) { |
| 940 | // if byte 4,5,6,7 |
| 941 | Index += 0x10; |
| 942 | } |
| 943 | if (Byte & 0x02) { |
| 944 | // if byte 2,3,6,7 |
| 945 | Index++; |
| 946 | } |
| 947 | if (Byte > 7) { |
| 948 | Index += 2; |
| 949 | } |
| 950 | Offset = 16 * (Byte % 2); |
| 951 | Index |= (Rank << 8); |
| 952 | Index |= (Nibble << 9); |
| 953 | Address = Index; |
| 954 | break; |
| 955 | |
| 956 | case AccessRdDqsDly: |
| 957 | case AccessWrDatDly: |
| 958 | |
| 959 | if (NBPtr->IsSupported[DimmBasedOnSpeed]) { |
| 960 | if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) { |
| 961 | // if DDR speed is below 800, use DIMM 0 delays for all DIMMs. |
| 962 | Dimm = 0; |
| 963 | } |
| 964 | } |
| 965 | |
| 966 | Index += (Dimm * 0x100); |
| 967 | if (Nibble) { |
| 968 | if (Rank) { |
| 969 | Index += 0xA0; |
| 970 | } else { |
| 971 | Index += 0x70; |
| 972 | } |
| 973 | } else if (Rank) { |
| 974 | Index += 0x60; |
| 975 | } |
Jacob Garber | 4c33a3a | 2019-07-12 10:34:06 -0600 | [diff] [blame] | 976 | // fall through - AccessRdDqsDly and AccessWrDatDly also need to run AccessPhRecDly sequence |
zbao | 7d94cf9 | 2012-07-02 14:19:14 +0800 | [diff] [blame] | 977 | case AccessPhRecDly: |
| 978 | Index += (Byte / 4); |
| 979 | Offset = 8 * (Byte % 4); |
| 980 | Address = Index; |
| 981 | break; |
| 982 | case excel845 : |
| 983 | Address = 0x0D0F0000; |
| 984 | Index += (DimmNibble >> 1) * 0x100; |
| 985 | Index += 0x20; |
| 986 | Index = Index + Dimm; |
| 987 | Offset = 4 * ((DimmNibble & 0x01) * 2); |
| 988 | Address += Index; |
| 989 | break; |
| 990 | default: |
| 991 | Offset = 0; |
| 992 | IDS_ERROR_TRAP; |
| 993 | Address = Index; |
| 994 | } |
| 995 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 996 | MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); |
| 997 | Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); |
| 998 | if (TrnDly == AccessRdDqsDly) { |
| 999 | NBPtr->FamilySpecificHook[AdjustRdDqsDlyOffset] (NBPtr, &Offset); |
| 1000 | } |
| 1001 | |
| 1002 | if (IsSet) { |
| 1003 | if (TrnDly == AccessPhRecDly) { |
| 1004 | Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; |
| 1005 | } |
| 1006 | if (TrnDly != excel845 ) { |
| 1007 | Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF) << Offset))); |
| 1008 | } else { |
| 1009 | Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) 0x1F << Offset))); |
| 1010 | } |
| 1011 | MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); |
| 1012 | Address |= DCT_ACCESS_WRITE; |
| 1013 | MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); |
| 1014 | MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); |
| 1015 | if (TrnDly == AccessPhRecDly) { |
| 1016 | NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; |
| 1017 | } |
| 1018 | } else { |
| 1019 | if (TrnDly != excel845 ) { |
| 1020 | Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x3FF : 0xFF); |
| 1021 | } else { |
| 1022 | Value = (Value >> Offset) & (UINT32) (0x1F); |
| 1023 | } |
| 1024 | } |
| 1025 | return Value; |
| 1026 | } |
| 1027 | /* -----------------------------------------------------------------------------*/ |
| 1028 | /** |
| 1029 | * |
| 1030 | * This function initializes the training pattern. |
| 1031 | * |
| 1032 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1033 | * |
| 1034 | * @return AGESA_STATUS - Result |
| 1035 | * AGESA_SUCCESS - Training pattern is ready to use |
| 1036 | * AGESA_ERROR - Unable to initialize the pattern. |
| 1037 | */ |
| 1038 | |
| 1039 | AGESA_STATUS |
| 1040 | MemNTrainingPatternInitNb ( |
| 1041 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1042 | ) |
| 1043 | { |
| 1044 | MEM_TECH_BLOCK *TechPtr; |
| 1045 | ALLOCATE_HEAP_PARAMS AllocHeapParams; |
| 1046 | TRAIN_PATTERN TrainPattern; |
| 1047 | AGESA_STATUS Status; |
| 1048 | |
| 1049 | TechPtr = NBPtr->TechPtr; |
| 1050 | TrainPattern = 0; |
| 1051 | // |
| 1052 | // Check the training type |
| 1053 | // |
| 1054 | if (TechPtr->TrainingType == TRN_DQS_POSITION) { |
| 1055 | // |
| 1056 | // DQS Position Training |
| 1057 | // |
| 1058 | if (NBPtr->PosTrnPattern == POS_PATTERN_256B) { |
| 1059 | // |
| 1060 | // 256 Bit pattern |
| 1061 | // |
| 1062 | if (NBPtr->MCTPtr->Status[Sb128bitmode]) { |
| 1063 | TrainPattern = TestPatternJD256B; |
| 1064 | TechPtr->PatternLength = 64; |
| 1065 | } else { |
| 1066 | TrainPattern = TestPatternJD256A; |
| 1067 | TechPtr->PatternLength = 32; |
| 1068 | } |
| 1069 | } else { |
| 1070 | // |
| 1071 | // 72 bit pattern will be used if PosTrnPattern is not specified |
| 1072 | // |
| 1073 | if (NBPtr->MCTPtr->Status[Sb128bitmode]) { |
| 1074 | TrainPattern = TestPatternJD1B; |
| 1075 | TechPtr->PatternLength = 18; |
| 1076 | } else { |
| 1077 | TrainPattern = TestPatternJD1A; |
| 1078 | TechPtr->PatternLength = 9; |
| 1079 | } |
| 1080 | } |
| 1081 | } else if (TechPtr->TrainingType == TRN_MAX_READ_LATENCY) { |
| 1082 | // |
| 1083 | // Max Read Latency Training |
| 1084 | // |
| 1085 | TrainPattern = TestPatternML; |
| 1086 | TechPtr->PatternLength = (NBPtr->MCTPtr->Status[Sb128bitmode]) ? 6 : 3; |
| 1087 | } else { |
| 1088 | // |
| 1089 | // Error - TechPtr->Training Type must be set to one of the types handled in this function |
| 1090 | // |
| 1091 | ASSERT (FALSE); |
| 1092 | } |
| 1093 | // |
| 1094 | // Allocate training buffer |
| 1095 | // |
| 1096 | AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16; |
| 1097 | AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE; |
| 1098 | AllocHeapParams.Persist = HEAP_LOCAL_CACHE; |
| 1099 | Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader); |
| 1100 | ASSERT (Status == AGESA_SUCCESS); |
| 1101 | if (Status != AGESA_SUCCESS) { |
| 1102 | return Status; |
| 1103 | } |
| 1104 | TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr; |
| 1105 | AlignPointerTo16Byte (&TechPtr->PatternBufPtr); |
| 1106 | TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64); |
| 1107 | |
| 1108 | // Prepare training pattern |
| 1109 | MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64); |
| 1110 | |
| 1111 | return Status; |
| 1112 | } |
| 1113 | |
| 1114 | /* -----------------------------------------------------------------------------*/ |
| 1115 | /** |
| 1116 | * |
| 1117 | * This function determined the settings for the Reliable Read/Write engine |
| 1118 | * for each specific type of training |
| 1119 | * |
| 1120 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1121 | * @param[in] *OptParam - Pointer to an Enum of TRAINING_TYPE |
| 1122 | * |
| 1123 | * @return TRUE |
| 1124 | */ |
| 1125 | |
| 1126 | BOOLEAN |
| 1127 | MemNSetupHwTrainingEngineUnb ( |
| 1128 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1129 | IN VOID *OptParam |
| 1130 | ) |
| 1131 | { |
| 1132 | TRAINING_TYPE TrnType; |
| 1133 | RRW_SETTINGS *Rrw; |
| 1134 | |
| 1135 | TrnType = *(TRAINING_TYPE*) OptParam; |
| 1136 | Rrw = &NBPtr->RrwSettings; |
| 1137 | // |
| 1138 | // Common Settings |
| 1139 | // |
| 1140 | Rrw->TgtBankAddressA = CPG_BANK_ADDRESS_A; |
| 1141 | Rrw->TgtRowAddressA = CPG_ROW_ADDRESS_A; |
| 1142 | Rrw->TgtColAddressA = CPG_COL_ADDRESS_A; |
| 1143 | Rrw->TgtBankAddressB = CPG_BANK_ADDRESS_B; |
| 1144 | Rrw->TgtRowAddressB = CPG_ROW_ADDRESS_B; |
| 1145 | Rrw->TgtColAddressB = CPG_COL_ADDRESS_B; |
| 1146 | Rrw->CompareMaskHigh = CPG_COMPARE_MASK_HI; |
| 1147 | Rrw->CompareMaskLow = CPG_COMPARE_MASK_LOW; |
| 1148 | Rrw->CompareMaskEcc = CPG_COMPARE_MASK_ECC; |
| 1149 | |
| 1150 | switch (TrnType) { |
| 1151 | case TRN_RCVR_ENABLE: |
| 1152 | // |
| 1153 | // Receiver Enable Training |
| 1154 | // |
| 1155 | NBPtr->TechPtr->PatternLength = 192; |
| 1156 | break; |
| 1157 | case TRN_MAX_READ_LATENCY: |
| 1158 | // |
| 1159 | // Max Read Latency Training |
| 1160 | // |
| 1161 | Rrw->CmdTgt = CMD_TGT_A; |
| 1162 | NBPtr->TechPtr->PatternLength = 32; |
| 1163 | Rrw->DataPrbsSeed = PRBS_SEED_32; |
| 1164 | break; |
| 1165 | case TRN_DQS_POSITION: |
| 1166 | // |
| 1167 | // Read/Write DQS Position training |
| 1168 | // |
| 1169 | Rrw->CmdTgt = CMD_TGT_AB; |
| 1170 | NBPtr->TechPtr->PatternLength = 256; |
| 1171 | Rrw->DataPrbsSeed = PRBS_SEED_256; |
| 1172 | break; |
| 1173 | default: |
| 1174 | ASSERT (FALSE); |
| 1175 | } |
| 1176 | return TRUE; |
| 1177 | } |
| 1178 | |
| 1179 | /* -----------------------------------------------------------------------------*/ |
| 1180 | /** |
| 1181 | * |
| 1182 | * This function finalizes the training pattern. |
| 1183 | * |
| 1184 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1185 | * @param[in] Index - Index of Write Data Delay Value |
| 1186 | * @param[in,out] *Value - Write Data Delay Value |
| 1187 | * @return BOOLEAN - TRUE - Use the value returned. |
| 1188 | * FALSE - No more values in table. |
| 1189 | */ |
| 1190 | |
| 1191 | BOOLEAN |
| 1192 | MemNGetApproximateWriteDatDelayNb ( |
| 1193 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1194 | IN UINT8 Index, |
| 1195 | IN OUT UINT8 *Value |
| 1196 | ) |
| 1197 | { |
| 1198 | CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F}; |
| 1199 | if (Index < GET_SIZE_OF (WriteDatDelayValue)) { |
| 1200 | *Value = WriteDatDelayValue[Index]; |
| 1201 | return TRUE; |
| 1202 | } |
| 1203 | return FALSE; |
| 1204 | } |
| 1205 | |
| 1206 | |
| 1207 | /* -----------------------------------------------------------------------------*/ |
| 1208 | /** |
| 1209 | * |
| 1210 | * This function finalizes the training pattern. |
| 1211 | * |
| 1212 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1213 | * |
| 1214 | * @return AGESA_STATUS - Result |
| 1215 | * AGESA_SUCCESS - Training pattern has been finalized. |
| 1216 | * AGESA_ERROR - Unable to initialize the pattern. |
| 1217 | */ |
| 1218 | |
| 1219 | AGESA_STATUS |
| 1220 | MemNTrainingPatternFinalizeNb ( |
| 1221 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1222 | ) |
| 1223 | { |
| 1224 | AGESA_STATUS Status; |
| 1225 | // |
| 1226 | // Deallocate training buffer |
| 1227 | // |
| 1228 | Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader); |
| 1229 | ASSERT (Status == AGESA_SUCCESS); |
| 1230 | return Status; |
| 1231 | } |
| 1232 | |
| 1233 | /* -----------------------------------------------------------------------------*/ |
| 1234 | /** |
| 1235 | * |
| 1236 | * This function returns the number of Chipselects controlled by each set |
| 1237 | * of Delay registers under current conditions. |
| 1238 | * |
| 1239 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1240 | * |
| 1241 | * @return |
| 1242 | */ |
| 1243 | |
| 1244 | UINT8 |
| 1245 | MemNCSPerDelayNb ( |
| 1246 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1247 | ) |
| 1248 | { |
| 1249 | if (MemNGetBitFieldNb (NBPtr, BFPerRankTimingEn) == 1) { |
| 1250 | return 1; |
| 1251 | } else { |
| 1252 | return 2; |
| 1253 | } |
| 1254 | } |
| 1255 | |
| 1256 | /* -----------------------------------------------------------------------------*/ |
| 1257 | /** |
| 1258 | * |
| 1259 | * This function returns the minimum data eye width in 32nds of a UI for |
| 1260 | * the type of data eye(Rd/Wr) that is being trained. This value will |
| 1261 | * be the minimum number of consecutive delays that yield valid data. |
| 1262 | * Uses TechPtr->Direction to determine read or write. |
| 1263 | * |
| 1264 | * |
| 1265 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1266 | * |
| 1267 | * @return |
| 1268 | */ |
| 1269 | |
| 1270 | UINT8 |
| 1271 | MemNMinDataEyeWidthNb ( |
| 1272 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1273 | ) |
| 1274 | { |
| 1275 | UINT8 MinRdDataeye; |
| 1276 | UINT8 MinWrDataeye; |
| 1277 | UINT8 *MinRdWrDataeyePtr; |
| 1278 | |
| 1279 | MinRdWrDataeyePtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MIN_RD_WR_DATAEYE_WIDTH, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr->ChannelID, 0, |
| 1280 | &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader)); |
| 1281 | |
| 1282 | if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { |
| 1283 | if (MinRdWrDataeyePtr != NULL) { |
| 1284 | MinRdDataeye = MinRdWrDataeyePtr[0]; |
| 1285 | return MinRdDataeye; |
| 1286 | } else { |
| 1287 | return MIN_RD_DATAEYE_WIDTH_NB; |
| 1288 | } |
| 1289 | } else { |
| 1290 | if (MinRdWrDataeyePtr != NULL) { |
| 1291 | MinWrDataeye = MinRdWrDataeyePtr[1]; |
| 1292 | return MinWrDataeye; |
| 1293 | } else { |
| 1294 | return MIN_WR_DATAEYE_WIDTH_NB; |
| 1295 | } |
| 1296 | } |
| 1297 | } |
| 1298 | |
| 1299 | /* -----------------------------------------------------------------------------*/ |
| 1300 | /** |
| 1301 | * |
| 1302 | * This function programs the phy registers according to the desired phy VDDIO voltage level |
| 1303 | * |
| 1304 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1305 | * |
| 1306 | */ |
| 1307 | |
| 1308 | VOID |
| 1309 | MemNPhyVoltageLevelNb ( |
| 1310 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1311 | ) |
| 1312 | { |
| 1313 | BIT_FIELD_NAME BitField; |
| 1314 | BIT_FIELD_NAME BFEnd; |
| 1315 | UINT16 BFValue; |
| 1316 | UINT16 RegValue; |
| 1317 | |
| 1318 | BFValue = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3; |
| 1319 | BFEnd = NBPtr->IsSupported[ProgramCsrComparator] ? BFCsrComparator : BFCmpVioLvl; |
| 1320 | |
| 1321 | for (BitField = BFDataRxVioLvl; BitField <= BFEnd; BitField++) { |
| 1322 | RegValue = BFValue; |
| 1323 | if (BitField == BFCsrComparator) { |
| 1324 | RegValue >>= (3 - 2); |
| 1325 | // Setting this bit in DCT0 adjusts the comparator for DCT0 and DCT1. Setting this bit in DCT1 has no effect. |
| 1326 | NBPtr->SwitchDCT (NBPtr, 0); |
| 1327 | MemNSetBitFieldNb (NBPtr, BitField, RegValue); |
| 1328 | break; |
| 1329 | } else if (BitField == BFCmpVioLvl) { |
| 1330 | RegValue <<= (14 - 3); |
| 1331 | // Must set this bit on DCT0 even when DCT0 has no memory |
| 1332 | NBPtr->SwitchDCT (NBPtr, 0); |
| 1333 | MemNSetBitFieldNb (NBPtr, BitField, RegValue); |
| 1334 | } |
| 1335 | MemNBrdcstSetNb (NBPtr, BitField, RegValue); |
| 1336 | } |
| 1337 | } |
| 1338 | |
| 1339 | /* -----------------------------------------------------------------------------*/ |
| 1340 | /** |
| 1341 | * |
| 1342 | * |
| 1343 | * This function adjusts Avg PRE value of Phy fence training according to specific CPU family. |
| 1344 | * |
| 1345 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1346 | * @param[in,out] *Value16 - Pointer to the value that we want to adjust |
| 1347 | * |
| 1348 | */ |
| 1349 | VOID |
| 1350 | MemNPFenceAdjustUnb ( |
| 1351 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1352 | IN OUT INT16 *Value16 |
| 1353 | ) |
| 1354 | { |
| 1355 | *Value16 += 2; //The Avg PRE value is subtracted by 6 only. |
| 1356 | } |
| 1357 | |
| 1358 | /* -----------------------------------------------------------------------------*/ |
| 1359 | /** |
| 1360 | * |
| 1361 | * |
| 1362 | * This function initializes the DDR phy compensation logic |
| 1363 | * |
| 1364 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1365 | * |
| 1366 | */ |
| 1367 | |
| 1368 | VOID |
| 1369 | MemNInitPhyCompClientNb ( |
| 1370 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1371 | ) |
| 1372 | { |
| 1373 | // Slew rate table array [x][y][z] |
| 1374 | // array[0]: slew rate for VDDIO 1.5V |
| 1375 | // array[1]: slew rate for VDDIO 1.35V |
| 1376 | // array[2]: slew rate for VDDIO 1.25V |
| 1377 | // array[x][y]: slew rate for a certain frequency |
| 1378 | // array[x][y][0]: frequency mask for current entry |
| 1379 | CONST STATIC UINT16 TxPrePNDataDqs[3][3][5] = { |
| 1380 | {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924}, |
| 1381 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, |
| 1382 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| 1383 | {{ (UINT16) DDR800, 0xFF6, 0xB6D, 0xB6D, 0x924}, |
| 1384 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, |
| 1385 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| 1386 | {{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924}, |
| 1387 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, |
| 1388 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| 1389 | }; |
| 1390 | CONST STATIC UINT16 TxPrePNCmdAddr[3][3][5] = { |
| 1391 | {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492}, |
| 1392 | { (UINT16) (DDR1066 + DDR1333), 0x6DB, 0x6DB, 0x6DB, 0x6DB}, |
| 1393 | { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0xB6D, 0xB6D}}, |
| 1394 | {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492}, |
| 1395 | { (UINT16) (DDR1066 + DDR1333), 0x924, 0x6DB, 0x6DB, 0x6DB}, |
| 1396 | { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0x924, 0x924}}, |
| 1397 | {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492}, |
| 1398 | { (UINT16) (DDR1066 + DDR1333), 0xDAD, 0x924, 0x6DB, 0x492}, |
| 1399 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xDAD, 0xB64, 0xB64}} |
| 1400 | }; |
| 1401 | CONST STATIC UINT16 TxPrePNClock[3][3][5] = { |
| 1402 | {{ (UINT16) DDR800, 0x924, 0x924, 0x924, 0x924}, |
| 1403 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xB6D}, |
| 1404 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}, |
| 1405 | {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924}, |
| 1406 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xDAD}, |
| 1407 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xDAD}}, |
| 1408 | {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924}, |
| 1409 | { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, |
| 1410 | { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}} |
| 1411 | }; |
| 1412 | |
| 1413 | CONST PHY_COMP_INIT_CLIENTNB PhyCompInitBitField[] = { |
| 1414 | // 3. Program TxPreP/TxPreN for Data and DQS according toTable 14 if VDDIO is 1.5V or Table 15 if 1.35V. |
| 1415 | // A. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0[A,6]={0000b, TxPreP, TxPreN}. |
| 1416 | // B. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]02={1000b, TxPreP, TxPreN}. |
| 1417 | {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqs}, |
| 1418 | {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqs}, |
| 1419 | {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqs}, |
| 1420 | // 4. Program TxPreP/TxPreN for Cmd/Addr according toTable 16 if VDDIO is 1.5V or Table 17 if 1.35V. |
| 1421 | // A. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0][12,0E,0A,06]={0000b, TxPreP, TxPreN}. |
| 1422 | // B. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0]02={1000b, TxPreP, TxPreN}. |
| 1423 | {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddr}, |
| 1424 | {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddr}, |
| 1425 | {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddr}, |
| 1426 | {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddr}, |
| 1427 | {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddr}, |
| 1428 | // 5. Program TxPreP/TxPreN for Clock according toTable 18 if VDDIO is 1.5V or Table 19 if 1.35V. |
| 1429 | // A. Program D18F2x[1,0]9C_x0D0F_2[1:0]02={1000b, TxPreP, TxPreN}. |
| 1430 | {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock1TxPreDriverCalPad0, 8, TxPrePNClock} |
| 1431 | }; |
| 1432 | |
| 1433 | BIT_FIELD_NAME CurrentBitField; |
| 1434 | UINT16 SpeedMask; |
| 1435 | CONST UINT16 (*TxPrePNArray)[5]; |
| 1436 | UINT8 Voltage; |
| 1437 | UINT8 i; |
| 1438 | UINT8 j; |
| 1439 | UINT8 k; |
| 1440 | UINT8 Dct; |
| 1441 | |
| 1442 | Dct = NBPtr->Dct; |
| 1443 | NBPtr->SwitchDCT (NBPtr, 0); |
| 1444 | // 1. Program D18F2x[1,0]9C_x0D0F_E003[DisAutoComp, DisablePreDriverCal] = {1b, 1b}. |
| 1445 | MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x6000); |
| 1446 | NBPtr->SwitchDCT (NBPtr, Dct); |
| 1447 | |
| 1448 | SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66); |
| 1449 | Voltage = (UINT8) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage); |
| 1450 | |
| 1451 | for (j = 0; j < GET_SIZE_OF (PhyCompInitBitField); j ++) { |
| 1452 | i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitField[j].IndexBitField); |
| 1453 | TxPrePNArray = PhyCompInitBitField[j].TxPrePN[Voltage]; |
| 1454 | for (k = 0; k < 3; k ++) { |
| 1455 | if ((TxPrePNArray[k][0] & SpeedMask) != 0) { |
| 1456 | for (CurrentBitField = PhyCompInitBitField[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitField[j].EndTargetBitField; CurrentBitField ++) { |
| 1457 | MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitField[j].ExtraValue << 12) | TxPrePNArray[k][i + 1])); |
| 1458 | } |
| 1459 | break; |
| 1460 | } |
| 1461 | } |
| 1462 | ASSERT (k < 3); |
| 1463 | } |
| 1464 | |
| 1465 | NBPtr->FamilySpecificHook[ForceAutoComp] (NBPtr, NBPtr); |
| 1466 | } |
| 1467 | |
| 1468 | /*----------------------------------------------------------------------------- |
| 1469 | * |
| 1470 | * |
| 1471 | * This function re-enable phy compensation. |
| 1472 | * |
| 1473 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1474 | * @param[in,out] OptParam - Optional parameter |
| 1475 | * |
| 1476 | * @return TRUE |
| 1477 | * ---------------------------------------------------------------------------- |
| 1478 | */ |
| 1479 | BOOLEAN |
| 1480 | MemNReEnablePhyCompNb ( |
| 1481 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1482 | IN OUT VOID *OptParam |
| 1483 | ) |
| 1484 | { |
| 1485 | UINT8 Dct; |
| 1486 | |
| 1487 | Dct = NBPtr->Dct; |
| 1488 | |
| 1489 | NBPtr->SwitchDCT (NBPtr, 0); |
| 1490 | // Clear DisableCal and set DisablePredriverCal |
| 1491 | MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, 0x2000); |
| 1492 | NBPtr->SwitchDCT (NBPtr, Dct); |
| 1493 | |
| 1494 | return TRUE; |
| 1495 | } |
| 1496 | |
| 1497 | /*----------------------------------------------------------------------------- |
| 1498 | * |
| 1499 | * |
| 1500 | * This function calculates the value of WrDqDqsEarly and programs it into |
| 1501 | * the DCT and adds it to the WrDqsGrossDelay of each byte lane on each |
| 1502 | * DIMM of the channel. |
| 1503 | * |
| 1504 | * |
| 1505 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1506 | * @param[in,out] OptParam - Optional parameter |
| 1507 | * |
| 1508 | * @return TRUE |
| 1509 | * ---------------------------------------------------------------------------- |
| 1510 | */ |
| 1511 | BOOLEAN |
| 1512 | MemNCalcWrDqDqsEarlyUnb ( |
| 1513 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1514 | IN OUT VOID *OptParam |
| 1515 | ) |
| 1516 | { |
| 1517 | MEM_TECH_BLOCK *TechPtr; |
| 1518 | DCT_STRUCT *DCTPtr; |
| 1519 | CH_DEF_STRUCT *ChannelPtr; |
| 1520 | UINT8 Dimm; |
| 1521 | UINT8 ByteLane; |
| 1522 | UINT8 *WrDqsDlysPtr; |
| 1523 | UINT8 WrDqDqsEarly; |
| 1524 | UINT8 ChipSel; |
| 1525 | |
| 1526 | ASSERT ((NBPtr->IsSupported[WLSeedAdjust]) && (NBPtr->IsSupported[WLNegativeDelay])); |
| 1527 | |
| 1528 | TechPtr = NBPtr->TechPtr; |
| 1529 | ChannelPtr = NBPtr->ChannelPtr; |
| 1530 | DCTPtr = NBPtr->DCTPtr; |
| 1531 | |
| 1532 | ASSERT (NBPtr != NULL); |
| 1533 | ASSERT (ChannelPtr != NULL); |
| 1534 | ASSERT (DCTPtr != NULL); |
| 1535 | // |
| 1536 | // For each DIMM: |
| 1537 | // - The Critical Gross Delay (CGD) is the minimum GrossDly of all byte lanes and all DIMMs. |
| 1538 | // - If (CGD < 0) Then |
| 1539 | // - D18F2xA8_dct[1:0][WrDqDqsEarly] = ABS(CGD) |
| 1540 | // - WrDqsGrossDly = GrossDly + WrDqDqsEarly |
| 1541 | // - Else |
| 1542 | // - D18F2xA8_dct[1:0][WrDqDqsEarly] = 0. |
| 1543 | // - WrDqsGrossDly = GrossDly |
| 1544 | // |
| 1545 | WrDqDqsEarly = 0; |
| 1546 | if (TechPtr->WLCriticalDelay < 0) { |
| 1547 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCalculating WrDqDqsEarly, adjusting WrDqs.\n"); |
| 1548 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMin. Critical Delay: %x\n", TechPtr->WLCriticalDelay); |
| 1549 | // We've saved the entire negative delay value, so take the ABS and convert to GrossDly. |
| 1550 | WrDqDqsEarly = (UINT8) (0x00FF &((((ABS (TechPtr->WLCriticalDelay)) + 0x1F) / 0x20))); |
| 1551 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tWrDqDqsEarly : %02x\n\n", WrDqDqsEarly); |
| 1552 | // |
| 1553 | // Loop through All WrDqsDlys on all DIMMs |
| 1554 | // |
| 1555 | for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay) { |
| 1556 | if ((NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : |
| 1557 | ((DCTPtr->Timings.CsEnabled & ((UINT16) ((NBPtr->CsPerDelay == 2)? 3 : 1) << ChipSel)) != 0)) { |
| 1558 | // |
| 1559 | // If LRDIMMs, only include the physical dimms, not logical Dimms |
| 1560 | // |
| 1561 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS %x:", ChipSel); |
| 1562 | Dimm = ChipSel / NBPtr->CsPerDelay; |
| 1563 | |
| 1564 | WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]); |
| 1565 | for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { |
| 1566 | WrDqsDlysPtr[ByteLane] += (WrDqDqsEarly << 5); |
| 1567 | NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]); |
| 1568 | IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]); |
| 1569 | } |
| 1570 | IDS_HDT_CONSOLE (MEM_FLOW, "\n"); |
| 1571 | } |
| 1572 | } |
| 1573 | } |
| 1574 | MemNSetBitFieldNb (NBPtr, BFWrDqDqsEarly, WrDqDqsEarly); |
| 1575 | return TRUE; |
| 1576 | } |
| 1577 | |
| 1578 | /*----------------------------------------------------------------------------- |
| 1579 | * |
| 1580 | * |
| 1581 | * This function forces phy to M0 state |
| 1582 | * |
| 1583 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1584 | * |
| 1585 | * @return none |
| 1586 | * ---------------------------------------------------------------------------- |
| 1587 | */ |
| 1588 | VOID |
| 1589 | MemNForcePhyToM0Unb ( |
| 1590 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1591 | ) |
| 1592 | { |
| 1593 | IDS_SKIP_HOOK (IDS_FORCE_PHY_TO_M0, NBPtr, &(NBPtr->MemPtr->StdHeader)) { |
| 1594 | // 1. Program D18F2x9C_x0D0F_E013_dct[1:0] = 0118h. |
| 1595 | MemNBrdcstSetUnConditionalNb (NBPtr, BFPllRegWaitTime, 0x118); |
| 1596 | // 2. For each byte lane and each memory P-state: Program D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxSsbMntClkEn] = 0. |
| 1597 | MemNBrdcstSetUnConditionalNb (NBPtr, BFRxSsbMntClkEn, 0); |
| 1598 | // 3. D18F2xA8_dct[1:0][MemPhyPllPdMode] = 00b. |
| 1599 | MemNBrdcstSetUnConditionalNb (NBPtr, BFMemPhyPllPdMode, 0); |
| 1600 | // 4. Force the phy to M0 with the following sequence: |
| 1601 | // A. Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 190h. Restore the default PLL lock time. |
| 1602 | MemNBrdcstSetUnConditionalNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault); |
| 1603 | // B. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80800000h. |
| 1604 | MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80800000); |
| 1605 | NBPtr->SwitchDCT (NBPtr, 0); |
| 1606 | // C. Program D18F2x9C_x0D0F_E018_dct[0][PhyPSMasterChannel] = 0. |
| 1607 | MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0); |
| 1608 | // D. Program D18F2x9C_x0000_000B_dct[0] = 40000000h. CH0 only; |
| 1609 | MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x40000000); |
| 1610 | // E. For each DCT: Program D18F2x9C_x0000_000B_dct[1:0] = 80000000h. |
| 1611 | MemNBrdcstSetUnConditionalNb (NBPtr, BFDramPhyStatusReg, 0x80000000); |
| 1612 | } |
| 1613 | } |
| 1614 | |
| 1615 | /*----------------------------------------------------------------------------- |
| 1616 | * |
| 1617 | * |
| 1618 | * This function sets SkewMemClk before enabling MemClk |
| 1619 | * |
| 1620 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1621 | * @param[in,out] *OptParam - Optional parameter |
| 1622 | * |
| 1623 | * @return TRUE - always |
| 1624 | * ---------------------------------------------------------------------------- |
| 1625 | */ |
| 1626 | BOOLEAN |
| 1627 | MemNSetSkewMemClkUnb ( |
| 1628 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1629 | IN OUT VOID *OptParam |
| 1630 | ) |
| 1631 | { |
| 1632 | UINT8 Dct; |
| 1633 | |
| 1634 | // SkewMemClk is set to 1 if all DCTs are enabled, else 0. |
| 1635 | for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { |
| 1636 | MemNSwitchDCTNb (NBPtr, Dct); |
| 1637 | if (NBPtr->DCTPtr->Timings.DctMemSize == 0) { |
| 1638 | break; |
| 1639 | } |
| 1640 | } |
| 1641 | MemNSwitchDCTNb (NBPtr, 0); |
| 1642 | if (Dct == NBPtr->DctCount) { |
| 1643 | MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0x10); |
| 1644 | } else { |
| 1645 | MemNSetBitFieldNb (NBPtr, BFSkewMemClk, 0); |
| 1646 | } |
| 1647 | |
| 1648 | return TRUE; |
| 1649 | } |
| 1650 | |
| 1651 | /* -----------------------------------------------------------------------------*/ |
| 1652 | /** |
| 1653 | * |
| 1654 | * This function masks the RdDqsDly Bit 0 before writing to register for UNB. |
| 1655 | * |
| 1656 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1657 | * @param[in,out] *Offset - Bit offset of the field to be programmed |
| 1658 | * |
| 1659 | * @return TRUE |
| 1660 | */ |
| 1661 | BOOLEAN |
| 1662 | MemNAdjustRdDqsDlyOffsetUnb ( |
| 1663 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1664 | IN OUT VOID *Offset |
| 1665 | ) |
| 1666 | { |
| 1667 | *(UINT16*) Offset = *(UINT16*) Offset + 1; |
| 1668 | return TRUE; |
| 1669 | } |
| 1670 | |
| 1671 | /* -----------------------------------------------------------------------------*/ |
| 1672 | /** |
| 1673 | * |
| 1674 | * |
| 1675 | * This function delays MEMCLK to prevent WrDqs skew due to negative PRE result. |
| 1676 | * |
| 1677 | * |
| 1678 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1679 | * @param[in,out] OptParam - Optional parameter |
| 1680 | * |
| 1681 | * @return TRUE |
| 1682 | * ---------------------------------------------------------------------------- |
| 1683 | */ |
| 1684 | BOOLEAN |
| 1685 | MemNCalcWrDqDqsEarlyClientNb ( |
| 1686 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1687 | IN OUT VOID *OptParam |
| 1688 | ) |
| 1689 | { |
| 1690 | MEM_TECH_BLOCK *TechPtr; |
| 1691 | DCT_STRUCT *DCTPtr; |
| 1692 | CH_DEF_STRUCT *ChannelPtr; |
| 1693 | UINT8 Dimm; |
| 1694 | UINT8 ByteLane; |
| 1695 | UINT8 *WrDqsDlysPtr; |
| 1696 | UINT8 NewClkDllDelay; |
| 1697 | UINT16 ClkDllFineDly; |
| 1698 | UINT32 AddrCmdTmg; |
| 1699 | |
| 1700 | TechPtr = NBPtr->TechPtr; |
| 1701 | ChannelPtr = NBPtr->ChannelPtr; |
| 1702 | DCTPtr = NBPtr->DCTPtr; |
| 1703 | |
| 1704 | ASSERT (NBPtr != NULL); |
| 1705 | ASSERT (ChannelPtr != NULL); |
| 1706 | ASSERT (DCTPtr != NULL); |
| 1707 | |
| 1708 | if (NBPtr->IsSupported[WLNegativeDelay]) { |
| 1709 | if (TechPtr->WLCriticalDelay < 0) { |
| 1710 | NewClkDllDelay = (UINT8) ABS (TechPtr->WLCriticalDelay); |
| 1711 | |
| 1712 | // Prepare new delay for MEMCLK |
| 1713 | ClkDllFineDly = (UINT16) ((MemNGetBitFieldNb (NBPtr, BFPhyClkDllFine0) & 0xBF60) | NewClkDllDelay); |
| 1714 | |
| 1715 | // Program bit 7(FenceBit) = 1 if NewClkDllDelay >= > F2x9C[FenceThresholdTxPad], else 0. |
| 1716 | ClkDllFineDly |= (NewClkDllDelay >= MemNGetBitFieldNb (NBPtr, BFPhyFence)) ? 0x80 : 0; |
| 1717 | |
| 1718 | // Apply new delay to both chiplets |
| 1719 | MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly | 0x4000); |
| 1720 | MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine0, ClkDllFineDly); |
| 1721 | MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly | 0x4000); |
| 1722 | MemNSetBitFieldNb (NBPtr, BFPhyClkDllFine1, ClkDllFineDly); |
| 1723 | IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tShift MemClk, AddrCmd, CsOdt, Cke by %d to eliminate negative WL\n", NewClkDllDelay); |
| 1724 | |
| 1725 | // |
| 1726 | // Adjust AddrCmd/CsOdt/Cke timing by amount MemClk was delayed |
| 1727 | // |
| 1728 | AddrCmdTmg = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl); |
| 1729 | AddrCmdTmg += (NewClkDllDelay << 16) | (NewClkDllDelay << 8) | NewClkDllDelay; |
| 1730 | AddrCmdTmg &= 0x003F3F3F; |
| 1731 | MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrCmdTmg); |
| 1732 | |
| 1733 | // |
| 1734 | // Adjust all WrDqsDlys on all DIMMs of the current channel |
| 1735 | // |
| 1736 | for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { |
| 1737 | if ((DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm << 1))) != 0) { |
| 1738 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCS%d\n\t\t\tWrDqs:", Dimm << 1); |
| 1739 | WrDqsDlysPtr = &(ChannelPtr->WrDqsDlys[(Dimm * TechPtr->DlyTableWidth ())]); |
| 1740 | for (ByteLane = 0; ByteLane < 8; ByteLane++) { |
| 1741 | WrDqsDlysPtr[ByteLane] = (UINT8) (WrDqsDlysPtr[ByteLane] + NewClkDllDelay); |
| 1742 | NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqsDlysPtr[ByteLane]); |
| 1743 | IDS_HDT_CONSOLE (MEM_FLOW, " %02x", WrDqsDlysPtr[ByteLane]); |
| 1744 | } |
| 1745 | IDS_HDT_CONSOLE (MEM_FLOW, "\n"); |
| 1746 | } |
| 1747 | } |
| 1748 | } |
| 1749 | } |
| 1750 | |
| 1751 | return TRUE; |
| 1752 | } |
| 1753 | /* -----------------------------------------------------------------------------*/ |
| 1754 | /** |
| 1755 | * |
| 1756 | * |
| 1757 | * This function initializes RxEn Delays for RxEn seedless training |
| 1758 | * |
| 1759 | * |
| 1760 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1761 | * @param[in,out] OptParam - Optional parameter |
| 1762 | * |
| 1763 | * @return TRUE |
| 1764 | * ---------------------------------------------------------------------------- |
| 1765 | */ |
| 1766 | BOOLEAN |
| 1767 | MemNInitializeRxEnSeedlessTrainingUnb ( |
| 1768 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1769 | IN OUT VOID *OptParam |
| 1770 | ) |
| 1771 | { |
| 1772 | UINT8 ByteLane; |
| 1773 | // Save original PRE based RxEnDly for RxEn Seedless training |
| 1774 | for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { |
| 1775 | NBPtr->TechPtr->RxOrig[ByteLane] = NBPtr->ChannelPtr->RcvEnDlys[(NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) * NBPtr->TechPtr->DlyTableWidth () + ByteLane]; |
| 1776 | } |
| 1777 | return TRUE; |
| 1778 | } |
| 1779 | /*----------------------------------------------------------------------------- |
| 1780 | * |
| 1781 | * |
| 1782 | * This function checks each bytelane for no window error. |
| 1783 | * |
| 1784 | * |
| 1785 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1786 | * @param[in,out] OptParam - Optional parameter |
| 1787 | * |
| 1788 | * @return TRUE |
| 1789 | * ---------------------------------------------------------------------------- |
| 1790 | */ |
| 1791 | BOOLEAN |
| 1792 | MemNTrackRxEnSeedlessRdWrNoWindBLErrorUnb ( |
| 1793 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1794 | IN OUT VOID *OptParam |
| 1795 | ) |
| 1796 | { |
| 1797 | MemTTrackRxEnSeedlessRdWrNoWindBLError (NBPtr->TechPtr, OptParam); |
| 1798 | return TRUE; |
| 1799 | } |
| 1800 | /*----------------------------------------------------------------------------- |
| 1801 | * |
| 1802 | * |
| 1803 | * This function checks each bytelane for small window error. |
| 1804 | * |
| 1805 | * |
| 1806 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1807 | * @param[in,out] OptParam - Optional parameter |
| 1808 | * |
| 1809 | * @return TRUE |
| 1810 | * ---------------------------------------------------------------------------- |
| 1811 | */ |
| 1812 | BOOLEAN |
| 1813 | MemNTrackRxEnSeedlessRdWrSmallWindBLErrorUnb ( |
| 1814 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1815 | IN OUT VOID *OptParam |
| 1816 | ) |
| 1817 | { |
| 1818 | MemTTrackRxEnSeedlessRdWrSmallWindBLError (NBPtr->TechPtr, OptParam); |
| 1819 | return TRUE; |
| 1820 | } |
| 1821 | /*----------------------------------------------------------------------------- |
| 1822 | * |
| 1823 | * |
| 1824 | * This function initializes a ByteLaneError error. |
| 1825 | * |
| 1826 | * |
| 1827 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1828 | * @param[in,out] OptParam - Optional parameter |
| 1829 | * |
| 1830 | * @return TRUE |
| 1831 | * ---------------------------------------------------------------------------- |
| 1832 | */ |
| 1833 | BOOLEAN |
| 1834 | MemNInitialzeRxEnSeedlessByteLaneErrorUnb ( |
| 1835 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1836 | IN OUT VOID *OptParam |
| 1837 | ) |
| 1838 | { |
| 1839 | UINT8 ByteLane; |
| 1840 | for (ByteLane = 0; ByteLane < (NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8); ByteLane++) { |
| 1841 | NBPtr->TechPtr->ByteLaneError[ByteLane] = FALSE; // All Bytelanes have no errors |
| 1842 | } |
| 1843 | return TRUE; |
| 1844 | } |
| 1845 | /* -----------------------------------------------------------------------------*/ |
| 1846 | /** |
| 1847 | * |
| 1848 | * |
| 1849 | * This function sets phy power saving related settings in different MPstate context. |
| 1850 | * |
| 1851 | * |
| 1852 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1853 | * |
| 1854 | * @return none |
| 1855 | * ---------------------------------------------------------------------------- |
| 1856 | */ |
| 1857 | VOID |
| 1858 | MemNPhyPowerSavingMPstateUnb ( |
| 1859 | IN OUT MEM_NB_BLOCK *NBPtr |
| 1860 | ) |
| 1861 | { |
| 1862 | STATIC UINT8 Sequence[] = {8, 4, 3, 5, 2, 6, 1, 7, 0}; |
| 1863 | UINT16 DllPower[9]; |
| 1864 | UINT8 NumLanes; |
| 1865 | UINT8 DllWakeTime; |
| 1866 | UINT8 MaxRxStggrDly; |
| 1867 | UINT8 MinRcvEnGrossDly; |
| 1868 | UINT8 MinWrDatGrossDly; |
| 1869 | UINT8 dRxStggrDly; |
| 1870 | UINT8 dTxStggrDly; |
| 1871 | UINT8 TempStggrDly; |
| 1872 | UINT8 MaxTxStggrDly; |
| 1873 | UINT8 Tcwl; |
| 1874 | UINT8 i; |
| 1875 | |
| 1876 | IDS_HDT_CONSOLE (MEM_FLOW, "Start Phy power saving setting for memory Pstate %d\n", NBPtr->MemPstate); |
| 1877 | // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b. |
| 1878 | // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b. |
| 1879 | // 6. D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0][RxDqsUDllPowerDown] = 1. |
| 1880 | MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x83); |
| 1881 | // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn]. |
| 1882 | // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b} |
| 1883 | if (NBPtr->MemPstate == MEMORY_PSTATE0) { |
| 1884 | MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1); |
| 1885 | } |
| 1886 | // 9. IF (DimmsPopulated == 1)&& ((D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==010b) || |
| 1887 | // (D18F2x9C_x0000_0000_dct[1:0]_mp[1:0][CkeDrvStren]==011b)) THEN THEN |
| 1888 | // program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 1 |
| 1889 | // ELSE program D18F2x9C_x0D0F_C0[40,00]_dct[1:0][LowPowerDrvStrengthEn] = 0 ENDIF. |
| 1890 | if ((NBPtr->ChannelPtr->Dimms == 1) && ((MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 2) || (MemNGetBitFieldNb (NBPtr, BFCkeDrvStren) == 3))) { |
| 1891 | MemNSetBitFieldNb (NBPtr, BFLowPowerDrvStrengthEn, 0x100); |
| 1892 | } |
| 1893 | // 10. Program D18F2x9C_x0D0F_0[F,7:0][50,10]_dct[1:0][EnRxPadStandby] = IF |
| 1894 | // (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz) THEN 1 ELSE 0 ENDIF. |
| 1895 | MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0); |
| 1896 | // 11. Program D18F2x9C_x0000_000D_dct[1:0]_mp[1:0] as follows: |
| 1897 | // If (DDR rate < = 1600) TxMaxDurDllNoLock = RxMaxDurDllNoLock = 8h |
| 1898 | // else TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h. |
| 1899 | if (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) { |
| 1900 | MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 8); |
| 1901 | MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 8); |
| 1902 | } else { |
| 1903 | MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7); |
| 1904 | MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7); |
| 1905 | } |
| 1906 | // TxCPUpdPeriod = RxCPUpdPeriod = 011b. |
| 1907 | MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3); |
| 1908 | MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3); |
| 1909 | // TxDLLWakeupTime = RxDLLWakeupTime = 11b. |
| 1910 | MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3); |
| 1911 | MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3); |
| 1912 | |
| 1913 | if (NBPtr->IsSupported[DllStaggerEn]) { |
| 1914 | // 12. Program D18F2x9C_x0D0F_0[F,7:0][5C,1C]_dct[1:0] as follows. |
| 1915 | // Let Numlanes = 8. = 9 with ECC. |
| 1916 | NumLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8; |
| 1917 | // RxDllStggrEn = TxDllStggrEn = 1. |
| 1918 | for (i = 0; i < 9; i ++) { |
| 1919 | DllPower[i] = 0x8080; |
| 1920 | } |
| 1921 | // If (DDR rate > = 1866) DllWakeTime = 1, Else DllWakeTime = 0. |
| 1922 | DllWakeTime = (NBPtr->DCTPtr->Timings.Speed >= DDR1866_FREQUENCY) ? 1 : 0; |
| 1923 | // Let MaxRxStggrDly = (Tcl*2) + MIN(DqsRcvEnGrossDelay for all byte lanes (see D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4. |
| 1924 | MinRcvEnGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessRcvEnDly, FALSE); |
| 1925 | ASSERT ((NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly) >= 4); |
| 1926 | MaxRxStggrDly = NBPtr->DCTPtr->Timings.CasL * 2 + MinRcvEnGrossDly - 4; |
| 1927 | // Let (real) dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (Numlanes - 1). |
| 1928 | ASSERT (MaxRxStggrDly >= DllWakeTime); |
| 1929 | dRxStggrDly = (MaxRxStggrDly - DllWakeTime) / (NumLanes - 1); |
| 1930 | IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum RcvEnGrossDly: 0x%02x MaxRxStggrDly: 0x%02x dRxStggrDly: 0x%02x\n", MinRcvEnGrossDly, MaxRxStggrDly, dRxStggrDly); |
| 1931 | // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program RxDllStggrDly[5:0] = an |
| 1932 | // increasing value, starting with 0 for the first byte lane in the sequence and increasing at a rate of dRxStggrDly |
| 1933 | // for each subsequent byte lane. Convert the real to integer by rounding down or using C (int) typecast after linearization. |
| 1934 | i = 9 - NumLanes; |
| 1935 | TempStggrDly = 0; |
| 1936 | for (; i < 9; i ++) { |
| 1937 | DllPower[Sequence[i]] |= ((TempStggrDly & 0x3F) << 8); |
| 1938 | TempStggrDly = TempStggrDly + dRxStggrDly; |
| 1939 | } |
| 1940 | |
| 1941 | // Let MaxTxStggrDly = (Tcwl*2) + MIN(MIN (WrDatGrossDly for all byte lanes (see |
| 1942 | // D18F2x9C_x0000_0[3:0]0[2:1]_dct[1:0]_mp[1:0])), MIN(DqsRcvEnGrossDelay for all byte lanes (see |
| 1943 | // D18F2x9C_x0000_00[2A:10]_dct[1:0]_mp[1:0])) - 4. |
| 1944 | Tcwl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcwl); |
| 1945 | MinWrDatGrossDly = NBPtr->TechPtr->GetMinMaxGrossDly (NBPtr->TechPtr, AccessWrDatDly, FALSE); |
| 1946 | MaxTxStggrDly = Tcwl * 2 + MIN (MinRcvEnGrossDly, MinWrDatGrossDly) - 4; |
| 1947 | // Let dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (Numlanes - 1). |
| 1948 | ASSERT (MaxTxStggrDly >= DllWakeTime); |
| 1949 | dTxStggrDly = (MaxTxStggrDly - DllWakeTime) / (NumLanes - 1); |
| 1950 | // For each byte lane in the ordered sequence {8, 4, 3, 5, 2, 6, 1, 7, 0}, program TxDllStggrDly[5:0] = an |
| 1951 | // increasing integer value, starting with 0 for the first byte lane in the sequence and increasing at a rate of |
| 1952 | // dTxStggrDly for each subsequent byte lane. |
| 1953 | IDS_HDT_CONSOLE (MEM_FLOW, "\tMinimum WrDatGrossDly: 0x%02x MaxTxStggrDly: 0x%02x dTxStggrDly: 0x%02x\n", MinWrDatGrossDly, MaxTxStggrDly, dTxStggrDly); |
| 1954 | i = 9 - NumLanes; |
| 1955 | TempStggrDly = 0; |
| 1956 | for (; i < 9; i ++) { |
| 1957 | DllPower[Sequence[i]] |= (TempStggrDly & 0x3F); |
| 1958 | TempStggrDly = TempStggrDly + dTxStggrDly; |
| 1959 | } |
| 1960 | |
| 1961 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : ECC 07 06 05 04 03 02 01 00\n"); |
| 1962 | IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDll Power : %04x %04x %04x %04x %04x %04x %04x %04x %04x\n", |
| 1963 | DllPower[8], DllPower[7], DllPower[6], DllPower[5], DllPower[4], DllPower[3], DllPower[2], DllPower[1], DllPower[0]); |
| 1964 | |
| 1965 | for (i = 0; i < NumLanes; i ++) { |
| 1966 | MemNSetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i, (MemNGetBitFieldNb (NBPtr, BFDataByteDllPowerMgnByte0 + i) & 0x4040) | DllPower[i]); |
| 1967 | } |
| 1968 | } |
| 1969 | // 13. Program D18F2x248_dct[1:0]_mp[1:0] and then D18F2x9C_x0D0F_0[F,7:0][53,13]_dct[1:0] as follows: |
| 1970 | // For M1 context program RxChMntClkEn=RxSsbMntClkEn=0. |
| 1971 | // For M0 context program RxChMntClkEn=RxSsbMntClkEn=1. |
| 1972 | if (NBPtr->MemPstate == MEMORY_PSTATE1) { |
| 1973 | MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 0); |
| 1974 | MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0); |
| 1975 | } else { |
| 1976 | MemNSetBitFieldNb (NBPtr, BFRxChMntClkEn, 1); |
| 1977 | MemNSetBitFieldNb (NBPtr, BFRxSsbMntClkEn, 0x100); |
| 1978 | } |
| 1979 | |
| 1980 | IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader); |
| 1981 | } |
| 1982 | |
| 1983 | /* -----------------------------------------------------------------------------*/ |
| 1984 | /** |
| 1985 | * |
| 1986 | * This function resets RxFifo pointer during Read DQS training |
| 1987 | * |
| 1988 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 1989 | * @param[in,out] *OptParam - Optional parameter |
| 1990 | * |
| 1991 | * @return TRUE |
| 1992 | */ |
| 1993 | |
| 1994 | BOOLEAN |
| 1995 | MemNResetRxFifoPtrClientNb ( |
| 1996 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 1997 | IN OUT VOID *OptParam |
| 1998 | ) |
| 1999 | { |
| 2000 | if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { |
| 2001 | MemNSetBitFieldNb (NBPtr, BFRxPtrInitReq, 1); |
| 2002 | MemNPollBitFieldNb (NBPtr, BFRxPtrInitReq, 0, PCI_ACCESS_TIMEOUT, FALSE); |
| 2003 | } |
| 2004 | return TRUE; |
| 2005 | } |
| 2006 | |
| 2007 | |
| 2008 | /* -----------------------------------------------------------------------------*/ |
| 2009 | /** |
| 2010 | * |
| 2011 | * This function adjusts WrDqsBias before seed scaling |
| 2012 | * |
| 2013 | * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK |
| 2014 | * @param[in,out] *WrDqsBias - Pointer to WrDqsBias |
| 2015 | * |
| 2016 | * @return FALSE - Supported |
| 2017 | * @return TRUE - Not supported |
| 2018 | */ |
| 2019 | |
| 2020 | BOOLEAN |
| 2021 | MemNAdjustWrDqsBeforeSeedScalingUnb ( |
| 2022 | IN OUT MEM_NB_BLOCK *NBPtr, |
| 2023 | IN OUT VOID *WrDqsBias |
| 2024 | ) |
| 2025 | { |
| 2026 | // Subtract (0x20 * WrDqDqsEarly) since it is a non-scalable component |
| 2027 | * (INT16 *) WrDqsBias = (INT16) (0x20 * MemNGetBitFieldNb (NBPtr, BFWrDqDqsEarly)); |
| 2028 | return TRUE; |
| 2029 | } |