/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2015,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.C /// @brief Run and manage the MCBIST engine /// // *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 using fapi2::TARGET_TYPE_MCBIST; using fapi2::TARGET_TYPE_MCA; using fapi2::TARGET_TYPE_MCS; namespace mss { /// /// @brief Gets the attribute for freq /// @param[in] const ref to the target /// @param[out] uint64_t& reference to store the value /// @note Generated by gen_accessors.pl generate_mc_port_params /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK /// @note Frequency of this memory channel in MT/s (Mega Transfers per second) /// template<> fapi2::ReturnCode freq(const fapi2::Target& i_target, uint64_t& o_value) { return mss::freq(i_target, o_value); } const std::pair mcbistTraits<>::address_pairs[] = { { START_ADDRESS_0, END_ADDRESS_0 }, { START_ADDRESS_1, END_ADDRESS_1 }, { START_ADDRESS_2, END_ADDRESS_2 }, { START_ADDRESS_3, END_ADDRESS_3 }, }; const std::vector< mss::mcbist::op_type > mcbistTraits<>::FIFO_MODE_REQUIRED_OP_TYPES = { mss::mcbist::op_type::WRITE , mss::mcbist::op_type::READ , mss::mcbist::op_type::READ_WRITE , mss::mcbist::op_type::WRITE_READ , mss::mcbist::op_type::READ_WRITE_READ , mss::mcbist::op_type::READ_WRITE_WRITE , mss::mcbist::op_type::RAND_SEQ , mss::mcbist::op_type::READ_READ_WRITE , }; // These valus are pulled out of the MCBIST specification // The index is the fixed width - the value is the LFSR_MASK value to be used const std::vector< uint64_t > mcbistTraits::LFSR_MASK_VALUES = { 0x000000031, 0x00000001F, 0x001000000, 0x100000000, 0x004000003, 0x000080000, 0x040000018, 0x008000000, 0x010006000, 0x004000000, 0x001000000, 0x003200000, 0x001880000, 0x000200000, 0x000610000, 0x000100000, 0x000040000, 0x000010000, 0x000023000, 0x000002000, 0x000000400, 0x000002000, 0x000005008, 0x000002000, 0x000001088, 0x000000B00, 0x0000004A0, 0x000000100, 0x000000040, 0x000000010, 0x000000038, 0x000000008, 0x000000010, 0x000000004, 0x000000004, 0x000000002, 0x000000001, }; namespace mcbist { /// /// @brief Get a list of ports involved in the program /// Specialization for program<> /// @param[in] i_target the target for this program /// @return vector of port targets /// template<> std::vector> program<>::get_port_list( const fapi2::Target& i_target ) const { using TT = mss::mcbistTraits<>; std::vector> l_list; fapi2::buffer l_ports_selected; // extract port sel to left of l_ports_selected so port relatve pos maps to bit number iv_control.extract(l_ports_selected); for (const auto& p : find_targets(i_target)) { if (l_ports_selected.getBit(mss::relative_pos(p))) { l_list.push_back(p); } } return l_list; } /// /// @brief Read entries from MCBIST Read Buffer (RB) array /// Specialization for fapi2::TARGET_TYPE_MCA /// @param[in] i_target the target to effect /// @param[in] i_start_addr the array address to read first /// @param[in] i_num_entries the number of array entries to read /// @param[out] o_data vector of output data /// @param[out] o_ecc_data vector of ecc data /// @return FAPI2_RC_SUCCSS iff ok /// template<> fapi2::ReturnCode read_rb_array(const fapi2::Target& i_target, const uint64_t i_start_addr, const uint64_t i_num_entries, std::vector< fapi2::buffer >& o_data, std::vector< fapi2::buffer >& o_ecc_data) { using TT = mcbistTraits; fapi2::buffer l_control_value; fapi2::buffer l_data; uint64_t l_array_addr = i_start_addr; const auto& l_mcs = mss::find_target(i_target); // Clear out any stale values from output vectors o_data.clear(); o_ecc_data.clear(); // set BUFFER according to port position within MCS l_control_value.writeBit(mss::relative_pos(i_target)) // set start address .insertFromRight(l_array_addr) // enable the auto increment bit .setBit(); FAPI_INF("Setting the RB array access control register."); FAPI_TRY( mss::putScom(l_mcs, TT::RD_BUF_CTL_REG, l_control_value) ); for (uint8_t l_index = 0; l_index < i_num_entries; ++l_index) { // Note that since we enabled AUTOINC above, reading ECC_REG will increment // the array pointer so the next DATA_REG read will read the next array entry FAPI_TRY( mss::getScom(l_mcs, TT::RD_BUF_DATA_REG, l_data) ); FAPI_INF("RB data index %d is: 0x%016lx", l_array_addr, l_data); o_data.push_back(l_data); // Need to read ecc register to increment array index FAPI_TRY( mss::getScom(l_mcs, TT::RD_BUF_ECC_REG, l_data) ); o_ecc_data.push_back(l_data); ++l_array_addr; // Array address automatically rolls over if we go beyond NUM_COMPARE_LOG_ENTRIES if (l_array_addr >= TT::NUM_COMPARE_LOG_ENTRIES) { l_array_addr = 0; } } fapi_try_exit: return fapi2::current_err; } /// /// @brief Checks if broadcast mode is capable of being enabled on this vector of targets /// @param[in] i_targets the vector of targets to analyze - specialization for MCA target type /// @return l_capable - yes iff these vector of targets are broadcast capable /// template<> const mss::states is_broadcast_capable(const std::vector>& i_targets) { // If we don't have MCA's exit out if(i_targets.size() == 0) { FAPI_INF("No MCA's found. Not broadcast capable, exiting..."); return mss::states::NO; } // Now the fun begins // First, get the number of DIMM's on the 0th MCA const uint64_t l_first_mca_num_dimm = mss::count_dimm(i_targets[0]); // Now, find if we have any MCA's that have a different number of DIMM's // Note: starts on the next MCA target due to the fact that something always equals itself const auto l_mca_it = std::find_if(i_targets.begin() + 1, i_targets.end(), [l_first_mca_num_dimm]( const fapi2::Target& i_rhs) -> bool { // Count the number of DIMM and compare to the expected const uint64_t l_num_dimm = mss::count_dimm(i_rhs); // If they're different, we found the MCA that is different return (l_first_mca_num_dimm != l_num_dimm); }); // If no MCA was found that have a different number of DIMMs (aka a different drop), then we are broadcast capable if(l_mca_it == i_targets.end()) { const auto l_mcbist = mss::find_target(i_targets[0]); FAPI_INF("MCA vector from %s has the same number of DIMMs per port: %lu - is broadcast capable", mss::c_str(l_mcbist), l_first_mca_num_dimm); return mss::states::YES; } // Otherwise, note the MCA that differs and return that this is not broadcast capable FAPI_INF("MCA %s differs on the number of DIMMs per port: (number of DIMMs from i_targets[0] %lu, found %lu) - is NOT broadcast capable", mss::c_str(*l_mca_it), l_first_mca_num_dimm, mss::count_dimm(*l_mca_it)); return mss::states::NO; } /// /// @brief Checks if broadcast mode is capable of being enabled on this target /// @param[in] i_target the target to effect /// @param[in] i_bc_force attribute's value to force off broadcast mode /// @param[in] i_bc_enable attribute's value to enable or disable broadcast mode /// @param[in] i_chip_bc_capable true if the chip is BC capable /// @return o_capable - yes iff these vector of targets are broadcast capable /// const mss::states is_broadcast_capable_helper(const fapi2::Target& i_target, const uint8_t i_bc_force, const uint8_t i_bc_enable, const bool i_chip_bc_capable) { // First off, check if we need to disable broadcast mode due to a chip size bug // Note: the bug check is decidedly more complicated than the EC check, but we'll just disable BC mode out of safety concerns // Better to go slow and steady and initialize the chip properly than to go fast and leave the memory initialized poorly if( !i_chip_bc_capable ) { FAPI_INF("%s A chip bug prevents broadcast mode. Chip is not brodcast capable", mss::c_str(i_target)); return mss::states::NO; } // If BC mode is forced off, then we're done if( i_bc_force == fapi2::ENUM_ATTR_MSS_MRW_FORCE_BCMODE_OFF_YES ) { FAPI_INF("%s MRW attribute has broadcast mode forced off", mss::c_str(i_target)); return mss::states::NO; } // Now check the override broadcast mode capable attribute if( i_bc_enable == fapi2::ENUM_ATTR_MSS_OVERRIDE_MEMDIAGS_BCMODE_DISABLE ) { FAPI_INF("%s attribute has broadcast mode disabled", mss::c_str(i_target)); return mss::states::NO; } // Otherwise, our chip and attributes allow us to be BC capable FAPI_INF("%s chip and attributes allow for BC mode", mss::c_str(i_target)); return mss::states::YES; } /// /// @brief Checks if broadcast mode is capable of being enabled on this target /// @param[in] i_target the target to effect - specialization for MCBIST target type /// @return l_capable - yes iff these vector of targets are broadcast capable /// template< > const mss::states is_broadcast_capable(const fapi2::Target& i_target) { // Chip is BC capable IFF the MCBIST end of rank bug is not present const auto l_chip_bc_capable = !mss::chip_ec_feature_mcbist_end_of_rank(i_target); // If BC mode is disabled in the MRW, then it's disabled here uint8_t l_bc_mode_enable = 0; uint8_t l_bc_mode_force_off = 0; FAPI_TRY(mss::override_memdiags_bcmode(l_bc_mode_enable)); FAPI_TRY(mss::mrw_force_bcmode_off(l_bc_mode_force_off)); // Check if the chip and attributes allows memdiags/mcbist to be in broadcast mode { const auto l_state = is_broadcast_capable_helper(i_target, l_bc_mode_force_off, l_bc_mode_enable, l_chip_bc_capable); if(l_state == mss::states::NO) { return l_state; } } // Now that we are guaranteed to have a chip that could run broadcast mode and the system is allowed to do so, // do the following steps to check whether our config is broadcast capable: { // Steps to determine if this MCBIST is broadcastable // 1) Check the number of DIMM's on each MCA - true only if they all match // 2) Check that all of the DIMM kinds are equal - if they are, then we can do broadcast mode // 3) if both 1 and 2 are true, then broadcast capable, otherwise false // 1) Check the number of DIMM's on each MCA - if they don't match, then no const auto l_mca_check = is_broadcast_capable(mss::find_targets(i_target)); // 2) Check that all of the DIMM kinds are equal - if they are, then we can do broadcast mode const auto l_dimms = mss::find_targets(i_target); const auto l_dimm_kinds = mss::dimm::kind<>::vector(l_dimms); const auto l_dimm_kind_check = is_broadcast_capable(l_dimm_kinds); // 3) if both 1/2 are true, then broadcastable, otherwise false const auto l_capable = ((l_mca_check == mss::states::YES) && (l_dimm_kind_check == mss::states::YES)) ? mss::states::YES : mss::states::NO; FAPI_INF("%s %s broadcast capable", mss::c_str(i_target), (l_capable == mss::states::YES) ? "is" : "is not"); return l_capable; } fapi_try_exit: FAPI_ERR("%s failed to get an MRW attribute, an egregious error. Returning NOT broadcast capable", mss::c_str(i_target)); return mss::states::NO; } /// /// @brief Checks if broadcast mode is capable of being enabled on this vector of targets /// @param[in] i_target the target to effect /// @return l_capable - yes iff these vector of targets are broadcast capable /// const mss::states is_broadcast_capable(const std::vector>& i_kinds) { // If we don't have any DIMM kinds exit out if(i_kinds.size() == 0) { FAPI_INF("No DIMM kinds are located. Not broadcast capable, exiting..."); return mss::states::NO; } // Now the fun begins // First, get the starting kind - the 0th kind in the vector const auto l_expected_kind = i_kinds[0]; // Now, find if we have any kinds that differ from our first kind // Note: starts on the next DIMM kind due to the fact that something always equals itself const auto l_kind_it = std::find_if(i_kinds.begin() + 1, i_kinds.end(), [&l_expected_kind]( const mss::dimm::kind<>& i_rhs) -> bool { // If they're different, we found a DIMM that is differs return (l_expected_kind != i_rhs); }); // If no DIMM kind was found that differs, then we are broadcast capable if(l_kind_it == i_kinds.end()) { FAPI_INF("DIMM kinds vector starting with %s has the kinds for all DIMM's - is broadcast capable", mss::c_str(l_expected_kind.iv_target)); return mss::states::YES; } // Otherwise, note the MCA that differs and return that this is not broadcast capable FAPI_INF("DIMM kinds vector differs with %s has the kinds for all DIMM's - is not broadcast capable", mss::c_str(l_kind_it->iv_target)); return mss::states::NO; } /// /// @brief Configures all of the ports for broadcast mode /// @param[in] i_target the target to effect - MCBIST specialization /// @param[out] o_port_select - the configuration of the selected ports /// @return FAPI2_RC_SUCCSS iff ok /// template< > fapi2::ReturnCode setup_broadcast_port_select(const fapi2::Target& i_target, uint64_t& o_port_select) { // Starting location - the MCBIST type takes in ports as a right justified uint64_t value // As we have 4 ports per-MCBIST, they occupy 60-63 // START contains the starting offset for each port constexpr uint64_t START = 60; // Loops through all the ports and adds them to the broadcast value fapi2::buffer l_port_select; fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; for(const auto& l_mca : mss::find_targets(i_target)) { // Configures each port individually const uint64_t l_offset = mss::relative_pos(l_mca); FAPI_TRY(l_port_select.setBit(START + l_offset), "%s setBit error for relative pos of %lu", mss::c_str(l_mca), l_offset); } // Assigns the returned value o_port_select = l_port_select; fapi_try_exit: return fapi2::current_err; } /// /// @brief Enables broadcast mode /// @param[in] i_target the target to effect - MCBIST specialization /// @param[in,out] io_program the mcbist::program - MCBIST specialization /// @return FAPI2_RC_SUCCSS iff ok /// template< > fapi2::ReturnCode enable_broadcast_mode(const fapi2::Target& i_target, mcbist::program<>& io_program) { // constexpr's for beautification constexpr bool ENABLE = true; constexpr auto BROADCAST_SYNC_ENABLE = mss::states::ON; constexpr auto MAX_BROADCAST_SYNC_WAIT = mss::mcbist::broadcast_timebase::TB_COUNT_128; // First, enable broadcast mode io_program.change_maint_broadcast_mode(ENABLE); // Second, configure broadcast mode on all enabled ports uint64_t l_port_select = 0; FAPI_TRY(setup_broadcast_port_select(i_target, l_port_select)); io_program.select_ports(l_port_select); // Finally, enable broadcast sync mode and max out the wait to avoid timeout issues io_program.broadcast_sync_enable(BROADCAST_SYNC_ENABLE); io_program.change_broadcast_timebase(MAX_BROADCAST_SYNC_WAIT); fapi_try_exit: return fapi2::current_err; } /// /// @brief Configures broadcast mode, if it is needed /// @param[in] i_target the target to effect /// @param[in,out] io_program the mcbist::program /// @return FAPI2_RC_SUCCSS iff ok /// template<> fapi2::ReturnCode configure_broadcast_mode(const fapi2::Target& i_target, mcbist::program<>& io_program) { // If we're not capable to do broadcast mode on this target, exit out const auto l_broadcast_capable = is_broadcast_capable(i_target); if(l_broadcast_capable == mss::states::NO) { FAPI_INF("%s is not broadcast capable, skipping enablement of broadcast mode", mss::c_str(i_target)); return fapi2::FAPI2_RC_SUCCESS; } // Enable broadcast mode FAPI_INF("%s is broadcast capable, enabling broadcast mode", mss::c_str(i_target)); return enable_broadcast_mode(i_target, io_program); } /// /// @brief Clear the errors helper. Chip can specialise this /// function to put any necessary workaround in it. /// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK /// template< > fapi2::ReturnCode clear_error_helper( const fapi2::Target& i_target, const program<>& i_program ) { // Implement any mcbist work-arounds. // I'm going to do the unthinkable here - and cast away the const of the mcbist program input. // The work arounds need to change this, and so it needs to not be const. However, I don't want // to risk general const-correctness by changing the input parameter to non-const. So, I use // const_cast<> (ducks out of the way of the flying adjectives.) These are work-arounds ... FAPI_TRY( workarounds::mcbist::end_of_rank(i_target, const_cast&>(i_program)) ); FAPI_TRY( clear_errors(i_target) ); fapi_try_exit: return fapi2::current_err; } } // namespace MCBIST // Note: outside of the mcbist namespace /// /// @brief Dump the registers of an mcbist /// @param[in] i_target, the mcbist in question /// @return fapi2::FAPI2_RC_SUCCESS if ok /// template<> fapi2::ReturnCode dump_regs( const fapi2::Target& i_target ) { return fapi2::current_err; } } // namespace