#include <sys/sysmacros.h>
#include <sys/debug.h>
#include "libjedec_spd.h"
static const spd_value_map_t spd_ddr3_nbytes_used_map[] = {
{ SPD_DDR3_NBYTES_USED_UNDEF, 0, true },
{ SPD_DDR3_NBYTES_USED_128, 128, false },
{ SPD_DDR3_NBYTES_USED_176, 176, false },
{ SPD_DDR3_NBYTES_USED_256, 256, false },
};
static const spd_value_map_t spd_ddr3_nbytes_total_map[] = {
{ SPD_DDR3_NBYTES_TOTAL_UNDEF, 0, true },
{ SPD_DDR3_NBYTES_TOTAL_256, 256, false }
};
static const spd_value_map_t spd_ddr3_crc_map[] = {
{ SPD_DDR3_NBYTES_CRC_125, 126, false },
{ SPD_DDR3_NBYTES_CRC_116, 117, false }
};
static void
spd_parse_ddr3_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_DDR3_NBYTES_USED(data);
const uint8_t total = SPD_DDR3_NBYTES_TOTAL(data);
const uint8_t crc = SPD_DDR3_NBYTES_CRC(data);
spd_insert_map(si, SPD_KEY_NBYTES_USED, used, spd_ddr3_nbytes_used_map,
ARRAY_SIZE(spd_ddr3_nbytes_used_map));
spd_insert_map(si, SPD_KEY_NBYTES_TOTAL, total,
spd_ddr3_nbytes_total_map, ARRAY_SIZE(spd_ddr3_nbytes_total_map));
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_SPD);
spd_nvl_insert_u32(si, SPD_KEY_DEV_SPD_TYPE, SPD_SPD_T_EE1002);
spd_insert_map(si, SPD_KEY_CRC_DDR3_LEN, crc, spd_ddr3_crc_map,
ARRAY_SIZE(spd_ddr3_crc_map));
}
static const spd_value_map_t spd_ddr3_mod_type_map[] = {
{ SPD_DDR3_MOD_TYPE_TYPE_UNDEF, UINT32_MAX, true },
{ SPD_DDR3_MOD_TYPE_TYPE_RDIMM, SPD_MOD_TYPE_RDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_UDIMM, SPD_MOD_TYPE_UDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_SODIMM, SPD_MOD_TYPE_SODIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_MICRO_DIMM, SPD_MOD_TYPE_MICRO_DIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_MINI_RDIMM, SPD_MOD_TYPE_MINI_RDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_MINI_UDIMM, SPD_MOD_TYPE_MINI_UDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_MINI_CDIMM, SPD_MOD_TYPE_MINI_CDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_72b_SORDIMM, SPD_MOD_TYPE_72b_SO_RDIMM,
false },
{ SPD_DDR3_MOD_TYPE_TYPE_72b_SOUDIMM, SPD_MOD_TYPE_72b_SO_UDIMM,
false },
{ SPD_DDR3_MOD_TYPE_TYPE_72b_SOCDIMM, SPD_MOD_TYPE_72b_SO_CDIMM,
false },
{ SPD_DDR3_MOD_TYPE_TYPE_LRDIMM, SPD_MOD_TYPE_LRDIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_16b_SODIMM, SPD_MOD_TYPE_16b_SO_DIMM, false },
{ SPD_DDR3_MOD_TYPE_TYPE_32b_SODIMM, SPD_MOD_TYPE_32b_SO_DIMM, false },
};
static void
spd_parse_ddr3_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_DDR3_MOD_TYPE_TYPE(data);
spd_insert_map(si, SPD_KEY_MOD_TYPE, type, spd_ddr3_mod_type_map,
ARRAY_SIZE(spd_ddr3_mod_type_map));
}
static const spd_value_range_t spd_ddr3_nba_range = {
.svr_max = SPD_DDR3_DENSITY_NBA_BITS_MAX,
.svr_base = SPD_DDR3_DENSITY_NBA_BITS_BASE
};
static const spd_value_map64_t spd_ddr3_density_map[] = {
{SPD_DDR3_DENSITY_DENSITY_256Mb, 256ULL * 1024ULL * 1024ULL, false },
{SPD_DDR3_DENSITY_DENSITY_512Mb, 512ULL * 1024ULL * 1024ULL, false },
{SPD_DDR3_DENSITY_DENSITY_1Gb, 1ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_2Gb, 2ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_4Gb, 4ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_8Gb, 8ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_16Gb, 16ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_32Gb, 32ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_12Gb, 12ULL * 1024ULL * 1024ULL * 1024ULL,
false },
{SPD_DDR3_DENSITY_DENSITY_24Gb, 24ULL * 1024ULL * 1024ULL * 1024ULL,
false },
};
static void
spd_parse_ddr3_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 dens = SPD_DDR3_DENSITY_DENSITY(data);
const uint8_t nba = SPD_DDR3_DENSITY_NBA_BITS(data);
spd_insert_range(si, SPD_KEY_NBANK_BITS, nba, &spd_ddr3_nba_range);
spd_nvl_insert_u32(si, SPD_KEY_NBGRP_BITS, 0);
spd_insert_map64(si, SPD_KEY_DIE_SIZE, dens, spd_ddr3_density_map,
ARRAY_SIZE(spd_ddr3_density_map));
}
static const spd_value_range_t spd_ddr3_nrow_range = {
.svr_max = SPD_DDR3_ADDR_NROWS_MAX,
.svr_base = SPD_DDR3_ADDR_NROWS_BASE
};
static const spd_value_range_t spd_ddr3_ncol_range = {
.svr_max = SPD_DDR3_ADDR_NCOLS_MAX,
.svr_base = SPD_DDR3_ADDR_NCOLS_BASE
};
static void
spd_parse_ddr3_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_DDR3_ADDR_NROWS(data);
const uint8_t ncols = SPD_DDR3_ADDR_NCOLS(data);
spd_insert_range(si, SPD_KEY_NROW_BITS, nrows, &spd_ddr3_nrow_range);
spd_insert_range(si, SPD_KEY_NCOL_BITS, ncols, &spd_ddr3_ncol_range);
}
static void
spd_parse_ddr3_volt(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
uint32_t volts[3];
uint_t nvolt = 0;
if (SPD_DDR3_VOLT_V1P5_OPER(data) == 0) {
volts[nvolt] = 1500;
nvolt++;
}
if (SPD_DDR3_VOLT_V1P35_OPER(data) != 0) {
volts[nvolt] = 1350;
nvolt++;
}
if (SPD_DDR3_VOLT_V1P25_OPER(data) != 0) {
volts[nvolt] = 1250;
nvolt++;
}
if (nvolt > 0) {
spd_nvl_insert_u32_array(si, key, volts, nvolt);
}
}
static const spd_value_range_t spd_ddr3_width_range = {
.svr_base = SPD_DDR3_MOD_ORG_WIDTH_BASE,
.svr_max = SPD_DDR3_MOD_ORG_WIDTH_MAX,
.svr_exp = true
};
static const spd_value_map_t spd_ddr3_nranks[] = {
{ SPD_DDR3_MOD_ORG_NRANKS_1, 1, false },
{ SPD_DDR3_MOD_ORG_NRANKS_2, 2, false },
{ SPD_DDR3_MOD_ORG_NRANKS_3, 3, false },
{ SPD_DDR3_MOD_ORG_NRANKS_4, 4, false },
{ SPD_DDR3_MOD_ORG_NRANKS_8, 8, false }
};
static void
spd_parse_ddr3_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 nranks = SPD_DDR4_MOD_ORG_NPKG_RANK(data);
const uint8_t width = SPD_DDR4_MOD_ORG_WIDTH(data);
spd_insert_range(si, SPD_KEY_DRAM_WIDTH, width, &spd_ddr3_width_range);
spd_insert_map(si, SPD_KEY_NRANKS, nranks, spd_ddr3_nranks,
ARRAY_SIZE(spd_ddr3_nranks));
}
static const spd_value_map_t spd_ddr3_ext_width[] = {
{ SPD_DDR4_MOD_BUS_WIDTH_EXT_NONE, 0, false },
{ SPD_DDR4_MOD_BUS_WIDTH_EXT_8b, 8, false }
};
static const spd_value_range_t spd_ddr3_pri_range = {
.svr_base = SPD_DDR3_BUS_WIDTH_PRI_BASE,
.svr_max = SPD_DDR3_BUS_WIDTH_PRI_MAX,
.svr_exp = true
};
static void
spd_parse_ddr3_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 ext = SPD_DDR3_BUS_WIDTH_EXT(data);
const uint8_t pri = SPD_DDR3_BUS_WIDTH_PRI(data);
spd_nvl_insert_u32(si, SPD_KEY_NSUBCHAN, 1);
spd_nvl_insert_u32(si, SPD_KEY_DRAM_NCHAN, 1);
spd_insert_range(si, SPD_KEY_DATA_WIDTH, pri, &spd_ddr3_pri_range);
spd_insert_map(si, SPD_KEY_ECC_WIDTH, ext, spd_ddr3_ext_width,
ARRAY_SIZE(spd_ddr3_ext_width));
}
static void
spd_parse_ddr3_ftb(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
if (SPD_DDR3_FTB_DIVIDEND(data) != 1 ||
SPD_DDR3_FTB_DIVISOR(data) != 1) {
spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "library cannot "
"handle FTB value that's not 1ps, found divisor and "
"dividend %u/%u", SPD_DDR3_FTB_DIVISOR(data),
SPD_DDR3_FTB_DIVIDEND(data));
return;
}
spd_nvl_insert_u32(si, key, SPD_DDR3_FTB_PS);
}
static void
spd_parse_ddr3_mtb(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t dividend = si->si_data[off];
const uint8_t divisor = si->si_data[off + 1];
if (dividend != SPD_DDR3_MTB_125PS_DIVIDEND ||
divisor != SPD_DDR3_MTB_125PS_DIVISOR) {
spd_nvl_err(si, key, SPD_ERROR_NO_XLATE, "library encountered "
"undefined MTB value (not 125ps): found divisor and "
"dividend %u/%u", divisor, dividend);
return;
}
spd_nvl_insert_u32(si, key, SPD_DDR3_MTB_PS);
}
static void
spd_parse_ddr3_cas(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
uint32_t cas[16] = { 0 };
uint_t ncas = 0;
ASSERT3U(len, ==, 2);
for (uint32_t byte = 0; byte < len; byte++) {
const uint32_t data = si->si_data[off + byte];
uint32_t nbits = NBBY;
if (byte == len - 1)
nbits--;
for (uint32_t i = 0; i < nbits; i++) {
if (bitx8(data, i, i) == 1) {
cas[ncas] = SPD_DDR3_CAS_BASE + i + NBBY * byte;
ncas++;
}
}
}
spd_nvl_insert_u32_array(si, key, cas, ncas);
}
static void
spd_parse_ddr3_mtb_time(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint64_t ps = (uint64_t)si->si_data[off] * SPD_DDR3_MTB_PS;
if (ps == 0) {
spd_nvl_err(si, key, SPD_ERROR_NO_XLATE,
"encountered unexpected zero time value");
return;
}
spd_nvl_insert_u64(si, key, ps);
}
static void
spd_parse_ddr3_tras(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t ras_nib = SPD_DDR3_RAS_RC_UPPER_RAS(si->si_data[off]);
ASSERT3U(len, ==, 2);
return (spd_parse_ddr_time(si, key, ras_nib, si->si_data[off + 1], 0));
}
static void
spd_parse_ddr3_trc(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t rc_nib = SPD_DDR3_RAS_RC_UPPER_RC(si->si_data[off]);
return (spd_parse_ddr_time(si, key, rc_nib, si->si_data[off + 2],
si->si_data[off + len - 1]));
}
static void
spd_parse_ddr3_tfaw(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t nib = SPD_DDR3_TFAB_NIB_UPPER_TFAW(si->si_data[off]);
ASSERT3U(len, ==, 2);
return (spd_parse_ddr_time(si, key, nib, si->si_data[off + 1], 0));
}
static void
spd_parse_ddr3_opt_feat(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
spd_ddr3_feat_t flags = 0;
if (SPD_DDR3_OPT_FEAT_RZQ6(data) != 0)
flags |= SPD_DDR3_FEAT_RZQ_6;
if (SPD_DDR3_OPT_FEAT_RZQ7(data) != 0)
flags |= SPD_DDR3_FEAT_RZQ_7;
if (SPD_DDR3_OPT_FEAT_DLLO(data) != 0)
flags |= SPD_DDR3_FEAT_DLL_OFF;
if (flags != 0) {
spd_upsert_flag(si, SPD_KEY_DDR3_FEAT, flags);
}
}
static const spd_value_map_t spd_ddr3_temp[] = {
{ SPD_DDR3_REFRESH_ETR_TEMP_85C, JEDEC_TEMP_CASE_NT, false },
{ SPD_DDR3_REFRESH_ETR_TEMP_95C, JEDEC_TEMP_CASE_XT, false },
};
static const spd_value_map_t spd_ddr3_xtrr[] = {
{ SPD_DDR3_REFRESH_ETR_REF_2X, 2, false },
{ SPD_DDR3_REFRESH_ETR_REF_1X, 1, false },
};
static void
spd_parse_ddr3_refresh(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t etr = SPD_DDR3_REFRESH_ETR_TEMP(data);
const uint8_t rr = SPD_DDR3_REFRESH_ETR_REF(data);
spd_insert_map(si, SPD_KEY_MOD_OPER_TEMP, etr,
spd_ddr3_temp, ARRAY_SIZE(spd_ddr3_temp));
spd_insert_map(si, SPD_KEY_DDR3_XTRR, rr,
spd_ddr3_xtrr, ARRAY_SIZE(spd_ddr3_xtrr));
if (SPD_DDR3_REFRESH_ASR_SUP(data) != 0) {
spd_upsert_flag(si, SPD_KEY_DDR3_FEAT, SPD_DDR3_FEAT_ASR);
}
if (SPD_DDR3_REFRESH_PASR_SUP(data) != 0) {
spd_nvl_insert_key(si, SPD_KEY_DDR_PASR);
}
}
static void
spd_parse_ddr3_ts(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
if (SPD_DDR3_MOD_THERM_PRES(data)) {
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_TEMP_1);
spd_nvl_insert_u32(si, SPD_KEY_DEV_TEMP_TYPE,
SPD_TEMP_T_TSE2002);
}
}
static const spd_value_map_t spd_ddr3_sl_map[] = {
{ SPD_DDR3_PKG_SIG_LOAD_UNSPEC, SPD_SL_UNSPECIFIED, false },
{ SPD_DDR3_PKG_SIG_LOAD_MULTI, SPD_SL_MUTLI_STACK, false },
{ SPD_DDR3_PKG_SIG_LOAD_SINGLE, SPD_SL_3DS, false }
};
static const spd_value_range_t spd_ddr3_ndie_range = {
.svr_min = SPD_DDR3_PKG_DIE_CNT_MIN,
.svr_max = SPD_DDR3_PKG_DIE_CNT_MAX,
.svr_exp = true
};
static void
spd_parse_ddr3_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 ndie = SPD_DDR3_PKG_DIE_CNT(data);
const uint8_t sl = SPD_DDR3_PKG_SIG_LOAD(data);
if (SPD_DDR3_PKG_TYPE(data) == SPD_DDR3_PKG_TYPE_NOT) {
spd_nvl_insert_key(si, SPD_KEY_PKG_NOT_MONO);
}
if (SPD_DDR3_PKG_DIE_CNT(data) != 0) {
spd_insert_range(si, SPD_KEY_PKG_NDIE, ndie,
&spd_ddr3_ndie_range);
}
spd_insert_map(si, SPD_KEY_PKG_SL, sl, spd_ddr3_sl_map,
ARRAY_SIZE(spd_ddr3_sl_map));
}
static const spd_value_map_t spd_ddr3_maw_map[] = {
{ SPD_DDR3_MAC_MAW_8192X, 8192, false },
{ SPD_DDR3_MAC_MAW_4096X, 4096, false },
{ SPD_DDR3_MAC_MAW_2048X, 2048, false }
};
static const spd_value_map_t spd_ddr3_mac_map[] = {
{ SPD_DDR3_MAC_MAC_UNTESTED, 0, true},
{ SPD_DDR3_MAC_MAC_700K, 700000, false },
{ SPD_DDR3_MAC_MAC_600K, 600000, false },
{ SPD_DDR3_MAC_MAC_500K, 500000, false },
{ SPD_DDR3_MAC_MAC_400K, 400000, false },
{ SPD_DDR3_MAC_MAC_300K, 300000, false },
{ SPD_DDR3_MAC_MAC_200K, 200000, false },
{ SPD_DDR3_MAC_MAC_UNLIMITED, SPD_KEY_MAC_UNLIMITED, false }
};
static void
spd_parse_ddr3_mac(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_DDR3_MAC_MAW(data);
const uint8_t mac = SPD_DDR3_MAC_MAC(data);
spd_insert_map(si, SPD_KEY_MAW, maw, spd_ddr3_maw_map,
ARRAY_SIZE(spd_ddr3_maw_map));
spd_insert_map(si, SPD_KEY_MAC, mac, spd_ddr3_mac_map,
ARRAY_SIZE(spd_ddr3_mac_map));
}
static void
spd_parse_ddr3_crc(spd_info_t *si, uint32_t off, uint32_t len, const char *key)
{
const uint16_t expect = si->si_data[off + len - 2] |
(si->si_data[off + len - 1] << 8);
const uint8_t crc = SPD_DDR3_NBYTES_CRC(si->si_data[SPD_DDR3_NBYTES]);
const uint32_t crc_len = crc == SPD_DDR3_NBYTES_CRC_125 ? 127 : 117;
spd_parse_crc_expect(si, off, crc_len, expect, key);
}
static const spd_parse_t spd_ddr3_common[] = {
{ .sp_off = SPD_DDR3_NBYTES, .sp_parse = spd_parse_ddr3_nbytes },
{ .sp_off = SPD_DDR3_SPD_REV, .sp_parse = spd_parse_rev },
{ .sp_off = SPD_DDR3_DRAM_TYPE, .sp_key = SPD_KEY_DRAM_TYPE,
.sp_parse = spd_parse_raw_u8 },
{ .sp_off = SPD_DDR3_MOD_TYPE, .sp_parse = spd_parse_ddr3_mod_type },
{ .sp_off = SPD_DDR3_DENSITY, .sp_parse = spd_parse_ddr3_density },
{ .sp_off = SPD_DDR3_ADDR, .sp_parse = spd_parse_ddr3_addr },
{ .sp_off = SPD_DDR3_VOLT, .sp_key = SPD_KEY_NOM_VDD,
.sp_parse = spd_parse_ddr3_volt },
{ .sp_off = SPD_DDR3_MOD_ORG, .sp_parse = spd_parse_ddr3_mod_org },
{ .sp_off = SPD_DDR3_BUS_WIDTH, .sp_parse = spd_parse_ddr3_bus_width },
{ .sp_off = SPD_DDR3_FTB, .sp_key = SPD_KEY_FTB,
.sp_parse = spd_parse_ddr3_ftb },
{ .sp_off = SPD_DDR3_MTB_DIVIDEND, .sp_key = SPD_KEY_MTB, .sp_len = 2,
.sp_parse = spd_parse_ddr3_mtb },
{ .sp_off = SPD_DDR3_TCK_MIN, .sp_key = SPD_KEY_TCKAVG_MIN,
.sp_len = SPD_DDR3_TCK_MIN_FINE - SPD_DDR3_TCK_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_DDR3_CAS_SUP0, .sp_key = SPD_KEY_CAS, .sp_len = 2,
.sp_parse = spd_parse_ddr3_cas },
{ .sp_off = SPD_DDR3_TAA_MIN, .sp_key = SPD_KEY_TAA_MIN,
.sp_len = SPD_DDR3_TAA_MIN_FINE - SPD_DDR3_TAA_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_DDR3_TWR_MIN, .sp_key = SPD_KEY_TWR_MIN,
.sp_parse = spd_parse_ddr3_mtb_time },
{ .sp_off = SPD_DDR3_TRCD_MIN, .sp_key = SPD_KEY_TRCD_MIN,
.sp_len = SPD_DDR3_TRCD_MIN_FINE - SPD_DDR3_TRCD_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_DDR3_TRRD_MIN, .sp_key = SPD_KEY_TRRD_L_MIN,
.sp_parse = spd_parse_ddr3_mtb_time },
{ .sp_off = SPD_DDR3_TRP_MIN, .sp_key = SPD_KEY_TRP_MIN,
.sp_len = SPD_DDR3_TRP_MIN_FINE - SPD_DDR3_TRP_MIN + 1,
.sp_parse = spd_parse_mtb_ftb_time_pair },
{ .sp_off = SPD_DDR3_RAS_RC_UPPER, .sp_len = 2,
.sp_key = SPD_KEY_TRAS_MIN, .sp_parse = spd_parse_ddr3_tras },
{ .sp_off = SPD_DDR3_RAS_RC_UPPER, .sp_key = SPD_KEY_TRC_MIN,
.sp_len = SPD_DDR3_TRC_MIN_FINE - SPD_DDR3_RAS_RC_UPPER + 1,
.sp_parse = spd_parse_ddr3_trc },
{ .sp_off = SPD_DDR3_TRFC_MIN_LSB, .sp_len = 2,
.sp_key = SPD_KEY_TRFC1_MIN, .sp_parse = spd_parse_mtb_pair },
{ .sp_off = SPD_DDR3_TWTR_MIN, .sp_key = SPD_KEY_TWTRS_MIN,
.sp_parse = spd_parse_ddr3_mtb_time },
{ .sp_off = SPD_DDR3_TRTP_MIN, .sp_key = SPD_KEY_TRTP,
.sp_parse = spd_parse_ddr3_mtb_time },
{ .sp_off = SPD_DDR3_TFAW_NIB, .sp_len = 2,
.sp_key = SPD_KEY_TFAW, .sp_parse = spd_parse_ddr3_tfaw },
{ .sp_off = SPD_DDR3_OPT_FEAT, .sp_parse = spd_parse_ddr3_opt_feat },
{ .sp_off = SPD_DDR3_REFRESH, .sp_parse = spd_parse_ddr3_refresh },
{ .sp_off = SPD_DDR3_MOD_THERM, .sp_parse = spd_parse_ddr3_ts },
{ .sp_off = SPD_DDR3_TYPE, .sp_parse = spd_parse_ddr3_type },
{ .sp_off = SPD_DDR3_MAC, .sp_parse = spd_parse_ddr3_mac},
{ .sp_len = SPD_DDR3_CRC_MSB + 1, .sp_key = SPD_KEY_CRC_DDR3,
.sp_parse = spd_parse_ddr3_crc },
};
static const spd_parse_t spd_ddr3_mfg[] = {
{ .sp_off = SPD_DDR3_MFG_MOD_ID0, .sp_len = 2,
.sp_key = SPD_KEY_MFG_MOD_MFG_ID,
.sp_parse = spd_parse_jedec_id },
{ .sp_off = SPD_DDR3_MFG_MOD_ID0, .sp_len = 2,
.sp_key = SPD_KEY_MFG_MOD_MFG_NAME,
.sp_parse = spd_parse_jedec_id_str },
{ .sp_off = SPD_DDR3_MFG_LOC, .sp_key = SPD_KEY_MFG_MOD_LOC_ID,
.sp_parse = spd_parse_raw_u8 },
{ .sp_off = SPD_DDR3_MFG_YEAR, .sp_key = SPD_KEY_MFG_MOD_YEAR,
.sp_parse = spd_parse_hex_string },
{ .sp_off = SPD_DDR3_MFG_WEEK, .sp_key = SPD_KEY_MFG_MOD_WEEK,
.sp_parse = spd_parse_hex_string },
{ .sp_off = SPD_DDR3_MOD_SN, .sp_len = SPD_DDR3_MOD_SN_LEN,
.sp_key = SPD_KEY_MFG_MOD_SN, .sp_parse = spd_parse_hex_string },
{ .sp_off = SPD_DDR3_MOD_PN, .sp_len = SPD_DDR3_MOD_PN_LEN,
.sp_key = SPD_KEY_MFG_MOD_PN, .sp_parse = spd_parse_string },
{ .sp_off = SPD_DDR3_MOD_REV, .sp_len = SPD_DDR3_MOD_REV_LEN,
.sp_key = SPD_KEY_MFG_MOD_REV, .sp_parse = spd_parse_hex_string },
{ .sp_off = SPD_DDR3_MFG_DRAM_ID0, .sp_len = 2,
.sp_key = SPD_KEY_MFG_DRAM_MFG_ID,
.sp_parse = spd_parse_jedec_id },
{ .sp_off = SPD_DDR3_MFG_DRAM_ID0, .sp_len = 2,
.sp_key = SPD_KEY_MFG_DRAM_MFG_NAME,
.sp_parse = spd_parse_jedec_id_str },
};
static void
spd_parse_ddr3_design(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
ASSERT3U(off, >=, SPD_DDR3_RDIMM_HEIGHT);
return (spd_parse_design(si, off, SPD_DDR3_UDIMM_HEIGHT));
}
static void
spd_parse_ddr3_edge(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
if (SPD_DDR3_UDIMM_MAP_R1(data) == SPD_DDR3_UDIMM_MAP_R1_MIRROR)
spd_nvl_insert_key(si, SPD_KEY_MOD_EDGE_MIRROR);
}
static const spd_parse_t spd_ddr3_udimm[] = {
{ .sp_off = SPD_DDR3_UDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
.sp_parse = spd_parse_height },
{ .sp_off = SPD_DDR3_UDIMM_THICK, .sp_parse = spd_parse_thickness },
{ .sp_off = SPD_DDR3_UDIMM_REF, .sp_parse = spd_parse_ddr3_design },
{ .sp_off = SPD_DDR3_UDIMM_MAP, .sp_parse = spd_parse_ddr3_edge }
};
static const spd_value_map_t spd_ddr3_rdimm_nrows_map[] = {
{ 0, 0, true },
{ 1, 1, false },
{ 2, 2, false },
{ 3, 4, false }
};
static void
spd_parse_ddr3_rdimm_attr(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t nregs = SPD_DDR3_RDIMM_ATTR_NREGS(data);
const uint8_t nrows = SPD_DDR3_RDIMM_ATTR_NROWS(data);
spd_insert_map(si, SPD_KEY_MOD_NROWS, nrows, spd_ddr3_rdimm_nrows_map,
ARRAY_SIZE(spd_ddr3_rdimm_nrows_map));
spd_insert_map(si, SPD_KEY_MOD_NREGS, nregs, spd_ddr3_rdimm_nrows_map,
ARRAY_SIZE(spd_ddr3_rdimm_nrows_map));
}
static void
spd_parse_ddr3_rdimm_hs(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
if (SPD_DDR3_RDIMM_THERM_IMPL(data) != 0)
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_HS);
}
static void
spd_parse_ddr3_rdimm_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_DDR3_RDIMM_RTYPE_TYPE(data);
if (type != SPD_DDR3_RDIMM_RTYPE_TYPE_SSTE32882) {
spd_nvl_err(si, SPD_KEY_DEV_RCD_TYPE, SPD_ERROR_NO_XLATE,
"encountered unknown register type value: 0x%x", type);
return;
}
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_RCD);
spd_nvl_insert_u32(si, SPD_KEY_DEV_RCD_TYPE, SPD_RCD_T_SSTE32882);
}
static const spd_value_map_t spd_ddr3_rdimm_lmsv_ds_map[] = {
{ SPD_DDR3_RDIMM_DS_LIGHT, SPD_DRIVE_LIGHT, false },
{ SPD_DDR3_RDIMM_DS_MODERATE, SPD_DRIVE_MODERATE, false },
{ SPD_DDR3_RDIMM_DS_STRONG, SPD_DRIVE_STRONG, false },
{ SPD_DDR3_RDIMM_DS_VERY_STRONG, SPD_DRIVE_VERY_STRONG, false },
};
static const spd_value_map_t spd_ddr3_rdimm_lms_ds_map[] = {
{ SPD_DDR3_RDIMM_DS_LIGHT, SPD_DRIVE_LIGHT, false },
{ SPD_DDR3_RDIMM_DS_MODERATE, SPD_DRIVE_MODERATE, false },
{ SPD_DDR3_RDIMM_DS_STRONG, SPD_DRIVE_STRONG, false },
};
static const spd_value_map_t spd_ddr3_rdimm_lm_ds_map[] = {
{ SPD_DDR3_RDIMM_DS_LIGHT, SPD_DRIVE_LIGHT, false },
{ SPD_DDR3_RDIMM_DS_MODERATE, SPD_DRIVE_MODERATE, false },
};
static void
spd_parse_ddr3_rdimm_cads(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t caa = SPD_DDR3_RDIMM_CADS_CAA(data);
const uint8_t cab = SPD_DDR3_RDIMM_CADS_CAB(data);
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_CAA, caa,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_CAB, cab,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
}
static void
spd_parse_ddr3_rdimm_ccds(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t ctla = SPD_DDR3_RDIMM_CCDS_CTLA(data);
const uint8_t ctlb = SPD_DDR3_RDIMM_CCDS_CTLB(data);
const uint8_t y1 = SPD_DDR3_RDIMM_CCDS_CLK1(data);
const uint8_t y0 = SPD_DDR3_RDIMM_CCDS_CLK0(data);
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_CTLA, ctla,
spd_ddr3_rdimm_lm_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lm_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_CTLB, ctlb,
spd_ddr3_rdimm_lm_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lm_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_Y0, y0,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_RCD_DS_Y1, y1,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
}
static const spd_parse_t spd_ddr3_rdimm[] = {
{ .sp_off = SPD_DDR3_RDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
.sp_parse = spd_parse_height },
{ .sp_off = SPD_DDR3_RDIMM_THICK, .sp_parse = spd_parse_thickness },
{ .sp_off = SPD_DDR3_RDIMM_REF, .sp_parse = spd_parse_ddr3_design },
{ .sp_off = SPD_DDR3_RDIMM_ATTR,
.sp_parse = spd_parse_ddr3_rdimm_attr },
{ .sp_off = SPD_DDR3_RDIMM_THERM, .sp_parse = spd_parse_ddr3_rdimm_hs },
{ .sp_off = SPD_DDR3_RDIMM_REG_MFG_ID0, .sp_len = 2,
.sp_key = SPD_KEY_DEV_RCD_MFG,
.sp_parse = spd_parse_jedec_id },
{ .sp_off = SPD_DDR3_RDIMM_REG_MFG_ID0, .sp_len = 2,
.sp_key = SPD_KEY_DEV_RCD_MFG_NAME,
.sp_parse = spd_parse_jedec_id_str },
{ .sp_off = SPD_DDR3_RDIMM_REV, .sp_key = SPD_KEY_DEV_RCD_REV,
.sp_parse = spd_parse_dram_step },
{ .sp_off = SPD_DDR3_RDIMM_RTYPE,
.sp_parse = spd_parse_ddr3_rdimm_type },
{ .sp_off = SPD_DDR3_RDIMM_CADS,
.sp_parse = spd_parse_ddr3_rdimm_cads },
{ .sp_off = SPD_DDR3_RDIMM_CCDS,
.sp_parse = spd_parse_ddr3_rdimm_ccds },
};
static const spd_parse_t spd_ddr3_cdimm[] = {
{ .sp_off = SPD_DDR3_CDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
.sp_parse = spd_parse_height },
{ .sp_off = SPD_DDR3_CDIMM_THICK, .sp_parse = spd_parse_thickness },
{ .sp_off = SPD_DDR3_CDIMM_REF, .sp_parse = spd_parse_ddr3_design },
};
static const spd_value_map_t spd_ddr3_lrdimm_nrows_map[] = {
{ 0, 0, true },
{ 1, 1, false },
{ 2, 2, false },
};
static const spd_value_map_t spd_ddr3_lrdimm_orient_map[] = {
{ SPD_DDR3_LRDIMM_ATTR_ORIENT_VERT, SPD_ORNT_VERTICAL, false },
{ SPD_DDR3_LRDIMM_ATTR_ORIENT_HORIZ, SPD_ORNT_HORIZONTAL, false }
};
static void
spd_parse_ddr3_lrdimm_attr(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t mirror = SPD_DDR3_LRDIMM_ATTR_MIR(data);
const uint8_t nrows = SPD_DDR3_LRDIMM_ATTR_NROWS(data);
const uint8_t orient = SPD_DDR3_LRDIMM_ATTR_ORIENT(data);
if (mirror == SPD_DDR3_LRDIMM_ATTR_MIR_ODD_ARE)
spd_nvl_insert_key(si, SPD_KEY_MOD_EDGE_MIRROR);
spd_insert_map(si, SPD_KEY_MOD_NROWS, nrows, spd_ddr3_lrdimm_nrows_map,
ARRAY_SIZE(spd_ddr3_lrdimm_nrows_map));
spd_insert_map(si, SPD_KEY_DDR3_MB_ORIENT, orient,
spd_ddr3_lrdimm_orient_map, ARRAY_SIZE(spd_ddr3_lrdimm_orient_map));
if (SPD_DDR3_LRDIMM_ATTR_HS(data) != 0)
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_HS);
}
static void
spd_parse_ddr3_lrdimm_mb(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
ASSERT3U(len, ==, 2);
spd_upsert_flag(si, SPD_KEY_DEVS, SPD_DEVICE_DB);
spd_nvl_insert_u32(si, SPD_KEY_DEV_DB_TYPE, SPD_DB_T_DDR3MB);
spd_parse_jedec_id(si, off, 2, SPD_KEY_DEV_DB_MFG);
spd_parse_jedec_id_str(si, off, 2, SPD_KEY_DEV_DB_MFG_NAME);
}
static void
spd_parse_ddr3_lrdimm_tcds(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t ca = SPD_DDR3_LRDIMM_TCDS_AC(data);
const uint8_t cs = SPD_DDR3_LRDIMM_TCDS_QxCS(data);
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_CA, ca,
spd_ddr3_rdimm_lmsv_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lmsv_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_CS, cs,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
}
static void
spd_parse_ddr3_lrdimm_ckds(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t odt = SPD_DDR3_LRDIMM_CKDS_ODT(data);
const uint8_t cke = SPD_DDR3_LRDIMM_CKDS_CKE(data);
const uint8_t y1 = SPD_DDR3_LRDIMM_CKDS_Y1Y3(data);
const uint8_t y0 = SPD_DDR3_LRDIMM_CKDS_Y0Y2(data);
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_ODT, odt,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_CKE, cke,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_Y1, y1,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
spd_insert_map(si, SPD_KEY_DDR3_MB_DS_Y0, y0,
spd_ddr3_rdimm_lms_ds_map, ARRAY_SIZE(spd_ddr3_rdimm_lms_ds_map));
}
static void
spd_parse_ddr3_lrdimm_ext_delay(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t y = SPD_DDR3_LRDIMM_EXTD_Y(data);
const uint8_t cs = SPD_DDR3_LRDIMM_EXTD_CS(data);
const uint8_t odt = SPD_DDR3_LRDIMM_EXTD_ODT(data);
const uint8_t cke = SPD_DDR3_LRDIMM_EXTD_CKE(data);
if (y != 0)
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_EXTD_Y, y);
if (cs != 0)
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_EXTD_CS, cs);
if (odt != 0)
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_EXTD_ODT, odt);
if (cke != 0)
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_EXTD_CKE, cke);
}
static void
spd_parse_ddr3_lrdimm_add_delay_csy(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t y = SPD_DDR3_LRDIMM_ADDD_CSY_Y(data);
const uint8_t cs = SPD_DDR3_LRDIMM_ADDD_CSY_CS(data);
ASSERT3U(off, >, SPD_DDR3_LRDIMM_TCDS);
const uint8_t yen = si->si_data[SPD_DDR3_LRDIMM_TCDS];
if (SPD_DDR3_LRDIMM_TCDS_ACPL(yen) != SPD_DDR3_LRDIMM_TCDS_ACPL_STD) {
const uint8_t val = SPD_DDR3_LRDIMM_ADD_BASE - y;
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_ADDD_Y, val);
}
if (SPD_DDR3_LRDIMM_ADDD_CSY_CS_EN(data) != 0) {
const uint8_t val = SPD_DDR3_LRDIMM_ADD_BASE - cs;
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_ADDD_CS, val);
}
}
static void
spd_parse_ddr3_lrdimm_add_delay_odt(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t data = si->si_data[off];
const uint8_t cke = SPD_DDR3_LRDIMM_ADDD_ODT_CKE(data);
const uint8_t odt = SPD_DDR3_LRDIMM_ADDD_ODT_ODT(data);
if (SPD_DDR3_LRDIMM_ADDD_ODT_CKE_EN(data) != 0) {
const uint8_t val = SPD_DDR3_LRDIMM_ADD_BASE - cke;
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_ADDD_CKE, val);
}
if (SPD_DDR3_LRDIMM_ADDD_ODT_ODT_EN(data) != 0) {
const uint8_t val = SPD_DDR3_LRDIMM_ADD_BASE - odt;
spd_nvl_insert_u32(si, SPD_KEY_DDR3_MB_ADDD_ODT, val);
}
}
static const spd_value_map_t spd_ddr3_mdq_ds_map[] = {
{ SPD_DDR3_LRDIMM_MDQ_DS_40R, 40, false },
{ SPD_DDR3_LRDIMM_MDQ_DS_34R, 34, false },
{ SPD_DDR3_LRDIMM_MDQ_DS_48R, 48, false },
{ SPD_DDR3_LRDIMM_MDQ_DS_27R, 27, false },
{ SPD_DDR3_LRDIMM_MDQ_DS_20R, 20, false }
};
static const spd_value_map_t spd_ddr3_odt_map[] = {
{ SPD_DDR3_LRDIMM_MDQ_ODT_DIS, SPD_TERM_DISABLED, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_60R, 60, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_120R, 120, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_40R, 40, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_30R, 30, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_240R, 240, false },
{ SPD_DDR3_LRDIMM_MDQ_ODT_80R, 80, false },
};
static void
spd_parse_ddr3_lrdimm_mdq(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t d800 = si->si_data[off];
const uint8_t d1333 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE];
const uint8_t d1866 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE * 2];
const uint8_t odt[3] = { SPD_DDR3_LRDIMM_MDQ_ODT(d800),
SPD_DDR3_LRDIMM_MDQ_ODT(d1333), SPD_DDR3_LRDIMM_MDQ_ODT(d1866) };
const uint8_t ds[3] = { SPD_DDR3_LRDIMM_MDQ_DS(d800),
SPD_DDR3_LRDIMM_MDQ_DS(d1333), SPD_DDR3_LRDIMM_MDQ_DS(d1866) };
spd_insert_map_array(si, SPD_KEY_DDR3_MDQ_ODT, odt, ARRAY_SIZE(odt),
spd_ddr3_odt_map, ARRAY_SIZE(spd_ddr3_odt_map));
spd_insert_map_array(si, SPD_KEY_DDR3_MDQ_DS, ds, ARRAY_SIZE(ds),
spd_ddr3_mdq_ds_map, ARRAY_SIZE(spd_ddr3_mdq_ds_map));
}
static void
spd_parse_ddr3_lrdimm_odt_common(spd_info_t *si, uint32_t off,
const char *r0_odt0_rd_key, const char *r0_odt1_rd_key,
const char *r1_odt0_rd_key, const char *r1_odt1_rd_key,
const char *r0_odt0_wr_key, const char *r0_odt1_wr_key,
const char *r1_odt0_wr_key, const char *r1_odt1_wr_key)
{
const uint8_t d800 = si->si_data[off];
const uint8_t d1333 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE];
const uint8_t d1866 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE * 2];
boolean_t r0_odt0_rd[3] = { SPD_DDR3_LRDIMM_ODT_R0_ODT0_RD(d800),
SPD_DDR3_LRDIMM_ODT_R0_ODT0_RD(d1333),
SPD_DDR3_LRDIMM_ODT_R0_ODT0_RD(d1866) };
boolean_t r0_odt1_rd[3] = { SPD_DDR3_LRDIMM_ODT_R0_ODT1_RD(d800),
SPD_DDR3_LRDIMM_ODT_R0_ODT1_RD(d1333),
SPD_DDR3_LRDIMM_ODT_R0_ODT1_RD(d1866) };
boolean_t r1_odt0_rd[3] = { SPD_DDR3_LRDIMM_ODT_R1_ODT0_RD(d800),
SPD_DDR3_LRDIMM_ODT_R1_ODT0_RD(d1333),
SPD_DDR3_LRDIMM_ODT_R1_ODT0_RD(d1866) };
boolean_t r1_odt1_rd[3] = { SPD_DDR3_LRDIMM_ODT_R1_ODT1_RD(d800),
SPD_DDR3_LRDIMM_ODT_R1_ODT1_RD(d1333),
SPD_DDR3_LRDIMM_ODT_R1_ODT1_RD(d1866) };
boolean_t r0_odt0_wr[3] = { SPD_DDR3_LRDIMM_ODT_R0_ODT0_WR(d800),
SPD_DDR3_LRDIMM_ODT_R0_ODT0_WR(d1333),
SPD_DDR3_LRDIMM_ODT_R0_ODT0_WR(d1866) };
boolean_t r0_odt1_wr[3] = { SPD_DDR3_LRDIMM_ODT_R0_ODT1_WR(d800),
SPD_DDR3_LRDIMM_ODT_R0_ODT1_WR(d1333),
SPD_DDR3_LRDIMM_ODT_R0_ODT1_WR(d1866) };
boolean_t r1_odt0_wr[3] = { SPD_DDR3_LRDIMM_ODT_R1_ODT0_WR(d800),
SPD_DDR3_LRDIMM_ODT_R1_ODT0_WR(d1333),
SPD_DDR3_LRDIMM_ODT_R1_ODT0_WR(d1866) };
boolean_t r1_odt1_wr[3] = { SPD_DDR3_LRDIMM_ODT_R1_ODT1_WR(d800),
SPD_DDR3_LRDIMM_ODT_R1_ODT1_WR(d1333),
SPD_DDR3_LRDIMM_ODT_R1_ODT1_WR(d1866) };
spd_nvl_insert_boolean_array(si, r0_odt0_rd_key, r0_odt0_rd,
ARRAY_SIZE(r0_odt0_rd));
spd_nvl_insert_boolean_array(si, r0_odt1_rd_key, r0_odt1_rd,
ARRAY_SIZE(r0_odt1_rd));
spd_nvl_insert_boolean_array(si, r1_odt0_rd_key, r1_odt0_rd,
ARRAY_SIZE(r1_odt0_rd));
spd_nvl_insert_boolean_array(si, r1_odt1_rd_key, r1_odt1_rd,
ARRAY_SIZE(r1_odt1_rd));
spd_nvl_insert_boolean_array(si, r0_odt0_wr_key, r0_odt0_wr,
ARRAY_SIZE(r0_odt0_wr));
spd_nvl_insert_boolean_array(si, r0_odt1_wr_key, r0_odt1_wr,
ARRAY_SIZE(r0_odt1_wr));
spd_nvl_insert_boolean_array(si, r1_odt0_wr_key, r1_odt0_wr,
ARRAY_SIZE(r1_odt0_wr));
spd_nvl_insert_boolean_array(si, r1_odt1_wr_key, r1_odt1_wr,
ARRAY_SIZE(r1_odt1_wr));
}
static void
spd_parse_ddr3_lrdimm_odt_r0(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
spd_parse_ddr3_lrdimm_odt_common(si, off,
SPD_KEY_DDR3_MB_R0_ODT0_RD, SPD_KEY_DDR3_MB_R0_ODT1_RD,
SPD_KEY_DDR3_MB_R1_ODT0_RD, SPD_KEY_DDR3_MB_R1_ODT1_RD,
SPD_KEY_DDR3_MB_R0_ODT0_WR, SPD_KEY_DDR3_MB_R0_ODT1_WR,
SPD_KEY_DDR3_MB_R1_ODT0_WR, SPD_KEY_DDR3_MB_R1_ODT1_WR);
}
static void
spd_parse_ddr3_lrdimm_odt_r2(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
spd_parse_ddr3_lrdimm_odt_common(si, off,
SPD_KEY_DDR3_MB_R2_ODT0_RD, SPD_KEY_DDR3_MB_R2_ODT1_RD,
SPD_KEY_DDR3_MB_R3_ODT0_RD, SPD_KEY_DDR3_MB_R3_ODT1_RD,
SPD_KEY_DDR3_MB_R2_ODT0_WR, SPD_KEY_DDR3_MB_R2_ODT1_WR,
SPD_KEY_DDR3_MB_R3_ODT0_WR, SPD_KEY_DDR3_MB_R3_ODT1_WR);
}
static void
spd_parse_ddr3_lrdimm_odt_r4(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
spd_parse_ddr3_lrdimm_odt_common(si, off,
SPD_KEY_DDR3_MB_R4_ODT0_RD, SPD_KEY_DDR3_MB_R4_ODT1_RD,
SPD_KEY_DDR3_MB_R5_ODT0_RD, SPD_KEY_DDR3_MB_R5_ODT1_RD,
SPD_KEY_DDR3_MB_R4_ODT0_WR, SPD_KEY_DDR3_MB_R4_ODT1_WR,
SPD_KEY_DDR3_MB_R5_ODT0_WR, SPD_KEY_DDR3_MB_R5_ODT1_WR);
}
static void
spd_parse_ddr3_lrdimm_odt_r6(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
spd_parse_ddr3_lrdimm_odt_common(si, off,
SPD_KEY_DDR3_MB_R6_ODT0_RD, SPD_KEY_DDR3_MB_R6_ODT1_RD,
SPD_KEY_DDR3_MB_R7_ODT0_RD, SPD_KEY_DDR3_MB_R7_ODT1_RD,
SPD_KEY_DDR3_MB_R6_ODT0_WR, SPD_KEY_DDR3_MB_R6_ODT1_WR,
SPD_KEY_DDR3_MB_R7_ODT0_WR, SPD_KEY_DDR3_MB_R7_ODT1_WR);
}
static const spd_value_map_t spd_ddr3_rtt_wr_map[] = {
{ SPD_DDR3_LRDIMM_RTT_WR_DIS, SPD_TERM_DISABLED, false },
{ SPD_DDR3_LRDIMM_RTT_WR_60R, 60, false },
{ SPD_DDR3_LRDIMM_RTT_WR_120R, 120, false },
};
static const spd_value_map_t spd_ddr3_rtt_nom_map[] = {
{ SPD_DDR3_LRDIMM_RTT_NOM_DIS, SPD_TERM_DISABLED, false },
{ SPD_DDR3_LRDIMM_RTT_NOM_60R, 60, false },
{ SPD_DDR3_LRDIMM_RTT_NOM_120R, 120, false },
{ SPD_DDR3_LRDIMM_RTT_NOM_40R, 40, false },
{ SPD_DDR3_LRDIMM_RTT_NOM_20R, 20, false },
{ SPD_DDR3_LRDIMM_RTT_NOM_30R, 30, false },
};
static const spd_value_map_t spd_ddr3_dram_imp_map[] = {
{ SPD_DDR3_LRDIMM_RTT_IMP_40R, 40, false },
{ SPD_DDR3_LRDIMM_RTT_IMP_34R, 34, false }
};
static void
spd_parse_ddr3_lrdimm_rtt(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t d800 = si->si_data[off];
const uint8_t d1333 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE];
const uint8_t d1866 = si->si_data[off + SPD_DDR3_LRDIMM_STRIDE * 2];
const uint8_t imp[3] = { SPD_DDR3_LRDIMM_RTT_IMP(d800),
SPD_DDR3_LRDIMM_RTT_IMP(d1333), SPD_DDR3_LRDIMM_RTT_IMP(d1866) };
const uint8_t nom[3] = { SPD_DDR3_LRDIMM_RTT_NOM(d800),
SPD_DDR3_LRDIMM_RTT_NOM(d1333), SPD_DDR3_LRDIMM_RTT_NOM(d1866) };
const uint8_t wr[3] = { SPD_DDR3_LRDIMM_RTT_WR(d800),
SPD_DDR3_LRDIMM_RTT_WR(d1333), SPD_DDR3_LRDIMM_RTT_WR(d1866) };
spd_insert_map_array(si, SPD_KEY_DDR3_DRAM_DS, imp, ARRAY_SIZE(imp),
spd_ddr3_dram_imp_map, ARRAY_SIZE(spd_ddr3_dram_imp_map));
spd_insert_map_array(si, SPD_KEY_DDR3_RTT_NOM, nom, ARRAY_SIZE(nom),
spd_ddr3_rtt_nom_map, ARRAY_SIZE(spd_ddr3_rtt_nom_map));
spd_insert_map_array(si, SPD_KEY_DDR3_RTT_WRT, wr, ARRAY_SIZE(wr),
spd_ddr3_rtt_wr_map, ARRAY_SIZE(spd_ddr3_rtt_wr_map));
}
static void
spd_parse_ddr3_lrdimm_mod_delay(spd_info_t *si, uint32_t off, uint32_t len,
const char *key)
{
const uint8_t d1v5 = si->si_data[off];
const uint8_t d1v35 = si->si_data[off + 2];
const uint8_t d1v25 = si->si_data[off + 4];
uint64_t delay[3] = { d1v25 * SPD_DDR3_MTB_PS, d1v35 * SPD_DDR3_MTB_PS,
d1v5 * SPD_DDR3_MTB_PS };
spd_nvl_insert_u64_array(si, key, delay, ARRAY_SIZE(delay));
}
static const spd_parse_t spd_ddr3_lrdimm[] = {
{ .sp_off = SPD_DDR3_LRDIMM_HEIGHT, .sp_key = SPD_KEY_MOD_HEIGHT,
.sp_parse = spd_parse_height },
{ .sp_off = SPD_DDR3_LRDIMM_THICK, .sp_parse = spd_parse_thickness },
{ .sp_off = SPD_DDR3_LRDIMM_REF, .sp_parse = spd_parse_ddr3_design },
{ .sp_off = SPD_DDR3_LRDIMM_ATTR,
.sp_parse = spd_parse_ddr3_lrdimm_attr },
{ .sp_off = SPD_DDR3_LRDIMM_MB_REV, .sp_key = SPD_KEY_DEV_DB_REV,
.sp_parse = spd_parse_dram_step },
{ .sp_off = SPD_DDR3_LRDIMM_MB_MFG_ID0, .sp_len = 2,
.sp_parse = spd_parse_ddr3_lrdimm_mb },
{ .sp_off = SPD_DDR3_LRDIMM_TCDS,
.sp_parse = spd_parse_ddr3_lrdimm_tcds },
{ .sp_off = SPD_DDR3_LRDIMM_CKDS,
.sp_parse = spd_parse_ddr3_lrdimm_ckds },
{ .sp_off = SPD_DDR3_LRDIMM_EXTD,
.sp_parse = spd_parse_ddr3_lrdimm_ext_delay },
{ .sp_off = SPD_DDR3_LRDIMM_ADDD_CSY,
.sp_parse = spd_parse_ddr3_lrdimm_add_delay_csy },
{ .sp_off = SPD_DDR3_LRDIMM_ADDD_ODT,
.sp_parse = spd_parse_ddr3_lrdimm_add_delay_odt },
{ .sp_off = SPD_DDR3_LRDIMM_MDQ_800,
.sp_len = SPD_DDR3_LRDIMM_MDQ_1866 - SPD_DDR3_LRDIMM_MDQ_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_mdq },
{ .sp_off = SPD_DDR3_LRDIMM_ODT_R0_800,
.sp_len = SPD_DDR3_LRDIMM_ODT_R0_1866 -
SPD_DDR3_LRDIMM_ODT_R0_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_odt_r0 },
{ .sp_off = SPD_DDR3_LRDIMM_ODT_R2_800,
.sp_len = SPD_DDR3_LRDIMM_ODT_R2_1866 -
SPD_DDR3_LRDIMM_ODT_R2_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_odt_r2 },
{ .sp_off = SPD_DDR3_LRDIMM_ODT_R4_800,
.sp_len = SPD_DDR3_LRDIMM_ODT_R4_1866 -
SPD_DDR3_LRDIMM_ODT_R4_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_odt_r4 },
{ .sp_off = SPD_DDR3_LRDIMM_ODT_R6_800,
.sp_len = SPD_DDR3_LRDIMM_ODT_R6_1866 -
SPD_DDR3_LRDIMM_ODT_R6_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_odt_r6 },
{ .sp_off = SPD_DDR3_LRDIMM_RTT_800,
.sp_len = SPD_DDR3_LRDIMM_RTT_1866 - SPD_DDR3_LRDIMM_RTT_800 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_rtt },
{ .sp_off = SPD_DDR3_LRDIMM_MIN_DELAY_1V5,
.sp_key = SPD_KEY_DDR3_MOD_MIN_DELAY,
.sp_len = SPD_DDR3_LRDIMM_MIN_DELAY_1V25 -
SPD_DDR3_LRDIMM_MIN_DELAY_1V5 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_mod_delay },
{ .sp_off = SPD_DDR3_LRDIMM_MAX_DELAY_1V5,
.sp_key = SPD_KEY_DDR3_MOD_MAX_DELAY,
.sp_len = SPD_DDR3_LRDIMM_MAX_DELAY_1V25 -
SPD_DDR3_LRDIMM_MAX_DELAY_1V5 + 1,
.sp_parse = spd_parse_ddr3_lrdimm_mod_delay },
{ .sp_off = SPD_DDR3_LRDIMM_PERS, .sp_key = SPD_KEY_DDR3_MB_PERS,
.sp_len = SPD_DDR3_LRDIMM_PERS_NBYTES,
.sp_parse = spd_parse_u8_array }
};
static void
spd_parse_ddr3_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_UDIMM:
case SPD_MOD_TYPE_SODIMM:
case SPD_MOD_TYPE_MICRO_DIMM:
case SPD_MOD_TYPE_MINI_UDIMM:
case SPD_MOD_TYPE_16b_SO_DIMM:
case SPD_MOD_TYPE_32b_SO_DIMM:
case SPD_MOD_TYPE_72b_SO_UDIMM:
spd_parse(si, spd_ddr3_udimm, ARRAY_SIZE(spd_ddr3_udimm));
break;
case SPD_MOD_TYPE_RDIMM:
case SPD_MOD_TYPE_MINI_RDIMM:
case SPD_MOD_TYPE_72b_SO_RDIMM:
spd_parse(si, spd_ddr3_rdimm, ARRAY_SIZE(spd_ddr3_rdimm));
break;
case SPD_MOD_TYPE_72b_SO_CDIMM:
case SPD_MOD_TYPE_MINI_CDIMM:
spd_parse(si, spd_ddr3_cdimm, ARRAY_SIZE(spd_ddr3_cdimm));
break;
case SPD_MOD_TYPE_LRDIMM:
spd_parse(si, spd_ddr3_lrdimm, ARRAY_SIZE(spd_ddr3_lrdimm));
break;
default:
break;
}
}
void
spd_parse_ddr3(spd_info_t *si)
{
if (SPD_DDR3_SPD_REV_ENC(si->si_data[SPD_DDR3_SPD_REV]) !=
SPD_DDR3_SPD_REV_V1) {
si->si_error = LIBJEDEC_SPD_UNSUP_REV;
return;
}
spd_parse(si, spd_ddr3_common, ARRAY_SIZE(spd_ddr3_common));
spd_parse_ddr3_mod_specific(si);
spd_parse(si, spd_ddr3_mfg, ARRAY_SIZE(spd_ddr3_mfg));
}