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