<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>