0014-haswell-NRI-Add-timings-refresh-programming.patch 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. From b64d728bfe7c8ee44af252338257e95d87864659 Mon Sep 17 00:00:00 2001
  2. From: Angel Pons <th3fanbus@gmail.com>
  3. Date: Sat, 7 May 2022 20:59:58 +0200
  4. Subject: [PATCH 14/26] haswell NRI: Add timings/refresh programming
  5. Program the registers with timing and refresh parameters.
  6. Change-Id: Id2ea339d2c9ea8b56c71d6e88ec76949653ff5c2
  7. Signed-off-by: Angel Pons <th3fanbus@gmail.com>
  8. ---
  9. .../haswell/native_raminit/lookup_timings.c | 102 ++++++++
  10. .../haswell/native_raminit/raminit_native.h | 14 ++
  11. .../haswell/native_raminit/reg_structs.h | 93 +++++++
  12. .../haswell/native_raminit/timings_refresh.c | 233 +++++++++++++++++-
  13. .../intel/haswell/registers/mchbar.h | 12 +
  14. 5 files changed, 452 insertions(+), 2 deletions(-)
  15. diff --git a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
  16. index 038686c844..afe2c615d2 100644
  17. --- a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
  18. +++ b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
  19. @@ -60,3 +60,105 @@ uint32_t get_tXP(const uint32_t mem_clock_mhz)
  20. };
  21. return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  22. }
  23. +
  24. +static uint32_t get_lpddr_tCKE(const uint32_t mem_clock_mhz)
  25. +{
  26. + const struct timing_lookup lut[] = {
  27. + { 533, 4 },
  28. + { 666, 5 },
  29. + { fmax, 6 },
  30. + };
  31. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  32. +}
  33. +
  34. +static uint32_t get_ddr_tCKE(const uint32_t mem_clock_mhz)
  35. +{
  36. + const struct timing_lookup lut[] = {
  37. + { 533, 3 },
  38. + { 800, 4 },
  39. + { 933, 5 },
  40. + { 1200, 6 },
  41. + { fmax, 7 },
  42. + };
  43. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  44. +}
  45. +
  46. +uint32_t get_tCKE(const uint32_t mem_clock_mhz, const bool lpddr)
  47. +{
  48. + return lpddr ? get_lpddr_tCKE(mem_clock_mhz) : get_ddr_tCKE(mem_clock_mhz);
  49. +}
  50. +
  51. +uint32_t get_tXPDLL(const uint32_t mem_clock_mhz)
  52. +{
  53. + const struct timing_lookup lut[] = {
  54. + { 400, 10 },
  55. + { 533, 13 },
  56. + { 666, 16 },
  57. + { 800, 20 },
  58. + { 933, 23 },
  59. + { 1066, 26 },
  60. + { 1200, 29 },
  61. + { fmax, 32 },
  62. + };
  63. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  64. +}
  65. +
  66. +uint32_t get_tAONPD(const uint32_t mem_clock_mhz)
  67. +{
  68. + const struct timing_lookup lut[] = {
  69. + { 400, 4 },
  70. + { 533, 5 },
  71. + { 666, 6 },
  72. + { 800, 7 }, /* SNB had 8 */
  73. + { 933, 8 },
  74. + { 1066, 10 },
  75. + { 1200, 11 },
  76. + { fmax, 12 },
  77. + };
  78. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  79. +}
  80. +
  81. +uint32_t get_tMOD(const uint32_t mem_clock_mhz)
  82. +{
  83. + const struct timing_lookup lut[] = {
  84. + { 800, 12 },
  85. + { 933, 14 },
  86. + { 1066, 16 },
  87. + { 1200, 18 },
  88. + { fmax, 20 },
  89. + };
  90. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  91. +}
  92. +
  93. +uint32_t get_tXS_offset(const uint32_t mem_clock_mhz)
  94. +{
  95. + return DIV_ROUND_UP(mem_clock_mhz, 100);
  96. +}
  97. +
  98. +static uint32_t get_lpddr_tZQOPER(const uint32_t mem_clock_mhz)
  99. +{
  100. + return (mem_clock_mhz * 360) / 1000;
  101. +}
  102. +
  103. +static uint32_t get_ddr_tZQOPER(const uint32_t mem_clock_mhz)
  104. +{
  105. + const struct timing_lookup lut[] = {
  106. + { 800, 256 },
  107. + { 933, 299 },
  108. + { 1066, 342 },
  109. + { 1200, 384 },
  110. + { fmax, 427 },
  111. + };
  112. + return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
  113. +}
  114. +
  115. +/* tZQOPER defines the period required for ZQCL after SR exit */
  116. +uint32_t get_tZQOPER(const uint32_t mem_clock_mhz, const bool lpddr)
  117. +{
  118. + return lpddr ? get_lpddr_tZQOPER(mem_clock_mhz) : get_ddr_tZQOPER(mem_clock_mhz);
  119. +}
  120. +
  121. +uint32_t get_tZQCS(const uint32_t mem_clock_mhz, const bool lpddr)
  122. +{
  123. + return DIV_ROUND_UP(get_tZQOPER(mem_clock_mhz, lpddr), 4);
  124. +}
  125. diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  126. index aa86b9aa39..cd1f2eb2a5 100644
  127. --- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  128. +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  129. @@ -155,6 +155,12 @@ struct sysinfo {
  130. uint8_t cke_cmd_pi_code[NUM_CHANNELS][NUM_GROUPS];
  131. uint8_t cmd_north_pi_code[NUM_CHANNELS][NUM_GROUPS];
  132. uint8_t cmd_south_pi_code[NUM_CHANNELS][NUM_GROUPS];
  133. +
  134. + union tc_bank_reg tc_bank[NUM_CHANNELS];
  135. + union tc_bank_rank_a_reg tc_bankrank_a[NUM_CHANNELS];
  136. + union tc_bank_rank_b_reg tc_bankrank_b[NUM_CHANNELS];
  137. + union tc_bank_rank_c_reg tc_bankrank_c[NUM_CHANNELS];
  138. + union tc_bank_rank_d_reg tc_bankrank_d[NUM_CHANNELS];
  139. };
  140. static inline bool is_hsw_ult(void)
  141. @@ -200,6 +206,14 @@ enum raminit_status configure_mc(struct sysinfo *ctrl);
  142. void configure_timings(struct sysinfo *ctrl);
  143. void configure_refresh(struct sysinfo *ctrl);
  144. +uint32_t get_tCKE(uint32_t mem_clock_mhz, bool lpddr);
  145. +uint32_t get_tXPDLL(uint32_t mem_clock_mhz);
  146. +uint32_t get_tAONPD(uint32_t mem_clock_mhz);
  147. +uint32_t get_tMOD(uint32_t mem_clock_mhz);
  148. +uint32_t get_tXS_offset(uint32_t mem_clock_mhz);
  149. +uint32_t get_tZQOPER(uint32_t mem_clock_mhz, bool lpddr);
  150. +uint32_t get_tZQCS(uint32_t mem_clock_mhz, bool lpddr);
  151. +
  152. enum raminit_status wait_for_first_rcomp(void);
  153. uint8_t get_rx_bias(const struct sysinfo *ctrl);
  154. diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h
  155. index d11cda4b3d..70487e1640 100644
  156. --- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h
  157. +++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h
  158. @@ -335,6 +335,99 @@ union mcscheds_cbit_reg {
  159. uint32_t raw;
  160. };
  161. +union tc_bank_reg {
  162. + struct __packed {
  163. + uint32_t tRCD : 5; // Bits 4:0
  164. + uint32_t tRP : 5; // Bits 9:5
  165. + uint32_t tRAS : 6; // Bits 15:10
  166. + uint32_t tRDPRE : 4; // Bits 19:16
  167. + uint32_t tWRPRE : 6; // Bits 25:20
  168. + uint32_t tRRD : 4; // Bits 29:26
  169. + uint32_t tRPab_ext : 2; // Bits 31:30
  170. + };
  171. + uint32_t raw;
  172. +};
  173. +
  174. +union tc_bank_rank_a_reg {
  175. + struct __packed {
  176. + uint32_t tCKE : 4; // Bits 3:0
  177. + uint32_t tFAW : 8; // Bits 11:4
  178. + uint32_t tRDRD_sr : 3; // Bits 14:12
  179. + uint32_t tRDRD_dr : 4; // Bits 18:15
  180. + uint32_t tRDRD_dd : 4; // Bits 22:19
  181. + uint32_t tRDPDEN : 5; // Bits 27:23
  182. + uint32_t : 1; // Bits 28:28
  183. + uint32_t cmd_3st_dis : 1; // Bits 29:29
  184. + uint32_t cmd_stretch : 2; // Bits 31:30
  185. + };
  186. + uint32_t raw;
  187. +};
  188. +
  189. +union tc_bank_rank_b_reg {
  190. + struct __packed {
  191. + uint32_t tWRRD_sr : 6; // Bits 5:0
  192. + uint32_t tWRRD_dr : 4; // Bits 9:6
  193. + uint32_t tWRRD_dd : 4; // Bits 13:10
  194. + uint32_t tWRWR_sr : 3; // Bits 16:14
  195. + uint32_t tWRWR_dr : 4; // Bits 20:17
  196. + uint32_t tWRWR_dd : 4; // Bits 24:21
  197. + uint32_t tWRPDEN : 6; // Bits 30:25
  198. + uint32_t dec_wrd : 1; // Bits 31:31
  199. + };
  200. + uint32_t raw;
  201. +};
  202. +
  203. +union tc_bank_rank_c_reg {
  204. + struct __packed {
  205. + uint32_t tXPDLL : 6; // Bits 5:0
  206. + uint32_t tXP : 4; // Bits 9:6
  207. + uint32_t tAONPD : 4; // Bits 13:10
  208. + uint32_t tRDWR_sr : 5; // Bits 18:14
  209. + uint32_t tRDWR_dr : 5; // Bits 23:19
  210. + uint32_t tRDWR_dd : 5; // Bits 28:24
  211. + uint32_t : 3; // Bits 31:29
  212. + };
  213. + uint32_t raw;
  214. +};
  215. +
  216. +/* NOTE: Non-ULT only implements the lower 21 bits (odt_write_delay is 2 bits) */
  217. +union tc_bank_rank_d_reg {
  218. + struct __packed {
  219. + uint32_t tAA : 5; // Bits 4:0
  220. + uint32_t tCWL : 5; // Bits 9:5
  221. + uint32_t tCPDED : 2; // Bits 11:10
  222. + uint32_t tPRPDEN : 2; // Bits 13:12
  223. + uint32_t odt_read_delay : 3; // Bits 16:14
  224. + uint32_t odt_read_duration : 2; // Bits 18:17
  225. + uint32_t odt_write_duration : 3; // Bits 21:19
  226. + uint32_t odt_write_delay : 3; // Bits 24:22
  227. + uint32_t odt_always_rank_0 : 1; // Bits 25:25
  228. + uint32_t cmd_delay : 2; // Bits 27:26
  229. + uint32_t : 4; // Bits 31:28
  230. + };
  231. + uint32_t raw;
  232. +};
  233. +
  234. +union tc_rftp_reg {
  235. + struct __packed {
  236. + uint32_t tREFI : 16; // Bits 15:0
  237. + uint32_t tRFC : 9; // Bits 24:16
  238. + uint32_t tREFIx9 : 7; // Bits 31:25
  239. + };
  240. + uint32_t raw;
  241. +};
  242. +
  243. +union tc_srftp_reg {
  244. + struct __packed {
  245. + uint32_t tXSDLL : 12; // Bits 11:0
  246. + uint32_t tXS_offset : 4; // Bits 15:12
  247. + uint32_t tZQOPER : 10; // Bits 25:16
  248. + uint32_t : 2; // Bits 27:26
  249. + uint32_t tMOD : 4; // Bits 31:28
  250. + };
  251. + uint32_t raw;
  252. +};
  253. +
  254. union mcmain_command_rate_limit_reg {
  255. struct __packed {
  256. uint32_t enable_cmd_limit : 1; // Bits 0:0
  257. diff --git a/src/northbridge/intel/haswell/native_raminit/timings_refresh.c b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c
  258. index a9d960f31b..20a05b359b 100644
  259. --- a/src/northbridge/intel/haswell/native_raminit/timings_refresh.c
  260. +++ b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c
  261. @@ -1,13 +1,242 @@
  262. /* SPDX-License-Identifier: GPL-2.0-or-later */
  263. +#include <assert.h>
  264. +#include <commonlib/clamp.h>
  265. +#include <console/console.h>
  266. +#include <delay.h>
  267. +#include <device/pci_ops.h>
  268. +#include <northbridge/intel/haswell/haswell.h>
  269. +
  270. #include "raminit_native.h"
  271. +#define BL 8 /* Burst length */
  272. +#define tCCD 4
  273. +#define tRPRE 1
  274. +#define tWPRE 1
  275. +#define tDLLK 512
  276. +
  277. +static bool is_sodimm(const enum spd_dimm_type_ddr3 type)
  278. +{
  279. + return type == SPD_DDR3_DIMM_TYPE_SO_DIMM || type == SPD_DDR3_DIMM_TYPE_72B_SO_UDIMM;
  280. +}
  281. +
  282. +static uint8_t get_odt_stretch(const struct sysinfo *const ctrl)
  283. +{
  284. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  285. + /* Only stretch with 2 DIMMs per channel */
  286. + if (ctrl->dpc[channel] != 2)
  287. + continue;
  288. +
  289. + const struct raminit_dimm_info *dimms = ctrl->dimms[channel];
  290. +
  291. + /* Only stretch when using SO-DIMMs */
  292. + if (!is_sodimm(dimms[0].data.dimm_type) || !is_sodimm(dimms[1].data.dimm_type))
  293. + continue;
  294. +
  295. + /* Only stretch with mismatched card types */
  296. + if (dimms[0].data.reference_card == dimms[1].data.reference_card)
  297. + continue;
  298. +
  299. + /* Stretch if one SO-DIMM is card F */
  300. + for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) {
  301. + if (dimms[slot].data.reference_card == 5)
  302. + return 1;
  303. + }
  304. + }
  305. + return 0;
  306. +}
  307. +
  308. +static union tc_bank_reg make_tc_bank(struct sysinfo *const ctrl)
  309. +{
  310. + return (union tc_bank_reg) {
  311. + .tRCD = ctrl->tRCD,
  312. + .tRP = ctrl->tRP,
  313. + .tRAS = ctrl->tRAS,
  314. + .tRDPRE = ctrl->tRTP,
  315. + .tWRPRE = 4 + ctrl->tCWL + ctrl->tWR,
  316. + .tRRD = ctrl->tRRD,
  317. + .tRPab_ext = 0, /** TODO: For LPDDR, this is ctrl->tRPab - ctrl->tRP **/
  318. + };
  319. +}
  320. +
  321. +static union tc_bank_rank_a_reg make_tc_bankrank_a(struct sysinfo *ctrl, uint8_t odt_stretch)
  322. +{
  323. + /* Use 3N mode for DDR during training, but always use 1N mode for LPDDR */
  324. + const uint32_t tCMD = ctrl->lpddr ? 0 : 3;
  325. + const uint32_t tRDRD_drdd = BL / 2 + 1 + tRPRE + odt_stretch + !!ctrl->lpddr;
  326. +
  327. + return (union tc_bank_rank_a_reg) {
  328. + .tCKE = get_tCKE(ctrl->mem_clock_mhz, ctrl->lpddr),
  329. + .tFAW = ctrl->tFAW,
  330. + .tRDRD_sr = tCCD,
  331. + .tRDRD_dr = tRDRD_drdd,
  332. + .tRDRD_dd = tRDRD_drdd,
  333. + .tRDPDEN = ctrl->tAA + BL / 2 + 1,
  334. + .cmd_3st_dis = 1, /* Disable command tri-state before training */
  335. + .cmd_stretch = tCMD,
  336. + };
  337. +}
  338. +
  339. +static union tc_bank_rank_b_reg make_tc_bankrank_b(struct sysinfo *const ctrl)
  340. +{
  341. + const uint8_t tWRRD_drdd = ctrl->tCWL - ctrl->tAA + BL / 2 + 2 + tRPRE;
  342. + const uint8_t tWRWR_drdd = BL / 2 + 2 + tWPRE;
  343. +
  344. + return (union tc_bank_rank_b_reg) {
  345. + .tWRRD_sr = tCCD + ctrl->tCWL + ctrl->tWTR + 2,
  346. + .tWRRD_dr = ctrl->lpddr ? 8 : tWRRD_drdd,
  347. + .tWRRD_dd = ctrl->lpddr ? 8 : tWRRD_drdd,
  348. + .tWRWR_sr = tCCD,
  349. + .tWRWR_dr = tWRWR_drdd,
  350. + .tWRWR_dd = tWRWR_drdd,
  351. + .tWRPDEN = ctrl->tWR + ctrl->tCWL + BL / 2,
  352. + .dec_wrd = ctrl->tCWL >= 6,
  353. + };
  354. +}
  355. +
  356. +static uint32_t get_tRDWR_sr(const struct sysinfo *ctrl)
  357. +{
  358. + if (ctrl->lpddr) {
  359. + const uint32_t tdqsck_max = DIV_ROUND_UP(5500, ctrl->qclkps * 2);
  360. + return ctrl->tAA - ctrl->tCWL + tCCD + tWPRE + tdqsck_max + 1;
  361. + } else {
  362. + const bool fast_clock = ctrl->mem_clock_mhz > 666;
  363. + return ctrl->tAA - ctrl->tCWL + tCCD + tWPRE + 2 + fast_clock;
  364. + }
  365. +}
  366. +
  367. +static union tc_bank_rank_c_reg make_tc_bankrank_c(struct sysinfo *ctrl, uint8_t odt_stretch)
  368. +{
  369. + const uint32_t tRDWR_sr = get_tRDWR_sr(ctrl);
  370. + const uint32_t tRDWR_drdd = tRDWR_sr + odt_stretch;
  371. +
  372. + return (union tc_bank_rank_c_reg) {
  373. + .tXPDLL = get_tXPDLL(ctrl->mem_clock_mhz),
  374. + .tXP = MAX(ctrl->tXP, 7), /* Use a higher tXP for training */
  375. + .tAONPD = get_tAONPD(ctrl->mem_clock_mhz),
  376. + .tRDWR_sr = tRDWR_sr,
  377. + .tRDWR_dr = tRDWR_drdd,
  378. + .tRDWR_dd = tRDWR_drdd,
  379. + };
  380. +}
  381. +
  382. +static union tc_bank_rank_d_reg make_tc_bankrank_d(struct sysinfo *ctrl, uint8_t odt_stretch)
  383. +{
  384. + const uint32_t odt_rd_delay = ctrl->tAA - ctrl->tCWL;
  385. + if (!ctrl->lpddr) {
  386. + return (union tc_bank_rank_d_reg) {
  387. + .tAA = ctrl->tAA,
  388. + .tCWL = ctrl->tCWL,
  389. + .tCPDED = 1,
  390. + .tPRPDEN = 1,
  391. + .odt_read_delay = odt_rd_delay,
  392. + .odt_read_duration = odt_stretch,
  393. + };
  394. + }
  395. +
  396. + /* tCWL has 1 extra clock because of tDQSS, subtract it here */
  397. + const uint32_t tCWL_lpddr = ctrl->tCWL - 1;
  398. + const uint32_t odt_wr_delay = tCWL_lpddr + DIV_ROUND_UP(3500, ctrl->qclkps * 2);
  399. + const uint32_t odt_wr_duration = DIV_ROUND_UP(3500 - 1750, ctrl->qclkps * 2) + 1;
  400. +
  401. + return (union tc_bank_rank_d_reg) {
  402. + .tAA = ctrl->tAA,
  403. + .tCWL = tCWL_lpddr,
  404. + .tCPDED = 2, /* Required by JEDEC LPDDR3 spec */
  405. + .tPRPDEN = 1,
  406. + .odt_read_delay = odt_rd_delay,
  407. + .odt_read_duration = odt_stretch,
  408. + .odt_write_delay = odt_wr_delay,
  409. + .odt_write_duration = odt_wr_duration,
  410. + .odt_always_rank_0 = ctrl->lpddr_dram_odt
  411. + };
  412. +}
  413. +
  414. +/* ZQCS period values, in (tREFI * 128) units */
  415. +#define ZQCS_PERIOD_DDR3 128 /* tREFI * 128 = 7.8 us * 128 = 1ms */
  416. +#define ZQCS_PERIOD_LPDDR3 256 /* tREFI * 128 = 3.9 us * 128 = 0.5ms */
  417. +
  418. +static uint32_t make_tc_zqcal(const struct sysinfo *const ctrl)
  419. +{
  420. + const uint32_t zqcs_period = ctrl->lpddr ? ZQCS_PERIOD_LPDDR3 : ZQCS_PERIOD_DDR3;
  421. + const uint32_t tZQCS = get_tZQCS(ctrl->mem_clock_mhz, ctrl->lpddr);
  422. + return tZQCS << (is_hsw_ult() ? 10 : 8) | zqcs_period;
  423. +}
  424. +
  425. +static union tc_rftp_reg make_tc_rftp(const struct sysinfo *const ctrl)
  426. +{
  427. + /*
  428. + * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow
  429. + * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024.
  430. + */
  431. + return (union tc_rftp_reg) {
  432. + .tREFI = ctrl->tREFI,
  433. + .tRFC = ctrl->tRFC,
  434. + .tREFIx9 = ctrl->tREFI * 89 / 10240,
  435. + };
  436. +}
  437. +
  438. +static union tc_srftp_reg make_tc_srftp(const struct sysinfo *const ctrl)
  439. +{
  440. + return (union tc_srftp_reg) {
  441. + .tXSDLL = tDLLK,
  442. + .tXS_offset = get_tXS_offset(ctrl->mem_clock_mhz),
  443. + .tZQOPER = get_tZQOPER(ctrl->mem_clock_mhz, ctrl->lpddr),
  444. + .tMOD = get_tMOD(ctrl->mem_clock_mhz) - 8,
  445. + };
  446. +}
  447. +
  448. void configure_timings(struct sysinfo *ctrl)
  449. {
  450. - /** TODO: Stub **/
  451. + if (ctrl->lpddr)
  452. + die("%s: Missing support for LPDDR\n");
  453. +
  454. + const uint8_t odt_stretch = get_odt_stretch(ctrl);
  455. + const union tc_bank_reg tc_bank = make_tc_bank(ctrl);
  456. + const union tc_bank_rank_a_reg tc_bank_rank_a = make_tc_bankrank_a(ctrl, odt_stretch);
  457. + const union tc_bank_rank_b_reg tc_bank_rank_b = make_tc_bankrank_b(ctrl);
  458. + const union tc_bank_rank_c_reg tc_bank_rank_c = make_tc_bankrank_c(ctrl, odt_stretch);
  459. + const union tc_bank_rank_d_reg tc_bank_rank_d = make_tc_bankrank_d(ctrl, odt_stretch);
  460. +
  461. + const uint8_t wr_delay = tc_bank_rank_b.dec_wrd + 1;
  462. + uint8_t sc_wr_add_delay = 0;
  463. + sc_wr_add_delay |= wr_delay << 0;
  464. + sc_wr_add_delay |= wr_delay << 2;
  465. + sc_wr_add_delay |= wr_delay << 4;
  466. + sc_wr_add_delay |= wr_delay << 6;
  467. +
  468. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  469. + if (!does_ch_exist(ctrl, channel))
  470. + continue;
  471. +
  472. + ctrl->tc_bank[channel] = tc_bank;
  473. + ctrl->tc_bankrank_a[channel] = tc_bank_rank_a;
  474. + ctrl->tc_bankrank_b[channel] = tc_bank_rank_b;
  475. + ctrl->tc_bankrank_c[channel] = tc_bank_rank_c;
  476. + ctrl->tc_bankrank_d[channel] = tc_bank_rank_d;
  477. +
  478. + mchbar_write32(TC_BANK_ch(channel), ctrl->tc_bank[channel].raw);
  479. + mchbar_write32(TC_BANK_RANK_A_ch(channel), ctrl->tc_bankrank_a[channel].raw);
  480. + mchbar_write32(TC_BANK_RANK_B_ch(channel), ctrl->tc_bankrank_b[channel].raw);
  481. + mchbar_write32(TC_BANK_RANK_C_ch(channel), ctrl->tc_bankrank_c[channel].raw);
  482. + mchbar_write32(TC_BANK_RANK_D_ch(channel), ctrl->tc_bankrank_d[channel].raw);
  483. + mchbar_write8(SC_WR_ADD_DELAY_ch(channel), sc_wr_add_delay);
  484. + }
  485. }
  486. void configure_refresh(struct sysinfo *ctrl)
  487. {
  488. - /** TODO: Stub **/
  489. + const union tc_srftp_reg tc_srftp = make_tc_srftp(ctrl);
  490. + const union tc_rftp_reg tc_rftp = make_tc_rftp(ctrl);
  491. + const uint32_t tc_zqcal = make_tc_zqcal(ctrl);
  492. +
  493. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  494. + if (!does_ch_exist(ctrl, channel))
  495. + continue;
  496. +
  497. + mchbar_setbits32(TC_RFP_ch(channel), 0xff);
  498. + mchbar_write32(TC_RFTP_ch(channel), tc_rftp.raw);
  499. + mchbar_write32(TC_SRFTP_ch(channel), tc_srftp.raw);
  500. + mchbar_write32(TC_ZQCAL_ch(channel), tc_zqcal);
  501. + }
  502. }
  503. diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h
  504. index 4c3f399b5d..2acc5cbbc8 100644
  505. --- a/src/northbridge/intel/haswell/registers/mchbar.h
  506. +++ b/src/northbridge/intel/haswell/registers/mchbar.h
  507. @@ -86,9 +86,21 @@
  508. #define DDR_COMP_VSSHI_CONTROL 0x3a24
  509. /* MCMAIN per-channel */
  510. +#define TC_BANK_ch(ch) _MCMAIN_C(0x4000, ch)
  511. +#define TC_BANK_RANK_A_ch(ch) _MCMAIN_C(0x4004, ch)
  512. +#define TC_BANK_RANK_B_ch(ch) _MCMAIN_C(0x4008, ch)
  513. +#define TC_BANK_RANK_C_ch(ch) _MCMAIN_C(0x400c, ch)
  514. #define COMMAND_RATE_LIMIT_ch(ch) _MCMAIN_C(0x4010, ch)
  515. +#define TC_BANK_RANK_D_ch(ch) _MCMAIN_C(0x4014, ch)
  516. +#define SC_ROUNDT_LAT_ch(ch) _MCMAIN_C(0x4024, ch)
  517. +#define SC_WR_ADD_DELAY_ch(ch) _MCMAIN_C(0x40d0, ch)
  518. +
  519. +#define TC_ZQCAL_ch(ch) _MCMAIN_C(0x4290, ch)
  520. +#define TC_RFP_ch(ch) _MCMAIN_C(0x4294, ch)
  521. +#define TC_RFTP_ch(ch) _MCMAIN_C(0x4298, ch)
  522. #define MC_INIT_STATE_ch(ch) _MCMAIN_C(0x42a0, ch)
  523. +#define TC_SRFTP_ch(ch) _MCMAIN_C(0x42a4, ch)
  524. /* MCMAIN broadcast */
  525. #define MCSCHEDS_CBIT 0x4c20
  526. --
  527. 2.39.2