/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (c) 2018, Joyent, Inc. * Copyright 2024 Oxide Computer Company */ #ifndef _LIBJEDEC_H #define _LIBJEDEC_H /* * Library routines that support various JEDEC standards: * * o JEDEC JEP-106 vendor data * o Temperature range and Measurement Standards for Components and Modules * (JESD402-1) * o DDR3 Serial Presence Detect (SPD) decoding * o DDR4 Serial Presence Detect (SPD) decoding * o LPDDR3/4/4x Serial Presence Detect (SPD) decoding * o DDR5 Serial Presence Detect (SPD) decoding * o LPDDR5/x Serial Presence Detect (SPD) decoding */ #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Decode a JEDEC continuation ID (without parity) and a group ID. */ extern const char *libjedec_vendor_string(uint_t, uint_t); /* * JEDEC operating temperature ranges. These are defined in JESD402-1B * (September 2024). */ typedef enum { /* * Operating Case Temperature Ranges */ JEDEC_TEMP_CASE_A1T, JEDEC_TEMP_CASE_A2T, JEDEC_TEMP_CASE_A3T, JEDEC_TEMP_CASE_IT, JEDEC_TEMP_CASE_ET, JEDEC_TEMP_CASE_ST, JEDEC_TEMP_CASE_XT, JEDEC_TEMP_CASE_NT, JEDEC_TEMP_CASE_RT, /* * Operating Ambient Temperature Ranges */ JEDEC_TEMP_AMB_CT, JEDEC_TEMP_AMB_IOT, JEDEC_TEMP_AMB_IPT, JEDEC_TEMP_AMB_IXT, JEDEC_TEMP_AMB_AO3T, JEDEC_TEMP_AMB_AO2T, JEDEC_TEMP_AMB_AO1T, /* * Storage Temperature Ranges */ JEDEC_TEMP_STOR_2, JEDEC_TEMP_STOR_1B, JEDEC_TEMP_STOR_1A, JEDEC_TEMP_STOR_ST, /* * Operating Junction Temperature Ranges */ JEDEC_TEMP_JNCT_A135, JEDEC_TEMP_JNCT_A130, JEDEC_TEMP_JNCT_A1T, JEDEC_TEMP_JNCT_A120, JEDEC_TEMP_JNCT_A115, JEDEC_TEMP_JNCT_A110, JEDEC_TEMP_JNCT_A2T, JEDEC_TEMP_JNCT_A100, JEDEC_TEMP_JNCT_A95, JEDEC_TEMP_JNCT_A90, JEDEC_TEMP_JNCT_A3T, JEDEC_TEMP_JNCT_LT135, JEDEC_TEMP_JNCT_LT130, JEDEC_TEMP_JNCT_LT125, JEDEC_TEMP_JNCT_LT120, JEDEC_TEMP_JNCT_LT115, JEDEC_TEMP_JNCT_LT110, JEDEC_TEMP_JNCT_LT105, JEDEC_TEMP_JNCT_LT100, JEDEC_TEMP_JNCT_IT, JEDEC_TEMP_JNCT_LT90, JEDEC_TEMP_JNCT_LT85, JEDEC_TEMP_JNCT_ET120, JEDEC_TEMP_JNCT_ET115, JEDEC_TEMP_JNCT_ET110, JEDEC_TEMP_JNCT_ET, JEDEC_TEMP_JNCT_ET100, JEDEC_TEMP_JNCT_ET95, JEDEC_TEMP_JNCT_ET90, JEDEC_TEMP_JNCT_ST, JEDEC_TEMP_JNCT_120, JEDEC_TEMP_JNCT_115, JEDEC_TEMP_JNCT_110, JEDEC_TEMP_JNCT_105, JEDEC_TEMP_JNCT_100, JEDEC_TEMP_JNCT_XT, JEDEC_TEMP_JNCT_90, JEDEC_TEMP_JNCT_NT } libjedec_temp_range_t; extern boolean_t libjedec_temp_range(libjedec_temp_range_t, int32_t *, int32_t *); /* * This is a series of error codes that libjedec may produce while trying to * parse the overall SPD data structure. These represent a top-level failure and * have meaning when no nvlist_t is returned. */ typedef enum { /* * Indicates that we didn't encounter a fatal error; however, we may * have a specific parsing error that relates to a key in the nvlist. */ LIBJEDEC_SPD_OK = 0, /* * Indicates that we did not have enough memory while trying to * construct the generated nvlist_t. */ LIBJEDEC_SPD_NOMEM, /* * Indicates that the data that we found was insufficient to * successfully parse basic information. The required size varies per * SPD key byte type. */ LIBJEDEC_SPD_TOOSHORT, /* * Indicates that we found an unsupported type of SPD data and therefore * cannot parse this. */ LIBJEDEC_SPD_UNSUP_TYPE, /* * Indicates that while we found a supported type of SPD data, we do not * understand its revision. */ LIBJEDEC_SPD_UNSUP_REV } spd_error_t; /* * Decode a binary payload of SPD data, if possible. The returned nvlist is made * up of a series of keys described below. Parsing errors are broken into two * categories. Fatal errors set a value in the spd_error_t below. Non-fatal * errors, such as encountering a value which we don't have a translation for, * are in a nested errors nvlist_t indexed by key. * * The keys are all dot delineated to create a few different top-level * namespaces. These include: * * "meta" -- Which includes information about the SPD, encoding, and things like * the type of module. * * "dram" -- Parameters that are specific to the SDRAM dies present. What one * thinks of as a stick of DRAM consists of several different SDRAM dies on the * PCB. This includes things like the row and columns bits and timing * information. * * "channel" -- Parameters that are tied to an implementation of a channel. DDR4 * has a single channel where as DDR5 and LPDDR[345] have some number of * sub-channels. * * "ddr4", "ddr5" -- These include information which is specific to the general * DDR standard. While we have tried to consolidate information between them * where applicable, some things are specific to the standard. * * "lp" -- These are parameters that are currently specific to one of the * low-power DDR specifications such as LPDDR5. * * "module" -- Parameters that are specific to the broader module and PCB * itself. This includes information like the height or devices present. * * "ddr4.rdimm", "ddr4.lrdimm", "ddr5.rdimm", etc. -- These are parameter that * are specific to a module being both the combination of a specific DDR * standard and a specific type of module. Common parameters are often in the * "module" section. * * "ddr3.mb", "ddr4.rcd", etc. -- These are generation-specific parameters that * refer to a specific component like the rcd found on RDIMMs and LRDIMMs or the * mb on LRDIMMs. * * "mfg" -- Manufacturing related information. * * "errors" -- The key for the errors nvlist_t. See the spd_error_kind_t * definition later on. Each error has both a numeric code and a string message. */ extern nvlist_t *libjedec_spd(const uint8_t *, size_t, spd_error_t *); /* * The following are keys in the metadata nvlist_t. The SPD_KEY_NBYTES_TOTAL is * present in DDR4 and DDR5. The SPD_KEY_NBYTES_USED is only present on DDR4 * right now. All supported SPD encodings have the raw revision information. If * the values for the total bytes or used bytes are set to undefined, then they * will not be present. * * DDR5 introduces an idea of a public beta level that gets reset between * external releases. It theoretically modifies every scion. DDR5 also * introduces a second revision that is for the module information. This will * not be present on systems prior to DDR5. */ #define SPD_KEY_NBYTES_TOTAL "meta.total-bytes" /* uint32_t */ #define SPD_KEY_NBYTES_USED "meta.used-bytes" /* uint32_t */ #define SPD_KEY_REV_ENC "meta.revision-encoding" /* uint32_t */ #define SPD_KEY_REV_ADD "meta.revision-additions" /* uint32_t */ #define SPD_KEY_BETA "meta.beta-version" /* uint32_t */ #define SPD_KEY_MOD_REV_ENC "meta.module-revision-encoding" /* uint32_t */ #define SPD_KEY_MOD_REV_ADD "meta.module-revision-additions" /* uint32_t */ /* * DRAM Type information. This indicates the standard that the device conforms * to. This enumeration's values match the JEDEC specification's values. This is * present for everything. */ typedef enum { SPD_DT_FAST_PAGE_MODE = 0x01, SPD_DT_EDO = 0x02, SPD_DT_PIPE_NIBBLE = 0x03, SPD_DT_SDRAM = 0x04, SPD_DT_ROM = 0x05, SPD_DT_DDR_SGRAM = 0x06, SPD_DT_DDR_SDRAM = 0x07, SPD_DT_DDR2_SDRAM = 0x08, SPD_DT_DDR2_SDRAM_FBDIMM = 0x09, SPD_DT_DDR2_SDRAM_FDIMM_P = 0x0a, SPD_DT_DDR3_SDRAM = 0x0b, SPD_DT_DDR4_SDRAM = 0x0c, SPD_DT_DDR4E_SDRAM = 0x0e, SPD_DT_LPDDR3_SDRAM = 0x0f, SPD_DT_LPDDR4_SDRAM = 0x10, SPD_DT_LPDDR4X_SDRAM = 0x11, SPD_DT_DDR5_SDRAM = 0x12, SPD_DT_LPDDR5_SDRAM = 0x13, SPD_DT_DDR5_NVDIMM_P = 0x14, SPD_DT_LPDDR5X_SDRAM = 0x15 } spd_dram_type_t; #define SPD_KEY_DRAM_TYPE "meta.dram-type" /* uint32_t (enum) */ typedef enum { SPD_MOD_TYPE_RDIMM, SPD_MOD_TYPE_UDIMM, SPD_MOD_TYPE_SODIMM, SPD_MOD_TYPE_LRDIMM, SPD_MOD_TYPE_MRDIMM, SPD_MOD_TYPE_DDIMM, SPD_MOD_TYPE_SOLDER, SPD_MOD_TYPE_MINI_RDIMM, SPD_MOD_TYPE_MINI_UDIMM, SPD_MOD_TYPE_MINI_CDIMM, SPD_MOD_TYPE_72b_SO_RDIMM, SPD_MOD_TYPE_72b_SO_UDIMM, SPD_MOD_TYPE_72b_SO_CDIMM, SPD_MOD_TYPE_16b_SO_DIMM, SPD_MOD_TYPE_32b_SO_DIMM, SPD_MOD_TYPE_CUDIMM, SPD_MOD_TYPE_CSODIMM, SPD_MOD_TYPE_CAMM2, SPD_MOD_TYPE_LPDIMM, SPD_MOD_TYPE_MICRO_DIMM } spd_module_type_t; #define SPD_KEY_MOD_TYPE "meta.module-type" /* uint32_t (enum) */ typedef enum { SPD_MOD_NOT_HYBRID, SPD_MOD_HYBRID_NVDIMMM } spd_module_hybrid_t; #define SPD_KEY_MOD_HYBRID_TYPE "meta.hybrid-type" /* uint32_t */ typedef enum { SPD_MOD_TYPE_NVDIMM_N, SPD_MOD_TYPE_NVDIMM_P, SPD_MOD_TYPE_NVDIMM_H } spd_module_nvdimm_type_t; #define SPD_KEY_MOD_NVDIMM_TYPE "meta.nvdimm-type" /* uint32_t */ /* * Different SPD standards have different integrity rules. The regions covered * by the CRCs also vary. We end up with per-spec keys. All data types for these * are uint32_t's so that way we can record the expected CRC. We use a uint32_t * for consistency even though the data only fits in a uint16_t. Note, callers * must check to see if these exist. If there are keys with these names in the * errors object, then the rest of the data should be considered suspect, but we * will have attempted to parse everything we can. The DDR4 values are shared * across LPDDR3/4/4X. The DDR5 values are shared across LPDDR5/5X. */ #define SPD_KEY_CRC_DDR3 "meta.crc-ddr3" /* uint32_t */ #define SPD_KEY_CRC_DDR3_LEN "meta.crc-ddr3-len" /* uint32_t */ #define SPD_KEY_CRC_DDR4_BASE "meta.crc-ddr4-base" /* uint32_t */ #define SPD_KEY_CRC_DDR4_BLK1 "meta.crc-ddr4-block1" /* uint32_t */ #define SPD_KEY_CRC_DDR5 "meta.crc-ddr5" /* uint32_t */ /* * DDR5 adds a field in the SPD to describe how data should be hashed to compute * and compare to an attribute certification to authenticate modules. This is * only present in DDR5. We only add a value here if this is actually supported. */ typedef enum { SPD_HASH_SEQ_ALG_1 } spd_hash_seq_alg_t; #define SPD_KEY_HASH_SEQ "meta.hash-sequence-algorithm" /* uint32_t */ /* * This section contains information related to DRAM technology. */ /* * Bank, bank group, row, and column bits. These are all present in both DDR4 * and DDR5. DDR4 allows cases where there are no bank groups. If no bits are * used, then this item is empty. */ #define SPD_KEY_NROW_BITS "dram.num-row-bits" /* uint32_t */ #define SPD_KEY_NCOL_BITS "dram.num-column-bits" /* uint32_t */ #define SPD_KEY_NBANK_BITS "dram.num-bank-bits" /* uint32_t */ #define SPD_KEY_NBGRP_BITS "dram.num-bank-group-bits" /* uint32_t */ #define SPD_KEY_SEC_NROW_BITS "dram.sec-num-row-bits" /* uint32_t */ #define SPD_KEY_SEC_NCOL_BITS "dram.sec-num-column-bits" /* uint32_t */ #define SPD_KEY_SEC_NBANK_BITS "dram.sec-num-bank-bits" /* uint32_t */ #define SPD_KEY_SEC_NBGRP_BITS "dram.sec-num-bank-group-bits" /* uint32_t */ /* * Die Density. This is the capacity that each die contains in bits. */ #define SPD_KEY_DIE_SIZE "dram.die-bit-size" /* uint64_t */ #define SPD_KEY_SEC_DIE_SIZE "dram.sec-die-bit-size" /* uint64_t */ /* * Package information. DRAM may be made up of a monolithic package type or * several different types. There is a boolean property present to indicate that * it is not monolithic. For these there is a die count and then a separate * notion of what the signal loading type is. If the property is present then we * will also have the die count and loading type for the secondary. Note, these * loading parameters are considered at the device balls as opposed to specific * signals. */ #define SPD_KEY_PKG_NOT_MONO "meta.non-monolithic-package" /* key only */ #define SPD_KEY_PKG_NDIE "dram.package-die-count" /* uint32_t */ #define SPD_KEY_SEC_PKG_NDIE "dram.sec-package-die-count" /* uint32_t */ typedef enum { SPD_SL_UNSPECIFIED, SPD_SL_MUTLI_STACK, SPD_SL_3DS } spd_signal_loading_t; #define SPD_KEY_PKG_SL "dram.package-sig-loading" /* uint32_t */ #define SPD_KEY_SEC_PKG_SL "dram.sec-package-sig-loading" /* uint32_t */ /* * Post-package Repair. PPR is supported in DDR4, DDR5. LPDDR4, and LPDDR5. PPR * support is indicated by the presence of the spd_ppr_flags_t structure. If it * is not supported, then there will be no PPR related keys present. In some * cases the PPR granularity may not be known, in which case it is not present. */ typedef enum { SPD_PPR_F_HARD_PPR = 1 << 0, SPD_PPR_F_SOFT_PPR = 1 << 1, SPD_PPR_F_MBIST_PPR = 1 << 2, SPD_PPR_F_PPR_UNDO = 1 << 3 } spd_ppr_flags_t; typedef enum { SPD_PPR_GRAN_BANK_GROUP, SPD_PPR_GRAN_BANK } spd_ppr_gran_t; #define SPD_KEY_PPR "dram.ppr-flags" /* uint32_t (enum) */ #define SPD_KEY_PPR_GRAN "dram.ppr-gran" /* uint32_t (enum) */ /* * Voltages in mV. This is an array of nominal voltages that are supported. DDR3 * defines multiple voltages, but DDR4 and DDR5 only have a single voltage * (specific to the supply). DDR3 and DDR4 only defined V~DD~ in SPD. While * V~DQ~ and V~PP~ are defined in DDR5. */ #define SPD_KEY_NOM_VDD "dram.nominal-vdd" /* uint32_t[] */ #define SPD_KEY_NOM_VDDQ "dram.nominal-vddq" /* uint32_t[] */ #define SPD_KEY_NOM_VPP "dram.nominal-vpp" /* uint32_t[] */ /* * DRAM module organization. * * This describes the number of ranks that exist on a per-channel basis. In * DDR4, there is only one channel and so this effectively covers the entire * module. In DDR5 and LPDDR there are multiple channels or sub-channels. The * rank mix may be symmetrical or asymmetrical. A key will be set if that's the * case. */ #define SPD_KEY_RANK_ASYM "dram.asymmetrical-ranks" /* key */ #define SPD_KEY_NRANKS "channel.num-ranks" /* uint32_t */ /* * DRAM and Module/Channel widths. * * A 'channel' refers to the entire interface between a memory * controller and memory. In DDR4 there is only a single channel that covers the * entire 72-bit (64-bit data, 8-bit ECC) bus. In DDR5 and LPDDR5 this is made * up of a pair of sub-channels. In LPDDR3/4 the device exposed a number of * channels, that are effecitvely similar in spirit to the DDR5 sub-channel. The * channel keys below cover whatever the smallest defined unit is. For DDR4 (and * earlier) this is the entire channel. For LPDDR3/4 these are the independent * channels in the SPD spec and for DDR5 and LPDDR5 these are sub-channels. The * channel width is split between a primary data and ECC size. * * In LPDDR3/4 a given DRAM die may support a varying number of channels. That * is stored in the num-channels calculation and otherwise set to 1 for all * other types of memory. * * Separately the individual DRAM dies themselves have a width which is * SPD_KEY_DRAM_WIDTH. This is the portion of each die that contributes to the * given channel. */ #define SPD_KEY_DRAM_WIDTH "dram.width" /* uint32_t */ #define SPD_KEY_SEC_DRAM_WIDTH "dram.sec-width" /* uint32_t */ #define SPD_KEY_DRAM_NCHAN "dram.num-channels" /* uint32_t */ #define SPD_KEY_NSUBCHAN "module.num-subchan" /* uint32_t */ #define SPD_KEY_DATA_WIDTH "channel.data-width" /* uint32_t */ #define SPD_KEY_ECC_WIDTH "channel.ecc-width" /* uint32_t */ /* * LPDDR offers a notion of a 'byte mode' where half of the I/Os can be shared * between multiple dies. This key is set when tihs is true. */ #define SPD_KEY_LP_BYTE_MODE "lp.byte-mode" /* key */ /* * LPDDR3-5 have a signal loading matrix that indicates the amount of load that * is on different groups of signals. All values are a uint32_t of the count of * loads. */ #define SPD_KEY_LP_LOAD_DSM "lp.load-data-strobe-mask" #define SPD_KEY_LP_LOAD_CAC "lp.load-command-address-clock" #define SPD_KEY_LP_LOAD_CS "lp.load-chip-select" /* * DDR3, DDR4, and LPDDR3-5/x specify specific timebases in the SPD data. DDR5 * just requires a specific timebase and therefore does not define these keys. * This like all other time values is explicitly in ps. */ #define SPD_KEY_MTB "dram.median-time-base" /* uint32_t */ #define SPD_KEY_FTB "dram.fine-time-base" /* uint32_t */ /* * Supported CAS Latencies. This is an array of integers to indicate which index * CAS latencies are possible. */ #define SPD_KEY_CAS "dram.cas-latencies" /* uint32_t [] */ /* * Time parameters. These are all in picoseconds. All values are uint64_t. */ #define SPD_KEY_TCKAVG_MIN "dram.t~CKAVG~min" #define SPD_KEY_TCKAVG_MAX "dram.t~CKAVG~max" #define SPD_KEY_TAA_MIN "dram.t~AA~min" #define SPD_KEY_TRCD_MIN "dram.t~RCD~min" #define SPD_KEY_TRP_MIN "dram.t~RP~min" #define SPD_KEY_TRAS_MIN "dram.t~RAS~min" #define SPD_KEY_TRC_MIN "dram.t~RC~min" #define SPD_KEY_TRFC1_MIN "dram.t~RFC1~min" #define SPD_KEY_TRFC2_MIN "dram.t~RFC2~min" #define SPD_KEY_TFAW "dram.t~FAW~" #define SPD_KEY_TRRD_L_MIN "dram.t~RRD_L~min" #define SPD_KEY_TCCD_L_MIN "dram.t~CCD_S~min" #define SPD_KEY_TWR_MIN "dram.t~WR~min" /* * The following time are only used in DDR4. While some of the DDR4 and DDR5 * write to read or write to write parameters are similar, because they use * different names for times, we distinguish them as different values. */ #define SPD_KEY_TRFC4_MIN "dram.t~RFC4~min" #define SPD_KEY_TRRD_S_MIN "dram.t~RRD_S~min" #define SPD_KEY_TWTRS_MIN "dram.t~WTR_S~min" #define SPD_KEY_TWTRL_MIN "dram.t~WTR_L~min" /* * The following times are specific to DDR5. t~CCD_L_WTR~ in DDR5 is the * equivalent to t~WTRS_L~min, same with t~CCD_S_WTR~. */ #define SPD_KEY_TCCDLWR "dram.t~CCD_L_WR" #define SPD_KEY_TCCDLWR2 "dram.t~CCD_L_WR2" #define SPD_KEY_TCCDLWTR "dram.t~CCD_L_WTR" #define SPD_KEY_TCCDSWTR "dram.t~CCD_S_WTR" #define SPD_KEY_TRTP "dram.t~RTP~" #define SPD_KEY_TCCDM "dram.t~CCD_M~" #define SPD_KEY_TCCDMWR "dram.t~CCD_M_WR~" #define SPD_KEY_TCCDMWTR "dram.t~CCD_M_WTR~" /* * While prior DDR standards did have minimum clock times for certain * activities, these were first added to the SPD data in DDR5. All values for * these are uint32_t's and are in clock cycles. */ #define SPD_KEY_TRRD_L_NCK "dram.t~RRD_L~nCK" #define SPD_KEY_TCCD_L_NCK "dram.t~CCD_L~nCK" #define SPD_KEY_TCCDLWR_NCK "dram.t~CCD_L_WR~nCK" #define SPD_KEY_TCCDLWR2_NCK "dram.t~CCD_L_WR2~nCK" #define SPD_KEY_TFAW_NCK "dram.t~FAW~nCK" #define SPD_KEY_TCCDLWTR_NCK "dram.t~CCD_L_WTR~nCK" #define SPD_KEY_TCCDSWTR_NCK "dram.t~CCD_S_WTR~nCK" #define SPD_KEY_TRTP_NCK "dram.t~RTP~nCK" #define SPD_KEY_TCCDM_NCK "dram.t~CCD_M~nCK" #define SPD_KEY_TCCDMWR_NCK "dram.t~CCD_M_WR~nCK" #define SPD_KEY_TCCDMWTR_NCK "dram.t~CCD_M_WTR~nCK" /* * The following times are only used in DDR5. The RFCx_dlr values are for 3DS * RDIMMs. */ #define SPD_KEY_TRFCSB "dram.t~RFCsb~" #define SPD_KEY_TRFC1_DLR "dram.3ds-t~RFC1_dlr~" #define SPD_KEY_TRFC2_DLR "dram.3ds-t~RFC2_dlr~" #define SPD_KEY_TRFCSB_DLR "dram.3ds-t~RFCsb_dlr~" /* * The following times are only used by LPDDR3-5, but like other variable timing * entries, we still use the "dram" prefix. These are per-bank and all bank row * precharges and minimum refresh recovery times. */ #define SPD_KEY_TRPAB_MIN "dram.t~RPab~" #define SPD_KEY_TRPPB_MIN "dram.t~RPpb~" #define SPD_KEY_TRFCAB_MIN "dram.t~RFCab~" #define SPD_KEY_TRFCPB_MIN "dram.t~RFCpb~" /* * These refer to the maximum activate window and the maximum activate count. In * cases where the MAC is unknown no key will be present. This was present in * DDR and LPDDR 3 and 4. It is no longer present in DDR and LPDDR4 5 and * therefore will not be present for those. */ #define SPD_KEY_MAW "dram.maw" /* uint32_t */ #define SPD_KEY_MAC "dram.mac" /* uint32_t */ #define SPD_KEY_MAC_UNLIMITED UINT32_MAX /* * LPDDR3/4/4X have specific latency sets. The following enum, stored as a u32 * contains these options. */ typedef enum { SPD_LP_RWLAT_WRITE_A = 1 << 0, SPD_LP_RWLAT_WRITE_B = 1 << 1, SPD_LP_RWLAT_DBIRD_EN = 1 << 2 } spd_lp_rwlat_t; #define SPD_KEY_LP_RWLAT "lp.read-write-latency" /* uint32_t */ /* * Partial Automatic self-refresh (PASR) was introduced in DDR3 and continued in * DDR5. Automatic self-refresh (ASR) was only in DDR3. We treat it as a part of * the other DDR3 assorted features. The last DDR3 specific thing is the * extended temperature fresh rate. */ #define SPD_KEY_DDR_PASR "dram.pasr" /* key */ typedef enum { SPD_DDR3_FEAT_ASR = 1 << 0, SPD_DDR3_FEAT_DLL_OFF = 1 << 1, SPD_DDR3_FEAT_RZQ_7 = 1 << 2, SPD_DDR3_FEAT_RZQ_6 = 1 << 3 } spd_ddr3_feat_t; #define SPD_KEY_DDR3_FEAT "ddr3.asr" /* uint32_t */ #define SPD_KEY_DDR3_XTRR "ddr3.xt-refresh-rate" /* uint32_t */ /* * The following are DDR5 specific properties. BL32 indicates whether burst * length 32 mode is supported, which is a key. Along with the partial array * self refresh. The Duty Cycle Adjuster is an enumeration because there are * multiple modes. The wide temperature sensing is another DDR5 bit represented * as a key as well as an enum of fault handling. */ #define SPD_KEY_DDR5_BL32 "ddr5.bl32" /* key */ typedef enum { SPD_DCA_UNSPPORTED, SPD_DCA_1_OR_2_PHASE, SPD_DCA_4_PHASE } spd_dca_t; #define SPD_KEY_DDR5_DCA "ddr5.dca" /* uint32_t */ #define SPD_KEY_DDR5_WIDE_TS "ddr5.wide-temp-sense" /* key */ typedef enum { SPD_FLT_BOUNDED = 1 << 0, SPD_FLT_WRSUP_MR9 = 1 << 1, SPD_FLT_WRSUP_MR15 = 1 << 2 } spd_fault_t; #define SPD_KEY_DDR5_FLT "ddr5.fault-handling" /* uint32_t */ /* * DDR5 allows for non-standard core timing options. This is indicated by a * single key that acts as a flag. */ #define SPD_KEY_DDR5_NONSTD_TIME "ddr5.non-standard-timing" /* key */ /* * DDR5 adds information about refresh management. This is split into * information about general refresh management and then optional adaptive * refresh management. There are three levels of adaptive refresh management * titled A, B, and C. Both the general refresh management and the adaptive * refresh management exist for both the primary and secondary types in * asymmetrical modules. Information about the RAAIMT and RAAMMT is only present * if refresh management is required. Similarly, BRC information is only present * if DRFM is supported. All values here are uint32_t's. */ typedef enum { SPD_RFM_F_REQUIRED = 1 << 0, SPD_RFM_F_DRFM_SUP = 1 << 1, } spd_rfm_flags_t; #define SPD_KEY_DDR5_RFM_FLAGS_PRI "ddr5.rfm.flags" #define SPD_KEY_DDR5_RFM_RAAIMT_PRI "ddr5.rfm.raaimt" #define SPD_KEY_DDR5_RFM_RAAIMT_FGR_PRI "ddr5.rfm.raaimt-fgr" #define SPD_KEY_DDR5_RFM_RAAMMT_PRI "ddr5.rfm.raammt" #define SPD_KEY_DDR5_RFM_RAAMMT_FGR_PRI "ddr5.rfm.raammt-fgr" #define SPD_KEY_DDR5_RFM_BRC_CFG_PRI "ddr5.rfm.brc-config" typedef enum { SPD_BRC_F_LVL_2 = 1 << 0, SPD_BRC_F_LVL_3 = 1 << 1, SPD_BRC_F_LVL_4 = 1 << 2 } spd_brc_flags_t; #define SPD_KEY_DDR5_RFM_BRC_SUP_PRI "ddr5.rfm.brc-level" #define SPD_KEY_DDR5_RFM_RAA_DEC_PRI "ddr5.rfm.raa-dec" #define SPD_KEY_DDR5_RFM_FLAGS_SEC "ddr5.rfm.sec-flags" #define SPD_KEY_DDR5_RFM_RAAIMT_SEC "ddr5.rfm.sec-raaimt" #define SPD_KEY_DDR5_RFM_RAAIMT_FGR_SEC "ddr5.rfm.sec-raaimt-fgr" #define SPD_KEY_DDR5_RFM_RAAMMT_SEC "ddr5.rfm.sec-raammt" #define SPD_KEY_DDR5_RFM_RAAMMT_FGR_SEC "ddr5.rfm.sec-raammt-fgr" #define SPD_KEY_DDR5_RFM_BRC_CFG_SEC "ddr5.rfm.sec-brc-config" #define SPD_KEY_DDR5_RFM_BRC_SUP_SEC "ddr5.rfm.sec-brc-level" #define SPD_KEY_DDR5_RFM_RAA_DEC_SEC "ddr5.rfm.sec-raa-dec" #define SPD_KEY_DDR5_ARFMA_FLAGS_PRI "ddr5.arfm-a.flags" #define SPD_KEY_DDR5_ARFMA_RAAIMT_PRI "ddr5.arfm-a.raaimt" #define SPD_KEY_DDR5_ARFMA_RAAIMT_FGR_PRI "ddr5.arfm-a.raaimt-fgr" #define SPD_KEY_DDR5_ARFMA_RAAMMT_PRI "ddr5.arfm-a.raammt" #define SPD_KEY_DDR5_ARFMA_RAAMMT_FGR_PRI "ddr5.arfm-a.raammt-fgr" #define SPD_KEY_DDR5_ARFMA_BRC_CFG_PRI "ddr5.arfm-a.brc-config" #define SPD_KEY_DDR5_ARFMA_BRC_SUP_PRI "ddr5.arfm-a.brc-level" #define SPD_KEY_DDR5_ARFMA_RAA_DEC_PRI "ddr5.arfm-a.raa-dec" #define SPD_KEY_DDR5_ARFMA_FLAGS_SEC "ddr5.arfm-a.sec-flags" #define SPD_KEY_DDR5_ARFMA_RAAIMT_SEC "ddr5.arfm-a.sec-raaimt" #define SPD_KEY_DDR5_ARFMA_RAAIMT_FGR_SEC "ddr5.arfm-a.sec-raaimt-fgr" #define SPD_KEY_DDR5_ARFMA_RAAMMT_SEC "ddr5.arfm-a.sec-raammt" #define SPD_KEY_DDR5_ARFMA_RAAMMT_FGR_SEC "ddr5.arfm-a.sec-raammt-fgr" #define SPD_KEY_DDR5_ARFMA_BRC_CFG_SEC "ddr5.arfm-a.sec-brc-config" #define SPD_KEY_DDR5_ARFMA_BRC_SUP_SEC "ddr5.arfm-a.sec-brc-level" #define SPD_KEY_DDR5_ARFMA_RAA_DEC_SEC "ddr5.arfm-a.sec-raa-dec" #define SPD_KEY_DDR5_ARFMB_FLAGS_PRI "ddr5.arfm-b.flags" #define SPD_KEY_DDR5_ARFMB_RAAIMT_PRI "ddr5.arfm-b.raaimt" #define SPD_KEY_DDR5_ARFMB_RAAIMT_FGR_PRI "ddr5.arfm-b.raaimt-fgr" #define SPD_KEY_DDR5_ARFMB_RAAMMT_PRI "ddr5.arfm-b.raammt" #define SPD_KEY_DDR5_ARFMB_RAAMMT_FGR_PRI "ddr5.arfm-b.raammt-fgr" #define SPD_KEY_DDR5_ARFMB_BRC_CFG_PRI "ddr5.arfm-b.brc-config" #define SPD_KEY_DDR5_ARFMB_BRC_SUP_PRI "ddr5.arfm-b.brc-level" #define SPD_KEY_DDR5_ARFMB_RAA_DEC_PRI "ddr5.arfm-b.raa-dec" #define SPD_KEY_DDR5_ARFMB_FLAGS_SEC "ddr5.arfm-b.sec-flags" #define SPD_KEY_DDR5_ARFMB_RAAIMT_SEC "ddr5.arfm-b.sec-raaimt" #define SPD_KEY_DDR5_ARFMB_RAAIMT_FGR_SEC "ddr5.arfm-b.sec-raaimt-fgr" #define SPD_KEY_DDR5_ARFMB_RAAMMT_SEC "ddr5.arfm-b.sec-raammt" #define SPD_KEY_DDR5_ARFMB_RAAMMT_FGR_SEC "ddr5.arfm-b.sec-raammt-fgr" #define SPD_KEY_DDR5_ARFMB_BRC_CFG_SEC "ddr5.arfm-b.sec-brc-config" #define SPD_KEY_DDR5_ARFMB_BRC_SUP_SEC "ddr5.arfm-b.sec-brc-level" #define SPD_KEY_DDR5_ARFMB_RAA_DEC_SEC "ddr5.arfm-b.sec-raa-dec" #define SPD_KEY_DDR5_ARFMC_FLAGS_PRI "ddr5.arfm-c.flags" #define SPD_KEY_DDR5_ARFMC_RAAIMT_PRI "ddr5.arfm-c.raaimt" #define SPD_KEY_DDR5_ARFMC_RAAIMT_FGR_PRI "ddr5.arfm-c.raaimt-fgr" #define SPD_KEY_DDR5_ARFMC_RAAMMT_PRI "ddr5.arfm-c.raammt" #define SPD_KEY_DDR5_ARFMC_RAAMMT_FGR_PRI "ddr5.arfm-c.raammt-fgr" #define SPD_KEY_DDR5_ARFMC_BRC_CFG_PRI "ddr5.arfm-c.brc-config" #define SPD_KEY_DDR5_ARFMC_BRC_SUP_PRI "ddr5.arfm-c.brc-level" #define SPD_KEY_DDR5_ARFMC_RAA_DEC_PRI "ddr5.arfm-c.raa-dec" #define SPD_KEY_DDR5_ARFMC_FLAGS_SEC "ddr5.arfm-c.sec-flags" #define SPD_KEY_DDR5_ARFMC_RAAIMT_SEC "ddr5.arfm-c.sec-raaimt" #define SPD_KEY_DDR5_ARFMC_RAAIMT_FGR_SEC "ddr5.arfm-c.sec-raaimt-fgr" #define SPD_KEY_DDR5_ARFMC_RAAMMT_SEC "ddr5.arfm-c.sec-raammt" #define SPD_KEY_DDR5_ARFMC_RAAMMT_FGR_SEC "ddr5.arfm-c.sec-raammt-fgr" #define SPD_KEY_DDR5_ARFMC_BRC_CFG_SEC "ddr5.arfm-c.sec-brc-config" #define SPD_KEY_DDR5_ARFMC_BRC_SUP_SEC "ddr5.arfm-c.sec-brc-level" #define SPD_KEY_DDR5_ARFMC_RAA_DEC_SEC "ddr5.arfm-c.sec-raa-dec" /* * Module-type specific keys and values. These are often the intersection of * both the DDR standard and the module type. That is, a DDR4 and DDR5 RDIMM * expose some information that isn't quite the same. These often contain things * that are drive strengths and slew rates. These kinds of items fall into two * categories. Ones where there is a fixed resistance and one where there is a * qualitative range that depends on things like the specific parts present. */ typedef enum { SPD_DRIVE_LIGHT, SPD_DRIVE_MODERATE, SPD_DRIVE_STRONG, SPD_DRIVE_VERY_STRONG, SPD_DRIVE_WEAK } spd_drive_t; typedef enum { SPD_SLEW_SLOW, SPD_SLEW_MODERATE, SPD_SLEW_FAST } spd_slew_t; /* * DDR4 RDIMM and LRDIMM drive strengths. These all use the spd_drive_t. These * are all on the RCD. There is also a key for whether or not slew-control is * supported. * * DDR3 has similar, but not identical drive strengths. Rather than trying to * combine them awkwardly, we just have a separate set of definitions. These may * be made more uniform in the future. In DDR3 the LRDIMM does not incorporate a * register like in DDR4, therefore the MB has overlapping drive strength keys. */ #define SPD_KEY_DDR3_RCD_DS_CAA "ddr3.rcd.ca-a-drive-strength" #define SPD_KEY_DDR3_RCD_DS_CAB "ddr3.rcd.ca-b-drive-strength" #define SPD_KEY_DDR3_RCD_DS_CTLA "ddr3.rcd.cs-a-drive-strength" #define SPD_KEY_DDR3_RCD_DS_CTLB "ddr3.rcd.cs-b-drive-strength" #define SPD_KEY_DDR3_RCD_DS_Y0 "ddr3.rcd.y0-drive-strength" #define SPD_KEY_DDR3_RCD_DS_Y1 "ddr3.rcd.y1-drive-strength" #define SPD_KEY_DDR3_MB_DS_Y0 "ddr3.mb.y0-drive-strength" #define SPD_KEY_DDR3_MB_DS_Y1 "ddr3.mb.y1-drive-strength" #define SPD_KEY_DDR3_MB_DS_CKE "ddr3.mb.cke-drive-strength" #define SPD_KEY_DDR3_MB_DS_ODT "ddr3.mb.cke-drive-strength" #define SPD_KEY_DDR3_MB_DS_CS "ddr3.mb.cs-drive-strength" #define SPD_KEY_DDR3_MB_DS_CA "ddr3.mb.ca-drive-strength" #define SPD_KEY_DDR4_RCD_SLEW "ddr4.rcd.rcd-slew-control" /* key */ #define SPD_KEY_DDR4_RCD_DS_CKE "ddr4.rcd.cke-drive-strength" #define SPD_KEY_DDR4_RCD_DS_ODT "ddr4.rcd.odt-drive-strength" #define SPD_KEY_DDR4_RCD_DS_CA "ddr4.rcd.ca-drive-strength" #define SPD_KEY_DDR4_RCD_DS_CS "ddr4.rcd.cs-drive-strength" #define SPD_KEY_DDR4_RCD_DS_Y0 "ddr4.rcd.y0-drive-strength" #define SPD_KEY_DDR4_RCD_DS_Y1 "ddr4.rcd.y1-drive-strength" #define SPD_KEY_DDR4_RCD_DS_BCOM "ddr4.rcd.bcom-drive-strength" #define SPD_KEY_DDR4_RCD_DS_BCK "ddr4.rcd.bck-drive-strength" /* * DDR3 LRDIMMs have the ability to specify the orientation of the memory * buffer. These describe the physical orientation relative to the edge * connector. */ typedef enum { SPD_ORNT_HORIZONTAL, SPD_ORNT_VERTICAL } spd_orientation_t; #define SPD_KEY_DDR3_MB_ORIENT "ddr3.mb.orientation" /* uint32_t */ /* * DDR3 LRDIMMs have various extended and additive clock delays for various * signals. The extended delay is x/128 * tCK while the additive delay is x/32 * * tCK. We store these all as uint32_t keys where the value is the value of x * above. If there is no delay or the delay is not enabled, then the key will * not exist. */ #define SPD_KEY_DDR3_MB_EXTD_Y "ddr4.mb.y-extended-delay" #define SPD_KEY_DDR3_MB_EXTD_CS "ddr4.mb.cs-extended-delay" #define SPD_KEY_DDR3_MB_EXTD_ODT "ddr4.mb.odt-extended-delay" #define SPD_KEY_DDR3_MB_EXTD_CKE "ddr4.mb.cke-extended-delay" #define SPD_KEY_DDR3_MB_ADDD_Y "ddr4.mb.y-additive-delay" #define SPD_KEY_DDR3_MB_ADDD_CS "ddr4.mb.cs-additive-delay" #define SPD_KEY_DDR3_MB_ADDD_ODT "ddr4.mb.odt-additive-delay" #define SPD_KEY_DDR3_MB_ADDD_CKE "ddr4.mb.cke-additive-delay" /* * DDR3 LRDIMMs have the ability to control whether or not QxODT[1:0] is * asserted during reads or writes on each rank. There is a value for each of * the three primary speed buckets in DDR3: 800/1066, 1333/1600, and * 1866/2133. This is organized as a series of boolean_t[3] entries where each * entry corresponds to one of the three speeds. */ #define SPD_KEY_DDR3_MB_R0_ODT0_RD "ddr3.mb.r0-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R0_ODT1_RD "ddr3.mb.r0-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R0_ODT0_WR "ddr3.mb.r0-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R0_ODT1_WR "ddr3.mb.r0-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R1_ODT0_RD "ddr3.mb.r1-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R1_ODT1_RD "ddr3.mb.r1-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R1_ODT0_WR "ddr3.mb.r1-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R1_ODT1_WR "ddr3.mb.r1-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R2_ODT0_RD "ddr3.mb.r2-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R2_ODT1_RD "ddr3.mb.r2-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R2_ODT0_WR "ddr3.mb.r2-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R2_ODT1_WR "ddr3.mb.r2-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R3_ODT0_RD "ddr3.mb.r3-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R3_ODT1_RD "ddr3.mb.r3-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R3_ODT0_WR "ddr3.mb.r3-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R3_ODT1_WR "ddr3.mb.r3-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R4_ODT0_RD "ddr3.mb.r4-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R4_ODT1_RD "ddr3.mb.r4-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R4_ODT0_WR "ddr3.mb.r4-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R4_ODT1_WR "ddr3.mb.r4-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R5_ODT0_RD "ddr3.mb.r5-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R5_ODT1_RD "ddr3.mb.r5-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R5_ODT0_WR "ddr3.mb.r5-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R5_ODT1_WR "ddr3.mb.r5-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R6_ODT0_RD "ddr3.mb.r6-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R6_ODT1_RD "ddr3.mb.r6-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R6_ODT0_WR "ddr3.mb.r6-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R6_ODT1_WR "ddr3.mb.r6-qxodt1-write-assert" #define SPD_KEY_DDR3_MB_R7_ODT0_RD "ddr3.mb.r7-qxodt0-read-assert" #define SPD_KEY_DDR3_MB_R7_ODT1_RD "ddr3.mb.r7-qxodt1-read-assert" #define SPD_KEY_DDR3_MB_R7_ODT0_WR "ddr3.mb.r7-qxodt0-write-assert" #define SPD_KEY_DDR3_MB_R7_ODT1_WR "ddr3.mb.r7-qxodt1-write-assert" /* * DDR4 LRDIMMs specify the VrefDQ for each package rank. These are communicated * in terms of the DDR4 spec which specifies them as a percentage of the actual * voltage. This is always phrased in the spec as AB.CD%, so for example 60.25%. * We treat this percentage as a four digit unsigned value rather than trying to * play games with whether or not the value can be represented in floating * point. Divide the value by 100 to get the percentage. That is, 47.60% will be * encoded as 4760. All of these values are a uint32_t. */ #define SPD_KEY_DDR4_VREFDQ_R0 "ddr4.lrdimm.VrefDQ-rank0" #define SPD_KEY_DDR4_VREFDQ_R1 "ddr4.lrdimm.VrefDQ-rank1" #define SPD_KEY_DDR4_VREFDQ_R2 "ddr4.lrdimm.VrefDQ-rank2" #define SPD_KEY_DDR4_VREFDQ_R3 "ddr4.lrdimm.VrefDQ-rank3" #define SPD_KEY_DDR4_VREFDQ_DB "ddr4.lrdimm.VrefDQ-db" /* * DDR4 LRDIMMs define the data buffer drive strength and termination in terms * of various data rate ranges. Specifically (0, 1866], (1866, 2400], and (2400, * 3200]. All of these values are measured in terms of Ohms. As such, all of * these values are an array of three uint32_t's whose values correspond to each * of those ranges. We define a few additional values for these to represent * cases where they are disabled or high-impedance. * * DDR3 LRDIMMs are similar, but their groups are 800/1066, 1333/1600, and * 1866/2133. */ #define SPD_TERM_DISABLED 0 #define SPD_TERM_HIZ UINT32_MAX #define SPD_KEY_DDR4_MDQ_RTT "ddr4.lrdimm.mdq-read-termination" #define SPD_KEY_DDR4_MDQ_DS "ddr4.lrdimm.mdq-drive-strength" #define SPD_KEY_DDR4_DRAM_DS "ddr4.lrdimm.dram-drive-strength" #define SPD_KEY_DDR4_RTT_WR "ddr4.lrdimm.odt-read-termination-wr" #define SPD_KEY_DDR4_RTT_NOM "ddr4.lrdimm.odt-read-termination-nom" #define SPD_KEY_DDR4_RTT_PARK_R0 "ddr4.lrdimm.odt-r0_1-rtt-park" #define SPD_KEY_DDR4_RTT_PARK_R2 "ddr4.lrdimm.odt-r2_3-rtt-park" #define SPD_KEY_DDR3_MDQ_DS "ddr3.lrdimm.mdq-drive-strength" #define SPD_KEY_DDR3_MDQ_ODT "ddr3.lrdimm.mdq-odt-strength" #define SPD_KEY_DDR3_RTT_WRT "ddr3.lrdimm.mdq-odt-read-termination-wr" #define SPD_KEY_DDR3_RTT_NOM "ddr3.lrdimm.mdq-odt-read-termination-nom" #define SPD_KEY_DDR3_DRAM_DS "ddr3.lrdimm.dram-drive-strength" /* * DDR3 LRDIMMs specify a minimum and maximum delay for the various supported * voltage types. These are stored as two uint64_t[3] arrays ordered as 1.25, * 1.35, and 1.5V. These are times in ps. */ #define SPD_KEY_DDR3_MOD_MIN_DELAY "ddr3.lrdimm.minimum-module-delay" #define SPD_KEY_DDR3_MOD_MAX_DELAY "ddr3.lrdimm.maximum-module-delay" /* * DDR3 LRDIMMs also have personality bytes that are loaded directly into the * memory buffer control words. We pass these through as a uint8_t[15]. */ #define SPD_KEY_DDR3_MB_PERS "ddr3.lrdimm.personality" /* * The last DDR4 LRDIMM specific component is whether or not the data buffer's * gain and decision feedback equalization are supported. These both are keys. */ #define SPD_KEY_DDR4_DB_GAIN "ddr4.lrdimm.db-gain-adjustment" #define SPD_KEY_DDR4_DB_DFE "ddr4.lrdimm.decision-feedback-eq" /* * DDR5 RDIMMs and LRDIMMs have specific enables for groups of pins. There are * then drive strength values which are encoded as a spd_drive_t. Note, prior to * DDR5 RDIMMs v1.1, these were differential impedance values measured in Ohms. * These have been normalized to the general drive strength enums. Separately * there are slew rates, those use the spd_slew_t. Because these use different * units between DDR4 and DDR5, we treat them as different keys. */ #define SPD_KEY_DDR5_RCD_QACK_EN "ddr5.rcd.qack-enabled" #define SPD_KEY_DDR5_RCD_QBCK_EN "ddr5.rcd.qbck-enabled" #define SPD_KEY_DDR5_RCD_QCCK_EN "ddr5.rcd.qcck-enabled" #define SPD_KEY_DDR5_RCD_QDCK_EN "ddr5.rcd.qdck-enabled" #define SPD_KEY_DDR5_RCD_BCK_EN "ddr5.rcd.bck-enabled" #define SPD_KEY_DDR5_RCD_QACA_EN "ddr5.rcd.qaca-enabled" #define SPD_KEY_DDR5_RCD_QBCA_EN "ddr5.rcd.qbca-enabled" #define SPD_KEY_DDR5_RCD_QxCS_EN "ddr5.rcd.qxcs-enabled" #define SPD_KEY_DDR5_RCD_QxCA13_EN "ddr5.rcd.qxca13-enabled" #define SPD_KEY_DDR5_RCD_QACS_EN "ddr5.rcd.qacs-enabled" #define SPD_KEY_DDR5_RCD_QBCS_EN "ddr5.rcd.qbcs-enabled" /* Drive strengths use the spd_drive_t encoded as a uint32_t */ #define SPD_KEY_DDR5_RCD_QACK_DS "ddr5.rcd.qack-drive-strength" #define SPD_KEY_DDR5_RCD_QBCK_DS "ddr5.rcd.qbck-drive-strength" #define SPD_KEY_DDR5_RCD_QCCK_DS "ddr5.rcd.qcck-drive-strength" #define SPD_KEY_DDR5_RCD_QDCK_DS "ddr5.rcd.qdck-drive-strength" #define SPD_KEY_DDR5_RCD_QxCS_DS "ddr5.rcd.qxcs-drive-strength" #define SPD_KEY_DDR5_RCD_CA_DS "ddr5.rcd.ca-drive-strength" /* Slew rates use the spd_rate_t encoded as a uint32_t */ #define SPD_KEY_DDR5_RCD_QCK_SLEW "ddr5.rcd.qck-slew" #define SPD_KEY_DDR5_RCD_QCA_SLEW "ddr5.rcd.qca-slew" #define SPD_KEY_DDR5_RCD_QCS_SLEW "ddr5.rcd.qcs-slew" /* * These are all specific to DDR5 LRDIMMs. The values are the same as above. The * RTT value is a value in Ohms. If RTT termination is disabled then the key * will not be present. */ #define SPD_KEY_DDR5_RCD_BCS_EN "ddr5.rcd.bcs-enabled" /* key */ #define SPD_KEY_DDR5_RCD_BCOM_DS "ddr5.rcd.bcom-drive-strength" #define SPD_KEY_DDR5_RCD_BCK_DS "ddr5.rcd.bck-drive-strength" #define SPD_KEY_DDR5_RCD_RTT_TERM "ddr5.rcd.dqs-rtt" #define SPD_KEY_DDR5_RCD_BCOM_SLEW "ddr5.rcd.bcom-slew" #define SPD_KEY_DDR5_RCD_BCK_SLEW "ddr5.rcd.bck-slew" /* * DDR5 UDIMM specific values. Note, these are only present in UDIMM v1.1 and * therefore may be missing in older revisions. */ /* * Unbuffered clock configuration, drivers, and slew rates. The various -enabled * values are keys. The drive strengths and slew rates use the spd_drive_t and * spd_slew_t respectively encoded as uint32_t values. */ #define SPD_KEY_DDR5_CKD_CHAQCK0_EN "ddr5.ckd.cha-qck0_A-enabled" #define SPD_KEY_DDR5_CKD_CHAQCK1_EN "ddr5.ckd.cha-qck1_A-enabled" #define SPD_KEY_DDR5_CKD_CHBQCK0_EN "ddr5.ckd.chb-qck0_B-enabled" #define SPD_KEY_DDR5_CKD_CHBQCK1_EN "ddr5.ckd.chb-qck1_B-enabled" #define SPD_KEY_DDR5_CKD_CHAQCK0_DS "ddr5.ckd.cha-qck0_A-drive-strength" #define SPD_KEY_DDR5_CKD_CHAQCK1_DS "ddr5.ckd.cha-qck1_A-drive-strength" #define SPD_KEY_DDR5_CKD_CHBQCK0_DS "ddr5.ckd.chb-qck0_B-drive-strength" #define SPD_KEY_DDR5_CKD_CHBQCK1_DS "ddr5.ckd.chb-qck1_B-drive-strength" #define SPD_KEY_DDR5_CKD_CHAQCK_SLEW "ddr5.ckd.cha-qck_slew" #define SPD_KEY_DDR5_CKD_CHBQCK_SLEW "ddr5.ckd.chb-qck_slew" /* * DDR5 MRDIMM specific values. Note, these are only present in MRDIMM v1.1 and * therefore may be missing in older revisions. While these values are really * similar to the RDIMM variants, because they are taken from the MRCD instead * of the RCD specification, we define different keys. */ #define SPD_KEY_DDR5_MRCD_QACK_EN "ddr5.mrcd.qack-enabled" #define SPD_KEY_DDR5_MRCD_QBCK_EN "ddr5.mrcd.qbck-enabled" #define SPD_KEY_DDR5_MRCD_QCCK_EN "ddr5.mrcd.qcck-enabled" #define SPD_KEY_DDR5_MRCD_QDCK_EN "ddr5.mrcd.qdck-enabled" #define SPD_KEY_DDR5_MRCD_BCK_EN "ddr5.mrcd.bck-enabled" #define SPD_KEY_DDR5_MRCD_QACA_EN "ddr5.mrcd.qaca-enabled" #define SPD_KEY_DDR5_MRCD_QBCA_EN "ddr5.mrcd.qbca-enabled" #define SPD_KEY_DDR5_MRCD_BCS_EN "ddr5.mrcd.bcs-enabled" #define SPD_KEY_DDR5_MRCD_QxCS_EN "ddr5.mrcd.qxcs-enabled" #define SPD_KEY_DDR5_MRCD_QxCA13_EN "ddr5.mrcd.qxca13-enabled" #define SPD_KEY_DDR5_MRCD_QACS_EN "ddr5.mrcd.qacs-enabled" #define SPD_KEY_DDR5_MRCD_QBCS_EN "ddr5.mrcd.qbcs-enabled" #define SPD_KEY_DDR5_MRCD_DCS1_EN "ddr5.mrcd.dcs1-enabled" /* Drive strengths use the spd_drive_t encoded as a uint32_t */ #define SPD_KEY_DDR5_MRCD_QACK_DS "ddr5.mrcd.qack-drive-strength" #define SPD_KEY_DDR5_MRCD_QBCK_DS "ddr5.mrcd.qbck-drive-strength" #define SPD_KEY_DDR5_MRCD_QCCK_DS "ddr5.mrcd.qcck-drive-strength" #define SPD_KEY_DDR5_MRCD_QDCK_DS "ddr5.mrcd.qdck-drive-strength" #define SPD_KEY_DDR5_MRCD_QxCS_DS "ddr5.mrcd.qxcs-drive-strength" #define SPD_KEY_DDR5_MRCD_CA_DS "ddr5.mrcd.ca-drive-strength" #define SPD_KEY_DDR5_MRCD_BCOM_DS "ddr5.mrcd.bcom-drive-strength" #define SPD_KEY_DDR5_MRCD_BCK_DS "ddr5.mrcd.bck-drive-strength" /* Slew rates use the spd_rate_t encoded as a uint32_t */ #define SPD_KEY_DDR5_MRCD_QCK_SLEW "ddr5.mrcd.qck-slew" #define SPD_KEY_DDR5_MRCD_QCA_SLEW "ddr5.mrcd.qca-slew" #define SPD_KEY_DDR5_MRCD_QCS_SLEW "ddr5.mrcd.qcs-slew" #define SPD_KEY_DDR5_MRCD_BCOM_SLEW "ddr5.mrcd.bcom-slew" #define SPD_KEY_DDR5_MRCD_BCK_SLEW "ddr5.mrcd.bck-slew" typedef enum { SPD_MRCD_OUT_NORMAL, SPD_MRCD_OUT_DISABLED, SPD_MRCD_OUT_LOW } spd_mrcd_output_ctrl_t; #define SPD_KEY_DDR5_MRCD_QxCS_OUT "ddr5.mrcd.qxcs-output-control" typedef enum { SPD_MRCD_DCA_CFG_0, SPD_MRCD_DCA_CFG_1 } spd_mrcd_dca_cfg_t; #define SPD_KEY_DDR5_MRCD_DCA_CFG "ddr5.mrcd.dca-configuration" typedef enum { SPD_MRDIMM_IRXT_UNMATCHED, SPD_MRDIMM_IRXT_MATCHED } spd_mrdimm_irxt_t; #define SPD_KEY_DDR5_MRDIMM_IRXT "ddr5.mrdimm.interface-rx-type" /* * Module Properties. These are items that generally relate to the module as a * whole. */ /* * Connection Mapping. In DDR4 there is the ability to remap groups of pins from * the connector to the various package SDRAMs. Every 4 bits can be remapped to * either another upper or lower nibble in a package. Separately bits can also * be flipped between packages. These exist for all 64-bits of DQ and 8 bits of * CBs. If mirroring is set, then a key will be added for that pin group. For * each pin group, the mapping to a specific type of rewriting will be done. We * conventionally use 0, 1, 2, and 3 as the lower nibble and 4, 5, 6, 7 as the * upper nibble, though the actual pins will vary based on where they are. */ #define SPD_KEY_DDR4_MAP_DQ0 "module.dq0-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ4 "module.dq4-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ8 "module.dq8-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ12 "module.dq12-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ16 "module.dq16-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ20 "module.dq20-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ24 "module.dq24-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ28 "module.dq28-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ32 "module.dq32-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ36 "module.dq36-map" /* uint36_t [4] */ #define SPD_KEY_DDR4_MAP_DQ40 "module.dq40-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ44 "module.dq44-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ48 "module.dq48-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ52 "module.dq52-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ56 "module.dq56-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_DQ60 "module.dq60-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_CB0 "module.cb0-map" /* uint32_t [4] */ #define SPD_KEY_DDR4_MAP_CB4 "module.cb4-map" /* uint32_t [4] */ /* * In addition, there is module level mapping in DDR3/DDR4 that is used to * indicate that odd ranks are mirrored. This is between the edge connector and * the DRAM itself. We only add a key when it is mirrored. */ #define SPD_KEY_MOD_EDGE_MIRROR "module.edge-odd-mirror" /* key */ /* * Present devices. Modules often have multiple additional types of devices * present like temperature sensors, voltage regulators, registers, etc. The * following key indicates what all is present on this DIMM. Depending on the * DDR revision, we will then have additional keys with its ID, revision, name, * and compliant type. In a few cases we will define the type and presence based * on information. For example, DDR4 only allows a single type of temperature * sensor or SPD device. Even though we don't know the manufacturer, we will * still note this. * * Each of these items will have four keys. One for the manufacturer ID, one for * their string name, one for the device type, and one for the revision. Note, * while TS1 and TS2 are both flags in DDR5, they share common manufacturer * information, which is why there is only one entry here. * * For each device type there is a separate enum with supported types of devices * that can be present for these. */ typedef enum { SPD_DEVICE_TEMP_1 = 1 << 0, SPD_DEVICE_TEMP_2 = 1 << 1, SPD_DEVICE_HS = 1 << 2, SPD_DEVICE_PMIC_0 = 1 << 3, SPD_DEVICE_PMIC_1 = 1 << 4, SPD_DEVICE_PMIC_2 = 1 << 5, SPD_DEVICE_CD_0 = 1 << 6, SPD_DEVICE_CD_1 = 1 << 7, SPD_DEVICE_RCD = 1 << 8, SPD_DEVICE_DB = 1 << 9, SPD_DEVICE_MRCD = 1 << 10, SPD_DEVICE_MDB = 1 << 11, SPD_DEVICE_DMB = 1 << 12, SPD_DEVICE_SPD = 1 << 13 } spd_device_t; #define SPD_KEY_DEVS "module.devices" /* uint32_t */ typedef enum { /* DDR3 */ SPD_TEMP_T_TSE2002, /* DDR4 and LPDDR4 */ SPD_TEMP_T_TSE2004av, /* DDR5 */ SPD_TEMP_T_TS5111, SPD_TEMP_T_TS5110, SPD_TEMP_T_TS5211, SPD_TEMP_T_TS5210 } spd_temp_type_t; typedef enum { /* DDR5 */ SPD_PMIC_T_PMIC5000, SPD_PMIC_T_PMIC5010, SPD_PMIC_T_PMIC5100, SPD_PMIC_T_PMIC5020, SPD_PMIC_T_PMIC5120, SPD_PMIC_T_PMIC5200, SPD_PMIC_T_PMIC5030 } spd_pmic_type_t; typedef enum { /* DDR5 */ SPD_CD_T_DDR5CK01 } spd_cd_type_t; typedef enum { /* DDR3 */ SPD_RCD_T_SSTE32882, /* DDR4 */ SPD_RCD_T_DDR4RCD01, SPD_RCD_T_DDR4RCD02, /* DDR5 */ SPD_RCD_T_DDR5RCD01, SPD_RCD_T_DDR5RCD02, SPD_RCD_T_DDR5RCD03, SPD_RCD_T_DDR5RCD04, SPD_RCD_T_DDR5RCD05 } spd_rcd_type_t; typedef enum { /* DDR4 */ SPD_DB_T_DDR4DB01, SPD_DB_T_DDR4DB02, /* DDR5 */ SPD_DB_T_DDR5DB01, SPD_DB_T_DDR5DB02, /* * DDR3 LRDIMMs had a memory buffer that did not have a full * desgination. We count them here. */ SPD_DB_T_DDR3MB } spd_db_type_t; typedef enum { /* DDR5 */ SPD_MRCD_T_DDR5MRCD01, SPD_MRCD_T_DDR5MRCD02, } spd_mrcd_type_t; typedef enum { /* DDR5 */ SPD_MDB_T_DDR5MDB01, SPD_MDB_T_DDR5MDB02 } spd_mdb_type_t; typedef enum { /* DDR5 */ SPD_DMB_T_DMB5011 } spd_dmb_type_t; typedef enum { /* DDR4 */ SPD_SPD_T_EE1004, /* DDR5 */ SPD_SPD_T_SPD5118, SPD_SPD_T_ESPD5216, /* DDR3 */ SPD_SPD_T_EE1002 } spd_spd_type_t; #define SPD_KEY_DEV_TEMP_MFG "module.temp.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_TEMP_MFG_NAME "module.temp.mfg-name" /* string */ #define SPD_KEY_DEV_TEMP_TYPE "module.temp.type" /* uint32_t */ #define SPD_KEY_DEV_TEMP_REV "module.temp.revision" /* string */ #define SPD_KEY_DEV_PMIC0_MFG "module.pmic0.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_PMIC0_MFG_NAME "module.pmic0.mfg-name" /* string */ #define SPD_KEY_DEV_PMIC0_TYPE "module.pmic0.type" /* uint32_t */ #define SPD_KEY_DEV_PMIC0_REV "module.pmic0.revision" /* string */ #define SPD_KEY_DEV_PMIC1_MFG "module.pmic1.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_PMIC1_MFG_NAME "module.pmic1.mfg-name" /* string */ #define SPD_KEY_DEV_PMIC1_TYPE "module.pmic1.type" /* uint32_t */ #define SPD_KEY_DEV_PMIC1_REV "module.pmic1.revision" /* string */ #define SPD_KEY_DEV_PMIC2_MFG "module.pmic2.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_PMIC2_MFG_NAME "module.pmic2.mfg-name" /* string */ #define SPD_KEY_DEV_PMIC2_TYPE "module.pmic2.type" /* uint32_t */ #define SPD_KEY_DEV_PMIC2_REV "module.pmic2.revision" /* string */ #define SPD_KEY_DEV_CD0_MFG "module.cd0.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_CD0_MFG_NAME "module.cd0.mfg-name" /* string */ #define SPD_KEY_DEV_CD0_TYPE "module.cd0.type" /* uint32_t */ #define SPD_KEY_DEV_CD0_REV "module.cd0.revision" /* string */ #define SPD_KEY_DEV_CD1_MFG "module.cd1.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_CD1_MFG_NAME "module.cd1.mfg-name" /* string */ #define SPD_KEY_DEV_CD1_TYPE "module.cd1.type" /* uint32_t */ #define SPD_KEY_DEV_CD1_REV "module.cd1.revision" /* string */ #define SPD_KEY_DEV_RCD_MFG "module.rcd.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_RCD_MFG_NAME "module.rcd.mfg-name" /* string */ #define SPD_KEY_DEV_RCD_TYPE "module.rcd.type" /* uint32_t */ #define SPD_KEY_DEV_RCD_REV "module.rcd.revision" /* string */ #define SPD_KEY_DEV_DB_MFG "module.db.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_DB_MFG_NAME "module.db.mfg-name" /* string */ #define SPD_KEY_DEV_DB_TYPE "module.db.type" /* uint32_t */ #define SPD_KEY_DEV_DB_REV "module.db.revision" /* string */ #define SPD_KEY_DEV_MRCD_MFG "module.mrcd.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_MRCD_MFG_NAME "module.mrcd.mfg-name" /* string */ #define SPD_KEY_DEV_MRCD_TYPE "module.mrcd.type" /* uint32_t */ #define SPD_KEY_DEV_MRCD_REV "module.mrcd.revision" /* string */ #define SPD_KEY_DEV_MDB_MFG "module.mdb.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_MDB_MFG_NAME "module.mdb.mfg-name" /* string */ #define SPD_KEY_DEV_MDB_TYPE "module.mdb.type" /* uint32_t */ #define SPD_KEY_DEV_MDB_REV "module.mdb.revision" /* string */ #define SPD_KEY_DEV_DMB_MFG "module.dmb.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_DMB_MFG_NAME "module.dmb.mfg-name" /* string */ #define SPD_KEY_DEV_DMB_TYPE "module.dmb.type" /* uint32_t */ #define SPD_KEY_DEV_DMB_REV "module.dmb.revision" /* string */ #define SPD_KEY_DEV_SPD_MFG "module.spd.mfg-id" /* uint32_t [2] */ #define SPD_KEY_DEV_SPD_MFG_NAME "module.spd.mfg-name" /* string */ #define SPD_KEY_DEV_SPD_TYPE "module.spd.type" /* uint32_t */ #define SPD_KEY_DEV_SPD_REV "module.spd.revision" /* string */ /* * Module physical dimensions. DRAM modules provide information about their * height and their front and back thicknesses. All values are in millimeters. * In general, values are defined as 1 mm ranges in the form such as 18mm < * height <= 19mm or 2mm < thickness <= 3mm. As such in all these ranges we * encode it as the less than or equal to side of the thickness or height. * * However, at the extremes of thickness and height, it can be arbitrary. The * minimum height can be any value <= 15mm and the maximum is just > 45mm. * Similarly the maximum thickness is just any value greater than 15mm. For * these values, we define aliases that can be used to indicate we're in these * conditions for the height and thickness, allowing this to otherwise be the * common well understood value. */ #define SPD_MOD_HEIGHT_LT15MM 15 #define SPD_MOD_HEIGHT_GT45MM 46 #define SPD_KEY_MOD_HEIGHT "module.height" /* uint32_t */ #define SPD_MOD_THICK_GT15MM 16 #define SPD_KEY_MOD_FRONT_THICK "module.front-thickness" /* uint32_t */ #define SPD_KEY_MOD_BACK_THICK "module.back-thickness" /* uint32_t */ /* * This is the number of rows of DRAM dies on the module. In addition, DDR3 and * DDR4 provides the number of registers present on the device. This is not * present in DDR5. */ #define SPD_KEY_MOD_NROWS "module.dram-die-rows" /* uint32_t */ #define SPD_KEY_MOD_NREGS "module.total-registers" /* uint32_t */ /* * Operating temperature ranges. These ranges are defined by JEDEC. The code can * be translated with libjedec_temp_range() to transform it into a pair of * values. */ #define SPD_KEY_MOD_OPER_TEMP "module.operating-temperature" /* uint32_t */ /* * Module reference card and design revision. JEDEC provides various reference * designs for modules and revisions of those. */ #define SPD_KEY_MOD_REF_DESIGN "module.reference-design" /* string */ #define SPD_KEY_MOD_DESIGN_REV "module.design-revision" /* uint32_t */ /* * Manufacturing Section. These keys are present if manufacturing related * information is made available. This space is not DIMM-revision specific. All * fields are defined in DDR4 and DDR5. Note, the SPD_KEY_MFG_DRAM_STEP is * optional and therefore an invalid value will result in this not being * present. */ #define SPD_KEY_MFG_MOD_MFG_ID "mfg.module-mfg-id" /* uint32[2] */ #define SPD_KEY_MFG_MOD_MFG_NAME "mfg.module-mfg-name" /* string */ #define SPD_KEY_MFG_DRAM_MFG_ID "mfg.dram-mfg-id" /* uint32[2] */ #define SPD_KEY_MFG_DRAM_MFG_NAME "mfg.dram-mfg-name" /* string */ #define SPD_KEY_MFG_MOD_LOC_ID "mfg.module-loc-id" /* uint32 */ #define SPD_KEY_MFG_MOD_YEAR "mfg.module-year" /* string */ #define SPD_KEY_MFG_MOD_WEEK "mfg.module-week" /* string */ #define SPD_KEY_MFG_MOD_PN "mfg.module-pn" /* string */ #define SPD_KEY_MFG_MOD_SN "mfg.module-sn" /* string */ #define SPD_KEY_MFG_MOD_REV "mfg.module-rev" /* string */ #define SPD_KEY_MFG_DRAM_STEP "mfg.dram-step" /* string */ /* * The errors nvlist_t is designed such that it is a nested nvlist_t in the * returned data. Each key in that nvlist_t corresponds to a key that we would * otherwise produce. Each key is an nvlist_t that has two keys, a 'code' and a * 'message'. * * There is currently an additional top-level special key. This is the * 'incomplete' key. When data is too short to process an entry, rather than * flag every possible missing key (as most times the consumer will know the * amount of data they have), for the time being we will insert a single * incomplete key with a uint32_t whose value indicates the starting offset of * the key that we could not process. Note, this may not be the first byte that * was missing (if we had 100 bytes and a 20 byte key at offset 90, we would * insert 90). */ typedef enum { /* * Indicates that the error occurred because we could not translate a * given piece of information. For example, a value that we didn't know * or a failure to look up something in a string table. */ SPD_ERROR_NO_XLATE, /* * This indicates that we encountered an non-ASCII or unprintable * character in an SPD string which should not be allowed per se. */ SPD_ERROR_UNPRINT, /* * This indicates that there was no data for a given key. For example, a * string that was all padded spaces. */ SPD_ERROR_NO_DATA, /* * Indicates that some kind of internal error occurred. */ SPD_ERROR_INTERNAL, /* * This indicates that there's something suspicious or weird to us about * the data in question. The most common case is a bad CRC. */ SPD_ERROR_BAD_DATA } spd_error_kind_t; #define SPD_KEY_INCOMPLETE "incomplete" /* uint32_t */ #define SPD_KEY_ERRS "errors" /* nvlist_t */ #define SPD_KEY_ERRS_CODE "code" /* uint32_t */ #define SPD_KEY_ERRS_MSG "message" /* string */ #ifdef __cplusplus } #endif #endif /* _LIBJEDEC_H */