diff options
Diffstat (limited to 'src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C')
-rw-r--r-- | src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C index ab74db8f2..40841c1e0 100644 --- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C +++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C @@ -22,3 +22,319 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file pmic_enable_utils.C +/// @brief Utility functions for PMIC enable operation +/// +// *HWP HWP Owner: Mark Pizzutillo <mark.pizzutillo@ibm.com> +// *HWP HWP Backup: Louis Stermole <stermole@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> +#include <lib/i2c/i2c_pmic.H> +#include <lib/utils/pmic_enable_utils.H> +#include <lib/utils/pmic_consts.H> +#include <lib/utils/pmic_common_utils.H> +#include <pmic_regs.H> +#include <pmic_regs_fld.H> +#include <generic/memory/lib/utils/c_str.H> +#include <generic/memory/lib/utils/index.H> +#include <generic/memory/lib/utils/find.H> +#include <mss_pmic_attribute_getters.H> + +namespace mss +{ +namespace pmic +{ + +/// +/// @breif set VR enable bit for system startup via R32 (not broadcast) +/// +/// @param[in] i_pmic_target PMIC target +/// @return fapi2::FAPI2_RC_SUCCESS iff success +/// +fapi2::ReturnCode start_vr_enable( + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs<J>; + using FIELDS = pmicFields<J>; + + + fapi2::buffer<uint8_t> l_programmable_mode_buffer; + fapi2::buffer<uint8_t> l_vr_enable_buffer; + + // Enable programmable mode + FAPI_TRY(mss::pmic::i2c::reg_read_reverse_buffer(i_pmic_target, REGS::R2F, l_programmable_mode_buffer), + "start_vr_enable: Error reading address 0x%02hhX of %s to enable programmable mode operation", + REGS::R2F, mss::c_str(i_pmic_target)); + + l_programmable_mode_buffer.writeBit<FIELDS::R2F_SECURE_MODE>( + mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>::PROGRAMMABLE_MODE); + + FAPI_TRY(mss::pmic::i2c::reg_write_reverse_buffer(i_pmic_target, REGS::R2F, l_programmable_mode_buffer), + "start_vr_enable: Error writing address 0x%02hhX of %s to enable programmable mode operation", + REGS::R2F, mss::c_str(i_pmic_target)); + + // Start VR Enable + // Write 1 to R2F(2) + l_vr_enable_buffer.setBit<FIELDS::R32_VR_ENABLE>(); + + FAPI_INF("Executing VR_ENABLE for PMIC %s", mss::c_str(i_pmic_target)); + FAPI_TRY(mss::pmic::i2c::reg_write_reverse_buffer(i_pmic_target, REGS::R32, l_vr_enable_buffer), + "start_vr_enable: Could not write to address 0x%02hhX of %s to execute VR Enable", + REGS::R32, + mss::c_str(i_pmic_target)); + + // At this point, the PWR_GOOD pin should begin to rise to high. This can't directly be checked via a register + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief bias PMIC with spd settings for phase combination (SWA, SWB or SWA+SWB) +/// +/// @param[in] i_pmic_target PMIC target +/// @param[in] i_dimm_target - DIMM target of PMIC +/// @param[in] i_id - PMIC0 or PMIC1 +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error +/// +fapi2::ReturnCode bias_with_spd_phase_comb( + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target, + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using FIELDS = pmicFields<J>; + using REGS = pmicRegs<J>; + + uint8_t l_phase_comb = 0; + fapi2::buffer<uint8_t> l_phase; + FAPI_TRY(mss::pmic::get_phase_comb[i_id](i_dimm_target, l_phase_comb)); + + // Read, replace bit, and then re-write + FAPI_TRY(mss::pmic::i2c::reg_read_reverse_buffer(i_pmic_target, REGS::R4F, l_phase)); + l_phase.writeBit<FIELDS::SWA_SWB_PHASE_MODE_SELECT>(l_phase_comb); + FAPI_TRY(mss::pmic::i2c::reg_write_reverse_buffer(i_pmic_target, REGS::R4F, l_phase)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief bias PMIC with SPD settings for voltage ranges +/// +/// @param[in] i_pmic_target PMIC target +/// @param[in] i_dimm_target dimm target of PMIC +/// @param[in] i_id PMIC0 or PMIC1 +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error +/// +fapi2::ReturnCode bias_with_spd_volt_ranges( + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target, + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using FIELDS = pmicFields<J>; + using REGS = pmicRegs<J>; + + uint8_t l_swa_range = 0; + uint8_t l_swb_range = 0; + uint8_t l_swc_range = 0; + uint8_t l_swd_range = 0; + + fapi2::buffer<uint8_t> l_volt_range_buffer; + + FAPI_TRY(mss::pmic::get_swa_voltage_range_select[i_id](i_dimm_target, l_swa_range)); + FAPI_TRY(mss::pmic::get_swb_voltage_range_select[i_id](i_dimm_target, l_swb_range)); + FAPI_TRY(mss::pmic::get_swc_voltage_range_select[i_id](i_dimm_target, l_swc_range)); + FAPI_TRY(mss::pmic::get_swd_voltage_range_select[i_id](i_dimm_target, l_swd_range)); + + // Read in what the register has, as to not overwrite any default values + FAPI_TRY(mss::pmic::i2c::reg_read_reverse_buffer(i_pmic_target, REGS::R2B, l_volt_range_buffer)); + + // Set the buffer bits appropriately + l_volt_range_buffer.writeBit<FIELDS::SWA_VOLTAGE_RANGE>(l_swa_range); + l_volt_range_buffer.writeBit<FIELDS::SWB_VOLTAGE_RANGE>(l_swb_range); + l_volt_range_buffer.writeBit<FIELDS::SWC_VOLTAGE_RANGE>(l_swc_range); + l_volt_range_buffer.writeBit<FIELDS::SWD_VOLTAGE_RANGE>(l_swd_range); + + // Write back to PMIC + FAPI_TRY(mss::pmic::i2c::reg_write_reverse_buffer(i_pmic_target, REGS::R2B, l_volt_range_buffer)); + +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief bias PMIC with SPD settings for startup sequence +/// +/// @param[in] i_pmic_target PMIC target +/// @param[in] i_dimm_target dimm target of PMIC +/// @param[in] i_id PMIC0 or PMIC1 +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error +/// @note TK - this function may greatly change. It relies on SPD fields that may not be correct. +/// +fapi2::ReturnCode bias_with_spd_startup_seq( + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target, + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs<J>; + using CONSTS = mss::pmic::consts<J>; + + // Arrays to key off of rail and PMIC ID + static const pmic_attr_ptr* l_get_sequence_order[] = {mss::pmic::get_swa_sequence_order, + mss::pmic::get_swb_sequence_order, + mss::pmic::get_swc_sequence_order, + mss::pmic::get_swd_sequence_order + }; + + static const pmic_attr_ptr* l_get_sequence_delay[] = {mss::pmic::get_swa_sequence_delay, + mss::pmic::get_swb_sequence_delay, + mss::pmic::get_swc_sequence_delay, + mss::pmic::get_swd_sequence_delay + }; + + // Arrays to store the attribute data + uint8_t l_sequence_orders[CONSTS::NUMBER_OF_RAILS]; + uint8_t l_sequence_delays[CONSTS::NUMBER_OF_RAILS]; + + // Loop through each rail to populate the order and delay arrays + for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index) + { + // We know after these FAPI_TRY's that all 4 entries must be populated, else the TRYs fail + FAPI_TRY(((l_get_sequence_order[l_rail_index][i_id]))(i_dimm_target, l_sequence_orders[l_rail_index])); + FAPI_TRY(((l_get_sequence_delay[l_rail_index][i_id]))(i_dimm_target, l_sequence_delays[l_rail_index])); + + // The SPD allows for up to 8 sequences, but there are only 4 on the PMIC. The SPD defaults never go higher than 2. + // We put this check in here as with anything over 4, we don't really know what we can do. + FAPI_ASSERT(((l_sequence_orders[l_rail_index] < CONSTS::ORDER_LIMIT)), + fapi2::PMIC_ORDER_OUT_OF_RANGE() + .set_TARGET(i_pmic_target) + .set_RAIL(l_rail_index) + .set_ORDER(l_sequence_orders[l_rail_index]), + "PMIC sequence order specified by the SPD was out of range for PMIC: %s Rail: %s Order: %u", + mss::c_str(i_pmic_target), + PMIC_RAIL_NAMES[l_rail_index], + l_sequence_orders[l_rail_index]); + } + + { + fapi2::buffer<uint8_t> l_power_on_sequence_config; + + // Zero out sequence registers first + FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R40_POWER_ON_SEQUENCE_CONFIG_1, l_power_on_sequence_config)); + FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R41_POWER_ON_SEQUENCE_CONFIG_2, l_power_on_sequence_config)); + FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R42_POWER_ON_SEQUENCE_CONFIG_3, l_power_on_sequence_config)); + FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R43_POWER_ON_SEQUENCE_CONFIG_4, l_power_on_sequence_config)); + + // Set the registers appropriately. + // We do manual reversing here (see above) so that we can also place in the delays as they're supposed to be + for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index) + { + if (l_sequence_orders[l_rail_index] != 0) // If 0, it will not be sequenced + { + // Set the register contents appropriately + FAPI_TRY(mss::pmic::set_startup_seq_register(i_pmic_target, l_rail_index, + l_sequence_orders[l_rail_index], l_sequence_delays[l_rail_index])); + } + + // else, zero, do nothing. + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Set the startup seq register with the given parameters +/// +/// @param[in] i_pmic_target PMIC target +/// @param[in] i_rail rail to +/// @param[in] i_round sequence round 1-4 +/// @param[in] i_delay delay after round +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error +/// +fapi2::ReturnCode set_startup_seq_register( + const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target, + const uint8_t i_rail, + const uint8_t i_round, + const uint8_t i_delay) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs<J>; + using FIELDS = pmicFields<J>; + using CONSTS = mss::pmic::consts<J>; + + // PMIC registers are indexed [7:0], so from the buffer point of view, our bit fields need to be flipped + static constexpr uint8_t RIGHT_ALIGN_OFFSET = 7; + + // Starts at bit 0 (right aligned) with length 3. So we need to take length 3 from bit 5 in our buffer + static constexpr uint8_t DELAY_START = 5; + + static const std::vector<uint8_t> SEQUENCE_REGS = + { + 0, // 0 would imply no sequence config (won't occur due to assert in bias_with_spd_startup_seq) + REGS::R40_POWER_ON_SEQUENCE_CONFIG_1, + REGS::R41_POWER_ON_SEQUENCE_CONFIG_2, + REGS::R42_POWER_ON_SEQUENCE_CONFIG_3, + REGS::R43_POWER_ON_SEQUENCE_CONFIG_4 + }; + + // Manual reversing of sequence bits + static const std::vector<uint8_t> SEQUENCE_BITS = + { + RIGHT_ALIGN_OFFSET - FIELDS::SEQUENCE_SWA_ENABLE, + RIGHT_ALIGN_OFFSET - FIELDS::SEQUENCE_SWB_ENABLE, + RIGHT_ALIGN_OFFSET - FIELDS::SEQUENCE_SWC_ENABLE, + RIGHT_ALIGN_OFFSET - FIELDS::SEQUENCE_SWD_ENABLE + }; + + fapi2::buffer<uint8_t> l_power_on_sequence_config; + + // Check and make sure the given delay is less than the bitmask (else, we overflow later on) + FAPI_ASSERT(i_delay <= CONSTS::MAX_DELAY_BITMAP, + fapi2::PMIC_DELAY_OUT_OF_RANGE() + .set_TARGET(i_pmic_target) + .set_RAIL(i_rail) + .set_DELAY(i_delay), + "PMIC sequence delay from the SPD attribute was out of range for PMIC: %s Rail: %s Delay: %u Max: %u", + mss::c_str(i_pmic_target), + PMIC_RAIL_NAMES[i_rail], + i_delay, + CONSTS::MAX_DELAY_BITMAP); + + // PMIC registers are indexed [7:0], so from the buffer point of view, our bit fields need to be flipped + static constexpr uint8_t SEQUENCE_ENABLE_REVERSED = (RIGHT_ALIGN_OFFSET - FIELDS::SEQUENCE_ENABLE); + + FAPI_TRY(mss::pmic::i2c::reg_read(i_pmic_target, SEQUENCE_REGS[i_round], l_power_on_sequence_config)); + + // Adding on the sequence delays (which are the far right 3 bits). + + // Note: The SPD has sequence delays per rail NOT per sequence (PMIC is by sequence). Here, + // if two rails on the same sequence have different delays, the last rail's delay (ex. SWD) will take precedence. + // In other words, they will be made to match. Otherwise, the SPD is flawed. + l_power_on_sequence_config.clearBit<DELAY_START, FIELDS::DELAY_FLD_LENGTH>(); + l_power_on_sequence_config = l_power_on_sequence_config + i_delay; + + l_power_on_sequence_config.setBit(SEQUENCE_BITS[i_rail]); + l_power_on_sequence_config.setBit(SEQUENCE_ENABLE_REVERSED); + + FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, SEQUENCE_REGS[i_round], l_power_on_sequence_config)); + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +} +} // mss |