Hi Bin, On 4 January 2015 at 00:49, Bin Meng <bmeng...@gmail.com> wrote: > 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?
It's about 3KB of data, so doesn't fit in CMOS. > >> 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:? OK > >> + 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; >> +} >> + [snip] >> @@ -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. Will do! > >> +#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); [snip] Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot