/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/chips/p9/procedures/hwp/memory/lib/workarounds/mcbist_workarounds.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2016,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ /// /// @file mcbist_workarounds.C /// @brief Workarounds for the MCBISt engine /// Workarounds are very device specific, so there is no attempt to generalize /// this code in any way. /// // *HWP HWP Owner: Stephen Glancy // *HWP HWP Backup: Andre Marin // *HWP Team: Memory // *HWP Level: 3 // *HWP Consumed by: FSP:HB #include #include #include #include #include #include #include #include #include #include #include using fapi2::TARGET_TYPE_MCBIST; using fapi2::TARGET_TYPE_DIMM; using fapi2::FAPI2_RC_SUCCESS; namespace mss { namespace workarounds { namespace mcbist { /// /// @brief Replace reads with displays in the passed in MCBIST program /// @param[in,out] the MCBIST program to check for read/display replacement /// @note Useful for testing /// void replace_read_helper(mss::mcbist::program<>& io_program) { using TT = mss::mcbistTraits<>; io_program.change_maint_broadcast_mode(mss::OFF); io_program.change_end_boundary(mss::mcbist::end_boundary::STOP_AFTER_ADDRESS); for (auto& st : io_program.iv_subtests) { uint64_t l_op = 0; st.iv_mcbmr.extractToRight(l_op); if (l_op == mss::mcbist::op_type::READ) { l_op = mss::mcbist::op_type::DISPLAY; } st.iv_mcbmr.insertFromRight(l_op); } } /// /// @brief End of rank work around /// For Nimbus DD1 the MCBIST engine doesn't detect the end of rank properly /// for a 1R DIMM during a super-fast read. To work around this, we check the /// MCBIST to see if any port has a 1R DIMM on it and if so we change our stop /// conditions to immediate. However, because that doesn't work (by design) with /// read, we also must change all reads to displays (slow read.) /// @param[in] i_target the fapi2 target of the mcbist /// @param[in,out] io_program the mcbist program to check /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode end_of_rank( const fapi2::Target& i_target, mss::mcbist::program<>& io_program ) { using TT = mss::mcbistTraits<>; // If we don't need the mcbist work-around, we're done. if (! mss::chip_ec_feature_mcbist_end_of_rank(i_target) ) { return FAPI2_RC_SUCCESS; } // First things first - lets find out if we have an 1R DIMM on our side of the chip. const auto l_dimm_kinds = dimm::kind<>::vector( mss::find_targets(i_target) ); const auto l_kind = std::find_if(l_dimm_kinds.begin(), l_dimm_kinds.end(), [](const dimm::kind<>& k) -> bool { // If total ranks are 1, we have a 1R DIMM, SDP. This is the fellow of concern return k.iv_total_ranks == 1; }); // If we don't find the fellow of concern, we can get outta here if (l_kind == l_dimm_kinds.end()) { FAPI_INF("no 1R SDP DIMM on this MCBIST (%s), we're ok", mss::c_str(i_target)); return FAPI2_RC_SUCCESS; } // Keep in mind that pause-on-error-mode is two bits and it doesn't encode master/slave. The // end_boundary enums are constructed such that STOP_AFTER_MASTER_RANK is really stop on // either master or slave for the purposes of this field. So, checking stop-after-master-rank // will catch both master and slave pauses which is correct for this work-around. uint64_t l_pause_mode = 0; io_program.iv_config.extractToRight(l_pause_mode); if( l_pause_mode != mss::mcbist::end_boundary::STOP_AFTER_MASTER_RANK ) { FAPI_INF("not checking rank boundaries on this MCBIST (%s), we're ok", mss::c_str(i_target)); return FAPI2_RC_SUCCESS; } // If we're here, we need to fix up our program. We need to set our stop to stop immediate, which implies // we don't do broadcasts and we can't do read, we have to do display. FAPI_INF("%s replacing reads and changing broadcast mode due to a chip bug", mss::c_str(i_target)); replace_read_helper(io_program); return fapi2::FAPI2_RC_SUCCESS; } /// /// @brief WAT debug attention /// For Nimbus DD1 the MCBIST engine uses the WAT debug bit as a workaround /// For Nimbus DD2 the WAT debug bit is used for a different workaround /// @param[in] i_target the fapi2 target of the mcbist /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode wat_debug_attention( const fapi2::Target& i_target ) { // MCBIST attentions are already special attention fapi2::ReturnCode l_rc; fir::reg mcbist_fir_register(i_target, l_rc); FAPI_TRY(l_rc, "unable to create fir::reg for %d", MCBIST_MCBISTFIRQ); FAPI_TRY(mcbist_fir_register.attention().write()); fapi_try_exit: return fapi2::current_err; } /// /// @brief BROADCAST OUT OF SYNC workaround /// A UE noise window is triggered by UE/AUEs causing an out of sync error /// @param[in] i_target the fapi2 target of the mcbist /// @param[in] i_value value to enable or disable workaround /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode broadcast_out_of_sync( const fapi2::Target& i_target, const mss::states i_value ) { // Check for enabled (post memdiag) or disabled (pre memdiag) workaround if ( i_value == mss::ON ) { fapi2::buffer l_mcbist_action_buffer; // Change Broadcast of out sync to checkstop post workaround // Current FIR register API resets FIR mask registers when setting up FIR // This can result in FIRs being incorrectly unmasked after being handled in memdiags // The scoms below set the mask to checkstop while preserving the current mask state FAPI_TRY( mss::getScom(i_target, MCBIST_MCBISTFIRACT1, l_mcbist_action_buffer) ); l_mcbist_action_buffer.clearBit(); FAPI_TRY( mss::putScom(i_target, MCBIST_MCBISTFIRACT1, l_mcbist_action_buffer) ); } else { fapi2::ReturnCode l_rc; fir::reg l_mcbist_fir_reg(i_target, l_rc); FAPI_TRY(l_rc, "unable to create fir::reg for %d", MCBIST_MCBISTFIRQ); // Initialize Broadcast out of sync to recoverable pre workaround l_mcbist_fir_reg.recoverable_error(); FAPI_TRY(l_mcbist_fir_reg.write(), "unable to write fir::reg %d", MCBIST_MCBISTFIRQ); } for (const auto& p : mss::find_targets(i_target)) { fapi2::buffer l_recr_buffer; // Set UE noise window for workaround mss::read_recr_register(p, l_recr_buffer); mss::set_enable_ue_noise_window(l_recr_buffer, i_value); mss::write_recr_register(p, l_recr_buffer); } fapi_try_exit: return fapi2::current_err; } } // close namespace mcbist } // close namespace workarounds } // close namespace mss