]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - arch/x86/cpu/ivybridge/sdram.c
x86: Rename MMCONF_BASE_ADDRESS and make it common across x86
[karo-tx-uboot.git] / arch / x86 / cpu / ivybridge / sdram.c
index 5f9ae5ecdbcd4c62176450d3f4687171ec78ba00..766b385c252a553262aeb4b5d8cf7003aa3e63fa 100644 (file)
  */
 
 #include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <net.h>
+#include <rtc.h>
+#include <spi.h>
+#include <spi_flash.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>
+#include <asm/arch/sandybridge.h>
+
+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.
+ * It overrides the default implementation found elsewhere which simply
+ * picks the end of ram, wherever that may be. The location of the stack,
+ * the relocation address, and how far U-Boot is moved by relocation are
+ * set in the global data structure.
+ */
+ulong board_get_usable_ram_top(ulong total_size)
+{
+       struct memory_info *info = &gd->arch.meminfo;
+       uintptr_t dest_addr = 0;
+       struct memory_area *largest = NULL;
+       int i;
+
+       /* Find largest area of memory below 4GB */
+
+       for (i = 0; i < info->num_areas; i++) {
+               struct memory_area *area = &info->area[i];
+
+               if (area->start >= 1ULL << 32)
+                       continue;
+               if (!largest || area->size > largest->size)
+                       largest = area;
+       }
+
+       /* If no suitable area was found, return an error. */
+       assert(largest);
+       if (!largest || largest->size < (2 << 20))
+               panic("No available memory found for relocation");
+
+       dest_addr = largest->start + largest->size;
+
+       return (ulong)dest_addr;
+}
+
+void dram_init_banksize(void)
+{
+       struct memory_info *info = &gd->arch.meminfo;
+       int num_banks;
+       int i;
+
+       for (i = 0, num_banks = 0; i < info->num_areas; i++) {
+               struct memory_area *area = &info->area[i];
+
+               if (area->start >= 1ULL << 32)
+                       continue;
+               gd->bd->bi_dram[num_banks].start = area->start;
+               gd->bd->bi_dram[num_banks].size = area->size;
+               num_banks++;
+       }
+}
+
+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_ICH_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 RAM. We don't want to store them in
+        * SPI flash since they change on every boot and that would wear down
+        * the flash too much. So we store these in CMOS and the large MRC
+        * data in SPI flash.
+        */
+       pei_data->scrambler_seed = rtc_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 = rtc_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 = rtc_read8(CMOS_OFFSET_MRC_SEED_CHK);
+       seed_checksum |= rtc_read8(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;
+
+       /*
+        * TODO(sjg@chromium.org): Skip this for now as it causes boot
+        * problems
+        */
+       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 */
+       rtc_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);
+
+       rtc_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);
+
+       rtc_write8(CMOS_OFFSET_MRC_SEED_CHK, checksum & 0xff);
+       rtc_write8(CMOS_OFFSET_MRC_SEED_CHK + 1, (checksum >> 8) & 0xff);
+
+       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",
+       "disabled on IO",
+       "active"
+};
+
+/*
+ * Dump in the log memory controller configuration as read from the memory
+ * controller registers.
+ */
+static void report_memory_config(void)
+{
+       u32 addr_decoder_common, addr_decode_ch[2];
+       int i;
+
+       addr_decoder_common = readl(MCHBAR_REG(0x5000));
+       addr_decode_ch[0] = readl(MCHBAR_REG(0x5004));
+       addr_decode_ch[1] = readl(MCHBAR_REG(0x5008));
+
+       debug("memcfg DDR3 clock %d MHz\n",
+             (readl(MCHBAR_REG(0x5e04)) * 13333 * 2 + 50) / 100);
+       debug("memcfg channel assignment: A: %d, B % d, C % d\n",
+             addr_decoder_common & 3,
+             (addr_decoder_common >> 2) & 3,
+             (addr_decoder_common >> 4) & 3);
+
+       for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) {
+               u32 ch_conf = addr_decode_ch[i];
+               debug("memcfg channel[%d] config (%8.8x):\n", i, ch_conf);
+               debug("   ECC %s\n", ecc_decoder[(ch_conf >> 24) & 3]);
+               debug("   enhanced interleave mode %s\n",
+                     ((ch_conf >> 22) & 1) ? "on" : "off");
+               debug("   rank interleave %s\n",
+                     ((ch_conf >> 21) & 1) ? "on" : "off");
+               debug("   DIMMA %d MB width x%d %s rank%s\n",
+                     ((ch_conf >> 0) & 0xff) * 256,
+                     ((ch_conf >> 19) & 1) ? 16 : 8,
+                     ((ch_conf >> 17) & 1) ? "dual" : "single",
+                     ((ch_conf >> 16) & 1) ? "" : ", selected");
+               debug("   DIMMB %d MB width x%d %s rank%s\n",
+                     ((ch_conf >> 8) & 0xff) * 256,
+                     ((ch_conf >> 20) & 1) ? 16 : 8,
+                     ((ch_conf >> 18) & 1) ? "dual" : "single",
+                     ((ch_conf >> 16) & 1) ? ", selected" : "");
+       }
+}
+
+static void post_system_agent_init(struct pei_data *pei_data)
+{
+       /* If PCIe init is skipped, set the PEG clock gating */
+       if (!pei_data->pcie_init)
+               setbits_le32(MCHBAR_REG(0x7010), 1);
+}
+
+static asmlinkage void console_tx_byte(unsigned char byte)
+{
+#ifdef DEBUG
+       putc(byte);
+#endif
+}
+
+static int recovery_mode_enabled(void)
+{
+       return false;
+}
+
+/**
+ * Find the PEI executable in the ROM and execute it.
+ *
+ * @param pei_data: configuration data for UEFI PEI reference code
+ */
+int sdram_initialise(struct pei_data *pei_data)
+{
+       unsigned version;
+       const char *data;
+       uint16_t done;
+       int ret;
+
+       report_platform_info();
+
+       /* Wait for ME to be ready */
+       ret = intel_early_me_init();
+       if (ret)
+               return ret;
+       ret = intel_early_me_uma_size();
+       if (ret < 0)
+               return ret;
+
+       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");
+               outb(0x6, PORT_RESET);
+               cpu_hlt();
+       }
+
+       /* Pass console handler in pei_data */
+       pei_data->tx_byte = console_tx_byte;
+
+       debug("PEI data at %p, size %x:\n", pei_data, sizeof(*pei_data));
+
+       data = (char *)CONFIG_X86_MRC_ADDR;
+       if (data) {
+               int rv;
+               int (*func)(struct pei_data *);
+
+               debug("Calling MRC at %p\n", data);
+               post_code(POST_PRE_MRC);
+               func = (int (*)(struct pei_data *))data;
+               rv = func(pei_data);
+               post_code(POST_MRC);
+               if (rv) {
+                       switch (rv) {
+                       case -1:
+                               printf("PEI version mismatch.\n");
+                               break;
+                       case -2:
+                               printf("Invalid memory frequency.\n");
+                               break;
+                       default:
+                               printf("MRC returned %x.\n", rv);
+                       }
+                       printf("Nonzero MRC return value.\n");
+                       return -EFAULT;
+               }
+       } else {
+               printf("UEFI PEI System Agent not found.\n");
+               return -ENOSYS;
+       }
+
+#if CONFIG_USBDEBUG
+       /* mrc.bin reconfigures USB, so reinit it to have debug */
+       early_usbdebug_init();
+#endif
+
+       version = readl(MCHBAR_REG(0x5034));
+       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
+        * SystemAgent binary on IvyBridge
+        */
+       done = pci_read_config32(PCH_DEV, PCI_DEVICE_ID);
+       done &= BASE_REV_MASK;
+       if (BASE_REV_SNB == done)
+               intel_early_me_init_done(ME_INIT_STATUS_SUCCESS);
+       else
+               intel_early_me_status();
+
+       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;
+}
+
+static int copy_spd(struct pei_data *peid)
+{
+       const int gpio_vector[] = {41, 42, 43, 10, -1};
+       int spd_index;
+       const void *blob = gd->fdt_blob;
+       int node, spd_node;
+       int ret, i;
+
+       for (i = 0; ; i++) {
+               if (gpio_vector[i] == -1)
+                       break;
+               ret = gpio_requestf(gpio_vector[i], "spd_id%d", i);
+               if (ret) {
+                       debug("%s: Could not request gpio %d\n", __func__,
+                             gpio_vector[i]);
+                       return ret;
+               }
+       }
+       spd_index = gpio_get_values_as_int(gpio_vector);
+       debug("spd index %d\n", spd_index);
+       node = fdtdec_next_compatible(blob, 0, COMPAT_MEMORY_SPD);
+       if (node < 0) {
+               printf("SPD data not found.\n");
+               return -ENOENT;
+       }
+
+       for (spd_node = fdt_first_subnode(blob, node);
+            spd_node > 0;
+            spd_node = fdt_next_subnode(blob, spd_node)) {
+               const char *data;
+               int len;
+
+               if (fdtdec_get_int(blob, spd_node, "reg", -1) != spd_index)
+                       continue;
+               data = fdt_getprop(blob, spd_node, "data", &len);
+               if (len < sizeof(peid->spd_data[0])) {
+                       printf("Missing SPD data\n");
+                       return -EINVAL;
+               }
+
+               debug("Using SDRAM SPD data for '%s'\n",
+                     fdt_get_name(blob, spd_node, NULL));
+               memcpy(peid->spd_data[0], data, sizeof(peid->spd_data[0]));
+               break;
+       }
+
+       if (spd_node < 0) {
+               printf("No SPD data found for index %d\n", spd_index);
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+/**
+ * add_memory_area() - Add a new usable memory area to our list
+ *
+ * Note: @start and @end must not span the first 4GB boundary
+ *
+ * @info:      Place to store memory info
+ * @start:     Start of this memory area
+ * @end:       End of this memory area + 1
+ */
+static int add_memory_area(struct memory_info *info,
+                          uint64_t start, uint64_t end)
+{
+       struct memory_area *ptr;
+
+       if (info->num_areas == CONFIG_NR_DRAM_BANKS)
+               return -ENOSPC;
+
+       ptr = &info->area[info->num_areas];
+       ptr->start = start;
+       ptr->size = end - start;
+       info->total_memory += ptr->size;
+       if (ptr->start < (1ULL << 32))
+               info->total_32bit_memory += ptr->size;
+       debug("%d: memory %llx size %llx, total now %llx / %llx\n",
+             info->num_areas, ptr->start, ptr->size,
+             info->total_32bit_memory, info->total_memory);
+       info->num_areas++;
+
+       return 0;
+}
+
+/**
+ * sdram_find() - Find available memory
+ *
+ * This is a bit complicated since on x86 there are system memory holes all
+ * over the place. We create a list of available memory blocks
+ */
+static int sdram_find(pci_dev_t dev)
+{
+       struct memory_info *info = &gd->arch.meminfo;
+       uint32_t tseg_base, uma_size, tolud;
+       uint64_t tom, me_base, touud;
+       uint64_t uma_memory_base = 0;
+       uint64_t uma_memory_size;
+       unsigned long long tomk;
+       uint16_t ggc;
+
+       /* Total Memory 2GB example:
+        *
+        *  00000000  0000MB-1992MB  1992MB  RAM     (writeback)
+        *  7c800000  1992MB-2000MB     8MB  TSEG    (SMRR)
+        *  7d000000  2000MB-2002MB     2MB  GFX GTT (uncached)
+        *  7d200000  2002MB-2034MB    32MB  GFX UMA (uncached)
+        *  7f200000   2034MB TOLUD
+        *  7f800000   2040MB MEBASE
+        *  7f800000  2040MB-2048MB     8MB  ME UMA  (uncached)
+        *  80000000   2048MB TOM
+        * 100000000  4096MB-4102MB     6MB  RAM     (writeback)
+        *
+        * Total Memory 4GB example:
+        *
+        *  00000000  0000MB-2768MB  2768MB  RAM     (writeback)
+        *  ad000000  2768MB-2776MB     8MB  TSEG    (SMRR)
+        *  ad800000  2776MB-2778MB     2MB  GFX GTT (uncached)
+        *  ada00000  2778MB-2810MB    32MB  GFX UMA (uncached)
+        *  afa00000   2810MB TOLUD
+        *  ff800000   4088MB MEBASE
+        *  ff800000  4088MB-4096MB     8MB  ME UMA  (uncached)
+        * 100000000   4096MB TOM
+        * 100000000  4096MB-5374MB  1278MB  RAM     (writeback)
+        * 14fe00000   5368MB TOUUD
+        */
+
+       /* Top of Upper Usable DRAM, including remap */
+       touud = pci_read_config32(dev, TOUUD+4);
+       touud <<= 32;
+       touud |= pci_read_config32(dev, TOUUD);
+
+       /* Top of Lower Usable DRAM */
+       tolud = pci_read_config32(dev, TOLUD);
+
+       /* Top of Memory - does not account for any UMA */
+       tom = pci_read_config32(dev, 0xa4);
+       tom <<= 32;
+       tom |= pci_read_config32(dev, 0xa0);
+
+       debug("TOUUD %llx TOLUD %08x TOM %llx\n", touud, tolud, tom);
+
+       /* ME UMA needs excluding if total memory <4GB */
+       me_base = pci_read_config32(dev, 0x74);
+       me_base <<= 32;
+       me_base |= pci_read_config32(dev, 0x70);
+
+       debug("MEBASE %llx\n", me_base);
+
+       /* TODO: Get rid of all this shifting by 10 bits */
+       tomk = tolud >> 10;
+       if (me_base == tolud) {
+               /* ME is from MEBASE-TOM */
+               uma_size = (tom - me_base) >> 10;
+               /* Increment TOLUD to account for ME as RAM */
+               tolud += uma_size << 10;
+               /* UMA starts at old TOLUD */
+               uma_memory_base = tomk * 1024ULL;
+               uma_memory_size = uma_size * 1024ULL;
+               debug("ME UMA base %llx size %uM\n", me_base, uma_size >> 10);
+       }
+
+       /* Graphics memory comes next */
+       ggc = pci_read_config16(dev, GGC);
+       if (!(ggc & 2)) {
+               debug("IGD decoded, subtracting ");
+
+               /* Graphics memory */
+               uma_size = ((ggc >> 3) & 0x1f) * 32 * 1024ULL;
+               debug("%uM UMA", uma_size >> 10);
+               tomk -= uma_size;
+               uma_memory_base = tomk * 1024ULL;
+               uma_memory_size += uma_size * 1024ULL;
+
+               /* GTT Graphics Stolen Memory Size (GGMS) */
+               uma_size = ((ggc >> 8) & 0x3) * 1024ULL;
+               tomk -= uma_size;
+               uma_memory_base = tomk * 1024ULL;
+               uma_memory_size += uma_size * 1024ULL;
+               debug(" and %uM GTT\n", uma_size >> 10);
+       }
+
+       /* Calculate TSEG size from its base which must be below GTT */
+       tseg_base = pci_read_config32(dev, 0xb8);
+       uma_size = (uma_memory_base - tseg_base) >> 10;
+       tomk -= uma_size;
+       uma_memory_base = tomk * 1024ULL;
+       uma_memory_size += uma_size * 1024ULL;
+       debug("TSEG base 0x%08x size %uM\n", tseg_base, uma_size >> 10);
+
+       debug("Available memory below 4GB: %lluM\n", tomk >> 10);
+
+       /* Report the memory regions */
+       add_memory_area(info, 1 << 20, 2 << 28);
+       add_memory_area(info, (2 << 28) + (2 << 20), 4 << 28);
+       add_memory_area(info, (4 << 28) + (2 << 20), tseg_base);
+       add_memory_area(info, 1ULL << 32, touud);
+
+       /* Add MTRRs for memory */
+       mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30);
+       mtrr_add_request(MTRR_TYPE_WRBACK, 2ULL << 30, 512 << 20);
+       mtrr_add_request(MTRR_TYPE_WRBACK, 0xaULL << 28, 256 << 20);
+       mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base, 16 << 20);
+       mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base + (16 << 20),
+                        32 << 20);
+
+       /*
+        * If >= 4GB installed then memory from TOLUD to 4GB
+        * is remapped above TOM, TOUUD will account for both
+        */
+       if (touud > (1ULL << 32ULL)) {
+               debug("Available memory above 4GB: %lluM\n",
+                     (touud >> 20) - 4096);
+       }
+
+       return 0;
+}
+
+static void rcba_config(void)
+{
+       /*
+        *             GFX    INTA -> PIRQA (MSI)
+        * D28IP_P3IP  WLAN   INTA -> PIRQB
+        * D29IP_E1P   EHCI1  INTA -> PIRQD
+        * D26IP_E2P   EHCI2  INTA -> PIRQF
+        * D31IP_SIP   SATA   INTA -> PIRQF (MSI)
+        * D31IP_SMIP  SMBUS  INTB -> PIRQH
+        * D31IP_TTIP  THRT   INTC -> PIRQA
+        * D27IP_ZIP   HDA    INTA -> PIRQA (MSI)
+        *
+        * TRACKPAD                -> PIRQE (Edge Triggered)
+        * TOUCHSCREEN             -> PIRQG (Edge Triggered)
+        */
+
+       /* Device interrupt pin register (board specific) */
+       writel((INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) |
+              (INTB << D31IP_SMIP) | (INTA << D31IP_SIP), RCB_REG(D31IP));
+       writel(NOINT << D30IP_PIP, RCB_REG(D30IP));
+       writel(INTA << D29IP_E1P, RCB_REG(D29IP));
+       writel(INTA << D28IP_P3IP, RCB_REG(D28IP));
+       writel(INTA << D27IP_ZIP, RCB_REG(D27IP));
+       writel(INTA << D26IP_E2P, RCB_REG(D26IP));
+       writel(NOINT << D25IP_LIP, RCB_REG(D25IP));
+       writel(NOINT << D22IP_MEI1IP, RCB_REG(D22IP));
+
+       /* Device interrupt route registers */
+       writel(DIR_ROUTE(PIRQB, PIRQH, PIRQA, PIRQC), RCB_REG(D31IR));
+       writel(DIR_ROUTE(PIRQD, PIRQE, PIRQF, PIRQG), RCB_REG(D29IR));
+       writel(DIR_ROUTE(PIRQB, PIRQC, PIRQD, PIRQE), RCB_REG(D28IR));
+       writel(DIR_ROUTE(PIRQA, PIRQH, PIRQA, PIRQB), RCB_REG(D27IR));
+       writel(DIR_ROUTE(PIRQF, PIRQE, PIRQG, PIRQH), RCB_REG(D26IR));
+       writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D25IR));
+       writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D22IR));
+
+       /* Enable IOAPIC (generic) */
+       writew(0x0100, RCB_REG(OIC));
+       /* PCH BWG says to read back the IOAPIC enable register */
+       (void)readw(RCB_REG(OIC));
+
+       /* Disable unused devices (board specific) */
+       setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS);
+}
 
 int dram_init(void)
 {
-       /* TODO: Set up DRAM */
+       struct pei_data pei_data __aligned(8) = {
+               .pei_version = PEI_VERSION,
+               .mchbar = DEFAULT_MCHBAR,
+               .dmibar = DEFAULT_DMIBAR,
+               .epbar = DEFAULT_EPBAR,
+               .pciexbar = CONFIG_PCIE_ECAM_BASE,
+               .smbusbar = SMBUS_IO_BASE,
+               .wdbbar = 0x4000000,
+               .wdbsize = 0x1000,
+               .hpet_address = CONFIG_HPET_ADDRESS,
+               .rcba = DEFAULT_RCBABASE,
+               .pmbase = DEFAULT_PMBASE,
+               .gpiobase = DEFAULT_GPIOBASE,
+               .thermalbase = 0xfed08000,
+               .system_type = 0, /* 0 Mobile, 1 Desktop/Server */
+               .tseg_size = CONFIG_SMM_TSEG_SIZE,
+               .ts_addresses = { 0x00, 0x00, 0x00, 0x00 },
+               .ec_present = 1,
+               .ddr3lv_support = 1,
+               /*
+                * 0 = leave channel enabled
+                * 1 = disable dimm 0 on channel
+                * 2 = disable dimm 1 on channel
+                * 3 = disable dimm 0+1 on channel
+                */
+               .dimm_channel0_disabled = 2,
+               .dimm_channel1_disabled = 2,
+               .max_ddr3_freq = 1600,
+               .usb_port_config = {
+                       /*
+                        * Empty and onboard Ports 0-7, set to un-used pin
+                        * OC3
+                        */
+                       { 0, 3, 0x0000 }, /* P0= Empty */
+                       { 1, 0, 0x0040 }, /* P1= Left USB 1  (OC0) */
+                       { 1, 1, 0x0040 }, /* P2= Left USB 2  (OC1) */
+                       { 1, 3, 0x0040 }, /* P3= SDCARD      (no OC) */
+                       { 0, 3, 0x0000 }, /* P4= Empty */
+                       { 1, 3, 0x0040 }, /* P5= WWAN        (no OC) */
+                       { 0, 3, 0x0000 }, /* P6= Empty */
+                       { 0, 3, 0x0000 }, /* P7= Empty */
+                       /*
+                        * Empty and onboard Ports 8-13, set to un-used pin
+                        * OC4
+                        */
+                       { 1, 4, 0x0040 }, /* P8= Camera      (no OC) */
+                       { 1, 4, 0x0040 }, /* P9= Bluetooth   (no OC) */
+                       { 0, 4, 0x0000 }, /* P10= Empty */
+                       { 0, 4, 0x0000 }, /* P11= Empty */
+                       { 0, 4, 0x0000 }, /* P12= Empty */
+                       { 0, 4, 0x0000 }, /* P13= Empty */
+               },
+       };
+       pci_dev_t dev = PCI_BDF(0, 0, 0);
+       int ret;
+
+       debug("Boot mode %d\n", gd->arch.pei_boot_mode);
+       debug("mcr_input %p\n", pei_data.mrc_input);
+       pei_data.boot_mode = gd->arch.pei_boot_mode;
+       ret = copy_spd(&pei_data);
+       if (!ret)
+               ret = sdram_initialise(&pei_data);
+       if (ret)
+               return ret;
+
+       rcba_config();
+       quick_ram_check();
+
+       writew(0xCAFE, MCHBAR_REG(SSKPD));
+
+       post_code(POST_DRAM);
+
+       ret = sdram_find(dev);
+       if (ret)
+               return ret;
+
+       gd->ram_size = gd->arch.meminfo.total_32bit_memory;
 
        return 0;
 }