<p>Arthur Heymans has uploaded this change for <strong>review</strong>.</p><p><a href="https://review.coreboot.org/22995">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">nb/intel/x4x: Implement write leveling<br><br>DDR3 adapter a fly-by topology which allows for better signal<br>integrity but at the same time requires additional calibration. This<br>is done by settings the targeted rank in write leveling mode while<br>disabling output buffer on the other ranks.<br><br>Change-Id: I695969868b4534f87dd1f37244fdfac891a417f0<br>Signed-off-by: Arthur Heymans <arthur@aheymans.xyz><br>---<br>M src/northbridge/intel/x4x/dq_dqs.c<br>M src/northbridge/intel/x4x/raminit_ddr23.c<br>M src/northbridge/intel/x4x/x4x.h<br>3 files changed, 401 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://review.coreboot.org:29418/coreboot refs/changes/95/22995/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/northbridge/intel/x4x/dq_dqs.c b/src/northbridge/intel/x4x/dq_dqs.c</span><br><span>index 97bfa42..12e2ab3 100644</span><br><span>--- a/src/northbridge/intel/x4x/dq_dqs.c</span><br><span>+++ b/src/northbridge/intel/x4x/dq_dqs.c</span><br><span>@@ -16,6 +16,7 @@</span><br><span> </span><br><span> #include <arch/io.h></span><br><span> #include <console/console.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <delay.h></span><br><span> #include <stdint.h></span><br><span> #include <string.h></span><br><span> #include <types.h></span><br><span>@@ -106,6 +107,34 @@</span><br><span> return CB_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int decrement_dq_dqs(const struct sysinfo *s,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dll_setting *dq_dqs_setting)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 max_tap_val = max_tap[s->selected_timings.mem_clk</span><br><span style="color: hsl(120, 100%, 40%);">+ - MEM_CLOCK_800MHz];</span><br><span style="color: hsl(120, 100%, 40%);">+ /* PI */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dq_dqs_setting->pi > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->pi -= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (dq_dqs_setting->tap > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->pi = 6;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->tap -= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (dq_dqs_setting->clk_delay > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->pi = 6;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->tap = max_tap_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->clk_delay -= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (dq_dqs_setting->coarse > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->pi = 6;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->tap = max_tap_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->clk_delay += 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_dqs_setting->coarse -= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CB_ERR;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ set_db(s, dq_dqs_setting);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CB_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define WT_PATTERN_SIZE 80</span><br><span> </span><br><span> static const u32 write_training_schedule[WT_PATTERN_SIZE] = {</span><br><span>@@ -476,3 +505,337 @@</span><br><span> printk(BIOS_DEBUG, "Done DQS read training\n");</span><br><span> return CB_SUCCESS;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Enable write leveling on rank0</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void set_rank_write_level(struct sysinfo *s, u8 channel, u8 config,</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 config_rank, u8 target_rank, int wl_enable)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ u32 emrs1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Is shifted by bits 2 later so u8 can be used to reduce size */</span><br><span style="color: hsl(120, 100%, 40%);">+ const static u8 emrs1_lut[8][4][4]={ /* [Config][Leveling Rank][Rank] */</span><br><span style="color: hsl(120, 100%, 40%);">+ { /* Config 0: 2R2R */</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x11, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x00, 0x11}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 1: 2R1R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x11, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 2: 1R2R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x00, 0x11}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 3: 1R1R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x91, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x91, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 4: 2R0R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x11, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 5: 0R2R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x11}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 6: 1R0R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x11, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00}</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+ { // Config 7: 0R1R</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x11, 0x00},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x00, 0x00, 0x00, 0x00}</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (wl_enable) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Entering WL mode\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Using WL ODT values\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 = emrs1_lut[config][target_rank][config_rank];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Exiting WL mode\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 = ddr3_emrs1_config[s->dimm_config[channel]][config_rank];</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Setting ODT for rank%d to ", config_rank);</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (emrs1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "0 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0x11:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "40 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0x81:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "30 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0x80:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "20 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0x10:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "120 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case 0x01:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "60 Ohm\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_WARNING, "ODT value Undefined!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 <<= 2;</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 |= (1 << 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (wl_enable && (target_rank != config_rank)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Disabling output for rank%d\n", config_rank);</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 |= (1 << 12);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (wl_enable && (target_rank == config_rank)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "Enabling WL for rank%d\n", config_rank);</span><br><span style="color: hsl(120, 100%, 40%);">+ emrs1 |= (1 << 7);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ send_jedec_cmd(s, config_rank, channel, EMRS1_CMD, emrs1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define N_SAMPLES 5</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void sample_dq(const struct sysinfo *s, u8 channel, u8 rank,</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 high_found[8]) {</span><br><span style="color: hsl(120, 100%, 40%);">+ u32 address = test_address(channel, rank);</span><br><span style="color: hsl(120, 100%, 40%);">+ int samples, lane;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(high_found, 0, TOTAL_BYTELANES * sizeof(high_found[0]));</span><br><span style="color: hsl(120, 100%, 40%);">+ for (samples = 0; samples < N_SAMPLES; samples++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ write32((u32 *)address, 0x12341234);</span><br><span style="color: hsl(120, 100%, 40%);">+ write32((u32 *)address + 4, 0x12341234);</span><br><span style="color: hsl(120, 100%, 40%);">+ udelay(5);</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 dq_high = (MCHBAR8(0x561 + 0x400 * channel</span><br><span style="color: hsl(120, 100%, 40%);">+ + (lane * 4)) >> 7) & 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ high_found[lane] += dq_high;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static enum cb_err increment_to_dqs_edge(struct sysinfo *s, u8 channel, u8 rank)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int lane;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 saved_24d;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dll_setting dqs_setting[TOTAL_BYTELANES];</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 bytelane_ok = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 dq_sample[TOTAL_BYTELANES];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(dqs_setting, s->dqs_settings[channel], sizeof(dqs_setting));</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane)</span><br><span style="color: hsl(120, 100%, 40%);">+ dqsset(channel, lane, &dqs_setting[lane]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ saved_24d = MCHBAR8(0x24d + 0x400 * channel);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Loop 0: Find DQ sample low, by decreasing */</span><br><span style="color: hsl(120, 100%, 40%);">+ while (bytelane_ok != 0xff) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sample_dq(s, channel, rank, dq_sample);</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (bytelane_ok & (1 << lane))</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_SPEW, "%d, %d, %02d, %d,"</span><br><span style="color: hsl(120, 100%, 40%);">+ " lane%d sample: %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].coarse,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].clk_delay,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].tap,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].pi,</span><br><span style="color: hsl(120, 100%, 40%);">+ lane,</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_sample[lane]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dq_sample[lane] > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (decrement_dq_dqs(s, &dqs_setting[lane])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_EMERG,</span><br><span style="color: hsl(120, 100%, 40%);">+ "DQS setting channel%d, "</span><br><span style="color: hsl(120, 100%, 40%);">+ "lane %d reached a minimum!\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ channel, lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CB_ERR;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ bytelane_ok |= (1 << lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ dqsset(channel, lane, &dqs_setting[lane]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "DQS settings on PASS #0:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "lane %d: ", lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_dll_setting(&dqs_setting[lane], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Loop 1: Find DQ sample high, by increasing */</span><br><span style="color: hsl(120, 100%, 40%);">+ bytelane_ok = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ while (bytelane_ok != 0xff) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sample_dq(s, channel, rank, dq_sample);</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (bytelane_ok & (1 << lane))</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_SPEW, "%d, %d, %02d, %d, lane%d sample: %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].coarse,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].clk_delay,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].tap,</span><br><span style="color: hsl(120, 100%, 40%);">+ dqs_setting[lane].pi,</span><br><span style="color: hsl(120, 100%, 40%);">+ lane,</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_sample[lane]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dq_sample[lane] == N_SAMPLES) {</span><br><span style="color: hsl(120, 100%, 40%);">+ bytelane_ok |= (1 << lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (increment_dq_dqs(s, &dqs_setting[lane])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_EMERG,</span><br><span style="color: hsl(120, 100%, 40%);">+ "DQS setting channel%d, "</span><br><span style="color: hsl(120, 100%, 40%);">+ "lane %d reached a maximum!\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ channel, lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CB_ERR;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ dqsset(channel, lane, &dqs_setting[lane]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "DQS settings on PASS #1:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(RAM_DEBUG, "lane %d: ", lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_dll_setting(&dqs_setting[lane], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "final WL DQS settings on CH%d\n", channel);</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_BYTELANE(lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "\tlane%d: ", lane);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_dll_setting(&dqs_setting[lane], 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ s->dqs_settings[channel][lane] = dqs_setting[lane];</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x24d + 0x400 * channel) = saved_24d;</span><br><span style="color: hsl(120, 100%, 40%);">+ return CB_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void search_write_leveling(struct sysinfo *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i, ch, count;</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 config, rank0, rank1, lane;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct dll_setting dq_setting;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 chanconfig_lut[16]={0, 6, 4, 6, 7, 3, 1, 3, 5, 2, 0, 2, 7, 3, 1, 3};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ u8 odt_force[8][4] = { /* [Config][leveling rank] */</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x5, 0x6, 0x5, 0x9},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x5, 0x6, 0x5, 0x0},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x5, 0x0, 0x5, 0x9},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x5, 0x0, 0x5, 0x0},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x1, 0x2, 0x0, 0x0},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x0, 0x0, 0x4, 0x8},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x1, 0x0, 0x0, 0x0},</span><br><span style="color: hsl(120, 100%, 40%);">+ {0x0, 0x0, 0x4, 0x0}</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "Starting write levelling.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "\tCH%d\n", ch);</span><br><span style="color: hsl(120, 100%, 40%);">+ config = chanconfig_lut[s->dimm_config[ch]];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5d8 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5d8 + 0x400 * ch) & ~0x0e;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) &</span><br><span style="color: hsl(120, 100%, 40%);">+ ~0x3fff) | 0x3fff;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x265 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x265 + 0x400 * ch) & ~0x1f;</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ set_rank_write_level(s, ch, config, rank1, rank0, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x298 + 2 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ (MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f)</span><br><span style="color: hsl(120, 100%, 40%);">+ | odt_force[config][rank0];</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x271 + 0x400 * ch) = (MCHBAR8(0x271 + 0x400 * ch) & ~0x7e)</span><br><span style="color: hsl(120, 100%, 40%);">+ | 0x4e;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5d9 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04) | 0x04;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR32(0x1a0) = (MCHBAR32(0x1a0) & ~0x07ffffff)</span><br><span style="color: hsl(120, 100%, 40%);">+ | 0x00014000;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (increment_to_dqs_edge(s, ch, rank0))</span><br><span style="color: hsl(120, 100%, 40%);">+ die("Write Leveling failed!");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x298 + 2 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x271 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ (MCHBAR8(0x271 + 0x400 * ch) & ~0x7e)</span><br><span style="color: hsl(120, 100%, 40%);">+ | 0x0e;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5d9 + 0x400 * ch) =</span><br><span style="color: hsl(120, 100%, 40%);">+ (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04);</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR32(0x1a0) = (MCHBAR32(0x1a0)</span><br><span style="color: hsl(120, 100%, 40%);">+ & ~0x07ffffff) | 0x00555801;</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch,</span><br><span style="color: hsl(120, 100%, 40%);">+ rank1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rank0 == rank1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ set_rank_write_level(s, ch, config,</span><br><span style="color: hsl(120, 100%, 40%);">+ rank1, rank0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ send_jedec_cmd(s, rank1, ch,</span><br><span style="color: hsl(120, 100%, 40%);">+ NORMALOP_CMD, 1 << 12);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ break; /* Break on first populated rank */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5d8 + 0x400 * ch) = (MCHBAR8(0x5d8 + 0x400 * ch) & ~0x0e) | 0x0e;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) &</span><br><span style="color: hsl(120, 100%, 40%);">+ ~0x3fff) | 0x1807;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x265 + 0x400 * ch) = MCHBAR8(0x265 + 0x400 * ch) & ~0x1f;</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ set_rank_write_level(s, ch, config, rank0, rank0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5dc) = (MCHBAR8(0x5dc) & ~0x80) | 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Increment DQ dll setting by a standard amount past DQS,</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is further trained in write training.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (s->selected_timings.mem_clk) {</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ case MEM_CLOCK_800MHz:</span><br><span style="color: hsl(120, 100%, 40%);">+ count = 39;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case MEM_CLOCK_1066MHz:</span><br><span style="color: hsl(120, 100%, 40%);">+ count = 32;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case MEM_CLOCK_1333MHz:</span><br><span style="color: hsl(120, 100%, 40%);">+ count = 42;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ FOR_EACH_POPULATED_CHANNEL_AND_BYTELANE(s->dimms, ch, lane) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dq_setting = s->dqs_settings[ch][lane];</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < count; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+ increment_dq_dqs(s, &dq_setting);</span><br><span style="color: hsl(120, 100%, 40%);">+ dqset(ch, lane, &dq_setting);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "Done write levelling.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/northbridge/intel/x4x/raminit_ddr23.c b/src/northbridge/intel/x4x/raminit_ddr23.c</span><br><span>index 2835890..13b98ee 100644</span><br><span>--- a/src/northbridge/intel/x4x/raminit_ddr23.c</span><br><span>+++ b/src/northbridge/intel/x4x/raminit_ddr23.c</span><br><span>@@ -1360,8 +1360,7 @@</span><br><span> return channel * 512 * MiB + rank * 128 * MiB;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void send_jedec_cmd(const struct sysinfo *s, u8 r,</span><br><span style="color: hsl(0, 100%, 40%);">- u8 ch, u8 cmd, u32 val)</span><br><span style="color: hsl(120, 100%, 40%);">+void send_jedec_cmd(const struct sysinfo *s, u8 r, u8 ch, u8 cmd, u32 val)</span><br><span> {</span><br><span> u32 addr = test_address(ch, r);</span><br><span> volatile u32 rubbish;</span><br><span>@@ -1926,6 +1925,23 @@</span><br><span> MCHBAR8(0x561 + (lane << 2)) = MCHBAR8(0x561 + (lane << 2)) & ~(1 << 3);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void software_ddr3_reset(struct sysinfo *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "Software initiated DDR3 reset.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) | 0x02;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x02;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = (MCHBAR8(0x5da) & ~0x03) | 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ udelay(200);</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x02;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ udelay(500);</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x03;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x03;</span><br><span style="color: hsl(120, 100%, 40%);">+ jedec_ddr3(s);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void do_raminit(struct sysinfo *s, int fast_boot)</span><br><span> {</span><br><span> u8 ch;</span><br><span>@@ -2012,6 +2028,17 @@</span><br><span> MCHBAR8(0x9d8) = MCHBAR8(0x9d8) | 0x7;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* DDR3 reset */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((s->spd_type == DDR3) && (s->boot_path != BOOT_PATH_RESUME)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ printk(BIOS_DEBUG, "DDR3 Reset.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x2;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ udelay(500);</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x2;</span><br><span style="color: hsl(120, 100%, 40%);">+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+ udelay(500);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> // Pre jedec</span><br><span> MCHBAR8(0x40) = MCHBAR8(0x40) | 0x2;</span><br><span> FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {</span><br><span>@@ -2031,6 +2058,13 @@</span><br><span> </span><br><span> printk(BIOS_DEBUG, "Done jedec steps\n");</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (s->spd_type == DDR3) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fast_boot)</span><br><span style="color: hsl(120, 100%, 40%);">+ search_write_leveling(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (s->boot_path == BOOT_PATH_NORMAL)</span><br><span style="color: hsl(120, 100%, 40%);">+ software_ddr3_reset(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> // After JEDEC reset</span><br><span> MCHBAR8(0x40) = MCHBAR8(0x40) & ~0x2;</span><br><span> FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {</span><br><span>diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h</span><br><span>index 37215b1..a781a80 100644</span><br><span>--- a/src/northbridge/intel/x4x/x4x.h</span><br><span>+++ b/src/northbridge/intel/x4x/x4x.h</span><br><span>@@ -396,7 +396,8 @@</span><br><span> struct abs_timings *saved_timings);</span><br><span> int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd,</span><br><span> struct abs_timings *saved_timings, struct sysinfo *s);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+void search_write_leveling(struct sysinfo *s);</span><br><span style="color: hsl(120, 100%, 40%);">+void send_jedec_cmd(const struct sysinfo *s, u8 r, u8 ch, u8 cmd, u32 val);</span><br><span> </span><br><span> extern const struct dll_setting default_ddr2_667_ctrl[7];</span><br><span> extern const struct dll_setting default_ddr2_800_ctrl[7];</span><br><span></span><br></pre><p>To view, visit <a href="https://review.coreboot.org/22995">change 22995</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://review.coreboot.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://review.coreboot.org/22995"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: coreboot </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I695969868b4534f87dd1f37244fdfac891a417f0 </div>
<div style="display:none"> Gerrit-Change-Number: 22995 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Arthur Heymans <arthur@aheymans.xyz> </div>