The branch stable/13 has been updated by rpokala:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=861fdb46300bd16808ca443ecc222f10db02d53d

commit 861fdb46300bd16808ca443ecc222f10db02d53d
Author:     Ravi Pokala <rpok...@freebsd.org>
AuthorDate: 2023-04-25 06:07:39 +0000
Commit:     Ravi Pokala <rpok...@freebsd.org>
CommitDate: 2023-05-05 00:27:55 +0000

    jedec_dimm(4): Add manufacturing year and week.
    
    DDR3 and DDR4 encode the week and year that the DIMM was manufactured,
    as a pair of two-digit binary-coded decimal values. Read the values, and
    report them as (uint8_t)s.
    
    Reviewed by:    imp, jhb
    MFC after:      1 week
    Sponsored by:   Panasas
    Differential Revision:  https://reviews.freebsd.org/D39795
    
    (cherry picked from commit de57e0ef5a15c75231191e643d4d1949ddbca92d)
---
 share/man/man4/jedec_dimm.4     |  10 ++-
 sys/dev/jedec_dimm/jedec_dimm.c | 131 +++++++++++++++++++++++++++++++++++++++-
 sys/dev/jedec_dimm/jedec_dimm.h |   6 +-
 3 files changed, 144 insertions(+), 3 deletions(-)

diff --git a/share/man/man4/jedec_dimm.4 b/share/man/man4/jedec_dimm.4
index ea4183fafc1a..6c13da1a450e 100644
--- a/share/man/man4/jedec_dimm.4
+++ b/share/man/man4/jedec_dimm.4
@@ -26,7 +26,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd July 31, 2018
+.Dd April 25, 2023
 .Dt JEDEC_DIMM 4
 .Os
 .Sh NAME
@@ -76,6 +76,10 @@ interface; all values are read-only:
 a string description of the DIMM, including TSOD and slotid info if present.
 .It Va dev.jedec_dimm.X.capacity
 the DIMM's memory capacity, in megabytes
+.It Va dev.jedec_dimm.X.mfg_week
+the week within the year in which the DIMM was manufactured
+.It Va dev.jedec_dimm.X.mfg_year
+the year in which the DIMM was manufactured
 .It Va dev.jedec_dimm.X.part
 the manufacturer's part number of the DIMM
 .It Va dev.jedec_dimm.X.serial
@@ -144,6 +148,8 @@ dev.jedec_dimm.0.%location: addr=0xa0
 dev.jedec_dimm.0.%parent: smbus0
 dev.jedec_dimm.0.%pnpinfo:
 dev.jedec_dimm.0.capacity: 16384
+dev.jedec_dimm.0.mfg_week: 30
+dev.jedec_dimm.0.mfg_year: 17
 dev.jedec_dimm.0.part: 36ASF2G72PZ-2G1A2
 dev.jedec_dimm.0.serial: 0ea815de
 dev.jedec_dimm.0.slotid: A1
@@ -156,6 +162,8 @@ dev.jedec_dimm.6.%location: addr=0xa8
 dev.jedec_dimm.6.%parent: smbus1
 dev.jedec_dimm.6.%pnpinfo:
 dev.jedec_dimm.6.capacity: 8192
+dev.jedec_dimm.6.mfg_week: 13
+dev.jedec_dimm.6.mfg_year: 19
 dev.jedec_dimm.6.part: VRA9MR8B2H1603
 dev.jedec_dimm.6.serial: 0c4c46ad
 dev.jedec_dimm.6.temp: 43.1C
diff --git a/sys/dev/jedec_dimm/jedec_dimm.c b/sys/dev/jedec_dimm/jedec_dimm.c
index 884da8b51061..73ea10c5c5e1 100644
--- a/sys/dev/jedec_dimm/jedec_dimm.c
+++ b/sys/dev/jedec_dimm/jedec_dimm.c
@@ -4,7 +4,7 @@
  * Authors: Ravi Pokala (rpok...@freebsd.org), Andriy Gapon (a...@freebsd.org)
  *
  * Copyright (c) 2016 Andriy Gapon <a...@freebsd.org>
