[U-Boot] [PATCH 07/10] x86: Implement a cache for Memory Reference Code parameters
Simon Glass
sjg at chromium.org
Mon Jan 5 02:49:52 CET 2015
Hi Bin,
On 4 January 2015 at 00:49, Bin Meng <bmeng.cn at gmail.com> wrote:
> Hi Simon,
>
> On Tue, Dec 30, 2014 at 9:12 AM, Simon Glass <sjg at 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 <sjg at 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
More information about the U-Boot
mailing list