xref: /illumos-gate/usr/src/lib/libjedec/common/libjedec_spd_ddr5.c (revision 201ceb75ab95f9bf1f42ea1dc9ab363b43ba47cf)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2023 Oxide Computer Company
14  */
15 
16 /*
17  * DDR5-specific SPD processing logic. For an overview of the processing design
18  * please see libjedec_spd.c. Note, this currently does not handle NVDIMMs.
19  */
20 
21 #include <sys/sysmacros.h>
22 #include <sys/debug.h>
23 #include "libjedec_spd.h"
24 
25 static const spd_value_map_t spd_ddr5_nbytes_total_map[] = {
26 	{ SPD_DDR5_NBYTES_TOTAL_UNDEF, 0, true },
27 	{ SPD_DDR5_NBYTES_TOTAL_256, 256, false },
28 	{ SPD_DDR5_NBYTES_TOTAL_512, 512, false },
29 	{ SPD_DDR5_NBYTES_TOTAL_1024, 1024, false },
30 	{ SPD_DDR5_NBYTES_TOTAL_2048, 2048, false }
31 };
32 
33 static void
34 spd_parse_ddr5_nbytes(spd_info_t *si, uint32_t off, uint32_t len,
35     const char *key)
36 {
37 	const uint8_t data = si->si_data[off];
38 	const uint8_t total = SPD_DDR5_NBYTES_TOTAL(data);
39 	uint8_t beta = SPD_DDR5_NBYTES_BETA(data);
40 	beta = bitset8(beta, 4, 4, SPD_DDR5_NBYTES_BETAHI(data));
41 
42 	spd_nvl_insert_u32(si, SPD_KEY_BETA, beta);
43 	spd_insert_map(si, SPD_KEY_NBYTES_TOTAL, total,
44 	    spd_ddr5_nbytes_total_map, ARRAY_SIZE(spd_ddr5_nbytes_total_map));
45 }
46 
47 static const spd_value_map_t spd_ddr5_mod_type_map[] = {
48 	{ SPD_DDR5_MOD_TYPE_TYPE_RDIMM, SPD_MOD_TYPE_RDIMM, false },
49 	{ SPD_DDR5_MOD_TYPE_TYPE_UDIMM, SPD_MOD_TYPE_UDIMM, false },
50 	{ SPD_DDR5_MOD_TYPE_TYPE_SODIMM, SPD_MOD_TYPE_SODIMM, false },
51 	{ SPD_DDR5_MOD_TYPE_TYPE_LRDIMM, SPD_MOD_TYPE_LRDIMM, false },
52 	{ SPD_DDR5_MOD_TYPE_TYPE_MRDIMM, SPD_MOD_TYPE_MRDIMM, false },
53 	{ SPD_DDR5_MOD_TYPE_TYPE_DDIMM, SPD_MOD_TYPE_DDIMM, false },
54 	{ SPD_DDR5_MOD_TYPE_TYPE_SOLDER, SPD_MOD_TYPE_SOLDER, false }
55 };
56 
57 static const spd_value_map_t spd_ddr5_mod_is_hybrid_map[] = {
58 	{ 0, SPD_MOD_NOT_HYBRID, false },
59 	{ 1, SPD_MOD_HYBRID_NVDIMMM, false }
60 };
61 
62 static const spd_value_map_t spd_ddr5_mod_hybrid_map[] = {
63 	{ SPD_DDR5_MOD_TYPE_HYBRID_NVDIMM_N, SPD_MOD_TYPE_NVDIMM_N, false },
64 	{ SPD_DDR5_MOD_TYPE_HYBRID_NVDIMM_P, SPD_MOD_TYPE_NVDIMM_P, false }
65 };
66 
67 static void
68 spd_parse_ddr5_mod_type(spd_info_t *si, uint32_t off, uint32_t len,
69     const char *key)
70 {
71 	const uint8_t data = si->si_data[off];
72 	const uint8_t type = SPD_DDR5_MOD_TYPE_TYPE(data);
73 	const uint8_t is_hyb = SPD_DDR5_MOD_TYPE_ISHYBRID(data);
74 	const uint8_t hybrid = SPD_DDR5_MOD_TYPE_HYBRID(data);
75 
76 	spd_insert_map(si, SPD_KEY_MOD_HYBRID_TYPE, is_hyb,
77 	    spd_ddr5_mod_is_hybrid_map, ARRAY_SIZE(spd_ddr5_mod_is_hybrid_map));
78 
79 	if (is_hyb != 0) {
80 		spd_insert_map(si, SPD_KEY_MOD_NVDIMM_TYPE, hybrid,
81 		    spd_ddr5_mod_hybrid_map,
82 		    ARRAY_SIZE(spd_ddr5_mod_hybrid_map));
83 	}
84 
85 	spd_insert_map(si, SPD_KEY_MOD_TYPE, type, spd_ddr5_mod_type_map,
86 	    ARRAY_SIZE(spd_ddr5_mod_type_map));
87 }
88 
89 static bool
90 spd_parse_ddr5_isassym(spd_info_t *si)
91 {
92 	ASSERT3U(si->si_size, >, SPD_DDR5_COM_ORG);
93 	const uint8_t data = si->si_data[SPD_DDR5_COM_ORG];
94 	const uint8_t is_asym = SPD_DDR5_COM_ORG_MIX(data);
95 
96 	return (is_asym == SPD_DDR5_COM_ORG_MIX_ASYM);
97 }
98 
99 static const spd_value_map64_t spd_ddr5_density_map[] = {
100 	{ SPD_DDR5_DENPKG_DPD_4Gb, 4ULL * 1024ULL * 1024ULL * 1024ULL, false },
101 	{ SPD_DDR5_DENPKG_DPD_8Gb, 8ULL * 1024ULL * 1024ULL * 1024ULL, false },
102 	{ SPD_DDR5_DENPKG_DPD_12Gb, 12ULL * 1024ULL * 1024ULL * 1024ULL,
103 	    false },
104 	{ SPD_DDR5_DENPKG_DPD_16Gb, 16ULL * 1024ULL * 1024ULL * 1024ULL,
105 	    false },
106 	{ SPD_DDR5_DENPKG_DPD_24Gb, 24ULL * 1024ULL * 1024ULL * 1024ULL,
107 	    false },
108 	{ SPD_DDR5_DENPKG_DPD_32Gb, 32ULL * 1024ULL * 1024ULL * 1024ULL,
109 	    false },
110 	{ SPD_DDR5_DENPKG_DPD_48Gb, 48ULL * 1024ULL * 1024ULL * 1024ULL,
111 	    false },
112 	{ SPD_DDR5_DENPKG_DPD_64Gb, 64ULL * 1024ULL * 1024ULL * 1024ULL,
113 	    false },
114 };
115 
116 static const spd_value_map_t spd_ddr5_ndies_map[] = {
117 	{ SPD_DDR5_DENPKG_DPP_MONO, 1, false },
118 	{ SPD_DDR5_DENPKG_DPP_DDP, 2, false },
119 	{ SPD_DDR5_DENPKG_DPP_2H3DS, 2, false },
120 	{ SPD_DDR5_DENPKG_DPP_4H3DS, 4, false },
121 	{ SPD_DDR5_DENPKG_DPP_8H3DS, 8, false },
122 	{ SPD_DDR5_DENPKG_DPP_16H3DS, 16, false },
123 };
124 
125 static const spd_value_map_t spd_ddr5_sl_map[] = {
126 	{ SPD_DDR5_DENPKG_DPP_MONO, SPD_SL_UNSPECIFIED, false },
127 	{ SPD_DDR5_DENPKG_DPP_DDP, SPD_SL_UNSPECIFIED, false },
128 	{ SPD_DDR5_DENPKG_DPP_2H3DS, SPD_SL_3DS, false },
129 	{ SPD_DDR5_DENPKG_DPP_4H3DS, SPD_SL_3DS, false },
130 	{ SPD_DDR5_DENPKG_DPP_8H3DS, SPD_SL_3DS, false },
131 	{ SPD_DDR5_DENPKG_DPP_16H3DS, SPD_SL_3DS, false },
132 };
133 
134 static void
135 spd_parse_ddr5_denpkg(spd_info_t *si, uint8_t data, const char *ndie_key,
136     const char *den_key, const char *sl_key)
137 {
138 	const uint8_t ndie = SPD_DDR5_DENPKG_DPP(data);
139 	const uint8_t dens = SPD_DDR5_DENPKG_DPD(data);
140 
141 	spd_insert_map(si, ndie_key, ndie, spd_ddr5_ndies_map,
142 	    ARRAY_SIZE(spd_ddr5_ndies_map));
143 	spd_insert_map(si, sl_key, ndie, spd_ddr5_sl_map,
144 	    ARRAY_SIZE(spd_ddr5_sl_map));
145 	spd_insert_map64(si, den_key, dens, spd_ddr5_density_map,
146 	    ARRAY_SIZE(spd_ddr5_density_map));
147 }
148 
149 static void
150 spd_parse_ddr5_denpkg_pri(spd_info_t *si, uint32_t off, uint32_t len,
151     const char *key)
152 {
153 	spd_parse_ddr5_denpkg(si, si->si_data[off], SPD_KEY_PKG_NDIE,
154 	    SPD_KEY_DIE_SIZE, SPD_KEY_PKG_SL);
155 }
156 
157 static void
158 spd_parse_ddr5_denpkg_sec(spd_info_t *si, uint32_t off, uint32_t len,
159     const char *key)
160 {
161 	if (!spd_parse_ddr5_isassym(si))
162 		return;
163 
164 	spd_parse_ddr5_denpkg(si, si->si_data[off], SPD_KEY_SEC_PKG_NDIE,
165 	    SPD_KEY_SEC_DIE_SIZE, SPD_KEY_SEC_PKG_SL);
166 }
167 
168 static const spd_value_range_t spd_ddr5_nrow_range = {
169 	.svr_max = SPD_DDR5_ADDR_NROWS_MAX,
170 	.svr_base = SPD_DDR5_ADDR_NROWS_BASE
171 };
172 
173 static const spd_value_range_t spd_ddr5_ncol_range = {
174 	.svr_max = SPD_DDR5_ADDR_NCOLS_MAX,
175 	.svr_base = SPD_DDR5_ADDR_NCOLS_BASE
176 };
177 
178 static void
179 spd_parse_ddr5_addr(spd_info_t *si, uint8_t data, const char *row_key,
180     const char *col_key)
181 {
182 	const uint8_t ncols = SPD_DDR5_ADDR_NCOLS(data);
183 	const uint8_t nrows = SPD_DDR5_ADDR_NROWS(data);
184 
185 	spd_insert_range(si, col_key, ncols, &spd_ddr5_ncol_range);
186 	spd_insert_range(si, row_key, nrows, &spd_ddr5_nrow_range);
187 }
188 
189 static void
190 spd_parse_ddr5_addr_pri(spd_info_t *si, uint32_t off, uint32_t len,
191     const char *key)
192 {
193 	spd_parse_ddr5_addr(si, si->si_data[off], SPD_KEY_NROW_BITS,
194 	    SPD_KEY_NCOL_BITS);
195 }
196 
197 static void
198 spd_parse_ddr5_addr_sec(spd_info_t *si, uint32_t off, uint32_t len,
199     const char *key)
200 {
201 	if (!spd_parse_ddr5_isassym(si))
202 		return;
203 
204 	spd_parse_ddr5_addr(si, si->si_data[off], SPD_KEY_SEC_NROW_BITS,
205 	    SPD_KEY_SEC_NCOL_BITS);
206 }
207 
208 static const spd_value_map_t spd_ddr5_width_map[] = {
209 	{ SPD_DDR5_WIDTH_X4, 4, false },
210 	{ SPD_DDR5_WIDTH_X8, 8, false },
211 	{ SPD_DDR5_WIDTH_X16, 16, false },
212 	{ SPD_DDR5_WIDTH_X32, 32, false }
213 };
214 
215 static void
216 spd_parse_ddr5_width(spd_info_t *si, uint8_t data, const char *key)
217 {
218 	const uint8_t width = SPD_DDR5_WIDTH_WIDTH(data);
219 
220 	spd_insert_map(si, key, width, spd_ddr5_width_map,
221 	    ARRAY_SIZE(spd_ddr5_width_map));
222 }
223 
224 static void
225 spd_parse_ddr5_width_pri(spd_info_t *si, uint32_t off, uint32_t len,
226     const char *key)
227 {
228 	spd_parse_ddr5_width(si, si->si_data[off], SPD_KEY_DRAM_WIDTH);
229 }
230 
231 static void
232 spd_parse_ddr5_width_sec(spd_info_t *si, uint32_t off, uint32_t len,
233     const char *key)
234 {
235 	if (!spd_parse_ddr5_isassym(si))
236 		return;
237 
238 	spd_parse_ddr5_width(si, si->si_data[off], SPD_KEY_SEC_DRAM_WIDTH);
239 }
240 
241 static const spd_value_range_t spd_ddr5_nbg_range = {
242 	.svr_max = SPD_DDR5_BANKS_NBG_MAX
243 };
244 
245 static const spd_value_range_t spd_ddr5_nba_range = {
246 	.svr_max = SPD_DDR5_BANKS_NBA_MAX
247 };
248 
249 static void
250 spd_parse_ddr5_banks(spd_info_t *si, uint8_t data, const char *bg_key,
251     const char *ba_key)
252 {
253 	const uint8_t nbg = 1 << SPD_DDR5_BANKS_NBG(data);
254 	const uint8_t nba = 1 << SPD_DDR5_BANKS_NBA(data);
255 
256 	spd_insert_range(si, bg_key, nbg, &spd_ddr5_nbg_range);
257 	spd_insert_range(si, ba_key, nba, &spd_ddr5_nba_range);
258 }
259 
260 static void
261 spd_parse_ddr5_banks_pri(spd_info_t *si, uint32_t off, uint32_t len,
262     const char *key)
263 {
264 	spd_parse_ddr5_banks(si, si->si_data[off], SPD_KEY_NBGRP_BITS,
265 	    SPD_KEY_NBANK_BITS);
266 }
267 
268 static void
269 spd_parse_ddr5_banks_sec(spd_info_t *si, uint32_t off, uint32_t len,
270     const char *key)
271 {
272 	if (!spd_parse_ddr5_isassym(si))
273 		return;
274 
275 	spd_parse_ddr5_banks(si, si->si_data[off], SPD_KEY_SEC_NBGRP_BITS,
276 	    SPD_KEY_SEC_NBANK_BITS);
277 }
278 
279 static void
280 spd_parse_ddr5_ppr(spd_info_t *si, uint32_t off, uint32_t len,
281     const char *key)
282 {
283 	const uint8_t data = si->si_data[off];
284 	spd_ppr_flags_t flags = SPD_PPR_F_HARD_PPR | SPD_PPR_F_SOFT_PPR;
285 
286 	if (SPD_DDR5_PPR_GRAN(data) == SPD_DDR5_PPR_GRAN_BGRP) {
287 		spd_nvl_insert_u32(si, SPD_KEY_PPR_GRAN,
288 		    SPD_PPR_GRAN_BANK_GROUP);
289 	} else {
290 		spd_nvl_insert_u32(si, SPD_KEY_PPR_GRAN,
291 		    SPD_PPR_GRAN_BANK);
292 	}
293 
294 	if (SPD_DDR5_PPR_LOCK_SUP(data) != 0)
295 		flags |= SPD_PPR_F_PPR_UNDO;
296 	if (SPD_DDR5_PPR_MPPR_SUP(data) != 0)
297 		flags |= SPD_PPR_F_MBIST_PPR;
298 	spd_nvl_insert_u32(si, SPD_KEY_PPR, flags);
299 
300 	if (SPD_DDR5_PPR_BL32_SUP(data) != 0)
301 		spd_nvl_insert_key(si, SPD_KEY_DDR5_BL32);
302 }
303 
304 static const spd_value_map_t spd_ddr5_dca_map[] = {
305 	{ SPD_DDR5_SPD_DCA_TYPE_UNSUP, SPD_DCA_UNSPPORTED, false },
306 	{ SPD_DDR5_SPD_DCA_TYPE_1_2P, SPD_DCA_1_OR_2_PHASE, false },
307 	{ SPD_DDR5_SPD_DCA_TYPE_4P, SPD_DCA_4_PHASE, false }
308 };
309 
310 static void
311 spd_parse_ddr5_dca(spd_info_t *si, uint32_t off, uint32_t len,
312     const char *key)
313 {
314 	const uint8_t data = si->si_data[off];
315 	const uint8_t dca = SPD_DDR5_SPD_DCA_TYPE(data);
316 
317 	if (SPD_DDR5_SPD_DCA_PASR(data) != 0)
318 		spd_nvl_insert_key(si, SPD_KEY_DDR5_PASR);
319 
320 	spd_insert_map(si, SPD_KEY_DDR5_DCA, dca, spd_ddr5_dca_map,
321 	    ARRAY_SIZE(spd_ddr5_dca_map));
322 }
323 
324 static void
325 spd_parse_ddr5_flt(spd_info_t *si, uint32_t off, uint32_t len,
326     const char *key)
327 {
328 	const uint8_t data = si->si_data[off];
329 	spd_fault_t flt = 0;
330 
331 	if (SPD_DDR5_FLT_WIDE_TS(data) != 0)
332 		spd_nvl_insert_key(si, SPD_KEY_DDR5_WIDE_TS);
333 
334 	if (SPD_DDR5_FLT_WBSUPR_SUP(data) != 0) {
335 		if (SPD_DDR5_FLT_WBSUPR_SEL(data) ==
336 		    SPD_DDR5_FLT_WBSUPR_SEL_MR15) {
337 			flt |= SPD_FLT_WRSUP_MR15;
338 		} else {
339 			flt |= SPD_FLT_WRSUP_MR9;
340 		}
341 	}
342 
343 	if (SPD_DDR5_FLT_BFLT(data))
344 		flt |= SPD_FLT_BOUNDED;
345 	spd_nvl_insert_u32(si, SPD_KEY_DDR5_WIDE_TS, flt);
346 }
347 
348 /*
349  * Voltages support describing the nominal, operational, and endurant ranges.
350  * Currently we only encode the nominal values.
351  */
352 static void
353 spd_parse_ddr5_voltage(spd_info_t *si, uint8_t data, const char *key,
354     uint32_t *mv, uint32_t nmv)
355 {
356 	const uint8_t nom_idx = SPD_DDR5_DRAM_VOLT_NOM(data);
357 
358 	if (nom_idx >= nmv) {
359 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
360 		    "encountered unknown value: 0x%x", nom_idx);
361 	} else {
362 		spd_nvl_insert_u32_array(si, key, &mv[nom_idx], 1);
363 	}
364 }
365 
366 static void
367 spd_parse_ddr5_vdd(spd_info_t *si, uint32_t off, uint32_t len,
368     const char *key)
369 {
370 	uint32_t volts[] = { 1100 };
371 	return (spd_parse_ddr5_voltage(si, si->si_data[off], key, volts,
372 	    ARRAY_SIZE(volts)));
373 }
374 
375 static void
376 spd_parse_ddr5_vddq(spd_info_t *si, uint32_t off, uint32_t len,
377     const char *key)
378 {
379 	uint32_t volts[] = { 1100 };
380 	return (spd_parse_ddr5_voltage(si, si->si_data[off], key, volts,
381 	    ARRAY_SIZE(volts)));
382 }
383 
384 static void
385 spd_parse_ddr5_vpp(spd_info_t *si, uint32_t off, uint32_t len,
386     const char *key)
387 {
388 	uint32_t volts[] = { 1800 };
389 	return (spd_parse_ddr5_voltage(si, si->si_data[off], key, volts,
390 	    ARRAY_SIZE(volts)));
391 }
392 
393 static void
394 spd_parse_ddr5_time(spd_info_t *si, uint32_t off, uint32_t len,
395     const char *key)
396 {
397 	const uint8_t data = si->si_data[off];
398 
399 	if (SPD_DDR5_TIME_STD(data) == SPD_DDR5_TIME_STD_NON)
400 		spd_nvl_insert_key(si, SPD_KEY_DDR5_NONSTD_TIME);
401 }
402 
403 /*
404  * Time in picoseconds. The LSB is at off. The MSB is at off + 1.
405  */
406 static void
407 spd_parse_ddr5_ps(spd_info_t *si, uint32_t off, uint32_t len,
408     const char *key)
409 {
410 	uint64_t ps;
411 
412 	ASSERT3U(len, ==, 2);
413 	ps = (uint64_t)si->si_data[off] << 8;
414 	ps |= (uint64_t)si->si_data[off + 1];
415 
416 	if (ps == 0) {
417 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
418 		    "encountered unexpected zero time value");
419 		return;
420 	}
421 
422 	spd_nvl_insert_u64(si, key, ps);
423 }
424 
425 /*
426  * Time in nanoseconds. The LSB is at off. The MSB is at off + 1. We normalize
427  * all times to ps.
428  */
429 static void
430 spd_parse_ddr5_ns(spd_info_t *si, uint32_t off, uint32_t len,
431     const char *key)
432 {
433 	uint64_t ns, ps;
434 
435 	ASSERT3U(len, ==, 2);
436 	ns = (uint64_t)si->si_data[off] << 8;
437 	ns |= (uint64_t)si->si_data[off + 1];
438 
439 	if (ns == 0) {
440 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
441 		    "encountered unexpected zero time value");
442 		return;
443 	}
444 
445 	ps = ns * 1000;
446 	spd_nvl_insert_u64(si, key, ps);
447 }
448 
449 /*
450  * Several DDR5 timing properties are only valid for 3DS type DIMMs. So we
451  * double check the actual DIMM type before we proceed to parse this.
452  */
453 static void
454 spd_parse_ddr5_3ds_ns(spd_info_t *si, uint32_t off, uint32_t len,
455     const char *key)
456 {
457 	ASSERT3U(off, >=, SPD_DDR5_DENPKG1);
458 	uint32_t val;
459 
460 	if (nvlist_lookup_uint32(si->si_nvl, SPD_KEY_PKG_SL, &val) != 0 ||
461 	    val != SPD_SL_3DS) {
462 		return;
463 	}
464 
465 	spd_parse_ddr5_ns(si, off, len, key);
466 }
467 
468 static void
469 spd_parse_ddr5_nck(spd_info_t *si, uint32_t off, uint32_t len,
470     const char *key)
471 {
472 	const uint8_t data = si->si_data[off];
473 
474 	if (data == 0) {
475 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
476 		    "encountered unexpected zero clock value");
477 		return;
478 	}
479 
480 	spd_nvl_insert_u32(si, key, data);
481 }
482 
483 static void
484 spd_parse_ddr5_cas(spd_info_t *si, uint32_t off, uint32_t len,
485     const char *key)
486 {
487 	uint32_t cas[40] = { 0 };
488 	uint_t ncas = 0;
489 	uint32_t cas_base = 20;
490 
491 	ASSERT3U(len, ==, 5);
492 
493 	for (uint32_t byte = 0; byte < len; byte++) {
494 		uint32_t data = si->si_data[off];
495 
496 		for (uint32_t i = 0; i < NBBY; i++) {
497 			if (bitx8(data, i, i) == 1) {
498 				cas[ncas] = cas_base + 2 * (i + NBBY * byte);
499 				ncas++;
500 			}
501 		}
502 	}
503 
504 	spd_nvl_insert_u32_array(si, key, cas, ncas);
505 }
506 
507 static const spd_value_range_t spd_ddr5_raammt_norm_range = {
508 	.svr_min = SPD_DDR5_RFM0_RAAMMT_NORM_MIN,
509 	.svr_max = SPD_DDR5_RFM0_RAAMMT_NORM_MAX,
510 	.svr_mult = SPD_DDR5_RFM0_RAAMMT_NORM_MULT
511 };
512 
513 static const spd_value_range_t spd_ddr5_raammt_fgr_range = {
514 	.svr_min = SPD_DDR5_RFM0_RAAMMT_FGR_MIN,
515 	.svr_max = SPD_DDR5_RFM0_RAAMMT_FGR_MAX,
516 	.svr_mult = SPD_DDR5_RFM0_RAAMMT_FGR_MULT
517 };
518 
519 static const spd_value_range_t spd_ddr5_raaimt_norm_range = {
520 	.svr_min = SPD_DDR5_RFM0_RAAIMT_NORM_MIN,
521 	.svr_max = SPD_DDR5_RFM0_RAAIMT_NORM_MAX,
522 	.svr_mult = SPD_DDR5_RFM0_RAAIMT_NORM_MULT
523 };
524 
525 static const spd_value_range_t spd_ddr5_raaimt_fgr_range = {
526 	.svr_min = SPD_DDR5_RFM0_RAAIMT_FGR_MIN,
527 	.svr_max = SPD_DDR5_RFM0_RAAIMT_FGR_MAX,
528 	.svr_mult = SPD_DDR5_RFM0_RAAIMT_FGR_MULT
529 };
530 
531 static const spd_value_range_t spd_ddr5_brc_cfg_range = {
532 	.svr_max = SPD_DDR5_RFM1_BRC_CFG_MAX,
533 	.svr_base = SPD_DDR5_RFM1_BRC_CFG_BASE
534 };
535 
536 static const spd_value_map_t spd_ddr5_raa_ctr_map[] = {
537 	{ SPD_DDR5_RFM1_CTR_1X, 1, false },
538 	{ SPD_DDR5_RFM1_CTR_2X, 2, false }
539 };
540 
541 static void
542 spd_parse_ddr5_rfm_flags(spd_info_t *si, uint8_t rfm0, uint8_t rfm1,
543     const char *key)
544 {
545 	spd_rfm_flags_t flags = 0;
546 
547 	if (SPD_DDR5_RFM0_RFM_REQ(rfm0) != 0)
548 		flags |= SPD_RFM_F_REQUIRED;
549 	if (SPD_DDR5_RFM1_DRFM_SUP(rfm1) != 0)
550 		flags |= SPD_RFM_F_DRFM_SUP;
551 
552 	spd_nvl_insert_u32(si, key, flags);
553 }
554 
555 static void
556 spd_parse_ddr5_arfm_flags(spd_info_t *si, uint8_t rfm1, const char *key)
557 {
558 	spd_rfm_flags_t flags = 0;
559 
560 	if (SPD_DDR5_RFM1_DRFM_SUP(rfm1) != 0)
561 		flags |= SPD_RFM_F_DRFM_SUP;
562 
563 	spd_nvl_insert_u32(si, key, flags);
564 }
565 
566 static void
567 spd_parse_ddr5_rfm_common(spd_info_t *si, uint8_t rfm0, uint8_t rfm1,
568     const char *raaimt_key, const char *raaimt_fgr_key, const char *raammt_key,
569     const char *raammt_fgr_key, const char *brc_cfg_key,
570     const char *brc_sup_key, const char *raa_ctr_key)
571 {
572 	const uint8_t raammt = SPD_DDR5_RFM0_RAAMMT_NORM(rfm0);
573 	const uint8_t raammt_fgr = SPD_DDR5_RFM0_RAAMMT_FGR(rfm0);
574 	const uint8_t raaimt = SPD_DDR5_RFM0_RAAIMT_NORM(rfm0);
575 	const uint8_t raaimt_fgr = SPD_DDR5_RFM0_RAAIMT_FGR(rfm0);
576 	const uint8_t brc_cfg = SPD_DDR5_RFM1_BRC_CFG(rfm1);
577 	const uint8_t brc_sup = SPD_DDR5_RFM1_BRC_SUP(rfm1);
578 	const uint8_t raa_ctr = SPD_DDR5_RFM1_CTR(rfm1);
579 	spd_brc_flags_t brc_flags = SPD_BRC_F_LVL_2;
580 
581 	if (brc_sup == SPD_DDR5_RFM1_BRC_SUP_234)
582 		brc_flags |= SPD_BRC_F_LVL_3 | SPD_BRC_F_LVL_4;
583 
584 	spd_insert_range(si, raaimt_key, raaimt, &spd_ddr5_raaimt_norm_range);
585 	spd_insert_range(si, raaimt_fgr_key, raaimt_fgr,
586 	    &spd_ddr5_raaimt_fgr_range);
587 	spd_insert_range(si, raammt_key, raammt, &spd_ddr5_raammt_norm_range);
588 	spd_insert_range(si, raammt_fgr_key, raammt_fgr,
589 	    &spd_ddr5_raammt_fgr_range);
590 	spd_insert_range(si, brc_cfg_key, brc_cfg, &spd_ddr5_brc_cfg_range);
591 	spd_nvl_insert_u32(si, brc_sup_key, brc_flags);
592 
593 	spd_insert_map(si, raa_ctr_key, raa_ctr, spd_ddr5_raa_ctr_map,
594 	    ARRAY_SIZE(spd_ddr5_raa_ctr_map));
595 }
596 
597 static void
598 spd_parse_ddr5_rfm_pri(spd_info_t *si, uint32_t off, uint32_t len,
599     const char *key)
600 {
601 	ASSERT3U(len, ==, 2);
602 
603 	spd_parse_ddr5_rfm_flags(si, si->si_data[off], si->si_data[off + 1],
604 	    SPD_KEY_DDR5_RFM_FLAGS_PRI);
605 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
606 	    SPD_KEY_DDR5_RFM_RAAIMT_PRI, SPD_KEY_DDR5_RFM_RAAIMT_FGR_PRI,
607 	    SPD_KEY_DDR5_RFM_RAAMMT_PRI, SPD_KEY_DDR5_RFM_RAAMMT_FGR_PRI,
608 	    SPD_KEY_DDR5_RFM_BRC_CFG_PRI, SPD_KEY_DDR5_RFM_BRC_SUP_PRI,
609 	    SPD_KEY_DDR5_RFM_RAA_DEC_PRI);
610 }
611 
612 static void
613 spd_parse_ddr5_rfm_sec(spd_info_t *si, uint32_t off, uint32_t len,
614     const char *key)
615 {
616 	if (!spd_parse_ddr5_isassym(si))
617 		return;
618 
619 	spd_parse_ddr5_rfm_flags(si, si->si_data[off], si->si_data[off + 1],
620 	    SPD_KEY_DDR5_RFM_FLAGS_SEC);
621 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
622 	    SPD_KEY_DDR5_RFM_RAAIMT_SEC, SPD_KEY_DDR5_RFM_RAAIMT_FGR_SEC,
623 	    SPD_KEY_DDR5_RFM_RAAMMT_SEC, SPD_KEY_DDR5_RFM_RAAMMT_FGR_SEC,
624 	    SPD_KEY_DDR5_RFM_BRC_CFG_SEC, SPD_KEY_DDR5_RFM_BRC_SUP_SEC,
625 	    SPD_KEY_DDR5_RFM_RAA_DEC_SEC);
626 }
627 
628 static void
629 spd_parse_ddr5_arfma_pri(spd_info_t *si, uint32_t off, uint32_t len,
630     const char *key)
631 {
632 	ASSERT3U(len, ==, 2);
633 
634 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
635 		return;
636 
637 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
638 	    SPD_KEY_DDR5_ARFMA_FLAGS_PRI);
639 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
640 	    SPD_KEY_DDR5_ARFMA_RAAIMT_PRI, SPD_KEY_DDR5_ARFMA_RAAIMT_FGR_PRI,
641 	    SPD_KEY_DDR5_ARFMA_RAAMMT_PRI, SPD_KEY_DDR5_ARFMA_RAAMMT_FGR_PRI,
642 	    SPD_KEY_DDR5_ARFMA_BRC_CFG_PRI, SPD_KEY_DDR5_ARFMA_BRC_SUP_PRI,
643 	    SPD_KEY_DDR5_ARFMA_RAA_DEC_PRI);
644 }
645 
646 static void
647 spd_parse_ddr5_arfma_sec(spd_info_t *si, uint32_t off, uint32_t len,
648     const char *key)
649 {
650 	if (!spd_parse_ddr5_isassym(si))
651 		return;
652 
653 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
654 		return;
655 
656 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
657 	    SPD_KEY_DDR5_ARFMA_FLAGS_SEC);
658 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
659 	    SPD_KEY_DDR5_ARFMA_RAAIMT_SEC, SPD_KEY_DDR5_ARFMA_RAAIMT_FGR_SEC,
660 	    SPD_KEY_DDR5_ARFMA_RAAMMT_SEC, SPD_KEY_DDR5_ARFMA_RAAMMT_FGR_SEC,
661 	    SPD_KEY_DDR5_ARFMA_BRC_CFG_SEC, SPD_KEY_DDR5_ARFMA_BRC_SUP_SEC,
662 	    SPD_KEY_DDR5_ARFMA_RAA_DEC_SEC);
663 }
664 
665 static void
666 spd_parse_ddr5_arfmb_pri(spd_info_t *si, uint32_t off, uint32_t len,
667     const char *key)
668 {
669 	ASSERT3U(len, ==, 2);
670 
671 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
672 		return;
673 
674 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
675 	    SPD_KEY_DDR5_ARFMB_FLAGS_PRI);
676 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
677 	    SPD_KEY_DDR5_ARFMB_RAAIMT_PRI, SPD_KEY_DDR5_ARFMB_RAAIMT_FGR_PRI,
678 	    SPD_KEY_DDR5_ARFMB_RAAMMT_PRI, SPD_KEY_DDR5_ARFMB_RAAMMT_FGR_PRI,
679 	    SPD_KEY_DDR5_ARFMB_BRC_CFG_PRI, SPD_KEY_DDR5_ARFMB_BRC_SUP_PRI,
680 	    SPD_KEY_DDR5_ARFMB_RAA_DEC_PRI);
681 }
682 
683 static void
684 spd_parse_ddr5_arfmb_sec(spd_info_t *si, uint32_t off, uint32_t len,
685     const char *key)
686 {
687 	if (!spd_parse_ddr5_isassym(si))
688 		return;
689 
690 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
691 		return;
692 
693 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
694 	    SPD_KEY_DDR5_ARFMB_FLAGS_SEC);
695 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
696 	    SPD_KEY_DDR5_ARFMB_RAAIMT_SEC, SPD_KEY_DDR5_ARFMB_RAAIMT_FGR_SEC,
697 	    SPD_KEY_DDR5_ARFMB_RAAMMT_SEC, SPD_KEY_DDR5_ARFMB_RAAMMT_FGR_SEC,
698 	    SPD_KEY_DDR5_ARFMB_BRC_CFG_SEC, SPD_KEY_DDR5_ARFMB_BRC_SUP_SEC,
699 	    SPD_KEY_DDR5_ARFMB_RAA_DEC_SEC);
700 }
701 
702 static void
703 spd_parse_ddr5_arfmc_pri(spd_info_t *si, uint32_t off, uint32_t len,
704     const char *key)
705 {
706 	ASSERT3U(len, ==, 2);
707 
708 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
709 		return;
710 
711 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
712 	    SPD_KEY_DDR5_ARFMC_FLAGS_PRI);
713 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
714 	    SPD_KEY_DDR5_ARFMC_RAAIMT_PRI, SPD_KEY_DDR5_ARFMC_RAAIMT_FGR_PRI,
715 	    SPD_KEY_DDR5_ARFMC_RAAMMT_PRI, SPD_KEY_DDR5_ARFMC_RAAMMT_FGR_PRI,
716 	    SPD_KEY_DDR5_ARFMC_BRC_CFG_PRI, SPD_KEY_DDR5_ARFMC_BRC_SUP_PRI,
717 	    SPD_KEY_DDR5_ARFMC_RAA_DEC_PRI);
718 }
719 
720 static void
721 spd_parse_ddr5_arfmc_sec(spd_info_t *si, uint32_t off, uint32_t len,
722     const char *key)
723 {
724 	if (!spd_parse_ddr5_isassym(si))
725 		return;
726 
727 	if (SPD_DDR5_ARFM_SUP(si->si_data[off]) == 0)
728 		return;
729 
730 	spd_parse_ddr5_arfm_flags(si, si->si_data[off + 1],
731 	    SPD_KEY_DDR5_ARFMC_FLAGS_SEC);
732 	spd_parse_ddr5_rfm_common(si, si->si_data[off], si->si_data[off + 1],
733 	    SPD_KEY_DDR5_ARFMC_RAAIMT_SEC, SPD_KEY_DDR5_ARFMC_RAAIMT_FGR_SEC,
734 	    SPD_KEY_DDR5_ARFMC_RAAMMT_SEC, SPD_KEY_DDR5_ARFMC_RAAMMT_FGR_SEC,
735 	    SPD_KEY_DDR5_ARFMC_BRC_CFG_SEC, SPD_KEY_DDR5_ARFMC_BRC_SUP_SEC,
736 	    SPD_KEY_DDR5_ARFMC_RAA_DEC_SEC);
737 }
738 
739 static const spd_parse_t spd_ddr5_base[] = {
740 	{ .sp_off = SPD_DDR5_NBYTES, .sp_parse = spd_parse_ddr5_nbytes },
741 	{ .sp_off = SPD_DDR5_SPD_REV, .sp_parse = spd_parse_rev },
742 	/*
743 	 * We have previously validated that the DRAM type is something that we
744 	 * understand. We pass through the raw enum to users here.
745 	 */
746 	{ .sp_off = SPD_DDR5_DRAM_TYPE, .sp_key = SPD_KEY_DRAM_TYPE,
747 	    .sp_parse = spd_parse_raw_u8 },
748 	{ .sp_off = SPD_DDR5_MOD_TYPE, .sp_parse = spd_parse_ddr5_mod_type },
749 	/*
750 	 * All secondary values must check whether an asymmetrical module is
751 	 * present in Byte 234. As such, for the secondary versions we set LEN
752 	 * to include that value. They then move to a common function.
753 	 */
754 	{ .sp_off = SPD_DDR5_DENPKG1, .sp_parse = spd_parse_ddr5_denpkg_pri },
755 	{ .sp_off = SPD_DDR5_DENPKG2, .sp_parse = spd_parse_ddr5_denpkg_sec,
756 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_DENPKG2 + 1 },
757 	{ .sp_off = SPD_DDR5_ADDR1, .sp_parse = spd_parse_ddr5_addr_pri },
758 	{ .sp_off = SPD_DDR5_ADDR2, .sp_parse = spd_parse_ddr5_addr_sec,
759 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_ADDR2 + 1 },
760 	{ .sp_off = SPD_DDR5_WIDTH1, .sp_parse = spd_parse_ddr5_width_pri },
761 	{ .sp_off = SPD_DDR5_WIDTH2, .sp_parse = spd_parse_ddr5_width_sec,
762 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_WIDTH2 + 1 },
763 	{ .sp_off = SPD_DDR5_BANKS1, .sp_parse = spd_parse_ddr5_banks_pri },
764 	{ .sp_off = SPD_DDR5_BANKS2, .sp_parse = spd_parse_ddr5_banks_sec,
765 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_BANKS2 + 1 },
766 	{ .sp_off = SPD_DDR5_PPR, .sp_parse = spd_parse_ddr5_ppr },
767 	{ .sp_off = SPD_DDR5_SDA, .sp_parse = spd_parse_ddr5_dca },
768 	{ .sp_off = SPD_DDR5_FLT, .sp_parse = spd_parse_ddr5_flt },
769 	{ .sp_off = SPD_DDR5_DRAM_VDD, .sp_key = SPD_KEY_NOM_VDD,
770 	    .sp_parse = spd_parse_ddr5_vdd },
771 	{ .sp_off = SPD_DDR5_DRAM_VDDQ, .sp_key = SPD_KEY_NOM_VDDQ,
772 	    .sp_parse = spd_parse_ddr5_vddq },
773 	{ .sp_off = SPD_DDR5_DRAM_VPP, .sp_key = SPD_KEY_NOM_VPP,
774 	    .sp_parse = spd_parse_ddr5_vpp },
775 	{ .sp_off = SPD_DDR5_TIME, .sp_parse = spd_parse_ddr5_time },
776 	{ .sp_off = SPD_DDR5_TCKAVG_MIN_LSB, .sp_len = 2,
777 	    .sp_key = SPD_KEY_TCKAVG_MIN, .sp_parse = spd_parse_ddr5_ps },
778 	{ .sp_off = SPD_DDR5_TCKAVG_MAX_LSB, .sp_len = 2,
779 	    .sp_key = SPD_KEY_TCKAVG_MAX, .sp_parse = spd_parse_ddr5_ps },
780 	{ .sp_off = SPD_DDR5_CAS_SUP0, .sp_len = 5, .sp_key = SPD_KEY_CAS,
781 	    .sp_parse = spd_parse_ddr5_cas },
782 	{ .sp_off = SPD_DDR5_TAA_LSB, .sp_len = 2,
783 	    .sp_key = SPD_KEY_TAA_MIN, .sp_parse = spd_parse_ddr5_ps },
784 	{ .sp_off = SPD_DDR5_TRCD_LSB, .sp_len = 2,
785 	    .sp_key = SPD_KEY_TRCD_MIN, .sp_parse = spd_parse_ddr5_ps },
786 	{ .sp_off = SPD_DDR5_TRP_LSB, .sp_len = 2,
787 	    .sp_key = SPD_KEY_TRP_MIN, .sp_parse = spd_parse_ddr5_ps },
788 	{ .sp_off = SPD_DDR5_TRAS_LSB, .sp_len = 2,
789 	    .sp_key = SPD_KEY_TRAS_MIN, .sp_parse = spd_parse_ddr5_ps },
790 	{ .sp_off = SPD_DDR5_TRC_LSB, .sp_len = 2,
791 	    .sp_key = SPD_KEY_TRC_MIN, .sp_parse = spd_parse_ddr5_ps },
792 	{ .sp_off = SPD_DDR5_TWR_LSB, .sp_len = 2,
793 	    .sp_key = SPD_KEY_TWR_MIN, .sp_parse = spd_parse_ddr5_ps },
794 	{ .sp_off = SPD_DDR5_TRFC1_LSB, .sp_len = 2,
795 	    .sp_key = SPD_KEY_TRFC1_MIN, .sp_parse = spd_parse_ddr5_ns },
796 	{ .sp_off = SPD_DDR5_TRFC2_LSB, .sp_len = 2,
797 	    .sp_key = SPD_KEY_TRFC2_MIN, .sp_parse = spd_parse_ddr5_ns },
798 	{ .sp_off = SPD_DDR5_TRFCSB_LSB, .sp_len = 2,
799 	    .sp_key = SPD_KEY_TRFCSB, .sp_parse = spd_parse_ddr5_ns },
800 	{ .sp_off = SPD_DDR5_3DS_TRFC1_LSB, .sp_len = 2,
801 	    .sp_key = SPD_KEY_TRFC1_DLR, .sp_parse = spd_parse_ddr5_3ds_ns },
802 	{ .sp_off = SPD_DDR5_3DS_TRFC2_LSB, .sp_len = 2,
803 	    .sp_key = SPD_KEY_TRFC2_DLR, .sp_parse = spd_parse_ddr5_3ds_ns },
804 	{ .sp_off = SPD_DDR5_3DS_TRFCSB_LSB, .sp_len = 2,
805 	    .sp_key = SPD_KEY_TRFCSB_DLR, .sp_parse = spd_parse_ddr5_3ds_ns },
806 	{ .sp_off = SPD_DDR5_RFM0_SDRAM0, .sp_len = 2,
807 	    .sp_parse = spd_parse_ddr5_rfm_pri },
808 	{ .sp_off = SPD_DDR5_RFM0_SDRAM1, .sp_parse = spd_parse_ddr5_rfm_sec,
809 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_RFM0_SDRAM1 + 1 },
810 	{ .sp_off = SPD_DDR5_ARFM0_A_SDRAM0, .sp_len = 2,
811 	    .sp_parse = spd_parse_ddr5_arfma_pri },
812 	{ .sp_off = SPD_DDR5_ARFM0_A_SDRAM1,
813 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_ARFM0_A_SDRAM1 + 1,
814 	    .sp_parse = spd_parse_ddr5_arfma_sec },
815 	{ .sp_off = SPD_DDR5_ARFM0_B_SDRAM0, .sp_len = 2,
816 	    .sp_parse = spd_parse_ddr5_arfmb_pri },
817 	{ .sp_off = SPD_DDR5_ARFM0_B_SDRAM1,
818 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_ARFM0_B_SDRAM1 + 1,
819 	    .sp_parse = spd_parse_ddr5_arfmb_sec },
820 	{ .sp_off = SPD_DDR5_ARFM0_C_SDRAM0, .sp_len = 2,
821 	    .sp_parse = spd_parse_ddr5_arfmc_pri },
822 	{ .sp_off = SPD_DDR5_ARFM0_C_SDRAM1,
823 	    .sp_len = SPD_DDR5_COM_ORG - SPD_DDR5_ARFM0_C_SDRAM1 + 1,
824 	    .sp_parse = spd_parse_ddr5_arfmc_sec },
825 	{ .sp_off = SPD_DDR5_TRRD_L_LSB, .sp_len = 2,
826 	    .sp_key = SPD_KEY_TRRD_L_MIN, .sp_parse = spd_parse_ddr5_ps },
827 	{ .sp_off = SPD_DDR5_TRRD_L_NCK, .sp_key = SPD_KEY_TRRDL_NCK,
828 	    .sp_parse = spd_parse_ddr5_nck },
829 	{ .sp_off = SPD_DDR5_TCCD_L_LSB, .sp_len = 2,
830 	    .sp_key = SPD_KEY_TCCD_L_MIN, .sp_parse = spd_parse_ddr5_ps },
831 	{ .sp_off = SPD_DDR5_TCCD_L_NCK, .sp_key = SPD_KEY_TCCDL_NCK,
832 	    .sp_parse = spd_parse_ddr5_nck },
833 	{ .sp_off = SPD_DDR5_TCCD_L_WR_LSB, .sp_len = 2,
834 	    .sp_key = SPD_KEY_TCCDLWR, .sp_parse = spd_parse_ddr5_ps },
835 	{ .sp_off = SPD_DDR5_TCCD_L_WR_NCK, .sp_key = SPD_KEY_TCCDLWR_NCK,
836 	    .sp_parse = spd_parse_ddr5_nck },
837 	{ .sp_off = SPD_DDR5_TCCD_L_WR2_LSB, .sp_len = 2,
838 	    .sp_key = SPD_KEY_TCCDLWR2, .sp_parse = spd_parse_ddr5_ps },
839 	{ .sp_off = SPD_DDR5_TCCD_L_WR2_NCK, .sp_key = SPD_KEY_TCCDLWR2_NCK,
840 	    .sp_parse = spd_parse_ddr5_nck },
841 	{ .sp_off = SPD_DDR5_TFAW_LSB, .sp_len = 2,
842 	    .sp_key = SPD_KEY_TFAW, .sp_parse = spd_parse_ddr5_ps },
843 	{ .sp_off = SPD_DDR5_TFAW_NCK, .sp_key = SPD_KEY_TFAW_NCK,
844 	    .sp_parse = spd_parse_ddr5_nck },
845 	{ .sp_off = SPD_DDR5_TCCD_L_WTR_LSB, .sp_len = 2,
846 	    .sp_key = SPD_KEY_TCCDLWTR, .sp_parse = spd_parse_ddr5_ps },
847 	{ .sp_off = SPD_DDR5_TCCD_L_WTR_NCK, .sp_key = SPD_KEY_TCCDLWTR_NCK,
848 	    .sp_parse = spd_parse_ddr5_nck },
849 	{ .sp_off = SPD_DDR5_TCCD_S_WTR_LSB, .sp_len = 2,
850 	    .sp_key = SPD_KEY_TCCDSWTR, .sp_parse = spd_parse_ddr5_ps },
851 	{ .sp_off = SPD_DDR5_TCCD_S_WTR_NCK, .sp_key = SPD_KEY_TCCDSWTR_NCK,
852 	    .sp_parse = spd_parse_ddr5_nck },
853 	{ .sp_off = SPD_DDR5_TRTP_LSB, .sp_len = 2,
854 	    .sp_key = SPD_KEY_TRTP, .sp_parse = spd_parse_ddr5_ps },
855 	{ .sp_off = SPD_DDR5_TRTP_NCK, .sp_key = SPD_KEY_TRTP_NCK,
856 	    .sp_parse = spd_parse_ddr5_nck }
857 };
858 
859 static void
860 spd_parse_ddr5_mod_rev(spd_info_t *si, uint32_t off, uint32_t len,
861     const char *key)
862 {
863 	const uint8_t data = si->si_data[off];
864 	const uint8_t enc = SPD_DDR5_SPD_REV_ENC(data);
865 	const uint8_t add = SPD_DDR5_SPD_REV_ENC(data);
866 
867 	spd_nvl_insert_u32(si, SPD_KEY_MOD_REV_ENC, enc);
868 	spd_nvl_insert_u32(si, SPD_KEY_MOD_REV_ADD, add);
869 }
870 
871 static const spd_value_map_t spd_ddr5_hash_map[] = {
872 	{ SPD_DDR5_COM_HASH_NONE, 0, true },
873 	{ SPD_DDR5_COM_HASH_ALG1, SPD_HASH_SEQ_ALG_1, false }
874 };
875 
876 static void
877 spd_parse_ddr5_hash_seq(spd_info_t *si, uint32_t off, uint32_t len,
878     const char *key)
879 {
880 	const uint8_t data = si->si_data[off];
881 	const uint8_t alg = SPD_DDR5_COM_HASH_HASH(data);
882 
883 	spd_insert_map(si, key, alg, spd_ddr5_hash_map,
884 	    ARRAY_SIZE(spd_ddr5_hash_map));
885 }
886 
887 static void
888 spd_parse_ddr5_dev_common(spd_info_t *si, uint32_t off, spd_device_t flags,
889     const char *id_key, const char *id_str_key, const char *rev_key,
890     const char *type_key, const spd_value_map_t *type_map, size_t ntypes)
891 {
892 	const uint8_t type = SPD_DDR5_COM_INFO_TYPE(si->si_data[off + 2]);
893 
894 	spd_parse_jedec_id(si, off, 2, id_key);
895 	spd_parse_jedec_id_str(si, off, 2, id_str_key);
896 	spd_parse_hex_vers(si, off + 3, 1, rev_key);
897 	spd_upsert_flag(si, SPD_KEY_DEVS, flags);
898 	spd_insert_map(si, type_key, type, type_map, ntypes);
899 }
900 
901 static const spd_value_map_t spd_ddr5_spd_type_map[] = {
902 	{ SPD_DDR5_COM_INFO_TYPE_SPD5118, SPD_SPD_T_SPD5118, false },
903 	{ SPD_DDR5_COM_INFO_TYPE_ESPD5216, SPD_SPD_T_ESPD5216, false }
904 };
905 
906 static void
907 spd_parse_ddr5_spd(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
908 {
909 	ASSERT3U(len, ==, 4);
910 	const uint8_t type = si->si_data[off + 2];
911 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
912 		return;
913 
914 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_SPD, SPD_KEY_DEV_SPD_MFG,
915 	    SPD_KEY_DEV_SPD_MFG_NAME, SPD_KEY_DEV_SPD_REV, SPD_KEY_DEV_SPD_TYPE,
916 	    spd_ddr5_spd_type_map, ARRAY_SIZE(spd_ddr5_spd_type_map));
917 }
918 
919 static const spd_value_map_t spd_ddr5_pmic_type_map[] = {
920 	{ SPD_DDR5_COM_INFO_TYPE_PMIC5000, SPD_PMIC_T_PMIC5000, false },
921 	{ SPD_DDR5_COM_INFO_TYPE_PMIC5010, SPD_PMIC_T_PMIC5010, false },
922 	{ SPD_DDR5_COM_INFO_TYPE_PMIC5100, SPD_PMIC_T_PMIC5100, false },
923 };
924 
925 static void
926 spd_parse_ddr5_pmic0(spd_info_t *si, uint32_t off, uint32_t len,
927     const char *key)
928 {
929 	ASSERT3U(len, ==, 4);
930 	const uint8_t type = si->si_data[off + 2];
931 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
932 		return;
933 
934 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_PMIC_0,
935 	    SPD_KEY_DEV_PMIC0_MFG, SPD_KEY_DEV_PMIC0_MFG_NAME,
936 	    SPD_KEY_DEV_PMIC0_REV, SPD_KEY_DEV_PMIC0_TYPE,
937 	    spd_ddr5_pmic_type_map, ARRAY_SIZE(spd_ddr5_pmic_type_map));
938 }
939 
940 static void
941 spd_parse_ddr5_pmic1(spd_info_t *si, uint32_t off, uint32_t len,
942     const char *key)
943 {
944 	ASSERT3U(len, ==, 4);
945 	const uint8_t type = si->si_data[off + 2];
946 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
947 		return;
948 
949 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_PMIC_1,
950 	    SPD_KEY_DEV_PMIC1_MFG, SPD_KEY_DEV_PMIC1_MFG_NAME,
951 	    SPD_KEY_DEV_PMIC1_REV, SPD_KEY_DEV_PMIC1_TYPE,
952 	    spd_ddr5_pmic_type_map, ARRAY_SIZE(spd_ddr5_pmic_type_map));
953 }
954 
955 static void
956 spd_parse_ddr5_pmic2(spd_info_t *si, uint32_t off, uint32_t len,
957     const char *key)
958 {
959 	ASSERT3U(len, ==, 4);
960 	const uint8_t type = si->si_data[off + 2];
961 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
962 		return;
963 
964 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_PMIC_2,
965 	    SPD_KEY_DEV_PMIC2_MFG, SPD_KEY_DEV_PMIC2_MFG_NAME,
966 	    SPD_KEY_DEV_PMIC2_REV, SPD_KEY_DEV_PMIC2_TYPE,
967 	    spd_ddr5_pmic_type_map, ARRAY_SIZE(spd_ddr5_pmic_type_map));
968 }
969 
970 static const spd_value_map_t spd_ddr5_temp_type_map[] = {
971 	{ SPD_DDR5_COM_INFO_TYPE_TS5111, SPD_TEMP_T_TS5111, false },
972 	{ SPD_DDR5_COM_INFO_TYPE_TS5110, SPD_TEMP_T_TS5110, false }
973 };
974 
975 static void
976 spd_parse_ddr5_ts(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
977 {
978 	ASSERT3U(len, ==, 4);
979 	const uint8_t type = si->si_data[off + 2];
980 	spd_device_t flags = 0;
981 	if (SPD_DDR5_COM_INFO_PRES(type) != 0)
982 		flags |= SPD_DEVICE_TEMP_1;
983 	if (SPD_DDR5_COM_INFO_TS1_PRES(type) != 0)
984 		flags |= SPD_DEVICE_TEMP_2;
985 	if (flags == 0)
986 		return;
987 
988 	spd_parse_ddr5_dev_common(si, off, flags, SPD_KEY_DEV_TEMP_MFG,
989 	    SPD_KEY_DEV_TEMP_MFG_NAME, SPD_KEY_DEV_TEMP_REV,
990 	    SPD_KEY_DEV_TEMP_TYPE, spd_ddr5_temp_type_map,
991 	    ARRAY_SIZE(spd_ddr5_temp_type_map));
992 }
993 
994 static const spd_str_map_t spd_ddr5_design_map[] = {
995 	{ 0, "A", false },
996 	{ 1, "B", false },
997 	{ 2, "C", false },
998 	{ 3, "D", false },
999 	{ 4, "E", false },
1000 	{ 5, "F", false },
1001 	{ 6, "G", false },
1002 	{ 7, "H", false },
1003 	{ 8, "J", false },
1004 	{ 9, "K", false },
1005 	{ 10, "L", false },
1006 	{ 11, "M", false },
1007 	{ 12, "N", false },
1008 	{ 13, "P", false },
1009 	{ 14, "R", false },
1010 	{ 15, "T", false },
1011 	{ 16, "U", false },
1012 	{ 17, "V", false },
1013 	{ 18, "W", false },
1014 	{ 19, "Y", false },
1015 	{ 20, "AA", false },
1016 	{ 21, "AB", false },
1017 	{ 22, "AC", false },
1018 	{ 23, "AD", false },
1019 	{ 24, "AE", false },
1020 	{ 25, "AF", false },
1021 	{ 26, "AG", false },
1022 	{ 27, "AH", false },
1023 	{ 28, "AJ", false },
1024 	{ 29, "AK", false },
1025 	{ 31, "ZZ", false }
1026 };
1027 
1028 static const spd_value_range_t spd_ddr5_design_rev_range = {
1029 	.svr_max = SPD_DDR5_COM_REF_REV_MAX
1030 };
1031 
1032 static void
1033 spd_parse_ddr5_design(spd_info_t *si, uint32_t off, uint32_t len,
1034     const char *key)
1035 {
1036 	const uint8_t data = si->si_data[off];
1037 	const uint8_t rev = SPD_DDR5_COM_REF_REV(data);
1038 	const uint8_t card = SPD_DDR5_COM_REF_REV(data);
1039 
1040 	spd_insert_str_map(si, SPD_KEY_MOD_REF_DESIGN, card,
1041 	    spd_ddr5_design_map, ARRAY_SIZE(spd_ddr5_design_map));
1042 	spd_insert_range(si, SPD_KEY_MOD_DESIGN_REV, rev,
1043 	    &spd_ddr5_design_rev_range);
1044 }
1045 
1046 static const spd_value_map_t spd_ddr5_attr_nrows_map[] = {
1047 	{ SPD_DDR5_COM_ATTR_NROWS_UNDEF, 0, true },
1048 	{ SPD_DDR5_COM_ATTR_NROWS_1, 1, false },
1049 	{ SPD_DDR5_COM_ATTR_NROWS_2, 2, false }
1050 };
1051 
1052 static const spd_value_map_t spd_ddr5_attr_otr_map[] = {
1053 	{ SPD_DDR5_COM_ATTR_OTR_A1T, JEDEC_TEMP_CASE_A1T, false },
1054 	{ SPD_DDR5_COM_ATTR_OTR_A2T, JEDEC_TEMP_CASE_A2T, false },
1055 	{ SPD_DDR5_COM_ATTR_OTR_A3T, JEDEC_TEMP_CASE_A3T, false },
1056 	{ SPD_DDR5_COM_ATTR_OTR_IT, JEDEC_TEMP_CASE_IT, false },
1057 	{ SPD_DDR5_COM_ATTR_OTR_ST, JEDEC_TEMP_CASE_ST, false },
1058 	{ SPD_DDR5_COM_ATTR_OTR_ET, JEDEC_TEMP_CASE_ET, false },
1059 	{ SPD_DDR5_COM_ATTR_OTR_RT, JEDEC_TEMP_CASE_RT, false },
1060 	{ SPD_DDR5_COM_ATTR_OTR_NT, JEDEC_TEMP_CASE_NT, false },
1061 	{ SPD_DDR5_COM_ATTR_OTR_XT, JEDEC_TEMP_CASE_XT, false }
1062 };
1063 
1064 static void
1065 spd_parse_ddr5_attr(spd_info_t *si, uint32_t off, uint32_t len,
1066     const char *key)
1067 {
1068 	const uint8_t data = si->si_data[off];
1069 	const uint8_t otr = SPD_DDR5_COM_ATTR_OTR(data);
1070 	const uint8_t nrows = SPD_DDR5_COM_ATTR_NROWS(data);
1071 
1072 	if (SPD_DDR5_COM_ATTR_SPREAD(data) != 0)
1073 		spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_HS);
1074 	spd_insert_map(si, SPD_KEY_MOD_NROWS, nrows,
1075 	    spd_ddr5_attr_nrows_map, ARRAY_SIZE(spd_ddr5_attr_nrows_map));
1076 	spd_insert_map(si, SPD_KEY_MOD_OPER_TEMP, otr,
1077 	    spd_ddr5_attr_otr_map, ARRAY_SIZE(spd_ddr5_attr_otr_map));
1078 }
1079 
1080 static const spd_value_range_t spd_ddr5_nrank_range = {
1081 	.svr_base = SPD_DDR5_COM_ORG_NRANK_BASE
1082 };
1083 
1084 static void
1085 spd_parse_ddr5_mod_org(spd_info_t *si, uint32_t off, uint32_t len,
1086     const char *key)
1087 {
1088 	const uint8_t data = si->si_data[off];
1089 	const uint8_t nranks = SPD_DDR4_MOD_ORG_NPKG_RANK(data);
1090 
1091 	if (SPD_DDR5_COM_ORG_MIX(data) == SPD_DDR5_COM_ORG_MIX_ASYM)
1092 		spd_nvl_insert_key(si, SPD_KEY_RANK_ASYM);
1093 	spd_insert_range(si, SPD_KEY_NRANKS, nranks, &spd_ddr5_nrank_range);
1094 }
1095 
1096 static const spd_value_map_t spd_ddr5_ext_width[] = {
1097 	{ SPD_DDR5_COM_BUS_WIDTH_EXT_NONE, 0, false },
1098 	{ SPD_DDR5_COM_BUS_WIDTH_EXT_4b, 4, false },
1099 	{ SPD_DDR5_COM_BUS_WIDTH_EXT_8b, 8, false }
1100 };
1101 
1102 static const spd_value_map_t spd_ddr5_pri_width[] = {
1103 	{ SPD_DDR5_COM_BUS_WIDTH_PRI_8b, 8, false },
1104 	{ SPD_DDR5_COM_BUS_WIDTH_PRI_16b, 16, false },
1105 	{ SPD_DDR5_COM_BUS_WIDTH_PRI_32b, 32, false },
1106 	{ SPD_DDR5_COM_BUS_WIDTH_PRI_64b, 64, false },
1107 };
1108 
1109 static const spd_value_range_t spd_ddr5_nsc_range = {
1110 	.svr_max = SPD_DDR5_COM_BUS_WIDTH_NSC_MAX,
1111 	.svr_base = SPD_DDR5_COM_BUS_WIDTH_NSC_BASE
1112 };
1113 
1114 static void
1115 spd_parse_ddr5_bus_width(spd_info_t *si, uint32_t off, uint32_t len,
1116     const char *key)
1117 {
1118 	const uint8_t data = si->si_data[off];
1119 	const uint8_t nsc = SPD_DDR5_COM_BUS_WIDTH_NSC(data);
1120 	const uint8_t ext = SPD_DDR5_COM_BUS_WIDTH_EXT(data);
1121 	const uint8_t pri = SPD_DDR5_COM_BUS_WIDTH_PRI(data);
1122 
1123 	spd_insert_range(si, SPD_KEY_NSUBCHAN, nsc, &spd_ddr5_nsc_range);
1124 	spd_insert_map(si, SPD_KEY_ECC_WIDTH, ext, spd_ddr5_ext_width,
1125 	    ARRAY_SIZE(spd_ddr5_ext_width));
1126 	spd_insert_map(si, SPD_KEY_DATA_WIDTH, pri, spd_ddr5_pri_width,
1127 	    ARRAY_SIZE(spd_ddr5_pri_width));
1128 }
1129 
1130 static const spd_parse_t spd_ddr5_module[] = {
1131 	{ .sp_off = SPD_DDR5_COM_REV, .sp_parse = spd_parse_ddr5_mod_rev },
1132 	{ .sp_off = SPD_DDR5_COM_HASH, .sp_parse = spd_parse_ddr5_hash_seq,
1133 	    .sp_key = SPD_KEY_HASH_SEQ },
1134 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_SPD, .sp_len = 4,
1135 	    .sp_parse = spd_parse_ddr5_spd },
1136 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_PMIC0, .sp_len = 4,
1137 	    .sp_parse = spd_parse_ddr5_pmic0 },
1138 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_PMIC1, .sp_len = 4,
1139 	    .sp_parse = spd_parse_ddr5_pmic1 },
1140 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_PMIC2, .sp_len = 4,
1141 	    .sp_parse = spd_parse_ddr5_pmic2 },
1142 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_TS, .sp_len = 4,
1143 	    .sp_parse = spd_parse_ddr5_ts },
1144 	{ .sp_off = SPD_DDR5_COM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
1145 	    .sp_parse = spd_parse_height },
1146 	{ .sp_off = SPD_DDR5_COM_THICK, .sp_parse = spd_parse_thickness },
1147 	{ .sp_off = SPD_DDR5_COM_REF, .sp_parse = spd_parse_ddr5_design },
1148 	{ .sp_off = SPD_DDR5_COM_ATTR, .sp_parse = spd_parse_ddr5_attr },
1149 	{ .sp_off = SPD_DDR5_COM_ORG, .sp_parse = spd_parse_ddr5_mod_org },
1150 	{ .sp_off = SPD_DDR5_COM_BUS_WIDTH,
1151 	    .sp_parse = spd_parse_ddr5_bus_width },
1152 	/* We include the DDR5 CRC in this group as it's considered common */
1153 	{ .sp_len = SPD_DDR5_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR5,
1154 	    .sp_parse = spd_parse_crc },
1155 };
1156 
1157 static const spd_parse_t spd_ddr5_mfg[] = {
1158 	{ .sp_off = SPD_DDR5_MOD_MFG_ID0, .sp_len = 2,
1159 	    .sp_key = SPD_KEY_MFG_MOD_MFG_ID, .sp_parse = spd_parse_jedec_id },
1160 	{ .sp_off = SPD_DDR5_MOD_MFG_ID0, .sp_len = 2,
1161 	    .sp_key = SPD_KEY_MFG_MOD_MFG_NAME,
1162 	    .sp_parse = spd_parse_jedec_id_str },
1163 	{ .sp_off = SPD_DDR5_DRAM_MFG_ID0, .sp_len = 2,
1164 	    .sp_key = SPD_KEY_MFG_DRAM_MFG_ID, .sp_parse = spd_parse_jedec_id },
1165 	{ .sp_off = SPD_DDR5_DRAM_MFG_ID0, .sp_len = 2,
1166 	    .sp_key = SPD_KEY_MFG_DRAM_MFG_NAME,
1167 	    .sp_parse = spd_parse_jedec_id_str },
1168 	{ .sp_off = SPD_DDR5_MOD_MFG_LOC, .sp_key = SPD_KEY_MFG_MOD_LOC_ID,
1169 	    .sp_parse = spd_parse_raw_u8 },
1170 	{ .sp_off = SPD_DDR5_MOD_MFG_YEAR, .sp_key = SPD_KEY_MFG_MOD_YEAR,
1171 	    .sp_parse = spd_parse_hex_string },
1172 	{ .sp_off = SPD_DDR5_MOD_MFG_WEEK, .sp_key = SPD_KEY_MFG_MOD_WEEK,
1173 	    .sp_parse = spd_parse_hex_string },
1174 	{ .sp_off = SPD_DDR5_MOD_SN, .sp_len = SPD_DDR5_MOD_SN_LEN,
1175 	    .sp_key = SPD_KEY_MFG_MOD_SN, .sp_parse = spd_parse_hex_string },
1176 	{ .sp_off = SPD_DDR5_MOD_PN, .sp_len = SPD_DDR5_MOD_PN_LEN,
1177 	    .sp_key = SPD_KEY_MFG_MOD_PN, .sp_parse = spd_parse_string },
1178 	{ .sp_off = SPD_DDR5_MOD_REV, .sp_key = SPD_KEY_MFG_MOD_REV,
1179 	    .sp_parse = spd_parse_dram_step },
1180 	{ .sp_off = SPD_DDR5_DRAM_STEP, .sp_key = SPD_KEY_MFG_DRAM_STEP,
1181 	    .sp_parse = spd_parse_dram_step }
1182 };
1183 
1184 /*
1185  * Annex A.2 UDIMM and SODIMM specific processing.
1186  */
1187 
1188 static const spd_value_map_t spd_ddr5_cd_type_map[] = {
1189 	{ SPD_DDR5_UDIMM_INFO_TYPE_DDR5CK01, SPD_CD_T_DDR5CK01, false }
1190 };
1191 
1192 static void
1193 spd_parse_ddr5_udimm_cd(spd_info_t *si, uint32_t off, uint32_t len,
1194     const char *key)
1195 {
1196 	ASSERT3U(len, ==, 4);
1197 	const uint8_t type = si->si_data[off + 2];
1198 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1199 		return;
1200 
1201 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_CD,
1202 	    SPD_KEY_DEV_CD_MFG, SPD_KEY_DEV_CD_MFG_NAME,
1203 	    SPD_KEY_DEV_CD_REV, SPD_KEY_DEV_CD_TYPE,
1204 	    spd_ddr5_cd_type_map, ARRAY_SIZE(spd_ddr5_cd_type_map));
1205 }
1206 
1207 static const spd_parse_t spd_ddr5_udimm[] = {
1208 	{ .sp_off = SPD_DDR5_COM_MFG_ID0_TS, .sp_len = 4,
1209 	    .sp_parse = spd_parse_ddr5_udimm_cd }
1210 };
1211 
1212 /*
1213  * Annex A.3 RDIMM and LRDIMM specific processing. Because certain fields are
1214  * LRDIMM-only, we use two different top-level tables to drive them; however,
1215  * they generally overlap otherwise. Items that are LRDIMM only will contain
1216  * lrdimm in the name. All items named rdimm are shared between both the LRDIMM
1217  * and RDIMM processing.
1218  */
1219 static const spd_value_map_t spd_ddr5_rcd_type_map[] = {
1220 	{ SPD_DDR5_RDIMM_INFO_TYPE_RCD01, SPD_RCD_T_DDR5RCD01, false },
1221 	{ SPD_DDR5_RDIMM_INFO_TYPE_RCD02, SPD_RCD_T_DDR5RCD02, false },
1222 	{ SPD_DDR5_RDIMM_INFO_TYPE_RCD03, SPD_RCD_T_DDR5RCD03, false }
1223 };
1224 
1225 static const spd_value_map_t spd_ddr5_db_type_map[] = {
1226 	{ SPD_DDR5_RDIMM_INFO_TYPE_DB01, SPD_DB_T_DDR5DB01, false },
1227 	{ SPD_DDR5_RDIMM_INFO_TYPE_DB02, SPD_DB_T_DDR5DB02, false }
1228 };
1229 
1230 static void
1231 spd_parse_ddr5_rdimm_rcd(spd_info_t *si, uint32_t off, uint32_t len,
1232     const char *key)
1233 {
1234 	ASSERT3U(len, ==, 4);
1235 	const uint8_t type = si->si_data[off + 2];
1236 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1237 		return;
1238 
1239 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_RCD,
1240 	    SPD_KEY_DEV_RCD_MFG, SPD_KEY_DEV_RCD_MFG_NAME,
1241 	    SPD_KEY_DEV_RCD_REV, SPD_KEY_DEV_RCD_TYPE,
1242 	    spd_ddr5_rcd_type_map, ARRAY_SIZE(spd_ddr5_rcd_type_map));
1243 }
1244 
1245 static void
1246 spd_parse_ddr5_lrdimm_db(spd_info_t *si, uint32_t off, uint32_t len,
1247     const char *key)
1248 {
1249 	ASSERT3U(len, ==, 4);
1250 	const uint8_t type = si->si_data[off + 2];
1251 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1252 		return;
1253 
1254 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_DB,
1255 	    SPD_KEY_DEV_DB_MFG, SPD_KEY_DEV_DB_MFG_NAME,
1256 	    SPD_KEY_DEV_DB_REV, SPD_KEY_DEV_DB_TYPE,
1257 	    spd_ddr5_db_type_map, ARRAY_SIZE(spd_ddr5_db_type_map));
1258 }
1259 
1260 static void
1261 spd_parse_ddr5_rdimm_clken(spd_info_t *si, uint32_t off, uint32_t len,
1262     const char *key)
1263 {
1264 	const uint8_t data = si->si_data[off];
1265 
1266 	if (SPD_DDR5_RDIMM_CLKEN_QACK(data) == 0)
1267 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QACK_EN);
1268 	if (SPD_DDR5_RDIMM_CLKEN_QBCK(data) == 0)
1269 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QBCK_EN);
1270 	if (SPD_DDR5_RDIMM_CLKEN_QCCK(data) == 0)
1271 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QCCK_EN);
1272 	if (SPD_DDR5_RDIMM_CLKEN_QDCK(data) == 0)
1273 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QDCK_EN);
1274 	if (SPD_DDR5_RDIMM_CLKEN_BCK(data) == 0)
1275 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_BCK_EN);
1276 }
1277 
1278 static void
1279 spd_parse_ddr5_rdimm_rwen(spd_info_t *si, uint32_t off, uint32_t len,
1280     const char *key)
1281 {
1282 	const uint8_t data = si->si_data[off];
1283 
1284 	if (SPD_DDR5_RDIMM_RW09_QBCS(data) == 0)
1285 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QBCS_EN);
1286 	if (SPD_DDR5_RDIMM_RW09_QACS(data) == 0)
1287 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QACS_EN);
1288 	if (SPD_DDR5_RDIMM_RW09_QXCA13(data) == 0)
1289 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QxCA13_EN);
1290 	if (SPD_DDR5_RDIMM_RW09_BCS(data) == 0)
1291 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_BCS_EN);
1292 	if (SPD_DDR5_RDIMM_RW09_DCS(data) == 0)
1293 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_DCS1_EN);
1294 	if (SPD_DDR5_RDIMM_RW09_QBCA(data) == 0)
1295 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QBCA_EN);
1296 	if (SPD_DDR5_RDIMM_RW09_QACA(data) == 0)
1297 		spd_nvl_insert_key(si, SPD_KEY_DDR5_RCD_QACA_EN);
1298 }
1299 
1300 static const spd_value_map_t spd_ddr5_imp_map[] = {
1301 	{ SPD_DDR5_RDIMM_DRV_20R, 20, false },
1302 	{ SPD_DDR5_RDIMM_DRV_14R, 14, false },
1303 	{ SPD_DDR5_RDIMM_DRV_10R, 10, false }
1304 };
1305 
1306 static void
1307 spd_parse_ddr5_rdimm_clkimp(spd_info_t *si, uint32_t off, uint32_t len,
1308     const char *key)
1309 {
1310 	const uint8_t data = si->si_data[off];
1311 	const uint8_t qack = SPD_DDR5_RDIMM_QCK_DRV_QACK(data);
1312 	const uint8_t qbck = SPD_DDR5_RDIMM_QCK_DRV_QBCK(data);
1313 	const uint8_t qcck = SPD_DDR5_RDIMM_QCK_DRV_QCCK(data);
1314 	const uint8_t qdck = SPD_DDR5_RDIMM_QCK_DRV_QDCK(data);
1315 
1316 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QACK_IMP, qack, spd_ddr5_imp_map,
1317 	    ARRAY_SIZE(spd_ddr5_imp_map));
1318 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QBCK_IMP, qbck, spd_ddr5_imp_map,
1319 	    ARRAY_SIZE(spd_ddr5_imp_map));
1320 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QCCK_IMP, qcck, spd_ddr5_imp_map,
1321 	    ARRAY_SIZE(spd_ddr5_imp_map));
1322 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QDCK_IMP, qdck, spd_ddr5_imp_map,
1323 	    ARRAY_SIZE(spd_ddr5_imp_map));
1324 }
1325 
1326 static void
1327 spd_parse_ddr5_rdimm_casimp(spd_info_t *si, uint32_t off, uint32_t len,
1328     const char *key)
1329 {
1330 	const uint8_t data = si->si_data[off];
1331 	const uint8_t cs = SPD_DDR5_RDIMM_QCA_DRV_CS(data);
1332 	const uint8_t ca = SPD_DDR5_RDIMM_QCA_DRV_CA(data);
1333 
1334 	spd_insert_map(si, SPD_KEY_DDR5_RCD_CS_IMP, cs, spd_ddr5_imp_map,
1335 	    ARRAY_SIZE(spd_ddr5_imp_map));
1336 	spd_insert_map(si, SPD_KEY_DDR5_RCD_CA_IMP, ca, spd_ddr5_imp_map,
1337 	    ARRAY_SIZE(spd_ddr5_imp_map));
1338 }
1339 
1340 /*
1341  * Unlike the other impedence values the BCOM signal does not allow a 10 Ohm
1342  * value.
1343  */
1344 static const spd_value_map_t spd_ddr5_bcom_map[] = {
1345 	{ SPD_DDR5_RDIMM_DRV_20R, 20, false },
1346 	{ SPD_DDR5_RDIMM_DRV_14R, 14, false }
1347 };
1348 
1349 static void
1350 spd_parse_ddr5_lrdimm_dbimp(spd_info_t *si, uint32_t off, uint32_t len,
1351     const char *key)
1352 {
1353 	const uint8_t data = si->si_data[off];
1354 	const uint8_t bck = SPD_DDR5_LRDIMM_DB_DRV_BCK(data);
1355 	const uint8_t bcom = SPD_DDR5_LRDIMM_DB_DRV_BCOM(data);
1356 
1357 	spd_insert_map(si, SPD_KEY_DDR5_RCD_BCK_IMP, bck, spd_ddr5_imp_map,
1358 	    ARRAY_SIZE(spd_ddr5_imp_map));
1359 	spd_insert_map(si, SPD_KEY_DDR5_RCD_BCOM_IMP, bcom, spd_ddr5_bcom_map,
1360 	    ARRAY_SIZE(spd_ddr5_bcom_map));
1361 }
1362 
1363 /*
1364  * Some slew rates only allow moderate and fast, others also allow slow. We use
1365  * different definitions to capture the allowed sets.
1366  */
1367 static const spd_value_map_t spd_ddr5_mfslew_map[] = {
1368 	{ SPD_DDR5_RDIMM_SLEW_MODERTE, SPD_SLEW_MODERATE, false },
1369 	{ SPD_DDR5_RDIMM_SLEW_FAST, SPD_SLEW_FAST, false }
1370 };
1371 
1372 static const spd_value_map_t spd_ddr5_smfslew_map[] = {
1373 	{ SPD_DDR5_RDIMM_SLEW_MODERTE, SPD_SLEW_MODERATE, false },
1374 	{ SPD_DDR5_RDIMM_SLEW_FAST, SPD_SLEW_FAST, false },
1375 	{ SPD_DDR5_RDIMM_SLEW_SLOW, SPD_SLEW_SLOW, false }
1376 };
1377 
1378 static void
1379 spd_parse_ddr5_rdimm_qslew(spd_info_t *si, uint32_t off, uint32_t len,
1380     const char *key)
1381 {
1382 	const uint8_t data = si->si_data[off];
1383 	const uint8_t qcs = SPD_DDR5_RDIMM_QXX_SLEW_QCS(data);
1384 	const uint8_t qca = SPD_DDR5_RDIMM_QXX_SLEW_QCA(data);
1385 	const uint8_t qck = SPD_DDR5_RDIMM_QXX_SLEW_QCK(data);
1386 
1387 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QCK_SLEW, qck, spd_ddr5_mfslew_map,
1388 	    ARRAY_SIZE(spd_ddr5_mfslew_map));
1389 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QCA_SLEW, qca, spd_ddr5_smfslew_map,
1390 	    ARRAY_SIZE(spd_ddr5_smfslew_map));
1391 	spd_insert_map(si, SPD_KEY_DDR5_RCD_QCS_SLEW, qcs, spd_ddr5_smfslew_map,
1392 	    ARRAY_SIZE(spd_ddr5_smfslew_map));
1393 }
1394 
1395 static void
1396 spd_parse_ddr5_lrdimm_bslew(spd_info_t *si, uint32_t off, uint32_t len,
1397     const char *key)
1398 {
1399 	const uint8_t data = si->si_data[off];
1400 	const uint8_t bck = SPD_DDR5_LRDIMM_BXX_SLEW_BCK(data);
1401 	const uint8_t bcom = SPD_DDR5_LRDIMM_BXX_SLEW_BCOM(data);
1402 
1403 	spd_insert_map(si, SPD_KEY_DDR5_RCD_BCK_SLEW, bck, spd_ddr5_mfslew_map,
1404 	    ARRAY_SIZE(spd_ddr5_mfslew_map));
1405 	spd_insert_map(si, SPD_KEY_DDR5_RCD_BCOM_SLEW, bcom,
1406 	    spd_ddr5_smfslew_map, ARRAY_SIZE(spd_ddr5_smfslew_map));
1407 }
1408 
1409 static const spd_value_map_t spd_ddr5_rtt_term_map[] = {
1410 	{ SPD_DDR5_LDRIMM_PARK_OFF, 0, true },
1411 	{ SPD_DDR5_LDRIMM_PARK_240R, 240, false },
1412 	{ SPD_DDR5_LDRIMM_PARK_120R, 120, false },
1413 	{ SPD_DDR5_LDRIMM_PARK_80R, 80, false },
1414 	{ SPD_DDR5_LDRIMM_PARK_60R, 60, false },
1415 	{ SPD_DDR5_LDRIMM_PARK_48R, 48, false },
1416 	{ SPD_DDR5_LDRIMM_PARK_40R, 40, false },
1417 	{ SPD_DDR5_LDRIMM_PARK_34R, 34, false }
1418 };
1419 
1420 static void
1421 spd_parse_ddr5_lrdimm_rtt(spd_info_t *si, uint32_t off, uint32_t len,
1422     const char *key)
1423 {
1424 	const uint8_t data = si->si_data[off];
1425 	const uint8_t rtt = SPD_DDR5_LRDIMM_PARK_TERM(data);
1426 
1427 	spd_insert_map(si, SPD_KEY_DDR5_RCD_RTT_TERM, rtt,
1428 	    spd_ddr5_rtt_term_map, ARRAY_SIZE(spd_ddr5_rtt_term_map));
1429 }
1430 
1431 static const spd_parse_t spd_ddr5_rdimm[] = {
1432 	{ .sp_off = SPD_DDR5_RDIMM_MFG_ID0_RCD, .sp_len = 4,
1433 	    .sp_parse = spd_parse_ddr5_rdimm_rcd },
1434 	{ .sp_off = SPD_DDR5_RDIMM_CLKEN,
1435 	    .sp_parse = spd_parse_ddr5_rdimm_clken },
1436 	{ .sp_off = SPD_DDR5_RDIMM_RW09,
1437 	    .sp_parse = spd_parse_ddr5_rdimm_rwen },
1438 	{ .sp_off = SPD_DDR5_RDIMM_QCK_DRV,
1439 	    .sp_parse = spd_parse_ddr5_rdimm_clkimp },
1440 	{ .sp_off = SPD_DDR5_RDIMM_QCA_DRV,
1441 	    .sp_parse = spd_parse_ddr5_rdimm_casimp },
1442 	{ .sp_off = SPD_DDR5_RDIMM_QXX_SLEW,
1443 	    .sp_parse = spd_parse_ddr5_rdimm_qslew }
1444 };
1445 
1446 static const spd_parse_t spd_ddr5_lrdimm[] = {
1447 	{ .sp_off = SPD_DDR5_RDIMM_MFG_ID0_RCD, .sp_len = 4,
1448 	    .sp_parse = spd_parse_ddr5_rdimm_rcd },
1449 	{ .sp_off = SPD_DDR5_RDIMM_MFG_ID0_DB, .sp_len = 4,
1450 	    .sp_parse = spd_parse_ddr5_lrdimm_db },
1451 	{ .sp_off = SPD_DDR5_RDIMM_CLKEN,
1452 	    .sp_parse = spd_parse_ddr5_rdimm_clken },
1453 	{ .sp_off = SPD_DDR5_RDIMM_RW09,
1454 	    .sp_parse = spd_parse_ddr5_rdimm_rwen },
1455 	{ .sp_off = SPD_DDR5_RDIMM_QCK_DRV,
1456 	    .sp_parse = spd_parse_ddr5_rdimm_clkimp },
1457 	{ .sp_off = SPD_DDR5_RDIMM_QCA_DRV,
1458 	    .sp_parse = spd_parse_ddr5_rdimm_casimp },
1459 	{ .sp_off = SPD_DDR5_LRDIMM_DB_DRV,
1460 	    .sp_parse = spd_parse_ddr5_lrdimm_dbimp },
1461 	{ .sp_off = SPD_DDR5_RDIMM_QXX_SLEW,
1462 	    .sp_parse = spd_parse_ddr5_rdimm_qslew },
1463 	{ .sp_off = SPD_DDR5_LRDIMM_BXX_SLEW,
1464 	    .sp_parse = spd_parse_ddr5_lrdimm_bslew },
1465 	{ .sp_off = SPD_DDR5_LRDIMM_PARK,
1466 	    .sp_parse = spd_parse_ddr5_lrdimm_rtt },
1467 };
1468 
1469 /*
1470  * Annex A.4 MRDIMM specific processing.
1471  */
1472 static const spd_value_map_t spd_ddr5_mrcd_type_map[] = {
1473 	{ SPD_DDR5_MRDIMM_INFO_TYPE_MRCD01, SPD_MRCD_T_DDR5MRCD01, false }
1474 };
1475 
1476 static const spd_value_map_t spd_ddr5_mdb_type_map[] = {
1477 	{ SPD_DDR5_MRDIMM_INFO_TYPE_MDB01, SPD_MDB_T_DDR5MDB01, false }
1478 };
1479 
1480 static void
1481 spd_parse_ddr5_mrdimm_mrcd(spd_info_t *si, uint32_t off, uint32_t len,
1482     const char *key)
1483 {
1484 	ASSERT3U(len, ==, 4);
1485 	const uint8_t type = si->si_data[off + 2];
1486 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1487 		return;
1488 
1489 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_MRCD,
1490 	    SPD_KEY_DEV_MRCD_MFG, SPD_KEY_DEV_MRCD_MFG_NAME,
1491 	    SPD_KEY_DEV_MRCD_REV, SPD_KEY_DEV_MRCD_TYPE,
1492 	    spd_ddr5_mrcd_type_map, ARRAY_SIZE(spd_ddr5_mrcd_type_map));
1493 }
1494 
1495 static void
1496 spd_parse_ddr5_mrdimm_mdb(spd_info_t *si, uint32_t off, uint32_t len,
1497     const char *key)
1498 {
1499 	ASSERT3U(len, ==, 4);
1500 	const uint8_t type = si->si_data[off + 2];
1501 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1502 		return;
1503 
1504 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_MDB,
1505 	    SPD_KEY_DEV_MDB_MFG, SPD_KEY_DEV_MDB_MFG_NAME,
1506 	    SPD_KEY_DEV_MDB_REV, SPD_KEY_DEV_MDB_TYPE,
1507 	    spd_ddr5_mdb_type_map, ARRAY_SIZE(spd_ddr5_mdb_type_map));
1508 }
1509 
1510 static const spd_parse_t spd_ddr5_mrdimm[] = {
1511 	{ .sp_off = SPD_DDR5_MRDIMM_MFG_ID0_MRCD, .sp_len = 4,
1512 	    .sp_parse = spd_parse_ddr5_mrdimm_mrcd },
1513 	{ .sp_off = SPD_DDR5_MRDIMM_MFG_ID0_MDB, .sp_len = 4,
1514 	    .sp_parse = spd_parse_ddr5_mrdimm_mdb }
1515 };
1516 
1517 /*
1518  * Annex A.5 Differential Memory Module processing.
1519  */
1520 static const spd_value_map_t spd_ddr5_dmb_type_map[] = {
1521 	{ SPD_DDR5_DDIMM_INFO_TYPE_DMB501, SPD_DMB_T_DMB5011, false }
1522 };
1523 
1524 static void
1525 spd_parse_ddr5_ddimm_dmb(spd_info_t *si, uint32_t off, uint32_t len,
1526     const char *key)
1527 {
1528 	ASSERT3U(len, ==, 4);
1529 	const uint8_t type = si->si_data[off + 2];
1530 	if (SPD_DDR5_COM_INFO_PRES(type) == 0)
1531 		return;
1532 
1533 	spd_parse_ddr5_dev_common(si, off, SPD_DEVICE_DMB,
1534 	    SPD_KEY_DEV_DMB_MFG, SPD_KEY_DEV_DMB_MFG_NAME,
1535 	    SPD_KEY_DEV_DMB_REV, SPD_KEY_DEV_DMB_TYPE,
1536 	    spd_ddr5_dmb_type_map, ARRAY_SIZE(spd_ddr5_dmb_type_map));
1537 }
1538 
1539 static const spd_parse_t spd_ddr5_ddimm[] = {
1540 	{ .sp_off = SPD_DDR5_DDIMM_MFG_ID0_DMB, .sp_len = 4,
1541 	    .sp_parse = spd_parse_ddr5_ddimm_dmb },
1542 };
1543 
1544 static void
1545 spd_parse_ddr5_mod_specific(spd_info_t *si)
1546 {
1547 	uint32_t type;
1548 
1549 	if (nvlist_lookup_uint32(si->si_nvl, SPD_KEY_MOD_TYPE, &type) != 0)
1550 		return;
1551 
1552 	switch (type) {
1553 	case SPD_MOD_TYPE_RDIMM:
1554 		spd_parse(si, spd_ddr5_rdimm, ARRAY_SIZE(spd_ddr5_rdimm));
1555 		break;
1556 	case SPD_MOD_TYPE_LRDIMM:
1557 		spd_parse(si, spd_ddr5_lrdimm, ARRAY_SIZE(spd_ddr5_lrdimm));
1558 		break;
1559 	case SPD_MOD_TYPE_UDIMM:
1560 	case SPD_MOD_TYPE_SODIMM:
1561 		spd_parse(si, spd_ddr5_udimm, ARRAY_SIZE(spd_ddr5_udimm));
1562 		break;
1563 	case SPD_MOD_TYPE_MRDIMM:
1564 		spd_parse(si, spd_ddr5_mrdimm, ARRAY_SIZE(spd_ddr5_mrdimm));
1565 		break;
1566 	case SPD_MOD_TYPE_DDIMM:
1567 		spd_parse(si, spd_ddr5_ddimm, ARRAY_SIZE(spd_ddr5_ddimm));
1568 		break;
1569 	/*
1570 	 * Soldered DIMMs don't have any data.
1571 	 */
1572 	case SPD_MOD_TYPE_SOLDER:
1573 	default:
1574 		break;
1575 	}
1576 }
1577 
1578 /*
1579  * DDR5 has two different revisions. One that is present in the base region and
1580  * one that is present in the common module region that covers the
1581  * module-related pieces. We check that both are present and go from there. We
1582  * may want to relax this in the future so that it's easier to just decode a
1583  * subset of this, but for the time being, we require both.
1584  */
1585 void
1586 spd_parse_ddr5(spd_info_t *si)
1587 {
1588 	if (SPD_DDR5_SPD_REV_ENC(si->si_data[SPD_DDR5_SPD_REV]) !=
1589 	    SPD_DDR5_SPD_REV_V1) {
1590 		si->si_error = LIBJEDEC_SPD_UNSUP_REV;
1591 		return;
1592 	}
1593 
1594 	if (si->si_nbytes <= SPD_DDR5_COM_REV) {
1595 		si->si_error = LIBJEDEC_SPD_TOOSHORT;
1596 		return;
1597 	}
1598 
1599 	if (SPD_DDR5_SPD_REV_ENC(si->si_data[SPD_DDR5_COM_REV]) !=
1600 	    SPD_DDR5_SPD_REV_V1) {
1601 		si->si_error = LIBJEDEC_SPD_UNSUP_REV;
1602 		return;
1603 	}
1604 
1605 	spd_parse(si, spd_ddr5_base, ARRAY_SIZE(spd_ddr5_base));
1606 	spd_parse(si, spd_ddr5_module, ARRAY_SIZE(spd_ddr5_module));
1607 	spd_parse(si, spd_ddr5_mfg, ARRAY_SIZE(spd_ddr5_mfg));
1608 	spd_parse_ddr5_mod_specific(si);
1609 }
1610