summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/intel/sandybridge/chip.h2
-rw-r--r--src/northbridge/intel/sandybridge/raminit.c54
-rw-r--r--src/northbridge/intel/sandybridge/raminit.h29
-rw-r--r--src/northbridge/intel/sandybridge/raminit_mrc.c63
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);