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
This commit is contained in:
Ravi Pokala 2023-04-24 23:07:39 -07:00
parent e315351fc7
commit de57e0ef5a
3 changed files with 144 additions and 3 deletions

View File

@ -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

View File

@ -4,7 +4,7 @@
* Authors: Ravi Pokala (rpokala@freebsd.org), Andriy Gapon (avg@FreeBSD.org)
*
* Copyright (c) 2016 Andriy Gapon <avg@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 @@ jedec_dimm_attach(device_t dev)
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 @@ jedec_dimm_field_to_str(struct jedec_dimm_softc *sc, char *dst, size_t dstsz,
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

View File

@ -3,7 +3,7 @@
*
* Authors: Ravi Pokala (rpokala@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