summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/intel/haswell/hostbridge_regs.h4
-rw-r--r--src/northbridge/intel/haswell/raminit.c48
-rw-r--r--src/northbridge/intel/sandybridge/hostbridge_regs.h5
-rw-r--r--src/northbridge/intel/sandybridge/raminit.c58
4 files changed, 112 insertions, 3 deletions
diff --git a/src/northbridge/intel/haswell/hostbridge_regs.h b/src/northbridge/intel/haswell/hostbridge_regs.h
index f5fa54aef7..70e15b74e2 100644
--- a/src/northbridge/intel/haswell/hostbridge_regs.h
+++ b/src/northbridge/intel/haswell/hostbridge_regs.h
@@ -60,7 +60,11 @@
#define SKPAD 0xdc /* Scratchpad Data */
#define CAPID0_A 0xe4
+#define CAPID_ECCDIS (1 << 25)
#define VTD_DISABLE (1 << 23)
+#define CAPID_DDPCD (1 << 14)
+#define CAPID_PDCD (1 << 12)
+#define CAPID_DDRSZ(x) (((x) >> 19) & 0x3)
#define CAPID0_B 0xe8
diff --git a/src/northbridge/intel/haswell/raminit.c b/src/northbridge/intel/haswell/raminit.c
index 5d67954125..0b5969249a 100644
--- a/src/northbridge/intel/haswell/raminit.c
+++ b/src/northbridge/intel/haswell/raminit.c
@@ -10,6 +10,7 @@
#include <memory_info.h>
#include <mrc_cache.h>
#include <device/pci_def.h>
+#include <device/pci_ops.h>
#include <device/dram/ddr3.h>
#include <smbios.h>
#include <spd.h>
@@ -170,6 +171,45 @@ void sdram_initialize(struct pei_data *pei_data)
report_memory_config();
}
+static bool nb_supports_ecc(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_ECCDIS);
+}
+
+static uint16_t nb_slots_per_channel(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_DDPCD) + 1;
+}
+
+static uint16_t nb_number_of_channels(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_PDCD) + 1;
+}
+
+static uint32_t nb_max_chan_capacity_mib(const uint32_t capid0_a)
+{
+ uint32_t ddrsz;
+
+ /* Values from documentation, which assume two DIMMs per channel */
+ switch (CAPID_DDRSZ(capid0_a)) {
+ case 1:
+ ddrsz = 8192;
+ break;
+ case 2:
+ ddrsz = 2048;
+ break;
+ case 3:
+ ddrsz = 512;
+ break;
+ default:
+ ddrsz = 16384;
+ break;
+ }
+
+ /* Account for the maximum number of DIMMs per channel */
+ return (ddrsz / 2) * nb_slots_per_channel(capid0_a);
+}
+
void setup_sdram_meminfo(struct pei_data *pei_data)
{
u32 addr_decode_ch[2];
@@ -221,4 +261,12 @@ void setup_sdram_meminfo(struct pei_data *pei_data)
}
}
mem_info->dimm_cnt = dimm_cnt;
+
+ const uint32_t capid0_a = pci_read_config32(HOST_BRIDGE, CAPID0_A);
+
+ const uint16_t channels = nb_number_of_channels(capid0_a);
+
+ mem_info->ecc_capable = nb_supports_ecc(capid0_a);
+ mem_info->max_capacity_mib = channels * nb_max_chan_capacity_mib(capid0_a);
+ mem_info->number_of_devices = channels * nb_slots_per_channel(capid0_a);
}
diff --git a/src/northbridge/intel/sandybridge/hostbridge_regs.h b/src/northbridge/intel/sandybridge/hostbridge_regs.h
index 00d37d4a6d..2d2fcff3b1 100644
--- a/src/northbridge/intel/sandybridge/hostbridge_regs.h
+++ b/src/northbridge/intel/sandybridge/hostbridge_regs.h
@@ -49,6 +49,11 @@
#define TOLUD 0xbc /* Top of Low Used Memory */
#define CAPID0_A 0xe4 /* Capabilities Register A */
+#define CAPID_ECCDIS (1 << 25)
+#define CAPID_DDPCD (1 << 14)
+#define CAPID_PDCD (1 << 12)
+#define CAPID_DDRSZ(x) (((x) >> 19) & 0x3)
+
#define CAPID0_B 0xe8 /* Capabilities Register B */
#define SKPAD 0xdc /* Scratchpad Data */
diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c
index 422067b5d2..2728037ac9 100644
--- a/src/northbridge/intel/sandybridge/raminit.c
+++ b/src/northbridge/intel/sandybridge/raminit.c
@@ -54,8 +54,47 @@ static void disable_channel(ramctr_timing *ctrl, int channel)
memset(&ctrl->info.dimm[channel][0], 0, sizeof(ctrl->info.dimm[0]));
}
-/* Fill cbmem with information for SMBIOS type 17 */
-static void fill_smbios17(ramctr_timing *ctrl)
+static bool nb_supports_ecc(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_ECCDIS);
+}
+
+static uint16_t nb_slots_per_channel(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_DDPCD) + 1;
+}
+
+static uint16_t nb_number_of_channels(const uint32_t capid0_a)
+{
+ return !(capid0_a & CAPID_PDCD) + 1;
+}
+
+static uint32_t nb_max_chan_capacity_mib(const uint32_t capid0_a)
+{
+ uint32_t ddrsz;
+
+ /* Values from documentation, which assume two DIMMs per channel */
+ switch (CAPID_DDRSZ(capid0_a)) {
+ case 1:
+ ddrsz = 8192;
+ break;
+ case 2:
+ ddrsz = 2048;
+ break;
+ case 3:
+ ddrsz = 512;
+ break;
+ default:
+ ddrsz = 16384;
+ break;
+ }
+
+ /* Account for the maximum number of DIMMs per channel */
+ return (ddrsz / 2) * nb_slots_per_channel(capid0_a);
+}
+
+/* Fill cbmem with information for SMBIOS type 16 and type 17 */
+static void setup_sdram_meminfo(ramctr_timing *ctrl)
{
int channel, slot;
const u16 ddr_freq = (1000 << 8) / ctrl->tCK;
@@ -66,6 +105,19 @@ static void fill_smbios17(ramctr_timing *ctrl)
if (ret != CB_SUCCESS)
printk(BIOS_ERR, "RAMINIT: Failed to add SMBIOS17\n");
}
+
+ /* The 'spd_add_smbios17' function allocates this CBMEM area */
+ struct memory_info *m = cbmem_find(CBMEM_ID_MEMINFO);
+ if (m == NULL)
+ return;
+
+ const uint32_t capid0_a = pci_read_config32(HOST_BRIDGE, CAPID0_A);
+
+ const uint16_t channels = nb_number_of_channels(capid0_a);
+
+ m->ecc_capable = nb_supports_ecc(capid0_a);
+ m->max_capacity_mib = channels * nb_max_chan_capacity_mib(capid0_a);
+ m->number_of_devices = channels * nb_slots_per_channel(capid0_a);
}
/* Return CRC16 match for all SPDs */
@@ -386,7 +438,7 @@ static void init_dram_ddr3(int s3resume, const u32 cpuid)
}
if (!s3resume)
- fill_smbios17(&ctrl);
+ setup_sdram_meminfo(&ctrl);
}
void perform_raminit(int s3resume)