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

Reply via email to