- * Copyright (c) 2018 Panasas
+ * Copyright (c) 2018-2023 Panasas
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -56,6 +56,8 @@ struct jedec_dimm_softc {
        device_t smbus;
        uint8_t spd_addr;       /* SMBus address of the SPD EEPROM. */
        uint8_t tsod_addr;      /* Address of the Thermal Sensor On DIMM */
+       uint8_t mfg_year;
+       uint8_t mfg_week;
        uint32_t capacity_mb;
        char type_str[5];
        char part_str[21]; /* 18 (DDR3) or 20 (DDR4) chars, plus terminator */
@@ -154,6 +156,9 @@ static int jedec_dimm_dump(struct jedec_dimm_softc *sc, 
enum dram_type type);
 static int jedec_dimm_field_to_str(struct jedec_dimm_softc *sc, char *dst,
     size_t dstsz, uint16_t offset, uint16_t len, bool ascii);
 
+static int jedec_dimm_mfg_date(struct jedec_dimm_softc *sc, enum dram_type 
type,
+    uint8_t *year, uint8_t *week);
+
 static int jedec_dimm_probe(device_t dev);
 
 static int jedec_dimm_readw_be(struct jedec_dimm_softc *sc, uint8_t reg,
@@ -257,6 +262,11 @@ jedec_dimm_attach(device_t dev)
                goto out;
        }
 
+       rc = jedec_dimm_mfg_date(sc, type, &sc->mfg_year, &sc->mfg_week);
+       if (rc != 0) {
+               goto out;
+       }
+
        rc = jedec_dimm_field_to_str(sc, sc->part_str, sizeof(sc->part_str),
            partnum_offset, partnum_len, true);
        if (rc != 0) {
@@ -336,6 +346,14 @@ no_tsod:
            CTLFLAG_RD | CTLFLAG_MPSAFE, sc->serial_str, 0,
            "DIMM Serial Number");
 
+       SYSCTL_ADD_U8(ctx, children, OID_AUTO, "mfg_year",
+           CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, sc->mfg_year,
+           "DIMM manufacturing year (20xx)");
+
+       SYSCTL_ADD_U8(ctx, children, OID_AUTO, "mfg_week",
+           CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, sc->mfg_week,
+           "DIMM manufacturing week");
+
        /* Create the temperature sysctl IFF the TSOD is present and valid */
        if (tsod_present && (tsod_match != NULL)) {
                SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "temp",
@@ -822,6 +840,117 @@ out:
        return (rc);
 }
 
+/**
+ * Both DDR3 and DDR4 encode manufacturing date as a one-byte BCD-encoded
+ * year (offset from 2000) and a one-byte BCD-encoded week within that year.
+ * The SPD offsets are different between the two types.
+ *
+ * @author rpokala
+ *
+ * @param[in] sc
+ *      Instance-specific context data
+ *
+ * @param[in] dram_type
+ *      The locations of the manufacturing date depends on the type of the 
DIMM.
+ *
+ * @param[out] year
+ *      The manufacturing year, offset from 2000
+ *
+ * @param[out] week
+ *      The manufacturing week within the year
+ */
+static int
+jedec_dimm_mfg_date(struct jedec_dimm_softc *sc, enum dram_type type,
+    uint8_t *year, uint8_t *week)
+{
+       uint8_t year_bcd;
+       uint8_t week_bcd;
+       uint16_t year_offset;
+       uint16_t week_offset;
+       bool page_changed;
+       int rc;
+
+       switch (type) {
+       case DRAM_TYPE_DDR3_SDRAM:
+               year_offset = SPD_OFFSET_DDR3_MOD_MFG_YEAR;
+               week_offset = SPD_OFFSET_DDR3_MOD_MFG_WEEK;
+               break;
+       case DRAM_TYPE_DDR4_SDRAM:
+               year_offset = SPD_OFFSET_DDR4_MOD_MFG_YEAR;
+               week_offset = SPD_OFFSET_DDR4_MOD_MFG_WEEK;
+               break;
+       default:
+               device_printf(sc->dev, "unsupported dram_type 0x%02x\n", type);
+               rc = EINVAL;
+               page_changed = false;
+               goto out;
+       }
+
+       /* Change to the proper page. Offsets [0, 255] are in page0; offsets
+        * [256, 512] are in page1.
+        *
+        * *The page must be reset to page0 before returning.*
+        *
+        * For the page-change operation, only the DTI and LSA matter; the
+        * offset and write-value are ignored, so use just 0.
+        *
+        * Mercifully, JEDEC defined the fields such that all of the
+        * manufacturing-related ones are on the same page, so we don't need to
+        * worry about that complication.
+        */
+       if (year_offset < JEDEC_SPD_PAGE_SIZE) {
+               page_changed = false;
+       } else if (year_offset < (2 * JEDEC_SPD_PAGE_SIZE)) {
+               page_changed = true;
+               rc = smbus_writeb(sc->smbus,
+                   (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET1), 0, 0);
+               if (rc != 0) {
+                       device_printf(sc->dev,
+                           "unable to change page for offset 0x%04x: %d\n",
+                           year_offset, rc);
+               }
+               /* Adjust the offset to account for the page change. */
+               year_offset -= JEDEC_SPD_PAGE_SIZE;
+               week_offset -= JEDEC_SPD_PAGE_SIZE;
+       } else {
+               device_printf(sc->dev, "invalid offset 0x%04x\n", year_offset);
+               rc = EINVAL;
+               page_changed = false;
+               goto out;
+       }
+
+       rc = smbus_readb(sc->smbus, sc->spd_addr, year_offset, &year_bcd);
+       if (rc != 0) {
+               device_printf(sc->dev, "failed to read mfg year: %d\n", rc);
+               goto out;
+       }
+
+       rc = smbus_readb(sc->smbus, sc->spd_addr, week_offset, &week_bcd);
+       if (rc != 0) {
+               device_printf(sc->dev, "failed to read mfg week: %d\n", rc);
+               goto out;
+       }
+
+       /* Convert from one-byte BCD to one-byte integer. */
+       *year = (((year_bcd & 0xf0) >> 4) * 10) + (year_bcd & 0x0f);
+       *week = (((week_bcd & 0xf0) >> 4) * 10) + (week_bcd & 0x0f);
+
+out:
+       if (page_changed) {
+               int rc2;
+               /* Switch back to page0 before returning. */
+               rc2 = smbus_writeb(sc->smbus,
+                   (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET0), 0, 0);
+               if (rc2 != 0) {
+                       device_printf(sc->dev,
+                           "unable to restore page for offset 0x%04x: %d\n",
+                           year_offset, rc2);
+               }
+       }
+
+       return (rc);
+}
+
 /**
  * device_probe() method. Validate the address that was given as a hint, and
  * display an error if it's bogus. Make sure that we're dealing with one of the
diff --git a/sys/dev/jedec_dimm/jedec_dimm.h b/sys/dev/jedec_dimm/jedec_dimm.h
index 3b330251efc5..00a9b3d521b6 100644
--- a/sys/dev/jedec_dimm/jedec_dimm.h
+++ b/sys/dev/jedec_dimm/jedec_dimm.h
@@ -3,7 +3,7 @@
  *
  * Authors: Ravi Pokala (rpok...@freebsd.org)
  *
- * Copyright (c) 2018 Panasas
+ * Copyright (c) 2018-2023 Panasas
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -90,6 +90,8 @@
 #define SPD_OFFSET_DDR3_SDRAM_WIDTH    7
 #define SPD_OFFSET_DDR3_BUS_WIDTH      8
 #define SPD_OFFSET_DDR3_TSOD_PRESENT   32
+#define SPD_OFFSET_DDR3_MOD_MFG_YEAR   120
+#define SPD_OFFSET_DDR3_MOD_MFG_WEEK   121
 #define SPD_OFFSET_DDR3_SERIAL         122
 #define SPD_LEN_DDR3_SERIAL            4
 #define SPD_OFFSET_DDR3_PARTNUM                128
@@ -100,6 +102,8 @@
 #define SPD_OFFSET_DDR4_SDRAM_WIDTH    12
 #define SPD_OFFSET_DDR4_BUS_WIDTH      13
 #define SPD_OFFSET_DDR4_TSOD_PRESENT   14
+#define SPD_OFFSET_DDR4_MOD_MFG_YEAR   323
+#define SPD_OFFSET_DDR4_MOD_MFG_WEEK   324
 #define SPD_OFFSET_DDR4_SERIAL         325
 #define SPD_LEN_DDR4_SERIAL            4
 #define SPD_OFFSET_DDR4_PARTNUM                329

Reply via email to