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 2024 Oxide Computer Company
14 */
15
16 /*
17 * LPDDR4, LPDDR4X, and LPDDR3 SPD processing logic. For an overview of the
18 * processing design please see libjedec_spd.c. These have a similar design to
19 * DDR4 and leverages the existing manufacturing logic there.
20 */
21
22 #include <sys/sysmacros.h>
23 #include <sys/debug.h>
24 #include "libjedec_spd.h"
25
26 static const spd_value_map_t spd_lp4_nbytes_used_map[] = {
27 { SPD_LP4_NBYTES_USED_UNDEF, 0, true },
28 { SPD_LP4_NBYTES_USED_128, 128, false },
29 { SPD_LP4_NBYTES_USED_256, 256, false },
30 { SPD_LP4_NBYTES_USED_384, 384, false },
31 { SPD_LP4_NBYTES_USED_512, 512, false }
32 };
33
34 static const spd_value_map_t spd_lp4_nbytes_total_map[] = {
35 { SPD_LP4_NBYTES_TOTAL_UNDEF, 0, true },
36 { SPD_LP4_NBYTES_TOTAL_256, 256, false },
37 { SPD_LP4_NBYTES_TOTAL_512, 512, false }
38 };
39
40 static void
spd_parse_lp4_nbytes(spd_info_t * si,uint32_t off,uint32_t len,const char * key)41 spd_parse_lp4_nbytes(spd_info_t *si, uint32_t off, uint32_t len,
42 const char *key)
43 {
44 const uint8_t data = si->si_data[off];
45 const uint8_t used = SPD_LP4_NBYTES_USED(data);
46 const uint8_t total = SPD_LP4_NBYTES_TOTAL(data);
47
48 spd_insert_map(si, SPD_KEY_NBYTES_USED, used, spd_lp4_nbytes_used_map,
49 ARRAY_SIZE(spd_lp4_nbytes_used_map));
50 spd_insert_map(si, SPD_KEY_NBYTES_TOTAL, total,
51 spd_lp4_nbytes_total_map, ARRAY_SIZE(spd_lp4_nbytes_total_map));
52
53 /*
54 * Like with DDR4 there is no specific way to determine the type. We
55 * take our best guess based upon the size. A 256 byte EEPROM is most
56 * likely the EE1002 spec and the 512 byte EEPROM is the EE1004 as those
57 * are the defended sized.
58 */
59 spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_SPD);
60 if (total == SPD_LP4_NBYTES_TOTAL_256) {
61 spd_nvl_insert_u32(si, SPD_KEY_DEV_SPD_TYPE, SPD_SPD_T_EE1002);
62 } else if (total == SPD_LP4_NBYTES_TOTAL_512) {
63 spd_nvl_insert_u32(si, SPD_KEY_DEV_SPD_TYPE, SPD_SPD_T_EE1004);
64 }
65 }
66
67 /*
68 * Like DDR4 the value of zero is defined for extensions; however, there are no
69 * defined type extensions. As such we don't check for it.
70 */
71 static const spd_value_map_t spd_lp4_mod_type_map[] = {
72 { SPD_LP4_MOD_TYPE_TYPE_LPDIMM, SPD_MOD_TYPE_LPDIMM, false },
73 { SPD_LP4_MOD_TYPE_TYPE_SOLDER, SPD_MOD_TYPE_SOLDER, false }
74 };
75
76 static const spd_value_map_t spd_lp4_mod_is_hybrid_map[] = {
77 { 0, SPD_MOD_NOT_HYBRID, false },
78 { 1, SPD_MOD_HYBRID_NVDIMMM, false }
79 };
80
81 static void
spd_parse_lp4_mod_type(spd_info_t * si,uint32_t off,uint32_t len,const char * key)82 spd_parse_lp4_mod_type(spd_info_t *si, uint32_t off, uint32_t len,
83 const char *key)
84 {
85 const uint8_t data = si->si_data[off];
86 const uint8_t type = SPD_LP4_MOD_TYPE_TYPE(data);
87 const uint8_t is_hyb = SPD_LP4_MOD_TYPE_ISHYBRID(data);
88
89 spd_insert_map(si, SPD_KEY_MOD_HYBRID_TYPE, is_hyb,
90 spd_lp4_mod_is_hybrid_map, ARRAY_SIZE(spd_lp4_mod_is_hybrid_map));
91
92 spd_insert_map(si, SPD_KEY_MOD_TYPE, type, spd_lp4_mod_type_map,
93 ARRAY_SIZE(spd_lp4_mod_type_map));
94 }
95
96 static const spd_value_map64_t spd_lp4_density_map[] = {
97 { SPD_LP4_DENSITY_DENSITY_1Gb, 1ULL * 1024ULL * 1024ULL * 1024ULL,
98 false },
99 { SPD_LP4_DENSITY_DENSITY_2Gb, 2ULL * 1024ULL * 1024ULL * 1024ULL,
100 false },
101 { SPD_LP4_DENSITY_DENSITY_4Gb, 4ULL * 1024ULL * 1024ULL * 1024ULL,
102 false },
103 { SPD_LP4_DENSITY_DENSITY_8Gb, 8ULL * 1024ULL * 1024ULL * 1024ULL,
104 false },
105 { SPD_LP4_DENSITY_DENSITY_16Gb, 16ULL * 1024ULL * 1024ULL * 1024ULL,
106 false },
107 { SPD_LP4_DENSITY_DENSITY_32Gb, 32ULL * 1024ULL * 1024ULL * 1024ULL,
108 false },
109 { SPD_LP4_DENSITY_DENSITY_12Gb, 12ULL * 1024ULL * 1024ULL * 1024ULL,
110 false },
111 { SPD_LP4_DENSITY_DENSITY_24Gb, 24ULL * 1024ULL * 1024ULL * 1024ULL,
112 false },
113 { SPD_LP4_DENSITY_DENSITY_3Gb, 3ULL * 1024ULL * 1024ULL * 1024ULL,
114 false },
115 { SPD_LP4_DENSITY_DENSITY_6Gb, 64ULL * 1024ULL * 1024ULL * 1024ULL,
116 false },
117 { SPD_LP4_DENSITY_DENSITY_18Gb, 18ULL * 1024ULL * 1024ULL * 1024ULL,
118 false },
119 };
120
121 static const spd_value_range_t spd_lp4_nbgrp_range = {
122 .svr_max = SPD_LP4_DENSITY_NBG_BITS_MAX
123 };
124
125 static const spd_value_range_t spd_lp4_nba_range = {
126 .svr_max = SPD_LP4_DENSITY_NBA_BITS_MAX,
127 .svr_base = SPD_LP4_DENSITY_NBA_BITS_BASE
128 };
129
130 static void
spd_parse_lp4_density(spd_info_t * si,uint32_t off,uint32_t len,const char * key)131 spd_parse_lp4_density(spd_info_t *si, uint32_t off, uint32_t len,
132 const char *key)
133 {
134 const uint8_t data = si->si_data[off];
135 const uint8_t nbg = SPD_LP4_DENSITY_NBG_BITS(data);
136 const uint8_t nbank = SPD_LP4_DENSITY_NBA_BITS(data);
137 const uint8_t dens = SPD_LP4_DENSITY_DENSITY(data);
138
139 spd_insert_range(si, SPD_KEY_NBGRP_BITS, nbg, &spd_lp4_nbgrp_range);
140 spd_insert_range(si, SPD_KEY_NBANK_BITS, nbank, &spd_lp4_nba_range);
141 spd_insert_map64(si, SPD_KEY_DIE_SIZE, dens, spd_lp4_density_map,
142 ARRAY_SIZE(spd_lp4_density_map));
143 }
144
145 static const spd_value_range_t spd_lp4_nrow_range = {
146 .svr_max = SPD_LP4_ADDR_NROWS_MAX,
147 .svr_base = SPD_LP4_ADDR_NROWS_BASE
148 };
149
150 static const spd_value_range_t spd_lp4_ncol_range = {
151 .svr_max = SPD_LP4_ADDR_NCOLS_MAX,
152 .svr_base = SPD_LP4_ADDR_NCOLS_BASE
153 };
154
155 static void
spd_parse_lp4_addr(spd_info_t * si,uint32_t off,uint32_t len,const char * key)156 spd_parse_lp4_addr(spd_info_t *si, uint32_t off, uint32_t len,
157 const char *key)
158 {
159 const uint8_t data = si->si_data[off];
160 const uint8_t nrows = SPD_LP4_ADDR_NROWS(data);
161 const uint8_t ncols = SPD_LP4_ADDR_NCOLS(data);
162
163 spd_insert_range(si, SPD_KEY_NROW_BITS, nrows, &spd_lp4_nrow_range);
164 spd_insert_range(si, SPD_KEY_NCOL_BITS, ncols, &spd_lp4_ncol_range);
165 }
166
167 static const spd_value_range_t spd_lp4_ndie_range = {
168 .svr_base = SPD_LP4_PKG_DIE_CNT_BASE
169 };
170
171 static const spd_value_range_t spd_lp4_nchan_range = {
172 .svr_max = SPD_LP4_PKG_NCHAN_MAX,
173 .svr_exp = true
174 };
175
176 static void
spd_parse_lp4_pkg(spd_info_t * si,uint32_t off,uint32_t len,const char * key)177 spd_parse_lp4_pkg(spd_info_t *si, uint32_t off, uint32_t len,
178 const char *key)
179 {
180 const uint8_t data = si->si_data[off];
181 const uint8_t ndie = SPD_LP4_PKG_DIE_CNT(data);
182 const uint8_t nchan = SPD_LP4_PKG_NCHAN(data);
183
184
185 if (SPD_LP4_PKG_TYPE(data) == SPD_LP4_PKG_TYPE_NOT) {
186 spd_nvl_insert_key(si, SPD_KEY_PKG_NOT_MONO);
187 }
188
189 spd_insert_range(si, SPD_KEY_PKG_NDIE, ndie, &spd_lp4_ndie_range);
190 spd_insert_range(si, SPD_KEY_DRAM_NCHAN, nchan, &spd_lp4_nchan_range);
191 }
192
193 static const spd_value_map_t spd_lp4_maw_map[] = {
194 { SPD_LP4_OPT_FEAT_MAW_8192X, 8192, false },
195 { SPD_LP4_OPT_FEAT_MAW_4096X, 4096, false },
196 { SPD_LP4_OPT_FEAT_MAW_2048X, 2048, false }
197 };
198
199 static const spd_value_map_t spd_lp4_mac_map[] = {
200 { SPD_LP4_OPT_FEAT_MAC_UNTESTED, 0, true},
201 { SPD_LP4_OPT_FEAT_MAC_700K, 700000, false },
202 { SPD_LP4_OPT_FEAT_MAC_600K, 600000, false },
203 { SPD_LP4_OPT_FEAT_MAC_500K, 500000, false },
204 { SPD_LP4_OPT_FEAT_MAC_400K, 400000, false },
205 { SPD_LP4_OPT_FEAT_MAC_300K, 300000, false },
206 { SPD_LP4_OPT_FEAT_MAC_200K, 200000, false },
207 { SPD_LP4_OPT_FEAT_MAC_UNLIMITED, SPD_KEY_MAC_UNLIMITED, false }
208 };
209
210 static void
spd_parse_lp4_feat(spd_info_t * si,uint32_t off,uint32_t len,const char * key)211 spd_parse_lp4_feat(spd_info_t *si, uint32_t off, uint32_t len,
212 const char *key)
213 {
214 const uint8_t data = si->si_data[off];
215 const uint8_t maw = SPD_DDR4_OPT_FEAT_MAW(data);
216 const uint8_t mac = SPD_DDR4_OPT_FEAT_MAC(data);
217
218 spd_insert_map(si, SPD_KEY_MAW, maw, spd_lp4_maw_map,
219 ARRAY_SIZE(spd_lp4_maw_map));
220 spd_insert_map(si, SPD_KEY_MAC, mac, spd_lp4_mac_map,
221 ARRAY_SIZE(spd_lp4_mac_map));
222 }
223
224 static void
spd_parse_lp4_feat2(spd_info_t * si,uint32_t off,uint32_t len,const char * key)225 spd_parse_lp4_feat2(spd_info_t *si, uint32_t off, uint32_t len,
226 const char *key)
227 {
228 const uint8_t data = si->si_data[off];
229 const uint8_t ppr_sup = SPD_DDR4_OPT_FEAT2_PPR(data);
230 spd_ppr_flags_t flags = 0;
231
232 switch (ppr_sup) {
233 case SPD_LP4_OPT_FEAT2_PPR_1RPBG:
234 spd_nvl_insert_u32(si, SPD_KEY_PPR_GRAN,
235 SPD_PPR_GRAN_BANK_GROUP);
236 flags |= SPD_PPR_F_HARD_PPR;
237 break;
238 case SPD_LP4_OPT_FEAT2_PPR_NOTSUP:
239 /*
240 * No PPR, nothing to do.
241 */
242 return;
243 default:
244 /*
245 * Unknown PPR value.
246 */
247 spd_nvl_err(si, SPD_KEY_PPR, SPD_ERROR_NO_XLATE,
248 "encountered unknown value: 0x%x", ppr_sup);
249 return;
250 }
251
252 if (SPD_LP4_OPT_FEAT2_SOFT_PPR(data))
253 flags |= SPD_PPR_F_SOFT_PPR;
254 spd_nvl_insert_u32(si, SPD_KEY_PPR, flags);
255 }
256
257 static const spd_value_range_t spd_lp4_nrank_range = {
258 .svr_max = SPD_LP4_MOD_ORG_NPKG_RANK_MAX,
259 .svr_base = SPD_LP4_MOD_ORG_NPKG_RANK_BASE
260 };
261
262 static const spd_value_range_t spd_lp4_width_range = {
263 .svr_max = SPD_LP4_MOD_ORG_WIDTH_MAX,
264 .svr_base = SPD_LP4_MOD_ORG_WIDTH_BASE,
265 .svr_exp = true
266 };
267
268 static void
spd_parse_lp4_mod_org(spd_info_t * si,uint32_t off,uint32_t len,const char * key)269 spd_parse_lp4_mod_org(spd_info_t *si, uint32_t off, uint32_t len,
270 const char *key)
271 {
272 const uint8_t data = si->si_data[off];
273 const uint8_t byte = SPD_LP4_MOD_ORG_IDENT(data);
274 const uint8_t nrank = SPD_LP4_MOD_ORG_NPKG_RANK(data);
275 const uint8_t width = SPD_LP4_MOD_ORG_WIDTH(data);
276
277 if (byte == SPD_LP4_MOD_ORG_IDENT_BYTE) {
278 spd_nvl_insert_key(si, SPD_KEY_LP_BYTE_MODE);
279 }
280
281 spd_insert_range(si, SPD_KEY_NRANKS, nrank, &spd_lp4_nrank_range);
282 spd_insert_range(si, SPD_KEY_DRAM_WIDTH, width, &spd_lp4_width_range);
283
284 }
285
286 static const spd_value_map_t spd_lp4_chan_map[] = {
287 { SPD_LP4_BUS_WIDTH_NCHAN_1ch, 1, false },
288 { SPD_LP4_BUS_WIDTH_NCHAN_2ch, 2, false },
289 { SPD_LP4_BUS_WIDTH_NCHAN_3ch, 3, false },
290 { SPD_LP4_BUS_WIDTH_NCHAN_4ch, 4, false },
291 { SPD_LP4_BUS_WIDTH_NCHAN_8ch, 8, false }
292 };
293
294 static const spd_value_range_t spd_lp4_chan_width_range = {
295 .svr_base = SPD_LP4_BUS_WIDTH_PRI_BASE,
296 .svr_max = SPD_LP4_BUS_WIDTH_PRI_MAX,
297 .svr_exp = true
298 };
299
300 static void
spd_parse_lp4_bus_width(spd_info_t * si,uint32_t off,uint32_t len,const char * key)301 spd_parse_lp4_bus_width(spd_info_t *si, uint32_t off, uint32_t len,
302 const char *key)
303 {
304 const uint8_t data = si->si_data[off];
305 const uint8_t nchan = SPD_LP4_BUS_WIDTH_NCHAN(data);
306 const uint8_t ext = SPD_LP4_BUS_WIDTH_EXT(data);
307 const uint8_t width = SPD_LP4_BUS_WIDTH_PRI(data);
308
309 spd_insert_map(si, SPD_KEY_NSUBCHAN, nchan, spd_lp4_chan_map,
310 ARRAY_SIZE(spd_lp4_chan_map));
311
312 if (ext != SPD_LP4_BUS_WIDTH_EXT_NONE) {
313 spd_nvl_err(si, SPD_KEY_ECC_WIDTH, SPD_ERROR_NO_XLATE,
314 "encountered invalid bus width extension: 0x%x", ext);
315 } else {
316 spd_nvl_insert_u32(si, SPD_KEY_ECC_WIDTH, 0);
317 }
318
319 spd_insert_range(si, SPD_KEY_DATA_WIDTH, width,
320 &spd_lp4_chan_width_range);
321 }
322
323 static void
spd_parse_lp4_therm(spd_info_t * si,uint32_t off,uint32_t len,const char * key)324 spd_parse_lp4_therm(spd_info_t *si, uint32_t off, uint32_t len,
325 const char *key)
326 {
327 const uint8_t data = si->si_data[off];
328
329 if (SPD_LP4_MOD_THERM_PRES(data) != 0)
330 spd_upsert_flag(si, key, SPD_DEVICE_TEMP_1);
331 /*
332 * Like DDR4, LPDDR3/4 only define that this must be TSE2004av
333 * compliant.
334 */
335 spd_nvl_insert_u32(si, SPD_KEY_DEV_TEMP_TYPE, SPD_TEMP_T_TSE2004av);
336 }
337
338 static const spd_value_range_t spd_lp4_dsm_range = {
339 .svr_max = SPD_LP4_SIGLOAD1_DSM_LOAD_MAX,
340 .svr_exp = true
341 };
342
343 static const spd_value_range_t spd_lp4_cac_range = {
344 .svr_max = SPD_LP4_SIGLOAD1_CAC_LOAD_MAX,
345 .svr_exp = true
346 };
347
348 static const spd_value_range_t spd_lp4_cs_range = {
349 .svr_max = SPD_LP4_SIGLOAD1_CS_LOAD_MAX,
350 .svr_exp = true
351 };
352
353 static void
spd_parse_lp4_sigload(spd_info_t * si,uint32_t off,uint32_t len,const char * key)354 spd_parse_lp4_sigload(spd_info_t *si, uint32_t off, uint32_t len,
355 const char *key)
356 {
357 const uint8_t data = si->si_data[off];
358 const uint8_t dsm = SPD_LP5_SIGLOAD1_DSM_LOAD(data);
359 const uint8_t cac = SPD_LP5_SIGLOAD1_CAC_LOAD(data);
360 const uint8_t cs = SPD_LP5_SIGLOAD1_CS_LOAD(data);
361
362 spd_insert_range(si, SPD_KEY_LP_LOAD_DSM, dsm, &spd_lp4_dsm_range);
363 spd_insert_range(si, SPD_KEY_LP_LOAD_CAC, cac, &spd_lp4_cac_range);
364 spd_insert_range(si, SPD_KEY_LP_LOAD_CS, cs, &spd_lp4_cs_range);
365 }
366
367 static const spd_value_map_t spd_lp4_ts_mtb[] = {
368 { SPD_LP4_TIMEBASE_MTB_125ps, SPD_LP4_MTB_PS, false }
369 };
370
371 static const spd_value_map_t spd_lp4_ts_ftb[] = {
372 { SPD_LP4_TIMEBASE_FTB_1ps, SPD_LP4_FTB_PS, false }
373 };
374
375 static void
spd_parse_lp4_timebase(spd_info_t * si,uint32_t off,uint32_t len,const char * key)376 spd_parse_lp4_timebase(spd_info_t *si, uint32_t off, uint32_t len,
377 const char *key)
378 {
379 const uint8_t data = si->si_data[off];
380 const uint8_t mtb = SPD_LP4_TIMEBASE_MTB(data);
381 const uint8_t ftb = SPD_LP4_TIMEBASE_FTB(data);
382
383 spd_insert_map(si, SPD_KEY_MTB, mtb, spd_lp4_ts_mtb,
384 ARRAY_SIZE(spd_lp4_ts_mtb));
385 spd_insert_map(si, SPD_KEY_FTB, ftb, spd_lp4_ts_ftb,
386 ARRAY_SIZE(spd_lp4_ts_ftb));
387 }
388
389 /*
390 * The first byte of CAS values is non-uniform. The second byte onwards begins
391 * at CL=16 and each bit indicates a CL two apart. We use this array of all CAS
392 * values as most of them aren't actually defined by the spec.
393 */
394 static const uint32_t spd_lp4_cas_map[32] = {
395 [0] = 3,
396 [1] = 6,
397 [2] = 8,
398 [3] = 9,
399 [4] = 10,
400 [5] = 11,
401 [6] = 12,
402 [7] = 14,
403 [8] = 16,
404 [9] = 18,
405 [10] = 20,
406 [11] = 22,
407 [12] = 24,
408 [13] = 26,
409 [14] = 28,
410 [15] = 30,
411 [16] = 32,
412 [18] = 36,
413 [20] = 40,
414 [22] = 44
415 };
416
417 static void
spd_parse_lp4_cas(spd_info_t * si,uint32_t off,uint32_t len,const char * key)418 spd_parse_lp4_cas(spd_info_t *si, uint32_t off, uint32_t len,
419 const char *key)
420 {
421 uint32_t cas[32] = { 0 };
422 uint_t ncas = 0;
423
424 ASSERT3U(len, ==, 4);
425 for (uint32_t byte = 0; byte < MIN(len, 4); byte++) {
426 uint32_t data = si->si_data[off + byte];
427 uint32_t nbits = NBBY;
428
429 for (uint32_t i = 0; i < nbits; i++) {
430 if (bitx8(data, i, i) == 1) {
431 uint8_t pos = i + byte * NBBY;
432 if (spd_lp4_cas_map[pos] != 0) {
433 cas[ncas] = spd_lp4_cas_map[pos];
434 ncas++;
435 } else {
436 spd_nvl_err(si, key, SPD_ERROR_BAD_DATA,
437 "invalid CAS byte %u/bit %u found",
438 byte, i);
439 return;
440 }
441 }
442 }
443 }
444
445 spd_nvl_insert_u32_array(si, key, cas, ncas);
446 }
447
448 static void
spd_parse_lp4_rwlat(spd_info_t * si,uint32_t off,uint32_t len,const char * key)449 spd_parse_lp4_rwlat(spd_info_t *si, uint32_t off, uint32_t len,
450 const char *key)
451 {
452 const uint8_t data = si->si_data[off];
453 const uint8_t wr = SPD_LP4_RWLAT_WRITE(data);
454 const uint8_t rd = SPD_LP4_RWLAT_READ(data);
455 spd_lp_rwlat_t rwlat = 0;
456
457 switch (wr) {
458 case SPD_LP4_RWLAT_WRITE_A:
459 rwlat |= SPD_LP_RWLAT_WRITE_A;
460 break;
461 case SPD_LP4_RWLAT_WRITE_B:
462 rwlat |= SPD_LP_RWLAT_WRITE_B;
463 break;
464 default:
465 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "unknown write "
466 "latency set value: 0x%x", wr);
467 return;
468 }
469
470 switch (rd) {
471 case SPD_LP4_RWLAT_DBIRD_DIS:
472 break;
473 case SPD_LP4_RWLAT_DBIRD_EN:
474 rwlat |= SPD_LP_RWLAT_DBIRD_EN;
475 break;
476 default:
477 spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "unknown read "
478 "latency mode value: 0x%x", rd);
479 return;
480 }
481
482 spd_nvl_insert_u32(si, key, rwlat);
483 }
484
485 static const spd_parse_t spd_lp4_base[] = {
486 { .sp_off = SPD_LP4_NBYTES, .sp_parse = spd_parse_lp4_nbytes },
487 { .sp_off = SPD_LP4_SPD_REV, .sp_parse = spd_parse_rev },
488 /*
489 * We have previously validated that the DRAM type is something that we
490 * understand. We pass through the raw enum to users here.
491 */
492 { .sp_off = SPD_LP4_DRAM_TYPE, .sp_key = SPD_KEY_DRAM_TYPE,
493 .sp_parse = spd_parse_raw_u8 },
494
495 { .sp_off = SPD_LP4_MOD_TYPE, .sp_parse = spd_parse_lp4_mod_type },
496 { .sp_off = SPD_LP4_DENSITY, .sp_parse = spd_parse_lp4_density },
497 { .sp_off = SPD_LP4_ADDR, .sp_parse = spd_parse_lp4_addr },
498 { .sp_off = SPD_LP4_PKG, .sp_parse = spd_parse_lp4_pkg },
499 { .sp_off = SPD_LP4_OPT_FEAT, .sp_parse = spd_parse_lp4_feat },
500 { .sp_off = SPD_LP4_OPT_FEAT2, .sp_parse = spd_parse_lp4_feat2 },
501 { .sp_off = SPD_LP4_MOD_ORG, .sp_parse = spd_parse_lp4_mod_org },
502 { .sp_off = SPD_LP4_BUS_WIDTH, .sp_parse = spd_parse_lp4_bus_width },
503 { .sp_off = SPD_LP4_MOD_THERM, .sp_parse = spd_parse_lp4_therm },
504 { .sp_off = SPD_LP4_SIGLOAD, .sp_parse = spd_parse_lp4_sigload },
505 { .sp_off = SPD_LP4_TIMEBASE, .sp_parse = spd_parse_lp4_timebase },
506 { .sp_off = SPD_LP4_TCKAVG_MIN, .sp_key = SPD_KEY_TCKAVG_MIN,
507 .sp_len = SPD_LP4_TCKAVG_MIN_FINE - SPD_LP4_TCKAVG_MIN + 1,
508 .sp_parse = spd_parse_mtb_ftb_time_pair },
509 { .sp_off = SPD_LP4_TCKAVG_MAX, .sp_key = SPD_KEY_TCKAVG_MAX,
510 .sp_len = SPD_LP4_TCKAVG_MAX_FINE - SPD_LP4_TCKAVG_MAX + 1,
511 .sp_parse = spd_parse_mtb_ftb_time_pair },
512 { .sp_off = SPD_LP4_CAS_SUP0, .sp_key = SPD_KEY_CAS,
513 .sp_len = SPD_LP4_CAS_SUP3 - SPD_LP4_CAS_SUP0 + 1,
514 .sp_parse = spd_parse_lp4_cas },
515 { .sp_off = SPD_LP5_TAA_MIN, .sp_key = SPD_KEY_TAA_MIN,
516 .sp_len = SPD_LP5_TAA_MIN_FINE - SPD_LP5_TAA_MIN + 1,
517 .sp_parse = spd_parse_mtb_ftb_time_pair },
518 { .sp_off = SPD_LP4_RWLAT, .sp_key = SPD_KEY_LP_RWLAT,
519 .sp_parse = spd_parse_lp4_rwlat },
520 { .sp_off = SPD_LP5_TRCD_MIN, .sp_key = SPD_KEY_TRCD_MIN,
521 .sp_len = SPD_LP5_TRCD_MIN_FINE - SPD_LP5_TRCD_MIN + 1,
522 .sp_parse = spd_parse_mtb_ftb_time_pair },
523 { .sp_off = SPD_LP5_TRPAB_MIN, .sp_key = SPD_KEY_TRPAB_MIN,
524 .sp_len = SPD_LP5_TRPAB_MIN_FINE - SPD_LP5_TRPAB_MIN + 1,
525 .sp_parse = spd_parse_mtb_ftb_time_pair },
526 { .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
527 .sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
528 .sp_parse = spd_parse_mtb_ftb_time_pair },
529 { .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
530 .sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
531 .sp_parse = spd_parse_mtb_ftb_time_pair },
532 { .sp_off = SPD_LP4_TRFCAB_MIN_LO, .sp_key = SPD_KEY_TRFCAB_MIN,
533 .sp_len = 2, .sp_parse = spd_parse_mtb_pair },
534 { .sp_off = SPD_LP4_TRFCPB_MIN_LO, .sp_key = SPD_KEY_TRFCPB_MIN,
535 .sp_len = 2, .sp_parse = spd_parse_mtb_pair },
536 { .sp_off = SPD_LP4_MAP_DQ0, .sp_key = SPD_KEY_DDR4_MAP_DQ0,
537 .sp_parse = spd_parse_ddr4_nib_map },
538 { .sp_off = SPD_LP4_MAP_DQ4, .sp_key = SPD_KEY_DDR4_MAP_DQ4,
539 .sp_parse = spd_parse_ddr4_nib_map },
540 { .sp_off = SPD_LP4_MAP_DQ8, .sp_key = SPD_KEY_DDR4_MAP_DQ8,
541 .sp_parse = spd_parse_ddr4_nib_map },
542 { .sp_off = SPD_LP4_MAP_DQ12, .sp_key = SPD_KEY_DDR4_MAP_DQ12,
543 .sp_parse = spd_parse_ddr4_nib_map },
544 { .sp_off = SPD_LP4_MAP_DQ16, .sp_key = SPD_KEY_DDR4_MAP_DQ16,
545 .sp_parse = spd_parse_ddr4_nib_map },
546 { .sp_off = SPD_LP4_MAP_DQ20, .sp_key = SPD_KEY_DDR4_MAP_DQ20,
547 .sp_parse = spd_parse_ddr4_nib_map },
548 { .sp_off = SPD_LP4_MAP_DQ24, .sp_key = SPD_KEY_DDR4_MAP_DQ24,
549 .sp_parse = spd_parse_ddr4_nib_map },
550 { .sp_off = SPD_LP4_MAP_DQ28, .sp_key = SPD_KEY_DDR4_MAP_DQ28,
551 .sp_parse = spd_parse_ddr4_nib_map },
552 { .sp_off = SPD_LP4_MAP_CB0, .sp_key = SPD_KEY_DDR4_MAP_CB0,
553 .sp_parse = spd_parse_ddr4_nib_map },
554 { .sp_off = SPD_LP4_MAP_CB4, .sp_key = SPD_KEY_DDR4_MAP_CB4,
555 .sp_parse = spd_parse_ddr4_nib_map },
556 { .sp_off = SPD_LP4_MAP_DQ32, .sp_key = SPD_KEY_DDR4_MAP_DQ32,
557 .sp_parse = spd_parse_ddr4_nib_map },
558 { .sp_off = SPD_LP4_MAP_DQ36, .sp_key = SPD_KEY_DDR4_MAP_DQ36,
559 .sp_parse = spd_parse_ddr4_nib_map },
560 { .sp_off = SPD_LP4_MAP_DQ40, .sp_key = SPD_KEY_DDR4_MAP_DQ40,
561 .sp_parse = spd_parse_ddr4_nib_map },
562 { .sp_off = SPD_LP4_MAP_DQ44, .sp_key = SPD_KEY_DDR4_MAP_DQ44,
563 .sp_parse = spd_parse_ddr4_nib_map },
564 { .sp_off = SPD_LP4_MAP_DQ48, .sp_key = SPD_KEY_DDR4_MAP_DQ48,
565 .sp_parse = spd_parse_ddr4_nib_map },
566 { .sp_off = SPD_LP4_MAP_DQ52, .sp_key = SPD_KEY_DDR4_MAP_DQ52,
567 .sp_parse = spd_parse_ddr4_nib_map },
568 { .sp_off = SPD_LP4_MAP_DQ56, .sp_key = SPD_KEY_DDR4_MAP_DQ56,
569 .sp_parse = spd_parse_ddr4_nib_map },
570 { .sp_off = SPD_LP4_MAP_DQ60, .sp_key = SPD_KEY_DDR4_MAP_DQ60,
571 .sp_parse = spd_parse_ddr4_nib_map },
572 { .sp_len = SPD_DDR4_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR4_BASE,
573 .sp_parse = spd_parse_crc },
574 { .sp_len = SPD_LP4_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR4_BASE,
575 .sp_parse = spd_parse_crc },
576 };
577
578 static const spd_parse_t spd_lp4_lpdimm[] = {
579 { .sp_off = SPD_LP4_LPDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
580 .sp_parse = spd_parse_height },
581 { .sp_off = SPD_LP4_LPDIMM_THICK, .sp_parse = spd_parse_thickness },
582 { .sp_off = SPD_LP4_LPDIMM_REF, .sp_parse = spd_parse_ddr4_design },
583 { .sp_off = SPD_LP4_BLK1_CRC_START, .sp_len = SPD_LP4_BLK1_CRC_MSB +
584 1 - SPD_LP4_BLK1_CRC_START, .sp_key = SPD_KEY_CRC_DDR4_BLK1,
585 .sp_parse = spd_parse_crc }
586 };
587
588 static void
spd_parse_lp4_mod_specific(spd_info_t * si)589 spd_parse_lp4_mod_specific(spd_info_t *si)
590 {
591 uint32_t type;
592
593 if (nvlist_lookup_uint32(si->si_nvl, SPD_KEY_MOD_TYPE, &type) != 0)
594 return;
595
596 switch (type) {
597 case SPD_MOD_TYPE_LPDIMM:
598 spd_parse(si, spd_lp4_lpdimm, ARRAY_SIZE(spd_lp4_lpdimm));
599 break;
600 default:
601 break;
602 }
603 }
604
605 void
spd_parse_lp4(spd_info_t * si)606 spd_parse_lp4(spd_info_t *si)
607 {
608 if (SPD_LP4_SPD_REV_ENC(si->si_data[SPD_LP4_SPD_REV]) !=
609 SPD_LP4_SPD_REV_V1) {
610 si->si_error = LIBJEDEC_SPD_UNSUP_REV;
611 return;
612 }
613
614 spd_parse(si, spd_lp4_base, ARRAY_SIZE(spd_lp4_base));
615 spd_parse_lp4_mod_specific(si);
616 spd_parse_ddr4_mfg(si);
617 }
618