From 01c730dd41576de25c422070d89e5117ed6975dc Mon Sep 17 00:00:00 2001 From: Stephen Glancy Date: Mon, 27 Nov 2017 14:47:16 -0600 Subject: Updates WR VREF for characterization results Change-Id: I9803ee77afa2ebc32e12d9d528ac4ae6a6c947f8 CQ:SW411492 Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/50709 Dev-Ready: STEPHEN GLANCY Tested-by: FSP CI Jenkins Reviewed-by: Louis Stermole Reviewed-by: ANDRE A. MARIN Tested-by: Jenkins Server Tested-by: HWSV CI Tested-by: Hostboot CI Reviewed-by: Jennifer A. Stofer Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/50713 Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Reviewed-by: Daniel M. Crowell --- .../hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.H | 93 ++- .../p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H | 72 ++- .../p9/procedures/hwp/memory/lib/dimm/eff_dimm.C | 97 +-- .../p9/procedures/hwp/memory/lib/dimm/eff_dimm.H | 25 +- .../chips/p9/procedures/hwp/memory/lib/dimm/rank.H | 4 +- .../chips/p9/procedures/hwp/memory/lib/fir/check.C | 4 +- .../hwp/memory/lib/mss_attribute_accessors.H | 23 +- .../chips/p9/procedures/hwp/memory/lib/phy/dp16.C | 461 +++++++++++++- .../chips/p9/procedures/hwp/memory/lib/phy/dp16.H | 48 +- .../procedures/hwp/memory/lib/phy/mss_training.C | 237 +++++++- .../procedures/hwp/memory/lib/phy/mss_training.H | 42 +- .../procedures/hwp/memory/lib/shared/mss_const.H | 23 +- .../hwp/memory/lib/workarounds/dp16_workarounds.C | 662 ++++++++++++++++++++- .../hwp/memory/lib/workarounds/dp16_workarounds.H | 236 +++++++- .../p9/procedures/hwp/memory/p9_mss_eff_config.C | 8 +- 15 files changed, 1920 insertions(+), 115 deletions(-) (limited to 'src/import/chips/p9/procedures/hwp/memory') diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.H index 746b1621e..89f9539ae 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs_load_ddr4.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -830,6 +830,9 @@ struct mrs00_data /// mrs00_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS00 default constructor + mrs00_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -873,6 +876,16 @@ struct mrs00_data return iv_burst_length < i_rhs.iv_burst_length; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs00_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_burst_length; uint8_t iv_read_burst_type; uint8_t iv_dll_reset; @@ -905,6 +918,9 @@ struct mrs01_data /// mrs01_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS01 default constructor + mrs01_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -958,6 +974,16 @@ struct mrs01_data return iv_dll_enable < i_rhs.iv_dll_enable; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs01_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_dll_enable; uint8_t iv_odic[MAX_RANK_PER_DIMM]; uint8_t iv_additive_latency; @@ -991,6 +1017,9 @@ struct mrs02_data /// mrs02_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS02 default constructor + mrs02_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -1024,6 +1053,16 @@ struct mrs02_data return iv_cwl < i_rhs.iv_cwl; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs02_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_lpasr; uint8_t iv_cwl; uint8_t iv_write_crc; @@ -1055,6 +1094,9 @@ struct mrs03_data /// mrs03_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS03 default constructor + mrs03_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -1110,6 +1152,16 @@ struct mrs03_data return iv_mpr_page < i_rhs.iv_mpr_page; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs03_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_mpr_mode; uint8_t iv_mpr_page; uint8_t iv_geardown; @@ -1144,6 +1196,9 @@ struct mrs04_data /// mrs04_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS04 default constructor + mrs04_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -1217,6 +1272,16 @@ struct mrs04_data return iv_max_pd_mode < i_rhs.iv_max_pd_mode; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs04_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_max_pd_mode; uint8_t iv_temp_refresh_range; uint8_t iv_temp_ref_mode; @@ -1254,6 +1319,9 @@ struct mrs05_data /// mrs05_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS05 default constructor + mrs05_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -1317,6 +1385,16 @@ struct mrs05_data return iv_ca_parity_latency < i_rhs.iv_ca_parity_latency; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs05_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_ca_parity_latency; uint8_t iv_crc_error_clear; uint8_t iv_ca_parity_error_status; @@ -1352,6 +1430,9 @@ struct mrs06_data /// mrs06_data( const fapi2::Target& i_target, fapi2::ReturnCode& o_rc ); + // Delete MRS06 default constructor + mrs06_data() = delete; + /// /// @brief Less than operator /// @param[in] i_rhs right hand comparison operator @@ -1388,6 +1469,16 @@ struct mrs06_data return memcmp(iv_vrefdq_train_value, i_rhs.iv_vrefdq_train_value, sizeof(i_rhs.iv_vrefdq_train_value)) < MEMCMP_EQUAL; } + /// + /// @brief Equal to operator + /// @param[in] i_rhs right hand comparison operator + /// @bool true if this object is equal to i_rhs + /// + bool operator==(const mss::ddr4::mrs06_data& i_rhs) const + { + return !((*this < i_rhs) || (i_rhs < *this)); + } + uint8_t iv_vrefdq_train_value[MAX_RANK_PER_DIMM]; uint8_t iv_vrefdq_train_range[MAX_RANK_PER_DIMM]; uint8_t iv_vrefdq_train_enable[MAX_RANK_PER_DIMM]; diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H index 271e90368..748907d44 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2017 */ +/* Contributors Listed Below - COPYRIGHT 2017,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -208,7 +208,8 @@ class commands public: // Typdefs to make the code more readable typedef std::pair, uint64_t> rank_target; - typedef std::map > mrs_drams; + typedef std::pair > mrs_dram; + typedef std::vector mrs_drams_vect; /// /// @brief Base constructor @@ -267,7 +268,57 @@ class commands set_FUNCTION(mss::ffdc_function_codes::PDA_ADD_COMMAND), "%s does not have rank %lu", mss::c_str(i_target), i_rank); - iv_commands[ {i_target, i_rank}][i_mrs].push_back(i_dram); + // Does the compression + { + // If the rank/target pair does not exist simply insert a new pair + const rank_target RANK_TARGET = {i_target, i_rank}; + // Note: technically this should be const auto to keep the iterator at the found position + // However, HB asumes that the const in front of the auto makes this a const_iterator + // Note: Find value from key prints an error if the key could not be found. We don't want that + // Rolling our own below + // TODO:RTC184689 Create find_iterator_from_value and find_iterator_from_key + auto l_it = std::find_if(iv_commands.begin(), + iv_commands.end(), [&RANK_TARGET](const std::pair& i_rhs) + { + return RANK_TARGET == i_rhs.first; + }); + + if( l_it == iv_commands.end() ) + { + FAPI_INF("%s rank%lu NEW target rank info DRAM%lu", mss::c_str(i_target), i_rank, i_dram); + mrs_drams_vect l_mrs_dram = {{ i_mrs, { i_dram } }}; + iv_commands.push_back( { RANK_TARGET, l_mrs_dram } ); + } + // The rank/target exist + else + { + // Does the MRS exist? + auto& l_mrs_vect = l_it->second; + // Note: technically this should be const auto to keep the iterator at the found position + // However, HB asumes that the const in front of the auto makes this a const_iterator + // Note: Find value from key prints an error if the key could not be found. We don't want that + // Rolling our own below + // TODO:RTC184689 Create find_iterator_from_value and find_iterator_from_key + auto l_mrs_it = std::find_if(l_mrs_vect.begin(), l_mrs_vect.end(), [&i_mrs](const mrs_dram & i_rhs) + { + return i_mrs == i_rhs.first; + }); + + // No, add a new DRAM mapping + if( l_mrs_it == l_mrs_vect.end() ) + { + FAPI_INF("%s rank%lu inserting new DRAM + MRS info DRAM%lu", mss::c_str(i_target), i_rank, i_dram); + const mrs_dram MRS_DRAM = { i_mrs, { i_dram } }; + l_mrs_vect.push_back( MRS_DRAM ); + } + // Yes, add a DRAM onto the vector + else + { + l_mrs_it->second.push_back( i_dram ); + FAPI_INF("%s rank%lu pushing back DRAM%lu size %lu", mss::c_str(i_target), i_rank, i_dram, l_mrs_it->second.size()); + } + } + } fapi_try_exit: return fapi2::current_err; @@ -294,23 +345,24 @@ class commands /// @brief Returns the command information /// @return iv_commands /// - inline const typename std::map& get() const + inline const typename std::vector>& get() const { return iv_commands; } private: - // The following is a map of targets/DIMM as the key to a map of + // The following is a vector of target/DIMM pairs as the key to a vector of // the MRS command as the key to the DRAM's to toggle. An explanation as to the data structure is included below + // Note: due to HB compile, a vector is used instead of a map // PDA compression is a little complex, but is organized to allow us to minimize the number of commands run - // Each individual map is designed to further minimize the number of commands run - // The compressed commands consist of a map within a map - // The outside map, maps the DIMM/rank to the MRS command and DRAM's that need to be run + // Each individual vector is designed to further minimize the number of commands run + // The compressed commands consist of a vector of pairs within a vector of pairs + // The outside vector, maps the DIMM/rank to the MRS command and DRAM's that need to be run // Basically, it's a list of a specific rank target with all the commands that need to be run // The rank-specific target information allows us to just issue the enter/exit commands for PDA for each rank once // The MRS commands to the DRAM are then looped over in the inside loop - // The inside map has a key of the MRS and a value of a vector of DRAM's to issue the MRS to + // The inside vector has a key of the MRS and a value of a vector of DRAM's to issue the MRS to // CCS does not allow the user to toggle the DQ during an MRS command // The DQ information is stored in separate registers in the PHY // What this means for issuing the commands is that we have to issue an invocation of CCS for each different MRS command we issue @@ -318,7 +370,7 @@ class commands // Each invocation of CCS creates a noticable increase in time, as the registers need to be configured, CCS needs to be started, and we need to poll for done // By only entering into PDA on a DIMM-rank once and by issuing the PDA MRS's to multiple DRAM's at a time, we can save a lot of runtime // Note: in shmoo, adding the compression reduced runtime from about 13 minutes down to 3 minutes - typename std::map iv_commands; + typename std::vector> iv_commands; }; /// diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.C index 6e9c4398e..f6e77862b 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -49,6 +49,8 @@ #include #include #include +#include +#include namespace mss { @@ -222,6 +224,35 @@ enum invalid_timing_function_encoding // Non-member function implementations ///////////////////////// +/// +/// @brief Gets the JEDEC train and range values from the encoded VPD value +/// @param[in] i_target - the DIMM target on which to operate +/// @param[out] o_range - the JEDEC VREFDQ range +/// @param[out] o_value - the JEDEC VREFDQ value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_vpd_wr_vref_range_and_value( const fapi2::Target& i_target, + uint8_t& o_range, + uint8_t& o_value ) +{ + fapi2::buffer l_vpd_value; + constexpr uint64_t VPD_TRAIN_RANGE_START = 1; + constexpr uint64_t VPD_TRAIN_VALUE_START = 2; + constexpr uint64_t VPD_TRAIN_VALUE_LEN = 6; + + FAPI_TRY(mss::vpd_mt_vref_dram_wr(i_target, l_vpd_value)); + + o_range = l_vpd_value.getBit() ? + fapi2::ENUM_ATTR_EFF_VREF_DQ_TRAIN_RANGE_RANGE2 : + fapi2::ENUM_ATTR_EFF_VREF_DQ_TRAIN_RANGE_RANGE1; + l_vpd_value.extractToRight(o_value); + + FAPI_INF("%s JEDEC range 0x%02x JEDEC value 0x%02x", mss::c_str(i_target), o_range, o_value); + +fapi_try_exit: + return fapi2::current_err; +} + /// /// @brief IBT helper - maps from VPD definition of IBT to the RCD control word bit fields /// @param[in] i_ibt the IBT from VPD (e.g., 10, 15, ...), stored as 10% of original val (10 in VPD == 100 Ohms) @@ -2449,25 +2480,24 @@ fapi_try_exit: } /// -/// @brief Determines & sets effective config for Vref DQ Train Value +/// @brief Determines & sets effective config for Vref DQ Train Value and Range /// @return fapi2::FAPI2_RC_SUCCESS if okay +/// @note The value and range attributes are combined as offsetting the WR VREF percentage can cause both the value and range to shift +/// The calculations would have to be done twice if the calculations were done separately. As such, they are combined below /// -fapi2::ReturnCode eff_dimm::vref_dq_train_value() +fapi2::ReturnCode eff_dimm::vref_dq_train_value_and_range() { + // Taken from DDR4 (this attribute is DDR4 only) spec MRS6 section VrefDQ training: values table + constexpr uint8_t JEDEC_MAX_TRAIN_VALUE = 0b00110010; uint8_t l_attrs_vref_dq_train_val[PORTS_PER_MCS][MAX_DIMM_PER_PORT][MAX_RANK_PER_DIMM] = {}; + uint8_t l_attrs_vref_dq_train_range[PORTS_PER_MCS][MAX_DIMM_PER_PORT][MAX_RANK_PER_DIMM] = {}; std::vector< uint64_t > l_ranks; - // value to set. - fapi2::buffer l_vpd_value; + // Gets the JEDEC VREFDQ range and value fapi2::buffer l_train_value; - constexpr uint64_t VPD_TRAIN_VALUE_START = 2; - constexpr uint64_t VPD_TRAIN_VALUE_LEN = 6; - // Taken from DDR4 (this attribute is DDR4 only) spec MRS6 section VrefDQ training: values table - constexpr uint8_t JEDEC_MAX_TRAIN_VALUE = 0b00110010; - - FAPI_TRY(mss::vpd_mt_vref_dram_wr(iv_dimm, l_vpd_value)); - l_vpd_value.extractToRight(l_train_value); + fapi2::buffer l_train_range; + FAPI_TRY(mss::get_vpd_wr_vref_range_and_value(iv_dimm, l_train_range, l_train_value)); FAPI_ASSERT(l_train_value <= JEDEC_MAX_TRAIN_VALUE, fapi2::MSS_INVALID_VPD_VREF_DRAM_WR_RANGE() @@ -2477,6 +2507,11 @@ fapi2::ReturnCode eff_dimm::vref_dq_train_value() "%s VPD DRAM VREF value out of range max 0x%02x value 0x%02x", mss::c_str(iv_dimm), JEDEC_MAX_TRAIN_VALUE, l_train_value ); + // Updates the training values and ranges + FAPI_TRY(mss::dp16::wr_vref::offset_values(iv_mca, l_train_range, l_train_value), + "Failed to offset VPD WR VREF values %s", mss::c_str(iv_mca)); + + // Attribute to set num dimm ranks is a pre-requisite FAPI_TRY( eff_vref_dq_train_value(iv_mcs, &l_attrs_vref_dq_train_val[0][0][0]) ); FAPI_TRY( mss::rank::ranks(iv_dimm, l_ranks) ); @@ -2484,11 +2519,15 @@ fapi2::ReturnCode eff_dimm::vref_dq_train_value() for(const auto& l_rank : l_ranks) { l_attrs_vref_dq_train_val[iv_port_index][iv_dimm_index][index(l_rank)] = l_train_value; + l_attrs_vref_dq_train_range[iv_port_index][iv_dimm_index][index(l_rank)] = l_train_range; } FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_VREF_DQ_TRAIN_VALUE, iv_mcs, l_attrs_vref_dq_train_val), "Failed setting attribute for ATTR_EFF_VREF_DQ_TRAIN_VALUE"); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_VREF_DQ_TRAIN_RANGE, iv_mcs, l_attrs_vref_dq_train_range), + "Failed setting attribute for ATTR_EFF_VREF_DQ_TRAIN_RANGE"); + fapi_try_exit: return fapi2::current_err; } @@ -2520,40 +2559,6 @@ fapi_try_exit: return fapi2::current_err; } -/// -/// @brief Determines & sets effective config for Vref DQ Train Range -/// @return fapi2::FAPI2_RC_SUCCESS if okay -/// -fapi2::ReturnCode eff_dimm::vref_dq_train_range() -{ - - // Attribute to set num dimm ranks is a pre-requisite - uint8_t l_attrs_vref_dq_train_range[PORTS_PER_MCS][MAX_DIMM_PER_PORT][MAX_RANK_PER_DIMM] = {}; - std::vector< uint64_t > l_ranks; - - // value to set. - fapi2::buffer l_vpd_value; - fapi2::buffer l_train_range; - constexpr uint64_t VPD_TRAIN_RANGE_START = 1; - FAPI_TRY(mss::vpd_mt_vref_dram_wr(iv_dimm, l_vpd_value)); - l_train_range = l_vpd_value.getBit(); - - // gets the current value of train_range - FAPI_TRY( eff_vref_dq_train_range(iv_mcs, &l_attrs_vref_dq_train_range[0][0][0]) ); - FAPI_TRY( mss::rank::ranks(iv_dimm, l_ranks) ); - - for(const auto& l_rank : l_ranks) - { - l_attrs_vref_dq_train_range[iv_port_index][iv_dimm_index][index(l_rank)] = l_train_range; - } - - FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_VREF_DQ_TRAIN_RANGE, iv_mcs, l_attrs_vref_dq_train_range), - "Failed setting attribute for ATTR_EFF_VREF_DQ_TRAIN_RANGE"); - -fapi_try_exit: - return fapi2::current_err; -} - /// /// @brief Determines & sets effective config for CA Parity Latency /// @return fapi2::FAPI2_RC_SUCCESS if okay diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.H index b0de045ed..9b8b1780d 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/eff_dimm.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -39,6 +39,17 @@ namespace mss { +/// +/// @brief Gets the JEDEC train and range values from the encoded VPD value +/// @param[in] i_target - the DIMM target on which to operate +/// @param[out] o_range - the JEDEC VREFDQ range +/// @param[out] o_value - the JEDEC VREFDQ value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_vpd_wr_vref_range_and_value( const fapi2::Target& i_target, + uint8_t& o_range, + uint8_t& o_value ); + /// /// @class mss::dimm::eff_dimm /// @brief A class made to perform eff_config functions based on the different dimm kinds (gen, type, buffer type) @@ -484,15 +495,17 @@ class eff_dimm /// /// @brief Determines & sets effective config for Output Buffer /// @return fapi2::FAPI2_RC_SUCCESS if okay + /// @note The value and range attributes are combined as offsetting the WR VREF percentage can cause both the value and range to shift + /// The calculations would have to be done twice if the calculations were done separately. As such, they are combined below /// fapi2::ReturnCode output_buffer(); /// - /// @brief Determines & sets effective config for Vref DQ Train Value + /// @brief Determines & sets effective config for Vref DQ Train Value and Range /// @return fapi2::FAPI2_RC_SUCCESS if okay /// - fapi2::ReturnCode vref_dq_train_value(); + fapi2::ReturnCode vref_dq_train_value_and_range(); /// /// @brief Determines & sets effective config for Vref DQ Train Enable @@ -500,12 +513,6 @@ class eff_dimm /// fapi2::ReturnCode vref_dq_train_enable(); - /// - /// @brief Determines & sets effective config for Vref DQ Train Range - /// @return fapi2::FAPI2_RC_SUCCESS if okay - /// - fapi2::ReturnCode vref_dq_train_range(); - /// /// @brief Determines & sets effective config for CA Parity Latency /// @return fapi2::FAPI2_RC_SUCCESS if okay diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H index e5b3b9041..7cc82c5e5 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -708,7 +708,7 @@ fapi2::ReturnCode get_dimm_target_from_rank(const fapi2::Target& i_target, /// @brief Given a target, get the rank pair assignments, based on DIMMs /// @tparam T the fapi2::TargetType /// @param[in] i_target the target (MCA or MBA?) -/// @param[out] o_registers the regiter settings for the appropriate rank pairs +/// @param[out] o_registers the register settings for the appropriate rank pairs /// @return FAPI2_RC_SUCCESS if and only if ok /// template< fapi2::TargetType T> diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/fir/check.C b/src/import/chips/p9/procedures/hwp/memory/lib/fir/check.C index 7a329aaed..aef337cde 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/fir/check.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/fir/check.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -225,7 +225,7 @@ fapi2::ReturnCode during_draminit_training( const fapi2::Target& i_target, int8_t& o_value) +{ + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_WR_VREF_OFFSET, i_target, o_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_WR_VREF_OFFSET: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + /// /// @brief ATTR_EFF_DRAM_GEN getter diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C index 3fabd4b4e..5522ec472 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -1072,6 +1072,349 @@ const std::vector< std::vector > dp16Traits::RDCLK_RE }, }; +const std::vector< std::vector > dp16Traits::WR_DQ_DELAY_REG = +{ + // RP0 + { + // DP0 + MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_1_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_2_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_3_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_4_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_5_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_6_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_7_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_8_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_9_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_10_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_11_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_12_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_13_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_14_RP0_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_15_RP0_REG_P0_0, + + // DP1 + MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_1_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_2_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_3_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_4_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_5_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_6_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_7_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_8_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_9_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_10_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_11_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_12_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_13_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_14_RP0_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_15_RP0_REG_P0_1, + + // DP2 + MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_1_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_2_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_3_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_4_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_5_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_6_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_7_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_8_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_9_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_10_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_11_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_12_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_13_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_14_RP0_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_15_RP0_REG_P0_2, + + // DP3 + MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_1_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_2_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_3_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_4_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_5_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_6_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_7_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_8_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_9_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_10_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_11_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_12_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_13_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_14_RP0_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_15_RP0_REG_P0_3, + + // DP4 + MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_1_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_2_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_3_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_4_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_5_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_6_RP0_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_7_RP0_REG_P0_4, + }, + + // RP1 + { + // DP0 + MCA_DP16_WR_DELAY_VALUE_0_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_1_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_2_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_3_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_4_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_5_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_6_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_7_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_8_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_9_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_10_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_11_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_12_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_13_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_14_RP1_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_15_RP1_REG_P0_0, + + // DP1 + MCA_DP16_WR_DELAY_VALUE_0_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_1_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_2_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_3_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_4_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_5_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_6_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_7_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_8_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_9_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_10_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_11_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_12_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_13_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_14_RP1_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_15_RP1_REG_P0_1, + + // DP2 + MCA_DP16_WR_DELAY_VALUE_0_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_1_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_2_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_3_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_4_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_5_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_6_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_7_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_8_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_9_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_10_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_11_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_12_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_13_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_14_RP1_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_15_RP1_REG_P0_2, + + // DP3 + MCA_DP16_WR_DELAY_VALUE_0_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_1_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_2_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_3_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_4_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_5_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_6_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_7_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_8_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_9_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_10_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_11_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_12_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_13_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_14_RP1_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_15_RP1_REG_P0_3, + + // DP4 + MCA_DP16_WR_DELAY_VALUE_0_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_1_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_2_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_3_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_4_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_5_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_6_RP1_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_7_RP1_REG_P0_4, + }, + + // RP2 + { + // DP0 + MCA_DP16_WR_DELAY_VALUE_0_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_1_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_2_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_3_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_4_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_5_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_6_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_7_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_8_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_9_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_10_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_11_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_12_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_13_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_14_RP2_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_15_RP2_REG_P0_0, + + // DP1 + MCA_DP16_WR_DELAY_VALUE_0_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_1_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_2_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_3_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_4_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_5_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_6_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_7_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_8_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_9_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_10_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_11_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_12_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_13_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_14_RP2_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_15_RP2_REG_P0_1, + + // DP2 + MCA_DP16_WR_DELAY_VALUE_0_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_1_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_2_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_3_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_4_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_5_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_6_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_7_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_8_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_9_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_10_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_11_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_12_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_13_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_14_RP2_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_15_RP2_REG_P0_2, + + // DP3 + MCA_DP16_WR_DELAY_VALUE_0_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_1_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_2_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_3_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_4_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_5_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_6_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_7_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_8_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_9_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_10_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_11_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_12_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_13_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_14_RP2_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_15_RP2_REG_P0_3, + + // DP4 + MCA_DP16_WR_DELAY_VALUE_0_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_1_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_2_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_3_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_4_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_5_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_6_RP2_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_7_RP2_REG_P0_4, + }, + + // RP3 + { + // DP0 + MCA_DP16_WR_DELAY_VALUE_0_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_1_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_2_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_3_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_4_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_5_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_6_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_7_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_8_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_9_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_10_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_11_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_12_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_13_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_14_RP3_REG_P0_0, + MCA_DP16_WR_DELAY_VALUE_15_RP3_REG_P0_0, + + // DP1 + MCA_DP16_WR_DELAY_VALUE_0_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_1_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_2_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_3_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_4_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_5_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_6_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_7_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_8_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_9_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_10_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_11_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_12_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_13_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_14_RP3_REG_P0_1, + MCA_DP16_WR_DELAY_VALUE_15_RP3_REG_P0_1, + + // DP2 + MCA_DP16_WR_DELAY_VALUE_0_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_1_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_2_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_3_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_4_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_5_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_6_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_7_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_8_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_9_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_10_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_11_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_12_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_13_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_14_RP3_REG_P0_2, + MCA_DP16_WR_DELAY_VALUE_15_RP3_REG_P0_2, + + // DP3 + MCA_DP16_WR_DELAY_VALUE_0_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_1_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_2_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_3_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_4_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_5_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_6_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_7_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_8_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_9_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_10_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_11_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_12_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_13_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_14_RP3_REG_P0_3, + MCA_DP16_WR_DELAY_VALUE_15_RP3_REG_P0_3, + + // DP4 + MCA_DP16_WR_DELAY_VALUE_0_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_1_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_2_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_3_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_4_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_5_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_6_RP3_REG_P0_4, + MCA_DP16_WR_DELAY_VALUE_7_RP3_REG_P0_4, + }, +}; + /// /// @brief Given a RD_VREF value, create a PHY 'standard' bit field for that percentage. /// @tparam T fapi2 Target Type - derived @@ -2539,33 +2882,32 @@ fapi2::ReturnCode reset_wr_vref_config0( const fapi2::Target& i // traits definition typedef dp16Traits TT; - // builds up the base register value + // Builds up the base register value by field for clarity, using characterized values that lead to the best results fapi2::buffer l_config0_data; - l_config0_data.clearBit() - // TK putting hardcoded defaults here - revisit how to handle this (values should be obtained through characterization) - // smallest available step size - algorithm adds 1 so this is a 1 not a 0 + l_config0_data.setBit() + // Smallest available step size - algorithm adds 1 so this is a 1 not a 0 .insertFromRight(0b000) - // step size of 4 - algorithm adds 1 so this is a 4, not a 3 - .insertFromRight(0b0011) - // for intermediary bits, skip all 7, aka only run one bit on each DRAM for intermediary bits - .insertFromRight(0b111) - // run for two VREFs looking for an increase - this is register value + 1 to the algorithm, so it's a 1, not a 0 - .insertFromRight(0b001); + // Big step size of 2 - algorithm adds 1 so the register setting is a 1 + .insertFromRight(0b0001) + // For intermediary bits, skip all 7, aka only run one bit on each DRAM for intermediary bits + .insertFromRight(0b000) + // Run for the maximum number of VREFs looking for an increase + .insertFromRight(0b111); // Whether the 2D VREF is enabled or not varies by the calibration attribute fapi2::buffer l_cal_steps_enabled; FAPI_TRY( mss::cal_step_enable(i_target, l_cal_steps_enabled) ); - // adds the information to the buffer + // Adds the information to the buffer l_config0_data.writeBit(!l_cal_steps_enabled.getBit()); - //blast out the scoms + // Blast out the scoms FAPI_TRY( mss::scom_blastah(i_target, TT::WR_VREF_CONFIG0_REG, l_config0_data) ); - // return success + // Return success return fapi2::FAPI2_RC_SUCCESS; - // handle errors + // Handle errors fapi_try_exit: return fapi2::current_err; } @@ -3679,6 +4021,95 @@ fapi_try_exit: return fapi2::current_err; } +namespace wr_vref +{ + +/// +/// @brief Gets the WR VREF range based upon the composite range +/// @param[in] i_value the composite range value +/// @return l_range the JEDEC WR VREF range +/// +uint8_t get_range(const uint64_t i_value) +{ + // According to JEDEC range 1 uses a 0 and range 2 uses a 1 + constexpr uint64_t RANGE1 = 0x00; + constexpr uint64_t RANGE2 = 0x01; + const uint8_t l_range = ((i_value >= WR_VREF_CROSSOVER_RANGE) ? RANGE1 : RANGE2); + return l_range; +} + +/// +/// @brief Gets the WR VREF value based upon the composite range +/// @param[in] i_value the composite range value +/// @return l_range the JEDEC WR VREF value +/// +uint8_t get_value(const uint64_t i_value) +{ + // Subtract out the crossover range if need be + // Remember, the JEDEC range overlaps for a number of VREFs + // The crossover range is used to offset the start of the JEDEC range that is higher than the other + const uint8_t l_value = ((i_value >= WR_VREF_CROSSOVER_RANGE) ? + (i_value - WR_VREF_CROSSOVER_RANGE) : (i_value)); + return l_value; +} + +/// +/// @brief Gets the WR VREF value based upon the inputted values +/// @param[in] i_range the JEDEC range to use +/// @param[in] i_value the JEDED value to use +/// @return l_range the JEDEC WR VREF value +/// +uint64_t compute_composite_value(const uint64_t i_range, const uint64_t i_value) +{ + // Add in the crossover range if need be + // Remember, the JEDEC range overlaps for a number of VREFs + // The crossover range is used to offset the start of the JEDEC range that is higher than the other + constexpr uint64_t USE_CROSSOVER_RANGE = 0; + return i_value + ((i_range == USE_CROSSOVER_RANGE) ? WR_VREF_CROSSOVER_RANGE : 0); +} + +/// +/// @brief Offsets the WR VREF train and range values based upon the offset attribute +/// @param[in] i_target the fapi2 target of the port +/// @param[in,out] io_train_range - train range value to update +/// @param[in,out] io_train_value - train range value to update +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode offset_values( const fapi2::Target& i_target, + uint8_t& io_train_range, + uint8_t& io_train_value ) +{ + const auto& l_mcs = mss::find_target(i_target); + // Computing the composite range simplifies the logic for offsetting a WR VREF range and value + int64_t l_composite = compute_composite_value(io_train_range, io_train_value); + + int8_t l_offset = 0; + FAPI_TRY( mss::wr_vref_offset(mss::find_target(i_target), l_offset) ); + l_composite += l_offset; + + // Check that the value is in range + FAPI_ASSERT( (l_composite >= 0) && (l_composite <= WR_VREF_MAX_COMPOSITE_RANGE), + fapi2::MSS_OFFSET_WR_VREF_OUT_OF_RANGE() + .set_TARGET(i_target) + .set_MCS_TARGET(l_mcs) + .set_MAX(WR_VREF_MAX_COMPOSITE_RANGE) + .set_OFFSET(l_offset) + .set_COMPOSITE(l_composite), + "%s Offset composite range is out of bounds (%d - %d): %d offset is: %d", + mss::c_str(i_target), + 0, + WR_VREF_MAX_COMPOSITE_RANGE, + l_composite, + l_offset ); + + io_train_range = get_range(l_composite); + io_train_value = get_value(l_composite); + +fapi_try_exit: + return fapi2::current_err; +} + +} // close namespace wr_vref } // close namespace dp16 } // close namespace mss diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H index 238eaa3dc..e36b5969a 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -196,6 +196,9 @@ class dp16Traits static const std::vector< std::pair > WR_VREF_VALUE_RP2_REG; static const std::vector< std::pair > WR_VREF_VALUE_RP3_REG; + // WR DQ delay registers - in terms of rank pair, then bit + static const std::vector< std::vector > WR_DQ_DELAY_REG; + static const std::vector< uint64_t > RD_DIA_CONFIG5_REG; static const std::vector< uint64_t > DATA_BIT_ENABLE0_REG; static const std::vector< uint64_t > DATA_BIT_ENABLE1_REG; @@ -379,6 +382,13 @@ class dp16Traits WR_VREF_VALUE_VALUE_DRAM_ODD = MCA_DDRPHY_DP16_WR_VREF_VALUE0_RANK_PAIR0_P0_0_01_VALUE_DRAM1 , WR_VREF_VALUE_VALUE_DRAM_ODD_LEN = MCA_DDRPHY_DP16_WR_VREF_VALUE0_RANK_PAIR0_P0_0_01_VALUE_DRAM1_LEN , + // Write delay fields + WR_DELAY = MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_0_01_DELAYG, + WR_DELAY_LEN = MCA_DP16_WR_DELAY_VALUE_0_RP0_REG_P0_0_01_DELAYG_LEN, + + // Bit disable field start + BIT_DISABLE = MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_0_01_DISABLE_15, + // Read Delay fields. READ_OFFSET_LOWER = MCA_DDRPHY_DP16_READ_DELAY_OFFSET0_RANK_PAIR0_P0_0_01, READ_OFFSET_LOWER_LEN = MCA_DDRPHY_DP16_READ_DELAY_OFFSET0_RANK_PAIR0_P0_0_01_LEN, @@ -2316,6 +2326,42 @@ fapi_try_exit: return fapi2::current_err; } +namespace wr_vref +{ + +/// +/// @brief Gets the WR VREF range based upon the composite range +/// @param[in] i_value the composite range value +/// @return l_range the JEDEC WR VREF range +/// +uint8_t get_range(const uint64_t i_value); + +/// +/// @brief Gets the WR VREF value based upon the composite range +/// @param[in] i_value the composite range value +/// @return l_range the JEDEC WR VREF value +/// +uint8_t get_value(const uint64_t i_value); + +/// +/// @brief Gets the WR VREF value based upon the inputted values +/// @param[in] i_range the JEDEC range to use +/// @param[in] i_value the JEDED value to use +/// @return l_range the JEDEC WR VREF value +/// +uint64_t compute_composite_value(const uint64_t i_range, const uint64_t i_value); + +/// +/// @brief Offsets the WR VREF train and range values based upon the offset attribute +/// @param[in] i_target the fapi2 target of the port +/// @param[in,out] io_train_range - train range value to update +/// @param[in,out] io_train_value - train range value to update +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode offset_values( const fapi2::Target& i_target, + uint8_t& io_train_range, + uint8_t& io_train_value ); +} // close namespace wr_vref } // close namespace dp16 } // close namespace mss diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.C b/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.C index c7438b096..06131bd61 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2017 */ +/* Contributors Listed Below - COPYRIGHT 2017,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -53,6 +53,7 @@ #include #include #include +#include namespace mss { @@ -468,17 +469,18 @@ uint64_t read_ctr::calculate_cycles( const fapi2::Target return l_read_ctr_cycles + rc::vref_guess_time(i_target); } - /// -/// @brief Sets up and runs the calibration step +/// @brief Sets up and runs the calibration step according to an external 1D vs 2D input /// @param[in] i_target - the MCA target on which to operate /// @param[in] i_rp - the rank pair /// @param[in] i_abort_on_error - whether or not we are aborting on cal error +/// @param[in] i_wr_vref - true IFF write VREF calibration needs to be run /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode write_ctr::run( const fapi2::Target& i_target, const uint64_t i_rp, - const uint8_t i_abort_on_error ) const + const uint8_t i_abort_on_error, + const bool i_wr_vref ) const { typedef mss::dp16Traits TT; std::vector> l_wr_vref_config; @@ -489,7 +491,7 @@ fapi2::ReturnCode write_ctr::run( const fapi2::Target& i { // 0: Run only the VREF (2D) write centering algorithm // 1: Run only the 1D - l_data.writeBit(!iv_wr_vref); + l_data.writeBit(!i_wr_vref); } FAPI_TRY(mss::scom_blastah(i_target, TT::WR_VREF_CONFIG0_REG, l_wr_vref_config)); @@ -500,6 +502,231 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Sets up and runs the calibration step +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair +/// @param[in] i_abort_on_error - whether or not we are aborting on cal error +/// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode write_ctr::run( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error ) const +{ + return run(i_target, i_rp, i_abort_on_error, iv_wr_vref); +} + +/// +/// @brief Executes the pre-cal step workaround +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair +/// @param[in] i_abort_on_error - whether or not we are aborting on cal error +/// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode write_ctr::pre_workaround( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error ) const +{ + iv_dram_to_check.clear(); + + // Only add DRAMs to check if: + // 1) WR VREF is enabled + // 2) the part is a DD2 or above + if(iv_wr_vref && (!mss::chip_ec_nimbus_lt_2_0(i_target))) + { + FAPI_INF("%s checking for clear DRAMs", mss::c_str(i_target)); + uint64_t l_num_dram = 0; + + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + l_num_dram = (l_width[0] == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8) ? MAX_DRAMS_X8 : MAX_DRAMS_X4; + + // Loops through all the DRAM and adds them to be checked if they are clean of any bad bits + // We only want to run the workaround on an entirely clean DRAM that goes bad + // If we have a DRAM that already has bad bit(s), there could be something else going on and the workaround will not help or could make matters worse + for(uint64_t l_dram = 0; l_dram < l_num_dram; l_dram++) + { + bool l_has_disables = false; + + FAPI_TRY(mss::workarounds::dp16::wr_vref::dram_has_disables(i_target, i_rp, l_dram, l_has_disables)); + + FAPI_INF("%s RP%lu DRAM%lu Disables? %s", mss::c_str(i_target), i_rp, l_dram, l_has_disables ? "yes" : "no"); + + // If there are no disables, then we need to check the DRAM + if(!l_has_disables) + { + // Gets the starting WR DQ delay for the DRAM + uint64_t l_value = 0; + + FAPI_TRY(mss::workarounds::dp16::wr_vref::get_starting_wr_dq_delay(i_target, i_rp, l_dram, l_value)); + + iv_dram_to_check.push_back({l_dram, l_value}); + } + } + } + else + { + FAPI_INF("%s workaround is not being run. WR VREF: %s chip version: %s", mss::c_str(i_target), + iv_wr_vref ? "enabled" : "disabled", mss::chip_ec_nimbus_lt_2_0(i_target) ? "DD1" : "DD2"); + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Executes the post-cal step workaround +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair +/// @param[in] i_abort_on_error - whether or not we are aborting on cal error +/// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode write_ctr::post_workaround( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error ) const +{ + // Loops through the DRAMs to check and creates a vector of bad DRAMs and their associated starting delays + std::vector> l_bad_drams; + + // Checking all of the DRAMs that had been good before WR VREF + // If any of them have gone bad, then note it and run the workaround + for(const auto l_pair : iv_dram_to_check) + { + const auto l_dram = l_pair.first; + const auto l_delay = l_pair.second; + bool l_is_bad = false; + FAPI_TRY(mss::workarounds::dp16::wr_vref::is_dram_disabled(i_target, i_rp, l_dram, l_is_bad)); + + // If we have a bad DRAM, note it and add it to the DRAM to test + if(l_is_bad) + { + FAPI_INF("%s RP%lu DRAM%lu is bad! Workaround will be run on it", mss::c_str(i_target), i_rp, l_dram); + l_bad_drams.push_back({l_dram, l_delay}); + } + } + + iv_dram_to_check.clear(); + + // Only run the rest of the workaround if we have any bad DRAMs + if(l_bad_drams.size() > 0) + { + fapi2::Target l_dimm; + std::vector l_ranks; + + // Gets the ranks on which to latch the VREF's + FAPI_TRY(mss::rank::get_ranks_in_pair( i_target, i_rp, l_ranks)); + + // If the rank vector is empty log an error + FAPI_ASSERT(!l_ranks.empty(), + fapi2::MSS_INVALID_RANK(). + set_MCA_TARGET(i_target). + set_RANK(i_rp). + set_FUNCTION(mss::ffdc_function_codes::WR_VREF_TRAINING_WORKAROUND), + "%s rank pair is empty! %lu", mss::c_str(i_target), i_rp); + + FAPI_ASSERT(l_ranks[0] != NO_RANK, + fapi2::MSS_INVALID_RANK(). + set_MCA_TARGET(i_target). + set_RANK(NO_RANK). + set_FUNCTION(mss::ffdc_function_codes::WR_VREF_TRAINING_WORKAROUND), + "%s rank pair has no ranks %lu", mss::c_str(i_target), i_rp); + + // Ensures we get a valid DIMM target / rank combo + FAPI_TRY( mss::rank::get_dimm_target_from_rank(i_target, l_ranks[0], l_dimm), + "%s Failed get_dimm_target_from_rank in write_ctr::post_workaround", + mss::c_str(i_target)); + + // Assembles the PDA container and fixes the disables + { + mss::ddr4::pda::commands l_container; + + // Loops through and sets up all the data needed the workaround + for(const auto& l_pair : l_bad_drams ) + { + const auto l_dram = l_pair.first; + const auto l_delay = l_pair.second; + + // Adds in the PDA necessary for the latching commands + fapi2::ReturnCode l_rc(fapi2::FAPI2_RC_SUCCESS); + mss::ddr4::mrs06_data l_mrs(l_dimm, l_rc); + FAPI_TRY(l_rc, "%s failed to create MRS06 data class", mss::c_str(l_dimm)); + + // Updates the MRS06 settings to have the proper VREF settings + FAPI_TRY(mss::workarounds::dp16::wr_vref::modify_mrs_vref_to_vpd( l_dimm, l_mrs)); + + FAPI_TRY(l_container.add_command(l_dimm, l_ranks[0], l_mrs, l_dram)); + + // Updates the WR VREF value in the DP + FAPI_TRY(mss::workarounds::dp16::wr_vref::configure_wr_vref_to_nominal( l_dimm, i_rp, l_dram)); + + // Restores the known good values for WR DQ delays + FAPI_TRY(mss::workarounds::dp16::wr_vref::reset_wr_dq_delay( i_target, i_rp, l_dram, l_delay )); + + // Clears the disable bits for PDA latching + FAPI_TRY(mss::workarounds::dp16::wr_vref::clear_dram_disable_bits( i_target, i_rp, l_dram )); + } + + // Latches the failing DRAM's originally good values out to the DRAMs with PDA + FAPI_TRY(mss::ddr4::pda::execute_wr_vref_latch(l_container)); + + // Disabling bits prior to PDA could cause issues with DRAM latching in the VREF values + // As such, we're setting disable bits after latching PDA + for(const auto& l_pair : l_bad_drams ) + { + const auto l_dram = l_pair.first; + FAPI_TRY(mss::workarounds::dp16::wr_vref::disable_bits( i_target, i_rp, l_dram)); + } + } + + FAPI_TRY(mss::workarounds::dp16::wr_vref::configure_skip_bits( i_target )); + + // Re-runs WR VREF calibration + FAPI_TRY(run( i_target, + i_rp, + i_abort_on_error, + true )); + + // Clears the training FIR's + FAPI_TRY(mss::workarounds::dp16::wr_vref::clear_training_firs( i_target )); + + // If the DRAM's are still bad, exit + for(const auto& l_pair : l_bad_drams ) + { + bool l_is_bad = false; + const auto l_dram = l_pair.first; + + FAPI_TRY(mss::workarounds::dp16::wr_vref::is_dram_disabled(i_target, i_rp, l_dram, l_is_bad)); + + if(l_is_bad) + { + FAPI_INF("%s RP%lu found DRAM%lu as bad after the second run of WR VREF! Exiting and letting ECC clean this up", + mss::c_str(i_target), i_rp, l_dram); + } + else + { + FAPI_INF("%s RP%lu found DRAM%lu as recovered after the second run of WR VREF! Restoring disable bits and running WR CTR 1D calibration", + mss::c_str(i_target), i_rp, l_dram); + FAPI_TRY(mss::workarounds::dp16::wr_vref::clear_dram_disable_bits( i_target, i_rp, l_dram )); + } + + // Logs the results for this DRAM + // Note: always logged as recovered, as we want this to be informational + FAPI_TRY(mss::workarounds::dp16::wr_vref::log_dram_results(i_target, i_rp, l_dram, l_is_bad)); + } + + // Re-runs WR VREF + FAPI_TRY(run( i_target, + i_rp, + i_abort_on_error, + false )); + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + /// /// @brief Calculates the number of cycles a given calibration step will take /// @param[in] i_target - the MCA target on which to operate diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.H b/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.H index 37f756098..69372ef85 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/mss_training.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2017 */ +/* Contributors Listed Below - COPYRIGHT 2017,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -579,6 +579,28 @@ class write_ctr : public phy_step const uint64_t i_rp, const uint8_t i_abort_on_error ) const override; + /// + /// @brief Executes the pre-cal step workaround + /// @param[in] i_target - the MCA target on which to operate + /// @param[in] i_rp - the rank pair + /// @param[in] i_abort_on_error - whether or not we are aborting on cal error + /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok + /// + fapi2::ReturnCode pre_workaround( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error ) const override; + + /// + /// @brief Executes the post-cal step workaround + /// @param[in] i_target - the MCA target on which to operate + /// @param[in] i_rp - the rank pair + /// @param[in] i_abort_on_error - whether or not we are aborting on cal error + /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok + /// + fapi2::ReturnCode post_workaround( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error ) const override; + /// /// @brief Calculates the number of cycles a given calibration step will take /// @param[in] i_target - the MCA target on which to operate @@ -597,6 +619,24 @@ class write_ctr : public phy_step private: bool iv_wr_vref; + // Contains all information related to any DRAMs that were disabled due to the WR VREF algorithm + // The pair contains the following information: + // first -> the DRAM's number to test + // second -> the DRAM's starting write delay value + mutable std::vector> iv_dram_to_check; + + /// + /// @brief Sets up and runs the calibration step according to an external 1D vs 2D input + /// @param[in] i_target - the MCA target on which to operate + /// @param[in] i_rp - the rank pair + /// @param[in] i_abort_on_error - whether or not we are aborting on cal error + /// @param[in] i_wr_vref - true IFF write VREF calibration needs to be run + /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ok + /// + fapi2::ReturnCode run( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint8_t i_abort_on_error, + const bool i_wr_vref ) const; }; /// diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H index eabb67fb9..4aa8db4cf 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -51,11 +51,12 @@ enum sizes MCBIST_PER_MC = 1, MAX_DIMM_PER_PORT = 2, MAX_RANK_PER_DIMM = 4, - BYTES_PER_DP = 2, - NIBBLES_PER_DP = 4, NIBBLES_PER_BYTE = 2, BITS_PER_NIBBLE = 4, BITS_PER_BYTE = 8, + BITS_PER_DP = 16, + NIBBLES_PER_DP = BITS_PER_DP / BITS_PER_NIBBLE, + BYTES_PER_DP = BITS_PER_DP / BITS_PER_BYTE, BITS_PER_DQS = 2, ///< Differential clock pair MAX_RANKS_DIMM1 = 2, ///< DIMM1 (inner DIMM) can't be a 4R DIMM MAX_PRIMARY_RANKS_PER_PORT = 4, @@ -87,6 +88,14 @@ enum sizes WR_LVL_PRE_DLY = 0b101010, WR_LVL_NUM_VALID_SAMPLES = 0x5, + // WR VREF JEDEC values - we're using em in multiple places, so let's define them in one + // Single range max is the maximum range for a single WR VREF range in JEDEC - 0b110010 + WR_VREF_SINGLE_RANGE_MAX = 0b110010, + // Crossover range is where the top of Range 2 (the lower range) equals the bottom of Range 1 (the upper range) + WR_VREF_CROSSOVER_RANGE = 0b011000, + // Max range is computed from single range max (50) + the crossover range (24) + WR_VREF_MAX_COMPOSITE_RANGE = WR_VREF_SINGLE_RANGE_MAX + WR_VREF_CROSSOVER_RANGE, + // Attribute? BRS COARSE_CAL_STEP_SIZE = 0x4, CONSEQ_PASS = 0x8, @@ -166,6 +175,14 @@ enum ffdc_function_codes PDA_WR_VREF_LATCH_CONTAINER = 80, PDA_WR_VREF_LATCH_VECTOR = 81, PDA_ADD_COMMAND = 82, + + // WR VREF workaround functions + WR_VREF_TRAINING_WORKAROUND = 90, + CONFIGURE_WR_VREF_TO_NOMINAL = 91, + RESET_WR_DQ_DELAY = 92, + READ_RD_VREF_VALUES_FOR_DRAM = 93, + GET_DRAM_DISABLE_REG_AND_POS = 94, + GET_STARTING_WR_DQ_DELAY_VALUE = 95, }; enum states diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.C b/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.C index 1ed664574..e67b4dc0f 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -49,6 +49,8 @@ #include #include #include +#include +#include namespace mss { @@ -1077,7 +1079,6 @@ fapi2::ReturnCode get_delay_data(const fapi2::Target& i_ MCA_DDRPHY_DP16_READ_DELAY3_RANK_PAIR3_P0_4, }, }; - // Bombs out if the rank pair is out of range FAPI_ASSERT( i_rank_pair < MAX_RANK_PAIRS, @@ -1115,6 +1116,7 @@ fapi2::ReturnCode get_delay_data(const fapi2::Target& i_ fapi_try_exit: return fapi2::current_err; } + /// /// @brief Finds the median and sorts the vector /// @param[in,out] io_reg_data register data @@ -1127,21 +1129,17 @@ fapi2::ReturnCode find_median_and_sort(std::vector& io_reg_data, uin // The fapi_try is in an if statement, this ensures we have a good value fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; - // Bomb out if the vector is empty to avoid accessing a non-existant element - FAPI_ASSERT(!io_reg_data.empty(), - fapi2::MSS_RD_CTR_WORKAROUND_EMPTY_VECTOR(), - "Empty vector passed in to find_median_and_sort" - ); + // Note: the null constructor is deleted + // We need to return the data value, so we're creating a temporary variable with 0's for it's values to get the median positon + // Below, we'll get the data and return it to o_median + delay_data l_temp(0, 0, 0); // Sorts first std::sort(io_reg_data.begin(), io_reg_data.end()); - // TODO:RTC172759 Add generic median function - that can replace the below code - // Finding the median is simply a matter of finding the midway point and getting the data there - { - const auto l_median_it = io_reg_data.begin() + io_reg_data.size() / 2; - o_median = l_median_it->iv_data; - } + // Returns the median value + FAPI_TRY(find_median(io_reg_data, l_temp)); + o_median = l_temp.iv_data; fapi_try_exit: return fapi2::current_err; @@ -1558,6 +1556,644 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Checks that the rank pair and DRAM are in bounds +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_function - the calling function to callout in FFDC +/// +fapi2::ReturnCode check_rp_and_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const ffdc_function_codes i_function ) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Checks inputs + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Checks for DRAM in bounds + { + const uint64_t MAX_NUM_DRAM = l_width[0] == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8 ? MAX_DRAMS_X8 : MAX_DRAMS_X4; + FAPI_ASSERT(i_dram < MAX_NUM_DRAM, + fapi2::MSS_INVALID_INDEX_PASSED() + .set_INDEX(i_dram) + .set_FUNCTION(i_function), + "%s Invalid DRAM index passed to check_for_dram_disabled (%d)", + mss::c_str(i_target), + i_dram); + } + + // Checks for i_rp in bounds + FAPI_ASSERT(i_rp < MAX_RANK_PAIRS, + fapi2::MSS_INVALID_RANK(). + set_MCA_TARGET(i_target). + set_RANK(i_rp). + set_FUNCTION(i_function), + "%s rank pair is out of bounds %lu", mss::c_str(i_target), i_rp); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Gets the disable register and bit position for the DRAM +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_reg - the register to access from +/// @param[out] o_bit_pos - the bit position in the register to access from +/// @param[out] o_bit_len - the bit length to access +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_disable_reg_and_pos_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_reg, + uint64_t& o_bit_pos, + uint64_t& o_bit_len) +{ + // Gets the DRAM width + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Checks inputs + FAPI_TRY(check_rp_and_dram(i_target, + i_rp, + i_dram, + ffdc_function_codes::GET_DRAM_DISABLE_REG_AND_POS)); + + // Get the register and bit positions + { + typedef dp16Traits TT; + + // Gets the DP associated with the DRAM and the DRAM's position in the DP + const auto l_dram_per_dp = (l_width[0] == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8) ? BYTES_PER_DP : NIBBLES_PER_DP; + const auto l_dp = i_dram / l_dram_per_dp; + const auto l_dram_pos = i_dram % l_dram_per_dp; + + // Bit position and length + o_bit_pos = TT::BIT_DISABLE + (l_dram_pos * l_width[0]); + o_bit_len = l_width[0]; + + // Gets the register address to read + o_reg = TT::BIT_DISABLE_REG[i_rp][l_dp].first; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Gets the disables for a specific DRAM +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_disables - true if the whole DRAM is disabled +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_disables_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_disables) +{ + uint64_t l_reg = 0; + uint64_t l_bit_pos = 0; + uint64_t l_len = 0; + fapi2::buffer l_data; + FAPI_TRY(get_disable_reg_and_pos_for_dram(i_target, i_rp, i_dram, l_reg, l_bit_pos, l_len)); + + // Reads + FAPI_TRY( mss::getScom(i_target, l_reg, l_data) ); + + // Gets the DRAM's data + FAPI_TRY(l_data.extractToRight(o_disables, l_bit_pos, l_len)); + + FAPI_INF("%s RP%lu DRAM%lu reg:0x%016lx reg_value:0x%016lx DRAM disables: 0x%lx", mss::c_str(i_target), i_rp, i_dram, + l_reg, l_data, o_disables); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Identifies if an inputted DRAM is wholely disabled +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_disabled - true if the whole DRAM is disabled +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode is_dram_disabled( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + bool& o_disabled) +{ + // Checks inputs + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + uint64_t l_disables = 0; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Gets the disables for this DRAM + FAPI_TRY(get_disables_for_dram(i_target, i_rp, i_dram, l_disables)); + + // A DRAM is disabled if all of it's bits are disabled + { + const uint64_t l_disabled_value = (l_width[0] == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8) ? 0xff : 0xf; + o_disabled = (l_disables == l_disabled_value); + FAPI_INF("%s RP%lu DRAM%lu DRAM disables: 0x%lx check value: 0x%lx %s disabled", mss::c_str(i_target), i_rp, i_dram, + l_disables, l_disabled_value, o_disabled ? "is" : "isn't"); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Identifies if an inputted DRAM has any disables +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_has_disables - true if the whole DRAM has any disables +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode dram_has_disables( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + bool& o_has_disables) +{ + // Checks inputs + uint64_t l_disables = 0; + + // Gets the disables for this DRAM + FAPI_TRY(get_disables_for_dram(i_target, i_rp, i_dram, l_disables)); + + // A DRAM is disabled if all of it's bits are disabled + { + constexpr uint64_t CLEAR = 0; + o_has_disables = (CLEAR != l_disables); + FAPI_INF("%s RP%lu DRAM%lu DRAM disables: 0x%lx clear value: 0x%lx %s disables", mss::c_str(i_target), i_rp, i_dram, + l_disables, CLEAR, o_has_disables ? "has" : "has no"); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Disables bits based upon RD VREF values that differ from the median significantly +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode disable_bits( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram ) +{ + // Gets the register for the disable bits + uint64_t l_reg = 0; + uint64_t l_dram_start_pos = 0; + uint64_t l_len = 0; + FAPI_TRY(get_disable_reg_and_pos_for_dram(i_target, i_rp, i_dram, l_reg, l_dram_start_pos, l_len)); + + // Sets up this DRAM's disable bits + { + // Gets the bit to test + uint64_t l_bit = 0; + fapi2::buffer l_data; + std::vector l_rd_vref_values; + FAPI_TRY(read_rd_vref_for_dram( i_target, i_rp, i_dram, l_rd_vref_values )); + FAPI_TRY(identify_first_good_rd_vref( i_target, i_rp, i_dram, l_rd_vref_values, l_bit )); + + // Read... + FAPI_TRY(mss::getScom(i_target, l_reg, l_data)); + + // Modify... + // First of all, disable all bits - for the workaround, we only want to run one bit in the DRAM + FAPI_TRY(l_data.setBit(l_dram_start_pos, l_len)); + // Next, clear the bit (set it as good) + FAPI_TRY(l_data.clearBit(l_dram_start_pos + l_bit)); + + // Write... + FAPI_TRY(mss::putScom(i_target, l_reg, l_data)); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Clears all disable bits for a recovered DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode clear_dram_disable_bits( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram ) +{ + // Gets the register for the disable bits + uint64_t l_reg = 0; + uint64_t l_bit_pos = 0; + uint64_t l_len = 0; + FAPI_TRY(get_disable_reg_and_pos_for_dram(i_target, i_rp, i_dram, l_reg, l_bit_pos, l_len)); + + // Restores the disabled bits + { + // Read... + fapi2::buffer l_data; + FAPI_TRY(mss::getScom(i_target, l_reg, l_data)); + + // Modify... + FAPI_TRY(l_data.clearBit(l_bit_pos, l_len)); + + // Write... + FAPI_TRY(mss::putScom(i_target, l_reg, l_data)); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Configures the WR VREF value of a DRAM to be the nominal values +/// @param[in] i_target the fapi2 target type DIMM +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode configure_wr_vref_to_nominal( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram) +{ + typedef dp16Traits TT; + const std::vector >> REGS = + { + TT::WR_VREF_VALUE_RP0_REG, + TT::WR_VREF_VALUE_RP1_REG, + TT::WR_VREF_VALUE_RP2_REG, + TT::WR_VREF_VALUE_RP3_REG, + }; + + const auto& l_mca = mss::find_target(i_target); + + uint8_t l_width = 0; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + FAPI_TRY(check_rp_and_dram(l_mca, i_rp, i_dram, ffdc_function_codes::CONFIGURE_WR_VREF_TO_NOMINAL)); + + // First gets the address + { + constexpr uint64_t NUM_DRAM_PER_REG = 2; + const auto l_dram_per_dp = (l_width == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8) ? BYTES_PER_DP : NIBBLES_PER_DP; + const auto l_dp = i_dram / l_dram_per_dp; + const auto l_reg_num = (i_dram % l_dram_per_dp) / NUM_DRAM_PER_REG; + const auto l_dram_pos = i_dram % NUM_DRAM_PER_REG; + const auto l_range_pos = (l_dram_pos == 0) ? TT::WR_VREF_VALUE_RANGE_DRAM_EVEN : TT::WR_VREF_VALUE_RANGE_DRAM_ODD; + const auto l_value_pos = (l_dram_pos == 0) ? TT::WR_VREF_VALUE_VALUE_DRAM_EVEN : TT::WR_VREF_VALUE_VALUE_DRAM_ODD; + + const auto l_reg = (l_reg_num == 0) ? REGS[i_rp][l_dp].first : REGS[i_rp][l_dp].second; + + // Now for read modify write + fapi2::buffer l_data; + + // Gets the VPD JEDEC WR VREF information + fapi2::buffer l_train_value; + fapi2::buffer l_train_range; + FAPI_TRY(mss::get_vpd_wr_vref_range_and_value(i_target, l_train_range, l_train_value)); + + FAPI_TRY(mss::getScom(l_mca, l_reg, l_data)); + + // Sends the data + FAPI_TRY(l_data.writeBit(l_train_range, l_range_pos)); + FAPI_TRY(l_data.insertFromRight(l_train_value, l_value_pos, TT::WR_VREF_VALUE_VALUE_DRAM_EVEN_LEN)); + + FAPI_TRY(mss::putScom(l_mca, l_reg, l_data)); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Configures the skip bits to be 0x7 for an entire port for the workaround +/// @param[in] i_target the fapi2 target type DIMM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode configure_skip_bits( const fapi2::Target& i_target ) +{ + typedef dp16Traits TT; + constexpr uint64_t SKIP_ALL = 0x7; + + std::vector> l_wr_vref_config; + FAPI_TRY( mss::scom_suckah(i_target, TT::WR_VREF_CONFIG0_REG, l_wr_vref_config) ); + + // Loops and sets or clears the 2D VREF bit on all DPs + for(auto& l_data : l_wr_vref_config) + { + // Updates the skip bits to be skip all + l_data.insertFromRight(SKIP_ALL); + } + + FAPI_TRY(mss::scom_blastah(i_target, TT::WR_VREF_CONFIG0_REG, l_wr_vref_config)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Modifies the WR VREF value in an MRS06 to have the VPD, not eff_config attribute values +/// @param[in] i_target - the DIMM target on which to operate +/// @param[in,out] io_mrs06 - the MRS data class to modify +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode modify_mrs_vref_to_vpd( const fapi2::Target& i_target, + mss::ddr4::mrs06_data& io_mrs06 ) +{ + // Gets the VPD JEDEC WR VREF information + uint8_t l_train_value = 0; + uint8_t l_train_range = 0; + FAPI_TRY(mss::get_vpd_wr_vref_range_and_value(i_target, l_train_range, l_train_value)); + + FAPI_INF("%s setting MRS06 to have the correct VPD value from 0x%02lx to range: %lu value 0x%02lx from 0x%02x", + mss::c_str(i_target), io_mrs06.iv_vrefdq_train_range[0], l_train_range, io_mrs06.iv_vrefdq_train_value[0], + l_train_value); + + for(uint64_t l_rank = 0; l_rank < MAX_RANK_PER_DIMM; ++l_rank) + { + io_mrs06.iv_vrefdq_train_value[l_rank] = l_train_value; + io_mrs06.iv_vrefdq_train_range[l_rank] = l_train_range; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Resets the WR DQ delays for a given DRAM to be a quarter clock before the DQS +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_delay - the write DQ delay value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode reset_wr_dq_delay( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const uint64_t i_delay ) +{ + typedef dp16Traits TT; + + // Gets the DRAM width + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Checks inputs + FAPI_TRY(check_rp_and_dram(i_target, i_rp, i_dram, ffdc_function_codes::RESET_WR_DQ_DELAY)); + + // Restores the inputted delay values + { + const uint64_t l_start_bit = i_dram * l_width[0]; + const uint64_t l_end_bit = l_start_bit + l_width[0]; + + // And restore the good DQ values + for(uint64_t l_bit = l_start_bit; l_bit < l_end_bit; ++l_bit) + { + const auto l_dq_reg = TT::WR_DQ_DELAY_REG[i_rp][l_bit]; + fapi2::buffer l_data; + l_data.insertFromRight(i_delay); + + FAPI_INF("%s RP%lu DRAM %lu DQ reg 0x%016lx DQ val 0x%lx reg val 0x%016lx", + mss::c_str(i_target), i_rp, i_dram, l_dq_reg, i_delay, l_data); + FAPI_TRY(mss::putScom(i_target, l_dq_reg, l_data)); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reads out the RD VREF values for a given DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[out] o_rd_vref_values - the RD VREF values for a given DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode read_rd_vref_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + std::vector& o_rd_vref_values ) +{ + typedef dp16Traits TT; + + // Clears the output, just in case + o_rd_vref_values.clear(); + + // Gets the DRAM width + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Checks inputs + FAPI_TRY(check_rp_and_dram(i_target, i_rp, i_dram, ffdc_function_codes::READ_RD_VREF_VALUES_FOR_DRAM)); + + // Gets the RD VREF values + { + // There are two RD VREF values stored per register + // One RD VREF value corresponds to one bit + // For a x4 DRAM, there are 4 bits per DRAM, meaning two registers need to be read + // For a x8 DRAM, there are 8 bits per DRAM, meaning four registers need to be read + constexpr uint64_t NUM_REGS_X4 = 2; + constexpr uint64_t NUM_REGS_X8 = 4; + const auto NUM_REGS = (l_width[0] == fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8) ? NUM_REGS_X8 : NUM_REGS_X4; + + // Computes the starting register's position + const uint64_t START_REG = i_dram * NUM_REGS; + + // Loops through all of the registers + for(uint64_t l_reg = 0; l_reg < NUM_REGS; ++l_reg) + { + const auto l_reg_pos = l_reg + START_REG; + uint64_t l_value = 0; + fapi2::buffer l_buff; + + FAPI_TRY(mss::getScom(i_target, TT::DD2_RD_VREF_CNTRL_REG[l_reg_pos], l_buff)); + l_buff.extractToRight(l_value); + FAPI_INF("%s RP%lu DRAM%lu has RD VREF value of 0x%02lx", mss::c_str(i_target), i_rp, i_dram, l_value); + o_rd_vref_values.push_back(l_value); + l_buff.extractToRight(l_value); + FAPI_INF("%s RP%lu DRAM%lu has RD VREF value of 0x%02lx", mss::c_str(i_target), i_rp, i_dram, l_value); + o_rd_vref_values.push_back(l_value); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Identifies the first good RD VREF bit +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_rd_vref_values - the RD VREF values for a given DRAM +/// @param[out] o_bit - the bit for the first good RD VREF +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode identify_first_good_rd_vref( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const std::vector& i_rd_vref_values, + uint64_t& o_bit ) +{ + constexpr uint64_t THRESHOLD = 3; + // Loop through all RD VREF values + uint64_t l_bit = 0; + + // Gets the median value + uint64_t l_median = 0; + FAPI_TRY(rd_dq::find_median(i_rd_vref_values, l_median)); + + for(const auto l_value : i_rd_vref_values) + { + // Error case, continue on... + if((l_median > (l_value + THRESHOLD)) || (l_value > (l_median + THRESHOLD))) + { + FAPI_INF("%s RP%lu DRAM%lu bit: %lu with value of %lu is out of the median (%lu) +/- threshold range (%lu). Testing the next bit", + mss::c_str(i_target), i_rp, i_dram, l_bit, l_value, l_median, THRESHOLD); + } + // The first bit inside the threshold is good, note it and exit out successfully + else + { + FAPI_INF("%s RP%lu DRAM%lu bit: %lu with value of %lu is inside of the median (%lu) +/- threshold range (%lu). Returning this as the bit to test", + mss::c_str(i_target), i_rp, i_dram, l_bit, l_value, l_median, THRESHOLD); + o_bit = l_bit; + break; + } + + ++l_bit; + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reads out the WR DQ delay value for a given DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[out] o_wr_dq_delay - the WR DQ delay value for a given DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note At this point, WR CTR hasn't been run, so all WR DQ delays should be the same on the DRAM +/// +fapi2::ReturnCode get_starting_wr_dq_delay( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_wr_dq_delay ) +{ + typedef dp16Traits TT; + + // Gets the DRAM width + uint8_t l_width[MAX_DIMM_PER_PORT] = {}; + FAPI_TRY( mss::eff_dram_width(i_target, l_width) ); + + // Checks inputs + FAPI_TRY(check_rp_and_dram(i_target, i_rp, i_dram, ffdc_function_codes::GET_STARTING_WR_DQ_DELAY_VALUE)); + + // Gets the data + { + // Gets the register - we only want bit 0 of the DRAM + const auto l_first_bit = i_dram * l_width[0]; + const auto l_reg = TT::WR_DQ_DELAY_REG[i_rp][l_first_bit]; + + // Gets the register + fapi2::buffer l_data; + FAPI_TRY(mss::getScom(i_target, l_reg, l_data)); + + // Returns the data + o_wr_dq_delay = 0; + l_data.extractToRight(o_wr_dq_delay); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Logs an informational callout detailing if we were able to recover +/// @param[in] i_target - the MCA target under calibration +/// @param[in] i_rp - the rank pair under calibration +/// @param[in] i_dram - the DRAM in question +/// @param[in] i_bad - true iff the DRAM failed to recover +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode log_dram_results( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const bool i_bad ) +{ + // Log this information for cronus, as the logs are normally supressed +#ifndef __HOSTBOOT_MODULE + FAPI_ERR("%s WR VREF workaround recovery %s on RP%lu DRAM%lu", + mss::c_str(i_target), i_bad ? "failed" : "succeeded", i_rp, i_dram); +#endif + + // If we call this function, we have a fail + FAPI_ASSERT( true, + fapi2::MSS_WR_VREF_DRAM_RECOVERY() + .set_TARGET(i_target) + .set_RP(i_rp) + .set_DRAM(i_dram) + .set_IS_BAD(i_bad), + "%s WR VREF workaround recovery %s on RP%lu DRAM%lu", + mss::c_str(i_target), + i_bad ? "failed" : "succeeded", + i_rp, + i_dram + ); + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + + // Log the error as recovered - we want this error to be informational + fapi2::logError(fapi2::current_err, fapi2::FAPI2_ERRL_SEV_RECOVERED); + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + return fapi2::current_err; +} + +/// +/// @brief Clears the PHY training FIRs for a given port +/// @param[in] i_target - the MCA target under calibration +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode clear_training_firs( const fapi2::Target& i_target ) +{ + + fapi2::buffer l_phyfir_mask; + l_phyfir_mask.setBit(); + + // Clear the PHY FIR ERROR 2 bit so we don't keep failing training and training advance on this port + FAPI_TRY( mss::putScom(i_target, MCA_IOM_PHY0_DDRPHY_FIR_REG_AND, l_phyfir_mask.invert()) ); + + FAPI_TRY( mss::getScom(i_target, MCA_IOM_PHY0_DDRPHY_FIR_REG, l_phyfir_mask)); + +fapi_try_exit: + return fapi2::current_err; +} + + } // close namespace wr_vref } // close namespace dp16 } // close namespace workarounds diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H b/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H index fd5cdb7e6..ac5a73f48 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/workarounds/dp16_workarounds.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -44,6 +44,7 @@ #include #include #include +#include namespace mss { @@ -495,6 +496,42 @@ fapi2::ReturnCode get_delay_data(const fapi2::Target& i_ const uint64_t i_rank_pair, std::vector& o_reg_data); +/// +/// @brief Finds the median and sorts the vector +/// @tparam T the type of data to sort +/// @param[in] i_data the data to find a median for +/// @param[out] o_median the median value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +template < typename T > +fapi2::ReturnCode find_median(const std::vector& i_data, T& o_median) +{ + + // The fapi_try is in an if statement, this ensures we have a good value + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Bomb out if the vector is empty to avoid accessing a non-existant element + FAPI_ASSERT(!i_data.empty(), + fapi2::MSS_RD_CTR_WORKAROUND_EMPTY_VECTOR(), + "Empty vector passed in to find_median_and_sort" + ); + { + // Copies the data to make finding the median easier + auto l_data = i_data; + + // Sorts first + std::sort(l_data.begin(), l_data.end()); + + // The only way to find the median, is to sort and find the mid point + const auto l_median_it = l_data.begin() + (l_data.size() / 2); + o_median = *l_median_it; + } + + +fapi_try_exit: + return fapi2::current_err; +} + /// /// @brief Finds the median and sorts the vector /// @param[in,out] io_reg_data register data @@ -603,6 +640,203 @@ fapi2::ReturnCode setup_values( const fapi2::Target& i_t uint8_t& o_train_range, uint8_t& o_train_value); +/// +/// @brief Gets the disable register and bit position for the DRAM +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_reg - the register to access from +/// @param[out] o_bit_pos - the bit position in the register to access from +/// @param[out] o_bit_len - the bit length to access +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_disable_reg_and_pos_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_reg, + uint64_t& o_bit_pos, + uint64_t& o_bit_len); + +/// +/// @brief Gets the disables for a specific DRAM +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_disables - true if the whole DRAM is disabled +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode get_disables_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_disables); + +/// +/// @brief Identifies if an inputted DRAM is wholely disabled +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_disabled - true if the whole DRAM is disabled +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode is_dram_disabled( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + bool& o_disabled); + +/// +/// @brief Identifies if an inputted DRAM has any disables +/// @param[in] i_target the fapi2 target type MCA of the port +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM to be checked +/// @param[out] o_has_disables - true if the whole DRAM has any disables +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode dram_has_disables( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + bool& o_has_disables); + +/// +/// @brief Disables bits based upon RD VREF values that differ from the median significantly +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode disable_bits( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram ); + +/// +/// @brief Clears all disable bits for a recovered DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode clear_dram_disable_bits( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram ); + +/// +/// @brief Checks that the rank pair and DRAM are in bounds +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_function - the calling function to callout in FFDC +/// +fapi2::ReturnCode check_rp_and_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const ffdc_function_codes i_function ); + +/// +/// @brief Configures the WR VREF value of a DRAM to be the nominal values +/// @param[in] i_target the fapi2 target type DIMM +/// @param[in] i_rp - rank pair to check and modify +/// @param[in] i_dram - the DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode configure_wr_vref_to_nominal( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram); + +/// +/// @brief Configures the skip bits to be 0x7 for an entire port for the workaround +/// @param[in] i_target the fapi2 target type DIMM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode configure_skip_bits( const fapi2::Target& i_target); + +/// +/// @brief Modifies the WR VREF value in an MRS06 to have the VPD, not eff_config attribute values +/// @param[in] i_target - the DIMM target on which to operate +/// @param[in,out] io_mrs06 - the MRS data class to modify +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode modify_mrs_vref_to_vpd( const fapi2::Target& i_target, + mss::ddr4::mrs06_data& io_mrs06 ); + +/// +/// @brief Resets the WR DQ delays for a given DRAM to be a quarter clock before the DQS +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_delay - the write DQ delay value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode reset_wr_dq_delay( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const uint64_t i_delay ); + +/// +/// @brief Reads out the RD VREF values for a given DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[out] o_rd_vref_values - the RD VREF values for a given DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode read_rd_vref_for_dram( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + std::vector& o_rd_vref_values ); + +/// +/// @brief Identifies the first good RD VREF bit +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[in] i_rd_vref_values - the RD VREF values for a given DRAM +/// @param[out] o_bit - the bit for the first good RD VREF +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note The differing values can cause WR VREF fail, so the bit(s) that differ are disabled temporarily +/// +fapi2::ReturnCode identify_first_good_rd_vref( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const std::vector& i_rd_vref_values, + uint64_t& o_bit ); + +/// +/// @brief Reads out the WR DQ delay value for a given DRAM +/// @param[in] i_target - the MCA target on which to operate +/// @param[in] i_rp - the rank pair on which to operate +/// @param[in] i_dram - the DRAM that needs to have the workaround applied to it +/// @param[out] o_wr_dq_delay - the WR DQ delay value for a given DRAM +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// @note At this point, WR CTR hasn't been run, so all WR DQ delays should be the same on the DRAM +/// +fapi2::ReturnCode get_starting_wr_dq_delay( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + uint64_t& o_wr_dq_delay ); + +/// +/// @brief Logs an informational callout detailing if we were able to recover +/// @param[in] i_target - the MCA target under calibration +/// @param[in] i_rp - the rank pair under calibration +/// @param[in] i_dram - the DRAM in question +/// @param[in] i_bad - true iff the DRAM failed to recover +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode log_dram_results( const fapi2::Target& i_target, + const uint64_t i_rp, + const uint64_t i_dram, + const bool i_bad ); + +/// +/// @brief Clears the PHY training FIRs for a given port +/// @param[in] i_target - the MCA target under calibration +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +fapi2::ReturnCode clear_training_firs( const fapi2::Target& i_target ); + } // close namespace wr_vref } // close namespace dp16 } // close namespace workarounds diff --git a/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config.C b/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config.C index b6068bcc7..4b508ede7 100644 --- a/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config.C +++ b/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2017 */ +/* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -209,10 +209,8 @@ fapi2::ReturnCode p9_mss_eff_config( const fapi2::Target "Failed write_level_enable for %s", mss::c_str(l_dimm) ); FAPI_TRY( l_eff_dimm->output_buffer(), "Failed output_buffer for %s", mss::c_str(l_dimm) ); - FAPI_TRY( l_eff_dimm->vref_dq_train_value(), - "Failed vref_dq_train_value for %s", mss::c_str(l_dimm) ); - FAPI_TRY( l_eff_dimm->vref_dq_train_range(), - "Failed vref_dq_train_range for %s", mss::c_str(l_dimm) ); + FAPI_TRY( l_eff_dimm->vref_dq_train_value_and_range(), + "Failed vref_dq_train_value_and_range for %s", mss::c_str(l_dimm) ); FAPI_TRY( l_eff_dimm->vref_dq_train_enable(), "Failed vref_dq_train_enable for %s", mss::c_str(l_dimm) ); FAPI_TRY( l_eff_dimm->ca_parity_latency(), -- cgit v1.2.1