0013-haswell-NRI-Add-read-MPR-training.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. From e263f0d2e9d6d016d603342651da261bbcb6af1f Mon Sep 17 00:00:00 2001
  2. From: Angel Pons <th3fanbus@gmail.com>
  3. Date: Sun, 8 May 2022 11:35:49 +0200
  4. Subject: [PATCH 13/20] haswell NRI: Add read MPR training
  5. Implement read training using DDR3 MPR (Multi-Purpose Register).
  6. Change-Id: Id17cb2c4c399ac9bcc937b595b58f863c152461b
  7. Signed-off-by: Angel Pons <th3fanbus@gmail.com>
  8. ---
  9. .../intel/haswell/native_raminit/Makefile.mk | 1 +
  10. .../haswell/native_raminit/raminit_main.c | 1 +
  11. .../haswell/native_raminit/raminit_native.h | 4 +
  12. .../haswell/native_raminit/train_read_mpr.c | 241 ++++++++++++++++++
  13. .../intel/haswell/registers/mchbar.h | 2 +-
  14. 5 files changed, 248 insertions(+), 1 deletion(-)
  15. create mode 100644 src/northbridge/intel/haswell/native_raminit/train_read_mpr.c
  16. diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk
  17. index e2fbfb4211..c442be0728 100644
  18. --- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk
  19. +++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk
  20. @@ -16,4 +16,5 @@ romstage-y += setup_wdb.c
  21. romstage-y += spd_bitmunching.c
  22. romstage-y += testing_io.c
  23. romstage-y += timings_refresh.c
  24. +romstage-y += train_read_mpr.c
  25. romstage-y += train_receive_enable.c
  26. diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
  27. index 7d444659c3..264d1468f5 100644
  28. --- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c
  29. +++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
  30. @@ -61,6 +61,7 @@ static const struct task_entry cold_boot[] = {
  31. { do_jedec_init, true, "JEDECINIT", },
  32. { pre_training, true, "PRETRAIN", },
  33. { train_receive_enable, true, "RCVET", },
  34. + { train_read_mpr, true, "RDMPRT", },
  35. };
  36. /* Return a generic stepping value to make stepping checks simpler */
  37. diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  38. index 5242b16f28..49e9214656 100644
  39. --- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  40. +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
  41. @@ -27,6 +27,8 @@
  42. /* Always use 12 legs for emphasis (not trained) */
  43. #define TXEQFULLDRV (3 << 4)
  44. +#define LOOPCOUNT_INFINITE 0xff
  45. +
  46. /* DDR3 mode register bits */
  47. #define MR0_DLL_RESET BIT(8)
  48. @@ -212,6 +214,7 @@ enum raminit_status {
  49. RAMINIT_STATUS_POLL_TIMEOUT,
  50. RAMINIT_STATUS_REUT_ERROR,
  51. RAMINIT_STATUS_RCVEN_FAILURE,
  52. + RAMINIT_STATUS_RMPR_FAILURE,
  53. RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/
  54. };
  55. @@ -433,6 +436,7 @@ enum raminit_status configure_mc(struct sysinfo *ctrl);
  56. enum raminit_status configure_memory_map(struct sysinfo *ctrl);
  57. enum raminit_status do_jedec_init(struct sysinfo *ctrl);
  58. enum raminit_status train_receive_enable(struct sysinfo *ctrl);
  59. +enum raminit_status train_read_mpr(struct sysinfo *ctrl);
  60. void configure_timings(struct sysinfo *ctrl);
  61. void configure_refresh(struct sysinfo *ctrl);
  62. diff --git a/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c b/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c
  63. new file mode 100644
  64. index 0000000000..ade1e36148
  65. --- /dev/null
  66. +++ b/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c
  67. @@ -0,0 +1,241 @@
  68. +/* SPDX-License-Identifier: GPL-2.0-or-later */
  69. +
  70. +#include <commonlib/bsd/clamp.h>
  71. +#include <console/console.h>
  72. +#include <delay.h>
  73. +#include <northbridge/intel/haswell/haswell.h>
  74. +#include <types.h>
  75. +
  76. +#include "raminit_native.h"
  77. +#include "ranges.h"
  78. +
  79. +#define RMPR_START (-32)
  80. +#define RMPR_STOP (32)
  81. +#define RMPR_STEP 1
  82. +
  83. +#define RMPR_MIN_WIDTH 12
  84. +
  85. +#define RMPR_PLOT RAM_DEBUG
  86. +
  87. +/*
  88. + * Clear rx_training_mode. For LPDDR, we first need to disable odt_samp_extend_en,
  89. + * then disable rx_training_mode, and finally re-enable odt_samp_extend_en.
  90. + */
  91. +static void clear_rx_training_mode(struct sysinfo *ctrl, const uint8_t channel)
  92. +{
  93. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++)
  94. + mchbar_write32(DQ_CONTROL_2(channel, byte), ctrl->dq_control_2[channel][byte]);
  95. +
  96. + if (ctrl->lpddr) {
  97. + union ddr_data_control_0_reg data_control_0 = {
  98. + .raw = mchbar_read32(DDR_DATA_ch_CONTROL_0(channel)),
  99. + };
  100. + data_control_0.odt_samp_extend_en = 0;
  101. + mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw);
  102. + tick_delay(1);
  103. + data_control_0.rx_training_mode = 0;
  104. + mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw);
  105. + tick_delay(1);
  106. + }
  107. + mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), ctrl->dq_control_0[channel]);
  108. +}
  109. +
  110. +static void set_rxdqs_edges_to_midpoint(struct sysinfo *ctrl)
  111. +{
  112. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  113. + if (!does_ch_exist(ctrl, channel))
  114. + continue;
  115. +
  116. + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) {
  117. + if (!rank_in_ch(ctrl, rank, channel))
  118. + continue;
  119. +
  120. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++)
  121. + update_rxt(ctrl, channel, rank, byte, RXT_RXDQS_BOTH, 32);
  122. + }
  123. + }
  124. +}
  125. +
  126. +static void enter_mpr_train_ddr_mode(struct sysinfo *ctrl, const uint8_t rank)
  127. +{
  128. + /* Program MR3 and mask RAS/WE to prevent scheduler from issuing non-read commands */
  129. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  130. + if (!rank_in_ch(ctrl, rank, channel))
  131. + continue;
  132. +
  133. + if (!ctrl->lpddr)
  134. + reut_issue_mrs(ctrl, channel, BIT(rank), 3, 1 << 2);
  135. +
  136. + union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = {
  137. + .raw = mchbar_read32(REUT_ch_MISC_ODT_CTRL(channel)),
  138. + };
  139. + reut_misc_odt_ctrl.mpr_train_ddr_on = 1;
  140. + mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), reut_misc_odt_ctrl.raw);
  141. + }
  142. +}
  143. +
  144. +static void leave_mpr_train_ddr_mode(struct sysinfo *ctrl, const uint8_t rank)
  145. +{
  146. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  147. + if (!rank_in_ch(ctrl, rank, channel))
  148. + continue;
  149. +
  150. + /*
  151. + * The mpr_train_ddr_on bit will force a special command.
  152. + * Therefore, clear it before issuing the MRS command.
  153. + */
  154. + union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = {
  155. + .raw = mchbar_read32(REUT_ch_MISC_ODT_CTRL(channel)),
  156. + };
  157. + reut_misc_odt_ctrl.mpr_train_ddr_on = 0;
  158. + mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), reut_misc_odt_ctrl.raw);
  159. + if (!ctrl->lpddr)
  160. + reut_issue_mrs(ctrl, channel, BIT(rank), 3, 0 << 2);
  161. + }
  162. +}
  163. +
  164. +enum raminit_status train_read_mpr(struct sysinfo *ctrl)
  165. +{
  166. + set_rxdqs_edges_to_midpoint(ctrl);
  167. + clear_data_offset_train_all(ctrl);
  168. + setup_io_test_mpr(ctrl, ctrl->chanmap, LOOPCOUNT_INFINITE, NSOE);
  169. + enum raminit_status status = RAMINIT_STATUS_SUCCESS;
  170. + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) {
  171. + if (!does_rank_exist(ctrl, rank))
  172. + continue;
  173. +
  174. + printk(BIOS_DEBUG, "Rank %u\n", rank);
  175. + printk(RMPR_PLOT, "Channel");
  176. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  177. + if (!rank_in_ch(ctrl, rank, channel))
  178. + continue;
  179. +
  180. + printk(RMPR_PLOT, "\t%u\t\t", channel);
  181. + }
  182. + printk(RMPR_PLOT, "\nByte");
  183. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  184. + if (!rank_in_ch(ctrl, rank, channel))
  185. + continue;
  186. +
  187. + printk(RMPR_PLOT, "\t");
  188. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++)
  189. + printk(RMPR_PLOT, "%u ", byte);
  190. + }
  191. + enter_mpr_train_ddr_mode(ctrl, rank);
  192. + struct linear_train_data region_data[NUM_CHANNELS][NUM_LANES] = { 0 };
  193. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++)
  194. + select_reut_ranks(ctrl, channel, BIT(rank));
  195. +
  196. + printk(RMPR_PLOT, "\nDqsDelay\n");
  197. + int8_t dqs_delay;
  198. + for (dqs_delay = RMPR_START; dqs_delay < RMPR_STOP; dqs_delay += RMPR_STEP) {
  199. + printk(RMPR_PLOT, "% 5d", dqs_delay);
  200. + const enum regfile_mode regfile = REG_FILE_USE_START;
  201. + /* Looks like MRC uses rank 0 here, but it feels wrong */
  202. + change_1d_margin_multicast(ctrl, RdT, dqs_delay, rank, false, regfile);
  203. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  204. + if (!rank_in_ch(ctrl, rank, channel))
  205. + continue;
  206. +
  207. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) {
  208. + union ddr_data_control_2_reg data_control_2 = {
  209. + .raw = ctrl->dq_control_2[channel][byte],
  210. + };
  211. + data_control_2.force_bias_on = 1;
  212. + data_control_2.force_rx_on = 1;
  213. + data_control_2.leaker_comp = 0;
  214. + mchbar_write32(DQ_CONTROL_2(channel, byte),
  215. + data_control_2.raw);
  216. + }
  217. + union ddr_data_control_0_reg data_control_0 = {
  218. + .raw = ctrl->dq_control_0[channel],
  219. + };
  220. + data_control_0.rx_training_mode = 1;
  221. + data_control_0.force_odt_on = !ctrl->lpddr;
  222. + data_control_0.en_read_preamble = 0;
  223. + data_control_0.odt_samp_extend_en = ctrl->lpddr;
  224. + const uint32_t reg_offset = DDR_DATA_ch_CONTROL_0(channel);
  225. + mchbar_write32(reg_offset, data_control_0.raw);
  226. + }
  227. + run_mpr_io_test(false);
  228. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  229. + if (!rank_in_ch(ctrl, rank, channel))
  230. + continue;
  231. +
  232. + printk(RMPR_PLOT, "\t");
  233. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) {
  234. + uint32_t fb = get_data_train_feedback(channel, byte);
  235. + const bool pass = fb == 1;
  236. + printk(RMPR_PLOT, pass ? ". " : "# ");
  237. + linear_record_pass(
  238. + &region_data[channel][byte],
  239. + pass,
  240. + dqs_delay,
  241. + RMPR_START,
  242. + RMPR_STEP);
  243. + }
  244. + }
  245. + printk(RMPR_PLOT, "\n");
  246. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  247. + if (!rank_in_ch(ctrl, rank, channel))
  248. + continue;
  249. +
  250. + clear_rx_training_mode(ctrl, channel);
  251. + }
  252. + io_reset();
  253. + }
  254. + printk(RMPR_PLOT, "\n");
  255. + leave_mpr_train_ddr_mode(ctrl, rank);
  256. + clear_data_offset_train_all(ctrl);
  257. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  258. + if (!rank_in_ch(ctrl, rank, channel))
  259. + continue;
  260. +
  261. + printk(BIOS_DEBUG, "C%u.R%u: \tLeft\tRight\tWidth\tCenter\tRxDqsPN\n",
  262. + channel, rank);
  263. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++) {
  264. + struct linear_train_data *data = &region_data[channel][byte];
  265. + const int32_t lwidth = range_width(data->largest);
  266. + if (lwidth <= RMPR_MIN_WIDTH) {
  267. + printk(BIOS_ERR,
  268. + "Bad eye (lwidth %d <= min %d) for byte %u\n",
  269. + lwidth, RMPR_MIN_WIDTH, byte);
  270. + status = RAMINIT_STATUS_RMPR_FAILURE;
  271. + }
  272. + /*
  273. + * The MPR center may not be ideal on certain platforms for
  274. + * unknown reasons. If so, adjust it with a magical number.
  275. + * For Haswell, the magical number is zero. Hell knows why.
  276. + */
  277. + const int32_t center = range_center(data->largest);
  278. + ctrl->rxdqsp[channel][rank][byte] = center - RMPR_START;
  279. + ctrl->rxdqsn[channel][rank][byte] = center - RMPR_START;
  280. + printk(BIOS_DEBUG, " B%u: \t%d\t%d\t%d\t%d\t%u\n", byte,
  281. + data->largest.start, data->largest.end, lwidth,
  282. + center, ctrl->rxdqsp[channel][rank][byte]);
  283. + }
  284. + printk(BIOS_DEBUG, "\n");
  285. + }
  286. + }
  287. +
  288. + /*
  289. + * Now program the DQS center values on populated ranks. data is taken from
  290. + * the host struct. We need to do it after all ranks are trained, because we
  291. + * need to keep the same DQS value on all ranks during the training procedure.
  292. + */
  293. + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
  294. + if (!does_ch_exist(ctrl, channel))
  295. + continue;
  296. +
  297. + for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) {
  298. + if (!rank_in_ch(ctrl, rank, channel))
  299. + continue;
  300. +
  301. + for (uint8_t byte = 0; byte < ctrl->lanes; byte++)
  302. + update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0);
  303. + }
  304. + }
  305. + change_1d_margin_multicast(ctrl, RdT, 0, 0, false, REG_FILE_USE_CURRENT);
  306. + io_reset();
  307. + return status;
  308. +}
  309. diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h
  310. index 0acafbc826..6a31d3a32c 100644
  311. --- a/src/northbridge/intel/haswell/registers/mchbar.h
  312. +++ b/src/northbridge/intel/haswell/registers/mchbar.h
  313. @@ -122,7 +122,7 @@
  314. #define REUT_ch_ERR_DATA_MASK(ch) _MCMAIN_C(0x40d8, ch)
  315. #define REUT_ch_MISC_CKE_CTRL(ch) _MCMAIN_C(0x4190, ch)
  316. -
  317. +#define REUT_ch_MISC_ODT_CTRL(ch) _MCMAIN_C(0x4194, ch)
  318. #define REUT_ch_MISC_PAT_CADB_CTRL(ch) _MCMAIN_C(0x4198, ch)
  319. #define REUT_ch_PAT_CADB_MRS(ch) _MCMAIN_C(0x419c, ch)
  320. #define REUT_ch_PAT_CADB_MUX_CTRL(ch) _MCMAIN_C(0x41a0, ch)
  321. --
  322. 2.39.2