<p>Arthur Heymans has uploaded this change for <strong>review</strong>.</p><p><a href="https://review.coreboot.org/21677">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">nb/intel/x4x: Use SPI flash to cache raminit results<br><br>Stores information obtained from decoding dimms and receive enable<br>results for future use.<br><br>Depreciates using rtc nvram to store receive enable settings.<br><br>A notable change is that receive enable results are always reused, not<br>just on a resume from S3.<br><br>TODO: With dimms with a working SPD chip with invalid content this<br>patch will result in normal boot each time except on resume from S3.<br><br>TESTED on Intel DG43GT with W25Q128.V.<br><br>Change-Id: I042dc5c52615d40781d9ef7ecd657ad0bf3ed08f<br>Signed-off-by: Arthur Heymans <arthur@aheymans.xyz><br>---<br>M src/northbridge/intel/x4x/Kconfig<br>M src/northbridge/intel/x4x/Makefile.inc<br>M src/northbridge/intel/x4x/early_init.c<br>M src/northbridge/intel/x4x/raminit.c<br>M src/northbridge/intel/x4x/raminit_ddr2.c<br>M src/northbridge/intel/x4x/rcven.c<br>M src/northbridge/intel/x4x/x4x.h<br>7 files changed, 153 insertions(+), 87 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://review.coreboot.org:29418/coreboot refs/changes/77/21677/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/src/northbridge/intel/x4x/Kconfig b/src/northbridge/intel/x4x/Kconfig<br>index 9239637..23c20b5 100644<br>--- a/src/northbridge/intel/x4x/Kconfig<br>+++ b/src/northbridge/intel/x4x/Kconfig<br>@@ -28,6 +28,7 @@<br>  select RELOCATABLE_RAMSTAGE<br>   select HAVE_LINEAR_FRAMEBUFFER if MAINBOARD_DO_NATIVE_VGA_INIT<br>        select HAVE_VGA_TEXT_FRAMEBUFFER if MAINBOARD_DO_NATIVE_VGA_INIT<br>+     select NORTHBRIDGE_INTEL_COMMON_MRC_CACHE<br> <br> config CBFS_SIZE<br>       hex<br>@@ -45,4 +46,8 @@<br>        hex<br>   default 0xe0000000<br> <br>+config MRC_CACHE_SIZE<br>+        hex<br>+  default 0x10000<br>+<br> endif<br>diff --git a/src/northbridge/intel/x4x/Makefile.inc b/src/northbridge/intel/x4x/Makefile.inc<br>index 5c64ca7..7661ec3 100644<br>--- a/src/northbridge/intel/x4x/Makefile.inc<br>+++ b/src/northbridge/intel/x4x/Makefile.inc<br>@@ -27,4 +27,15 @@<br> ramstage-y += gma.c<br> ramstage-y += northbridge.c<br> <br>+$(obj)/mrc.cache: $(obj)/config.h<br>+   dd if=/dev/zero count=1 \<br>+    bs=$(shell printf "%d" $(CONFIG_MRC_CACHE_SIZE) ) | \<br>+      tr '\000' '\377' > $@<br>+<br>+cbfs-files-y += mrc.cache<br>+mrc.cache-file := $(obj)/mrc.cache<br>+mrc.cache-align := 0x10000<br>+mrc.cache-type := mrc_cache<br>+<br>+<br> endif<br>diff --git a/src/northbridge/intel/x4x/early_init.c b/src/northbridge/intel/x4x/early_init.c<br>index 27fe916..f463840 100644<br>--- a/src/northbridge/intel/x4x/early_init.c<br>+++ b/src/northbridge/intel/x4x/early_init.c<br>@@ -235,15 +235,6 @@<br> <br> static void x4x_prepare_resume(int s3resume)<br> {<br>-   int cbmem_recovered;<br>-<br>-      cbmem_recovered = !cbmem_recovery(s3resume);<br>- if (!cbmem_recovered && s3resume) {<br>-          /* Failed S3 resume, reset to come up cleanly */<br>-             outb(0x6, 0xcf9);<br>-            halt();<br>-      }<br>-<br>  romstage_handoff_init(s3resume);<br> }<br> <br>diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c<br>index d5298f6..ac8b576 100644<br>--- a/src/northbridge/intel/x4x/raminit.c<br>+++ b/src/northbridge/intel/x4x/raminit.c<br>@@ -33,10 +33,38 @@<br> #include <spd.h><br> #include <string.h><br> #include <device/dram/ddr2.h><br>+#include <northbridge/intel/common/mrc_cache.h><br> <br> static inline int spd_read_byte(unsigned int device, unsigned int address)<br> {<br>      return smbus_read_byte(device, address);<br>+}<br>+<br>+static int verify_spds(const u8 *spd_map, struct sysinfo *ctrl_cached)<br>+{<br>+ int i;<br>+       int checksum;<br>+<br>+     for (i = 0; i < TOTAL_DIMMS; i++) {<br>+               checksum = 0;<br>+                if (!(spd_map[i]))<br>+                   continue;<br>+            if (ctrl_cached->spd_type == DDR2) {<br>+                      checksum = spd_read_byte(spd_map[i], 63);<br>+            } else { /* DDR3 */<br>+                  checksum = spd_read_byte(spd_map[i], 126);<br>+                   checksum |= spd_read_byte(spd_map[i], 127) << 8;<br>+               }<br>+            if (checksum < 0 && ctrl_cached->dimms[i].card_type<br>+                            == RAW_CARD_UNPOPULATED)<br>+                     continue;<br>+            if (checksum > 0 && ctrl_cached->dimms[i].card_type<br>+                    == RAW_CARD_UNPOPULATED)<br>+                     return CB_ERR;<br>+               if ((checksum & 0xffff) != ctrl_cached->dimms[i].checksum)<br>+                    return CB_ERR;<br>+       }<br>+    return CB_SUCCESS;<br> }<br> <br> struct abs_timings {<br>@@ -190,6 +218,7 @@<br>                                 MAX(saved_timings->min_tCLK_cas[i],<br>                                        decoded_dimm.cycle_time[i]);<br>  }<br>+    s->dimms[dimm_idx].checksum = decoded_dimm.checksum;<br>       return CB_SUCCESS;<br> }<br> <br>@@ -380,8 +409,10 @@<br>  */<br> void sdram_initialize(int boot_path, const u8 *spd_map)<br> {<br>-  struct sysinfo s;<br>+    struct sysinfo s, *ctrl_cached;<br>       u8 reg8;<br>+     int fast_boot, cbmem_was_inited;<br>+     struct mrc_data_container *mrc_cache;<br> <br>      printk(BIOS_DEBUG, "Setting up RAM controller.\n");<br> <br>@@ -389,28 +420,60 @@<br> <br>    memset(&s, 0, sizeof(struct sysinfo));<br> <br>-        s.boot_path = boot_path;<br>-     s.spd_map[0] = spd_map[0];<br>-   s.spd_map[1] = spd_map[1];<br>-   s.spd_map[2] = spd_map[2];<br>-   s.spd_map[3] = spd_map[3];<br>+   mrc_cache = find_current_mrc_cache();<br>+        if (!mrc_cache || (mrc_cache->mrc_data_size < sizeof(s))) {<br>+            if (boot_path == BOOT_PATH_RESUME) {<br>+                 /* Failed S3 resume, reset to come up cleanly */<br>+                     outb(0x6, 0xcf9);<br>+                    halt();<br>+              }<br>+            ctrl_cached = NULL;<br>+  } else {<br>+             ctrl_cached = (struct sysinfo *)mrc_cache->mrc_data;<br>+      }<br> <br>- checkreset_ddr2(s.boot_path);<br>+        /* verify MRC cache for fast boot */<br>+ if (boot_path != BOOT_PATH_RESUME  && ctrl_cached) {<br>+         /* check SPD checksum to make sure the DIMMs haven't been<br>+                 * replaced */<br>+               fast_boot = verify_spds(spd_map, ctrl_cached) == CB_SUCCESS;<br>+         if (!fast_boot)<br>+                      printk(BIOS_DEBUG, "SPD checksums don't match,"<br>+                                " dimm's have been replaced\n");<br>+       } else {<br>+             fast_boot = boot_path == BOOT_PATH_RESUME;<br>+   }<br> <br>- /* Detect dimms per channel */<br>-       reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xe9);<br>-     printk(BIOS_DEBUG, "Dimms per channel: %d\n", (reg8 & 0x10) ? 1 : 2);<br>+  if (fast_boot) {<br>+             printk(BIOS_DEBUG, "Using cached raminit settings\n");<br>+             memcpy(&s, ctrl_cached, sizeof(s));<br>+              s.boot_path = boot_path;<br>+             mchinfo_ddr2(&s);<br>+                print_selected_timings(&s);<br>+      } else {<br>+             s.boot_path = boot_path;<br>+             s.spd_map[0] = spd_map[0];<br>+           s.spd_map[1] = spd_map[1];<br>+           s.spd_map[2] = spd_map[2];<br>+           s.spd_map[3] = spd_map[3];<br>+           checkreset_ddr2(s.boot_path);<br> <br>-     mchinfo_ddr2(&s);<br>+                /* Detect dimms per channel */<br>+               reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xe9);<br>+             printk(BIOS_DEBUG, "Dimms per channel: %d\n",<br>+                      (reg8 & 0x10) ? 1 : 2);<br> <br>-       find_fsb_speed(&s);<br>-      decode_spd_select_timings(&s);<br>-   print_selected_timings(&s);<br>-      find_dimm_config(&s);<br>+            mchinfo_ddr2(&s);<br>+<br>+             find_fsb_speed(&s);<br>+              decode_spd_select_timings(&s);<br>+           print_selected_timings(&s);<br>+              find_dimm_config(&s);<br>+    }<br> <br>  switch (s.spd_type) {<br>         case DDR2:<br>-           raminit_ddr2(&s);<br>+                raminit_ddr2(&s, fast_boot);<br>              break;<br>        case DDR3:<br>            // FIXME Add: raminit_ddr3(&s);<br>@@ -426,4 +489,13 @@<br>     reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf4);<br>      pci_write_config8(PCI_DEV(0, 0, 0), 0xf4, reg8 | 1);<br>  printk(BIOS_DEBUG, "RAM initialization finished.\n");<br>+<br>+   cbmem_was_inited = !cbmem_recovery(s.boot_path == BOOT_PATH_RESUME);<br>+ if (!fast_boot)<br>+              store_current_mrc_cache(&s, sizeof(s));<br>+  if (s.boot_path == BOOT_PATH_RESUME && !cbmem_was_inited) {<br>+          /* Failed S3 resume, reset to come up cleanly */<br>+             outb(0x6, 0xcf9);<br>+            halt();<br>+      }<br> }<br>diff --git a/src/northbridge/intel/x4x/raminit_ddr2.c b/src/northbridge/intel/x4x/raminit_ddr2.c<br>index 079dcde..b9f11ab 100644<br>--- a/src/northbridge/intel/x4x/raminit_ddr2.c<br>+++ b/src/northbridge/intel/x4x/raminit_ddr2.c<br>@@ -1048,77 +1048,49 @@<br>       printk(BIOS_DEBUG, "MRS done\n");<br> }<br> <br>-static void sdram_save_receive_enable(void)<br>+static void sdram_recover_receive_enable(struct sysinfo *s)<br> {<br>-   int i = 0;<br>-   u16 reg16;<br>-   u8 values[18];<br>-       u8 lane, ch;<br>-<br>-      FOR_EACH_CHANNEL(ch) {<br>-               lane = 0;<br>-            while (lane < 8) {<br>-                        values[i] = (MCHBAR8(0x400*ch + 0x560 + lane++ * 4) & 0xf);<br>-                      values[i++] |= (MCHBAR8(0x400*ch + 0x560 + lane++ * 4) & 0xf) << 4;<br>-                }<br>-            values[i++] = (MCHBAR32(0x400*ch + 0x248) >> 16) & 0xf;<br>-            reg16 = MCHBAR16(0x400*ch + 0x5fa);<br>-          values[i++] = reg16 & 0xff;<br>-              values[i++] = (reg16 >> 8) & 0xff;<br>-         reg16 = MCHBAR16(0x400*ch + 0x58c);<br>-          values[i++] = reg16 & 0xff;<br>-              values[i++] = (reg16 >> 8) & 0xff;<br>- }<br>-<br>- for (i = 0; i < ARRAY_SIZE(values); i++)<br>-          cmos_write(values[i], 128 + i);<br>-}<br>-<br>-static void sdram_recover_receive_enable(void)<br>-{<br>-  u8 i;<br>         u32 reg32;<br>-   u16 reg16;<br>-   u8 values[18];<br>-       u8 ch, lane;<br>+ u16 medium, coarse_offset;<br>+   u8 pitap;<br>+    int lane, channel;<br> <br>-        for (i = 0; i < ARRAY_SIZE(values); i++)<br>-          values[i] = cmos_read(128 + i);<br>+      FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) {<br>+           medium = 0;<br>+          coarse_offset = 0;<br>+           reg32 = MCHBAR32(0x400 * channel + 0x248);<br>+           reg32 &= ~0xf0000;<br>+               reg32 |= s->rcven_t[channel].min_common_coarse << 16;<br>+               MCHBAR32(0x400 * channel + 0x248) = reg32;<br> <br>-        i = 0;<br>-       FOR_EACH_CHANNEL(ch) {<br>-               lane = 0;<br>-            while (lane < 8) {<br>-                        MCHBAR8(0x400*ch + 0x560 + lane++ * 4) = 0x70 |<br>-                              (values[i] & 0xf);<br>-                       MCHBAR8(0x400*ch + 0x560 + lane++ * 4) = 0x70 |<br>-                              ((values[i++] >> 4) & 0xf);<br>+                for (lane = 0; lane < 8; lane++) {<br>+                        medium |= s->rcven_t[channel].medium[lane]<br>+                                << (lane * 2);<br>+                 coarse_offset |=<br>+                             (s->rcven_t[channel].coarse_offset[lane] & 0x3)<br>+                               << (lane * 2);<br>+<br>+                      pitap = MCHBAR8(0x400 * channel + 0x560 + lane * 4);<br>+                 pitap &= ~0x7f;<br>+                  pitap |= s->rcven_t[channel].tap[lane];<br>+                   pitap |= s->rcven_t[channel].pi[lane];<br>+                    MCHBAR8(0x400 * channel + 0x560 + lane * 4) = pitap;<br>          }<br>-            reg32 = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000)<br>-            | ((values[i++] & 0xf) << 16);<br>-           MCHBAR32(0x400*ch + 0x248) = reg32;<br>-          reg16 = values[i++];<br>-         reg16 |= values[i++] << 8;<br>-             MCHBAR16(0x400*ch + 0x5fa) = reg16;<br>-          reg16 = values[i++];<br>-         reg16 |= values[i++] << 8;<br>-             MCHBAR16(0x400*ch + 0x58c) = reg16;<br>+          MCHBAR16(0x400 * channel + 0x58c) = medium;<br>+          MCHBAR16(0x400 * channel + 0x5fa) = coarse_offset;<br>    }<br> }<br> <br>-static void sdram_program_receive_enable(struct sysinfo *s)<br>+static void sdram_program_receive_enable(struct sysinfo *s, int fast_boot)<br> {<br>       /* enable upper CMOS */<br>       RCBA32(0x3400) = (1 << 2);<br> <br>   /* Program Receive Enable Timings */<br>- if ((s->boot_path == BOOT_PATH_WARM_RESET)<br>-                || (s->boot_path == BOOT_PATH_RESUME)) {<br>-          sdram_recover_receive_enable();<br>-      } else {<br>+     if (fast_boot)<br>+               sdram_recover_receive_enable(s);<br>+     else<br>          rcven(s);<br>-            sdram_save_receive_enable();<br>- }<br> }<br> <br> static void dradrb_ddr2(struct sysinfo *s)<br>@@ -1473,7 +1445,7 @@<br>          MCHBAR8(0x561 + (lane << 2)) = MCHBAR8(0x561 + (lane << 2)) & ~(1 << 3);<br> }<br> <br>-void raminit_ddr2(struct sysinfo *s)<br>+void raminit_ddr2(struct sysinfo *s, int fast_boot)<br> {<br>        u8 ch;<br>        u8 r, bank;<br>@@ -1616,7 +1588,7 @@<br>    }<br> <br>  // Receive enable<br>-    sdram_program_receive_enable(s);<br>+     sdram_program_receive_enable(s, fast_boot);<br>   printk(BIOS_DEBUG, "Done rcven\n");<br> <br>      // Finish rcven<br>diff --git a/src/northbridge/intel/x4x/rcven.c b/src/northbridge/intel/x4x/rcven.c<br>index 23f8d52..7ad1e0a 100644<br>--- a/src/northbridge/intel/x4x/rcven.c<br>+++ b/src/northbridge/intel/x4x/rcven.c<br>@@ -304,7 +304,7 @@<br>     return 0;<br> }<br> <br>-void rcven(const struct sysinfo *s)<br>+void rcven(struct sysinfo *s)<br> {<br>    int i;<br>        u8 channel, lane, reg8;<br>@@ -354,6 +354,7 @@<br>                          mincoarse = timing[lane].coarse;<br>              }<br>             printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse);<br>+            s->rcven_t[channel].min_common_coarse = mincoarse;<br>                 printk(BIOS_DEBUG, "Receive enable, final timings:\n");<br>             /* Normalise coarse */<br>                for (lane = 0; lane < 8; lane++) {<br>@@ -365,6 +366,10 @@<br>                           "medium: %d; tap: %d\n",<br>                            channel, lane, reg8, timing[lane].medium,<br>                             timing[lane].tap);<br>+                   s->rcven_t[channel].coarse_offset[lane] = reg8;<br>+                   s->rcven_t[channel].medium[lane] = timing[lane].medium;<br>+                   s->rcven_t[channel].tap[lane] = timing[lane].tap;<br>+                 s->rcven_t[channel].pi[lane] = timing[lane].pi;<br>                    MCHBAR16(0x400 * channel + 0x5fa) &=<br>                              ~(3 << (lane * 2)) | (reg8 << (lane * 2));<br>                }<br>diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h<br>index e4147a1..2fffe8b 100644<br>--- a/src/northbridge/intel/x4x/x4x.h<br>+++ b/src/northbridge/intel/x4x/x4x.h<br>@@ -276,6 +276,15 @@<br>  unsigned int    ranks;<br>        unsigned int    rows;<br>         unsigned int    cols;<br>+        u16             checksum;<br>+};<br>+<br>+struct rcven_timings {<br>+   u8 min_common_coarse;<br>+        u8 coarse_offset[8];<br>+ u8 medium[8];<br>+        u8 tap[8];<br>+   u8 pi[8];<br> };<br> <br> /* The setup is up to two DIMMs per channel */<br>@@ -290,6 +299,7 @@<br>       struct timings  selected_timings;<br>     struct dimminfo dimms[4];<br>     u8              spd_map[4];<br>+  struct rcven_timings rcven_t[TOTAL_CHANNELS];<br> };<br> #define BOOT_PATH_NORMAL   0<br> #define BOOT_PATH_WARM_RESET        1<br>@@ -328,8 +338,8 @@<br> u32 decode_igd_gtt_size(u32 gsm);<br> u8 decode_pciebar(u32 *const base, u32 *const len);<br> void sdram_initialize(int boot_path, const u8 *spd_map);<br>-void raminit_ddr2(struct sysinfo *);<br>-void rcven(const struct sysinfo *);<br>+void raminit_ddr2(struct sysinfo *s, int fast_boot);<br>+void rcven(struct sysinfo *s);<br> u32 fsb2mhz(u32 speed);<br> u32 ddr2mhz(u32 speed);<br> <br></pre><p>To view, visit <a href="https://review.coreboot.org/21677">change 21677</a>. To unsubscribe, 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/21677"/><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: I042dc5c52615d40781d9ef7ecd657ad0bf3ed08f </div>
<div style="display:none"> Gerrit-Change-Number: 21677 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Arthur Heymans <arthur@aheymans.xyz> </div>