Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Collect SPD data from DIMMs and memory-down, and find the common supported settings. Original-Change-Id: I4e6a1408a638a463ecae37a447cfed1d6556e44a Original-Signed-off-by: Angel Pons <th3fanbus@gmail.com> Signed-off-by: Bill XIE <persmule@hardenedlinux.org> Change-Id: I7948554eb02113bdca380222a11cfb322f9615f8 Reviewed-on: https://review.coreboot.org/c/coreboot/+/77049 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Martin L Roth <gaumless@gmail.com>
- Loading branch information
Showing
4 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
|
||
romstage-y += raminit_main.c | ||
romstage-y += raminit_native.c | ||
romstage-y += spd_bitmunching.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
206 changes: 206 additions & 0 deletions
206
src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
|
||
#include <cbfs.h> | ||
#include <commonlib/bsd/clamp.h> | ||
#include <console/console.h> | ||
#include <device/dram/ddr3.h> | ||
#include <device/smbus_host.h> | ||
#include <northbridge/intel/haswell/haswell.h> | ||
#include <northbridge/intel/haswell/raminit.h> | ||
#include <string.h> | ||
#include <types.h> | ||
|
||
#include "raminit_native.h" | ||
|
||
static const uint8_t *get_spd_data_from_cbfs(struct spd_info *spdi) | ||
{ | ||
if (!CONFIG(HAVE_SPD_IN_CBFS)) | ||
return NULL; | ||
|
||
printk(RAM_DEBUG, "SPD index %u\n", spdi->spd_index); | ||
|
||
size_t spd_file_len; | ||
uint8_t *spd_file = cbfs_map("spd.bin", &spd_file_len); | ||
|
||
if (!spd_file) { | ||
printk(BIOS_ERR, "SPD data not found in CBFS\n"); | ||
return NULL; | ||
} | ||
|
||
if (spd_file_len < ((spdi->spd_index + 1) * SPD_LEN)) { | ||
printk(BIOS_ERR, "SPD index override to 0 - old hardware?\n"); | ||
spdi->spd_index = 0; | ||
} | ||
|
||
if (spd_file_len < SPD_LEN) { | ||
printk(BIOS_ERR, "Invalid SPD data in CBFS\n"); | ||
return NULL; | ||
} | ||
|
||
return spd_file + (spdi->spd_index * SPD_LEN); | ||
} | ||
|
||
static void get_spd_for_dimm(struct raminit_dimm_info *const dimm, const uint8_t *cbfs_spd) | ||
{ | ||
if (dimm->spd_addr == SPD_MEMORY_DOWN) { | ||
if (cbfs_spd) { | ||
memcpy(dimm->raw_spd, cbfs_spd, SPD_LEN); | ||
dimm->valid = true; | ||
printk(RAM_DEBUG, "memory-down\n"); | ||
return; | ||
} else { | ||
printk(RAM_DEBUG, "memory-down but no CBFS SPD data, ignoring\n"); | ||
return; | ||
} | ||
} | ||
printk(RAM_DEBUG, "slotted "); | ||
const uint8_t spd_mem_type = smbus_read_byte(dimm->spd_addr, SPD_MEMORY_TYPE); | ||
if (spd_mem_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { | ||
printk(RAM_DEBUG, "and not DDR3, ignoring\n"); | ||
return; | ||
} | ||
printk(RAM_DEBUG, "and DDR3\n"); | ||
if (i2c_eeprom_read(dimm->spd_addr, 0, SPD_LEN, dimm->raw_spd) != SPD_LEN) { | ||
printk(BIOS_WARNING, "I2C block read failed, trying SMBus byte reads\n"); | ||
for (uint32_t i = 0; i < SPD_LEN; i++) | ||
dimm->raw_spd[i] = smbus_read_byte(dimm->spd_addr, i); | ||
} | ||
dimm->valid = true; | ||
} | ||
|
||
static void get_spd_data(struct sysinfo *ctrl) | ||
{ | ||
struct spd_info spdi = {0}; | ||
mb_get_spd_map(&spdi); | ||
const uint8_t *cbfs_spd = get_spd_data_from_cbfs(&spdi); | ||
for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { | ||
for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { | ||
struct raminit_dimm_info *const dimm = &ctrl->dimms[channel][slot]; | ||
dimm->spd_addr = spdi.addresses[NUM_SLOTS * channel + slot]; | ||
if (!dimm->spd_addr) | ||
continue; | ||
|
||
printk(RAM_DEBUG, "CH%uS%u is ", channel, slot); | ||
get_spd_for_dimm(dimm, cbfs_spd); | ||
} | ||
} | ||
} | ||
|
||
static void decode_spd(struct raminit_dimm_info *const dimm) | ||
{ | ||
/** TODO: Hook up somewhere, and handle lack of XMP data **/ | ||
const bool enable_xmp = false; | ||
memset(&dimm->data, 0, sizeof(dimm->data)); | ||
if (enable_xmp) | ||
spd_xmp_decode_ddr3(&dimm->data, dimm->raw_spd, DDR3_XMP_PROFILE_1); | ||
else | ||
spd_decode_ddr3(&dimm->data, dimm->raw_spd); | ||
|
||
if (CONFIG(DEBUG_RAM_SETUP)) | ||
dram_print_spd_ddr3(&dimm->data); | ||
} | ||
|
||
static enum raminit_status find_common_spd_parameters(struct sysinfo *ctrl) | ||
{ | ||
ctrl->cas_supported = 0xffff; | ||
ctrl->flags.raw = 0xffffffff; | ||
|
||
ctrl->tCK = 0; | ||
ctrl->tAA = 0; | ||
ctrl->tWR = 0; | ||
ctrl->tRCD = 0; | ||
ctrl->tRRD = 0; | ||
ctrl->tRP = 0; | ||
ctrl->tRAS = 0; | ||
ctrl->tRC = 0; | ||
ctrl->tRFC = 0; | ||
ctrl->tWTR = 0; | ||
ctrl->tRTP = 0; | ||
ctrl->tFAW = 0; | ||
ctrl->tCWL = 0; | ||
ctrl->tCMD = 0; | ||
ctrl->chanmap = 0; | ||
|
||
bool yes_ecc = false; | ||
bool not_ecc = false; | ||
|
||
for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { | ||
ctrl->dpc[channel] = 0; | ||
ctrl->rankmap[channel] = 0; | ||
ctrl->rank_mirrored[channel] = 0; | ||
ctrl->channel_size_mb[channel] = 0; | ||
for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { | ||
struct raminit_dimm_info *const dimm = &ctrl->dimms[channel][slot]; | ||
if (!dimm->valid) | ||
continue; | ||
|
||
printk(RAM_DEBUG, "\nCH%uS%u SPD:\n", channel, slot); | ||
decode_spd(dimm); | ||
|
||
ctrl->chanmap |= BIT(channel); | ||
ctrl->dpc[channel]++; | ||
ctrl->channel_size_mb[channel] += dimm->data.size_mb; | ||
|
||
/* The first rank of a populated slot is always present */ | ||
const uint8_t rank = slot + slot; | ||
assert(dimm->data.ranks); | ||
ctrl->rankmap[channel] |= (BIT(dimm->data.ranks) - 1) << rank; | ||
|
||
if (dimm->data.flags.pins_mirrored) | ||
ctrl->rank_mirrored[channel] |= BIT(rank + 1); | ||
|
||
/* Find common settings */ | ||
ctrl->cas_supported &= dimm->data.cas_supported; | ||
ctrl->flags.raw &= dimm->data.flags.raw; | ||
ctrl->tCK = MAX(ctrl->tCK, dimm->data.tCK); | ||
ctrl->tAA = MAX(ctrl->tAA, dimm->data.tAA); | ||
ctrl->tWR = MAX(ctrl->tWR, dimm->data.tWR); | ||
ctrl->tRCD = MAX(ctrl->tRCD, dimm->data.tRCD); | ||
ctrl->tRRD = MAX(ctrl->tRRD, dimm->data.tRRD); | ||
ctrl->tRP = MAX(ctrl->tRP, dimm->data.tRP); | ||
ctrl->tRAS = MAX(ctrl->tRAS, dimm->data.tRAS); | ||
ctrl->tRC = MAX(ctrl->tRC, dimm->data.tRC); | ||
ctrl->tRFC = MAX(ctrl->tRFC, dimm->data.tRFC); | ||
ctrl->tWTR = MAX(ctrl->tWTR, dimm->data.tWTR); | ||
ctrl->tRTP = MAX(ctrl->tRTP, dimm->data.tRTP); | ||
ctrl->tFAW = MAX(ctrl->tFAW, dimm->data.tFAW); | ||
ctrl->tCWL = MAX(ctrl->tCWL, dimm->data.tCWL); | ||
ctrl->tCMD = MAX(ctrl->tCMD, dimm->data.tCMD); | ||
|
||
yes_ecc |= dimm->data.flags.is_ecc; | ||
not_ecc |= !dimm->data.flags.is_ecc; | ||
} | ||
} | ||
|
||
if (!ctrl->chanmap) { | ||
printk(BIOS_ERR, "No DIMMs were found\n"); | ||
return RAMINIT_STATUS_NO_MEMORY_INSTALLED; | ||
} | ||
if (!ctrl->cas_supported) { | ||
printk(BIOS_ERR, "Could not resolve common CAS latency\n"); | ||
return RAMINIT_STATUS_UNSUPPORTED_MEMORY; | ||
} | ||
/** TODO: Properly handle ECC support and ECC forced **/ | ||
if (yes_ecc && not_ecc) { | ||
/** TODO: Test if the ECC DIMMs can be operated as non-ECC DIMMs **/ | ||
printk(BIOS_ERR, "Both ECC and non-ECC DIMMs present, this is unsupported\n"); | ||
return RAMINIT_STATUS_UNSUPPORTED_MEMORY; | ||
} | ||
if (yes_ecc) | ||
ctrl->lanes = NUM_LANES; | ||
else | ||
ctrl->lanes = NUM_LANES_NO_ECC; | ||
|
||
ctrl->is_ecc = yes_ecc; | ||
|
||
/** TODO: Complete LPDDR support **/ | ||
ctrl->lpddr = false; | ||
|
||
return RAMINIT_STATUS_SUCCESS; | ||
} | ||
|
||
enum raminit_status collect_spd_info(struct sysinfo *ctrl) | ||
{ | ||
get_spd_data(ctrl); | ||
return find_common_spd_parameters(ctrl); | ||
} |