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
spd_parse_ddr5_nbytes(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mod_type(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_isassym(spd_info_t * si)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
spd_parse_ddr5_denpkg(spd_info_t * si,uint8_t data,const char * ndie_key,const char * den_key,const char * sl_key)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
spd_parse_ddr5_denpkg_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_denpkg_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_addr(spd_info_t * si,uint8_t data,const char * row_key,const char * col_key)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
spd_parse_ddr5_addr_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_addr_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_width(spd_info_t * si,uint8_t data,const char * key)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
spd_parse_ddr5_width_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_width_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_banks(spd_info_t * si,uint8_t data,const char * bg_key,const char * ba_key)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
spd_parse_ddr5_banks_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_banks_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_ppr(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_dca(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_flt(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_voltage(spd_info_t * si,uint8_t data,const char * key,uint32_t * mv,uint32_t nmv)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
spd_parse_ddr5_vdd(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_vddq(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_vpp(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_time(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_ps(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_ns(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_3ds_ns(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_nck(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_cas(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rfm_flags(spd_info_t * si,uint8_t rfm0,uint8_t rfm1,const char * key)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
spd_parse_ddr5_arfm_flags(spd_info_t * si,uint8_t rfm1,const char * key)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
spd_parse_ddr5_rfm_common(spd_info_t * si,uint8_t rfm0,uint8_t rfm1,const char * raaimt_key,const char * raaimt_fgr_key,const char * raammt_key,const char * raammt_fgr_key,const char * brc_cfg_key,const char * brc_sup_key,const char * raa_ctr_key)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
spd_parse_ddr5_rfm_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rfm_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfma_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfma_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfmb_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfmb_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfmc_pri(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_arfmc_sec(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mod_rev(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_hash_seq(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_dev_common(spd_info_t * si,uint32_t off,spd_device_t flags,const char * id_key,const char * id_str_key,const char * rev_key,const char * type_key,const spd_value_map_t * type_map,size_t ntypes)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
spd_parse_ddr5_spd(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_pmic0(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_pmic1(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_pmic2(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_ts(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_design(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_attr(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mod_org(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_bus_width(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_udimm_cd(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_rcd(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_lrdimm_db(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_clken(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_rwen(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_clkimp(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_casimp(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_lrdimm_dbimp(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_rdimm_qslew(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_lrdimm_bslew(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_lrdimm_rtt(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mrdimm_mrcd(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mrdimm_mdb(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_ddimm_dmb(spd_info_t * si,uint32_t off,uint32_t len,const char * key)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
spd_parse_ddr5_mod_specific(spd_info_t * si)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
spd_parse_ddr5(spd_info_t * si)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