Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Initial pass at DDR4 support in spdmem(4)



Attached is a preliminary cut at updating spdmem(4) driver/module to support DDR4 memory. I think I got most of it right, but I am quite certain that the conversion of memory speed (MHz or cycle-time) to PC4-xxxxx designation is wrong. I'm also not sure that I got the CAS latency and other clock/cycle-count value correct (tAA, tRCD, tRP, and tRAS).

I would appreciate it if someone with DDR4 memory could try out this update and let me know the results. You don't need to build a whole new kernel for testing, just "build.sh modules", copy the spdmem module into the "usual" location (/stand/amd64/7.99.x/modules/spdmem/ for example), and load the module via "modload spdmem".

Please provide me with the relevant contents of your dmesg after the modload, as well as the output from "sysctl -xx hw.spdmem0.spd_data" and "sysctl hw.spdmem0". Of course, it would also help if you would provide the actual specs of your installed memory module(s), including the PC4-xxxxx values and the timing/clock-count information so I can compare them with the reported values.

I've already verified that the code still works fine for DDR3 memory, and (at least for my memory) still returns the same information as the old code.



-------------------------------------------------------------------------
| Paul Goyette     | PGP Key fingerprint:     | E-mail addresses:       |
| (Retired)        | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com    |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette at juniper.net |
| Kernel Developer |                          | pgoyette at netbsd.org  |
-------------------------------------------------------------------------
Index: spdmem.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/spdmem.c,v
retrieving revision 1.12
diff -u -p -r1.12 spdmem.c
--- spdmem.c	1 Apr 2015 06:08:39 -0000	1.12
+++ spdmem.c	12 Apr 2015 04:53:43 -0000
@@ -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: spdmemreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/spdmemreg.h,v
retrieving revision 1.2
diff -u -p -r1.2 spdmemreg.h
--- spdmemreg.h	27 Mar 2015 05:33:08 -0000	1.2
+++ spdmemreg.h	12 Apr 2015 04:53:43 -0000
@@ -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: spdmemvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/spdmemvar.h,v
retrieving revision 1.4
diff -u -p -r1.4 spdmemvar.h
--- spdmemvar.h	18 Mar 2014 18:20:41 -0000	1.4
+++ spdmemvar.h	12 Apr 2015 04:53:43 -0000
@@ -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);


Home | Main Index | Thread Index | Old Index