/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_inband.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2018,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 exp_inband.C /// @brief implement OpenCAPI config, scom, and MSCC MMIO operations. // // *HWP HWP Owner: bgass@us.ibm.com // *HWP FW Owner: dcrowell@us.ibm.com // *HWP Team: // *HWP Level: 2 // *HWP Consumed by: HB #include #include #include #include #include #include namespace mss { namespace exp { namespace ib { //-------------------------------------------------------------------------------- // Write operations //-------------------------------------------------------------------------------- /// @brief Writes 64 bits of data to MMIO space to the selected Explorer /// /// @param[in] i_target The Explorer chip to write /// @param[in] i_addr The address to write /// @param[in] i_data The data to write /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putMMIO64( const fapi2::Target& i_target, const uint64_t i_addr, const fapi2::buffer& i_data ) { uint64_t l_v = static_cast(i_data); std::vector l_wd; forceLE(l_v, l_wd); return fapi2::putMMIO(i_target, EXPLR_IB_MMIO_OFFSET | i_addr, 8, l_wd); } /// @brief Writes 32 bits of data to MMIO space to the selected Explorer /// /// @param[in] i_target The Explorer chip to write /// @param[in] i_addr The address to write /// @param[in] i_data The data to write /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putMMIO32( const fapi2::Target& i_target, const uint64_t i_addr, const fapi2::buffer& i_data ) { uint32_t l_v = static_cast(i_data); std::vector l_wd; forceLE(l_v, l_wd); return fapi2::putMMIO(i_target, EXPLR_IB_MMIO_OFFSET | i_addr, 4, l_wd); } /// @brief Writes 64 bits of data to SCOM MMIO space /// /// @param[in] i_target The Explorer chip to write /// @param[in] i_scomAddr The address to write /// @param[in] i_data The data to write /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putScom( const fapi2::Target& i_target, const uint64_t i_scomAddr, const fapi2::buffer& i_data) { // Converts from the scom address to the MMIO address by shifting left by 3 bits uint64_t l_scomAddr = i_scomAddr << OCMB_ADDR_SHIFT; return putMMIO64(i_target, l_scomAddr, i_data); } /// @brief Writes 32 bits of data to OpenCAPI config space /// /// @param[in] i_target The Explorer chip to write /// @param[in] i_cfgAddr The address to write /// @param[in] i_data The data to write /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putOCCfg( const fapi2::Target& i_target, const uint64_t i_cfgAddr, const fapi2::buffer& i_data) { uint32_t l_v = static_cast(i_data); std::vector l_wd; forceLE(l_v, l_wd); return fapi2::putMMIO(i_target, i_cfgAddr, 4, l_wd); } fapi2::ReturnCode user_input_msdg_to_little_endian(const user_input_msdg& i_input, std::vector& o_data, uint32_t& o_crc) { o_data.clear(); FAPI_TRY(forceCrctEndian(i_input.version_number, o_data)); FAPI_TRY(forceCrctEndian(i_input.DimmType, o_data)); FAPI_TRY(forceCrctEndian(i_input.CsPresent, o_data)); FAPI_TRY(forceCrctEndian(i_input.DramDataWidth, o_data)); FAPI_TRY(forceCrctEndian(i_input.Height3DS, o_data)); FAPI_TRY(forceCrctEndian(i_input.ActiveDBYTE, o_data)); FAPI_TRY(forceCrctEndian(i_input.ActiveNibble, o_data)); FAPI_TRY(forceCrctEndian(i_input.AddrMirror, o_data)); FAPI_TRY(forceCrctEndian(i_input.ColumnAddrWidth, o_data)); FAPI_TRY(forceCrctEndian(i_input.RowAddrWidth, o_data)); FAPI_TRY(forceCrctEndian(i_input.SpdCLSupported, o_data)); FAPI_TRY(forceCrctEndian(i_input.SpdtAAmin, o_data)); FAPI_TRY(forceCrctEndian(i_input.Rank4Mode, o_data)); FAPI_TRY(forceCrctEndian(i_input.EncodedQuadCs, o_data)); FAPI_TRY(forceCrctEndian(i_input.DDPCompatible, o_data)); FAPI_TRY(forceCrctEndian(i_input.TSV8HSupport, o_data)); FAPI_TRY(forceCrctEndian(i_input.MRAMSupport, o_data)); FAPI_TRY(forceCrctEndian(i_input.MDSSupport, o_data)); FAPI_TRY(forceCrctEndian(i_input.NumPStates, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.Frequency, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.PhyOdtImpedance, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.PhyDrvImpedancePU, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.PhyDrvImpedancePD, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.PhySlewRate, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndian(i_input.ATxImpedance, o_data)); FAPI_TRY(forceCrctEndian(i_input.ATxSlewRate, o_data)); FAPI_TRY(forceCrctEndian(i_input.CKTxImpedance, o_data)); FAPI_TRY(forceCrctEndian(i_input.CKTxSlewRate, o_data)); FAPI_TRY(forceCrctEndian(i_input.AlertOdtImpedance, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttNomR0, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttNomR1, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttNomR2, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttNomR3, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttWrR0, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttWrR1, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttWrR2, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttWrR3, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttParkR0, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttParkR1, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttParkR2, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramRttParkR3, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramDic, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramWritePreamble, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.DramReadPreamble, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.PhyEqualization, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.InitVrefDQ, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.InitPhyVref, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.OdtWrMapCs, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.OdtRdMapCs, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.Geardown, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.CALatencyAdder, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.BistCALMode, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.BistCAParityLatency, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.RcdDic, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.RcdVoltageCtrl, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.RcdIBTCtrl, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.RcdDBDic, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.RcdSlewRate, MSDG_MAX_PSTATE, o_data)); FAPI_TRY(forceCrctEndian(i_input.DFIMRL_DDRCLK, o_data)); for(uint8_t l_pstate = 0; l_pstate < MSDG_MAX_PSTATE; ++l_pstate) { FAPI_TRY(forceCrctEndianArray(i_input.ATxDly_A[l_pstate], DRAMINIT_NUM_ADDR_DELAYS, o_data)); } for(uint8_t l_pstate = 0; l_pstate < MSDG_MAX_PSTATE; ++l_pstate) { FAPI_TRY(forceCrctEndianArray(i_input.ATxDly_B[l_pstate], DRAMINIT_NUM_ADDR_DELAYS, o_data)); } o_crc = crc32_gen(o_data); padCommData(o_data); FAPI_TRY(correctMMIOEndianForStruct(o_data)); fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// @brief Writes user_input_msdg to the data buffer /// /// @param[in] i_target The Explorer chip to issue the command to /// @param[in] i_data The user_input_msdg data to write /// @param[out] o_crc The calculated crc of the data. /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putUserInputMsdg( const fapi2::Target& i_target, const user_input_msdg& i_data, uint32_t& o_crc) { std::vector l_data; FAPI_TRY(user_input_msdg_to_little_endian(i_data, l_data, o_crc)); FAPI_TRY(fapi2::putMMIO(i_target, EXPLR_IB_DATA_ADDR, BUFFER_TRANSACTION_SIZE, l_data)); fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } fapi2::ReturnCode host_fw_command_struct_to_little_endian(const host_fw_command_struct& i_input, std::vector& o_data) { uint32_t l_cmd_header_crc = 0; FAPI_TRY(forceCrctEndian(i_input.cmd_id, o_data)); FAPI_TRY(forceCrctEndian(i_input.cmd_flags, o_data)); FAPI_TRY(forceCrctEndian(i_input.request_identifier, o_data)); FAPI_TRY(forceCrctEndian(i_input.cmd_length, o_data)); FAPI_TRY(forceCrctEndian(i_input.cmd_crc, o_data)); FAPI_TRY(forceCrctEndian(i_input.host_work_area, o_data)); FAPI_TRY(forceCrctEndian(i_input.cmd_work_area, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.padding, CMD_PADDING_SIZE, o_data)); FAPI_TRY(forceCrctEndianArray(i_input.command_argument, ARGUMENT_SIZE, o_data)); // Generates and adds on the CRC l_cmd_header_crc = crc32_gen(o_data); FAPI_DBG("Command header crc: %xl", l_cmd_header_crc); FAPI_TRY(forceCrctEndian(l_cmd_header_crc, o_data)); padCommData(o_data); FAPI_TRY(correctMMIOEndianForStruct(o_data)); fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// @brief Writes a command to the command buffer and issues interrupt /// /// @param[in] i_target The Explorer chip to issue the command to /// @param[in] i_cmd The command structure to write /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode putCMD( const fapi2::Target& i_target, const host_fw_command_struct& i_cmd) { std::vector l_data; fapi2::buffer l_scom; FAPI_TRY(host_fw_command_struct_to_little_endian(i_cmd, l_data)); // Clear the doorbell l_scom.setBit(); FAPI_DBG("Clearing the inbound doorbell..."); FAPI_TRY(fapi2::putScom(i_target, EXPLR_MMIO_MDBELLC, l_scom), "Failed to clear inbound doorbell register"); // Set the command FAPI_DBG("Writing the command..."); FAPI_TRY(fapi2::putMMIO(i_target, EXPLR_IB_CMD_ADDR, BUFFER_TRANSACTION_SIZE, l_data), "Failed to write to the command buffer"); // Ring the doorbell - aka the bit that interrupts the microchip FW and tells it to do the thing l_scom.flush<0>(); l_scom.setBit(); FAPI_DBG("Setting the inbound doorbell..."); FAPI_TRY(fapi2::putScom(i_target, EXPLR_MMIO_MDBELL, l_scom), "Failed to set inbound doorbell bit"); fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } //-------------------------------------------------------------------------------- // Read operations //-------------------------------------------------------------------------------- /// @brief Reads 64 bits of data from MMIO space on the selected Explorer /// /// @param[in] i_target The Explorer chip to read data from /// @param[in] i_addr The address to read /// @param[out] o_data The data read from the address /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode getMMIO64( const fapi2::Target& i_target, const uint64_t i_addr, fapi2::buffer& o_data) { uint64_t l_rd = 0; std::vector l_data(8); uint32_t l_idx = 0; FAPI_TRY(fapi2::getMMIO(i_target, EXPLR_IB_MMIO_OFFSET | i_addr, 8, l_data)); readLE(l_data, l_idx, l_rd); o_data = l_rd; fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// @brief Reads 32 bits of data from MMIO space on the selected Explorer /// /// @param[in] i_target The Explorer chip to read data from /// @param[in] i_addr The address to read /// @param[out] o_data The data read from the address /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode getMMIO32( const fapi2::Target& i_target, const uint64_t i_addr, fapi2::buffer& o_data) { uint32_t l_rd = 0; std::vector l_data(4); uint32_t l_idx = 0; FAPI_TRY(fapi2::getMMIO(i_target, EXPLR_IB_MMIO_OFFSET | i_addr, 4, l_data)); readLE(l_data, l_idx, l_rd); o_data = l_rd; fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// @brief Reads 64 bits of data from SCOM MMIO space on the selected Explorer /// /// @param[in] i_target The Explorer chip to read data from /// @param[in] i_scomAddr The address to read /// @param[out] o_data The data read from the address /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode getScom( const fapi2::Target& i_target, const uint64_t i_scomAddr, fapi2::buffer& o_data) { // Converts from the scom address to the MMIO address by shifting left by 3 bits uint64_t l_scomAddr = i_scomAddr << OCMB_ADDR_SHIFT; return getMMIO64(i_target, l_scomAddr, o_data); } /// @brief Reads 32 bits of data from OpenCAPI config space on the selected Explorer /// /// @param[in] i_target The Explorer chip to read data from /// @param[in] i_cfgAddr The address to read /// @param[out] o_data The data read from the address /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode getOCCfg( const fapi2::Target& i_target, const uint64_t i_cfgAddr, fapi2::buffer& o_data) { uint32_t l_rd = 0; std::vector l_data(4); uint32_t l_idx = 0; FAPI_TRY(fapi2::getMMIO(i_target, i_cfgAddr, 4, l_data)); readLE(l_data, l_idx, l_rd); o_data = l_rd; fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// /// @brief Converts a little endian data array to a host_fw_response_struct /// @param[in] i_data little endian data to process /// @param[out] o_crc computed CRC /// @param[out] o_response response structure /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// fapi2::ReturnCode host_fw_response_struct_from_little_endian(std::vector& i_data, uint32_t& o_crc, host_fw_response_struct& o_response) { uint32_t l_idx = 0; FAPI_TRY(correctMMIOEndianForStruct(i_data)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.response_id)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.response_flags)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.request_identifier)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.response_length)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.response_crc)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.host_work_area)); FAPI_TRY(readCrctEndianArray(i_data, RSP_PADDING_SIZE, l_idx, o_response.padding)); FAPI_TRY(readCrctEndianArray(i_data, ARGUMENT_SIZE, l_idx, o_response.response_argument)); o_crc = crc32_gen(i_data, l_idx); FAPI_TRY(readCrctEndian(i_data, l_idx, o_response.response_header_crc)); fapi_try_exit: return fapi2::current_err; } /// /// @brief Converts a little endian data array to a host_fw_response_struct /// @param[in] i_target OCMB target on which to operate /// @param[in] i_data little endian data to process /// @param[out] o_response response structure /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// @note helper function to allow for checking FFDC /// fapi2::ReturnCode host_fw_response_struct_from_little_endian(const fapi2::Target& i_target, std::vector& i_data, host_fw_response_struct& o_response) { uint32_t l_crc = 0; fapi2::current_err = host_fw_response_struct_from_little_endian(i_data, l_crc, o_response); FAPI_ASSERT( fapi2::current_err == fapi2::FAPI2_RC_SUCCESS, fapi2::EXP_INBAND_LE_DATA_RANGE() .set_TARGET(i_target) .set_FUNCTION(mss::exp::READ_HOST_FW_RESPONSE_STRUCT) .set_DATA_SIZE(i_data.size()) .set_MAX_INDEX(sizeof(host_fw_response_struct)), "%s Failed to convert from data to host_fw_response_struct data size %u expected size %u", mss::c_str(i_target), i_data.size(), sizeof(host_fw_response_struct)); //TODO CQ: SW461052 Correct MMIO CRC responses #ifndef CONFIG_AXONE_BRING_UP FAPI_ASSERT(l_crc == o_response.response_header_crc, fapi2::EXP_INBAND_RSP_CRC_ERR() .set_COMPUTED(l_crc) .set_RECEIVED(o_response.response_header_crc) .set_OCMB_TARGET(i_target), "%s Response CRC failed to validate computed: 0x%08x got: 0x%08x", mss::c_str(i_target), l_crc, o_response.response_header_crc); #endif fapi_try_exit: return fapi2::current_err; } /// /// @brief Polls for response ready door bell bit /// @param[in] i_target the OCMB target on which to operate /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// fapi2::ReturnCode poll_for_response_ready(const fapi2::Target& i_target) { constexpr uint64_t NUM_LOOPS = 50; // So, why aren't we using the memory team's polling API? // This is a base function that will be utilized by the platform code // As such, we don't want to pull in more libraries than we need to: it would cause extra dependencies // So, we're decomposing the polling library below bool l_doorbell_response = false; uint64_t l_loop = 0; fapi2::buffer l_data; // Loop until we max our our loop count or get a doorbell response for(; l_loop < NUM_LOOPS && !l_doorbell_response; ++l_loop) { FAPI_TRY(fapi2::getScom(i_target, EXPLR_MIPS_TO_OCMB_INTERRUPT_REGISTER1, l_data)); l_doorbell_response = l_data.getBit(); FAPI_TRY( fapi2::delay( DELAY_100NS, 200) ); } FAPI_DBG("%s stopped on loop%u/%u data:0x%016lx %u", mss::c_str(i_target), l_loop, NUM_LOOPS, l_data, l_doorbell_response); // Error check - doorbell response should be true FAPI_ASSERT(l_doorbell_response, fapi2::EXP_INBAND_RSP_NO_DOORBELL() .set_OCMB_TARGET(i_target) .set_DATA(l_data) .set_NUM_LOOPS(l_loop), "%s doorbell timed out after %u loops: data 0x%016lx", mss::c_str(i_target), l_loop, l_data); // Ding-dong! the doorbell is rung and the response is ready return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: return fapi2::current_err; } /// /// @brief Clears outbound (response ready) door bell bit /// @param[in] i_target the OCMB target on which to operate /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// fapi2::ReturnCode clear_outbound_doorbell(const fapi2::Target& i_target) { fapi2::buffer l_data; // Doorbell is cleared by writing a '1' to the doorbell bit l_data.setBit(); FAPI_DBG("%s Clearing outbound doorbell...", mss::c_str(i_target)); FAPI_TRY(fapi2::putScom(i_target, EXPLR_MIPS_TO_OCMB_INTERRUPT_REGISTER1, l_data)); fapi_try_exit: return fapi2::current_err; } /// @brief Reads a response from the response buffer /// /// @param[in] i_target The Explorer chip to read data from /// @param[out] o_rsp The response data read from the buffer /// @param[out] o_data Raw (little-endian) response data buffer portion /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode getRSP( const fapi2::Target& i_target, host_fw_response_struct& o_rsp, std::vector& o_data) { std::vector l_data(static_cast(sizeof(o_rsp))); // Polls for the response to be ready first FAPI_TRY(poll_for_response_ready(i_target)); FAPI_DBG("Reading the response buffer..."); FAPI_TRY(fapi2::getMMIO(i_target, EXPLR_IB_RSP_ADDR, BUFFER_TRANSACTION_SIZE, l_data)); FAPI_TRY(host_fw_response_struct_from_little_endian(i_target, l_data, o_rsp)); FAPI_DBG("Checking if we have response data..."); // If response data in buffer portion, return that too if (o_rsp.response_length > 0) { // make sure expected size is a multiple of 8 const uint32_t l_padding = ((o_rsp.response_length % 8) > 0) ? (8 - (o_rsp.response_length % 8)) : 0; o_data.resize( o_rsp.response_length + l_padding ); FAPI_DBG("Reading response data..."); FAPI_TRY( fapi2::getMMIO(i_target, EXPLR_IB_DATA_ADDR, BUFFER_TRANSACTION_SIZE, o_data) ); FAPI_TRY( correctMMIOEndianForStruct(o_data) ); } else { FAPI_DBG("No response data returned..."); // make sure no buffer data is returned o_data.clear(); } FAPI_TRY(clear_outbound_doorbell(i_target)); fapi_try_exit: FAPI_DBG("%s Exiting with return code : 0x%08X...", mss::c_str(i_target), (uint64_t) fapi2::current_err); return fapi2::current_err; } /// /// @brief Converts a little endian data array to a sensor_cache_struct /// @param[in] i_data little endian data to process /// @param[out] o_data sensor cache structure /// @return true if success false if failure /// @note helper function - returning a bool and will have true FFDC in a separate function /// fapi2::ReturnCode sensor_cache_struct_from_little_endian(std::vector& i_data, sensor_cache_struct& o_data) { uint32_t l_idx = 0; FAPI_TRY(correctMMIOEndianForStruct(i_data)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.status)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.ocmb_dts)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mem_dts0)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mem_dts1)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_reads)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_writes)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_activations)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_powerups)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.self_timed_refresh)); FAPI_TRY(readCrctEndianArray(i_data, SENSOR_CACHE_PADDING_SIZE_0, l_idx, o_data.reserved0)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.frame_count)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_arrival_histo_base)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_arrival_histo_low)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_arrival_histo_med)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.mba_arrival_histo_high)); FAPI_TRY(readCrctEndian(i_data, l_idx, o_data.initial_packet1)); FAPI_TRY(readCrctEndianArray(i_data, SENSOR_CACHE_PADDING_SIZE_1, l_idx, o_data.reserved1)); fapi_try_exit: return fapi2::current_err; } /// /// @brief Converts a little endian data array to a sensor_cache_struct /// @param[in] i_target OCMB target on which to operate /// @param[in] i_data little endian data to process /// @param[out] o_data sensor cache structure /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// @note helper function to allow for checking FFDC /// fapi2::ReturnCode sensor_cache_struct_from_little_endian(const fapi2::Target& i_target, std::vector& i_data, sensor_cache_struct& o_data) { fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; FAPI_TRY(sensor_cache_struct_from_little_endian(i_data, o_data)); fapi_try_exit: return fapi2::current_err; } /// /// @brief Reads the complete 64 byte sensor cache on the selected Explorer /// /// @param[in] i_target The Explorer chip to read data from /// @param[out] o_data The data read from the buffer /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// fapi2::ReturnCode getSensorCache( const fapi2::Target& i_target, sensor_cache_struct& o_data) { std::vector l_data(static_cast(sizeof(o_data))); // The sensor cache is accessed exclusively via 2 32-byte MMIO reads FAPI_TRY(fapi2::getMMIO(i_target, EXPLR_IB_SENSOR_CACHE_ADDR, 32, l_data)); FAPI_TRY(sensor_cache_struct_from_little_endian(i_target, l_data, o_data)); fapi_try_exit: FAPI_DBG("%s Exiting with return code : 0x%08X...", mss::c_str(i_target), (uint64_t) fapi2::current_err); return fapi2::current_err; } /// @brief We will use 4 or 8 byte reads via fapi2::put/getMMIO for buffer /// data structures. The byte order of the 4 or 8 byte reads should be little /// endian. In order to represent the data structure in its proper layout /// the endianness of each 4 or 8 byte read must be corrected. /// /// @param[in,out] io_data Either data structure in proper byte order that we /// want to swizzle prior to writing to the buffer, or the data returned /// from reading the buffer that we want to unsizzle. /// /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. fapi2::ReturnCode correctMMIOEndianForStruct(std::vector& io_data) { fapi2::Target FAPI_SYSTEM; fapi2::ATTR_MSS_OCMB_EXP_STRUCT_MMIO_ENDIAN_CTRL_Type l_endian_ctrl; size_t l_loops = 0; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_OCMB_EXP_STRUCT_MMIO_ENDIAN_CTRL, FAPI_SYSTEM, l_endian_ctrl)); if (l_endian_ctrl == fapi2::ENUM_ATTR_MSS_OCMB_EXP_STRUCT_MMIO_ENDIAN_CTRL_NO_SWAP) { goto fapi_try_exit; } l_loops = io_data.size() / BUFFER_TRANSACTION_SIZE; for (size_t l_idx = 0; l_idx < l_loops; l_idx++) { for (int l_bidx = BUFFER_TRANSACTION_SIZE - 1; l_bidx >= 0; l_bidx--) { io_data.push_back(io_data.at(l_bidx)); } io_data.erase(io_data.begin(), io_data.begin() + BUFFER_TRANSACTION_SIZE); } // Because of how the AXI bridge in Explorer breaks up the transaction, we also need to swap 32-bit word order for (size_t l_idx = 0; l_idx < io_data.size(); l_idx += BUFFER_TRANSACTION_SIZE) { for (size_t l_bidx = l_idx; l_bidx < l_idx + BUFFER_TRANSACTION_SIZE / 2; l_bidx++) { uint8_t l_temp_first_word = io_data.at(l_bidx); uint8_t l_temp_second_word = io_data.at(l_bidx + BUFFER_TRANSACTION_SIZE / 2); io_data[l_bidx] = l_temp_second_word; io_data[l_bidx + BUFFER_TRANSACTION_SIZE / 2] = l_temp_first_word; } } fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// /// @brief Forces native data into the correct endianness necessary for Explorer /// buffer data structures. /// @tparam T the data type to process /// @param[in] i_input inputted data to process /// @param[in,out] io_data vector to append data to /// template < typename T > fapi2::ReturnCode forceCrctEndian(const T& i_input, std::vector& io_data) { fapi2::Target FAPI_SYSTEM; fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_Type l_struct_endian; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN, FAPI_SYSTEM, l_struct_endian)); if (l_struct_endian == fapi2::ENUM_ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_LITTLE_ENDIAN) { forceLE(i_input, io_data); } else { forceBE(i_input, io_data); } fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// /// @brief Forces native data into the correct endianness for an array buffer /// data structures. /// @tparam T the data type to process /// @param[in] i_input inputted data to process /// @param[in] i_size size of the array /// @param[in,out] io_data vector to append data to /// template < typename T > fapi2::ReturnCode forceCrctEndianArray(const T* i_input, const uint64_t i_size, std::vector& io_data) { fapi2::Target FAPI_SYSTEM; fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_Type l_struct_endian; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN, FAPI_SYSTEM, l_struct_endian)); if (l_struct_endian == fapi2::ENUM_ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_LITTLE_ENDIAN) { forceLEArray(i_input, i_size, io_data); } else { forceBEArray(i_input, i_size, io_data); } fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } /// /// @brief Converts endianness of data read from Explorer buffer data structures // into native order. /// @tparam T the data type to output to /// @param[in] i_input inputted data to process /// @param[in,out] io_idx current index /// @param[out] o_data data that has been converted into native endianness /// @return fapi2::ReturnCode. FAPI2_RC_SUCCESS if success, else error code. /// template < typename T > fapi2::ReturnCode readCrctEndian(const std::vector& i_input, uint32_t& io_idx, T& o_data) { fapi2::Target FAPI_SYSTEM; fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_Type l_struct_endian; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN, FAPI_SYSTEM, l_struct_endian)); if (l_struct_endian == fapi2::ENUM_ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_LITTLE_ENDIAN) { FAPI_ASSERT(readLE(i_input, io_idx, o_data), fapi2::EXP_INBAND_LE_DATA_RANGE() .set_TARGET(FAPI_SYSTEM) .set_FUNCTION(mss::exp::READ_CRCT_ENDIAN) .set_DATA_SIZE(i_input.size()) .set_MAX_INDEX(sizeof(o_data)), "Failed to convert from LE data read, size %u expected size %u", i_input.size(), sizeof(o_data)); } else { FAPI_ASSERT(readBE(i_input, io_idx, o_data), fapi2::EXP_INBAND_BE_DATA_RANGE() .set_TARGET(FAPI_SYSTEM) .set_FUNCTION(mss::exp::READ_CRCT_ENDIAN) .set_DATA_SIZE(i_input.size()) .set_MAX_INDEX(sizeof(o_data)), "Failed to convert from BE data read, size %u expected size %u", i_input.size(), sizeof(o_data)); } fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } template < typename T > fapi2::ReturnCode readCrctEndianArray(const std::vector& i_input, const uint32_t i_size, uint32_t& io_idx, T* o_data) { fapi2::Target FAPI_SYSTEM; fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_Type l_struct_endian; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN, FAPI_SYSTEM, l_struct_endian)); if (l_struct_endian == fapi2::ENUM_ATTR_MSS_OCMB_EXP_STRUCT_ENDIAN_LITTLE_ENDIAN) { FAPI_ASSERT(readLEArray(i_input, i_size, io_idx, o_data), fapi2::EXP_INBAND_LE_DATA_RANGE() .set_TARGET(FAPI_SYSTEM) .set_FUNCTION(mss::exp::READ_CRCT_ENDIAN) .set_DATA_SIZE(i_input.size()) .set_MAX_INDEX(sizeof(o_data)), "Failed to convert from LE array data read, size %u expected size %u", i_input.size(), sizeof(o_data)); } else { FAPI_ASSERT(readBEArray(i_input, i_size, io_idx, o_data), fapi2::EXP_INBAND_BE_DATA_RANGE() .set_TARGET(FAPI_SYSTEM) .set_FUNCTION(mss::exp::READ_CRCT_ENDIAN) .set_DATA_SIZE(i_input.size()) .set_MAX_INDEX(sizeof(o_data)), "Failed to convert from BE array data read, size %u expected size %u", i_input.size(), sizeof(o_data)); } fapi_try_exit: FAPI_DBG("Exiting with return code : 0x%08X...", (uint64_t) fapi2::current_err); return fapi2::current_err; } } // ns ib } // ns exp } // ns mss