Hi Simon, On Tue, Dec 30, 2014 at 9:12 AM, Simon Glass <s...@chromium.org> wrote: > The memory reference code takes a very long time to 'train' its SDRAM > interface, around half a second. To avoid this delay on every boot we can > store the parameters from the last training sessions to speed up the next. > > Add an implementation of this, storing the training data in CMOS RAM and > SPI flash.
Is storing mrc data to cmos ram not enough, so that we must store it to spi flash? > Signed-off-by: Simon Glass <s...@chromium.org> > --- > > arch/x86/cpu/ivybridge/Makefile | 1 + > arch/x86/cpu/ivybridge/mrccache.c | 156 ++++++++++++++ > arch/x86/cpu/ivybridge/sdram.c | 277 > +++++++++++++++++++++++++ > arch/x86/include/asm/arch-ivybridge/mrccache.h | 51 +++++ > arch/x86/include/asm/global_data.h | 3 + > 5 files changed, 488 insertions(+) > create mode 100644 arch/x86/cpu/ivybridge/mrccache.c > create mode 100644 arch/x86/include/asm/arch-ivybridge/mrccache.h > > diff --git a/arch/x86/cpu/ivybridge/Makefile b/arch/x86/cpu/ivybridge/Makefile > index 0c7efae..3576b83 100644 > --- a/arch/x86/cpu/ivybridge/Makefile > +++ b/arch/x86/cpu/ivybridge/Makefile > @@ -14,6 +14,7 @@ obj-y += lpc.o > obj-y += me_status.o > obj-y += model_206ax.o > obj-y += microcode_intel.o > +obj-y += mrccache.o > obj-y += northbridge.o > obj-y += pch.o > obj-y += pci.o > diff --git a/arch/x86/cpu/ivybridge/mrccache.c > b/arch/x86/cpu/ivybridge/mrccache.c > new file mode 100644 > index 0000000..182c995 > --- /dev/null > +++ b/arch/x86/cpu/ivybridge/mrccache.c > @@ -0,0 +1,156 @@ > +/* > + * From Coreboot src/southbridge/intel/bd82x6x/mrccache.c > + * > + * Copyright (C) 2014 Google Inc. > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include <common.h> > +#include <errno.h> > +#include <fdtdec.h> > +#include <spi.h> > +#include <spi_flash.h> > +#include <asm/ip_checksum.h> > +#include <asm/arch/mrccache.h> > +#include <asm/arch/sandybridge.h> > + > +static struct mrc_data_container *next_mrc_block( > + struct mrc_data_container *mrc_cache) > +{ > + /* MRC data blocks are aligned within the region */ > + u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->data_size; > + if (mrc_size & (MRC_DATA_ALIGN - 1UL)) { > + mrc_size &= ~(MRC_DATA_ALIGN - 1UL); > + mrc_size += MRC_DATA_ALIGN; > + } > + > + u8 *region_ptr = (u8 *)mrc_cache; > + region_ptr += mrc_size; > + return (struct mrc_data_container *)region_ptr; > +} > + > +static int is_mrc_cache(struct mrc_data_container *cache) > +{ > + return cache && (cache->signature == MRC_DATA_SIGNATURE); > +} > + > +/* > + * Find the largest index block in the MRC cache. Return NULL if none is > + * found. > + */ > +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry) > +{ > + struct mrc_data_container *cache, *next; > + ulong base_addr, end_addr; > + uint id; > + > + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset; > + end_addr = base_addr + entry->length; > + cache = NULL; > + > + /* Search for the last filled entry in the region */ > + for (id = 0, next = (struct mrc_data_container *)base_addr; > + is_mrc_cache(next); > + id++) { > + cache = next; > + next = next_mrc_block(next); > + if ((ulong)next >= end_addr) > + break; > + } > + > + if (id-- == 0) { > + debug("%s: No valid MRC cache found.\n", __func__); > + return NULL; > + } > + > + /* Verify checksum */ > + if (cache->checksum != compute_ip_checksum(cache->data, > + cache->data_size)) { > + printf("%s: MRC cache checksum mismatch\n", __func__); > + return NULL; > + } > + > + debug("%s: picked entry %u from cache block\n", __func__, id); > + > + return cache; > +} > + > +/** > + * find_next_mrc_cache() - get next cache entry > + * > + * @entry: MRC cache flash area > + * @cache: Entry to start from > + * > + * @return next cache entry if found, NULL if we got to the end > + */ > +static struct mrc_data_container *find_next_mrc_cache(struct fmap_entry > *entry, > + struct mrc_data_container *cache) > +{ > + ulong base_addr, end_addr; > + > + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset; > + end_addr = base_addr + entry->length; > + > + cache = next_mrc_block(cache); > + if ((ulong)cache >= end_addr) { > + /* Crossed the boundary */ > + cache = NULL; > + debug("%s: no available entries found\n", __func__); > + } else { > + debug("%s: picked next entry from cache block at %p\n", > + __func__, cache); > + } > + > + return cache; > +} > + > +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry, > + struct mrc_data_container *cur) > +{ > + struct mrc_data_container *cache; > + ulong offset; > + ulong base_addr; > + int ret; > + > + /* Find the last used block */ > + base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset; > + debug("Updating MRC cache data\n"); > + cache = mrccache_find_current(entry); > + if (cache && (cache->data_size == cur->data_size) && > + (!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) { > + debug("MRC data in flash is up to date. No update\n"); > + return -EEXIST; > + } > + > + /* Move to the next block, which will be the first unused block */ > + if (cache) > + cache = find_next_mrc_cache(entry, cache); > + > + /* > + * If we have got to the end, erase the entire mrc-cache area and > start > + * again at block 0. > + */ > + if (!cache) { > + debug("Erasing the MRC cache region of %x bytes at %x\n", > + entry->length, entry->offset); > + > + ret = spi_flash_erase(sf, entry->offset, entry->length); > + if (ret) { > + debug("Failed to erase flash region\n"); > + return ret; > + } > + cache = (struct mrc_data_container *)base_addr; > + } > + > + /* Write the data out */ > + offset = (ulong)cache - base_addr + entry->offset; > + debug("Write MRC cache update to flash at %lx\n", offset); > + ret = spi_flash_write(sf, offset, cur->data_size + sizeof(*cur), cur); > + if (ret) { > + debug("Failed to write to SPI flash\n"); > + return ret; > + } > + > + return 0; > +} > diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c > index 9504735..800c75b 100644 > --- a/arch/x86/cpu/ivybridge/sdram.c > +++ b/arch/x86/cpu/ivybridge/sdram.c > @@ -14,12 +14,17 @@ > #include <errno.h> > #include <fdtdec.h> > #include <malloc.h> > +#include <spi.h> > +#include <spi_flash.h> > +#include <asm/cmos.h> > +#include <asm/ip_checksum.h> > #include <asm/processor.h> > #include <asm/gpio.h> > #include <asm/global_data.h> > #include <asm/mtrr.h> > #include <asm/pci.h> > #include <asm/arch/me.h> > +#include <asm/arch/mrccache.h> > #include <asm/arch/pei_data.h> > #include <asm/arch/pch.h> > #include <asm/post.h> > @@ -27,6 +32,10 @@ > > DECLARE_GLOBAL_DATA_PTR; > > +#define CMOS_OFFSET_MRC_SEED 152 > +#define CMOS_OFFSET_MRC_SEED_S3 156 > +#define CMOS_OFFSET_MRC_SEED_CHK 160 > + > /* > * This function looks for the highest region of memory lower than 4GB which > * has enough space for U-Boot where U-Boot is aligned on a page boundary. > @@ -80,6 +89,195 @@ void dram_init_banksize(void) > } > } > > +static int get_mrc_entry(struct spi_flash **sfp, struct fmap_entry *entry) > +{ > + const void *blob = gd->fdt_blob; > + int node, spi_node, mrc_node; > + int upto; > + > + /* Find the flash chip within the SPI controller node */ > + upto = 0; > + spi_node = fdtdec_next_alias(blob, "spi", COMPAT_INTEL_ICH9_SPI, > &upto); > + if (spi_node < 0) > + return -ENOENT; > + node = fdt_first_subnode(blob, spi_node); > + if (node < 0) > + return -ECHILD; > + > + /* Find the place where we put the MRC cache */ > + mrc_node = fdt_subnode_offset(blob, node, "rw-mrc-cache"); > + if (mrc_node < 0) > + return -EPERM; > + > + if (fdtdec_read_fmap_entry(blob, mrc_node, "rm-mrc-cache", entry)) > + return -EINVAL; > + > + if (sfp) { > + *sfp = spi_flash_probe_fdt(blob, node, spi_node); > + if (!*sfp) > + return -EBADF; > + } > + > + return 0; > +} > + > +static int read_seed_from_cmos(struct pei_data *pei_data) > +{ > + u16 c1, c2, checksum, seed_checksum; > + > + /* Read scrambler seeds from CMOS */ > + pei_data->scrambler_seed = cmos_read32(CMOS_OFFSET_MRC_SEED); > + debug("Read scrambler seed 0x%08x from CMOS 0x%02x\n", > + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); > + > + pei_data->scrambler_seed_s3 = cmos_read32(CMOS_OFFSET_MRC_SEED_S3); > + debug("Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n", > + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); > + > + /* Compute seed checksum and compare */ > + c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed, > + sizeof(u32)); > + c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3, > + sizeof(u32)); > + checksum = add_ip_checksums(sizeof(u32), c1, c2); > + > + seed_checksum = cmos_read(CMOS_OFFSET_MRC_SEED_CHK); > + seed_checksum |= cmos_read(CMOS_OFFSET_MRC_SEED_CHK + 1) << 8; > + > + if (checksum != seed_checksum) { > + debug("%s: invalid seed checksum\n", __func__); > + pei_data->scrambler_seed = 0; > + pei_data->scrambler_seed_s3 = 0; > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int prepare_mrc_cache(struct pei_data *pei_data) > +{ > + struct mrc_data_container *mrc_cache; > + struct fmap_entry entry; > + int ret; > + > + ret = read_seed_from_cmos(pei_data); > + if (ret) > + return ret; > + ret = get_mrc_entry(NULL, &entry); > + if (ret) > + return ret; > + mrc_cache = mrccache_find_current(&entry); > + if (!mrc_cache) > + return -ENOENT; > + > + /* Skip this for now as it causes boot problems */ Add a TODO:? > + if (0) { > + pei_data->mrc_input = mrc_cache->data; > + pei_data->mrc_input_len = mrc_cache->data_size; > + > + debug("%s: at %p, size %x checksum %04x\n", __func__, > + pei_data->mrc_input, pei_data->mrc_input_len, > + mrc_cache->checksum); > + } > + > + return 0; > +} > + > +static int build_mrc_data(struct mrc_data_container **datap) > +{ > + struct mrc_data_container *data; > + int orig_len; > + int output_len; > + > + orig_len = gd->arch.mrc_output_len; > + output_len = ALIGN(orig_len, 16); > + data = malloc(output_len + sizeof(*data)); > + if (!data) > + return -ENOMEM; > + data->signature = MRC_DATA_SIGNATURE; > + data->data_size = output_len; > + data->reserved = 0; > + memcpy(data->data, gd->arch.mrc_output, orig_len); > + > + /* Zero the unused space in aligned buffer. */ > + if (output_len > orig_len) > + memset(data->data + orig_len, 0, output_len - orig_len); > + > + data->checksum = compute_ip_checksum(data->data, output_len); > + *datap = data; > + > + return 0; > +} > + > +static int write_seeds_to_cmos(struct pei_data *pei_data) > +{ > + u16 c1, c2, checksum; > + > + /* Save the MRC seed values to CMOS */ > + cmos_write32(CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed); > + debug("Save scrambler seed 0x%08x to CMOS 0x%02x\n", > + pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); > + > + cmos_write32(CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3); > + debug("Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n", > + pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); > + > + /* Save a simple checksum of the seed values */ > + c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed, > + sizeof(u32)); > + c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3, > + sizeof(u32)); > + checksum = add_ip_checksums(sizeof(u32), c1, c2); > + > + cmos_write(checksum & 0xff, CMOS_OFFSET_MRC_SEED_CHK); > + cmos_write((checksum >> 8) & 0xff, CMOS_OFFSET_MRC_SEED_CHK+1); > + > + return 0; > +} > + > +static int sdram_save_mrc_data(void) > +{ > + struct mrc_data_container *data; > + struct fmap_entry entry; > + struct spi_flash *sf; > + int ret; > + > + if (!gd->arch.mrc_output_len) > + return 0; > + debug("Saving %d bytes of MRC output data to SPI flash\n", > + gd->arch.mrc_output_len); > + > + ret = get_mrc_entry(&sf, &entry); > + if (ret) > + goto err_entry; > + ret = build_mrc_data(&data); > + if (ret) > + goto err_data; > + ret = mrccache_update(sf, &entry, data); > + if (!ret) > + debug("Saved MRC data with checksum %04x\n", data->checksum); > + > + free(data); > +err_data: > + spi_flash_free(sf); > +err_entry: > + if (ret) > + debug("%s: Failed: %d\n", __func__, ret); > + return ret; > +} > + > +/* Use this hook to save our SDRAM parameters */ > +int misc_init_r(void) > +{ > + int ret; > + > + ret = sdram_save_mrc_data(); > + if (ret) > + printf("Unable to save MRC data: %d\n", ret); > + > + return 0; > +} > + > static const char *const ecc_decoder[] = { > "inactive", > "active on IO", > @@ -142,6 +340,11 @@ static asmlinkage void console_tx_byte(unsigned char > byte) > #endif > } > > +static int recovery_mode_enabled(void) > +{ > + return false; > +} > + > /** > * Find the PEI executable in the ROM and execute it. > * > @@ -166,6 +369,17 @@ int sdram_initialise(struct pei_data *pei_data) > > debug("Starting UEFI PEI System Agent\n"); > > + /* > + * Do not pass MRC data in for recovery mode boot, > + * Always pass it in for S3 resume. > + */ > + if (!recovery_mode_enabled() || > + pei_data->boot_mode == PEI_BOOT_RESUME) { > + ret = prepare_mrc_cache(pei_data); > + if (ret) > + debug("prepare_mrc_cache failed: %d\n", ret); > + } > + > /* If MRC data is not found we cannot continue S3 resume. */ > if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) { > debug("Giving up in sdram_initialize: No MRC data\n"); > @@ -216,6 +430,8 @@ int sdram_initialise(struct pei_data *pei_data) > debug("System Agent Version %d.%d.%d Build %d\n", > version >> 24 , (version >> 16) & 0xff, > (version >> 8) & 0xff, version & 0xff); > + debug("MCR output data length %#x at %p\n", pei_data->mrc_output_len, > + pei_data->mrc_output); > > /* > * Send ME init done for SandyBridge here. This is done inside the > @@ -231,6 +447,36 @@ int sdram_initialise(struct pei_data *pei_data) > post_system_agent_init(pei_data); > report_memory_config(); > > + /* S3 resume: don't save scrambler seed or MRC data */ > + if (pei_data->boot_mode != PEI_BOOT_RESUME) { > + /* > + * This will be copied to SDRAM in reserve_arch(), then > written > + * to SPI flash in sdram_save_mrc_data() > + */ > + gd->arch.mrc_output = (char *)pei_data->mrc_output; > + gd->arch.mrc_output_len = pei_data->mrc_output_len; > + ret = write_seeds_to_cmos(pei_data); > + if (ret) > + debug("Failed to write seeds to CMOS: %d\n", ret); > + } > + > + return 0; > +} > + > +int reserve_arch(void) > +{ > + u16 checksum; > + > + checksum = compute_ip_checksum(gd->arch.mrc_output, > + gd->arch.mrc_output_len); > + debug("Saving %d bytes for MRC output data, checksum %04x\n", > + gd->arch.mrc_output_len, checksum); > + gd->start_addr_sp -= gd->arch.mrc_output_len; > + memcpy((void *)gd->start_addr_sp, gd->arch.mrc_output, > + gd->arch.mrc_output_len); > + gd->arch.mrc_output = (char *)gd->start_addr_sp; > + gd->start_addr_sp &= ~0xf; > + > return 0; > } > > @@ -569,6 +815,37 @@ int dram_init(void) > > writew(0xCAFE, MCHBAR_REG(SSKPD)); > > + /* TODO: Work out ACPI resume */ Or maybe we can simply remove the following as there are lots of coreboot sutff. > +#if CONFIG_HAVE_ACPI_RESUME && 0 > + /* > + * If there is no high memory area, we didn't boot before, so > + * this is not a resume. In that case we just create the cbmem toc. > + */ > + > + *(u32 *)CBMEM_BOOT_MODE = 0; > + *(u32 *)CBMEM_RESUME_BACKUP = 0; > + > + if ((boot_mode == 2) && cbmem_was_initted) { > + void *resume_backup_memory = cbmem_find(CBMEM_ID_RESUME); > + if (resume_backup_memory) { > + *(u32 *)CBMEM_BOOT_MODE = boot_mode; > + *(u32 *)CBMEM_RESUME_BACKUP = > (u32)resume_backup_memory; > + } > + /* Magic for S3 resume */ > + pci_write_config32(PCI_BDF(0, 0x00, 0), SKPAD, 0xcafed00d); > + } else if (boot_mode == 2) { > + /* Failed S3 resume, reset to come up cleanly */ > + outb(0x6, 0xcf9); > + cpu_hlt(); > + } else { > + pci_write_config32(PCI_BDF(0, 0x00, 0), SKPAD, 0xcafebabe); > + } > +#endif > + post_code(0x3f); > +#if CONFIG_CHROMEOS > + init_chromeos(boot_mode); > +#endif > + > post_code(POST_DRAM); > > ret = sdram_find(dev); > diff --git a/arch/x86/include/asm/arch-ivybridge/mrccache.h > b/arch/x86/include/asm/arch-ivybridge/mrccache.h > new file mode 100644 > index 0000000..968b2ef > --- /dev/null > +++ b/arch/x86/include/asm/arch-ivybridge/mrccache.h > @@ -0,0 +1,51 @@ > +/* > + * Copyright (c) 2014 Google, Inc > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef _ASM_ARCH_MRCCACHE_H > +#define _ASM_ARCH_MRCCACHE_H > + > +#define MRC_DATA_ALIGN 0x1000 > +#define MRC_DATA_SIGNATURE (('M' << 0) | ('R' << 8) | ('C' << 16) | \ > + ('D'<<24)) > + > +__packed struct mrc_data_container { > + u32 signature; /* "MRCD" */ > + u32 data_size; /* Size of the 'data' field */ > + u32 checksum; /* IP style checksum */ > + u32 reserved; /* For header alignment */ > + u8 data[0]; /* Variable size, platform/run time dependent > */ > +}; > + > +struct fmap_entry; > +struct spi_flash; > + > +/** > + * mrccache_find_current() - find the latest MRC cache record > + * > + * This searches the MRC cache region looking for the latest record to use > + * for setting up SDRAM > + * > + * @entry: Information about the position and size of the MRC cache > + * @return pointer to latest record, or NULL if none > + */ > +struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry); > + > +/** > + * mrccache_update() - update the MRC cache with a new record > + * > + * This writes a new record to the end of the MRC cache. If the new record is > + * the same as the latest record then the write is skipped > + * > + * @sf: SPI flash to write to > + * @entry: Position and size of MRC cache in SPI flash > + * @cur: Record to write > + * @return 0 if updated, -EEXIST if the record is the same as the latest > + * record, other error if SPI write failed > + */ > +int mrccache_update(struct spi_flash *sf, struct fmap_entry *entry, > + struct mrc_data_container *cur); > + > +#endif > diff --git a/arch/x86/include/asm/global_data.h > b/arch/x86/include/asm/global_data.h > index 15e76f6..b7e7faf 100644 > --- a/arch/x86/include/asm/global_data.h > +++ b/arch/x86/include/asm/global_data.h > @@ -65,6 +65,9 @@ struct arch_global_data { > #endif > struct mtrr_request mtrr_req[MAX_MTRR_REQUESTS]; > int mtrr_req_count; > + /* MRC training data to save for the next boot */ > + char *mrc_output; > + unsigned int mrc_output_len; > }; > > #endif > -- Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot