xref: /illumos-gate/usr/src/lib/libjedec/common/libjedec_spd_lp5.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
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  * LPDDR5/x-specific SPD processing logic. For an overview of the processing
18*8119dad8SRobert Mustacchi  * design please see libjedec_spd.c. LPDDR5 has a similar design to DDR5 and
19*8119dad8SRobert Mustacchi  * even uses the same common module, manufacturing, and module-specific data.
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_lp5_nbytes_total_map[] = {
27*8119dad8SRobert Mustacchi 	{ SPD_DDR5_NBYTES_TOTAL_UNDEF, 0, true },
28*8119dad8SRobert Mustacchi 	{ SPD_DDR5_NBYTES_TOTAL_256, 256, false },
29*8119dad8SRobert Mustacchi 	{ SPD_DDR5_NBYTES_TOTAL_512, 512, false },
30*8119dad8SRobert Mustacchi 	{ SPD_DDR5_NBYTES_TOTAL_1024, 1024, false },
31*8119dad8SRobert Mustacchi 	{ SPD_DDR5_NBYTES_TOTAL_2048, 2048, false }
32*8119dad8SRobert Mustacchi };
33*8119dad8SRobert Mustacchi 
34*8119dad8SRobert Mustacchi static void
spd_parse_lp5_nbytes(spd_info_t * si,uint32_t off,uint32_t len,const char * key)35*8119dad8SRobert Mustacchi spd_parse_lp5_nbytes(spd_info_t *si, uint32_t off, uint32_t len,
36*8119dad8SRobert Mustacchi     const char *key)
37*8119dad8SRobert Mustacchi {
38*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
39*8119dad8SRobert Mustacchi 	const uint8_t total = SPD_LP5_NBYTES_TOTAL(data);
40*8119dad8SRobert Mustacchi 	uint8_t beta = SPD_LP5_NBYTES_BETA(data);
41*8119dad8SRobert Mustacchi 	beta = bitset8(beta, 4, 4, SPD_LP5_NBYTES_BETAHI(data));
42*8119dad8SRobert Mustacchi 
43*8119dad8SRobert Mustacchi 	spd_nvl_insert_u32(si, SPD_KEY_BETA, beta);
44*8119dad8SRobert Mustacchi 	spd_insert_map(si, SPD_KEY_NBYTES_TOTAL, total,
45*8119dad8SRobert Mustacchi 	    spd_lp5_nbytes_total_map, ARRAY_SIZE(spd_lp5_nbytes_total_map));
46*8119dad8SRobert Mustacchi }
47*8119dad8SRobert Mustacchi 
48*8119dad8SRobert Mustacchi static const spd_value_map64_t spd_lp5_density_map[] = {
49*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_1Gb, 1ULL * 1024ULL * 1024ULL * 1024ULL,
50*8119dad8SRobert Mustacchi 	    false },
51*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_2Gb, 2ULL * 1024ULL * 1024ULL * 1024ULL,
52*8119dad8SRobert Mustacchi 	    false },
53*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_4Gb, 4ULL * 1024ULL * 1024ULL * 1024ULL,
54*8119dad8SRobert Mustacchi 	    false },
55*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_8Gb, 8ULL * 1024ULL * 1024ULL * 1024ULL,
56*8119dad8SRobert Mustacchi 	    false },
57*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_16Gb, 16ULL * 1024ULL * 1024ULL * 1024ULL,
58*8119dad8SRobert Mustacchi 	    false },
59*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_32Gb, 32ULL * 1024ULL * 1024ULL * 1024ULL,
60*8119dad8SRobert Mustacchi 	    false },
61*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_12Gb, 12ULL * 1024ULL * 1024ULL * 1024ULL,
62*8119dad8SRobert Mustacchi 	    false },
63*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_24Gb, 24ULL * 1024ULL * 1024ULL * 1024ULL,
64*8119dad8SRobert Mustacchi 	    false },
65*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_3Gb, 3ULL * 1024ULL * 1024ULL * 1024ULL,
66*8119dad8SRobert Mustacchi 	    false },
67*8119dad8SRobert Mustacchi 	{ SPD_LP5_DENSITY_DENSITY_6Gb, 6ULL * 1024ULL * 1024ULL * 1024ULL,
68*8119dad8SRobert Mustacchi 	    false },
69*8119dad8SRobert Mustacchi };
70*8119dad8SRobert Mustacchi 
71*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_nbg_range = {
72*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_DENSITY_NBG_BITS_MAX
73*8119dad8SRobert Mustacchi };
74*8119dad8SRobert Mustacchi 
75*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_nba_range = {
76*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_DENSITY_NBA_BITS_MAX,
77*8119dad8SRobert Mustacchi 	.svr_base = SPD_LP5_DENSITY_NBA_BITS_BASE
78*8119dad8SRobert Mustacchi };
79*8119dad8SRobert Mustacchi 
80*8119dad8SRobert Mustacchi static void
spd_parse_lp5_density(spd_info_t * si,uint32_t off,uint32_t len,const char * key)81*8119dad8SRobert Mustacchi spd_parse_lp5_density(spd_info_t *si, uint32_t off, uint32_t len,
82*8119dad8SRobert Mustacchi     const char *key)
83*8119dad8SRobert Mustacchi {
84*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
85*8119dad8SRobert Mustacchi 	const uint8_t nbg = SPD_LP5_DENSITY_NBG_BITS(data);
86*8119dad8SRobert Mustacchi 	const uint8_t nba = SPD_LP5_DENSITY_NBA_BITS(data);
87*8119dad8SRobert Mustacchi 	const uint8_t dens = SPD_LP5_DENSITY_DENSITY(data);
88*8119dad8SRobert Mustacchi 
89*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_NBGRP_BITS, nbg, &spd_lp5_nbg_range);
90*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_NBANK_BITS, nba, &spd_lp5_nba_range);
91*8119dad8SRobert Mustacchi 	spd_insert_map64(si, SPD_KEY_DIE_SIZE, dens, spd_lp5_density_map,
92*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_density_map));
93*8119dad8SRobert Mustacchi }
94*8119dad8SRobert Mustacchi 
95*8119dad8SRobert Mustacchi static const spd_value_map_t spd_lp5_ncol_map[] = {
96*8119dad8SRobert Mustacchi 	{ SPD_LP5_ADDRESS_BCOL_3BA6C, 6, false },
97*8119dad8SRobert Mustacchi 	{ SPD_LP5_ADDRESS_BCOL_4BA6C, 6, false }
98*8119dad8SRobert Mustacchi };
99*8119dad8SRobert Mustacchi 
100*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_nrow_range = {
101*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_ADDRESS_NROW_MAX,
102*8119dad8SRobert Mustacchi 	.svr_base = SPD_LP5_ADDRESS_NROW_BASE
103*8119dad8SRobert Mustacchi };
104*8119dad8SRobert Mustacchi 
105*8119dad8SRobert Mustacchi static void
spd_parse_lp5_address(spd_info_t * si,uint32_t off,uint32_t len,const char * key)106*8119dad8SRobert Mustacchi spd_parse_lp5_address(spd_info_t *si, uint32_t off, uint32_t len,
107*8119dad8SRobert Mustacchi     const char *key)
108*8119dad8SRobert Mustacchi {
109*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
110*8119dad8SRobert Mustacchi 	const uint8_t nrow = SPD_LP5_ADDRESS_NROWS(data);
111*8119dad8SRobert Mustacchi 	const uint8_t bcol = SPD_LP5_ADDRESS_BCOL(data);
112*8119dad8SRobert Mustacchi 
113*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_NROW_BITS, nrow, &spd_lp5_nrow_range);
114*8119dad8SRobert Mustacchi 	spd_insert_map(si, SPD_KEY_NCOL_BITS, bcol, spd_lp5_ncol_map,
115*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_ncol_map));
116*8119dad8SRobert Mustacchi }
117*8119dad8SRobert Mustacchi 
118*8119dad8SRobert Mustacchi static const spd_value_map_t spd_lp5_ndie_map[] = {
119*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_1, 1, false },
120*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_2, 2, false },
121*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_3, 3, false },
122*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_4, 4, false },
123*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_5, 5, false },
124*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_6, 6, false },
125*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_16, 16, false },
126*8119dad8SRobert Mustacchi 	{ SPD_LP5_DIE_CNT_8, 8, false }
127*8119dad8SRobert Mustacchi };
128*8119dad8SRobert Mustacchi 
129*8119dad8SRobert Mustacchi /*
130*8119dad8SRobert Mustacchi  * To insert the total number of DQs we need the die width which comes later.
131*8119dad8SRobert Mustacchi  * Similarly, the signal loading index comes into play for a later word. As such
132*8119dad8SRobert Mustacchi  * we process that later as well and therefore the only thing we process here
133*8119dad8SRobert Mustacchi  * are the total number of dies.
134*8119dad8SRobert Mustacchi  */
135*8119dad8SRobert Mustacchi static void
spd_parse_lp5_pkg(spd_info_t * si,uint32_t off,uint32_t len,const char * key)136*8119dad8SRobert Mustacchi spd_parse_lp5_pkg(spd_info_t *si, uint32_t off, uint32_t len,
137*8119dad8SRobert Mustacchi     const char *key)
138*8119dad8SRobert Mustacchi {
139*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
140*8119dad8SRobert Mustacchi 	const uint8_t ndie = SPD_LP5_PKG_DIE_CNT(data);
141*8119dad8SRobert Mustacchi 
142*8119dad8SRobert Mustacchi 	if (SPD_LP5_PKG_TYPE(data) == SPD_LP5_PKG_TYPE_NOT) {
143*8119dad8SRobert Mustacchi 		spd_nvl_insert_key(si, SPD_KEY_PKG_NOT_MONO);
144*8119dad8SRobert Mustacchi 	}
145*8119dad8SRobert Mustacchi 
146*8119dad8SRobert Mustacchi 	spd_insert_map(si, SPD_KEY_PKG_NDIE, ndie, spd_lp5_ndie_map,
147*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_ndie_map));
148*8119dad8SRobert Mustacchi }
149*8119dad8SRobert Mustacchi 
150*8119dad8SRobert Mustacchi static void
spd_parse_lp5_opt_feat(spd_info_t * si,uint32_t off,uint32_t len,const char * key)151*8119dad8SRobert Mustacchi spd_parse_lp5_opt_feat(spd_info_t *si, uint32_t off, uint32_t len,
152*8119dad8SRobert Mustacchi     const char *key)
153*8119dad8SRobert Mustacchi {
154*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
155*8119dad8SRobert Mustacchi 	const uint8_t ppr_sup = SPD_LP5_OPT_FEAT_PPR(data);
156*8119dad8SRobert Mustacchi 	spd_ppr_flags_t flags = 0;
157*8119dad8SRobert Mustacchi 
158*8119dad8SRobert Mustacchi 	switch (ppr_sup) {
159*8119dad8SRobert Mustacchi 	case SPD_LP5_OPT_FEAT_PPR_SUP:
160*8119dad8SRobert Mustacchi 		flags |= SPD_PPR_F_HARD_PPR;
161*8119dad8SRobert Mustacchi 		break;
162*8119dad8SRobert Mustacchi 	case SPD_LP5_OPT_FEAT_PPR_NOTSUP:
163*8119dad8SRobert Mustacchi 		break;
164*8119dad8SRobert Mustacchi 	default:
165*8119dad8SRobert Mustacchi 		spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
166*8119dad8SRobert Mustacchi 		    "encountered unknown value: 0x%x", ppr_sup);
167*8119dad8SRobert Mustacchi 		return;
168*8119dad8SRobert Mustacchi 	}
169*8119dad8SRobert Mustacchi 
170*8119dad8SRobert Mustacchi 	if (SPD_LP5_OPT_FEAT_SOFT_PPR(data) != 0) {
171*8119dad8SRobert Mustacchi 		flags |= SPD_PPR_F_SOFT_PPR;
172*8119dad8SRobert Mustacchi 	}
173*8119dad8SRobert Mustacchi 
174*8119dad8SRobert Mustacchi 	if (flags != 0) {
175*8119dad8SRobert Mustacchi 		spd_nvl_insert_u32(si, key, flags);
176*8119dad8SRobert Mustacchi 	}
177*8119dad8SRobert Mustacchi }
178*8119dad8SRobert Mustacchi 
179*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_nrank_range = {
180*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_MOD_ORG_RANK_MAX,
181*8119dad8SRobert Mustacchi 	.svr_base = SPD_LP5_MOD_ORG_RANK_BASE
182*8119dad8SRobert Mustacchi };
183*8119dad8SRobert Mustacchi 
184*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_width_range = {
185*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_MOD_ORG_WIDTH_MAX,
186*8119dad8SRobert Mustacchi 	.svr_base = SPD_LP5_MOD_ORG_WIDTH_BASE,
187*8119dad8SRobert Mustacchi 	.svr_exp = true
188*8119dad8SRobert Mustacchi };
189*8119dad8SRobert Mustacchi 
190*8119dad8SRobert Mustacchi static void
spd_parse_lp5_mod_org(spd_info_t * si,uint32_t off,uint32_t len,const char * key)191*8119dad8SRobert Mustacchi spd_parse_lp5_mod_org(spd_info_t *si, uint32_t off, uint32_t len,
192*8119dad8SRobert Mustacchi     const char *key)
193*8119dad8SRobert Mustacchi {
194*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
195*8119dad8SRobert Mustacchi 	const uint8_t byte = SPD_LP5_MOD_ORG_IDENT(data);
196*8119dad8SRobert Mustacchi 	const uint8_t nrank = SPD_LP5_MOD_ORG_RANK(data);
197*8119dad8SRobert Mustacchi 	const uint8_t width = SPD_LP5_MOD_ORG_WIDTH(data);
198*8119dad8SRobert Mustacchi 
199*8119dad8SRobert Mustacchi 	if (byte == SPD_LP5_MOD_ORG_IDENT_BYTE) {
200*8119dad8SRobert Mustacchi 		spd_nvl_insert_key(si, SPD_KEY_LP_BYTE_MODE);
201*8119dad8SRobert Mustacchi 	}
202*8119dad8SRobert Mustacchi 
203*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_NRANKS, nrank, &spd_lp5_nrank_range);
204*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_DRAM_WIDTH, width, &spd_lp5_width_range);
205*8119dad8SRobert Mustacchi }
206*8119dad8SRobert Mustacchi 
207*8119dad8SRobert Mustacchi static const spd_value_map_t spd_lp5_subchan_width[] = {
208*8119dad8SRobert Mustacchi 	{ SP5_LP5_WIDTH_SUBCHAN_16b, 16, false },
209*8119dad8SRobert Mustacchi 	{ SP5_LP5_WIDTH_SUBCHAN_32b, 16, false }
210*8119dad8SRobert Mustacchi };
211*8119dad8SRobert Mustacchi 
212*8119dad8SRobert Mustacchi /*
213*8119dad8SRobert Mustacchi  * While this is nominally duplicative of the common memory channel
214*8119dad8SRobert Mustacchi  * organization, we implement it here anyways just in case.
215*8119dad8SRobert Mustacchi  */
216*8119dad8SRobert Mustacchi static void
spd_parse_lp5_width(spd_info_t * si,uint32_t off,uint32_t len,const char * key)217*8119dad8SRobert Mustacchi spd_parse_lp5_width(spd_info_t *si, uint32_t off, uint32_t len,
218*8119dad8SRobert Mustacchi     const char *key)
219*8119dad8SRobert Mustacchi {
220*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
221*8119dad8SRobert Mustacchi 	const uint8_t scw = SPD_LP5_WIDTH_SUBCHAN(data);
222*8119dad8SRobert Mustacchi 
223*8119dad8SRobert Mustacchi 	spd_insert_map(si, key, scw, spd_lp5_subchan_width,
224*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_subchan_width));
225*8119dad8SRobert Mustacchi }
226*8119dad8SRobert Mustacchi 
227*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_dsm_range = {
228*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_SIGLOAD1_DSM_LOAD_MAX,
229*8119dad8SRobert Mustacchi 	.svr_exp = true
230*8119dad8SRobert Mustacchi };
231*8119dad8SRobert Mustacchi 
232*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_cac_range = {
233*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_SIGLOAD1_CAC_LOAD_MAX,
234*8119dad8SRobert Mustacchi 	.svr_exp = true
235*8119dad8SRobert Mustacchi };
236*8119dad8SRobert Mustacchi 
237*8119dad8SRobert Mustacchi static const spd_value_range_t spd_lp5_cs_range = {
238*8119dad8SRobert Mustacchi 	.svr_max = SPD_LP5_SIGLOAD1_CS_LOAD_MAX,
239*8119dad8SRobert Mustacchi 	.svr_exp = true
240*8119dad8SRobert Mustacchi };
241*8119dad8SRobert Mustacchi 
242*8119dad8SRobert Mustacchi static void
spd_parse_lp5_sigload(spd_info_t * si,uint32_t off,uint32_t len,const char * key)243*8119dad8SRobert Mustacchi spd_parse_lp5_sigload(spd_info_t *si, uint32_t off, uint32_t len,
244*8119dad8SRobert Mustacchi     const char *key)
245*8119dad8SRobert Mustacchi {
246*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
247*8119dad8SRobert Mustacchi 	const uint8_t dsm = SPD_LP5_SIGLOAD1_DSM_LOAD(data);
248*8119dad8SRobert Mustacchi 	const uint8_t cac = SPD_LP5_SIGLOAD1_CAC_LOAD(data);
249*8119dad8SRobert Mustacchi 	const uint8_t cs = SPD_LP5_SIGLOAD1_CS_LOAD(data);
250*8119dad8SRobert Mustacchi 
251*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_LP_LOAD_DSM, dsm, &spd_lp5_dsm_range);
252*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_LP_LOAD_CAC, cac, &spd_lp5_cac_range);
253*8119dad8SRobert Mustacchi 	spd_insert_range(si, SPD_KEY_LP_LOAD_CS, cs, &spd_lp5_cs_range);
254*8119dad8SRobert Mustacchi }
255*8119dad8SRobert Mustacchi 
256*8119dad8SRobert Mustacchi static const spd_value_map_t spd_lp5_ts_mtb[] = {
257*8119dad8SRobert Mustacchi 	{ SPD_LP5_TIMEBASE_MTB_125ps, SPD_LP5_MTB_PS, false }
258*8119dad8SRobert Mustacchi };
259*8119dad8SRobert Mustacchi 
260*8119dad8SRobert Mustacchi static const spd_value_map_t spd_lp5_ts_ftb[] = {
261*8119dad8SRobert Mustacchi 	{ SPD_LP5_TIMEBASE_FTB_1ps, SPD_LP5_FTB_PS, false }
262*8119dad8SRobert Mustacchi };
263*8119dad8SRobert Mustacchi 
264*8119dad8SRobert Mustacchi static void
spd_parse_lp5_timebase(spd_info_t * si,uint32_t off,uint32_t len,const char * key)265*8119dad8SRobert Mustacchi spd_parse_lp5_timebase(spd_info_t *si, uint32_t off, uint32_t len,
266*8119dad8SRobert Mustacchi     const char *key)
267*8119dad8SRobert Mustacchi {
268*8119dad8SRobert Mustacchi 	const uint8_t data = si->si_data[off];
269*8119dad8SRobert Mustacchi 	const uint8_t mtb = SPD_LP5_TIMEBASE_MTB(data);
270*8119dad8SRobert Mustacchi 	const uint8_t ftb = SPD_LP5_TIMEBASE_FTB(data);
271*8119dad8SRobert Mustacchi 
272*8119dad8SRobert Mustacchi 	spd_insert_map(si, SPD_KEY_MTB, mtb, spd_lp5_ts_mtb,
273*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_ts_mtb));
274*8119dad8SRobert Mustacchi 	spd_insert_map(si, SPD_KEY_FTB, ftb, spd_lp5_ts_ftb,
275*8119dad8SRobert Mustacchi 	    ARRAY_SIZE(spd_lp5_ts_ftb));
276*8119dad8SRobert Mustacchi }
277*8119dad8SRobert Mustacchi 
278*8119dad8SRobert Mustacchi static const spd_parse_t spd_lp5_base[] = {
279*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_NBYTES, .sp_parse = spd_parse_lp5_nbytes },
280*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_SPD_REV, .sp_parse = spd_parse_rev },
281*8119dad8SRobert Mustacchi 	/*
282*8119dad8SRobert Mustacchi 	 * We have previously validated that the DRAM type is something that we
283*8119dad8SRobert Mustacchi 	 * understand. We pass through the raw enum to users here.
284*8119dad8SRobert Mustacchi 	 */
285*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_DRAM_TYPE, .sp_key = SPD_KEY_DRAM_TYPE,
286*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_raw_u8 },
287*8119dad8SRobert Mustacchi 	/*
288*8119dad8SRobert Mustacchi 	 * DDR5 and LPDDR5 use the same values here, so we reuse the logic.
289*8119dad8SRobert Mustacchi 	 */
290*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_MOD_TYPE, .sp_parse = spd_parse_ddr5_mod_type },
291*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_DENSITY, .sp_parse = spd_parse_lp5_density },
292*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_ADDRESS, .sp_parse = spd_parse_lp5_address },
293*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_PKG, .sp_parse = spd_parse_lp5_pkg},
294*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_OPT_FEAT, .sp_key = SPD_KEY_PPR,
295*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_lp5_opt_feat },
296*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_MOD_ORG, .sp_parse = spd_parse_lp5_mod_org },
297*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_WIDTH, .sp_key = SPD_KEY_DATA_WIDTH,
298*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_lp5_width },
299*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_WIDTH, .sp_parse = spd_parse_lp5_sigload },
300*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TIMEBASE, .sp_parse = spd_parse_lp5_timebase },
301*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TCKAVG_MIN, .sp_key = SPD_KEY_TCKAVG_MIN,
302*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TCKAVG_MIN_FINE - SPD_LP5_TCKAVG_MIN + 1,
303*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
304*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TCKAVG_MAX, .sp_key = SPD_KEY_TCKAVG_MAX,
305*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TCKAVG_MAX_FINE - SPD_LP5_TCKAVG_MAX + 1,
306*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
307*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TAA_MIN, .sp_key = SPD_KEY_TAA_MIN,
308*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TAA_MIN_FINE - SPD_LP5_TAA_MIN + 1,
309*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
310*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRCD_MIN, .sp_key = SPD_KEY_TRCD_MIN,
311*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TRCD_MIN_FINE - SPD_LP5_TRCD_MIN + 1,
312*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
313*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRPAB_MIN, .sp_key = SPD_KEY_TRPAB_MIN,
314*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TRPAB_MIN_FINE - SPD_LP5_TRPAB_MIN + 1,
315*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
316*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
317*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
318*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
319*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
320*8119dad8SRobert Mustacchi 	    .sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
321*8119dad8SRobert Mustacchi 	    .sp_parse = spd_parse_mtb_ftb_time_pair },
322*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRFCAB_MIN_LO, .sp_key = SPD_KEY_TRFCAB_MIN,
323*8119dad8SRobert Mustacchi 	    .sp_len = 2, .sp_parse = spd_parse_mtb_pair },
324*8119dad8SRobert Mustacchi 	{ .sp_off = SPD_LP5_TRFCPB_MIN_LO, .sp_key = SPD_KEY_TRFCPB_MIN,
325*8119dad8SRobert Mustacchi 	    .sp_len = 2, .sp_parse = spd_parse_mtb_pair },
326*8119dad8SRobert Mustacchi };
327*8119dad8SRobert Mustacchi 
328*8119dad8SRobert Mustacchi void
spd_parse_lp5(spd_info_t * si)329*8119dad8SRobert Mustacchi spd_parse_lp5(spd_info_t *si)
330*8119dad8SRobert Mustacchi {
331*8119dad8SRobert Mustacchi 	if (SPD_LP5_SPD_REV_ENC(si->si_data[SPD_LP5_SPD_REV]) !=
332*8119dad8SRobert Mustacchi 	    SPD_LP5_SPD_REV_V1) {
333*8119dad8SRobert Mustacchi 		si->si_error = LIBJEDEC_SPD_UNSUP_REV;
334*8119dad8SRobert Mustacchi 		return;
335*8119dad8SRobert Mustacchi 	}
336*8119dad8SRobert Mustacchi 
337*8119dad8SRobert Mustacchi 	spd_parse(si, spd_lp5_base, ARRAY_SIZE(spd_lp5_base));
338*8119dad8SRobert Mustacchi 	spd_parse_ddr5_common(si);
339*8119dad8SRobert Mustacchi }
340