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 uint8_t fam15_dimm_dic(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) |
| 18 | { |
| 19 | uint8_t dic; |
| 20 | |
| 21 | /* Calculate DIC based on recommendations in MR1_dct[1:0] */ |
| 22 | if (pDCTstat->Status & (1 << SB_LoadReduced)) { |
| 23 | /* TODO |
| 24 | * LRDIMM unimplemented |
| 25 | */ |
| 26 | dic = 0x0; |
| 27 | } else { |
| 28 | dic = 0x1; |
| 29 | } |
| 30 | |
| 31 | return dic; |
| 32 | } |
| 33 | |
| 34 | static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) |
| 35 | { |
| 36 | uint8_t term = 0; |
| 37 | sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; |
| 38 | uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; |
| 39 | uint8_t frequency_index; |
| 40 | uint8_t rank_count = pDCTData->DimmRanks[dimm]; |
| 41 | |
| 42 | if (is_fam15h()) |
| 43 | frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; |
| 44 | else |
| 45 | frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7; |
| 46 | |
| 47 | /* FIXME |
| 48 | * Mainboards need to be able to specify the maximum number of DIMMs installable per channel |
| 49 | * For now assume a maximum of 2 DIMMs per channel can be installed |
| 50 | */ |
| 51 | uint8_t MaxDimmsInstallable = 2; |
| 52 | |
| 53 | if (is_fam15h()) { |
| 54 | if (pDCTstat->Status & (1 << SB_Registered)) { |
| 55 | /* TODO |
| 56 | * RDIMM unimplemented |
| 57 | */ |
| 58 | } else { |
| 59 | if (package_type == PT_GR) { |
| 60 | /* Socket G34: Fam15h BKDG v3.14 Table 56 */ |
| 61 | if (MaxDimmsInstallable == 1) { |
| 62 | term = 0x0; |
| 63 | } else if (MaxDimmsInstallable == 2) { |
| 64 | if ((number_of_dimms == 2) && (frequency_index == 0x12)) { |
| 65 | term = 0x1; |
| 66 | } else if (number_of_dimms == 1) { |
| 67 | term = 0x0; |
| 68 | } else { |
| 69 | term = 0x2; |
| 70 | } |
| 71 | } else if (MaxDimmsInstallable == 3) { |
| 72 | if (number_of_dimms == 1) { |
| 73 | if (frequency_index <= 0xa) { |
| 74 | term = 0x2; |
| 75 | } else { |
| 76 | if (rank_count < 3) { |
| 77 | term = 0x1; |
| 78 | } else { |
| 79 | term = 0x2; |
| 80 | } |
| 81 | } |
| 82 | } else if (number_of_dimms == 2) { |
| 83 | term = 0x2; |
| 84 | } |
| 85 | } |
| 86 | } else { |
| 87 | /* TODO |
| 88 | * Other sockets unimplemented |
| 89 | */ |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | return term; |
| 95 | } |
| 96 | |
| 97 | static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) |
| 98 | { |
| 99 | uint8_t term = 0; |
| 100 | sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; |
| 101 | uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; |
| 102 | uint8_t frequency_index; |
| 103 | |
| 104 | if (is_fam15h()) |
| 105 | frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; |
| 106 | else |
| 107 | frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7; |
| 108 | |
| 109 | /* FIXME |
| 110 | * Mainboards need to be able to specify the maximum number of DIMMs installable per channel |
| 111 | * For now assume a maximum of 2 DIMMs per channel can be installed |
| 112 | */ |
| 113 | uint8_t MaxDimmsInstallable = 2; |
| 114 | |
| 115 | if (is_fam15h()) { |
| 116 | if (pDCTstat->Status & (1 << SB_LoadReduced)) { |
| 117 | /* TODO |
| 118 | * LRDIMM unimplemented |
| 119 | */ |
| 120 | } else if (pDCTstat->Status & (1 << SB_Registered)) { |
| 121 | /* TODO |
| 122 | * RDIMM unimplemented |
| 123 | */ |
| 124 | } else { |
| 125 | if (package_type == PT_GR) { |
| 126 | /* Socket G34: Fam15h BKDG v3.14 Table 56 */ |
| 127 | if (MaxDimmsInstallable == 1) { |
| 128 | if ((frequency_index == 0x4) || (frequency_index == 0x6)) |
| 129 | term = 0x2; |
| 130 | else if ((frequency_index == 0xa) || (frequency_index == 0xe)) |
| 131 | term = 0x1; |
| 132 | else |
| 133 | term = 0x3; |
| 134 | } |
| 135 | if (MaxDimmsInstallable == 2) { |
| 136 | if (number_of_dimms == 1) { |
| 137 | if (frequency_index <= 0x6) { |
| 138 | term = 0x2; |
| 139 | } else if (frequency_index <= 0xe) { |
| 140 | term = 0x1; |
| 141 | } else { |
| 142 | term = 0x3; |
| 143 | } |
| 144 | } else { |
| 145 | if (frequency_index <= 0xa) { |
| 146 | term = 0x3; |
| 147 | } else if (frequency_index <= 0xe) { |
| 148 | term = 0x5; |
| 149 | } else { |
| 150 | term = 0x4; |
| 151 | } |
| 152 | } |
| 153 | } else if (MaxDimmsInstallable == 3) { |
| 154 | if (number_of_dimms == 1) { |
| 155 | term = 0x0; |
| 156 | } else if (number_of_dimms == 2) { |
| 157 | if (frequency_index <= 0xa) { |
| 158 | if (rank == 1) { |
| 159 | term = 0x0; |
| 160 | } else { |
| 161 | term = 0x3; |
| 162 | } |
| 163 | } else if (frequency_index <= 0xe) { |
| 164 | if (rank == 1) { |
| 165 | term = 0x0; |
| 166 | } else { |
| 167 | term = 0x5; |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | } else { |
| 173 | /* TODO |
| 174 | * Other sockets unimplemented |
| 175 | */ |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | return term; |
| 181 | } |
| 182 | |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 183 | static void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, |
| 184 | struct DCTStatStruc *pDCTstat, u8 dct); |
| 185 | |
| 186 | static void mct_DCTAccessDone(struct DCTStatStruc *pDCTstat, u8 dct) |
| 187 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 188 | u32 dev = pDCTstat->dev_dct; |
| 189 | u32 val; |
| 190 | |
| 191 | do { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 192 | val = Get_NB32_DCT(dev, dct, 0x98); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 193 | } while (!(val & (1 << DctAccessDone))); |
| 194 | } |
| 195 | |
| 196 | static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting, u8 MrsChipSel, u8 dct) |
| 197 | { |
| 198 | u16 word; |
| 199 | u32 ret; |
| 200 | |
| 201 | if (!(pDCTstat->Status & (1 << SB_Registered))) { |
| 202 | word = pDCTstat->MirrPresU_NumRegR; |
| 203 | if (dct == 0) { |
| 204 | word &= 0x55; |
| 205 | word <<= 1; |
| 206 | } else |
| 207 | word &= 0xAA; |
| 208 | |
| 209 | if (word & (1 << MrsChipSel)) { |
| 210 | /* A3<->A4,A5<->A6,A7<->A8,BA0<->BA1 */ |
| 211 | ret = 0; |
| 212 | if (MR_register_setting & (1 << 3)) ret |= 1 << 4; |
| 213 | if (MR_register_setting & (1 << 4)) ret |= 1 << 3; |
| 214 | if (MR_register_setting & (1 << 5)) ret |= 1 << 6; |
| 215 | if (MR_register_setting & (1 << 6)) ret |= 1 << 5; |
| 216 | if (MR_register_setting & (1 << 7)) ret |= 1 << 8; |
| 217 | if (MR_register_setting & (1 << 8)) ret |= 1 << 7; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 218 | if (is_fam15h()) { |
| 219 | if (MR_register_setting & (1 << 18)) ret |= 1 << 19; |
| 220 | if (MR_register_setting & (1 << 19)) ret |= 1 << 18; |
| 221 | MR_register_setting &= ~0x000c01f8; |
| 222 | } else { |
| 223 | if (MR_register_setting & (1 << 16)) ret |= 1 << 17; |
| 224 | if (MR_register_setting & (1 << 17)) ret |= 1 << 16; |
| 225 | MR_register_setting &= ~0x000301f8; |
| 226 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 227 | MR_register_setting |= ret; |
| 228 | } |
| 229 | } |
| 230 | return MR_register_setting; |
| 231 | } |
| 232 | |
| 233 | static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS) |
| 234 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 235 | u32 dev = pDCTstat->dev_dct; |
| 236 | u32 val; |
| 237 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 238 | val = Get_NB32_DCT(dev, dct, 0x7c); |
| 239 | val &= ~0x00ffffff; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 240 | val |= EMRS; |
| 241 | val |= 1 << SendMrsCmd; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 242 | Set_NB32_DCT(dev, dct, 0x7c, val); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 243 | |
| 244 | do { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 245 | val = Get_NB32_DCT(dev, dct, 0x7c); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 246 | } while (val & (1 << SendMrsCmd)); |
| 247 | } |
| 248 | |
| 249 | static u32 mct_MR2(struct MCTStatStruc *pMCTstat, |
| 250 | struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) |
| 251 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 252 | u32 dev = pDCTstat->dev_dct; |
| 253 | u32 dword, ret; |
| 254 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 255 | if (is_fam15h()) { |
| 256 | uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 257 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 258 | /* The formula for chip select number is: CS = dimm*2+rank */ |
| 259 | uint8_t dimm = MrsChipSel / 2; |
| 260 | uint8_t rank = MrsChipSel % 2; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 261 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 262 | /* FIXME: These parameters should be configurable |
| 263 | * For now, err on the side of caution and enable automatic 2x refresh |
| 264 | * when the DDR temperature rises above the internal limits |
| 265 | */ |
| 266 | uint8_t force_2x_self_refresh = 0; /* ASR */ |
| 267 | uint8_t auto_2x_self_refresh = 1; /* SRT */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 268 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 269 | ret = 0x80000; |
| 270 | ret |= (MrsChipSel << 21); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 271 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 272 | /* Set self refresh parameters */ |
| 273 | ret |= (force_2x_self_refresh << 6); |
| 274 | ret |= (auto_2x_self_refresh << 7); |
| 275 | |
| 276 | /* Obtain Tcwl, adjust, and set CWL with the adjusted value */ |
| 277 | dword = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f; |
| 278 | ret |= ((dword - 5) << 3); |
| 279 | |
| 280 | /* Obtain and set RttWr */ |
| 281 | ret |= (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); |
| 282 | } else { |
| 283 | ret = 0x20000; |
| 284 | ret |= (MrsChipSel << 20); |
| 285 | |
| 286 | /* program MrsAddress[5:3]=CAS write latency (CWL): |
| 287 | * based on F2x[1,0]84[Tcwl] */ |
| 288 | dword = Get_NB32_DCT(dev, dct, 0x84); |
| 289 | dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword); |
| 290 | |
| 291 | ret |= ((dword >> 20) & 7) << 3; |
| 292 | |
| 293 | /* program MrsAddress[6]=auto self refresh method (ASR): |
| 294 | * based on F2x[1,0]84[ASR] |
| 295 | * program MrsAddress[7]=self refresh temperature range (SRT): |
| 296 | * based on F2x[1,0]84[ASR and SRT] |
| 297 | */ |
| 298 | ret |= ((dword >> 18) & 3) << 6; |
| 299 | |
| 300 | /* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR) |
| 301 | * based on F2x[1,0]84[DramTermDyn] |
| 302 | */ |
| 303 | ret |= ((dword >> 10) & 3) << 9; |
| 304 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 305 | |
| 306 | return ret; |
| 307 | } |
| 308 | |
| 309 | static u32 mct_MR3(struct MCTStatStruc *pMCTstat, |
| 310 | struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) |
| 311 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 312 | u32 dev = pDCTstat->dev_dct; |
| 313 | u32 dword, ret; |
| 314 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 315 | if (is_fam15h()) { |
| 316 | ret = 0xc0000; |
| 317 | ret |= (MrsChipSel << 21); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 318 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 319 | /* Program MPR and MPRLoc to 0 */ |
| 320 | // ret |= 0x0; /* MPR */ |
| 321 | // ret |= (0x0 << 2); /* MPRLoc */ |
| 322 | } else { |
| 323 | ret = 0x30000; |
| 324 | ret |= (MrsChipSel << 20); |
| 325 | |
| 326 | /* program MrsAddress[1:0]=multi purpose register address location |
| 327 | * (MPR Location):based on F2x[1,0]84[MprLoc] |
| 328 | * program MrsAddress[2]=multi purpose register |
| 329 | * (MPR):based on F2x[1,0]84[MprEn] |
| 330 | */ |
| 331 | dword = Get_NB32_DCT(dev, dct, 0x84); |
| 332 | ret |= (dword >> 24) & 7; |
| 333 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 334 | |
| 335 | return ret; |
| 336 | } |
| 337 | |
| 338 | static u32 mct_MR1(struct MCTStatStruc *pMCTstat, |
| 339 | struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) |
| 340 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 341 | u32 dev = pDCTstat->dev_dct; |
| 342 | u32 dword, ret; |
| 343 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 344 | if (is_fam15h()) { |
| 345 | uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 346 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 347 | /* Set defaults */ |
| 348 | uint8_t qoff = 0; /* Enable output buffers */ |
| 349 | uint8_t wrlvl = 0; /* Disable write levelling */ |
| 350 | uint8_t tqds = 0; |
| 351 | uint8_t rttnom = 0; |
| 352 | uint8_t dic = 0; |
| 353 | uint8_t additive_latency = 0; |
| 354 | uint8_t dll_enable = 0; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 355 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 356 | ret = 0x40000; |
| 357 | ret |= (MrsChipSel << 21); |
| 358 | |
| 359 | /* The formula for chip select number is: CS = dimm*2+rank */ |
| 360 | uint8_t dimm = MrsChipSel / 2; |
| 361 | uint8_t rank = MrsChipSel % 2; |
| 362 | |
| 363 | /* Determine if TQDS should be set */ |
| 364 | if ((pDCTstat->Dimmx8Present & (1 << dimm)) |
| 365 | && (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0) |
| 366 | && (pDCTstat->Status & (1 << SB_LoadReduced))) |
| 367 | tqds = 1; |
| 368 | |
| 369 | /* Obtain RttNom */ |
| 370 | rttnom = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); |
| 371 | |
| 372 | /* Obtain DIC */ |
| 373 | dic = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); |
| 374 | |
| 375 | /* Load data into MRS word */ |
| 376 | ret |= (qoff & 0x1) << 12; |
| 377 | ret |= (tqds & 0x1) << 11; |
| 378 | ret |= ((rttnom & 0x4) >> 2) << 9; |
| 379 | ret |= ((rttnom & 0x2) >> 1) << 6; |
| 380 | ret |= ((rttnom & 0x1) >> 0) << 2; |
| 381 | ret |= (wrlvl & 0x1) << 7; |
| 382 | ret |= ((dic & 0x2) >> 1) << 5; |
| 383 | ret |= ((dic & 0x1) >> 0) << 1; |
| 384 | ret |= (additive_latency & 0x3) << 3; |
| 385 | ret |= (dll_enable & 0x1); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 386 | } else { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 387 | ret = 0x10000; |
| 388 | ret |= (MrsChipSel << 20); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 389 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 390 | /* program MrsAddress[5,1]=output driver impedance control (DIC): |
| 391 | * based on F2x[1,0]84[DrvImpCtrl] |
| 392 | */ |
| 393 | dword = Get_NB32_DCT(dev, dct, 0x84); |
| 394 | if (dword & (1 << 3)) |
| 395 | ret |= 1 << 5; |
| 396 | if (dword & (1 << 2)) |
| 397 | ret |= 1 << 1; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 398 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 399 | /* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT): |
| 400 | * based on F2x[1,0]84[DramTerm] |
| 401 | */ |
| 402 | if (!(pDCTstat->Status & (1 << SB_Registered))) { |
| 403 | if (dword & (1 << 9)) |
| 404 | ret |= 1 << 9; |
| 405 | if (dword & (1 << 8)) |
| 406 | ret |= 1 << 6; |
| 407 | if (dword & (1 << 7)) |
| 408 | ret |= 1 << 2; |
| 409 | } else { |
| 410 | ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel); |
| 411 | } |
| 412 | |
| 413 | /* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */ |
| 414 | if (Get_NB32_DCT(dev, dct, 0x94) & (1 << RDqsEn)) { |
| 415 | u8 bit; |
| 416 | /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ |
| 417 | bit = (ret >> 21) << 1; |
| 418 | if ((dct & 1) != 0) |
| 419 | bit ++; |
| 420 | if (pDCTstat->Dimmx8Present & (1 << bit)) |
| 421 | ret |= 1 << 11; |
| 422 | } |
| 423 | |
| 424 | /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */ |
| 425 | if (dword & (1 << 13)) |
| 426 | ret |= 1 << 12; |
| 427 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 428 | |
| 429 | return ret; |
| 430 | } |
| 431 | |
| 432 | static u32 mct_MR0(struct MCTStatStruc *pMCTstat, |
| 433 | struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) |
| 434 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 435 | u32 dev = pDCTstat->dev_dct; |
| 436 | u32 dword, ret, dword2; |
| 437 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 438 | if (is_fam15h()) { |
| 439 | ret = 0x00000; |
| 440 | ret |= (MrsChipSel << 21); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 441 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 442 | /* Set defaults */ |
| 443 | uint8_t ppd = 0; |
| 444 | uint8_t wr_ap = 0; |
| 445 | uint8_t dll_reset = 1; |
| 446 | uint8_t test_mode = 0; |
| 447 | uint8_t cas_latency = 0; |
| 448 | uint8_t read_burst_type = 1; |
| 449 | uint8_t burst_length = 0; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 450 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 451 | /* Obtain PchgPDModeSel */ |
| 452 | dword = Get_NB32_DCT(dev, dct, 0x84); |
| 453 | ppd = (dword >> 23) & 0x1; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 454 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 455 | /* Obtain Twr */ |
| 456 | dword = Get_NB32_DCT(dev, dct, 0x22c) & 0x1f; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 457 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 458 | /* Calculate wr_ap (Fam15h BKDG v3.14 Table 82) */ |
| 459 | if (dword == 0x10) |
| 460 | wr_ap = 0x0; |
| 461 | else if (dword == 0x5) |
| 462 | wr_ap = 0x1; |
| 463 | else if (dword == 0x6) |
| 464 | wr_ap = 0x2; |
| 465 | else if (dword == 0x7) |
| 466 | wr_ap = 0x3; |
| 467 | else if (dword == 0x8) |
| 468 | wr_ap = 0x4; |
| 469 | else if (dword == 0xa) |
| 470 | wr_ap = 0x5; |
| 471 | else if (dword == 0xc) |
| 472 | wr_ap = 0x6; |
| 473 | else if (dword == 0xe) |
| 474 | wr_ap = 0x7; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 475 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 476 | /* Obtain Tcl */ |
| 477 | dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 478 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 479 | /* Calculate cas_latency (Fam15h BKDG v3.14 Table 83) */ |
| 480 | if (dword == 0x5) |
| 481 | cas_latency = 0x2; |
| 482 | else if (dword == 0x6) |
| 483 | cas_latency = 0x4; |
| 484 | else if (dword == 0x7) |
| 485 | cas_latency = 0x6; |
| 486 | else if (dword == 0x8) |
| 487 | cas_latency = 0x8; |
| 488 | else if (dword == 0x9) |
| 489 | cas_latency = 0xa; |
| 490 | else if (dword == 0xa) |
| 491 | cas_latency = 0xc; |
| 492 | else if (dword == 0xb) |
| 493 | cas_latency = 0xe; |
| 494 | else if (dword == 0xc) |
| 495 | cas_latency = 0x1; |
| 496 | else if (dword == 0xd) |
| 497 | cas_latency = 0x3; |
| 498 | else if (dword == 0xe) |
| 499 | cas_latency = 0x5; |
| 500 | else if (dword == 0xf) |
| 501 | cas_latency = 0x7; |
| 502 | else if (dword == 0x10) |
| 503 | cas_latency = 0x9; |
| 504 | |
| 505 | /* Obtain BurstCtrl */ |
| 506 | burst_length = Get_NB32_DCT(dev, dct, 0x84) & 0x3; |
| 507 | |
| 508 | /* Load data into MRS word */ |
| 509 | ret |= (ppd & 0x1) << 12; |
| 510 | ret |= (wr_ap & 0x3) << 9; |
| 511 | ret |= (dll_reset & 0x1) << 8; |
| 512 | ret |= (test_mode & 0x1) << 7; |
| 513 | ret |= ((cas_latency & 0xe) >> 1) << 4; |
| 514 | ret |= ((cas_latency & 0x1) >> 0) << 2; |
| 515 | ret |= (read_burst_type & 0x1) << 3; |
| 516 | ret |= (burst_length & 0x3); |
| 517 | } else { |
| 518 | ret = 0x00000; |
| 519 | ret |= (MrsChipSel << 20); |
| 520 | |
| 521 | /* program MrsAddress[1:0]=burst length and control method |
| 522 | (BL):based on F2x[1,0]84[BurstCtrl] */ |
| 523 | dword = Get_NB32_DCT(dev, dct, 0x84); |
| 524 | ret |= dword & 3; |
| 525 | |
| 526 | /* program MrsAddress[3]=1 (BT):interleaved */ |
| 527 | ret |= 1 << 3; |
| 528 | |
| 529 | /* program MrsAddress[6:4,2]=read CAS latency |
| 530 | (CL):based on F2x[1,0]88[Tcl] */ |
| 531 | dword2 = Get_NB32_DCT(dev, dct, 0x88); |
| 532 | ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */ |
| 533 | ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */ |
| 534 | |
| 535 | /* program MrsAddress[12]=0 (PPD):slow exit */ |
| 536 | if (dword & (1 << 23)) |
| 537 | ret |= 1 << 12; |
| 538 | |
| 539 | /* program MrsAddress[11:9]=write recovery for auto-precharge |
| 540 | (WR):based on F2x[1,0]84[Twr] */ |
| 541 | ret |= ((dword >> 4) & 7) << 9; |
| 542 | |
| 543 | /* program MrsAddress[8]=1 (DLL):DLL reset |
| 544 | just issue DLL reset at first time */ |
| 545 | ret |= 1 << 8; |
| 546 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 547 | |
| 548 | return ret; |
| 549 | } |
| 550 | |
| 551 | static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct) |
| 552 | { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 553 | u32 dev = pDCTstat->dev_dct; |
| 554 | u32 dword; |
| 555 | |
| 556 | /*1.Program MrsAddress[10]=1 |
| 557 | 2.Set SendZQCmd=1 |
| 558 | */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 559 | dword = Get_NB32_DCT(dev, dct, 0x7C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 560 | dword &= ~0xFFFFFF; |
| 561 | dword |= 1 << 10; |
| 562 | dword |= 1 << SendZQCmd; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 563 | Set_NB32_DCT(dev, dct, 0x7C, dword); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 564 | |
| 565 | /* Wait for SendZQCmd=0 */ |
| 566 | do { |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 567 | dword = Get_NB32_DCT(dev, dct, 0x7C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 568 | } while (dword & (1 << SendZQCmd)); |
| 569 | |
| 570 | /* 4.Wait 512 MEMCLKs */ |
| 571 | mct_Wait(300); |
| 572 | } |
| 573 | |
| 574 | void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, |
| 575 | struct DCTStatStruc *pDCTstat, u8 dct) |
| 576 | { |
| 577 | u8 MrsChipSel; |
| 578 | u32 dword; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 579 | u32 dev = pDCTstat->dev_dct; |
| 580 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 581 | if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 582 | /* 3.Program F2x[1,0]7C[EnDramInit]=1 */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 583 | dword = Get_NB32_DCT(dev, dct, 0x7c); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 584 | dword |= 1 << EnDramInit; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 585 | Set_NB32_DCT(dev, dct, 0x7c, dword); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 586 | mct_DCTAccessDone(pDCTstat, dct); |
| 587 | |
| 588 | /* 4.wait 200us */ |
| 589 | mct_Wait(40000); |
| 590 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 591 | /* 5.Program F2x[1, 0]7C[DeassertMemRstX] = 1. */ |
| 592 | dword = Get_NB32_DCT(dev, dct, 0x7c); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 593 | dword |= 1 << DeassertMemRstX; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 594 | Set_NB32_DCT(dev, dct, 0x7c, dword); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 595 | |
| 596 | /* 6.wait 500us */ |
| 597 | mct_Wait(200000); |
| 598 | |
| 599 | /* 7.Program F2x[1,0]7C[AssertCke]=1 */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 600 | dword = Get_NB32_DCT(dev, dct, 0x7c); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 601 | dword |= 1 << AssertCke; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 602 | Set_NB32_DCT(dev, dct, 0x7c, dword); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 603 | |
| 604 | /* 8.wait 360ns */ |
| 605 | mct_Wait(80); |
| 606 | |
| 607 | /* The following steps are performed with registered DIMMs only and |
| 608 | * must be done for each chip select pair */ |
| 609 | if (pDCTstat->Status & (1 << SB_Registered)) |
| 610 | mct_DramControlReg_Init_D(pMCTstat, pDCTstat, dct); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 611 | |
| 612 | /* The following steps are performed with load reduced DIMMs only and |
| 613 | * must be done for each DIMM */ |
| 614 | // if (pDCTstat->Status & (1 << SB_LoadReduced)) |
| 615 | /* TODO |
| 616 | * Implement LRDIMM configuration |
| 617 | */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 618 | } |
| 619 | |
| 620 | /* The following steps are performed once for unbuffered DIMMs and once for each |
| 621 | * chip select on registered DIMMs: */ |
| 622 | for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel++) { |
| 623 | if (pDCTstat->CSPresent & (1 << MrsChipSel)) { |
| 624 | u32 EMRS; |
| 625 | /* 13.Send EMRS(2) */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 626 | EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 627 | EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); |
| 628 | mct_SendMrsCmd(pDCTstat, dct, EMRS); |
| 629 | /* 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 630 | EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 631 | EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); |
| 632 | mct_SendMrsCmd(pDCTstat, dct, EMRS); |
| 633 | /* 15.Send EMRS(1) */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 634 | EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 635 | EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); |
| 636 | mct_SendMrsCmd(pDCTstat, dct, EMRS); |
| 637 | /* 16.Send MRS with MrsAddress[8]=1(reset the DLL) */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 638 | EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 639 | EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); |
| 640 | mct_SendMrsCmd(pDCTstat, dct, EMRS); |
| 641 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 642 | if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 643 | if (!(pDCTstat->Status & (1 << SB_Registered))) |
| 644 | break; /* For UDIMM, only send MR commands once per channel */ |
| 645 | } |
Zheng Bao | dd676dd | 2011-01-20 02:09:24 +0000 | [diff] [blame] | 646 | if (pDCTstat->LogicalCPUID & (AMD_DR_Bx/* | AMD_RB_C0 */)) /* TODO: We dont support RB_C0 now. need to be added and tested. */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 647 | if (!(pDCTstat->Status & (1 << SB_Registered))) |
| 648 | MrsChipSel ++; |
| 649 | } |
| 650 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 651 | if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 652 | /* 17.Send two ZQCL commands */ |
| 653 | mct_SendZQCmd(pDCTstat, dct); |
| 654 | mct_SendZQCmd(pDCTstat, dct); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 655 | |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 656 | /* 18.Program F2x[1,0]7C[EnDramInit]=0 */ |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 657 | dword = Get_NB32_DCT(dev, dct, 0x7C); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 658 | dword &= ~(1 << EnDramInit); |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 659 | Set_NB32_DCT(dev, dct, 0x7C, dword); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 660 | mct_DCTAccessDone(pDCTstat, dct); |
| 661 | } |
| 662 | } |