fapi2::ReturnCode ppe_resume( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target, const uint64_t i_base_address) { fapi2::buffer<uint64_t> l_data64; static const uint32_t RESUME_TRIES = 10; uint32_t l_timeout_count = RESUME_TRIES; //Before reume always clear debug status (Michael's comment) FAPI_INF(" Clear debug status via XCR..."); l_data64.flush<0>(); FAPI_TRY(putScom(i_target, i_base_address + PPE_XIXCR, l_data64), "Error in PUTSCOM in XCR to clear dbg status"); FAPI_INF(" Send RESUME command via XCR..."); l_data64.flush<0>().insertFromRight(p9hcd::RESUME, 1, 3); FAPI_TRY(putScom(i_target, i_base_address + PPE_XIXCR, l_data64), "Error in PUTSCOM in XCR to resume condition"); do { FAPI_TRY(getScom(i_target, i_base_address + PPE_XIRAMEDR, l_data64)); FAPI_DBG(" Poll content: XSR: 0x%16llX", l_data64); } while((l_data64.getBit<p9hcd::HALTED_STATE>() != 0) && (--l_timeout_count != 0)); fapi_try_exit: return fapi2::current_err; }
/// /// @brief Equalize the throttles among OCMB chips /// @param[in] i_targets vector of OCMB chips /// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK /// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value /// fapi2::ReturnCode equalize_throttles( const std::vector< fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP> >& i_targets, const mss::throttle_type i_throttle_type) { FAPI_INF("Start equalize_throttles for %s type throttling", (( i_throttle_type == mss::throttle_type::THERMAL) ? "THERMAL" : "POWER")); std::vector< fapi2::Target<fapi2::TARGET_TYPE_MEM_PORT> > l_exceeded_power; // Set all of the throttles to the lowest value per port for performance reasons FAPI_TRY(mss::power_thermal::equalize_throttles(i_targets, i_throttle_type, l_exceeded_power)); // Report any port that exceeded the max power limit, and return a failing RC if we have any for (const auto& l_port : l_exceeded_power) { FAPI_ERR(" MEM_PORT %s estimated power exceeded the maximum allowed", mss::c_str(l_port) ); fapi2::current_err = fapi2::FAPI2_RC_FALSE; } FAPI_INF("End equalize_throttles"); return fapi2::current_err; fapi_try_exit: FAPI_ERR("Error calculating equalize_throttles using %s throttling", ((i_throttle_type == mss::throttle_type::POWER) ? "power" : "thermal")); return fapi2::current_err; }
//------------------------------------------------------------------------------ // function: apply PBCQ/AIB customization via SCOM initfile // parameters: i_target => processor chip target // returns: FAPI_RC_SUCCESS if initfile evaluation is successful, // else error //------------------------------------------------------------------------------ fapi::ReturnCode proc_pcie_config_pbcq( const fapi::Target & i_target) { fapi::ReturnCode rc; std::vector<fapi::Target> targets; // mark function entry FAPI_INF("proc_pcie_config_pbcq: Start"); do { // execute Phase2 SCOM initfile targets.push_back(i_target); FAPI_INF("proc_pcie_config_pbcq: Executing %s on %s", PROC_PCIE_CONFIG_PHASE2_IF, i_target.toEcmdString()); FAPI_EXEC_HWP( rc, fapiHwpExecInitFile, targets, PROC_PCIE_CONFIG_PHASE2_IF); if (!rc.ok()) { FAPI_ERR("proc_pcie_config_pbcq: Error from fapiHwpExecInitfile executing %s on %s", PROC_PCIE_CONFIG_PHASE2_IF, i_target.toEcmdString()); break; } } while(0); // mark function exit FAPI_INF("proc_pcie_config_pbcq: End"); return rc; }
/// /// @brief Scominit for Explorer /// @param[in] i_target the OCMB target to operate on /// @return FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode exp_scominit( const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target) { mss::display_git_commit_info("exp_scominit"); if (mss::count_dimm(i_target) == 0) { FAPI_INF("... skipping mss_scominit %s - no DIMM ...", mss::c_str(i_target)); return fapi2::FAPI2_RC_SUCCESS; } // We need to make sure we hit all ports const auto& l_port_targets = mss::find_targets<fapi2::TARGET_TYPE_MEM_PORT>(i_target); fapi2::Target<fapi2::TARGET_TYPE_SYSTEM> FAPI_SYSTEM; const auto& l_mc = i_target.getParent<fapi2::TARGET_TYPE_OMI>() .getParent<fapi2::TARGET_TYPE_MCC>() .getParent<fapi2::TARGET_TYPE_MI>() .getParent<fapi2::TARGET_TYPE_MC>(); for(const auto& l_port : l_port_targets) { fapi2::ReturnCode l_rc; FAPI_INF("phy scominit for %s", mss::c_str(l_port)); FAPI_EXEC_HWP(l_rc, explorer_scom, i_target, l_port, FAPI_SYSTEM, l_mc); FAPI_TRY(l_rc, "Error from explorer.scom.initfile %s", mss::c_str(l_port)); } return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: FAPI_INF("End MSS SCOM init"); return fapi2::current_err; }
/// @brief FW Team Utility function that sets the Bad DQ Bitmap. /// @param[in] i_mba Reference to MBA Chiplet /// @param[in] i_port MBA port number (0-(MAX_PORTS_PER_MBA - 1)) /// @param[in] i_dimm MBA port DIMM number (0-(MAX_DIMM_PER_PORT - 1)) /// @param[in] i_rank DIMM rank number (0-(MAX_RANKS_PER_DIMM -1)) /// @param[in] i_data Reference to data where Bad DQ bitmap is copied from /// @return FAPI2_RC_SUCCESS fapi2::ReturnCode dimmSetBadDqBitmap(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_mba, const uint8_t i_port, const uint8_t i_dimm, const uint8_t i_rank, const uint8_t (&i_data)[DIMM_DQ_RANK_BITMAP_SIZE]) { FAPI_INF(">>dimmSetBadDqBitmap. %s:%d:%d:%d", mss::c_str(i_mba), i_port, i_dimm, i_rank); // Check parameters and find the DIMM fapi2::Target<fapi2::TARGET_TYPE_MBA> fapi2::Target<fapi2::TARGET_TYPE_DIMM> l_dimm; // Get the Bad DQ bitmap by querying ATTR_BAD_DQ_BITMAP. // Use a heap based array to avoid large stack alloc uint8_t (&l_dqBitmap)[MAX_RANKS_PER_DIMM][DIMM_DQ_RANK_BITMAP_SIZE] = *(reinterpret_cast<uint8_t(*)[MAX_RANKS_PER_DIMM][DIMM_DQ_RANK_BITMAP_SIZE]> (new uint8_t[MAX_RANKS_PER_DIMM * DIMM_DQ_RANK_BITMAP_SIZE])); FAPI_TRY(dimmBadDqCheckParamFindDimm(i_mba, i_port, i_dimm, i_rank, l_dimm)); FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_BAD_DQ_BITMAP, l_dimm, l_dqBitmap)); // Add the rank bitmap to the DIMM bitmap and write the bitmap memcpy(l_dqBitmap[i_rank], i_data, DIMM_DQ_RANK_BITMAP_SIZE); FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_BAD_DQ_BITMAP, l_dimm, l_dqBitmap)); delete [] &l_dqBitmap; FAPI_INF("<<dimmSetBadDqBitmap"); fapi_try_exit: return fapi2::current_err; }
/// @brief Enable Drivers/Recievers of O, PCIE, MC chiplets /// /// @param[in] i_target_chiplet Reference to TARGET_TYPE_PERV target /// @return FAPI2_RC_SUCCESS if success, else error code. static fapi2::ReturnCode p9_chiplet_enable_ridi_net_ctrl_action_function( const fapi2::Target<fapi2::TARGET_TYPE_PERV>& i_target_chiplet) { bool l_read_reg = false; fapi2::buffer<uint64_t> l_data64; FAPI_DBG("Entering ..."); FAPI_INF("Check for chiplet enable"); //Getting NET_CTRL0 register value FAPI_TRY(fapi2::getScom(i_target_chiplet, PERV_NET_CTRL0, l_data64)); l_read_reg = l_data64.getBit<0>(); //l_read_reg = NET_CTRL0.CHIPLET_ENABLE if ( l_read_reg ) { FAPI_INF("Enable Recievers, Drivers DI1 & DI2"); //Setting NET_CTRL0 register value l_data64.flush<0>(); l_data64.setBit<19>(); //NET_CTRL0.RI_N = 1 l_data64.setBit<20>(); //NET_CTRL0.DI1_N = 1 l_data64.setBit<21>(); //NET_CTRL0.DI2_N = 1 FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_NET_CTRL0_WOR, l_data64)); } FAPI_DBG("Exiting ..."); fapi_try_exit: return fapi2::current_err; }
fapi2::ReturnCode p9_i2ctest_puti2c_fail( fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target) { // This will fail because PROC_CHIP not supported type FAPI_INF("Entering p9_i2ctest_puti2c_fail..."); std::vector<uint8_t> l_i2cdata; l_i2cdata.push_back(1); l_i2cdata.push_back(2); l_i2cdata.push_back(3); l_i2cdata.push_back(4); l_i2cdata.push_back(5); FAPI_INF( "Do putI2c on proc target" ); FAPI_TRY(fapi2::putI2c(i_target, l_i2cdata)); fapi_try_exit: FAPI_INF( "Exiting p9_i2ctest_puti2c_fail... rc = 0x%.8X", (uint64_t)fapi2::current_err ); return fapi2::current_err; }
fapi2::ReturnCode p9_i2ctest_puti2c_pass( fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target) { FAPI_INF("Entering p9_i2ctest_puti2c_pass..."); /* std::vector<uint8_t> l_i2cdata; // purposely truncate address to the last 2 bytes uint16_t offsetAddr = (uint16_t)SBE::SBE_VERSION_SEEPROM_ADDRESS; uint8_t * pOffset = (uint8_t*)&offsetAddr; l_i2cdata.push_back(pOffset[0]); l_i2cdata.push_back(pOffset[1]); l_i2cdata.push_back('P'); l_i2cdata.push_back('U'); l_i2cdata.push_back('T'); l_i2cdata.push_back('I'); l_i2cdata.push_back('2'); l_i2cdata.push_back('C'); l_i2cdata.push_back('-'); l_i2cdata.push_back('P'); l_i2cdata.push_back('A'); l_i2cdata.push_back('S'); l_i2cdata.push_back('S'); FAPI_INF("Do putI2c on proc target"); FAPI_TRY(fapi2::putI2c(i_target, l_i2cdata)); fapi_try_exit: */ FAPI_INF("Exiting p9_i2ctest_puti2c_pass..."); return fapi2::current_err; }
/// @brief check clocks status /// /// @param[in] i_regions regions from upper level input /// @param[in] i_clock_status clock status /// @param[in] i_reg bit status /// @param[in] i_clock_cmd clock command /// @param[out] o_exp_clock_status expected clock status /// @return FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode p9_sbe_common_check_status(const fapi2::buffer<uint64_t> i_regions, const fapi2::buffer<uint64_t> i_clock_status, const bool i_reg, const fapi2::buffer<uint8_t> i_clock_cmd, fapi2::buffer<uint64_t>& o_exp_clock_status) { FAPI_INF("p9_sbe_common_check_status: Entering ..."); if ( (i_reg) && (i_clock_cmd == 0b01) ) { o_exp_clock_status = i_clock_status & (~(i_regions << 49)); } else { if ( (i_reg) && (i_clock_cmd == 0b10) ) { o_exp_clock_status = i_clock_status | (i_regions << 49); } else { o_exp_clock_status = i_clock_status; } } FAPI_INF("p9_sbe_common_check_status: Exiting ..."); return fapi2::FAPI2_RC_SUCCESS; }
//------------------------------------------------------------------------------ // function: proc_tod_init // // parameters: i_tod_node Reference to TOD topology (FAPI targets included within) // // returns: FAPI_RC_SUCCESS if TOD topology is successfully initialized // else FAPI or ECMD error is sent through //------------------------------------------------------------------------------ fapi::ReturnCode proc_tod_init(const tod_topology_node* i_tod_node) { fapi::ReturnCode rc; FAPI_INF("proc_tod_init: Start"); do { if (i_tod_node == NULL) { FAPI_ERR("proc_tod_setup: null node passed into function!"); FAPI_SET_HWP_ERROR(rc, RC_PROC_TOD_NULL_NODE); break; } rc = proc_tod_clear_error_reg(i_tod_node); if (!rc.ok()) { FAPI_ERR("proc_tod_setup: Failure clearing TOD error registers!"); break; } //Start configuring each node; (init_tod_node will recurse on each child) rc = init_tod_node(i_tod_node); if (!rc.ok()) { FAPI_ERR("proc_tod_setup: Failure initializing TOD!"); break; } } while (0); FAPI_INF("proc_tod_init: End"); return rc; }
/// /// @brief Checks that the starting port/dimm address is in range for broadcast mode - helper for testing /// @param[in] i_targets a vector of MCA targets /// @param[in] i_start_addr the starting port_dimm select address /// @return FAPI2_RC_SUCCESS iff okay /// fapi2::ReturnCode broadcast_mode_start_address_check_helper( const std::vector< fapi2::Target<fapi2::TARGET_TYPE_MCA> >& i_targets, const uint64_t i_start_addr) { if( i_targets.size() == 0 ) { // Programming bug, multi_port_init check assures we shouldn't get here FAPI_INF("No ports passed in"); fapi2::Assert(false); } // The check makes for bugs of not hitting the first port or hitting the middle dimm's multiple times // since multi_port_init loops through all valid DIMM's and plops the addresses in const auto l_first_configured_mca = i_targets[0]; const auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(l_first_configured_mca); const auto l_mcbist = mss::find_target<fapi2::TARGET_TYPE_MCBIST>(l_first_configured_mca); const size_t l_dimms_under_mca = mss::count_dimm(l_first_configured_mca); if( l_dimms.size() == 0) { FAPI_INF("No DIMMs under %s", mss::c_str(l_first_configured_mca)); return fapi2::FAPI2_RC_SUCCESS; } FAPI_INF("%d DIMMs under %s", l_dimms_under_mca, mss::c_str(l_first_configured_mca)); // Bomb out if we have incorrect addresses // The following assert catches the error incorrect address error earlier // It also keeps the error meaningful with an invalid address callout rather than a generic MCBIST error callout // Note: we are guaranteed to have at least one DIMM, as we are not broadcast capable without DIMM's // The ports are also required to have the same number and type of DIMM's to be broadcast capable // As such, we can be guaranteed that we have at least one DIMM below const uint64_t l_port_dimm_offset = l_dimms_under_mca - 1; const uint64_t l_portdimm_this_dimm_min = mss::relative_pos<fapi2::TARGET_TYPE_MCBIST>(l_dimms[0]); const uint64_t l_portdimm_this_dimm_max = l_portdimm_this_dimm_min + l_port_dimm_offset; FAPI_INF("Start port_dimm address %d, %s first configured mca start address %d", i_start_addr, mss::c_str(l_first_configured_mca), l_portdimm_this_dimm_min); // Checking that we are received a valid address (port_dimm) that is no less than the first configured port_dimm // on this MCBIST. This vector is sorted to make sure this is true. // Note: cronus always passes in a 0 address, so we need to support an address that is on or before this port FAPI_ASSERT( i_start_addr <= l_portdimm_this_dimm_max, fapi2::MSS_MEMDIAGS_BCMODE_INVALID_ADDRESS() .set_MCA_TARGET(l_first_configured_mca) .set_START_ADDRESS(i_start_addr) .set_MCA_START_ADDRESS(l_portdimm_this_dimm_min), "%s address (%lu) is not the MCBIST's first configured port address (%lu)", mss::c_str(l_mcbist), i_start_addr, l_portdimm_this_dimm_min); return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: return fapi2::current_err; }
/// /// @brief Calcuate the throttle values based on throttle type /// @param[in] i_target /// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK /// @note Called in p9_mss_bulk_pwr_throttles /// @note determines the throttle levels based off of the port's power curve, /// sets the slot throttles to the same /// @note Enums are POWER for power egulator throttles and THERMAL for thermal throttles /// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value /// fapi2::ReturnCode pwr_throttles( const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target, const mss::throttle_type i_throttle_type) { FAPI_INF("Start exp_bulk_pwr_throttle for %s type throttling for %s", (( i_throttle_type == mss::throttle_type::THERMAL) ? "THERMAL" : "POWER"), mss::c_str(i_target)); if (mss::count_dimm (i_target) == 0) { return fapi2::FAPI2_RC_SUCCESS; } uint16_t l_slot = 0; uint16_t l_port = 0; uint32_t l_power = 0; for (const auto& l_port_target : mss::find_targets<fapi2::TARGET_TYPE_MEM_PORT>(i_target)) { fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; //Don't run if there are no dimms on the port if (mss::count_dimm(l_port_target) == 0) { continue; } mss::power_thermal::throttle<> l_pwr_struct(l_port_target, l_rc); FAPI_TRY(l_rc, "Error constructing mss:power_thermal::throttle object for target %s", mss::c_str(l_port_target)); //Let's do the actual work now if ( i_throttle_type == mss::throttle_type::THERMAL) { FAPI_TRY (l_pwr_struct.thermal_throttles()); } else { FAPI_TRY (l_pwr_struct.power_regulator_throttles()); } l_slot = l_pwr_struct.iv_n_slot; l_port = l_pwr_struct.iv_n_port; l_power = l_pwr_struct.iv_calc_port_maxpower; FAPI_INF("For target %s Calculated power is %d, throttle per slot is %d, throttle per port is %d", mss::c_str(l_port_target), l_power, l_slot, l_port); FAPI_TRY(mss::attr::set_port_maxpower( l_port_target, l_power)); FAPI_TRY(mss::attr::set_mem_throttled_n_commands_per_slot( l_port_target, l_slot)); FAPI_TRY(mss::attr::set_mem_throttled_n_commands_per_port( l_port_target, l_port)); } FAPI_INF("End bulk_pwr_throttles for %s", mss::c_str(i_target)); return fapi2::current_err; fapi_try_exit: FAPI_ERR("Error calculating bulk_pwr_throttles using %s throttling", ((i_throttle_type == mss::throttle_type::POWER) ? "power" : "thermal")); return fapi2::current_err; }
/// /// @brief Check the omi status in Axone side /// @param[in] i_target the OMIC target to operate on /// @return FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode p9a_omi_train_check( const fapi2::Target<fapi2::TARGET_TYPE_OMI>& i_target) { mss::display_git_commit_info("p9a_omi_train_check"); FAPI_INF("%s Start p9a_omi_train_check", mss::c_str(i_target)); // Const constexpr uint8_t STATE_MACHINE_SUCCESS = 0b111; // This value is from Lonny Lambrecht constexpr uint8_t MAX_LOOP_COUNT = 20; // Retry times // Declares variables fapi2::buffer<uint64_t> l_omi_status; fapi2::buffer<uint64_t> l_omi_training_status; uint8_t l_state_machine_state = 0; uint8_t l_tries = 0; FAPI_TRY(mss::mc::omi_train_status(i_target, l_state_machine_state, l_omi_status)); while (l_tries < MAX_LOOP_COUNT && l_state_machine_state != STATE_MACHINE_SUCCESS) { // Delay fapi2::delay(mss::DELAY_100US, 10 * mss::DELAY_1MS); // Check OMI training status FAPI_TRY(mss::mc::omi_train_status(i_target, l_state_machine_state, l_omi_status)); // Note: this is very useful debug information while trying to debug training during polling FAPI_TRY(mss::getScom(i_target, P9A_MC_REG2_DL0_TRAINING_STATUS, l_omi_training_status)); l_tries++; } FAPI_TRY(mss::getScom(i_target, P9A_MC_REG2_DL0_TRAINING_STATUS, l_omi_training_status)); FAPI_ASSERT(l_state_machine_state == STATE_MACHINE_SUCCESS, fapi2::P9A_OMI_TRAIN_ERR() .set_TARGET(i_target) .set_EXPECTED_SM_STATE(STATE_MACHINE_SUCCESS) .set_ACTUAL_SM_STATE(l_state_machine_state) .set_DL0_STATUS(l_omi_status) .set_DL0_TRAINING_STATUS(l_omi_training_status), "%s OMI Training Failure, expected state:%d/actual state:%d", mss::c_str(i_target), STATE_MACHINE_SUCCESS, l_state_machine_state ); FAPI_INF("%s End p9a_omi_train_check, expected state:%d/actual state:%d, DL0_STATUS:0x%016llx, DL0_TRAINING_STATUS:0x%016llx", mss::c_str(i_target), STATE_MACHINE_SUCCESS, l_state_machine_state, l_omi_status, l_omi_training_status); return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: return fapi2::current_err; }// p9a_omi_train_check
// ---------------------------------------------------------------------- // Function definitions // ---------------------------------------------------------------------- fapi2::ReturnCode p9_pm_firinit( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target, const p9pm::PM_FLOW_MODE i_mode) { FAPI_IMP("p9_pm_firinit start"); fapi2::ReturnCode l_rc; uint8_t l_pm_firinit_flag; fapi2::buffer<uint64_t> l_data64; // CHECKING FOR FIRS BEFORE RESET and INIT FAPI_DBG("Checking PBA FIRs"); FAPI_TRY(fapi2::getScom(i_target, PU_PBAFIR , l_data64), "ERROR: Failed to fetch PBA FIR"); if(l_data64) { FAPI_INF("WARNING: PBA has active error(s)"); } // Handle PBA FIRs, Masks and actions FAPI_DBG("Calling PBA firinit ..."); FAPI_EXEC_HWP(l_rc, p9_pm_pba_firinit, i_target, i_mode); FAPI_TRY(l_rc); // Handle Core and Quad errors FAPI_DBG("Calling PPM firinit ..."); FAPI_EXEC_HWP(l_rc, p9_pm_ppm_firinit, i_target, i_mode); FAPI_TRY(l_rc); // Handle CME FIRs, Masks and actions FAPI_DBG("Calling CME firinit ..."); FAPI_EXEC_HWP(l_rc, p9_pm_cme_firinit, i_target, i_mode); FAPI_TRY(l_rc); FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_PM_FIRINIT_DONE_ONCE_FLAG, i_target, l_pm_firinit_flag), "ERROR: Failed to fetch the firinit call status flag"); // Set the ATTR_PM_FIRINIT_DONE_ONCE_FLAG attribute if (i_mode == p9pm::PM_INIT) { if (l_pm_firinit_flag != 1) { l_pm_firinit_flag = 1; FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_PM_FIRINIT_DONE_ONCE_FLAG, i_target, l_pm_firinit_flag), "ERROR: Failed to set firinit call status after init"); } } fapi_try_exit: FAPI_INF("p9_pm_firinit end"); return fapi2::current_err; } // END p9_pm_firinit
//------------------------------------------------------------------------------ // function: proc_tod_clear_error_reg // // parameters: i_tod_node Reference to TOD topology (FAPI targets included within) // // returns: FAPI_RC_SUCCESS if every TOD node is cleared of errors // else FAPI or ECMD error is sent through //------------------------------------------------------------------------------ fapi::ReturnCode proc_tod_clear_error_reg(const tod_topology_node* i_tod_node) { fapi::ReturnCode rc; ecmdDataBufferBase data(64); uint32_t rc_ecmd = 0; fapi::Target* target = i_tod_node->i_target; FAPI_INF("proc_tod_clear_error_reg: Start"); do { if (i_tod_node == NULL) { FAPI_ERR("proc_tod_clear_error_reg: null node passed into function!"); FAPI_SET_HWP_ERROR(rc, RC_PROC_TOD_NULL_NODE); break; } FAPI_DBG("proc_tod_clear_error_reg: Clear any previous errors from TOD_ERROR_REG_00040030"); rc_ecmd |= data.flushTo1(); if (rc_ecmd) { FAPI_ERR("proc_tod_clear_error_reg: Error 0x%08X in ecmdDataBuffer setup for TOD_ERROR_REG_00040030.", rc_ecmd); rc.setEcmdError(rc_ecmd); break; } rc = fapiPutScom(*target, TOD_ERROR_REG_00040030, data); if (!rc.ok()) { FAPI_ERR("proc_tod_clear_error_reg: Could not write TOD_ERROR_REG_00040030."); break; } for (std::list<tod_topology_node*>::const_iterator child = (i_tod_node->i_children).begin(); child != (i_tod_node->i_children).end(); ++child) { tod_topology_node* tod_node = *child; rc = proc_tod_clear_error_reg(tod_node); if (!rc.ok()) { FAPI_ERR("proc_tod_clear_error_reg: Failure clearing errors from downstream node!"); break; } } if (!rc.ok()) { break; // error in above for loop } } while (0); FAPI_INF("proc_tod_clear_error_reg: End"); return rc; }
fapi2::ReturnCode p9_i2ctest_write_read_pass( fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target) { FAPI_INF("Entering p9_i2ctest_write_read_pass..."); /* std::vector<uint8_t> l_i2cdata; std::vector<uint8_t> l_read_i2cdata; std::vector<uint8_t> l_read_offset; const uint8_t l_data[] = {0xFE,0xD9, 0x49,0x4E,0x56,0x41,0x4C,0x49,0x44,0x00,0x8C, 0x49,0x4E,0x56,0x41,0x4C,0x49,0x44,0x00,0x8C, 0x49,0x4E,0x56,0x41,0x4C,0x49,0x44,0x00,0x8C, 0x49,0x4E,0x56,0x41,0x4C,0x49,0x44,0x00,0x8C}; const size_t l_read_size = sizeof(l_data) - 2; l_i2cdata.insert( l_i2cdata.end(), &l_data[0], &l_data[sizeof(l_data)] ); l_read_offset.insert( l_read_offset.end(), &l_data[0], &l_data[2]); FAPI_INF("Calling putI2c on the target"); FAPI_TRY(fapi2::putI2c(i_target, l_i2cdata)); // now read it out and verify it was written correctly FAPI_INF("Now read the just written data"); FAPI_TRY(fapi2::getI2c(i_target, l_read_size, l_read_offset, l_read_i2cdata)); // remove 2-byte address part at beginning l_i2cdata.clear(); l_i2cdata.insert( l_i2cdata.end(), &l_data[2], &l_data[sizeof(l_data)] ); if (l_i2cdata == l_read_i2cdata) { FAPI_INF("Data found matches what was written"); } else { FAPI_ERR( "Data found (%d) does NOT match written values (%d)", l_read_i2cdata.size(), l_i2cdata.size() ); TRACFBIN(g_fapiTd, "getI2c returned", l_read_i2cdata.data(), l_read_i2cdata.size()); TRACFBIN(g_fapiTd, "putI2c wrote", l_i2cdata.data(), l_i2cdata.size()); } fapi_try_exit: */ FAPI_INF("Exiting p9_i2ctest_write_read_pass..."); return fapi2::current_err; }
/// @brief configure chiplet pervasive FIRs / XFIRs /// /// @param[in] i_target_chiplet Reference to TARGET_TYPE_PERV target /// @return FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode p9_sbe_common_configure_chiplet_FIR( const fapi2::Target<fapi2::TARGET_TYPE_PERV>& i_target_chiplet) { uint8_t l_unit_idx; fapi2::buffer<uint64_t> l_scom_data; FAPI_INF("p9_sbe_common_configure_chiplet_FIR: Entering ..."); FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CHIP_UNIT_POS, i_target_chiplet, l_unit_idx), "Error from FAPI_ATTR_GET (ATTR_CHIP_UNIT_POS)"); l_unit_idx--; // PERV LFIR FAPI_DBG("Configuring PERV LFIR (chiplet ID: %02X)", l_unit_idx + 1); // reset pervasive FIR l_scom_data = 0; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_LOCAL_FIR, l_scom_data), "Error from putScom (PERV_LOCAL_FIR)"); // configure pervasive FIR action/mask l_scom_data = PERV_LFIR_ACTION0[l_unit_idx]; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_LOCAL_FIR_ACTION0, l_scom_data), "Error from putScom (PERV_LOCAL_FIR_ACTION0)"); l_scom_data = PERV_LFIR_ACTION1[l_unit_idx]; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_LOCAL_FIR_ACTION1, l_scom_data), "Error from putScom (PERV_LOCAL_FIR_ACTION1)"); l_scom_data = PERV_LFIR_MASK[l_unit_idx]; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_LOCAL_FIR_MASK, l_scom_data), "Error from putScom (PERV_LOCAL_FIR_MASK)"); // XFIR FAPI_DBG("Configuring chiplet XFIR (chiplet ID: %02X)", l_unit_idx + 1); // reset XFIR l_scom_data = 0; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_XFIR, l_scom_data), "Error from putScom (PERV_XFIR)"); // configure XFIR mask l_scom_data = PERV_XFIR_MASK[l_unit_idx]; FAPI_TRY(fapi2::putScom(i_target_chiplet, PERV_FIR_MASK, l_scom_data), "Error from putScom (PERV_FIR_MASK"); FAPI_INF("p9_sbe_common_configure_chiplet_FIR: Exiting ..."); fapi_try_exit: return fapi2::current_err; }
// HWP entry point, comments in header fapi2::ReturnCode p9_io_obus_scominit( const fapi2::Target<fapi2::TARGET_TYPE_OBUS>& i_target ) { // mark HWP entry FAPI_INF("p9_io_obus_scominit: Entering..."); const uint8_t GROUP_00 = 0; const uint8_t LANE_00 = 0; const uint8_t SET_RESET = 1; const uint8_t CLEAR_RESET = 0; fapi2::ReturnCode rc = fapi2::FAPI2_RC_SUCCESS; // get system target const fapi2::Target<fapi2::TARGET_TYPE_SYSTEM> l_system_target; // get a proc target fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_proc_target = i_target.getParent<fapi2::TARGET_TYPE_PROC_CHIP>(); // assert IO reset to power-up bus endpoint logic FAPI_TRY( io::rmw( OPT_IORESET_HARD_BUS0, i_target, GROUP_00, LANE_00, SET_RESET ) ); // Bus Reset is relatively fast, only needing < a hundred cycles to allow the signal to propogate. FAPI_TRY( fapi2::delay( 10, 1000 ) ); FAPI_TRY( io::rmw( OPT_IORESET_HARD_BUS0, i_target, GROUP_00, LANE_00, CLEAR_RESET ) ); FAPI_INF("Invoke FAPI procedure core: input_target"); FAPI_EXEC_HWP(rc, p9_obus_scom, i_target, l_system_target, l_proc_target); // configure FIR { FAPI_TRY(fapi2::putScom(i_target, OBUS_FIR_ACTION0_REG, OBUS_PHY_FIR_ACTION0), "Error from putScom (OBUS_FIR_ACTION0_REG)"); FAPI_TRY(fapi2::putScom(i_target, OBUS_FIR_ACTION1_REG, OBUS_PHY_FIR_ACTION1), "Error from putScom (OBUS_FIR_ACTION1_REG)"); FAPI_TRY(fapi2::putScom(i_target, OBUS_FIR_MASK_REG, OBUS_PHY_FIR_MASK), "Error from putScom (OBUS_FIR_MASK_REG)"); } // mark HWP exit FAPI_INF("p9_io_obus_scominit: ...Exiting"); fapi_try_exit: return fapi2::current_err; }
/// /// @brief Helper function for mrs05_decode /// @param[in] i_inst the CCS instruction /// @param[in] i_rank the rank in question /// @param[out] o_crc_error_clear the crc error clear setting /// @param[out] o_ca_parity_error_status the c/a parity error status /// @param[out] o_odt_input_buffer the odt input buffer during power down mode setting /// @param[out] o_ca_parity the c/a parity persistent error setting /// @param[out] o_data_mask the data mask setting /// @param[out] o_write_dbi the write dbi setting /// @param[out] o_read_dbi the read dbi setting /// @param[out] o_ca_parity_latency_buffer the c/a parity latency mode setting /// @param[out] o_rtt_park_buffer the rtt_park setting /// @return FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode mrs05_decode_helper(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, const uint64_t i_rank, uint8_t& o_crc_error_clear, uint8_t& o_ca_parity_error_status, uint8_t& o_odt_input_buffer, uint8_t& o_ca_parity, uint8_t& o_data_mask, uint8_t& o_write_dbi, uint8_t& o_read_dbi, fapi2::buffer<uint8_t>& o_ca_parity_latency_buffer, fapi2::buffer<uint8_t>& o_rtt_park_buffer) { o_ca_parity_latency_buffer = 0; o_rtt_park_buffer = 0; mss::swizzle<5, 3, A2>(i_inst.arr0, o_ca_parity_latency_buffer); mss::swizzle<5, 3, A8>(i_inst.arr0, o_rtt_park_buffer); o_crc_error_clear = i_inst.arr0.getBit<A3>(); o_ca_parity_error_status = i_inst.arr0.getBit<A4>(); o_odt_input_buffer = i_inst.arr0.getBit<A5>(); o_ca_parity = i_inst.arr0.getBit<A9>(); o_data_mask = i_inst.arr0.getBit<A10>(); o_write_dbi = i_inst.arr0.getBit<A11>(); o_read_dbi = i_inst.arr0.getBit<A12>(); FAPI_INF("MR5 rank %d decode: CAPL: 0x%x, CRC_EC: 0x%x, CA_PES: 0x%x, ODT_IB: 0x%x " "RTT_PARK: 0x%x, CAP: 0x%x, DM: 0x%x, WDBI: 0x%x, RDBI: 0x%x", i_rank, uint8_t(o_ca_parity_latency_buffer), o_crc_error_clear, o_ca_parity_error_status, o_odt_input_buffer, uint8_t(o_rtt_park_buffer), o_ca_parity, o_data_mask, o_write_dbi, o_read_dbi); return FAPI2_RC_SUCCESS; }
/// /// @brief Setup the OCMB for enterprise and half-DIMM modes as desired /// @param[in] i_target the OCMB target to operate on /// @return FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode exp_omi_setup( const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target) { mss::display_git_commit_info("exp_omi_setup"); // Declares variables fapi2::buffer<uint64_t> l_data; bool l_is_enterprise = false; bool l_is_half_dimm = false; // Gets the configuration information from attributes FAPI_TRY(mss::enterprise_mode(i_target, l_is_enterprise)); FAPI_TRY(mss::half_dimm_mode(i_target, l_is_half_dimm)); // Prints out the data FAPI_INF("%s %s enterprise mode %s-DIMM mode", mss::c_str(i_target), l_is_enterprise ? "is" : "isn't", l_is_half_dimm ? "half" : "full"); // Sets up the register mss::exp::omi::set_enterprise_set_bit(l_data, l_is_enterprise); mss::exp::omi::set_half_dimm_mode(l_data, l_is_half_dimm); // Writes the data to the register FAPI_TRY(mss::exp::omi::write_enterprise_config(i_target, l_data)); // Checks that the chip is configured correctly FAPI_TRY(mss::exp::omi::read_enterprise_config(i_target, l_data)); FAPI_TRY(mss::exp::omi::check_enterprise_mode(i_target, l_is_enterprise, l_data)); fapi_try_exit: return fapi2::current_err; }
//****************************************************************************** // fapiUnloadInitFile //****************************************************************************** fapi::ReturnCode fapiUnloadInitFile(const char * i_file, const char *& io_addr, size_t & io_size) { #ifndef __HOSTBOOT_RUNTIME fapi::ReturnCode l_rc = fapi::FAPI_RC_SUCCESS; errlHndl_t l_pError = NULL; FAPI_INF("fapiUnloadInitFile: %s", i_file); l_pError = VFS::module_unload(i_file); if(l_pError) { // Add the error log pointer as data to the ReturnCode FAPI_ERR("fapiUnloadInitFile: module_unload failed %s", i_file); l_rc.setPlatError(reinterpret_cast<void *> (l_pError)); } else { io_addr = NULL; io_size = 0; } #else fapi::ReturnCode l_rc = fapi::FAPI_RC_PLAT_NOT_SUPPORTED_AT_RUNTIME; #endif return l_rc; }
fapi2::ReturnCode p9_pm_cme_firinit( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target, const p9pm::PM_FLOW_MODE i_mode) { FAPI_IMP("p9_pm_cme_firinit start"); if(i_mode == p9pm::PM_RESET) { FAPI_TRY(pm_cme_fir_reset(i_target), "ERROR: Failed to reset the CME FIRs"); } else if(i_mode == p9pm::PM_INIT) { FAPI_TRY(pm_cme_fir_init(i_target), "ERROR: Failed to initialize the CME FIRs"); } else { FAPI_ASSERT(false, fapi2::PM_CME_FIRINIT_BAD_MODE().set_BADMODE(i_mode), "ERROR; Unknown mode passed to p9_pm_cme_firinit. Mode %x", i_mode); } fapi_try_exit: FAPI_INF("p9_pm_cme_firinit end"); return fapi2::current_err; }
/// /// @brief Helper function to set DAC_COARSE reg /// @param[in] i_target the fapi2 target /// @param[in] i_failed_dll_dac failed DLL VREG COARSE /// @param[in] i_value the value to set /// @return FAPI2_RC_SUCCESS iff ok /// static fapi2::ReturnCode dll_dac_helper(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, const fapi2::buffer<uint64_t>& i_failed_dll_dac, const uint64_t i_value) { constexpr uint64_t SOURCE_START = dll_map::REGS_RXDLL_VREG; // Read DAC coarse from failed DLL fapi2::buffer<uint64_t> l_data; FAPI_TRY( mss::getScom(i_target, i_failed_dll_dac, l_data), "Failed getScom() operation on %s reg 0x%016llx", mss::c_str(i_target), i_failed_dll_dac ); l_data.insert<dll_map::REGS_RXDLL_VREG, dll_map::REGS_RXDLL_VREG_LEN, SOURCE_START>(i_value); FAPI_INF("%s Writing to DAC_REG 0x%016llx, data 0x%016llx, value 0x%llx", mss::c_str(i_target), i_failed_dll_dac, l_data, i_value); // Write DAC coarse from failed DLL with DAC coarse from neighboring DLL FAPI_TRY( mss::putScom(i_target, i_failed_dll_dac, l_data), "Failed putScom() operation on %s reg 0x%016llx", mss::c_str(i_target), i_failed_dll_dac ); fapi_try_exit: return fapi2::current_err; }
/// /// @brief Change VREG_COARSE for failed DLLs /// @param[in] i_target the fapi2 target /// @param[in] i_failed_dll_map failed DLL VREG COARSE map /// @return FAPI2_RC_SUCCESS iff ok /// fapi2::ReturnCode change_vreg_coarse(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, const std::map< fapi2::buffer<uint64_t>, fapi2::buffer<uint64_t> >& i_failed_dll_map) { for( const auto& map : i_failed_dll_map) { // Little renaming to help clarify map fields const auto FAILING_COARSE_REG = map.first; const auto NEIGHBOR_DATA = map.second; fapi2::buffer<uint64_t> l_data; // Read DAC coarse from failed DLL FAPI_TRY( mss::getScom(i_target, FAILING_COARSE_REG, l_data), "Failed getScom() operation on %s reg 0x%016llx", mss::c_str(i_target), FAILING_COARSE_REG ); FAPI_DBG("%s Read DLL_VREG_COARSE reg 0x%016llx, data 0x%016llx", mss::c_str(i_target), FAILING_COARSE_REG, l_data); l_data.insertFromRight< dll_map::REGS_RXDLL_DAC_COARSE, dll_map::REGS_RXDLL_DAC_COARSE_LEN>(NEIGHBOR_DATA); FAPI_INF("%s Writing to DLL_VREG_COARSE reg 0x%016llx, data 0x%016llx, value 0x%llx", mss::c_str(i_target), FAILING_COARSE_REG, l_data, NEIGHBOR_DATA); // Write DAC coarse from failed DLL with DAC coarse from neighboring DLL FAPI_TRY( mss::putScom(i_target, FAILING_COARSE_REG, l_data), "Failed putScom() operation on %s reg 0x%016llx", mss::c_str(i_target), FAILING_COARSE_REG ); } fapi_try_exit: return fapi2::current_err; }
//****************************************************************************** // targetTest1 //****************************************************************************** uint32_t targetTest1() { uint32_t l_result = 0; // Create Target using default constructor Target l_target; // Ensure that the handle pointer is NULL void * l_pHandle = l_target.get(); if (l_pHandle != NULL) { FAPI_ERR("targetTest1. Handle is not NULL"); l_result = 1; } else { // Ensure that the type is TARGET_TYPE_NONE TargetType l_type = l_target.getType(); if (l_type != TARGET_TYPE_NONE) { FAPI_ERR("targetTest1. Type is 0x%x, expected NONE", l_type); l_result = 2; } else { FAPI_INF("targetTest1. Success!"); } } return l_result; }
//****************************************************************************** // targetTest8 //****************************************************************************** uint32_t targetTest8() { uint32_t l_result = 0; uint8_t l_handle = 7; void * l_pHandle = reinterpret_cast<void *>(&l_handle); // Create Target Target l_target(TARGET_TYPE_L4, l_pHandle); // an L4 Target is not a chip if ( l_target.isChip() ) { FAPI_ERR("targetTest8. L4 target incorrectly" " identified itself as a chip"); l_result = 1; } else { if ( !l_target.isChiplet() ) { FAPI_ERR("targetTest8. L4 target failed to identify as a chiplett" ); l_result = 2; } else { FAPI_INF("targetTest8. Success!"); } } // Set the handle pointer to NULL to prevent any problem on destruction l_target.set(NULL); return l_result; }
/// /// @brief Helper to evaluate the unsupported rank config override attribute /// @param[in] i_dimm0_ranks count of the ranks on DIMM in slot 0 /// @param[in] i_dimm1_ranks count of the ranks on DIMM in slot 1 /// @param[in] i_attr value of the attribute containing the unsupported rank configs /// @return true iff this rank config is supported according to the unsupported attribute /// @note not to be used to enforce populated/unpopulated - e.g., 0 ranks in both slots is ignored /// bool unsupported_rank_helper(const uint64_t i_dimm0_ranks, const uint64_t i_dimm1_ranks, const fapi2::buffer<uint64_t>& i_attr) { // Quick - if the attribute is 0 (typically is) then we're out. if (i_attr == 0) { FAPI_INF("(%d, %d) is supported, override empty", i_dimm0_ranks, i_dimm1_ranks); return true; } // Quick - if both rank configs are 0 (no ranks seen in any slots) we return true. This is always OK. if ((i_dimm0_ranks == 0) && (i_dimm1_ranks == 0)) { FAPI_INF("(%d, %d) is always supported", i_dimm0_ranks, i_dimm1_ranks); return true; } // We use 8 bits to represent a config in the unsupported ranks attribute. Each 'config' is a byte in // the attribute. The left nibble is the count of ranks on DIMM0, right nibble is the count of unsupported // ranks on DIMM1. Total ranks so we need the bits to represent stacks too. uint64_t l_current_byte = 0; do { uint8_t l_config = 0; uint64_t l_current_dimm0 = 0; uint64_t l_current_dimm1 = 0; i_attr.extractToRight(l_config, l_current_byte * BITS_PER_BYTE, BITS_PER_BYTE); fapi2::buffer<uint8_t>(l_config).extractToRight<0, BITS_PER_NIBBLE>(l_current_dimm0); fapi2::buffer<uint8_t>(l_config).extractToRight<BITS_PER_NIBBLE, BITS_PER_NIBBLE>(l_current_dimm1); FAPI_INF("Seeing 0x%x for unsupported rank config (%d, %d)", l_config, l_current_dimm0, l_current_dimm1); if ((l_current_dimm0 == i_dimm0_ranks) && (l_current_dimm1 == i_dimm1_ranks)) { FAPI_INF("(%d, %d) is unsupported", i_dimm0_ranks, i_dimm1_ranks); return false; } } while (++l_current_byte < sizeof(uint64_t)); FAPI_INF("(%d, %d) is supported", i_dimm0_ranks, i_dimm1_ranks); return true; }
void platSetOpMode(const OpModes i_mode) { FAPI_INF("Setting fapi2::opMode to be 0x%x", i_mode); opMode = static_cast<OpModes>( static_cast<uint8_t>(opMode) | static_cast<uint8_t>(i_mode) ); return; }
// ----------------------------------------------------------------------------- // pba_reset -- mode = PM_RESET // ----------------------------------------------------------------------------- fapi2::ReturnCode pba_reset( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target) { std::vector<uint64_t> v_pba_reset_regs = { PU_BCDE_PBADR_SCOM, PU_BCDE_OCIBAR_SCOM, PU_BCUE_CTL_SCOM, PU_BCUE_SET_SCOM, PU_BCUE_PBADR_SCOM, PU_BCUE_OCIBAR_SCOM, PU_PBAXSHBR0_SCOM, PU_PBAXSHBR1_SCOM, PU_PBAXSHCS0_SCOM, PU_PBAXSHCS1_SCOM, PU_PBASLVCTL0_SCOM, PU_PBASLVCTL1_SCOM, PU_PBASLVCTL2_SCOM, PU_PBAFIR, PU_PBACFG, PU_PBAERRRPT0 }; FAPI_IMP(">> pba_reset ..."); fapi2::buffer<uint64_t> l_data64; // Stop the BCDE and BCUE FAPI_TRY(pba_bc_stop(i_target), "pba_bc_stop() detected an error"); // Reset each slave and wait for completion. FAPI_TRY(pba_slave_reset(i_target), "pba_slave_reset() failed."); for (auto it : v_pba_reset_regs) { FAPI_DBG("Resetting PBA register 0x%08llX", it); FAPI_TRY(fapi2::putScom(i_target, it, 0), "Failed to reset register 0x%08llX", it); } // Perform non-zero reset operations // Reset PBAX errors via Configuration Register // Bit 2: PBAXCFG_SND_RESET // Bit 3: PBAXCFG_RCV_RESET l_data64.setBit<2, 2>(); FAPI_INF("Resetting PBAX errors via PBAX config register 0x%08llX with " "value = 0x%16llX", PU_PBAXCFG_SCOM, uint64_t(l_data64)); FAPI_TRY(fapi2::putScom(i_target, PU_PBAXCFG_SCOM, l_data64)); // Perform PBA Slave setup to prepare for the boot phase. FAPI_TRY(pba_slave_setup_boot_phase(i_target), "pba_slave_setup_boot_phase() failed. "); fapi_try_exit: FAPI_IMP("<< pba_reset ..."); return fapi2::current_err; }
/// /// @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<TARGET_TYPE_MCBIST>& i_target, mss::mcbist::program<TARGET_TYPE_MCBIST>& io_program ) { using TT = mss::mcbistTraits<TARGET_TYPE_MCBIST>; // 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<TARGET_TYPE_DIMM>(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<TT::CFG_PAUSE_ON_ERROR_MODE, TT::CFG_PAUSE_ON_ERROR_MODE_LEN>(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. replace_read_helper(io_program); return fapi2::FAPI2_RC_SUCCESS; }