mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-15 09:27:35 +00:00
ba4575626e
Convert this driver over to use driver model. Since all x86 platforms use it, move x86 to use driver model for SPI and SPI flash. Adjust all dependent code and remove the old x86 spi_init() function. Note that this does not make full use of the new PCI uclass as yet. We still scan the bus looking for the device. It should move to finding its details in the device tree. Signed-off-by: Simon Glass <sjg@chromium.org>
837 lines
22 KiB
C
837 lines
22 KiB
C
/*
|
|
* Copyright (c) 2011 The Chromium OS Authors.
|
|
* (C) Copyright 2010,2011
|
|
* Graeme Russ, <graeme.russ@gmail.com>
|
|
*
|
|
* Portions from Coreboot mainboard/google/link/romstage.c
|
|
* Copyright (C) 2007-2010 coresystems GmbH
|
|
* Copyright (C) 2011 Google Inc.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#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 udevice **devp, struct fmap_entry *entry)
|
|
{
|
|
const void *blob = gd->fdt_blob;
|
|
int node, spi_node, mrc_node;
|
|
int upto;
|
|
int ret;
|
|
|
|
/* 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 (devp) {
|
|
debug("getting sf\n");
|
|
ret = uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node,
|
|
devp);
|
|
debug("ret = %d\n", ret);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
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 udevice *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:
|
|
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 = x86_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 = x86_pci_read_config32(dev, TOUUD+4);
|
|
touud <<= 32;
|
|
touud |= x86_pci_read_config32(dev, TOUUD);
|
|
|
|
/* Top of Lower Usable DRAM */
|
|
tolud = x86_pci_read_config32(dev, TOLUD);
|
|
|
|
/* Top of Memory - does not account for any UMA */
|
|
tom = x86_pci_read_config32(dev, 0xa4);
|
|
tom <<= 32;
|
|
tom |= x86_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 = x86_pci_read_config32(dev, 0x74);
|
|
me_base <<= 32;
|
|
me_base |= x86_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 = x86_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 = x86_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)
|
|
{
|
|
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;
|
|
}
|