Module Name: src Committed By: pgoyette Date: Mon Apr 20 02:55:14 UTC 2015
Modified Files: src/sys/dev/ic: spdmem.c spdmemreg.h spdmemvar.h Log Message: Initial pass at supporting decode for DDR4. XXX I fully expect that reporting of PC4-xxxxx values is incorrect. XXX Please report this, or any other errors that you may see. To generate a diff of this commit: cvs rdiff -u -r1.12 -r1.13 src/sys/dev/ic/spdmem.c cvs rdiff -u -r1.2 -r1.3 src/sys/dev/ic/spdmemreg.h cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ic/spdmemvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/spdmem.c diff -u src/sys/dev/ic/spdmem.c:1.12 src/sys/dev/ic/spdmem.c:1.13 --- src/sys/dev/ic/spdmem.c:1.12 Wed Apr 1 06:08:39 2015 +++ src/sys/dev/ic/spdmem.c Mon Apr 20 02:55:14 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: spdmem.c,v 1.12 2015/04/01 06:08:39 matt Exp $ */ +/* $NetBSD: spdmem.c,v 1.13 2015/04/20 02:55:14 pgoyette Exp $ */ /* * Copyright (c) 2007 Nicolas Joly @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.12 2015/04/01 06:08:39 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.13 2015/04/20 02:55:14 pgoyette Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -55,6 +55,7 @@ static void decode_sdram(const struct sy static void decode_ddr(const struct sysctlnode *, device_t, struct spdmem *); static void decode_ddr2(const struct sysctlnode *, device_t, struct spdmem *); static void decode_ddr3(const struct sysctlnode *, device_t, struct spdmem *); +static void decode_ddr4(const struct sysctlnode *, device_t, struct spdmem *); static void decode_fbdimm(const struct sysctlnode *, device_t, struct spdmem *); static void decode_size_speed(device_t, const struct sysctlnode *, @@ -79,6 +80,25 @@ static const char* const spdmem_basic_ty "DDR4 SDRAM" }; +static const char* const spdmem_ddr4_module_types[] = { + "DDR4 Extended", + "DDR4 RDIMM", + "DDR4 UDIMM", + "DDR4 SO-DIMM", + "DDR4 Load-Reduced DIMM", + "DDR4 Mini-RDIMM", + "DDR4 Mini-UDIMM", + "DDR4 Reserved", + "DDR4 72Bit SO-RDIMM", + "DDR4 72Bit SO-UDIMM", + "DDR4 Undefined", + "DDR4 Reserved", + "DDR4 16Bit SO-DIMM", + "DDR4 32Bit SO-DIMM", + "DDR4 Reserved", + "DDR4 Undefined" +}; + static const char* const spdmem_superset_types[] = { "unknown", "ESDRAM", @@ -116,6 +136,9 @@ static const char* const spdmem_parity_t "cmd/addr/data parity, data ECC" }; +int spd_rom_sizes[] = { 0, 128, 256, 384, 512 }; + + /* Cycle time fractional values (units of .001 ns) for DDR2 SDRAM */ static const uint16_t spdmem_cycle_frac[] = { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, @@ -202,7 +225,31 @@ spdmem_common_probe(struct spdmem_softc return 0; } return 1; - } + } else if (spd_type == SPDMEM_MEMTYPE_DDR4SDRAM) { + spd_len = (sc->sc_read)(sc, 0) & 0x0f; + if ((unsigned int)spd_len > __arraycount(spd_rom_sizes)) + return 0; + spd_len = spd_rom_sizes[spd_len]; + spd_crc_cover=128; + if (spd_crc_cover > spd_len) + return 0; + crc_calc = spdcrc16(sc, spd_crc_cover); + crc_spd = (sc->sc_read)(sc, 127) << 8; + crc_spd |= (sc->sc_read)(sc, 126); + if (crc_calc != crc_spd) { + aprint_debug("crc16 failed, covers %d bytes, " + "calc = 0x%04x, spd = 0x%04x\n", + spd_crc_cover, crc_calc, crc_spd); + return 0; + } + /* + * We probably could also verify the CRC for the other + * "pages" of SPD data in blocks 1 and 2, but we'll do + * it some other time. + */ + return 1; + } else + return 0; /* For unrecognized memory types, don't match at all */ return 0; @@ -218,15 +265,26 @@ spdmem_common_attach(struct spdmem_softc unsigned int i, spd_len, spd_size; const struct sysctlnode *node = NULL; - /* - * FBDIMM and DDR3 (and probably all newer) have a different - * encoding of the SPD EEPROM used/total sizes - */ s->sm_len = (sc->sc_read)(sc, 0); s->sm_size = (sc->sc_read)(sc, 1); s->sm_type = (sc->sc_read)(sc, 2); - if (s->sm_type >= SPDMEM_MEMTYPE_FBDIMM) { + if (s->sm_type == SPDMEM_MEMTYPE_DDR4SDRAM) { + /* + * An even newer encoding with one byte holding both + * the used-size and capacity values + */ + spd_len = s->sm_len & 0x0f; + spd_size = (s->sm_len >> 4) & 0x07; + + spd_len = spd_rom_sizes[spd_len]; + spd_size *= 512; + + } else if (s->sm_type >= SPDMEM_MEMTYPE_FBDIMM) { + /* + * FBDIMM and DDR3 (and probably all newer) have a different + * encoding of the SPD EEPROM used/total sizes + */ spd_size = 64 << (s->sm_len & SPDMEM_SPDSIZE_MASK); switch (s->sm_len & SPDMEM_SPDLEN_MASK) { case SPDMEM_SPDLEN_128: @@ -316,6 +374,11 @@ spdmem_common_attach(struct spdmem_softc s->sm_sdr.sdr_superset == SPDMEM_SUPERSET_ESDRAM) { type = spdmem_superset_types[SPDMEM_SUPERSET_ESDRAM]; } + if (s->sm_type == SPDMEM_MEMTYPE_DDR4SDRAM && + s->sm_ddr4.ddr4_mod_type < + __arraycount(spdmem_ddr4_module_types)) { + type = spdmem_ddr4_module_types[s->sm_ddr4.ddr4_mod_type]; + } } strlcpy(sc->sc_type, type, SPDMEM_TYPE_MAXLEN); @@ -364,6 +427,9 @@ spdmem_common_attach(struct spdmem_softc case SPDMEM_MEMTYPE_FBDIMM_PROBE: decode_fbdimm(node, self, s); break; + case SPDMEM_MEMTYPE_DDR4SDRAM: + decode_ddr4(node, self, s); + break; } /* Dump SPD */ @@ -739,3 +805,107 @@ decode_fbdimm(const struct sysctlnode *n decode_voltage_refresh(self, s); } + +static void +decode_ddr4(const struct sysctlnode *node, device_t self, struct spdmem *s) { + int dimm_size, cycle_time; + int tAA_clocks, tRCD_clocks,tRP_clocks, tRAS_clocks; + + aprint_naive("\n"); + aprint_normal(": %20s\n", s->sm_ddr4.ddr4_part_number); + aprint_normal_dev(self, "%s", spdmem_basic_types[s->sm_type]); + if (s->sm_ddr4.ddr4_mod_type < __arraycount(spdmem_ddr4_module_types)) + aprint_normal(" (%s)", + spdmem_ddr4_module_types[s->sm_ddr4.ddr4_mod_type]); + aprint_normal(", %stemp-sensor, ", + (s->sm_ddr4.ddr4_has_therm_sensor)?"":"no "); + + /* + * DDR4 size calculation from JEDEC spec + * + * Module capacity in bytes is defined as + * Chip_Capacity_in_bits / 8bits-per-byte * + * primary_bus_width / DRAM_width * + * logical_ranks_per_DIMM + * + * logical_ranks_per DIMM equals package_ranks, but multiply + * by diecount for 3DS packages + * + * We further divide by 2**20 to get our answer in MB + */ + dimm_size = (s->sm_ddr4.ddr4_capacity + 28) /* chip_capacity */ + - 20 /* convert to MB */ + - 3 /* bits --> bytes */ + + (s->sm_ddr4.ddr4_primary_bus_width + 3); /* bus width */ + switch (s->sm_ddr4.ddr4_device_width) { /* DRAM width */ + case 0: dimm_size -= 2; + break; + case 1: dimm_size -= 3; + break; + case 2: dimm_size -= 4; + break; + case 4: dimm_size -= 5; + break; + default: + dimm_size = -1; /* flag invalid value */ + } + if (dimm_size >=0) { + dimm_size = (1 << dimm_size) * + (s->sm_ddr4.ddr4_package_ranks + 1); /* log.ranks/DIMM */ + if (s->sm_ddr4.ddr4_signal_loading == 2) { + dimm_size *= s->sm_ddr4.ddr4_diecount; + } + } + + /* + * For now, the only value for mtb is 1 = 125ps, and ftp = 1ps + * so we don't need to figure out the time-base units - just + * hard-code them for now. + */ + cycle_time = 125 * s->sm_ddr4.ddr4_tCKAVGmin_mtb + + s->sm_ddr4.ddr4_tCKAVGmin_ftb; + aprint_normal("%d MB, %d.%03dns cycle time (%dMHz)\n", dimm_size, + cycle_time/1000, cycle_time % 1000, 1000000 / cycle_time); + + decode_size_speed(self, node, dimm_size, cycle_time, 2, + 1 << (s->sm_ddr4.ddr4_device_width + 3), + TRUE, "PC4", 0); + + aprint_verbose_dev(self, + "%d rows, %d cols, %d banks, %d bank groups\n", + s->sm_ddr3.ddr3_rows + 9, s->sm_ddr3.ddr3_cols + 12, + 1 << (2 + s->sm_ddr4.ddr4_logbanks), + 1 << s->sm_ddr4.ddr4_bankgroups); + +/* + * Note that the ddr4_xxx_ftb fields are actually signed offsets from + * the corresponding mtb value, so we might have to subtract 256! + */ +#define __DDR4_VALUE(field) (s->sm_ddr4.ddr4_##field##_mtb * 256 + \ + s->sm_ddr4.ddr4_##field##_ftb) - \ + ((s->sm_ddr4.ddr4_##field##_ftb > 127)?256:0) + + tAA_clocks = (__DDR4_VALUE(tAAmin) * 1000 ) / cycle_time; + tRP_clocks = (__DDR4_VALUE(tRPmin) * 1000 ) / cycle_time; + tRCD_clocks = (__DDR4_VALUE(tRCDmin) * 1000 ) / cycle_time; + tRAS_clocks = (s->sm_ddr4.ddr4_tRASmin_msb * 256 + + s->sm_ddr4.ddr4_tRASmin_lsb) * 125 * 1000 / cycle_time; + +/* + * Per JEDEC spec, rounding is done by taking the time value, dividing + * by the cycle time, subtracting .010 from the result, and then + * rounded up to the nearest integer. Unfortunately, none of their + * examples say what to do when the result of the subtraction is already + * an integer. For now, assume that we still round up (so an interval + * of exactly 12.010 clock cycles will be printed as 13). + */ +#define __DDR4_ROUND(value) ((value - 10) / 1000 + 1) + + aprint_verbose_dev(self, LATENCY, __DDR4_ROUND(tAA_clocks), + __DDR4_ROUND(tRP_clocks), + __DDR4_ROUND(tRCD_clocks), + __DDR4_ROUND(tRAS_clocks)); + +#undef __DDR4_VALUE +#undef __DDR4_ROUND +} Index: src/sys/dev/ic/spdmemreg.h diff -u src/sys/dev/ic/spdmemreg.h:1.2 src/sys/dev/ic/spdmemreg.h:1.3 --- src/sys/dev/ic/spdmemreg.h:1.2 Fri Mar 27 05:33:08 2015 +++ src/sys/dev/ic/spdmemreg.h Mon Apr 20 02:55:14 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: spdmemreg.h,v 1.2 2015/03/27 05:33:08 msaitoh Exp $ */ +/* $NetBSD: spdmemreg.h,v 1.3 2015/04/20 02:55:14 pgoyette Exp $ */ /* * Copyright (c) 2007 Paul Goyette @@ -100,3 +100,20 @@ #define SPDMEM_DDR3_TYPE_MICRODIMM 0x04 #define SPDMEM_DDR3_TYPE_MINI_RDIMM 0x05 #define SPDMEM_DDR3_TYPE_MINI_UDIMM 0x06 + +#define SPDMEM_DDR4_TYPE_EXTENDED 0x00 +#define SPDMEM_DDR4_TYPE_RDIMM 0x01 +#define SPDMEM_DDR4_TYPE_UDIMM 0x02 +#define SPDMEM_DDR4_TYPE_SODIMM 0x03 +#define SPDMEM_DDR4_TYPE_LRDIMM 0x04 +#define SPDMEM_DDR4_TYPE_MINI_RDIMM 0x05 +#define SPDMEM_DDR4_TYPE_MINI_UDIMM 0x06 +#define SPDMEM_DDR4_TYPE_RESERVED1 0x07 +#define SPDMEM_DDR4_TYPE_72B_SO_RDIMM 0x08 +#define SPDMEM_DDR4_TYPE_72B_SO_UDIMM 0x09 +/* not defined 0x0a */ +#define SPDMEM_DDR4_TYPE_RESERVED2 0x0b +#define SPDMEM_DDR4_TYPE_16B_SO_DIMM 0x0c +#define SPDMEM_DDR4_TYPE_32B_SO_DIMM 0x0d +#define SPDMEM_DDR4_TYPE_RESERVED3 0x0e +/* not defined 0x0f */ Index: src/sys/dev/ic/spdmemvar.h diff -u src/sys/dev/ic/spdmemvar.h:1.4 src/sys/dev/ic/spdmemvar.h:1.5 --- src/sys/dev/ic/spdmemvar.h:1.4 Tue Mar 18 18:20:41 2014 +++ src/sys/dev/ic/spdmemvar.h Mon Apr 20 02:55:14 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: spdmemvar.h,v 1.4 2014/03/18 18:20:41 riastradh Exp $ */ +/* $NetBSD: spdmemvar.h,v 1.5 2015/04/20 02:55:14 pgoyette Exp $ */ /* * Copyright (c) 2007 Paul Goyette @@ -39,7 +39,19 @@ #define SPD_BITFIELD(a, b, c, d) a; b; c; d #endif + /* + * NOTE + * + * Fields with "offsets" are field widths, measured in bits, + * with "offset" additional bits. Thus, a field with value + * of 2 with an offset of 14 defines a field with total width + * of 16 bits. + */ + struct spdmem_fpm { /* FPM and EDO DIMMS */ + uint8_t fpm_len; + uint8_t fpm_size; + uint8_t fpm_type; uint8_t fpm_rows; uint8_t fpm_cols; uint8_t fpm_banks; @@ -61,6 +73,9 @@ struct spdmem_fpm { /* FPM and EDO DI } __packed; struct spdmem_sdram { /* PC66/PC100/PC133 SDRAM */ + uint8_t sdr_len; + uint8_t sdr_size; + uint8_t sdr_type; SPD_BITFIELD( \ uint8_t sdr_rows:4, \ uint8_t sdr_rows2:4, , \ @@ -128,6 +143,9 @@ struct spdmem_sdram { /* PC66/PC100/P } __packed; struct spdmem_rom { + uint8_t rom_len; + uint8_t rom_size; + uint8_t rom_type; uint8_t rom_rows; uint8_t rom_cols; uint8_t rom_banks; @@ -147,6 +165,9 @@ struct spdmem_rom { struct spdmem_ddr { /* Dual Data Rate SDRAM */ + uint8_t ddr_len; + uint8_t ddr_size; + uint8_t ddr_type; SPD_BITFIELD( \ uint8_t ddr_rows:4, \ uint8_t ddr_rows2:4, , \ @@ -217,6 +238,9 @@ struct spdmem_ddr { /* Dual Data Rate } __packed; struct spdmem_ddr2 { /* Dual Data Rate 2 SDRAM */ + uint8_t ddr2_len; + uint8_t ddr2_size; + uint8_t ddr2_type; SPD_BITFIELD( \ uint8_t ddr2_rows:5, \ uint8_t ddr2_unused1:3, , \ @@ -304,6 +328,9 @@ struct spdmem_ddr2 { /* Dual Data Rat } __packed; struct spdmem_fbdimm { /* Fully-buffered DIMM */ + uint8_t fbdimm_len; + uint8_t fbdimm_size; + uint8_t fbdimm_type; SPD_BITFIELD( \ uint8_t fbdimm_ps1_voltage:4, \ uint8_t fbdimm_ps2_voltage:4, , \ @@ -381,6 +408,9 @@ struct spdmem_fbdimm { /* Fully-buffe } __packed; struct spdmem_rambus { /* Direct Rambus DRAM */ + uint8_t rdr_len; + uint8_t rdr_size; + uint8_t rdr_type; SPD_BITFIELD( \ uint8_t rdr_rows:4, \ uint8_t rdr_cols:4, , \ @@ -388,6 +418,9 @@ struct spdmem_rambus { /* Direct Ramb } __packed; struct spdmem_ddr3 { /* Dual Data Rate 3 SDRAM */ + uint8_t ddr3_len; + uint8_t ddr3_size; + uint8_t ddr3_type; uint8_t ddr3_mod_type; SPD_BITFIELD( \ /* chipsize is offset by 28: 0 = 256M, 1 = 512M, ... */ \ @@ -485,10 +518,337 @@ struct spdmem_ddr3 { /* Dual Data Rat uint8_t ddr3_vendor[26]; } __packed; +/* DDR4 info from JEDEC Standard No. 21-C, Annex L - 4.1.2.12 */ + +/* Module-type specific bytes - bytes 0x080 thru 0x0ff */ + +struct spdmem_ddr4_mod_unbuffered { + SPD_BITFIELD( \ + uint8_t ddr4_unbuf_mod_height:4, \ + uint8_t ddr4_unbuf_card_ext:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_unbuf_max_thick_front:4, \ + uint8_t ddr4_unbuf_max_thick_back:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_unbuf_refcard:5, \ + uint8_t ddr4_unbuf_refcard_rev:2, \ + uint8_t ddr4_unbuf_refcard_ext:1, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_unbuf_mirror_mapping:1, \ + uint8_t ddr4_unbuf_unused1:7, , \ + ); + uint8_t ddr4_unbuf_unused2[122]; + uint8_t ddr4_unbuf_crc[2]; +} __packed; + +struct spdmem_ddr4_mod_registered { + SPD_BITFIELD( \ + uint8_t ddr4_reg_mod_height:4, \ + uint8_t ddr4_reg_card_ext:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_max_thick_front:4, \ + uint8_t ddr4_reg_max_thick_back:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_refcard:5, \ + uint8_t ddr4_reg_refcard_rev:2, \ + uint8_t ddr4_reg_refcard_ext:1, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_regcnt:2, \ + uint8_t ddr4_reg_dram_rows:2, \ + uint8_t ddr4_reg_unused1:4, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_heat_spread_char:7, \ + uint8_t ddr4_reg_heat_spread_exist:1, , \ + ); + uint8_t ddr4_reg_mfg_id_lsb; + uint8_t ddr4_reg_mfg_id_msb; + uint8_t ddr4_reg_revision; + SPD_BITFIELD( \ + uint8_t ddr4_reg_mirror_mapping:1, \ + uint8_t ddr4_reg_unused2:7, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_output_drive_CKE:2, \ + uint8_t ddr4_reg_output_drive_ODT:2, \ + uint8_t ddr4_reg_output_drive_CmdAddr:2,\ + uint8_t ddr4_reg_output_drive_chipsel:2 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_reg_output_drive_CK_Y0Y2:2,\ + uint8_t ddr4_reg_output_drive_CK_Y1Y3:2,\ + uint8_t ddr4_reg_unused3:4, \ + ); + uint8_t ddr4_reg_unused4[115]; + uint8_t ddr4_reg_crc[2]; +} __packed; + +struct spdmem_ddr4_mod_reduced_load { + SPD_BITFIELD( \ + uint8_t ddr4_rload_mod_height:4, \ + uint8_t ddr4_rload_card_ext:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_max_thick_front:4, \ + uint8_t ddr4_rload_max_thick_back:4, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_refcard:5, \ + uint8_t ddr4_rload_refcard_rev:2, \ + uint8_t ddr4_rload_refcard_ext:1, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_regcnt:2, \ + uint8_t ddr4_rload_dram_rows:2, \ + uint8_t ddr4_rload_unused1:4, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_unused2:7, \ + uint8_t ddr4_rload_heat_spread_exist:1, , \ + ); + uint8_t ddr4_rload_reg_mfg_id_lsb; + uint8_t ddr4_rload_reg_mfg_id_msb; + uint8_t ddr4_rload_reg_revision; + SPD_BITFIELD( \ + uint8_t ddr4_rload_reg_mirror_mapping:1,\ + uint8_t ddr4_rload_unused3:7, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_output_drive_CKE:2, \ + uint8_t ddr4_rload_output_drive_ODT:2, \ + uint8_t ddr4_rload_output_drive_CmdAddr:2, \ + uint8_t ddr4_rload_output_drive_chipsel:2 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_output_drive_CK_Y0Y2:2, \ + uint8_t ddr4_rload_output_drive_CK_Y1Y3:2, \ + uint8_t ddr4_rload_unused4:4, \ + ); + uint8_t ddr4_rload_dbuff_revision; + SPD_BITFIELD( \ + uint8_t ddr4_rload_VrefDQ_0:6, \ + uint8_t ddr4_rload_unused5:2, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_VrefDQ_1:6, \ + uint8_t ddr4_rload_unused6:2, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_VrefDQ_2:6, \ + uint8_t ddr4_rload_unused7:2, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_VrefDQ_3:6, \ + uint8_t ddr4_rload_unused8:2, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_VrefDQ_buffer:6, \ + uint8_t ddr4_rload_unused9:2, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_MDQ_Read_Term_Str_1866:3, \ + uint8_t ddr4_rload_unused10:1, \ + uint8_t ddr4_rload_MDQ_Drive_Str_1866:3, \ + uint8_t ddr4_rload_unused11:1 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_MDQ_Read_Term_Str_2400:3, \ + uint8_t ddr4_rload_unused12:1, \ + uint8_t ddr4_rload_MDQ_Drive_Str_2400:3, \ + uint8_t ddr4_rload_unused13:1 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_MDQ_Read_Term_Str_3200:3, \ + uint8_t ddr4_rload_unused14:1, \ + uint8_t ddr4_rload_MDQ_Drive_Str_3200:3, \ + uint8_t ddr4_rload_unused15:1 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_Drive_Str_1866:2, \ + uint8_t ddr4_rload_DRAM_Drive_Str_2400:2, \ + uint8_t ddr4_rload_DRAM_Drive_Str_3200:2, \ + uint8_t ddr4_rload_unused16:2 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_NOM_1866:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_WR_1866:3, \ + uint8_t ddr4_rload_unused17:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_NOM_2400:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_WR_2400:3, \ + uint8_t ddr4_rload_unused18:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_NOM_3200:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_WR_3200:3, \ + uint8_t ddr4_rload_unused19:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_01_1866:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_23_1866:3, \ + uint8_t ddr4_rload_unused20:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_01_2400:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_23_2400:3, \ + uint8_t ddr4_rload_unused21:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_01_3200:3, \ + uint8_t ddr4_rload_DRAM_ODT_RTT_PARK_23_3200:3, \ + uint8_t ddr4_rload_unused22:2, \ + ); + uint8_t ddr4_rload_unused23[99]; + uint8_t ddr4_rload_crc[2]; +} __packed; + +struct spdmem_ddr4 { /* Dual Data Rate 4 SDRAM */ + SPD_BITFIELD( \ + uint8_t ddr4_ROM_used:4, \ + uint8_t ddr4_ROM_size:3, \ + uint8_t ddr4_unused0:1, \ + ); + uint8_t ddr4_romrev; + uint8_t ddr4_type; + SPD_BITFIELD( \ + uint8_t ddr4_mod_type:4, \ + uint8_t ddr4_unused1:4, , \ + ); + SPD_BITFIELD( \ + /* capacity is offset by 28: 0 = 256M, 1 = 512M, ... */ \ + uint8_t ddr4_capacity:4, \ + /* logbanks is offset by 2 */ \ + uint8_t ddr4_logbanks:2, \ + /* bankgroups is offset by 0 */ + uint8_t ddr4_bankgroups:2, \ + ); + /* cols is offset by 9, rows offset by 12 */ + SPD_BITFIELD( \ + uint8_t ddr4_cols:3, \ + uint8_t ddr4_rows:3, \ + uint8_t ddr4_unused2:2, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_signal_loading:2, \ + uint8_t ddr4_unused3:2, \ + uint8_t ddr4_diecount:3, \ + uint8_t ddr4_non_monolithic:1 \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_max_activate_count:4, \ + uint8_t ddr4_max_activate_window:2, \ + uint8_t ddr4_unused4:2, \ + ); + uint8_t ddr4_unused5; /* SDRAM Thermal & Refresh Options */ + SPD_BITFIELD( \ + uint8_t ddr4_unused6:6, \ + uint8_t ddr4_ppr_support:2, , /* post package repair */ \ + ); + uint8_t ddr4_unused7; + SPD_BITFIELD( \ + uint8_t ddr4_dram_vdd_12:2, \ + uint8_t ddr4_dram_vdd_tbd1:2, \ + uint8_t ddr4_dram_vdd_tbd2:2, \ + uint8_t ddr4_unused8:2 \ + ); + SPD_BITFIELD( \ + /* device width is 0=4, 1=8, 2=16, or 4=32 bits */ \ + uint8_t ddr4_device_width:3, \ + /* number of package ranks is field value plus 1 */ \ + uint8_t ddr4_package_ranks:3, \ + uint8_t ddr4_unused9:2, \ + ); + SPD_BITFIELD( \ + /* primary width is offset by 3, extension is offset by 2 */ \ + uint8_t ddr4_primary_bus_width:3, \ + uint8_t ddr4_bus_width_extension:2, \ + uint8_t ddr4_unused10:3, \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_unused11:7, \ + uint8_t ddr4_has_therm_sensor:1, , \ + ); + SPD_BITFIELD( \ + uint8_t ddr4_ext_mod_type:4, \ + uint8_t ddr4_unused12:4, , \ + ); + uint8_t ddr4_unused13; + SPD_BITFIELD( \ + /* units = 1ps (10**-12sec) */ \ + uint8_t ddr4_fine_timebase:2, \ + /* units = 125ps */ \ + uint8_t ddr4_medium_timebase:2, , \ + ); + uint8_t ddr4_tCKAVGmin_mtb; + uint8_t ddr4_tCKAVGmax_mtb; + /* Bit 0 of CAS_supported[0 corresponds to CL=7 */ + uint8_t ddr4_CAS_supported[4]; + uint8_t ddr4_tAAmin_mtb; + uint8_t ddr4_tRCDmin_mtb; + uint8_t ddr4_tRPmin_mtb; + SPD_BITFIELD( \ + uint8_t ddr4_tRASmin_msb:4, \ + uint8_t ddr4_tRCmin_mtb_msb:4, , \ + ); + uint8_t ddr4_tRASmin_lsb; + uint8_t ddr4_tRCmin_mtb_lsb; + uint8_t ddr4_tRFC1min_lsb; + uint8_t ddr4_tRFC1min_msb; + uint8_t ddr4_tRFC2min_lsb; + uint8_t ddr4_tRFC2min_msb; + uint8_t ddr4_tRFC4min_lsb; + uint8_t ddr4_tRFC4min_msb; + SPD_BITFIELD( \ + uint8_t ddr4_tFAW_mtb_msb, \ + uint8_t ddr4_unused14, , \ + ); + uint8_t ddr4_tFAWmin_mtb_lsb; + uint8_t ddr4_tRRD_Smin_mtb; + uint8_t ddr4_tRRD_Lmin_mtb; + uint8_t ddr4_tCCD_Lmin_mtb; + uint8_t ddr4_unused15[19]; + uint8_t ddr4_connector_map[18]; + uint8_t ddr4_unused16[39]; + uint8_t ddr4_tCCD_Lmin_ftb; + uint8_t ddr4_tRRD_Lmin_ftb; + uint8_t ddr4_tRRD_Smin_ftb; + uint8_t ddr4_tRCmin_ftb; + uint8_t ddr4_tRPmin_ftb; + uint8_t ddr4_tRCDmin_ftb; + uint8_t ddr4_tAAmin_ftb; + uint8_t ddr4_tCKAVGmax_ftb; + uint8_t ddr4_tCKAVGmin_ftb; + uint16_t ddr4_crc; + union { + struct spdmem_ddr4_mod_unbuffered u2_unbuf; + struct spdmem_ddr4_mod_registered u2_reg; + struct spdmem_ddr4_mod_reduced_load u2_red_load; + } ddr4_u2; + uint8_t ddr4_unused17[64]; + uint8_t ddr4_module_mfg_lsb; + uint8_t ddr4_module_mfg_msb; + uint8_t ddr4_module_mfg_loc; + uint8_t ddr4_module_mfg_year; + uint8_t ddr4_module_mfg_week; + uint8_t ddr4_serial_number[4]; + uint8_t ddr4_part_number[20]; + uint8_t ddr4_revision_code; + uint8_t ddr4_dram_mfgID_lsb; + uint8_t ddr4_dram_mfgID_msb; + uint8_t ddr4_dram_stepping; + uint8_t ddr4_mfg_specific_data[29]; + uint8_t ddr4_unused18[2]; + uint8_t ddr4_user_data[128] +} __packed; + struct spdmem { - uint8_t sm_len; - uint8_t sm_size; - uint8_t sm_type; union { struct spdmem_fbdimm u1_fbd; struct spdmem_fpm u1_fpm; @@ -498,6 +858,7 @@ struct spdmem { struct spdmem_rambus u1_rdr; struct spdmem_rom u1_rom; struct spdmem_ddr3 u1_ddr3; + struct spdmem_ddr4 u1_ddr4; } sm_u1; } __packed; #define sm_fbd sm_u1.u1_fbd @@ -508,16 +869,20 @@ struct spdmem { #define sm_rom sm_u1.u1_rom #define sm_ddr3 sm_u1.u1_ddr3 #define sm_sdr sm_u1.u1_sdr +#define sm_ddr4 sm_u1.u1_ddr4 /* some fields are in the same place for all memory types */ +#define sm_len sm_fpm.fpm_len +#define sm_size sm_fpm.fpm_size +#define sm_type sm_fpm.fpm_type #define sm_cksum sm_fpm.fpm_cksum #define sm_config sm_fpm.fpm_config #define sm_voltage sm_fpm.fpm_voltage #define sm_refresh sm_fpm.fpm_refresh #define sm_selfrefresh sm_fpm.fpm_selfrefresh -#define SPDMEM_TYPE_MAXLEN 16 +#define SPDMEM_TYPE_MAXLEN 24 struct spdmem_softc { uint8_t (*sc_read)(struct spdmem_softc *, uint8_t);