diff options
-rw-r--r-- | src/northbridge/intel/sandybridge/chip.h | 2 | ||||
-rw-r--r-- | src/northbridge/intel/sandybridge/raminit.c | 54 | ||||
-rw-r--r-- | src/northbridge/intel/sandybridge/raminit.h | 29 | ||||
-rw-r--r-- | src/northbridge/intel/sandybridge/raminit_mrc.c | 63 |
4 files changed, 137 insertions, 11 deletions
diff --git a/src/northbridge/intel/sandybridge/chip.h b/src/northbridge/intel/sandybridge/chip.h index 99d9763241..82e58f6186 100644 --- a/src/northbridge/intel/sandybridge/chip.h +++ b/src/northbridge/intel/sandybridge/chip.h @@ -45,7 +45,7 @@ struct northbridge_intel_sandybridge_config { /* Data for RAM init */ - /* DIMM SPD address. Use 8bit notation where BIT0 is always zero. */ + /* DIMM SPD address. */ u8 spd_addresses[4]; /* PEI data for RAM init and early silicon init */ diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c index 82931da3d9..2575a9c6fc 100644 --- a/src/northbridge/intel/sandybridge/raminit.c +++ b/src/northbridge/intel/sandybridge/raminit.c @@ -4,7 +4,10 @@ #include <commonlib/region.h> #include <cf9_reset.h> #include <string.h> +#include <cbfs.h> #include <arch/cpu.h> +#include <device/device.h> +#include <device/dram/ddr3.h> #include <device/mmio.h> #include <device/pci_ops.h> #include <device/smbus_host.h> @@ -16,9 +19,11 @@ #include <cpu/x86/msr.h> #include <types.h> +#include "raminit.h" #include "raminit_native.h" #include "raminit_common.h" #include "sandybridge.h" +#include "chip.h" /* FIXME: no support for 3-channel chipsets */ @@ -139,14 +144,59 @@ void read_spd(spd_raw_data * spd, u8 addr, bool id_only) { int j; if (id_only) { - for (j = 117; j < 128; j++) + for (j = SPD_DIMM_MOD_ID1; j < 128; j++) (*spd)[j] = smbus_read_byte(addr, j); } else { - for (j = 0; j < 256; j++) + for (j = 0; j < SPD_SIZE_MAX_DDR3; j++) (*spd)[j] = smbus_read_byte(addr, j); } } +/* Temporary stub */ +__weak void mb_get_spd_map(struct spd_info *spdi) {} + +__weak void mainboard_get_spd(spd_raw_data *spd, bool id_only) +{ + const struct northbridge_intel_sandybridge_config *cfg = config_of_soc(); + unsigned int i; + + if (CONFIG(HAVE_SPD_IN_CBFS)) { + struct spd_info spdi = {0}; + + mb_get_spd_map(&spdi); + + size_t spd_file_len; + uint8_t *spd_file = cbfs_map("spd.bin", &spd_file_len); + + printk(BIOS_DEBUG, "SPD index %d\n", spdi.spd_index); + + /* SPD file sanity check */ + if (!spd_file) + die("SPD data %s!", "not found"); + + if (spd_file_len < ((spdi.spd_index + 1) * SPD_SIZE_MAX_DDR3)) + die("SPD data %s!", "incomplete"); + + /* + * Copy SPD data specified by spd_info.spd_index to all slots marked as + * SPD_MEMORY_DOWN. + * + * Read SPD data from slots with a real SMBus address. + */ + for (i = 0; i < ARRAY_SIZE(spdi.addresses); i++) { + if (spdi.addresses[i] == SPD_MEMORY_DOWN) + memcpy(&spd[i], spd_file + (spdi.spd_index * SPD_SIZE_MAX_DDR3), SPD_SIZE_MAX_DDR3); + else if (spdi.addresses[i] != 0) + read_spd(&spd[i], spdi.addresses[i], id_only); + } + } else { + for (i = 0; i < ARRAY_SIZE(cfg->spd_addresses); i++) { + if (cfg->spd_addresses[i] != 0) + read_spd(&spd[i], cfg->spd_addresses[i], id_only); + } + } /* CONFIG(HAVE_SPD_IN_CBFS) */ +} + static void dram_find_spds_ddr3(spd_raw_data *spd, ramctr_timing *ctrl) { int dimms = 0, ch_dimms; diff --git a/src/northbridge/intel/sandybridge/raminit.h b/src/northbridge/intel/sandybridge/raminit.h index fa9fa98680..8e87fdf402 100644 --- a/src/northbridge/intel/sandybridge/raminit.h +++ b/src/northbridge/intel/sandybridge/raminit.h @@ -11,4 +11,33 @@ void mainboard_fill_pei_data(struct pei_data *pei_data); +/* + * SPD information API adopted from nb/intel/haswell. + * + * This applies to both MRC and native raminit, only for boards with + * CONFIG(HAVE_SPD_IN_CBFS). + * + * spd_info.addresses is an array of 4 bytes representing the SMBus addresses + * of the SPD EEPROM of (respectively) Channel 0 Slot 0, C0S1, C1S0, C1S1. + * Boards with onboard memory for the slot without actual SPD EEPROM enter + * SPD_MEMORY_DOWN in that position and enter in spd_info.spd_index a 0-based index into + * spd.bin file in CBFS, which is a concatenation of 256-byte SPD data blobs. + * + * Only one set of SPD data is supported. + */ + +#define SPD_MEMORY_DOWN 0xFF + +struct spd_info { + uint8_t addresses[4]; + unsigned int spd_index; +}; + +/* + * Mainboard callback to fill in the SPD addresses. + * + * @param spdi Pointer to spd_info struct to be populated by mainboard. + */ +void mb_get_spd_map(struct spd_info *spdi); + #endif /* RAMINIT_H */ diff --git a/src/northbridge/intel/sandybridge/raminit_mrc.c b/src/northbridge/intel/sandybridge/raminit_mrc.c index 69baf44270..de41ae590f 100644 --- a/src/northbridge/intel/sandybridge/raminit_mrc.c +++ b/src/northbridge/intel/sandybridge/raminit_mrc.c @@ -264,13 +264,7 @@ static void southbridge_fill_pei_data(struct pei_data *pei_data) static void devicetree_fill_pei_data(struct pei_data *pei_data) { - const struct northbridge_intel_sandybridge_config *cfg; - - const struct device *dev = pcidev_on_root(0, 0); - if (!dev || !dev->chip_info) - return; - - cfg = dev->chip_info; + const struct northbridge_intel_sandybridge_config *cfg = config_of_soc(); switch (cfg->max_mem_clock_mhz) { /* MRC only supports fixed numbers of frequencies */ @@ -292,7 +286,15 @@ static void devicetree_fill_pei_data(struct pei_data *pei_data) } - memcpy(pei_data->spd_addresses, cfg->spd_addresses, sizeof(pei_data->spd_addresses)); + /* + * SPD addresses are listed in devicetree as actual addresses, + * and for MRC need to be shifted left so bit 0 is always zero. + */ + if (!CONFIG(HAVE_SPD_IN_CBFS)) { + for (unsigned int i = 0; i < ARRAY_SIZE(cfg->spd_addresses); i++) { + pei_data->spd_addresses[i] = cfg->spd_addresses[i] << 1; + } + } memcpy(pei_data->ts_addresses, cfg->ts_addresses, sizeof(pei_data->ts_addresses)); pei_data->ec_present = cfg->ec_present; @@ -310,6 +312,49 @@ static void devicetree_fill_pei_data(struct pei_data *pei_data) pei_data->usb3.xhci_streams = cfg->usb3.xhci_streams; } +/* Temporary stub */ +__weak void mb_get_spd_map(struct spd_info *spdi) {} + +static void spd_fill_pei_data(struct pei_data *pei_data) +{ + struct spd_info spdi = {0}; + unsigned int i, have_memory_down; + + mb_get_spd_map(&spdi); + + for (i = 0; i < ARRAY_SIZE(spdi.addresses); i++) { + if (spdi.addresses[i] == SPD_MEMORY_DOWN) { + pei_data->spd_addresses[i] = 0; + have_memory_down = 1; + } else { + /* MRC expects left-aligned SMBus addresses. */ + pei_data->spd_addresses[i] = spdi.addresses[i] << 1; + } + } + /* Copy SPD data from CBFS for on-board memory */ + if (have_memory_down) { + printk(BIOS_DEBUG, "SPD index %d\n", spdi.spd_index); + + size_t spd_file_len; + uint8_t *spd_file = cbfs_map("spd.bin", &spd_file_len); + + if (!spd_file) + die("SPD data %s!", "not found"); + + if (spd_file_len < ((spdi.spd_index + 1) * SPD_SIZE_MAX_DDR3)) + die("SPD data %s!", "incomplete"); + + /* MRC only uses index 0... */ + memcpy(pei_data->spd_data[0], spd_file + (spdi.spd_index * SPD_SIZE_MAX_DDR3), SPD_SIZE_MAX_DDR3); + + /* but coreboot uses the other indices */ + for (i = 1; i < ARRAY_SIZE(spdi.addresses); i++) { + if (spdi.addresses[i] == SPD_MEMORY_DOWN) + memcpy(pei_data->spd_data[i], pei_data->spd_data[0], SPD_SIZE_MAX_DDR3); + } + } +} + static void disable_p2p(void) { /* Disable PCI-to-PCI bridge early to prevent probing by MRC */ @@ -337,6 +382,8 @@ void perform_raminit(int s3resume) northbridge_fill_pei_data(&pei_data); southbridge_fill_pei_data(&pei_data); devicetree_fill_pei_data(&pei_data); + if (CONFIG(HAVE_SPD_IN_CBFS)) + spd_fill_pei_data(&pei_data); mainboard_fill_pei_data(&pei_data); post_code(0x3a); |