nb/intel/*: Fill in SMBIOS type 16 on SNB/HSW

Fill in the maximum DRAM capacity and slot count read from CAPID0_A
registers on Sandy Bridge and Haswell.

While the register isn't part of the Core Series datasheet, it can be
found in the corresponding "Intel Open Source Graphics Programmer's
Reference" datasheets.

Note that the values for DDRSZ (maximum allowed memory size per channel)
need to be halved when only one DIMM per channel is supported. On mobile
platforms, all but quad-core processors are subject to this restriction.

Tested on Lenovo X230:
On Linux, verify that `dmidecode -t 16` reports the actual maximum
capacity (16 GiB) instead of the currently-installed capacity (4 GiB) or
the max capacity assuming two DIMMs per channel is possible (32 GiB).

Change-Id: I6e2346de1ffe52e8685276acbdbf25755f4cc162
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/43971
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Christian Walter <christian.walter@9elements.com>
This commit is contained in:
Patrick Rudolph 2020-07-27 16:23:36 +02:00 committed by Angel Pons
parent 5e007808cd
commit 42609d807b
4 changed files with 112 additions and 3 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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 */

View File

@ -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)