[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