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