From c731fd720cf46d347f9b15502beba2284ad6699f Mon Sep 17 00:00:00 2001 From: Matt DeVillier Date: Mon, 9 Feb 2026 09:00:39 -0600 Subject: [PATCH] lib/spd_bin: Add support for DDR5 SPD parsing Without explicit DDR5 support, print_spd_info() was decoding DDR5 DIMMs with the DDR4 SPD layout, so banks, ranks, rows, columns, and module size were all displaying incorrect values. Add DDR5-specific decoding in spd_bin.c using JESD400-5 byte positions. Define these offsets in ddr5.h and branch in each getter when dram_type is DDR5 so printed SPD info matches the actual module. Fix printk reporting DIMM module size to only report "per channel" when the DIMM actually contains multiple channels. TEST=build/boot on out-of-tree board Erying SRMJ4 and Starlabs Starbook MTL. Verify DIMM info printed in cbmem console is correct. Change-Id: I7f418db3f89c67c2a71b2c327bb511a78faf7300 Signed-off-by: Matt DeVillier Reviewed-on: https://review.coreboot.org/c/coreboot/+/91145 Reviewed-by: Sean Rhodes Tested-by: build bot (Jenkins) --- src/include/device/dram/ddr5.h | 9 ++++ src/lib/spd_bin.c | 77 ++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/include/device/dram/ddr5.h b/src/include/device/dram/ddr5.h index 78d18b24f8..ba048f6cb3 100644 --- a/src/include/device/dram/ddr5.h +++ b/src/include/device/dram/ddr5.h @@ -15,6 +15,15 @@ /** Maximum SPD size supported */ #define SPD_SIZE_MAX_DDR5 1024 +/* DDR5 SPD byte offsets */ +#define DDR5_SPD_DENSITY_PACKAGE 4 +#define DDR5_SPD_ADDRESSING 5 +#define DDR5_SPD_IO_WIDTH 6 +#define DDR5_SPD_BANKS 7 +#define DDR5_SPD_MODULE_ORG 234 +#define DDR5_SPD_CHANNEL_BUS_WIDTH 235 +#define DDR5_SPD_MIN_LEN 236 + enum spd_dimm_type_ddr5 { SPD_DDR5_DIMM_TYPE_RDIMM = 0x01, SPD_DDR5_DIMM_TYPE_UDIMM = 0x02, diff --git a/src/lib/spd_bin.c b/src/lib/spd_bin.c index e3108cbcfd..2f387f38ea 100644 --- a/src/lib/spd_bin.c +++ b/src/lib/spd_bin.c @@ -4,11 +4,18 @@ #include #include #include +#include #include #include #include +#include #include +static bool is_ddr5(int dram_type) +{ + return dram_type == SPD_MEMORY_TYPE_DDR5_SDRAM; +} + void dump_spd_info(struct spd_block *blk) { u8 i; @@ -76,8 +83,14 @@ static int spd_get_banks(const uint8_t spd[], int dram_type) { static const int ddr3_banks[4] = { 8, 16, 32, 64 }; static const int ddr4_banks[10] = { 4, 8, -1, -1, 8, 16, -1, -1, 16, 32 }; - int index = (spd[SPD_DENSITY_BANKS] >> 4) & 0xf; + if (is_ddr5(dram_type)) { + /* DDR5 byte 7: BanksPerBG (bits 2:0) * BankGroups (bits 5:3) */ + int banks_per_bg = 1 << (spd[DDR5_SPD_BANKS] & 7); + int bank_groups = 1 << ((spd[DDR5_SPD_BANKS] >> 3) & 7); + return banks_per_bg * bank_groups; + } + int index = (spd[SPD_DENSITY_BANKS] >> 4) & 0xf; if (use_ddr4_params(dram_type)) { if (index >= ARRAY_SIZE(ddr4_banks)) return -1; @@ -89,8 +102,24 @@ static int spd_get_banks(const uint8_t spd[], int dram_type) } } -static int spd_get_capmb(const uint8_t spd[]) +static int spd_get_capmb(const uint8_t spd[], int dram_type) { + /* + * DDR5: byte 4 = Density (5 bits, Gb per die) + DiePerPkg (3 bits). + * Capacity in Mb per package. + */ + if (is_ddr5(dram_type)) { + static const uint8_t ddr5_density_gb[32] = { + 0, 4, 8, 12, 16, 24, 32, 48, 64, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const uint8_t ddr5_die_per_pkg[8] = { 1, 0, 2, 4, 8, 16, 0, 0 }; + uint8_t density_gb = ddr5_density_gb[spd[DDR5_SPD_DENSITY_PACKAGE] & 0x1f]; + uint8_t dies = ddr5_die_per_pkg[(spd[DDR5_SPD_DENSITY_PACKAGE] >> 5) & 7]; + if (density_gb == 0 || dies == 0) + return -1; + return (int)density_gb * dies * 1024; /* Mb per package */ + } static const int spd_capmb[13] = { 1, 2, 4, 8, 16, 32, 64, 128, 48, 96, 12, 24, 72 }; int index = spd[SPD_DENSITY_BANKS] & 0xf; @@ -99,8 +128,12 @@ static int spd_get_capmb(const uint8_t spd[]) return spd_capmb[index] * 256; } -static int spd_get_rows(const uint8_t spd[]) +static int spd_get_rows(const uint8_t spd[], int dram_type) { + if (is_ddr5(dram_type)) { + /* DDR5 byte 5 bits 7:3 = row address count; rows = 16 + value */ + return 16 + ((spd[DDR5_SPD_ADDRESSING] >> 3) & 0x1f); + } static const int spd_rows[7] = { 12, 13, 14, 15, 16, 17, 18 }; int index = (spd[SPD_ADDRESSING] >> 3) & 7; if (index >= ARRAY_SIZE(spd_rows)) @@ -108,8 +141,12 @@ static int spd_get_rows(const uint8_t spd[]) return spd_rows[index]; } -static int spd_get_cols(const uint8_t spd[]) +static int spd_get_cols(const uint8_t spd[], int dram_type) { + if (is_ddr5(dram_type)) { + /* DDR5 byte 5 bits 2:0 = column address count; cols = 10 + value */ + return 10 + (spd[DDR5_SPD_ADDRESSING] & 7); + } static const int spd_cols[4] = { 9, 10, 11, 12 }; int index = spd[SPD_ADDRESSING] & 7; if (index >= ARRAY_SIZE(spd_cols)) @@ -119,6 +156,10 @@ static int spd_get_cols(const uint8_t spd[]) static int spd_get_ranks(const uint8_t spd[], int dram_type) { + if (is_ddr5(dram_type)) { + /* DDR5 byte 234 bits 5:3 = package ranks per channel; ranks = 1 + value */ + return 1 + ((spd[DDR5_SPD_MODULE_ORG] >> 3) & 7); + } static const int spd_ranks[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; int organ_offset = use_ddr4_params(dram_type) ? DDR4_ORGANIZATION : DDR3_ORGANIZATION; @@ -130,6 +171,10 @@ static int spd_get_ranks(const uint8_t spd[], int dram_type) static int spd_get_devw(const uint8_t spd[], int dram_type) { + if (is_ddr5(dram_type)) { + /* DDR5 byte 6 bits 2:0 = SDRAM I/O width; 4 << value */ + return 4 << (spd[DDR5_SPD_IO_WIDTH] & 7); + } static const int spd_devw[4] = { 4, 8, 16, 32 }; int organ_offset = use_ddr4_params(dram_type) ? DDR4_ORGANIZATION : DDR3_ORGANIZATION; @@ -141,6 +186,10 @@ static int spd_get_devw(const uint8_t spd[], int dram_type) static int spd_get_busw(const uint8_t spd[], int dram_type) { + if (is_ddr5(dram_type)) { + /* DDR5 byte 235 bits 2:0 = primary bus width; 8 << value */ + return 8 << (spd[DDR5_SPD_CHANNEL_BUS_WIDTH] & 7); + } static const int spd_busw[4] = { 8, 16, 32, 64 }; int busw_offset = use_ddr4_params(dram_type) ? DDR4_BUS_DEV_WIDTH : DDR3_BUS_DEV_WIDTH; @@ -188,12 +237,14 @@ void print_spd_info(uint8_t spd[]) size_t len; int type = spd[SPD_MEMORY_TYPE]; int banks = spd_get_banks(spd, type); - int capmb = spd_get_capmb(spd); - int rows = spd_get_rows(spd); - int cols = spd_get_cols(spd); + int capmb = spd_get_capmb(spd, type); + int rows = spd_get_rows(spd, type); + int cols = spd_get_cols(spd, type); int ranks = spd_get_ranks(spd, type); int devw = spd_get_devw(spd, type); int busw = spd_get_busw(spd, type); + int channels; + u32 size_mb; /* Module type */ printk(BIOS_INFO, "SPD: module type is %s\n", @@ -211,8 +262,16 @@ void print_spd_info(uint8_t spd[]) if (capmb > 0 && busw > 0 && devw > 0 && ranks > 0) { /* SIZE = DENSITY / 8 * BUS_WIDTH / SDRAM_WIDTH * RANKS */ - printk(BIOS_INFO, "SPD: module size is %u MB (per channel)\n", - capmb / 8 * busw / devw * ranks); + size_mb = (u32)capmb / 8 * busw / devw * ranks; + if (size_mb > 0) { + channels = 1 + (is_ddr5(type) ? ((spd[DDR5_SPD_CHANNEL_BUS_WIDTH] >> 5) & 3) : 0); + if (channels > 1) { + printk(BIOS_INFO, "SPD: module size is %u MB (%u MB per channel)\n", + size_mb, size_mb / channels); + return; + } + printk(BIOS_INFO, "SPD: module size is %u MB\n", size_mb); + } } }