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