#include <sys/sysmacros.h>
#include <sys/debug.h>
#include "libjedec_spd.h"
static const spd_value_map_t spd_lp4_nbytes_used_map[] = {
{ SPD_LP4_NBYTES_USED_UNDEF, 0, true },
{ SPD_LP4_NBYTES_USED_128, 128, false },
{ SPD_LP4_NBYTES_USED_256, 256, false },
{ SPD_LP4_NBYTES_USED_384, 384, false },
{ SPD_LP4_NBYTES_USED_512, 512, false }
};
static const spd_value_map_t spd_lp4_nbytes_total_map[] = {
{ SPD_LP4_NBYTES_TOTAL_UNDEF, 0, true },
{ SPD_LP4_NBYTES_TOTAL_256, 256, false },
{ SPD_LP4_NBYTES_TOTAL_512, 512, false }
};
static void
spd_parse_lp4_nbytes(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t used = SPD_LP4_NBYTES_USED(data);
const uint8_t total = SPD_LP4_NBYTES_TOTAL(data);
spd_insert_map(si, SPD_KEY_NBYTES_USED, used, spd_lp4_nbytes_used_map,
ARRAY_SIZE(spd_lp4_nbytes_used_map));
spd_insert_map(si, SPD_KEY_NBYTES_TOTAL, total,
spd_lp4_nbytes_total_map, ARRAY_SIZE(spd_lp4_nbytes_total_map));
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_SPD);
if (total == SPD_LP4_NBYTES_TOTAL_256) {
spd_nvl_insert_u32(si, SPD_KEY_DEV_SPD_TYPE, SPD_SPD_T_EE1002);
} else if (total == SPD_LP4_NBYTES_TOTAL_512) {
spd_nvl_insert_u32(si, SPD_KEY_DEV_SPD_TYPE, SPD_SPD_T_EE1004);
}
}
static const spd_value_map_t spd_lp4_mod_type_map[] = {
{ SPD_LP4_MOD_TYPE_TYPE_LPDIMM, SPD_MOD_TYPE_LPDIMM, false },
{ SPD_LP4_MOD_TYPE_TYPE_SOLDER, SPD_MOD_TYPE_SOLDER, false }
};
static const spd_value_map_t spd_lp4_mod_is_hybrid_map[] = {
{ 0, SPD_MOD_NOT_HYBRID, false },
{ 1, SPD_MOD_HYBRID_NVDIMMM, false }
};
static void
spd_parse_lp4_mod_type(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t type = SPD_LP4_MOD_TYPE_TYPE(data);
const uint8_t is_hyb = SPD_LP4_MOD_TYPE_ISHYBRID(data);
spd_insert_map(si, SPD_KEY_MOD_HYBRID_TYPE, is_hyb,
spd_lp4_mod_is_hybrid_map, ARRAY_SIZE(spd_lp4_mod_is_hybrid_map));
spd_insert_map(si, SPD_KEY_MOD_TYPE, type, spd_lp4_mod_type_map,
ARRAY_SIZE(spd_lp4_mod_type_map));
}
static const spd_value_map64_t spd_lp4_density_map[] = {
{ SPD_LP4_DENSITY_DENSITY_1Gb, 1ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_2Gb, 2ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_4Gb, 4ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_8Gb, 8ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_16Gb, 16ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_32Gb, 32ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_12Gb, 12ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_24Gb, 24ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_3Gb, 3ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_6Gb, 64ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{ SPD_LP4_DENSITY_DENSITY_18Gb, 18ULL * 1024ULL * 1024ULL * 1024ULL,
false },
};
static const spd_value_range_t spd_lp4_nbgrp_range = {
.svr_max = SPD_LP4_DENSITY_NBG_BITS_MAX
};
static const spd_value_range_t spd_lp4_nba_range = {
.svr_max = SPD_LP4_DENSITY_NBA_BITS_MAX,
.svr_base = SPD_LP4_DENSITY_NBA_BITS_BASE
};
static void
spd_parse_lp4_density(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t nbg = SPD_LP4_DENSITY_NBG_BITS(data);
const uint8_t nbank = SPD_LP4_DENSITY_NBA_BITS(data);
const uint8_t dens = SPD_LP4_DENSITY_DENSITY(data);
spd_insert_range(si, SPD_KEY_NBGRP_BITS, nbg, &spd_lp4_nbgrp_range);
spd_insert_range(si, SPD_KEY_NBANK_BITS, nbank, &spd_lp4_nba_range);
spd_insert_map64(si, SPD_KEY_DIE_SIZE, dens, spd_lp4_density_map,
ARRAY_SIZE(spd_lp4_density_map));
}
static const spd_value_range_t spd_lp4_nrow_range = {
.svr_max = SPD_LP4_ADDR_NROWS_MAX,
.svr_base = SPD_LP4_ADDR_NROWS_BASE
};
static const spd_value_range_t spd_lp4_ncol_range = {
.svr_max = SPD_LP4_ADDR_NCOLS_MAX,
.svr_base = SPD_LP4_ADDR_NCOLS_BASE
};
static void
spd_parse_lp4_addr(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t nrows = SPD_LP4_ADDR_NROWS(data);
const uint8_t ncols = SPD_LP4_ADDR_NCOLS(data);
spd_insert_range(si, SPD_KEY_NROW_BITS, nrows, &spd_lp4_nrow_range);
spd_insert_range(si, SPD_KEY_NCOL_BITS, ncols, &spd_lp4_ncol_range);
}
static const spd_value_range_t spd_lp4_ndie_range = {
.svr_base = SPD_LP4_PKG_DIE_CNT_BASE
};
static const spd_value_range_t spd_lp4_nchan_range = {
.svr_max = SPD_LP4_PKG_NCHAN_MAX,
.svr_exp = true
};
static void
spd_parse_lp4_pkg(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t ndie = SPD_LP4_PKG_DIE_CNT(data);
const uint8_t nchan = SPD_LP4_PKG_NCHAN(data);
if (SPD_LP4_PKG_TYPE(data) == SPD_LP4_PKG_TYPE_NOT) {
spd_nvl_insert_key(si, SPD_KEY_PKG_NOT_MONO);
}
spd_insert_range(si, SPD_KEY_PKG_NDIE, ndie, &spd_lp4_ndie_range);
spd_insert_range(si, SPD_KEY_DRAM_NCHAN, nchan, &spd_lp4_nchan_range);
}
static const spd_value_map_t spd_lp4_maw_map[] = {
{ SPD_LP4_OPT_FEAT_MAW_8192X, 8192, false },
{ SPD_LP4_OPT_FEAT_MAW_4096X, 4096, false },
{ SPD_LP4_OPT_FEAT_MAW_2048X, 2048, false }
};
static const spd_value_map_t spd_lp4_mac_map[] = {
{ SPD_LP4_OPT_FEAT_MAC_UNTESTED, 0, true},
{ SPD_LP4_OPT_FEAT_MAC_700K, 700000, false },
{ SPD_LP4_OPT_FEAT_MAC_600K, 600000, false },
{ SPD_LP4_OPT_FEAT_MAC_500K, 500000, false },
{ SPD_LP4_OPT_FEAT_MAC_400K, 400000, false },
{ SPD_LP4_OPT_FEAT_MAC_300K, 300000, false },
{ SPD_LP4_OPT_FEAT_MAC_200K, 200000, false },
{ SPD_LP4_OPT_FEAT_MAC_UNLIMITED, SPD_KEY_MAC_UNLIMITED, false }
};
static void
spd_parse_lp4_feat(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t maw = SPD_DDR4_OPT_FEAT_MAW(data);
const uint8_t mac = SPD_DDR4_OPT_FEAT_MAC(data);
spd_insert_map(si, SPD_KEY_MAW, maw, spd_lp4_maw_map,
ARRAY_SIZE(spd_lp4_maw_map));
spd_insert_map(si, SPD_KEY_MAC, mac, spd_lp4_mac_map,
ARRAY_SIZE(spd_lp4_mac_map));
}
static void
spd_parse_lp4_feat2(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t ppr_sup = SPD_DDR4_OPT_FEAT2_PPR(data);
spd_ppr_flags_t flags = 0;
switch (ppr_sup) {
case SPD_LP4_OPT_FEAT2_PPR_1RPBG:
spd_nvl_insert_u32(si, SPD_KEY_PPR_GRAN,
SPD_PPR_GRAN_BANK_GROUP);
flags |= SPD_PPR_F_HARD_PPR;
break;
case SPD_LP4_OPT_FEAT2_PPR_NOTSUP:
return;
default:
spd_nvl_err(si, SPD_KEY_PPR, SPD_ERROR_NO_XLATE,
"encountered unknown value: 0x%x", ppr_sup);
return;
}
if (SPD_LP4_OPT_FEAT2_SOFT_PPR(data))
flags |= SPD_PPR_F_SOFT_PPR;
spd_nvl_insert_u32(si, SPD_KEY_PPR, flags);
}
static const spd_value_range_t spd_lp4_nrank_range = {
.svr_max = SPD_LP4_MOD_ORG_NPKG_RANK_MAX,
.svr_base = SPD_LP4_MOD_ORG_NPKG_RANK_BASE
};
static const spd_value_range_t spd_lp4_width_range = {
.svr_max = SPD_LP4_MOD_ORG_WIDTH_MAX,
.svr_base = SPD_LP4_MOD_ORG_WIDTH_BASE,
.svr_exp = true
};
static void
spd_parse_lp4_mod_org(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t byte = SPD_LP4_MOD_ORG_IDENT(data);
const uint8_t nrank = SPD_LP4_MOD_ORG_NPKG_RANK(data);
const uint8_t width = SPD_LP4_MOD_ORG_WIDTH(data);
if (byte == SPD_LP4_MOD_ORG_IDENT_BYTE) {
spd_nvl_insert_key(si, SPD_KEY_LP_BYTE_MODE);
}
spd_insert_range(si, SPD_KEY_NRANKS, nrank, &spd_lp4_nrank_range);
spd_insert_range(si, SPD_KEY_DRAM_WIDTH, width, &spd_lp4_width_range);
}
static const spd_value_map_t spd_lp4_chan_map[] = {
{ SPD_LP4_BUS_WIDTH_NCHAN_1ch, 1, false },
{ SPD_LP4_BUS_WIDTH_NCHAN_2ch, 2, false },
{ SPD_LP4_BUS_WIDTH_NCHAN_3ch, 3, false },
{ SPD_LP4_BUS_WIDTH_NCHAN_4ch, 4, false },
{ SPD_LP4_BUS_WIDTH_NCHAN_8ch, 8, false }
};
static const spd_value_range_t spd_lp4_chan_width_range = {
.svr_base = SPD_LP4_BUS_WIDTH_PRI_BASE,
.svr_max = SPD_LP4_BUS_WIDTH_PRI_MAX,
.svr_exp = true
};
static void
spd_parse_lp4_bus_width(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t nchan = SPD_LP4_BUS_WIDTH_NCHAN(data);
const uint8_t ext = SPD_LP4_BUS_WIDTH_EXT(data);
const uint8_t width = SPD_LP4_BUS_WIDTH_PRI(data);
spd_insert_map(si, SPD_KEY_NSUBCHAN, nchan, spd_lp4_chan_map,
ARRAY_SIZE(spd_lp4_chan_map));
if (ext != SPD_LP4_BUS_WIDTH_EXT_NONE) {
spd_nvl_err(si, SPD_KEY_ECC_WIDTH, SPD_ERROR_NO_XLATE,
"encountered invalid bus width extension: 0x%x", ext);
} else {
spd_nvl_insert_u32(si, SPD_KEY_ECC_WIDTH, 0);
}
spd_insert_range(si, SPD_KEY_DATA_WIDTH, width,
&spd_lp4_chan_width_range);
}
static void
spd_parse_lp4_therm(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
if (SPD_LP4_MOD_THERM_PRES(data) != 0)
spd_upsert_flag(si, key, SPD_DEVICE_TEMP_1);
spd_nvl_insert_u32(si, SPD_KEY_DEV_TEMP_TYPE, SPD_TEMP_T_TSE2004av);
}
static const spd_value_range_t spd_lp4_dsm_range = {
.svr_max = SPD_LP4_SIGLOAD1_DSM_LOAD_MAX,
.svr_exp = true
};
static const spd_value_range_t spd_lp4_cac_range = {
.svr_max = SPD_LP4_SIGLOAD1_CAC_LOAD_MAX,
.svr_exp = true
};
static const spd_value_range_t spd_lp4_cs_range = {
.svr_max = SPD_LP4_SIGLOAD1_CS_LOAD_MAX,
.svr_exp = true
};
static void
spd_parse_lp4_sigload(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t dsm = SPD_LP5_SIGLOAD1_DSM_LOAD(data);
const uint8_t cac = SPD_LP5_SIGLOAD1_CAC_LOAD(data);
const uint8_t cs = SPD_LP5_SIGLOAD1_CS_LOAD(data);
spd_insert_range(si, SPD_KEY_LP_LOAD_DSM, dsm, &spd_lp4_dsm_range);
spd_insert_range(si, SPD_KEY_LP_LOAD_CAC, cac, &spd_lp4_cac_range);
spd_insert_range(si, SPD_KEY_LP_LOAD_CS, cs, &spd_lp4_cs_range);
}
static const spd_value_map_t spd_lp4_ts_mtb[] = {
{ SPD_LP4_TIMEBASE_MTB_125ps, SPD_LP4_MTB_PS, false }
};
static const spd_value_map_t spd_lp4_ts_ftb[] = {
{ SPD_LP4_TIMEBASE_FTB_1ps, SPD_LP4_FTB_PS, false }
};
static void
spd_parse_lp4_timebase(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t mtb = SPD_LP4_TIMEBASE_MTB(data);
const uint8_t ftb = SPD_LP4_TIMEBASE_FTB(data);
spd_insert_map(si, SPD_KEY_MTB, mtb, spd_lp4_ts_mtb,
ARRAY_SIZE(spd_lp4_ts_mtb));
spd_insert_map(si, SPD_KEY_FTB, ftb, spd_lp4_ts_ftb,
ARRAY_SIZE(spd_lp4_ts_ftb));
}
static const uint32_t spd_lp4_cas_map[32] = {
[0] = 3,
[1] = 6,
[2] = 8,
[3] = 9,
[4] = 10,
[5] = 11,
[6] = 12,
[7] = 14,
[8] = 16,
[9] = 18,
[10] = 20,
[11] = 22,
[12] = 24,
[13] = 26,
[14] = 28,
[15] = 30,
[16] = 32,
[18] = 36,
[20] = 40,
[22] = 44
};
static void
spd_parse_lp4_cas(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
uint32_t cas[32] = { 0 };
uint_t ncas = 0;
ASSERT3U(len, ==, 4);
for (uint32_t byte = 0; byte < MIN(len, 4); byte++) {
uint32_t data = si->si_data[off + byte];
uint32_t nbits = NBBY;
for (uint32_t i = 0; i < nbits; i++) {
if (bitx8(data, i, i) == 1) {
uint8_t pos = i + byte * NBBY;
if (spd_lp4_cas_map[pos] != 0) {
cas[ncas] = spd_lp4_cas_map[pos];
ncas++;
} else {
spd_nvl_err(si, key, SPD_ERROR_BAD_DATA,
"invalid CAS byte %u/bit %u found",
byte, i);
return;
}
}
}
}
spd_nvl_insert_u32_array(si, key, cas, ncas);
}
static void
spd_parse_lp4_rwlat(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t wr = SPD_LP4_RWLAT_WRITE(data);
const uint8_t rd = SPD_LP4_RWLAT_READ(data);
spd_lp_rwlat_t rwlat = 0;
switch (wr) {
case SPD_LP4_RWLAT_WRITE_A:
rwlat |= SPD_LP_RWLAT_WRITE_A;
break;
case SPD_LP4_RWLAT_WRITE_B:
rwlat |= SPD_LP_RWLAT_WRITE_B;
break;
default:
spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "unknown write "
"latency set value: 0x%x", wr);
return;
}
switch (rd) {
case SPD_LP4_RWLAT_DBIRD_DIS:
break;
case SPD_LP4_RWLAT_DBIRD_EN:
rwlat |= SPD_LP_RWLAT_DBIRD_EN;
break;
default:
spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "unknown read "
"latency mode value: 0x%x", rd);
return;
}
spd_nvl_insert_u32(si, key, rwlat);
}
static const spd_parse_t spd_lp4_base[] = {
{ .sp_off = SPD_LP4_NBYTES, .sp_parse = spd_parse_lp4_nbytes },
{ .sp_off = SPD_LP4_SPD_REV, .sp_parse = spd_parse_rev },
{ .sp_off = SPD_LP4_DRAM_TYPE, .sp_key = SPD_KEY_DRAM_TYPE,
.sp_parse = spd_parse_raw_u8 },
{ .sp_off = SPD_LP4_MOD_TYPE, .sp_parse = spd_parse_lp4_mod_type },
{ .sp_off = SPD_LP4_DENSITY, .sp_parse = spd_parse_lp4_density },
{ .sp_off = SPD_LP4_ADDR, .sp_parse = spd_parse_lp4_addr },
{ .sp_off = SPD_LP4_PKG, .sp_parse = spd_parse_lp4_pkg },
{ .sp_off = SPD_LP4_OPT_FEAT, .sp_parse = spd_parse_lp4_feat },
{ .sp_off = SPD_LP4_OPT_FEAT2, .sp_parse = spd_parse_lp4_feat2 },
{ .sp_off = SPD_LP4_MOD_ORG, .sp_parse = spd_parse_lp4_mod_org },
{ .sp_off = SPD_LP4_BUS_WIDTH, .sp_parse = spd_parse_lp4_bus_width },
{ .sp_off = SPD_LP4_MOD_THERM, .sp_parse = spd_parse_lp4_therm },
{ .sp_off = SPD_LP4_SIGLOAD, .sp_parse = spd_parse_lp4_sigload },
{ .sp_off = SPD_LP4_TIMEBASE, .sp_parse = spd_parse_lp4_timebase },
{ .sp_off = SPD_LP4_TCKAVG_MIN, .sp_key = SPD_KEY_TCKAVG_MIN,
.sp_len = SPD_LP4_TCKAVG_MIN_FINE - SPD_LP4_TCKAVG_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP4_TCKAVG_MAX, .sp_key = SPD_KEY_TCKAVG_MAX,
.sp_len = SPD_LP4_TCKAVG_MAX_FINE - SPD_LP4_TCKAVG_MAX + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP4_CAS_SUP0, .sp_key = SPD_KEY_CAS,
.sp_len = SPD_LP4_CAS_SUP3 - SPD_LP4_CAS_SUP0 + 1,
.sp_parse = spd_parse_lp4_cas },
{ .sp_off = SPD_LP5_TAA_MIN, .sp_key = SPD_KEY_TAA_MIN,
.sp_len = SPD_LP5_TAA_MIN_FINE - SPD_LP5_TAA_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP4_RWLAT, .sp_key = SPD_KEY_LP_RWLAT,
.sp_parse = spd_parse_lp4_rwlat },
{ .sp_off = SPD_LP5_TRCD_MIN, .sp_key = SPD_KEY_TRCD_MIN,
.sp_len = SPD_LP5_TRCD_MIN_FINE - SPD_LP5_TRCD_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP5_TRPAB_MIN, .sp_key = SPD_KEY_TRPAB_MIN,
.sp_len = SPD_LP5_TRPAB_MIN_FINE - SPD_LP5_TRPAB_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
.sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP5_TRPPB_MIN, .sp_key = SPD_KEY_TRPPB_MIN,
.sp_len = SPD_LP5_TRPPB_MIN_FINE - SPD_LP5_TRPPB_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_LP4_TRFCAB_MIN_LO, .sp_key = SPD_KEY_TRFCAB_MIN,
.sp_len = 2, .sp_parse = spd_parse_mtb_pair },
{ .sp_off = SPD_LP4_TRFCPB_MIN_LO, .sp_key = SPD_KEY_TRFCPB_MIN,
.sp_len = 2, .sp_parse = spd_parse_mtb_pair },
{ .sp_off = SPD_LP4_MAP_DQ0, .sp_key = SPD_KEY_DDR4_MAP_DQ0,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ4, .sp_key = SPD_KEY_DDR4_MAP_DQ4,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ8, .sp_key = SPD_KEY_DDR4_MAP_DQ8,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ12, .sp_key = SPD_KEY_DDR4_MAP_DQ12,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ16, .sp_key = SPD_KEY_DDR4_MAP_DQ16,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ20, .sp_key = SPD_KEY_DDR4_MAP_DQ20,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ24, .sp_key = SPD_KEY_DDR4_MAP_DQ24,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ28, .sp_key = SPD_KEY_DDR4_MAP_DQ28,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_CB0, .sp_key = SPD_KEY_DDR4_MAP_CB0,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_CB4, .sp_key = SPD_KEY_DDR4_MAP_CB4,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ32, .sp_key = SPD_KEY_DDR4_MAP_DQ32,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ36, .sp_key = SPD_KEY_DDR4_MAP_DQ36,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ40, .sp_key = SPD_KEY_DDR4_MAP_DQ40,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ44, .sp_key = SPD_KEY_DDR4_MAP_DQ44,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ48, .sp_key = SPD_KEY_DDR4_MAP_DQ48,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ52, .sp_key = SPD_KEY_DDR4_MAP_DQ52,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ56, .sp_key = SPD_KEY_DDR4_MAP_DQ56,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_off = SPD_LP4_MAP_DQ60, .sp_key = SPD_KEY_DDR4_MAP_DQ60,
.sp_parse = spd_parse_ddr4_nib_map },
{ .sp_len = SPD_DDR4_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR4_BASE,
.sp_parse = spd_parse_crc },
{ .sp_len = SPD_LP4_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR4_BASE,
.sp_parse = spd_parse_crc },
};
static const spd_parse_t spd_lp4_lpdimm[] = {
{ .sp_off = SPD_LP4_LPDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
.sp_parse = spd_parse_height },
{ .sp_off = SPD_LP4_LPDIMM_THICK, .sp_parse = spd_parse_thickness },
{ .sp_off = SPD_LP4_LPDIMM_REF, .sp_parse = spd_parse_ddr4_design },
{ .sp_off = SPD_LP4_BLK1_CRC_START, .sp_len = SPD_LP4_BLK1_CRC_MSB +
1 - SPD_LP4_BLK1_CRC_START, .sp_key = SPD_KEY_CRC_DDR4_BLK1,
.sp_parse = spd_parse_crc }
};
static void
spd_parse_lp4_mod_specific(spd_info_t *si)
{
uint32_t type;
if (nvlist_lookup_uint32(si->si_nvl, SPD_KEY_MOD_TYPE, &type) != 0)
return;
switch (type) {
case SPD_MOD_TYPE_LPDIMM:
spd_parse(si, spd_lp4_lpdimm, ARRAY_SIZE(spd_lp4_lpdimm));
break;
default:
break;
}
}
void
spd_parse_lp4(spd_info_t *si)
{
if (SPD_LP4_SPD_REV_ENC(si->si_data[SPD_LP4_SPD_REV]) !=
SPD_LP4_SPD_REV_V1) {
si->si_error = LIBJEDEC_SPD_UNSUP_REV;
return;
}
spd_parse(si, spd_lp4_base, ARRAY_SIZE(spd_lp4_base));
spd_parse_lp4_mod_specific(si);
spd_parse_ddr4_mfg(si);
}