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