summaryrefslogtreecommitdiffstats
path: root/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C
diff options
context:
space:
mode:
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.C316
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
OpenPOWER on IntegriCloud