xref: /freebsd/sys/dev/jedec_dimm/jedec_dimm.c (revision 83caa244bc9eef6949a1250a875ad1409775a46e)
1dcd935dfSRavi Pokala /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3dcd935dfSRavi Pokala  *
4dcd935dfSRavi Pokala  * Authors: Ravi Pokala (rpokala@freebsd.org), Andriy Gapon (avg@FreeBSD.org)
5dcd935dfSRavi Pokala  *
6dcd935dfSRavi Pokala  * Copyright (c) 2016 Andriy Gapon <avg@FreeBSD.org>
7de57e0efSRavi Pokala  * Copyright (c) 2018-2023 Panasas
8dcd935dfSRavi Pokala  *
9dcd935dfSRavi Pokala  * Redistribution and use in source and binary forms, with or without
10dcd935dfSRavi Pokala  * modification, are permitted provided that the following conditions
11dcd935dfSRavi Pokala  * are met:
12dcd935dfSRavi Pokala  * 1. Redistributions of source code must retain the above copyright
13dcd935dfSRavi Pokala  *    notice, this list of conditions and the following disclaimer.
14dcd935dfSRavi Pokala  * 2. Redistributions in binary form must reproduce the above copyright
15dcd935dfSRavi Pokala  *    notice, this list of conditions and the following disclaimer in the
16dcd935dfSRavi Pokala  *    documentation and/or other materials provided with the distribution.
17dcd935dfSRavi Pokala  *
18dcd935dfSRavi Pokala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19dcd935dfSRavi Pokala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20dcd935dfSRavi Pokala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21dcd935dfSRavi Pokala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22dcd935dfSRavi Pokala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23dcd935dfSRavi Pokala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24dcd935dfSRavi Pokala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25dcd935dfSRavi Pokala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26dcd935dfSRavi Pokala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27dcd935dfSRavi Pokala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28dcd935dfSRavi Pokala  * SUCH DAMAGE.
29dcd935dfSRavi Pokala  */
30dcd935dfSRavi Pokala 
31dcd935dfSRavi Pokala /*
32b5988450SRavi Pokala  * This driver is a super-set of the now-deleted jedec_ts(4), and most of the
33b5988450SRavi Pokala  * code for reading and reporting the temperature is either based on that driver,
34b5988450SRavi Pokala  * or copied from it verbatim.
35dcd935dfSRavi Pokala  */
36dcd935dfSRavi Pokala 
37dcd935dfSRavi Pokala #include <sys/param.h>
38dcd935dfSRavi Pokala #include <sys/kernel.h>
39dcd935dfSRavi Pokala #include <sys/bus.h>
40dcd935dfSRavi Pokala #include <sys/endian.h>
41dcd935dfSRavi Pokala #include <sys/malloc.h>
42dcd935dfSRavi Pokala #include <sys/module.h>
43dcd935dfSRavi Pokala #include <sys/sysctl.h>
44dcd935dfSRavi Pokala #include <sys/systm.h>
45dcd935dfSRavi Pokala 
46dcd935dfSRavi Pokala #include <dev/jedec_dimm/jedec_dimm.h>
47dcd935dfSRavi Pokala #include <dev/smbus/smbconf.h>
48dcd935dfSRavi Pokala #include <dev/smbus/smbus.h>
49dcd935dfSRavi Pokala 
50dcd935dfSRavi Pokala #include "smbus_if.h"
51dcd935dfSRavi Pokala 
52dcd935dfSRavi Pokala struct jedec_dimm_softc {
53dcd935dfSRavi Pokala 	device_t dev;
54dcd935dfSRavi Pokala 	device_t smbus;
55dcd935dfSRavi Pokala 	uint8_t spd_addr;	/* SMBus address of the SPD EEPROM. */
56dcd935dfSRavi Pokala 	uint8_t tsod_addr;	/* Address of the Thermal Sensor On DIMM */
57de57e0efSRavi Pokala 	uint8_t mfg_year;
58de57e0efSRavi Pokala 	uint8_t mfg_week;
59dcd935dfSRavi Pokala 	uint32_t capacity_mb;
60dcd935dfSRavi Pokala 	char type_str[5];
61dcd935dfSRavi Pokala 	char part_str[21]; /* 18 (DDR3) or 20 (DDR4) chars, plus terminator */
62dcd935dfSRavi Pokala 	char serial_str[9]; /* 4 bytes = 8 nybble characters, plus terminator */
63dcd935dfSRavi Pokala 	char *slotid_str; /* Optional DIMM slot identifier (silkscreen) */
64dcd935dfSRavi Pokala };
65dcd935dfSRavi Pokala 
66dcd935dfSRavi Pokala /* General Thermal Sensor on DIMM (TSOD) identification notes.
67dcd935dfSRavi Pokala  *
68dcd935dfSRavi Pokala  * The JEDEC TSE2004av specification defines the device ID that all compliant
69dcd935dfSRavi Pokala  * devices should use, but very few do in practice. Maybe that's because the
70dcd935dfSRavi Pokala  * earlier TSE2002av specification was rather vague about that.
71dcd935dfSRavi Pokala  * Rare examples are IDT TSE2004GB2B0 and Atmel AT30TSE004A, not sure if
72dcd935dfSRavi Pokala  * they are TSE2004av compliant by design or by accident.
73dcd935dfSRavi Pokala  * Also, the specification mandates that PCI SIG manufacturer IDs are to be
74dcd935dfSRavi Pokala  * used, but in practice the JEDEC manufacturer IDs are often used.
75dcd935dfSRavi Pokala  */
76dcd935dfSRavi Pokala const struct jedec_dimm_tsod_dev {
77dcd935dfSRavi Pokala 	uint16_t	vendor_id;
78dcd935dfSRavi Pokala 	uint8_t		device_id;
79dcd935dfSRavi Pokala 	const char	*description;
80dcd935dfSRavi Pokala } known_tsod_devices[] = {
81dcd935dfSRavi Pokala 	/* Analog Devices ADT7408.
82dcd935dfSRavi Pokala 	 * http://www.analog.com/media/en/technical-documentation/data-sheets/ADT7408.pdf
83dcd935dfSRavi Pokala 	 */
84dcd935dfSRavi Pokala 	{ 0x11d4, 0x08, "Analog Devices TSOD" },
85dcd935dfSRavi Pokala 
86dcd935dfSRavi Pokala 	/* Atmel AT30TSE002B, AT30TSE004A.
87dcd935dfSRavi Pokala 	 * http://www.atmel.com/images/doc8711.pdf
88dcd935dfSRavi Pokala 	 * http://www.atmel.com/images/atmel-8868-dts-at30tse004a-datasheet.pdf
89dcd935dfSRavi Pokala 	 * Note how one chip uses the JEDEC Manufacturer ID while the other
90dcd935dfSRavi Pokala 	 * uses the PCI SIG one.
91dcd935dfSRavi Pokala 	 */
92dcd935dfSRavi Pokala 	{ 0x001f, 0x82, "Atmel TSOD" },
93dcd935dfSRavi Pokala 	{ 0x1114, 0x22, "Atmel TSOD" },
94dcd935dfSRavi Pokala 
95dcd935dfSRavi Pokala 	/* Integrated Device Technology (IDT) TS3000B3A, TSE2002B3C,
96dcd935dfSRavi Pokala 	 * TSE2004GB2B0 chips and their variants.
97dcd935dfSRavi Pokala 	 * http://www.idt.com/sites/default/files/documents/IDT_TSE2002B3C_DST_20100512_120303152056.pdf
98dcd935dfSRavi Pokala 	 * http://www.idt.com/sites/default/files/documents/IDT_TS3000B3A_DST_20101129_120303152013.pdf
99dcd935dfSRavi Pokala 	 * https://www.idt.com/document/dst/tse2004gb2b0-datasheet
100dcd935dfSRavi Pokala 	 */
101dcd935dfSRavi Pokala 	{ 0x00b3, 0x29, "IDT TSOD" },
102dcd935dfSRavi Pokala 	{ 0x00b3, 0x22, "IDT TSOD" },
103dcd935dfSRavi Pokala 
104dcd935dfSRavi Pokala 	/* Maxim Integrated MAX6604.
105dcd935dfSRavi Pokala 	 * Different document revisions specify different Device IDs.
106dcd935dfSRavi Pokala 	 * Document 19-3837; Rev 0; 10/05 has 0x3e00 while
107dcd935dfSRavi Pokala 	 * 19-3837; Rev 3; 10/11 has 0x5400.
108dcd935dfSRavi Pokala 	 * http://datasheets.maximintegrated.com/en/ds/MAX6604.pdf
109dcd935dfSRavi Pokala 	 */
110dcd935dfSRavi Pokala 	{ 0x004d, 0x3e, "Maxim Integrated TSOD" },
111dcd935dfSRavi Pokala 	{ 0x004d, 0x54, "Maxim Integrated TSOD" },
112dcd935dfSRavi Pokala 
113dcd935dfSRavi Pokala 	/* Microchip Technology MCP9805, MCP9843, MCP98242, MCP98243
114dcd935dfSRavi Pokala 	 * and their variants.
115dcd935dfSRavi Pokala 	 * http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf
116dcd935dfSRavi Pokala 	 * Microchip Technology EMC1501.
117dcd935dfSRavi Pokala 	 * http://ww1.microchip.com/downloads/en/DeviceDoc/00001605A.pdf
118dcd935dfSRavi Pokala 	 */
119dcd935dfSRavi Pokala 	{ 0x0054, 0x00, "Microchip TSOD" },
120dcd935dfSRavi Pokala 	{ 0x0054, 0x20, "Microchip TSOD" },
121dcd935dfSRavi Pokala 	{ 0x0054, 0x21, "Microchip TSOD" },
122dcd935dfSRavi Pokala 	{ 0x1055, 0x08, "Microchip TSOD" },
123dcd935dfSRavi Pokala 
124dcd935dfSRavi Pokala 	/* NXP Semiconductors SE97 and SE98.
125dcd935dfSRavi Pokala 	 * http://www.nxp.com/docs/en/data-sheet/SE97B.pdf
126dcd935dfSRavi Pokala 	 */
127dcd935dfSRavi Pokala 	{ 0x1131, 0xa1, "NXP TSOD" },
128dcd935dfSRavi Pokala 	{ 0x1131, 0xa2, "NXP TSOD" },
129dcd935dfSRavi Pokala 
130dcd935dfSRavi Pokala 	/* ON Semiconductor CAT34TS02 revisions B and C, CAT6095 and compatible.
131dcd935dfSRavi Pokala 	 * https://www.onsemi.com/pub/Collateral/CAT34TS02-D.PDF
132dcd935dfSRavi Pokala 	 * http://www.onsemi.com/pub/Collateral/CAT6095-D.PDF
133dcd935dfSRavi Pokala 	 */
134dcd935dfSRavi Pokala 	{ 0x1b09, 0x08, "ON Semiconductor TSOD" },
135dcd935dfSRavi Pokala 	{ 0x1b09, 0x0a, "ON Semiconductor TSOD" },
136dcd935dfSRavi Pokala 
137dcd935dfSRavi Pokala 	/* ST[Microelectronics] STTS424E02, STTS2002 and others.
138dcd935dfSRavi Pokala 	 * http://www.st.com/resource/en/datasheet/cd00157558.pdf
139dcd935dfSRavi Pokala 	 * http://www.st.com/resource/en/datasheet/stts2002.pdf
140dcd935dfSRavi Pokala 	 */
141dcd935dfSRavi Pokala 	{ 0x104a, 0x00, "ST Microelectronics TSOD" },
142dcd935dfSRavi Pokala 	{ 0x104a, 0x03, "ST Microelectronics TSOD" },
143dcd935dfSRavi Pokala };
144dcd935dfSRavi Pokala 
14515d69c84SRavi Pokala static int jedec_dimm_adjust_offset(struct jedec_dimm_softc *sc,
14615d69c84SRavi Pokala     uint16_t orig_offset, uint16_t *new_offset, bool *page_changed);
14715d69c84SRavi Pokala 
148dcd935dfSRavi Pokala static int jedec_dimm_attach(device_t dev);
149dcd935dfSRavi Pokala 
150dcd935dfSRavi Pokala static int jedec_dimm_capacity(struct jedec_dimm_softc *sc, enum dram_type type,
151dcd935dfSRavi Pokala     uint32_t *capacity_mb);
152dcd935dfSRavi Pokala 
153dcd935dfSRavi Pokala static int jedec_dimm_detach(device_t dev);
154dcd935dfSRavi Pokala 
155dcd935dfSRavi Pokala static int jedec_dimm_dump(struct jedec_dimm_softc *sc, enum dram_type type);
156dcd935dfSRavi Pokala 
157dcd935dfSRavi Pokala static int jedec_dimm_field_to_str(struct jedec_dimm_softc *sc, char *dst,
158dcd935dfSRavi Pokala     size_t dstsz, uint16_t offset, uint16_t len, bool ascii);
159dcd935dfSRavi Pokala 
160de57e0efSRavi Pokala static int jedec_dimm_mfg_date(struct jedec_dimm_softc *sc, enum dram_type type,
161de57e0efSRavi Pokala     uint8_t *year, uint8_t *week);
162de57e0efSRavi Pokala 
163dcd935dfSRavi Pokala static int jedec_dimm_probe(device_t dev);
164dcd935dfSRavi Pokala 
165dcd935dfSRavi Pokala static int jedec_dimm_readw_be(struct jedec_dimm_softc *sc, uint8_t reg,
166dcd935dfSRavi Pokala     uint16_t *val);
167dcd935dfSRavi Pokala 
16815d69c84SRavi Pokala static int jedec_dimm_reset_page0(struct jedec_dimm_softc *sc);
16915d69c84SRavi Pokala 
170dcd935dfSRavi Pokala static int jedec_dimm_temp_sysctl(SYSCTL_HANDLER_ARGS);
171dcd935dfSRavi Pokala 
172dcd935dfSRavi Pokala static const char *jedec_dimm_tsod_match(uint16_t vid, uint16_t did);
173dcd935dfSRavi Pokala 
174dcd935dfSRavi Pokala 
175dcd935dfSRavi Pokala /**
17615d69c84SRavi Pokala  * The DDR4 SPD information is spread across two 256-byte pages, but the
17715d69c84SRavi Pokala  * offsets in the spec are absolute, not per-page. If a given offset is on the
17815d69c84SRavi Pokala  * second page, we need to change to that page and adjust the offset to be
17915d69c84SRavi Pokala  * relative to that page.
18015d69c84SRavi Pokala  *
18115d69c84SRavi Pokala  * @author rpokala
18215d69c84SRavi Pokala  *
18315d69c84SRavi Pokala  * @param[in] sc
18415d69c84SRavi Pokala  *      Instance-specific context data
18515d69c84SRavi Pokala  *
18615d69c84SRavi Pokala  * @param[in] orig_offset
18715d69c84SRavi Pokala  *      The original value of the offset, before any adjustment is made.
18815d69c84SRavi Pokala  *
18915d69c84SRavi Pokala  * @param[out] new_offset
19015d69c84SRavi Pokala  *      The offset to actually read from, adjusted if needed.
19115d69c84SRavi Pokala  *
19215d69c84SRavi Pokala  * @param[out] page_changed
19315d69c84SRavi Pokala  *      Whether or not the page was actually changed, and the offset adjusted.
19415d69c84SRavi Pokala  *      *If 'true', caller must call reset_page0() before itself returning.*
19515d69c84SRavi Pokala  */
19615d69c84SRavi Pokala static int
jedec_dimm_adjust_offset(struct jedec_dimm_softc * sc,uint16_t orig_offset,uint16_t * new_offset,bool * page_changed)19715d69c84SRavi Pokala jedec_dimm_adjust_offset(struct jedec_dimm_softc *sc, uint16_t orig_offset,
19815d69c84SRavi Pokala 	uint16_t *new_offset, bool *page_changed)
19915d69c84SRavi Pokala {
20015d69c84SRavi Pokala 	int rc;
20115d69c84SRavi Pokala 
20215d69c84SRavi Pokala 	/* Don't change the offset, or indicate that the page has been changed,
20315d69c84SRavi Pokala 	 * until the page has actually been changed.
20415d69c84SRavi Pokala 	 */
20515d69c84SRavi Pokala 	*new_offset = orig_offset;
20615d69c84SRavi Pokala 	*page_changed = false;
20715d69c84SRavi Pokala 
20815d69c84SRavi Pokala 	/* Change to the proper page. Offsets [0, 255] are in page0; offsets
20915d69c84SRavi Pokala 	 * [256, 511] are in page1.
21015d69c84SRavi Pokala 	 */
21115d69c84SRavi Pokala 	if (orig_offset < JEDEC_SPD_PAGE_SIZE) {
21215d69c84SRavi Pokala 		/* Within page0; no adjustment or page-change required. */
21315d69c84SRavi Pokala 		rc = 0;
21415d69c84SRavi Pokala 		goto out;
21515d69c84SRavi Pokala 	} else if (orig_offset >= (2 * JEDEC_SPD_PAGE_SIZE)) {
21615d69c84SRavi Pokala 		/* Outside of expected range. */
21715d69c84SRavi Pokala 		rc = EINVAL;
21815d69c84SRavi Pokala 		device_printf(sc->dev, "invalid offset 0x%04x\n", orig_offset);
21915d69c84SRavi Pokala 		goto out;
22015d69c84SRavi Pokala 	}
22115d69c84SRavi Pokala 
22215d69c84SRavi Pokala 	/* 'orig_offset' is in page1. Switch to that page, and adjust
22315d69c84SRavi Pokala 	 * 'new_offset' accordingly if successful.
22415d69c84SRavi Pokala 	 *
22515d69c84SRavi Pokala 	 * For the page-change operation, only the DTI and LSA matter; the
22615d69c84SRavi Pokala 	 * offset and write-value are ignored, so just use 0.
22715d69c84SRavi Pokala 	 */
22815d69c84SRavi Pokala 	rc = smbus_writeb(sc->smbus, (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET1),
22915d69c84SRavi Pokala 	    0, 0);
23015d69c84SRavi Pokala 	if (rc != 0) {
23115d69c84SRavi Pokala 		device_printf(sc->dev,
23215d69c84SRavi Pokala 		    "unable to change page for offset 0x%04x: %d\n",
23315d69c84SRavi Pokala 		    orig_offset, rc);
23415d69c84SRavi Pokala 		goto out;
23515d69c84SRavi Pokala 	}
23615d69c84SRavi Pokala 	*page_changed = true;
23715d69c84SRavi Pokala 	*new_offset = orig_offset - JEDEC_SPD_PAGE_SIZE;
23815d69c84SRavi Pokala 
23915d69c84SRavi Pokala out:
24015d69c84SRavi Pokala 	return (rc);
24115d69c84SRavi Pokala }
24215d69c84SRavi Pokala 
24315d69c84SRavi Pokala /**
244dcd935dfSRavi Pokala  * device_attach() method. Read the DRAM type, use that to determine the offsets
245dcd935dfSRavi Pokala  * and lengths of the asset string fields. Calculate the capacity. If a TSOD is
246dcd935dfSRavi Pokala  * present, figure out exactly what it is, and update the device description.
247dcd935dfSRavi Pokala  * If all of that was successful, create the sysctls for the DIMM. If an
248dcd935dfSRavi Pokala  * optional slotid has been hinted, create a sysctl for that too.
249dcd935dfSRavi Pokala  *
250dcd935dfSRavi Pokala  * @author rpokala
251dcd935dfSRavi Pokala  *
252dcd935dfSRavi Pokala  * @param[in,out] dev
253dcd935dfSRavi Pokala  *      Device being attached.
254dcd935dfSRavi Pokala  */
255dcd935dfSRavi Pokala static int
jedec_dimm_attach(device_t dev)256dcd935dfSRavi Pokala jedec_dimm_attach(device_t dev)
257dcd935dfSRavi Pokala {
258dcd935dfSRavi Pokala 	uint8_t byte;
259dcd935dfSRavi Pokala 	uint16_t devid;
260dcd935dfSRavi Pokala 	uint16_t partnum_len;
261dcd935dfSRavi Pokala 	uint16_t partnum_offset;
262dcd935dfSRavi Pokala 	uint16_t serial_len;
263dcd935dfSRavi Pokala 	uint16_t serial_offset;
264dcd935dfSRavi Pokala 	uint16_t tsod_present_offset;
265dcd935dfSRavi Pokala 	uint16_t vendorid;
266dcd935dfSRavi Pokala 	bool tsod_present;
267dcd935dfSRavi Pokala 	int rc;
268dcd935dfSRavi Pokala 	enum dram_type type;
269dcd935dfSRavi Pokala 	struct jedec_dimm_softc *sc;
270dcd935dfSRavi Pokala 	struct sysctl_ctx_list *ctx;
271dcd935dfSRavi Pokala 	struct sysctl_oid *oid;
272dcd935dfSRavi Pokala 	struct sysctl_oid_list *children;
273dcd935dfSRavi Pokala 	const char *tsod_match;
274dcd935dfSRavi Pokala 	const char *slotid_str;
275dcd935dfSRavi Pokala 
276dcd935dfSRavi Pokala 	sc = device_get_softc(dev);
277dcd935dfSRavi Pokala 	ctx = device_get_sysctl_ctx(dev);
278dcd935dfSRavi Pokala 	oid = device_get_sysctl_tree(dev);
279dcd935dfSRavi Pokala 	children = SYSCTL_CHILDREN(oid);
280dcd935dfSRavi Pokala 
281dcd935dfSRavi Pokala 	bzero(sc, sizeof(*sc));
282dcd935dfSRavi Pokala 	sc->dev = dev;
283dcd935dfSRavi Pokala 	sc->smbus = device_get_parent(dev);
284dcd935dfSRavi Pokala 	sc->spd_addr = smbus_get_addr(dev);
285dcd935dfSRavi Pokala 
286dcd935dfSRavi Pokala 	/* The TSOD address has a different DTI from the SPD address, but shares
287dcd935dfSRavi Pokala 	 * the LSA bits.
288dcd935dfSRavi Pokala 	 */
289dcd935dfSRavi Pokala 	sc->tsod_addr = JEDEC_DTI_TSOD | (sc->spd_addr & 0x0f);
290dcd935dfSRavi Pokala 
291dcd935dfSRavi Pokala 	/* Read the DRAM type, and set the various offsets and lengths. */
292dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, SPD_OFFSET_DRAM_TYPE, &byte);
293dcd935dfSRavi Pokala 	if (rc != 0) {
294dcd935dfSRavi Pokala 		device_printf(dev, "failed to read dram_type: %d\n", rc);
295dcd935dfSRavi Pokala 		goto out;
296dcd935dfSRavi Pokala 	}
297dcd935dfSRavi Pokala 	type = (enum dram_type) byte;
298dcd935dfSRavi Pokala 	switch (type) {
299dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR3_SDRAM:
300dcd935dfSRavi Pokala 		(void) snprintf(sc->type_str, sizeof(sc->type_str), "DDR3");
301dcd935dfSRavi Pokala 		partnum_len = SPD_LEN_DDR3_PARTNUM;
302dcd935dfSRavi Pokala 		partnum_offset = SPD_OFFSET_DDR3_PARTNUM;
303dcd935dfSRavi Pokala 		serial_len = SPD_LEN_DDR3_SERIAL;
304dcd935dfSRavi Pokala 		serial_offset = SPD_OFFSET_DDR3_SERIAL;
305dcd935dfSRavi Pokala 		tsod_present_offset = SPD_OFFSET_DDR3_TSOD_PRESENT;
306dcd935dfSRavi Pokala 		break;
307dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR4_SDRAM:
308dcd935dfSRavi Pokala 		(void) snprintf(sc->type_str, sizeof(sc->type_str), "DDR4");
309dcd935dfSRavi Pokala 		partnum_len = SPD_LEN_DDR4_PARTNUM;
310dcd935dfSRavi Pokala 		partnum_offset = SPD_OFFSET_DDR4_PARTNUM;
311dcd935dfSRavi Pokala 		serial_len = SPD_LEN_DDR4_SERIAL;
312dcd935dfSRavi Pokala 		serial_offset = SPD_OFFSET_DDR4_SERIAL;
313dcd935dfSRavi Pokala 		tsod_present_offset = SPD_OFFSET_DDR4_TSOD_PRESENT;
314dcd935dfSRavi Pokala 		break;
315dcd935dfSRavi Pokala 	default:
316dcd935dfSRavi Pokala 		device_printf(dev, "unsupported dram_type 0x%02x\n", type);
317dcd935dfSRavi Pokala 		rc = EINVAL;
318dcd935dfSRavi Pokala 		goto out;
319dcd935dfSRavi Pokala 	}
320dcd935dfSRavi Pokala 
321dcd935dfSRavi Pokala 	if (bootverbose) {
322dcd935dfSRavi Pokala 		/* bootverbose debuggery is best-effort, so ignore the rc. */
323dcd935dfSRavi Pokala 		(void) jedec_dimm_dump(sc, type);
324dcd935dfSRavi Pokala 	}
325dcd935dfSRavi Pokala 
326dcd935dfSRavi Pokala 	/* Read all the required info from the SPD. If any of it fails, error
327dcd935dfSRavi Pokala 	 * out without creating the sysctls.
328dcd935dfSRavi Pokala 	 */
329dcd935dfSRavi Pokala 	rc = jedec_dimm_capacity(sc, type, &sc->capacity_mb);
330dcd935dfSRavi Pokala 	if (rc != 0) {
331dcd935dfSRavi Pokala 		goto out;
332dcd935dfSRavi Pokala 	}
333dcd935dfSRavi Pokala 
334de57e0efSRavi Pokala 	rc = jedec_dimm_mfg_date(sc, type, &sc->mfg_year, &sc->mfg_week);
335de57e0efSRavi Pokala 	if (rc != 0) {
336de57e0efSRavi Pokala 		goto out;
337de57e0efSRavi Pokala 	}
338de57e0efSRavi Pokala 
339dcd935dfSRavi Pokala 	rc = jedec_dimm_field_to_str(sc, sc->part_str, sizeof(sc->part_str),
340dcd935dfSRavi Pokala 	    partnum_offset, partnum_len, true);
341dcd935dfSRavi Pokala 	if (rc != 0) {
342dcd935dfSRavi Pokala 		goto out;
343dcd935dfSRavi Pokala 	}
344dcd935dfSRavi Pokala 
345dcd935dfSRavi Pokala 	rc = jedec_dimm_field_to_str(sc, sc->serial_str, sizeof(sc->serial_str),
346dcd935dfSRavi Pokala 	    serial_offset, serial_len, false);
347dcd935dfSRavi Pokala 	if (rc != 0) {
348dcd935dfSRavi Pokala 		goto out;
349dcd935dfSRavi Pokala 	}
350dcd935dfSRavi Pokala 
351dcd935dfSRavi Pokala 	/* The MSBit of the TSOD-presence byte reports whether or not the TSOD
352ca219866SRavi Pokala 	 * is in fact present. (While DDR3 and DDR4 don't explicitly require a
353ca219866SRavi Pokala 	 * TSOD, essentially all DDR3 and DDR4 DIMMs include one.) But, as
354ca219866SRavi Pokala 	 * discussed in [PR 235944], it turns out that some DIMMs claim to have
355ca219866SRavi Pokala 	 * a TSOD when they actually don't. (Or maybe the firmware blocks it?)
356ca219866SRavi Pokala 	 * <sigh>
357ca219866SRavi Pokala 	 * If the SPD data says the TSOD is present, try to read manufacturer
358ca219866SRavi Pokala 	 * and device info from it to confirm that it's a valid TSOD device.
359ca219866SRavi Pokala 	 * If the data is unreadable, just continue as if the TSOD isn't there.
360ca219866SRavi Pokala 	 * If the data was read successfully, see if it is a known TSOD device;
361ca219866SRavi Pokala 	 * it's okay if it isn't (tsod_match == NULL).
362dcd935dfSRavi Pokala 	 */
363dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, tsod_present_offset, &byte);
364dcd935dfSRavi Pokala 	if (rc != 0) {
365dcd935dfSRavi Pokala 		device_printf(dev, "failed to read TSOD-present byte: %d\n",
366dcd935dfSRavi Pokala 		    rc);
367dcd935dfSRavi Pokala 		goto out;
368dcd935dfSRavi Pokala 	}
369dcd935dfSRavi Pokala 	if (byte & 0x80) {
370dcd935dfSRavi Pokala 		tsod_present = true;
371dcd935dfSRavi Pokala 		rc = jedec_dimm_readw_be(sc, TSOD_REG_MANUFACTURER, &vendorid);
372dcd935dfSRavi Pokala 		if (rc != 0) {
373dcd935dfSRavi Pokala 			device_printf(dev,
374dcd935dfSRavi Pokala 			    "failed to read TSOD Manufacturer ID\n");
375ca219866SRavi Pokala 			rc = 0;
376ca219866SRavi Pokala 			goto no_tsod;
377dcd935dfSRavi Pokala 		}
378dcd935dfSRavi Pokala 		rc = jedec_dimm_readw_be(sc, TSOD_REG_DEV_REV, &devid);
379dcd935dfSRavi Pokala 		if (rc != 0) {
380dcd935dfSRavi Pokala 			device_printf(dev, "failed to read TSOD Device ID\n");
381ca219866SRavi Pokala 			rc = 0;
382ca219866SRavi Pokala 			goto no_tsod;
383dcd935dfSRavi Pokala 		}
384dcd935dfSRavi Pokala 
385dcd935dfSRavi Pokala 		tsod_match = jedec_dimm_tsod_match(vendorid, devid);
386dcd935dfSRavi Pokala 		if (bootverbose) {
387dcd935dfSRavi Pokala 			if (tsod_match == NULL) {
388dcd935dfSRavi Pokala 				device_printf(dev,
389dcd935dfSRavi Pokala 				    "Unknown TSOD Manufacturer and Device IDs,"
390dcd935dfSRavi Pokala 				    " 0x%x and 0x%x\n", vendorid, devid);
391dcd935dfSRavi Pokala 			} else {
392dcd935dfSRavi Pokala 				device_printf(dev,
393dcd935dfSRavi Pokala 				    "TSOD: %s\n", tsod_match);
394dcd935dfSRavi Pokala 			}
395dcd935dfSRavi Pokala 		}
396dcd935dfSRavi Pokala 	} else {
397ca219866SRavi Pokala no_tsod:
398dcd935dfSRavi Pokala 		tsod_match = NULL;
399dcd935dfSRavi Pokala 		tsod_present = false;
400dcd935dfSRavi Pokala 	}
401dcd935dfSRavi Pokala 
402dcd935dfSRavi Pokala 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "type",
403dcd935dfSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, sc->type_str, 0,
404dcd935dfSRavi Pokala 	    "DIMM type");
405dcd935dfSRavi Pokala 
406dcd935dfSRavi Pokala 	SYSCTL_ADD_UINT(ctx, children, OID_AUTO, "capacity",
407dcd935dfSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, sc->capacity_mb,
408dcd935dfSRavi Pokala 	    "DIMM capacity (MB)");
409dcd935dfSRavi Pokala 
410dcd935dfSRavi Pokala 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "part",
411dcd935dfSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, sc->part_str, 0,
412dcd935dfSRavi Pokala 	    "DIMM Part Number");
413dcd935dfSRavi Pokala 
414dcd935dfSRavi Pokala 	SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "serial",
415dcd935dfSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, sc->serial_str, 0,
416dcd935dfSRavi Pokala 	    "DIMM Serial Number");
417dcd935dfSRavi Pokala 
418de57e0efSRavi Pokala 	SYSCTL_ADD_U8(ctx, children, OID_AUTO, "mfg_year",
419de57e0efSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, sc->mfg_year,
420de57e0efSRavi Pokala 	    "DIMM manufacturing year (20xx)");
421de57e0efSRavi Pokala 
422de57e0efSRavi Pokala 	SYSCTL_ADD_U8(ctx, children, OID_AUTO, "mfg_week",
423de57e0efSRavi Pokala 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, sc->mfg_week,
424de57e0efSRavi Pokala 	    "DIMM manufacturing week");
425de57e0efSRavi Pokala 
426dcd935dfSRavi Pokala 	/* Create the temperature sysctl IFF the TSOD is present and valid */
427dcd935dfSRavi Pokala 	if (tsod_present && (tsod_match != NULL)) {
428dcd935dfSRavi Pokala 		SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "temp",
429dcd935dfSRavi Pokala 		    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
430dcd935dfSRavi Pokala 		    jedec_dimm_temp_sysctl, "IK", "DIMM temperature (deg C)");
431dcd935dfSRavi Pokala 	}
432dcd935dfSRavi Pokala 
433dcd935dfSRavi Pokala 	/* If a "slotid" was hinted, add the sysctl for it. */
434dcd935dfSRavi Pokala 	if (resource_string_value(device_get_name(dev), device_get_unit(dev),
435dcd935dfSRavi Pokala 	    "slotid", &slotid_str) == 0) {
436dcd935dfSRavi Pokala 		if (slotid_str != NULL) {
4374754f6adSRavi Pokala 			sc->slotid_str = strdup(slotid_str, M_DEVBUF);
438dcd935dfSRavi Pokala 			SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "slotid",
439dcd935dfSRavi Pokala 			    CTLFLAG_RD | CTLFLAG_MPSAFE, sc->slotid_str, 0,
440dcd935dfSRavi Pokala 			    "DIMM Slot Identifier");
441dcd935dfSRavi Pokala 		}
442dcd935dfSRavi Pokala 	}
443dcd935dfSRavi Pokala 
444dcd935dfSRavi Pokala 	/* If a TSOD type string or a slotid are present, add them to the
445dcd935dfSRavi Pokala 	 * device description.
446dcd935dfSRavi Pokala 	 */
447dcd935dfSRavi Pokala 	if ((tsod_match != NULL) || (sc->slotid_str != NULL)) {
448*83caa244SMark Johnston 		device_set_descf(dev, "%s%s%s%s%s%s",
449dcd935dfSRavi Pokala 		    device_get_desc(dev),
450dcd935dfSRavi Pokala 		    (tsod_match ? " w/ " : ""),
451dcd935dfSRavi Pokala 		    (tsod_match ? tsod_match : ""),
452dcd935dfSRavi Pokala 		    (sc->slotid_str ? " (" : ""),
453dcd935dfSRavi Pokala 		    (sc->slotid_str ? sc->slotid_str : ""),
454dcd935dfSRavi Pokala 		    (sc->slotid_str ? ")" : ""));
455dcd935dfSRavi Pokala 	}
456dcd935dfSRavi Pokala 
457dcd935dfSRavi Pokala out:
458dcd935dfSRavi Pokala 	return (rc);
459dcd935dfSRavi Pokala }
460dcd935dfSRavi Pokala 
461dcd935dfSRavi Pokala /**
462dcd935dfSRavi Pokala  * Calculate the capacity of a DIMM. Both DDR3 and DDR4 encode "geometry"
463dcd935dfSRavi Pokala  * information in various SPD bytes. The standards documents codify everything
464dcd935dfSRavi Pokala  * in look-up tables, but it's trivial to reverse-engineer the the formulas for
465dcd935dfSRavi Pokala  * most of them. Unless otherwise noted, the same formulas apply for both DDR3
466dcd935dfSRavi Pokala  * and DDR4. The SPD offsets of where the data comes from are different between
467dcd935dfSRavi Pokala  * the two types, because having them be the same would be too easy.
468dcd935dfSRavi Pokala  *
469dcd935dfSRavi Pokala  * @author rpokala
470dcd935dfSRavi Pokala  *
471dcd935dfSRavi Pokala  * @param[in] sc
472dcd935dfSRavi Pokala  *      Instance-specific context data
473dcd935dfSRavi Pokala  *
474dcd935dfSRavi Pokala  * @param[in] dram_type
475dcd935dfSRavi Pokala  *      The locations of the data used to calculate the capacity depends on the
476dcd935dfSRavi Pokala  *      type of the DIMM.
477dcd935dfSRavi Pokala  *
478dcd935dfSRavi Pokala  * @param[out] capacity_mb
479dcd935dfSRavi Pokala  *      The calculated capacity, in MB
480dcd935dfSRavi Pokala  */
481dcd935dfSRavi Pokala static int
jedec_dimm_capacity(struct jedec_dimm_softc * sc,enum dram_type type,uint32_t * capacity_mb)482dcd935dfSRavi Pokala jedec_dimm_capacity(struct jedec_dimm_softc *sc, enum dram_type type,
483dcd935dfSRavi Pokala     uint32_t *capacity_mb)
484dcd935dfSRavi Pokala {
485dcd935dfSRavi Pokala 	uint8_t bus_width_byte;
486dcd935dfSRavi Pokala 	uint8_t bus_width_offset;
487dcd935dfSRavi Pokala 	uint8_t dimm_ranks_byte;
488dcd935dfSRavi Pokala 	uint8_t dimm_ranks_offset;
489dcd935dfSRavi Pokala 	uint8_t sdram_capacity_byte;
490dcd935dfSRavi Pokala 	uint8_t sdram_capacity_offset;
491dcd935dfSRavi Pokala 	uint8_t sdram_pkg_type_byte;
492dcd935dfSRavi Pokala 	uint8_t sdram_pkg_type_offset;
493dcd935dfSRavi Pokala 	uint8_t sdram_width_byte;
494dcd935dfSRavi Pokala 	uint8_t sdram_width_offset;
495dcd935dfSRavi Pokala 	uint32_t bus_width;
496dcd935dfSRavi Pokala 	uint32_t dimm_ranks;
497dcd935dfSRavi Pokala 	uint32_t sdram_capacity;
498dcd935dfSRavi Pokala 	uint32_t sdram_pkg_type;
499dcd935dfSRavi Pokala 	uint32_t sdram_width;
500dcd935dfSRavi Pokala 	int rc;
501dcd935dfSRavi Pokala 
502dcd935dfSRavi Pokala 	switch (type) {
503dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR3_SDRAM:
504dcd935dfSRavi Pokala 		bus_width_offset = SPD_OFFSET_DDR3_BUS_WIDTH;
505dcd935dfSRavi Pokala 		dimm_ranks_offset = SPD_OFFSET_DDR3_DIMM_RANKS;
506dcd935dfSRavi Pokala 		sdram_capacity_offset = SPD_OFFSET_DDR3_SDRAM_CAPACITY;
507dcd935dfSRavi Pokala 		sdram_width_offset = SPD_OFFSET_DDR3_SDRAM_WIDTH;
508dcd935dfSRavi Pokala 		break;
509dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR4_SDRAM:
510dcd935dfSRavi Pokala 		bus_width_offset = SPD_OFFSET_DDR4_BUS_WIDTH;
511dcd935dfSRavi Pokala 		dimm_ranks_offset = SPD_OFFSET_DDR4_DIMM_RANKS;
512dcd935dfSRavi Pokala 		sdram_capacity_offset = SPD_OFFSET_DDR4_SDRAM_CAPACITY;
513dcd935dfSRavi Pokala 		sdram_pkg_type_offset = SPD_OFFSET_DDR4_SDRAM_PKG_TYPE;
514dcd935dfSRavi Pokala 		sdram_width_offset = SPD_OFFSET_DDR4_SDRAM_WIDTH;
515dcd935dfSRavi Pokala 		break;
516dcd935dfSRavi Pokala 	default:
51715d69c84SRavi Pokala 		__assert_unreachable();
518dcd935dfSRavi Pokala 	}
519dcd935dfSRavi Pokala 
520dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, bus_width_offset,
521dcd935dfSRavi Pokala 	    &bus_width_byte);
522dcd935dfSRavi Pokala 	if (rc != 0) {
523dcd935dfSRavi Pokala 		device_printf(sc->dev, "failed to read bus_width: %d\n", rc);
524dcd935dfSRavi Pokala 		goto out;
525dcd935dfSRavi Pokala 	}
526dcd935dfSRavi Pokala 
527dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, dimm_ranks_offset,
528dcd935dfSRavi Pokala 	    &dimm_ranks_byte);
529dcd935dfSRavi Pokala 	if (rc != 0) {
530dcd935dfSRavi Pokala 		device_printf(sc->dev, "failed to read dimm_ranks: %d\n", rc);
531dcd935dfSRavi Pokala 		goto out;
532dcd935dfSRavi Pokala 	}
533dcd935dfSRavi Pokala 
534dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, sdram_capacity_offset,
535dcd935dfSRavi Pokala 	    &sdram_capacity_byte);
536dcd935dfSRavi Pokala 	if (rc != 0) {
537dcd935dfSRavi Pokala 		device_printf(sc->dev, "failed to read sdram_capacity: %d\n",
538dcd935dfSRavi Pokala 		    rc);
539dcd935dfSRavi Pokala 		goto out;
540dcd935dfSRavi Pokala 	}
541dcd935dfSRavi Pokala 
542dcd935dfSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, sdram_width_offset,
543dcd935dfSRavi Pokala 	    &sdram_width_byte);
544dcd935dfSRavi Pokala 	if (rc != 0) {
545dcd935dfSRavi Pokala 		device_printf(sc->dev, "failed to read sdram_width: %d\n", rc);
546dcd935dfSRavi Pokala 		goto out;
547dcd935dfSRavi Pokala 	}
548dcd935dfSRavi Pokala 
549dcd935dfSRavi Pokala 	/* The "SDRAM Package Type" is only needed for DDR4 DIMMs. */
550dcd935dfSRavi Pokala 	if (type == DRAM_TYPE_DDR4_SDRAM) {
551dcd935dfSRavi Pokala 		rc = smbus_readb(sc->smbus, sc->spd_addr, sdram_pkg_type_offset,
552dcd935dfSRavi Pokala 		    &sdram_pkg_type_byte);
553dcd935dfSRavi Pokala 		if (rc != 0) {
554dcd935dfSRavi Pokala 			device_printf(sc->dev,
555dcd935dfSRavi Pokala 			    "failed to read sdram_pkg_type: %d\n", rc);
556dcd935dfSRavi Pokala 			goto out;
557dcd935dfSRavi Pokala 		}
558dcd935dfSRavi Pokala 	}
559dcd935dfSRavi Pokala 
560dcd935dfSRavi Pokala 	/* "Primary bus width, in bits" is in bits [2:0]. */
561dcd935dfSRavi Pokala 	bus_width_byte &= 0x07;
562dcd935dfSRavi Pokala 	if (bus_width_byte <= 3) {
563dcd935dfSRavi Pokala 		bus_width = 1 << bus_width_byte;
564dcd935dfSRavi Pokala 		bus_width *= 8;
565dcd935dfSRavi Pokala 	} else {
566dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid bus width info\n");
567dcd935dfSRavi Pokala 		rc = EINVAL;
568dcd935dfSRavi Pokala 		goto out;
569dcd935dfSRavi Pokala 	}
570dcd935dfSRavi Pokala 
571dcd935dfSRavi Pokala 	/* "Number of ranks per DIMM" is in bits [5:3]. Values 4-7 are only
572dcd935dfSRavi Pokala 	 * valid for DDR4.
573dcd935dfSRavi Pokala 	 */
574dcd935dfSRavi Pokala 	dimm_ranks_byte >>= 3;
575dcd935dfSRavi Pokala 	dimm_ranks_byte &= 0x07;
576dcd935dfSRavi Pokala 	if (dimm_ranks_byte <= 7) {
577dcd935dfSRavi Pokala 		dimm_ranks = dimm_ranks_byte + 1;
578dcd935dfSRavi Pokala 	} else {
579dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid DIMM Rank info\n");
580dcd935dfSRavi Pokala 		rc = EINVAL;
581dcd935dfSRavi Pokala 		goto out;
582dcd935dfSRavi Pokala 	}
583dcd935dfSRavi Pokala 	if ((dimm_ranks_byte >= 4) && (type != DRAM_TYPE_DDR4_SDRAM)) {
584dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid DIMM Rank info\n");
585dcd935dfSRavi Pokala 		rc = EINVAL;
586dcd935dfSRavi Pokala 		goto out;
587dcd935dfSRavi Pokala 	}
588dcd935dfSRavi Pokala 
589dcd935dfSRavi Pokala 	/* "Total SDRAM capacity per die, in Mb" is in bits [3:0]. There are two
590dcd935dfSRavi Pokala 	 * different formulas, for values 0-7 and for values 8-9. Also, values
591dcd935dfSRavi Pokala 	 * 7-9 are only valid for DDR4.
592dcd935dfSRavi Pokala 	 */
593dcd935dfSRavi Pokala 	sdram_capacity_byte &= 0x0f;
594dcd935dfSRavi Pokala 	if (sdram_capacity_byte <= 7) {
595dcd935dfSRavi Pokala 		sdram_capacity = 1 << sdram_capacity_byte;
596dcd935dfSRavi Pokala 		sdram_capacity *= 256;
597dcd935dfSRavi Pokala 	} else if (sdram_capacity_byte <= 9) {
598dcd935dfSRavi Pokala 		sdram_capacity = 12 << (sdram_capacity_byte - 8);
599dcd935dfSRavi Pokala 		sdram_capacity *= 1024;
600dcd935dfSRavi Pokala 	} else {
601dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid SDRAM capacity info\n");
602dcd935dfSRavi Pokala 		rc = EINVAL;
603dcd935dfSRavi Pokala 		goto out;
604dcd935dfSRavi Pokala 	}
605dcd935dfSRavi Pokala 	if ((sdram_capacity_byte >= 7) && (type != DRAM_TYPE_DDR4_SDRAM)) {
606dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid SDRAM capacity info\n");
607dcd935dfSRavi Pokala 		rc = EINVAL;
608dcd935dfSRavi Pokala 		goto out;
609dcd935dfSRavi Pokala 	}
610dcd935dfSRavi Pokala 
611dcd935dfSRavi Pokala 	/* "SDRAM device width" is in bits [2:0]. */
612dcd935dfSRavi Pokala 	sdram_width_byte &= 0x7;
613dcd935dfSRavi Pokala 	if (sdram_width_byte <= 3) {
614dcd935dfSRavi Pokala 		sdram_width = 1 << sdram_width_byte;
615dcd935dfSRavi Pokala 		sdram_width *= 4;
616dcd935dfSRavi Pokala 	} else {
617dcd935dfSRavi Pokala 		device_printf(sc->dev, "invalid SDRAM width info\n");
618dcd935dfSRavi Pokala 		rc = EINVAL;
619dcd935dfSRavi Pokala 		goto out;
620dcd935dfSRavi Pokala 	}
621dcd935dfSRavi Pokala 
622dcd935dfSRavi Pokala 	/* DDR4 has something called "3DS", which is indicated by [1:0] = 2;
623dcd935dfSRavi Pokala 	 * when that is the case, the die count is encoded in [6:4], and
624dcd935dfSRavi Pokala 	 * dimm_ranks is multiplied by it.
625dcd935dfSRavi Pokala 	 */
626dcd935dfSRavi Pokala 	if ((type == DRAM_TYPE_DDR4_SDRAM) &&
627dcd935dfSRavi Pokala 	    ((sdram_pkg_type_byte & 0x3) == 2)) {
628dcd935dfSRavi Pokala 		sdram_pkg_type_byte >>= 4;
629dcd935dfSRavi Pokala 		sdram_pkg_type_byte &= 0x07;
630dcd935dfSRavi Pokala 		sdram_pkg_type = sdram_pkg_type_byte + 1;
631dcd935dfSRavi Pokala 		dimm_ranks *= sdram_pkg_type;
632dcd935dfSRavi Pokala 	}
633dcd935dfSRavi Pokala 
634dcd935dfSRavi Pokala 	/* Finally, assemble the actual capacity. The formula is the same for
635dcd935dfSRavi Pokala 	 * both DDR3 and DDR4.
636dcd935dfSRavi Pokala 	 */
637dcd935dfSRavi Pokala 	*capacity_mb = sdram_capacity / 8 * bus_width / sdram_width *
638dcd935dfSRavi Pokala 	    dimm_ranks;
639dcd935dfSRavi Pokala 
640dcd935dfSRavi Pokala out:
641dcd935dfSRavi Pokala 	return (rc);
642dcd935dfSRavi Pokala }
643dcd935dfSRavi Pokala 
644dcd935dfSRavi Pokala /**
645dcd935dfSRavi Pokala  * device_detach() method. If we allocated sc->slotid_str, free it. Even if we
646dcd935dfSRavi Pokala  *      didn't allocate, free it anyway; free(NULL) is safe.
647dcd935dfSRavi Pokala  *
648dcd935dfSRavi Pokala  * @author rpokala
649dcd935dfSRavi Pokala  *
650dcd935dfSRavi Pokala  * @param[in,out] dev
651dcd935dfSRavi Pokala  *      Device being detached.
652dcd935dfSRavi Pokala  */
653dcd935dfSRavi Pokala static int
jedec_dimm_detach(device_t dev)654dcd935dfSRavi Pokala jedec_dimm_detach(device_t dev)
655dcd935dfSRavi Pokala {
656dcd935dfSRavi Pokala 	struct jedec_dimm_softc *sc;
657dcd935dfSRavi Pokala 
658dcd935dfSRavi Pokala 	sc = device_get_softc(dev);
659dcd935dfSRavi Pokala 	free(sc->slotid_str, M_DEVBUF);
660dcd935dfSRavi Pokala 
661dcd935dfSRavi Pokala 	return (0);
662dcd935dfSRavi Pokala }
663dcd935dfSRavi Pokala 
664dcd935dfSRavi Pokala /**
665dcd935dfSRavi Pokala  * Read and dump the entire SPD contents.
666dcd935dfSRavi Pokala  *
667dcd935dfSRavi Pokala  * @author rpokala
668dcd935dfSRavi Pokala  *
669dcd935dfSRavi Pokala  * @param[in] sc
670dcd935dfSRavi Pokala  *      Instance-specific context data
671dcd935dfSRavi Pokala  *
672dcd935dfSRavi Pokala  * @param[in] dram_type
673dcd935dfSRavi Pokala  *      The length of data which needs to be read and dumped differs based on
674dcd935dfSRavi Pokala  *      the type of the DIMM.
675dcd935dfSRavi Pokala  */
676dcd935dfSRavi Pokala static int
jedec_dimm_dump(struct jedec_dimm_softc * sc,enum dram_type type)677dcd935dfSRavi Pokala jedec_dimm_dump(struct jedec_dimm_softc *sc, enum dram_type type)
678dcd935dfSRavi Pokala {
679dcd935dfSRavi Pokala 	int i;
680dcd935dfSRavi Pokala 	int rc;
681dcd935dfSRavi Pokala 	bool page_changed;
682dcd935dfSRavi Pokala 	uint8_t bytes[512];
683dcd935dfSRavi Pokala 
684dcd935dfSRavi Pokala 	page_changed = false;
685dcd935dfSRavi Pokala 
686dcd935dfSRavi Pokala 	for (i = 0; i < 256; i++) {
687dcd935dfSRavi Pokala 		rc = smbus_readb(sc->smbus, sc->spd_addr, i, &bytes[i]);
688dcd935dfSRavi Pokala 		if (rc != 0) {
689dcd935dfSRavi Pokala 			device_printf(sc->dev,
690dcd935dfSRavi Pokala 			    "unable to read page0:0x%02x: %d\n", i, rc);
691dcd935dfSRavi Pokala 			goto out;
692dcd935dfSRavi Pokala 		}
693dcd935dfSRavi Pokala 	}
694dcd935dfSRavi Pokala 
695dcd935dfSRavi Pokala 	/* The DDR4 SPD is 512 bytes, but SMBus only allows for 8-bit offsets.
696dcd935dfSRavi Pokala 	 * JEDEC gets around this by defining the "PAGE" DTI and LSAs.
697dcd935dfSRavi Pokala 	 */
698dcd935dfSRavi Pokala 	if (type == DRAM_TYPE_DDR4_SDRAM) {
699dcd935dfSRavi Pokala 		page_changed = true;
700dcd935dfSRavi Pokala 		rc = smbus_writeb(sc->smbus,
701dcd935dfSRavi Pokala 		    (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET1), 0, 0);
702dcd935dfSRavi Pokala 		if (rc != 0) {
703ca219866SRavi Pokala 			/* Some SPD devices (or SMBus controllers?) claim the
704ca219866SRavi Pokala 			 * page-change command failed when it actually
705ca219866SRavi Pokala 			 * succeeded. Log a message but soldier on.
706ca219866SRavi Pokala 			 */
707dcd935dfSRavi Pokala 			device_printf(sc->dev, "unable to change page: %d\n",
708dcd935dfSRavi Pokala 			    rc);
709dcd935dfSRavi Pokala 		}
710dcd935dfSRavi Pokala 		/* Add 256 to the store location, because we're in the second
711dcd935dfSRavi Pokala 		 * page.
712dcd935dfSRavi Pokala 		 */
713dcd935dfSRavi Pokala 		for (i = 0; i < 256; i++) {
714dcd935dfSRavi Pokala 			rc = smbus_readb(sc->smbus, sc->spd_addr, i,
715dcd935dfSRavi Pokala 			    &bytes[256 + i]);
716dcd935dfSRavi Pokala 			if (rc != 0) {
717dcd935dfSRavi Pokala 				device_printf(sc->dev,
718dcd935dfSRavi Pokala 				    "unable to read page1:0x%02x: %d\n", i, rc);
719dcd935dfSRavi Pokala 				goto out;
720dcd935dfSRavi Pokala 			}
721dcd935dfSRavi Pokala 		}
722dcd935dfSRavi Pokala 	}
723dcd935dfSRavi Pokala 
724dcd935dfSRavi Pokala 	/* Display the data in a nice hexdump format, with byte offsets. */
725dcd935dfSRavi Pokala 	hexdump(bytes, (page_changed ? 512 : 256), NULL, 0);
726dcd935dfSRavi Pokala 
727dcd935dfSRavi Pokala out:
728dcd935dfSRavi Pokala 	if (page_changed) {
729dcd935dfSRavi Pokala 		int rc2;
73015d69c84SRavi Pokala 
73115d69c84SRavi Pokala 		rc2 = jedec_dimm_reset_page0(sc);
73215d69c84SRavi Pokala 		if ((rc2 != 0) && (rc == 0)) {
73315d69c84SRavi Pokala 			rc = rc2;
734dcd935dfSRavi Pokala 		}
735dcd935dfSRavi Pokala 	}
73615d69c84SRavi Pokala 
737dcd935dfSRavi Pokala 	return (rc);
738dcd935dfSRavi Pokala }
739dcd935dfSRavi Pokala 
740dcd935dfSRavi Pokala /**
741dcd935dfSRavi Pokala  * Read a specified range of bytes from the SPD, convert them to a string, and
742dcd935dfSRavi Pokala  * store them in the provided buffer. Some SPD fields are space-padded ASCII,
743dcd935dfSRavi Pokala  * and some are just a string of bits that we want to convert to a hex string.
744dcd935dfSRavi Pokala  *
745dcd935dfSRavi Pokala  * @author rpokala
746dcd935dfSRavi Pokala  *
747dcd935dfSRavi Pokala  * @param[in] sc
748dcd935dfSRavi Pokala  *      Instance-specific context data
749dcd935dfSRavi Pokala  *
750dcd935dfSRavi Pokala  * @param[out] dst
751dcd935dfSRavi Pokala  *      The output buffer to populate
752dcd935dfSRavi Pokala  *
753dcd935dfSRavi Pokala  * @param[in] dstsz
754dcd935dfSRavi Pokala  *      The size of the output buffer
755dcd935dfSRavi Pokala  *
756dcd935dfSRavi Pokala  * @param[in] offset
757dcd935dfSRavi Pokala  *      The starting offset of the field within the SPD
758dcd935dfSRavi Pokala  *
759dcd935dfSRavi Pokala  * @param[in] len
760dcd935dfSRavi Pokala  *      The length in bytes of the field within the SPD
761dcd935dfSRavi Pokala  *
762dcd935dfSRavi Pokala  * @param[in] ascii
763dcd935dfSRavi Pokala  *      Is the field a sequence of ASCII characters? If not, it is binary data
764dcd935dfSRavi Pokala  *      which should be converted to characters.
765dcd935dfSRavi Pokala  */
766dcd935dfSRavi Pokala 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)767dcd935dfSRavi Pokala jedec_dimm_field_to_str(struct jedec_dimm_softc *sc, char *dst, size_t dstsz,
768dcd935dfSRavi Pokala     uint16_t offset, uint16_t len, bool ascii)
769dcd935dfSRavi Pokala {
770dcd935dfSRavi Pokala 	uint8_t byte;
77115d69c84SRavi Pokala 	uint16_t new_offset;
772dcd935dfSRavi Pokala 	int i;
773dcd935dfSRavi Pokala 	int rc;
774dcd935dfSRavi Pokala 	bool page_changed;
775dcd935dfSRavi Pokala 
77615d69c84SRavi Pokala 	rc = jedec_dimm_adjust_offset(sc, offset, &new_offset, &page_changed);
777dcd935dfSRavi Pokala 	if (rc != 0) {
778dcd935dfSRavi Pokala 		goto out;
779dcd935dfSRavi Pokala 	}
780dcd935dfSRavi Pokala 
781dcd935dfSRavi Pokala 	/* Sanity-check (adjusted) offset and length; everything must be within
782dcd935dfSRavi Pokala 	 * the same page.
783dcd935dfSRavi Pokala 	 */
78415d69c84SRavi Pokala 	if (new_offset >= JEDEC_SPD_PAGE_SIZE) {
785dcd935dfSRavi Pokala 		rc = EINVAL;
78615d69c84SRavi Pokala 		device_printf(sc->dev, "invalid offset 0x%04x\n", new_offset);
787dcd935dfSRavi Pokala 		goto out;
788dcd935dfSRavi Pokala 	}
78915d69c84SRavi Pokala 	if ((new_offset + len) >= JEDEC_SPD_PAGE_SIZE) {
790dcd935dfSRavi Pokala 		rc = EINVAL;
791dcd935dfSRavi Pokala 		device_printf(sc->dev,
79215d69c84SRavi Pokala 		    "(new_offset + len) would cross page (0x%04x + 0x%04x)\n",
79315d69c84SRavi Pokala 		    new_offset, len);
794dcd935dfSRavi Pokala 		goto out;
795dcd935dfSRavi Pokala 	}
796dcd935dfSRavi Pokala 
797dcd935dfSRavi Pokala 	/* Sanity-check the destination string length. If we're dealing with
798dcd935dfSRavi Pokala 	 * ASCII chars, then the destination must be at least the same length;
799dcd935dfSRavi Pokala 	 * otherwise, it must be *twice* the length, because each byte must
800dcd935dfSRavi Pokala 	 * be converted into two nybble characters.
801dcd935dfSRavi Pokala 	 *
802dcd935dfSRavi Pokala 	 * And, of course, there needs to be an extra byte for the terminator.
803dcd935dfSRavi Pokala 	 */
804dcd935dfSRavi Pokala 	if (ascii) {
805dcd935dfSRavi Pokala 		if (dstsz < (len + 1)) {
806dcd935dfSRavi Pokala 			rc = EINVAL;
807dcd935dfSRavi Pokala 			device_printf(sc->dev,
808dcd935dfSRavi Pokala 			    "destination too short (%u < %u)\n",
809dcd935dfSRavi Pokala 			    (uint16_t) dstsz, (len + 1));
810dcd935dfSRavi Pokala 			goto out;
811dcd935dfSRavi Pokala 		}
812dcd935dfSRavi Pokala 	} else {
813dcd935dfSRavi Pokala 		if (dstsz < ((2 * len) + 1)) {
814dcd935dfSRavi Pokala 			rc = EINVAL;
815dcd935dfSRavi Pokala 			device_printf(sc->dev,
816dcd935dfSRavi Pokala 			    "destination too short (%u < %u)\n",
817dcd935dfSRavi Pokala 			    (uint16_t) dstsz, ((2 * len) + 1));
818dcd935dfSRavi Pokala 			goto out;
819dcd935dfSRavi Pokala 		}
820dcd935dfSRavi Pokala 	}
821dcd935dfSRavi Pokala 
822dcd935dfSRavi Pokala 	/* Read a byte at a time. */
823dcd935dfSRavi Pokala 	for (i = 0; i < len; i++) {
82415d69c84SRavi Pokala 		rc = smbus_readb(sc->smbus, sc->spd_addr, (new_offset + i),
82515d69c84SRavi Pokala 		    &byte);
826dcd935dfSRavi Pokala 		if (rc != 0) {
827dcd935dfSRavi Pokala 			device_printf(sc->dev,
828dcd935dfSRavi Pokala 			    "failed to read byte at 0x%02x: %d\n",
82915d69c84SRavi Pokala 			    (new_offset + i), rc);
830dcd935dfSRavi Pokala 			goto out;
831dcd935dfSRavi Pokala 		}
832dcd935dfSRavi Pokala 		if (ascii) {
833dcd935dfSRavi Pokala 			/* chars can be copied directly. */
834dcd935dfSRavi Pokala 			dst[i] = byte;
835dcd935dfSRavi Pokala 		} else {
836dcd935dfSRavi Pokala 			/* Raw bytes need to be converted to a two-byte hex
837dcd935dfSRavi Pokala 			 * string, plus the terminator.
838dcd935dfSRavi Pokala 			 */
839dcd935dfSRavi Pokala 			(void) snprintf(&dst[(2 * i)], 3, "%02x", byte);
840dcd935dfSRavi Pokala 		}
841dcd935dfSRavi Pokala 	}
842dcd935dfSRavi Pokala 
843dcd935dfSRavi Pokala 	/* If we're dealing with ASCII, convert trailing spaces to NULs. */
844dcd935dfSRavi Pokala 	if (ascii) {
845641b7baaSEric van Gyzen 		for (i = dstsz - 1; i > 0; i--) {
846dcd935dfSRavi Pokala 			if (dst[i] == ' ') {
847dcd935dfSRavi Pokala 				dst[i] = 0;
848dcd935dfSRavi Pokala 			} else if (dst[i] == 0) {
849dcd935dfSRavi Pokala 				continue;
850dcd935dfSRavi Pokala 			} else {
851dcd935dfSRavi Pokala 				break;
852dcd935dfSRavi Pokala 			}
853dcd935dfSRavi Pokala 		}
854dcd935dfSRavi Pokala 	}
855dcd935dfSRavi Pokala 
856dcd935dfSRavi Pokala out:
857dcd935dfSRavi Pokala 	if (page_changed) {
858dcd935dfSRavi Pokala 		int rc2;
85915d69c84SRavi Pokala 
86015d69c84SRavi Pokala 		rc2 = jedec_dimm_reset_page0(sc);
86115d69c84SRavi Pokala 		if ((rc2 != 0) && (rc == 0)) {
86215d69c84SRavi Pokala 			rc = rc2;
863dcd935dfSRavi Pokala 		}
864dcd935dfSRavi Pokala 	}
865dcd935dfSRavi Pokala 
866dcd935dfSRavi Pokala 	return (rc);
867dcd935dfSRavi Pokala }
868dcd935dfSRavi Pokala 
869dcd935dfSRavi Pokala /**
870de57e0efSRavi Pokala  * Both DDR3 and DDR4 encode manufacturing date as a one-byte BCD-encoded
871de57e0efSRavi Pokala  * year (offset from 2000) and a one-byte BCD-encoded week within that year.
872de57e0efSRavi Pokala  * The SPD offsets are different between the two types.
873de57e0efSRavi Pokala  *
874de57e0efSRavi Pokala  * @author rpokala
875de57e0efSRavi Pokala  *
876de57e0efSRavi Pokala  * @param[in] sc
877de57e0efSRavi Pokala  *      Instance-specific context data
878de57e0efSRavi Pokala  *
879de57e0efSRavi Pokala  * @param[in] dram_type
880de57e0efSRavi Pokala  *      The locations of the manufacturing date depends on the type of the DIMM.
881de57e0efSRavi Pokala  *
882de57e0efSRavi Pokala  * @param[out] year
883de57e0efSRavi Pokala  *      The manufacturing year, offset from 2000
884de57e0efSRavi Pokala  *
885de57e0efSRavi Pokala  * @param[out] week
886de57e0efSRavi Pokala  *      The manufacturing week within the year
887de57e0efSRavi Pokala  */
888de57e0efSRavi Pokala static int
jedec_dimm_mfg_date(struct jedec_dimm_softc * sc,enum dram_type type,uint8_t * year,uint8_t * week)889de57e0efSRavi Pokala jedec_dimm_mfg_date(struct jedec_dimm_softc *sc, enum dram_type type,
890de57e0efSRavi Pokala     uint8_t *year, uint8_t *week)
891de57e0efSRavi Pokala {
892de57e0efSRavi Pokala 	uint8_t year_bcd;
893de57e0efSRavi Pokala 	uint8_t week_bcd;
894de57e0efSRavi Pokala 	uint16_t year_offset;
895de57e0efSRavi Pokala 	uint16_t week_offset;
896de57e0efSRavi Pokala 	bool page_changed;
897de57e0efSRavi Pokala 	int rc;
898de57e0efSRavi Pokala 
899de57e0efSRavi Pokala 	switch (type) {
900de57e0efSRavi Pokala 	case DRAM_TYPE_DDR3_SDRAM:
901de57e0efSRavi Pokala 		year_offset = SPD_OFFSET_DDR3_MOD_MFG_YEAR;
902de57e0efSRavi Pokala 		week_offset = SPD_OFFSET_DDR3_MOD_MFG_WEEK;
903de57e0efSRavi Pokala 		break;
904de57e0efSRavi Pokala 	case DRAM_TYPE_DDR4_SDRAM:
905de57e0efSRavi Pokala 		year_offset = SPD_OFFSET_DDR4_MOD_MFG_YEAR;
906de57e0efSRavi Pokala 		week_offset = SPD_OFFSET_DDR4_MOD_MFG_WEEK;
907de57e0efSRavi Pokala 		break;
908de57e0efSRavi Pokala 	default:
90915d69c84SRavi Pokala 		__assert_unreachable();
910de57e0efSRavi Pokala 	}
911de57e0efSRavi Pokala 
912de57e0efSRavi Pokala 	/* Change to the proper page. Offsets [0, 255] are in page0; offsets
913de57e0efSRavi Pokala 	 * [256, 512] are in page1.
914de57e0efSRavi Pokala 	 *
915de57e0efSRavi Pokala 	 * *The page must be reset to page0 before returning.*
916de57e0efSRavi Pokala 	 *
917de57e0efSRavi Pokala 	 * For the page-change operation, only the DTI and LSA matter; the
918de57e0efSRavi Pokala 	 * offset and write-value are ignored, so use just 0.
919de57e0efSRavi Pokala 	 *
920de57e0efSRavi Pokala 	 * Mercifully, JEDEC defined the fields such that all of the
921de57e0efSRavi Pokala 	 * manufacturing-related ones are on the same page, so we don't need to
922de57e0efSRavi Pokala 	 * worry about that complication.
923de57e0efSRavi Pokala 	 */
924de57e0efSRavi Pokala 	if (year_offset < JEDEC_SPD_PAGE_SIZE) {
925de57e0efSRavi Pokala 		page_changed = false;
926de57e0efSRavi Pokala 	} else if (year_offset < (2 * JEDEC_SPD_PAGE_SIZE)) {
927de57e0efSRavi Pokala 		page_changed = true;
928de57e0efSRavi Pokala 		rc = smbus_writeb(sc->smbus,
929de57e0efSRavi Pokala 		    (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET1), 0, 0);
930de57e0efSRavi Pokala 		if (rc != 0) {
931de57e0efSRavi Pokala 			device_printf(sc->dev,
932de57e0efSRavi Pokala 			    "unable to change page for offset 0x%04x: %d\n",
933de57e0efSRavi Pokala 			    year_offset, rc);
934de57e0efSRavi Pokala 		}
935de57e0efSRavi Pokala 		/* Adjust the offset to account for the page change. */
936de57e0efSRavi Pokala 		year_offset -= JEDEC_SPD_PAGE_SIZE;
937de57e0efSRavi Pokala 		week_offset -= JEDEC_SPD_PAGE_SIZE;
938de57e0efSRavi Pokala 	} else {
939de57e0efSRavi Pokala 		device_printf(sc->dev, "invalid offset 0x%04x\n", year_offset);
940de57e0efSRavi Pokala 		rc = EINVAL;
941de57e0efSRavi Pokala 		page_changed = false;
942de57e0efSRavi Pokala 		goto out;
943de57e0efSRavi Pokala 	}
944de57e0efSRavi Pokala 
945de57e0efSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, year_offset, &year_bcd);
946de57e0efSRavi Pokala 	if (rc != 0) {
947de57e0efSRavi Pokala 		device_printf(sc->dev, "failed to read mfg year: %d\n", rc);
948de57e0efSRavi Pokala 		goto out;
949de57e0efSRavi Pokala 	}
950de57e0efSRavi Pokala 
951de57e0efSRavi Pokala 	rc = smbus_readb(sc->smbus, sc->spd_addr, week_offset, &week_bcd);
952de57e0efSRavi Pokala 	if (rc != 0) {
953de57e0efSRavi Pokala 		device_printf(sc->dev, "failed to read mfg week: %d\n", rc);
954de57e0efSRavi Pokala 		goto out;
955de57e0efSRavi Pokala 	}
956de57e0efSRavi Pokala 
957de57e0efSRavi Pokala 	/* Convert from one-byte BCD to one-byte integer. */
958de57e0efSRavi Pokala 	*year = (((year_bcd & 0xf0) >> 4) * 10) + (year_bcd & 0x0f);
959de57e0efSRavi Pokala 	*week = (((week_bcd & 0xf0) >> 4) * 10) + (week_bcd & 0x0f);
960de57e0efSRavi Pokala 
961de57e0efSRavi Pokala out:
962de57e0efSRavi Pokala 	if (page_changed) {
963de57e0efSRavi Pokala 		int rc2;
964de57e0efSRavi Pokala 		/* Switch back to page0 before returning. */
965de57e0efSRavi Pokala 		rc2 = smbus_writeb(sc->smbus,
966de57e0efSRavi Pokala 		    (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET0), 0, 0);
967de57e0efSRavi Pokala 		if (rc2 != 0) {
968de57e0efSRavi Pokala 			device_printf(sc->dev,
969de57e0efSRavi Pokala 			    "unable to restore page for offset 0x%04x: %d\n",
970de57e0efSRavi Pokala 			    year_offset, rc2);
971de57e0efSRavi Pokala 		}
972de57e0efSRavi Pokala 	}
973de57e0efSRavi Pokala 
974de57e0efSRavi Pokala 	return (rc);
975de57e0efSRavi Pokala }
976de57e0efSRavi Pokala 
977de57e0efSRavi Pokala /**
978dcd935dfSRavi Pokala  * device_probe() method. Validate the address that was given as a hint, and
979dcd935dfSRavi Pokala  * display an error if it's bogus. Make sure that we're dealing with one of the
980dcd935dfSRavi Pokala  * SPD versions that we can handle.
981dcd935dfSRavi Pokala  *
982dcd935dfSRavi Pokala  * @author rpokala
983dcd935dfSRavi Pokala  *
984dcd935dfSRavi Pokala  * @param[in] dev
985dcd935dfSRavi Pokala  *      Device being probed.
986dcd935dfSRavi Pokala  */
987dcd935dfSRavi Pokala static int
jedec_dimm_probe(device_t dev)988dcd935dfSRavi Pokala jedec_dimm_probe(device_t dev)
989dcd935dfSRavi Pokala {
990dcd935dfSRavi Pokala 	uint8_t addr;
991dcd935dfSRavi Pokala 	uint8_t byte;
992dcd935dfSRavi Pokala 	int rc;
993dcd935dfSRavi Pokala 	enum dram_type type;
994dcd935dfSRavi Pokala 	device_t smbus;
995dcd935dfSRavi Pokala 
996dcd935dfSRavi Pokala 	smbus = device_get_parent(dev);
997dcd935dfSRavi Pokala 	addr = smbus_get_addr(dev);
998dcd935dfSRavi Pokala 
999dcd935dfSRavi Pokala 	/* Don't bother if this isn't an SPD address, or if the LSBit is set. */
1000dcd935dfSRavi Pokala 	if (((addr & 0xf0) != JEDEC_DTI_SPD) ||
1001dcd935dfSRavi Pokala 	    ((addr & 0x01) != 0)) {
1002dcd935dfSRavi Pokala 		device_printf(dev,
1003dcd935dfSRavi Pokala 		    "invalid \"addr\" hint; address must start with \"0x%x\","
1004dcd935dfSRavi Pokala 		    " and the least-significant bit must be 0\n",
1005dcd935dfSRavi Pokala 		    JEDEC_DTI_SPD);
1006dcd935dfSRavi Pokala 		rc = ENXIO;
1007dcd935dfSRavi Pokala 		goto out;
1008dcd935dfSRavi Pokala 	}
1009dcd935dfSRavi Pokala 
1010dcd935dfSRavi Pokala 	/* Try to read the DRAM_TYPE from the SPD. */
1011dcd935dfSRavi Pokala 	rc = smbus_readb(smbus, addr, SPD_OFFSET_DRAM_TYPE, &byte);
1012dcd935dfSRavi Pokala 	if (rc != 0) {
1013dcd935dfSRavi Pokala 		device_printf(dev, "failed to read dram_type\n");
1014dcd935dfSRavi Pokala 		goto out;
1015dcd935dfSRavi Pokala 	}
1016dcd935dfSRavi Pokala 
1017dcd935dfSRavi Pokala 	/* This driver currently only supports DDR3 and DDR4 SPDs. */
1018dcd935dfSRavi Pokala 	type = (enum dram_type) byte;
1019dcd935dfSRavi Pokala 	switch (type) {
1020dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR3_SDRAM:
1021dcd935dfSRavi Pokala 		rc = BUS_PROBE_DEFAULT;
1022dcd935dfSRavi Pokala 		device_set_desc(dev, "DDR3 DIMM");
1023dcd935dfSRavi Pokala 		break;
1024dcd935dfSRavi Pokala 	case DRAM_TYPE_DDR4_SDRAM:
1025dcd935dfSRavi Pokala 		rc = BUS_PROBE_DEFAULT;
1026dcd935dfSRavi Pokala 		device_set_desc(dev, "DDR4 DIMM");
1027dcd935dfSRavi Pokala 		break;
1028dcd935dfSRavi Pokala 	default:
1029dcd935dfSRavi Pokala 		rc = ENXIO;
1030dcd935dfSRavi Pokala 		break;
1031dcd935dfSRavi Pokala 	}
1032dcd935dfSRavi Pokala 
1033dcd935dfSRavi Pokala out:
1034dcd935dfSRavi Pokala 	return (rc);
1035dcd935dfSRavi Pokala }
1036dcd935dfSRavi Pokala 
1037dcd935dfSRavi Pokala /**
1038dcd935dfSRavi Pokala  * SMBus specifies little-endian byte order, but it looks like the TSODs use
1039dcd935dfSRavi Pokala  * big-endian. Read and convert.
1040dcd935dfSRavi Pokala  *
1041dcd935dfSRavi Pokala  * @author avg
1042dcd935dfSRavi Pokala  *
1043dcd935dfSRavi Pokala  * @param[in] sc
1044dcd935dfSRavi Pokala  *      Instance-specific context data
1045dcd935dfSRavi Pokala  *
1046dcd935dfSRavi Pokala  * @param[in] reg
1047dcd935dfSRavi Pokala  *      The register number to read.
1048dcd935dfSRavi Pokala  *
1049dcd935dfSRavi Pokala  * @param[out] val
1050dcd935dfSRavi Pokala  *      Pointer to populate with the value read.
1051dcd935dfSRavi Pokala  */
1052dcd935dfSRavi Pokala static int
jedec_dimm_readw_be(struct jedec_dimm_softc * sc,uint8_t reg,uint16_t * val)1053dcd935dfSRavi Pokala jedec_dimm_readw_be(struct jedec_dimm_softc *sc, uint8_t reg, uint16_t *val)
1054dcd935dfSRavi Pokala {
1055dcd935dfSRavi Pokala 	int rc;
1056dcd935dfSRavi Pokala 
1057dcd935dfSRavi Pokala 	rc = smbus_readw(sc->smbus, sc->tsod_addr, reg, val);
1058dcd935dfSRavi Pokala 	if (rc != 0) {
1059dcd935dfSRavi Pokala 		goto out;
1060dcd935dfSRavi Pokala 	}
1061dcd935dfSRavi Pokala 	*val = be16toh(*val);
1062dcd935dfSRavi Pokala 
1063dcd935dfSRavi Pokala out:
1064dcd935dfSRavi Pokala 	return (rc);
1065dcd935dfSRavi Pokala }
1066dcd935dfSRavi Pokala 
1067dcd935dfSRavi Pokala /**
106815d69c84SRavi Pokala  * Reset to the default condition of operating on page0. This must be called
106915d69c84SRavi Pokala  * after a previous operation changed to page1.
107015d69c84SRavi Pokala  *
107115d69c84SRavi Pokala  * @author rpokala
107215d69c84SRavi Pokala  *
107315d69c84SRavi Pokala  * @param[in] sc
107415d69c84SRavi Pokala  *      Instance-specific context data
107515d69c84SRavi Pokala  */
107615d69c84SRavi Pokala static int
jedec_dimm_reset_page0(struct jedec_dimm_softc * sc)107715d69c84SRavi Pokala jedec_dimm_reset_page0(struct jedec_dimm_softc *sc)
107815d69c84SRavi Pokala {
107915d69c84SRavi Pokala 	int rc;
108015d69c84SRavi Pokala 
108115d69c84SRavi Pokala 	/* For the page-change operation, only the DTI and LSA matter; the
108215d69c84SRavi Pokala 	 * offset and write-value are ignored, so just use 0.
108315d69c84SRavi Pokala 	 */
108415d69c84SRavi Pokala 	rc = smbus_writeb(sc->smbus, (JEDEC_DTI_PAGE | JEDEC_LSA_PAGE_SET0),
108515d69c84SRavi Pokala 	    0, 0);
108615d69c84SRavi Pokala 	if (rc != 0) {
108715d69c84SRavi Pokala 		device_printf(sc->dev, "unable to restore page: %d\n", rc);
108815d69c84SRavi Pokala 	}
108915d69c84SRavi Pokala 
109015d69c84SRavi Pokala 	return (rc);
109115d69c84SRavi Pokala }
109215d69c84SRavi Pokala 
109315d69c84SRavi Pokala /**
1094dcd935dfSRavi Pokala  * Read the temperature data from the TSOD and convert it to the deciKelvin
1095dcd935dfSRavi Pokala  * value that the sysctl expects.
1096dcd935dfSRavi Pokala  *
1097dcd935dfSRavi Pokala  * @author avg
1098dcd935dfSRavi Pokala  */
1099dcd935dfSRavi Pokala static int
jedec_dimm_temp_sysctl(SYSCTL_HANDLER_ARGS)1100dcd935dfSRavi Pokala jedec_dimm_temp_sysctl(SYSCTL_HANDLER_ARGS)
1101dcd935dfSRavi Pokala {
1102dcd935dfSRavi Pokala 	uint16_t val;
1103dcd935dfSRavi Pokala 	int rc;
1104dcd935dfSRavi Pokala 	int temp;
1105dcd935dfSRavi Pokala 	device_t dev = arg1;
1106dcd935dfSRavi Pokala 	struct jedec_dimm_softc *sc;
1107dcd935dfSRavi Pokala 
1108dcd935dfSRavi Pokala 	sc = device_get_softc(dev);
1109dcd935dfSRavi Pokala 
1110dcd935dfSRavi Pokala 	rc = jedec_dimm_readw_be(sc, TSOD_REG_TEMPERATURE, &val);
1111dcd935dfSRavi Pokala 	if (rc != 0) {
1112dcd935dfSRavi Pokala 		goto out;
1113dcd935dfSRavi Pokala 	}
1114dcd935dfSRavi Pokala 
1115dcd935dfSRavi Pokala 	/* The three MSBits are flags, and the next bit is a sign bit. */
1116dcd935dfSRavi Pokala 	temp = val & 0xfff;
1117dcd935dfSRavi Pokala 	if ((val & 0x1000) != 0)
1118dcd935dfSRavi Pokala 		temp = -temp;
1119dcd935dfSRavi Pokala 	/* Each step is 0.0625 degrees, so convert to 1000ths of a degree C. */
1120dcd935dfSRavi Pokala 	temp *= 625;
1121dcd935dfSRavi Pokala 	/* ... and then convert to 1000ths of a Kelvin */
1122dcd935dfSRavi Pokala 	temp += 2731500;
1123dcd935dfSRavi Pokala 	/* As a practical matter, few (if any) TSODs are more accurate than
1124dcd935dfSRavi Pokala 	 * about a tenth of a degree, so round accordingly. This correlates with
1125dcd935dfSRavi Pokala 	 * the "IK" formatting used for this sysctl.
1126dcd935dfSRavi Pokala 	 */
1127dcd935dfSRavi Pokala 	temp = (temp + 500) / 1000;
1128dcd935dfSRavi Pokala 
1129dcd935dfSRavi Pokala 	rc = sysctl_handle_int(oidp, &temp, 0, req);
1130dcd935dfSRavi Pokala 
1131dcd935dfSRavi Pokala out:
1132dcd935dfSRavi Pokala 	return (rc);
1133dcd935dfSRavi Pokala }
1134dcd935dfSRavi Pokala 
1135dcd935dfSRavi Pokala /**
1136dcd935dfSRavi Pokala  * Check the TSOD's Vendor ID and Device ID against the list of known TSOD
1137dcd935dfSRavi Pokala  * devices. Return the description, or NULL if this doesn't look like a valid
1138dcd935dfSRavi Pokala  * TSOD.
1139dcd935dfSRavi Pokala  *
1140dcd935dfSRavi Pokala  * @author avg
1141dcd935dfSRavi Pokala  *
1142dcd935dfSRavi Pokala  * @param[in] vid
1143dcd935dfSRavi Pokala  *      The Vendor ID of the TSOD device
1144dcd935dfSRavi Pokala  *
1145dcd935dfSRavi Pokala  * @param[in] did
1146dcd935dfSRavi Pokala  *      The Device ID of the TSOD device
1147dcd935dfSRavi Pokala  *
1148dcd935dfSRavi Pokala  * @return
1149dcd935dfSRavi Pokala  *      The description string, or NULL for a failure to match.
1150dcd935dfSRavi Pokala  */
1151dcd935dfSRavi Pokala static const char *
jedec_dimm_tsod_match(uint16_t vid,uint16_t did)1152dcd935dfSRavi Pokala jedec_dimm_tsod_match(uint16_t vid, uint16_t did)
1153dcd935dfSRavi Pokala {
1154dcd935dfSRavi Pokala 	const struct jedec_dimm_tsod_dev *d;
1155dcd935dfSRavi Pokala 	int i;
1156dcd935dfSRavi Pokala 
1157dcd935dfSRavi Pokala 	for (i = 0; i < nitems(known_tsod_devices); i++) {
1158dcd935dfSRavi Pokala 		d = &known_tsod_devices[i];
1159dcd935dfSRavi Pokala 		if ((vid == d->vendor_id) && ((did >> 8) == d->device_id)) {
1160dcd935dfSRavi Pokala 			return (d->description);
1161dcd935dfSRavi Pokala 		}
1162dcd935dfSRavi Pokala 	}
1163dcd935dfSRavi Pokala 
1164dcd935dfSRavi Pokala 	/* If no matches for a specific device, then check for a generic
1165dcd935dfSRavi Pokala 	 * TSE2004av-compliant device.
1166dcd935dfSRavi Pokala 	 */
1167dcd935dfSRavi Pokala 	if ((did >> 8) == 0x22) {
1168dcd935dfSRavi Pokala 		return ("TSE2004av compliant TSOD");
1169dcd935dfSRavi Pokala 	}
1170dcd935dfSRavi Pokala 
1171dcd935dfSRavi Pokala 	return (NULL);
1172dcd935dfSRavi Pokala }
1173dcd935dfSRavi Pokala 
1174dcd935dfSRavi Pokala static device_method_t jedec_dimm_methods[] = {
1175dcd935dfSRavi Pokala 	/* Methods from the device interface */
1176dcd935dfSRavi Pokala 	DEVMETHOD(device_probe,		jedec_dimm_probe),
1177dcd935dfSRavi Pokala 	DEVMETHOD(device_attach,	jedec_dimm_attach),
1178dcd935dfSRavi Pokala 	DEVMETHOD(device_detach,	jedec_dimm_detach),
1179dcd935dfSRavi Pokala 	DEVMETHOD_END
1180dcd935dfSRavi Pokala };
1181dcd935dfSRavi Pokala 
1182dcd935dfSRavi Pokala static driver_t jedec_dimm_driver = {
1183dcd935dfSRavi Pokala 	.name = "jedec_dimm",
1184dcd935dfSRavi Pokala 	.methods = jedec_dimm_methods,
1185dcd935dfSRavi Pokala 	.size = sizeof(struct jedec_dimm_softc),
1186dcd935dfSRavi Pokala };
1187dcd935dfSRavi Pokala 
11881f531265SJohn Baldwin DRIVER_MODULE(jedec_dimm, smbus, jedec_dimm_driver, 0, 0);
1189dcd935dfSRavi Pokala MODULE_DEPEND(jedec_dimm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
1190dcd935dfSRavi Pokala MODULE_VERSION(jedec_dimm, 1);
1191dcd935dfSRavi Pokala 
1192dcd935dfSRavi Pokala /* vi: set ts=8 sw=4 sts=8 noet: */
1193