Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/ic Initial pass at supporting decode for DDR4.



details:   https://anonhg.NetBSD.org/src/rev/a3007c7809b5
branches:  trunk
changeset: 807706:a3007c7809b5
user:      pgoyette <pgoyette%NetBSD.org@localhost>
date:      Mon Apr 20 02:55:14 2015 +0000

description:
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.

diffstat:

 sys/dev/ic/spdmem.c    |  186 +++++++++++++++++++++++-
 sys/dev/ic/spdmemreg.h |   19 ++-
 sys/dev/ic/spdmemvar.h |  375 ++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 566 insertions(+), 14 deletions(-)

diffs (truncated from 757 to 300 lines):

diff -r 4c1cd0619153 -r a3007c7809b5 sys/dev/ic/spdmem.c
--- a/sys/dev/ic/spdmem.c       Mon Apr 20 01:33:22 2015 +0000
+++ b/sys/dev/ic/spdmem.c       Mon Apr 20 02:55:14 2015 +0000
@@ -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_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 @@
        "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 @@
        "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 @@
                        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 @@
        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 @@
                    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 @@
        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_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
+}
diff -r 4c1cd0619153 -r a3007c7809b5 sys/dev/ic/spdmemreg.h
--- a/sys/dev/ic/spdmemreg.h    Mon Apr 20 01:33:22 2015 +0000
+++ b/sys/dev/ic/spdmemreg.h    Mon Apr 20 02:55:14 2015 +0000
@@ -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 */
diff -r 4c1cd0619153 -r a3007c7809b5 sys/dev/ic/spdmemvar.h
--- a/sys/dev/ic/spdmemvar.h    Mon Apr 20 01:33:22 2015 +0000
+++ b/sys/dev/ic/spdmemvar.h    Mon Apr 20 02:55:14 2015 +0000
@@ -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
 



Home | Main Index | Thread Index | Old Index