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