summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Pizzutillo <Mark.Pizzutillo@ibm.com>2019-04-15 16:08:22 -0400
committerChristian R. Geddes <crgeddes@us.ibm.com>2019-05-23 10:47:25 -0500
commit1dab92e705f6f8c893a09ff250eab1a762f7e88f (patch)
tree057239c62c8518640bad8c1a3ea306a84c108725
parent5de4c5ec41bf05f8e3d735ded4cc864d017e6279 (diff)
downloadtalos-hostboot-1dab92e705f6f8c893a09ff250eab1a762f7e88f.tar.gz
talos-hostboot-1dab92e705f6f8c893a09ff250eab1a762f7e88f.zip
Add PMIC enable procedure code and UTs
Change-Id: Iac5cd8016efa705be6512b842e0e793eb3d4c5fa Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/74639 Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Tested-by: Hostboot CI <hostboot-ci+hostboot@us.ibm.com> Tested-by: HWSV CI <hwsv-ci+hostboot@us.ibm.com> Reviewed-by: Louis Stermole <stermole@us.ibm.com> Reviewed-by: STEPHEN GLANCY <sglancy@us.ibm.com> Reviewed-by: Thi N. Tran <thi@us.ibm.com> Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/77132 Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com> Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
-rw-r--r--src/import/chips/ocmb/common/include/pmic_regs.H130
-rw-r--r--src/import/chips/ocmb/common/include/pmic_regs_fld.H53
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H1
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.C125
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.H104
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_consts.H154
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.C316
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.H784
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.C101
-rw-r--r--src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.H2
-rw-r--r--src/import/chips/ocmb/common/procedures/xml/error_info/pmic_errors.xml88
-rw-r--r--src/import/generic/memory/lib/utils/shared/mss_generic_consts.H1
12 files changed, 1849 insertions, 10 deletions
diff --git a/src/import/chips/ocmb/common/include/pmic_regs.H b/src/import/chips/ocmb/common/include/pmic_regs.H
index 4a6de40d4..f3010e727 100644
--- a/src/import/chips/ocmb/common/include/pmic_regs.H
+++ b/src/import/chips/ocmb/common/include/pmic_regs.H
@@ -22,3 +22,133 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file pmic_regs.H
+/// @brief PMIC Registers
+///
+// *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
+
+#ifndef __PMIC_REGS__
+#define __PMIC_REGS__
+
+#include <lib/utils/pmic_consts.H>
+
+///
+/// @brief Registers for PMIC devices
+/// @class pmicRegs
+/// @tparam P pmic_product
+///
+template<mss::pmic::product P>
+struct pmicRegs;
+
+///
+/// @brief JEDEC Common Registers
+/// @note These registers are not defined with any particular name other than RXX.
+/// Their purposes and bit-mappings are outlined in any of the JEDEC compliant PMIC specs.
+///
+template<>
+struct pmicRegs<mss::pmic::product::JEDEC_COMPLIANT>
+{
+ static constexpr uint8_t BROADCAST = 0x0;
+ /* R00 - R03 RESERVED */
+ static constexpr uint8_t R04 = 0x04;
+ static constexpr uint8_t R05 = 0x05;
+ static constexpr uint8_t R06 = 0x06;
+ /* R07 RESERVED */
+ static constexpr uint8_t R08 = 0x08;
+ static constexpr uint8_t R09 = 0x09;
+ static constexpr uint8_t R0A = 0x0A;
+ static constexpr uint8_t R0B = 0x0B;
+ static constexpr uint8_t R0C = 0x0C;
+ static constexpr uint8_t R0D = 0x0D;
+ static constexpr uint8_t R0E = 0x0E;
+ static constexpr uint8_t R0F = 0x0F;
+ static constexpr uint8_t R10 = 0x10;
+ static constexpr uint8_t R11 = 0x11;
+ static constexpr uint8_t R12 = 0x12;
+ static constexpr uint8_t R13 = 0x13;
+ static constexpr uint8_t R14 = 0x14;
+ static constexpr uint8_t R15 = 0x15;
+ static constexpr uint8_t R16 = 0x16;
+ static constexpr uint8_t R17 = 0x17;
+ static constexpr uint8_t R18 = 0x18;
+ static constexpr uint8_t R19 = 0x19;
+ static constexpr uint8_t R1A = 0x1A;
+ static constexpr uint8_t R1B = 0x1B;
+ static constexpr uint8_t R1C = 0x1C;
+ static constexpr uint8_t R1D = 0x1D;
+ static constexpr uint8_t R1E = 0x1E;
+ static constexpr uint8_t R1F = 0x1F;
+ static constexpr uint8_t R20 = 0x20;
+ static constexpr uint8_t R21_SWA_VOLTAGE_SETTING = 0x21;
+ static constexpr uint8_t R22 = 0x22;
+ static constexpr uint8_t R23_SWB_VOLTAGE_SETTING = 0x23;
+ static constexpr uint8_t R24 = 0x24;
+ static constexpr uint8_t R25_SWC_VOLTAGE_SETTING = 0x25;
+ static constexpr uint8_t R26 = 0x26;
+ static constexpr uint8_t R27_SWD_VOLTAGE_SETTING = 0x27;
+ static constexpr uint8_t R28 = 0x28;
+ static constexpr uint8_t R29 = 0x29;
+ static constexpr uint8_t R2A = 0x2A;
+ static constexpr uint8_t R2B = 0x2B;
+ static constexpr uint8_t R2C = 0x2C;
+ static constexpr uint8_t R2D = 0x2D;
+ static constexpr uint8_t R2E = 0x2E;
+ static constexpr uint8_t R2F = 0x2F;
+ static constexpr uint8_t R30 = 0x30;
+ static constexpr uint8_t R31 = 0x31;
+ static constexpr uint8_t R32 = 0x32;
+ static constexpr uint8_t R33 = 0x33;
+ static constexpr uint8_t R34 = 0x34;
+ static constexpr uint8_t R35 = 0x35;
+ /* R36 RESERVED */
+ static constexpr uint8_t R37_PASSWORD_LOWER_BYTE_0 = 0x37;
+ static constexpr uint8_t R38_PASSWORD_UPPER_BYTE_1 = 0x38;
+ static constexpr uint8_t R39_COMMAND_CODES = 0x39;
+ static constexpr uint8_t R3A = 0x3A;
+ static constexpr uint8_t R3B = 0x3B;
+ static constexpr uint8_t R3C_VENDOR_ID_BYTE_0 = 0x3C;
+ static constexpr uint8_t R3D_VENDOR_ID_BYTE_1 = 0x3D;
+
+ /* ----- DIMM VENDOR REGION ----- */
+
+ /* R3E - R3F RESERVED */
+ static constexpr uint8_t R40_POWER_ON_SEQUENCE_CONFIG_1 = 0x40;
+ static constexpr uint8_t R41_POWER_ON_SEQUENCE_CONFIG_2 = 0x41;
+ static constexpr uint8_t R42_POWER_ON_SEQUENCE_CONFIG_3 = 0x42;
+ static constexpr uint8_t R43_POWER_ON_SEQUENCE_CONFIG_4 = 0x43;
+ /* R44 RESERVED */
+ static constexpr uint8_t R45_SWA_VOLTAGE_SETTING = 0x45;
+ static constexpr uint8_t R46 = 0x46;
+ static constexpr uint8_t R47_SWB_VOLTAGE_SETTING = 0x47;
+ static constexpr uint8_t R48 = 0x48;
+ static constexpr uint8_t R49_SWC_VOLTAGE_SETTING = 0x49;
+ static constexpr uint8_t R4A = 0x4A;
+ static constexpr uint8_t R4B_SWD_VOLTAGE_SETTING = 0x4B;
+ static constexpr uint8_t R4C = 0x4C;
+ static constexpr uint8_t R4D = 0x4D;
+ static constexpr uint8_t R4E = 0x4E;
+ static constexpr uint8_t R4F = 0x4F;
+ static constexpr uint8_t R50 = 0x50;
+ static constexpr uint8_t R51 = 0x51;
+ /* R52 - R57 RESERVED */
+ static constexpr uint8_t R58_POWER_OFF_SEQUENCE_CONFIG_1 = 0x58;
+ static constexpr uint8_t R59_POWER_OFF_SEQUENCE_CONFIG_2 = 0x59;
+ static constexpr uint8_t R5A_POWER_OFF_SEQUENCE_CONFIG_3 = 0x5A;
+ static constexpr uint8_t R5B_POWER_OFF_SEQUENCE_CONFIG_4 = 0x5B;
+ /* R5C RESERVED */
+ static constexpr uint8_t R5D = 0x5D;
+ static constexpr uint8_t R5E = 0x5E;
+ static constexpr uint8_t R5F_PRIMARY_INFERFACE_IO_TYPE = 0x5F;
+ /* R60 - R6C RESERVED */
+ static constexpr uint8_t R6D = 0x6D;
+ static constexpr uint8_t R6E = 0x6E;
+ /* R6F RESERVED */
+};
+
+#endif
diff --git a/src/import/chips/ocmb/common/include/pmic_regs_fld.H b/src/import/chips/ocmb/common/include/pmic_regs_fld.H
index 5531e0888..2eb348df9 100644
--- a/src/import/chips/ocmb/common/include/pmic_regs_fld.H
+++ b/src/import/chips/ocmb/common/include/pmic_regs_fld.H
@@ -22,3 +22,56 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file pmic_regs_fld.H
+/// @brief PMIC Register Fields
+///
+// *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
+
+#ifndef __PMIC_REGS_FLD__
+#define __PMIC_REGS_FLD__
+
+#include <lib/utils/pmic_consts.H>
+
+///
+/// @brief Register fields for PMIC devices
+/// @class pmicFields
+/// @tparam P pmic_product
+///
+template<mss::pmic::product P>
+struct pmicFields;
+
+///
+/// @brief Fields for JEDEC_COMPLIANT PMICs
+///
+template<>
+struct pmicFields<mss::pmic::product::JEDEC_COMPLIANT>
+{
+ static constexpr uint8_t R2F_SECURE_MODE = 0x02;
+ static constexpr uint8_t R32_VR_ENABLE = 0x07;
+ static constexpr uint8_t PMIC_DEVICE = 0x00;
+
+ static constexpr uint8_t SWA_SWB_PHASE_MODE_SELECT = 0x00;
+
+ static constexpr uint8_t SWA_VOLTAGE_RANGE = 0x05;
+ static constexpr uint8_t SWB_VOLTAGE_RANGE = 0x04;
+ static constexpr uint8_t SWC_VOLTAGE_RANGE = 0x03;
+ static constexpr uint8_t SWD_VOLTAGE_RANGE = 0x00;
+
+ static constexpr uint8_t SEQUENCE_ENABLE = 0x07;
+ static constexpr uint8_t SEQUENCE_SWA_ENABLE = 0x06;
+ static constexpr uint8_t SEQUENCE_SWB_ENABLE = 0x05;
+ static constexpr uint8_t SEQUENCE_SWC_ENABLE = 0x04;
+ static constexpr uint8_t SEQUENCE_SWD_ENABLE = 0x03;
+
+ static constexpr uint8_t VIN_BULK_INPUT_PWR_GOOD_STATUS = 0x07;
+
+ static constexpr uint8_t DELAY_FLD_LENGTH = 3;
+};
+
+#endif
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H
index 1ba222402..fcd79dec0 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H
@@ -148,7 +148,6 @@ inline fapi2::ReturnCode reg_read_reverse_buffer(const fapi2::Target<fapi2::TARG
{
FAPI_TRY(mss::pmic::i2c::reg_read(i_target, i_addr, o_data_buffer));
o_data_buffer.reverse();
-
fapi_try_exit:
return fapi2::current_err;
}
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.C b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.C
index 2217411c0..27328334d 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.C
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.C
@@ -22,3 +22,128 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+
+///
+/// @file pmic_common_utils.C
+/// @brief Utility functions common for several PMIC procedures
+///
+// *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 <pmic_regs.H>
+#include <pmic_regs_fld.H>
+#include <lib/i2c/i2c_pmic.H>
+#include <lib/utils/pmic_consts.H>
+#include <lib/utils/pmic_common_utils.H>
+#include <generic/memory/lib/utils/poll.H>
+#include <generic/memory/lib/utils/c_str.H>
+
+namespace mss
+{
+namespace pmic
+{
+
+///
+/// @brief polls PMIC for PBULK PWR_GOOD status
+///
+/// @param[in] i_pmic_target PMIC target
+/// @return fapi2::ReturnCode success if good, error if polling fail or power not good
+///
+fapi2::ReturnCode poll_for_pbulk_good(
+ 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>;
+
+ // Using default poll parameters
+ mss::poll_parameters l_poll_params;
+
+ FAPI_ASSERT( mss::poll(i_pmic_target, l_poll_params, [&i_pmic_target]()->bool
+ {
+ fapi2::buffer<uint8_t> l_pbulk_status_buffer;
+
+ FAPI_TRY(mss::pmic::i2c::reg_read_reverse_buffer(i_pmic_target, REGS::R08, l_pbulk_status_buffer),
+ "pmic_enable: Could not read 0x%02hhX on %s ", REGS::R08, mss::c_str(i_pmic_target));
+
+ return l_pbulk_status_buffer.getBit<FIELDS::VIN_BULK_INPUT_PWR_GOOD_STATUS>() ==
+ mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>::PWR_GOOD;
+
+ fapi_try_exit:
+ // No ack, return false and continue polling
+ return false;
+ }),
+ fapi2::MSS_EXP_I2C_POLLING_TIMEOUT().
+ set_TARGET(i_pmic_target),
+ "I2C read from %s either did not ACK or VIN_BULK did not respond with PWR_GOOD status",
+ mss::c_str(i_pmic_target) );
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Unlocks PMIC vendor region
+///
+/// @param[in] i_pmic_target JEDEC-COMPLIANT PMIC to unlock
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+fapi2::ReturnCode unlock_vendor_region(const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target)
+{
+ using REGS = pmicRegs<mss::pmic::product::JEDEC_COMPLIANT>;
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+
+ // Unlock
+ const fapi2::buffer<uint8_t> l_password_low(CONSTS::VENDOR_PASSWORD_LOW);
+ const fapi2::buffer<uint8_t> l_password_high(CONSTS::VENDOR_PASSWORD_HIGH);
+ const fapi2::buffer<uint8_t> l_unlock_code(CONSTS::UNLOCK_VENDOR_REGION);
+
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R37_PASSWORD_LOWER_BYTE_0, l_password_low));
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R38_PASSWORD_UPPER_BYTE_1, l_password_high));
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R39_COMMAND_CODES, l_unlock_code));
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Locks PMIC vendor region
+///
+/// @param[in] i_pmic_target - JEDEC-COMPLIANT PMIC to lock
+/// @param[in] i_rc - return code from the end of the caller function (if applicable)
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff i_rc == SUCCESS && no errors in unlocking, else return current_err
+///
+fapi2::ReturnCode lock_vendor_region(const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::ReturnCode i_rc)
+{
+ using REGS = pmicRegs<mss::pmic::product::JEDEC_COMPLIANT>;
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+
+ // Lock vendor region, password registers are cleared automatically
+ const fapi2::buffer<uint8_t> l_lock_code(CONSTS::LOCK_VENDOR_REGION);
+ const fapi2::buffer<uint8_t> l_zero_code(0);
+
+ fapi2::ReturnCode l_lock_return_code = fapi2::FAPI2_RC_SUCCESS;
+
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, REGS::R39_COMMAND_CODES, l_lock_code));
+
+ return i_rc;
+
+fapi_try_exit:
+ // Since we could have 2 possible errors at the same time here, we are letting the caller's i_rc take precedence.
+ // So, if we find an error while locking, we will report it here. We will only "return" this error if the
+ // caller's error is success, as to not overwrite it.
+ FAPI_ERR("Error code 0x%0llx: Error while trying to lock vendor region", uint64_t(fapi2::current_err));
+ return ((i_rc == fapi2::FAPI2_RC_SUCCESS) ? fapi2::current_err : i_rc);
+}
+
+} // pmic
+} // mss
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.H b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.H
index 7ad24e99e..78cf87f6e 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.H
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_common_utils.H
@@ -22,3 +22,107 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+
+///
+/// @file pmic_common_utils.H
+/// @brief Utility functions common for several PMIC procedures
+///
+// *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
+
+#ifndef __PMIC_COMMON_UTILS_H__
+#define __PMIC_COMMON_UTILS_H__
+
+#include <fapi2.H>
+#include <pmic_regs.H>
+#include <pmic_regs_fld.H>
+#include <lib/utils/pmic_consts.H>
+#include <mss_pmic_attribute_getters.H>
+
+namespace mss
+{
+namespace pmic
+{
+
+// Attribute getter pointer for manufacturer/vendor ID
+typedef fapi2::ReturnCode (*mfg_id_attr_ptr)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, uint16_t& o_value);
+
+// Manufacturer / Vendor ID
+static constexpr mfg_id_attr_ptr get_mfg_id[] =
+{
+ mss::attr::get_pmic0_mfg_id,
+ mss::attr::get_pmic1_mfg_id
+};
+
+using REGS = pmicRegs<mss::pmic::product::JEDEC_COMPLIANT>;
+using FIELDS = pmicFields<mss::pmic::product::JEDEC_COMPLIANT>;
+using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;;
+
+// Arrays to easily index common parameters by rail
+static constexpr uint8_t const VOLT_SETTING_REGS[] =
+{
+ REGS::R21_SWA_VOLTAGE_SETTING,
+ REGS::R23_SWB_VOLTAGE_SETTING,
+ REGS::R25_SWC_VOLTAGE_SETTING,
+ REGS::R27_SWD_VOLTAGE_SETTING
+};
+
+static constexpr uint8_t const VOLT_RANGE_FLDS[] =
+{
+ FIELDS::SWA_VOLTAGE_RANGE,
+ FIELDS::SWB_VOLTAGE_RANGE,
+ FIELDS::SWC_VOLTAGE_RANGE,
+ FIELDS::SWD_VOLTAGE_RANGE
+};
+
+static constexpr float const VOLT_RANGE_MINS[][CONSTS::NUM_RANGES] =
+{
+ {CONSTS::SWABC_VOLT_RANGE0_MIN, CONSTS::SWABC_VOLT_RANGE1_MIN},
+ {CONSTS::SWABC_VOLT_RANGE0_MIN, CONSTS::SWABC_VOLT_RANGE1_MIN},
+ {CONSTS::SWABC_VOLT_RANGE0_MIN, CONSTS::SWABC_VOLT_RANGE1_MIN},
+ {CONSTS::SWD_VOLT_RANGE0_MIN, CONSTS::SWD_VOLT_RANGE1_MIN}
+};
+
+static constexpr float const VOLT_RANGE_MAXES[][CONSTS::NUM_RANGES] =
+{
+ {CONSTS::SWABC_VOLT_RANGE0_MAX, CONSTS::SWABC_VOLT_RANGE1_MAX},
+ {CONSTS::SWABC_VOLT_RANGE0_MAX, CONSTS::SWABC_VOLT_RANGE1_MAX},
+ {CONSTS::SWABC_VOLT_RANGE0_MAX, CONSTS::SWABC_VOLT_RANGE1_MAX},
+ {CONSTS::SWD_VOLT_RANGE0_MAX, CONSTS::SWD_VOLT_RANGE1_MAX}
+};
+
+///
+/// @brief polls PMIC for PBULK PWR_GOOD status
+///
+/// @param[in] i_pmic_target PMIC target
+/// @return fapi2::ReturnCode success if good, error if polling fail or power not good
+///
+fapi2::ReturnCode poll_for_pbulk_good(
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target);
+
+///
+/// @brief Unlocks PMIC vendor region
+///
+/// @param[in] i_pmic_target JEDEC-COMPLIANT PMIC to unlock
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+fapi2::ReturnCode unlock_vendor_region(const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target);
+
+///
+/// @brief Locks PMIC vendor region
+///
+/// @param[in] i_pmic_target - JEDEC-COMPLIANT PMIC to lock
+/// @param[in] i_rc - return code from the end of the caller function (if applicable)
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff i_rc == SUCCESS && no errors in unlocking, else return current_err
+///
+fapi2::ReturnCode lock_vendor_region(const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::ReturnCode i_rc = fapi2::FAPI2_RC_SUCCESS);
+
+} // pmic
+} // mss
+
+#endif
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_consts.H b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_consts.H
index c2c434a2c..3d5a45b5c 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_consts.H
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_consts.H
@@ -43,6 +43,58 @@ namespace mss
namespace pmic
{
+///
+/// @brief Module height from SPD (1U-4U)
+/// @note these values are taken directly from the SPD.
+///
+enum module_height
+{
+ HEIGHT_1U = 0b000,
+ HEIGHT_2U = 0b001,
+ HEIGHT_4U = 0b100,
+};
+
+///
+/// @brief IDs for PMIC0 or PMIC1
+///
+enum id
+{
+ PMIC0 = 0,
+ PMIC1 = 1,
+ PMIC2 = 2,
+ PMIC3 = 3,
+ UNKNOWN_ID = 4, // default constant for wrapper procedure
+};
+
+///
+/// @brief Constants for pmic product types
+///
+enum product
+{
+ JEDEC_COMPLIANT, // Only JEDEC exists
+};
+
+///
+/// @brief constants for pmic vendor
+///
+enum vendor
+{
+ UNKNOWN_VENDOR,
+ TI = 0x9780,
+ IDT = 0xB380,
+};
+
+///
+/// @brief Constants for pmic rail
+///
+enum rail
+{
+ SWA = 0,
+ SWB = 1,
+ SWC = 2,
+ SWD = 3,
+};
+
enum attr_eff_engine_fields
{
// Template recursive base case
@@ -186,6 +238,106 @@ enum ffdc_codes
SET_DRAM_MODULE_HEIGHT = 0x1086,
};
+///
+/// @brief constants for PMIC procedures
+///
+/// @tparam P product ID (jedec, etc.)
+///
+template<mss::pmic::product P>
+struct consts;
+
+///
+/// @brief constants for JEDEC_COMPLIANT PMICS
+///
+template<>
+struct consts<mss::pmic::product::JEDEC_COMPLIANT>
+{
+ static constexpr uint8_t RANGE_0 = 0;
+ static constexpr uint8_t RANGE_1 = 1;
+ static constexpr uint8_t NUM_UNIQUE_PMICS = 2; // PMIC0/2, PMIC1/3
+ static constexpr uint8_t NUMBER_OF_RAILS = 4;
+
+ static constexpr uint8_t VENDOR_PASSWORD_LOW = 0x73;
+ static constexpr uint8_t VENDOR_PASSWORD_HIGH = 0x94;
+ static constexpr uint8_t UNLOCK_VENDOR_REGION = 0x40;
+ static constexpr uint8_t LOCK_VENDOR_REGION = 0x00;
+
+ static constexpr uint8_t PROGRAMMABLE_MODE = 0x01;
+ static constexpr uint8_t SECURE_MODE = 0x00;
+
+ static constexpr uint8_t SINGLE_PHASE = 0x0;
+ static constexpr uint8_t DUAL_PHASE = 0x1;
+
+ static constexpr uint8_t PWR_GOOD = 0x0;
+ static constexpr uint8_t PWR_NOT_GOOD = 0x1;
+
+ // Sequencing
+ static constexpr uint8_t DELAY_LIMIT = 0b1000;
+
+ // Despite the SPD max of 1000, the PMIC can only really support this value
+ static constexpr uint8_t ORDER_LIMIT = 0b0101;
+
+ // Offset voltage from spd (+/-)
+ static constexpr uint8_t OFFSET_PLUS = 0;
+ static constexpr uint8_t OFFSET_MINUS = 1;
+
+ // Shift left 1 to match buffer position
+ static constexpr uint8_t SHIFT_VOLTAGE_FOR_REG = 1;
+
+ static constexpr uint8_t NUM_RANGES = 2; // RANGE0 and RANGE1
+ static constexpr uint8_t MAX_VOLT_BITMAP = 0b01111111;
+ static constexpr uint8_t MAX_DELAY_BITMAP = 0b00000111;
+ static constexpr uint8_t CONVERT_RANGE1_TO_RANGE0 = 40;
+
+ static constexpr float SWABC_VOLT_RANGE0_MIN = 0.8;
+ static constexpr float SWABC_VOLT_RANGE0_MAX = 1.435;
+ static constexpr float SWABC_VOLT_RANGE1_MIN = 0.6;
+ static constexpr float SWABC_VOLT_RANGE1_MAX = 1.235;
+
+ static constexpr float SWD_VOLT_RANGE0_MIN = 1.5;
+ static constexpr float SWD_VOLT_RANGE0_MAX = 2.135;
+ static constexpr float SWD_VOLT_RANGE1_MIN = 2.2;
+ static constexpr float SWD_VOLT_RANGE1_MAX = 2.835;
+
+ // the overarching JEDEC spec says 5. Sticking with 5 until we hear otherwise.
+ static constexpr float VOLT_STEP = 0.005; // 5 mV
+};
+
+///
+/// @brief PMIC traits that change depending on DIMM module height
+///
+/// @tparam H module_height enum
+///
+template<mss::pmic::module_height H>
+struct pmic_traits;
+
+///
+/// @brief pmic traits for 1U dimms
+///
+template <>
+struct pmic_traits<mss::pmic::module_height::HEIGHT_1U>
+{
+ static constexpr uint8_t PMICS_PER_DIMM = 2;
+};
+
+///
+/// @brief pmic traits for 2U dimms
+///
+template <>
+struct pmic_traits<mss::pmic::module_height::HEIGHT_2U>
+{
+ static constexpr uint8_t PMICS_PER_DIMM = 2;
+};
+
+///
+/// @brief pmic traits for 4U dimms
+///
+template <>
+struct pmic_traits<mss::pmic::module_height::HEIGHT_4U>
+{
+ static constexpr uint8_t PMICS_PER_DIMM = 4;
+};
+
namespace i2c
{
@@ -197,7 +349,7 @@ enum sizes
DATA_LENGTH = 1,
};
-}// i2c
+} // i2c
} // pmic
} // mss
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
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.H b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.H
index d673a0073..ba93774bb 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.H
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/lib/utils/pmic_enable_utils.H
@@ -30,13 +30,22 @@
// *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 Level: 2
// *HWP Consumed by: FSP:HB
#ifndef __PMIC_ENABLE_UTILS_H__
#define __PMIC_ENABLE_UTILS_H__
#include <fapi2.H>
+#include <pmic_regs.H>
+#include <pmic_regs_fld.H>
+#include <lib/i2c/i2c_pmic.H>
+#include <lib/utils/pmic_common_utils.H>
+#include <lib/utils/pmic_consts.H>
+#include <generic/memory/lib/utils/c_str.H>
+#include <generic/memory/lib/utils/index.H>
+#include <mss_pmic_attribute_getters.H>
+#include <mss_pmic_attribute_setters.H>
namespace mss
{
@@ -52,7 +61,776 @@ enum enable_mode
MANUAL = 1, // Use voltage settings currently in the vendor region. (Changed via pmic_update, or factory defaults)
};
-}// pmic
-}// mss
+/// @brief pointer to PMIC attribute getters for DIMM target
+typedef fapi2::ReturnCode (*pmic_attr_ptr)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, uint8_t& o_value);
+
+// Pointers below allow for run-time attribute getter selection by PMIC ID (0,1)
+
+// Voltage Setting
+static constexpr pmic_attr_ptr get_swa_voltage_setting[] =
+{
+ mss::attr::get_pmic0_swa_voltage_setting,
+ mss::attr::get_pmic1_swa_voltage_setting
+};
+static constexpr pmic_attr_ptr get_swb_voltage_setting[] =
+{
+ mss::attr::get_pmic0_swb_voltage_setting,
+ mss::attr::get_pmic1_swb_voltage_setting
+};
+static constexpr pmic_attr_ptr get_swc_voltage_setting[] =
+{
+ mss::attr::get_pmic0_swc_voltage_setting,
+ mss::attr::get_pmic1_swc_voltage_setting
+};
+static constexpr pmic_attr_ptr get_swd_voltage_setting[] =
+{
+ mss::attr::get_pmic0_swd_voltage_setting,
+ mss::attr::get_pmic1_swd_voltage_setting
+};
+
+// Voltage Range Select
+static constexpr pmic_attr_ptr get_swa_voltage_range_select[] =
+{
+ mss::attr::get_pmic0_swa_voltage_range_select,
+ mss::attr::get_pmic1_swa_voltage_range_select
+};
+static constexpr pmic_attr_ptr get_swb_voltage_range_select[] =
+{
+ mss::attr::get_pmic0_swb_voltage_range_select,
+ mss::attr::get_pmic1_swb_voltage_range_select
+};
+static constexpr pmic_attr_ptr get_swc_voltage_range_select[] =
+{
+ mss::attr::get_pmic0_swc_voltage_range_select,
+ mss::attr::get_pmic1_swc_voltage_range_select
+};
+static constexpr pmic_attr_ptr get_swd_voltage_range_select[] =
+{
+ mss::attr::get_pmic0_swd_voltage_range_select,
+ mss::attr::get_pmic1_swd_voltage_range_select
+};
+
+// Voltage Offset
+static constexpr pmic_attr_ptr get_swa_voltage_offset[] =
+{
+ mss::attr::get_pmic0_swa_voltage_offset,
+ mss::attr::get_pmic1_swa_voltage_offset
+};
+static constexpr pmic_attr_ptr get_swb_voltage_offset[] =
+{
+ mss::attr::get_pmic0_swb_voltage_offset,
+ mss::attr::get_pmic1_swb_voltage_offset
+};
+static constexpr pmic_attr_ptr get_swc_voltage_offset[] =
+{
+ mss::attr::get_pmic0_swc_voltage_offset,
+ mss::attr::get_pmic1_swc_voltage_offset
+};
+static constexpr pmic_attr_ptr get_swd_voltage_offset[] =
+{
+ mss::attr::get_pmic0_swd_voltage_offset,
+ mss::attr::get_pmic1_swd_voltage_offset
+};
+
+// Voltage Offset Direction
+static constexpr pmic_attr_ptr get_swa_voltage_offset_direction[] =
+{
+ mss::attr::get_pmic0_swa_voltage_offset_direction,
+ mss::attr::get_pmic1_swa_voltage_offset_direction
+};
+static constexpr pmic_attr_ptr get_swb_voltage_offset_direction[] =
+{
+ mss::attr::get_pmic0_swb_voltage_offset_direction,
+ mss::attr::get_pmic1_swb_voltage_offset_direction
+};
+static constexpr pmic_attr_ptr get_swc_voltage_offset_direction[] =
+{
+ mss::attr::get_pmic0_swc_voltage_offset_direction,
+ mss::attr::get_pmic1_swc_voltage_offset_direction
+};
+static constexpr pmic_attr_ptr get_swd_voltage_offset_direction[] =
+{
+ mss::attr::get_pmic0_swd_voltage_offset_direction,
+ mss::attr::get_pmic1_swd_voltage_offset_direction
+};
+
+// Sequence Delay
+static constexpr pmic_attr_ptr get_swa_sequence_delay[] =
+{
+ mss::attr::get_pmic0_swa_sequence_delay,
+ mss::attr::get_pmic1_swa_sequence_delay
+};
+static constexpr pmic_attr_ptr get_swb_sequence_delay[] =
+{
+ mss::attr::get_pmic0_swb_sequence_delay,
+ mss::attr::get_pmic1_swb_sequence_delay
+};
+static constexpr pmic_attr_ptr get_swc_sequence_delay[] =
+{
+ mss::attr::get_pmic0_swc_sequence_delay,
+ mss::attr::get_pmic1_swc_sequence_delay
+};
+static constexpr pmic_attr_ptr get_swd_sequence_delay[] =
+{
+ mss::attr::get_pmic0_swd_sequence_delay,
+ mss::attr::get_pmic1_swd_sequence_delay
+};
+
+// Sequence Order
+static constexpr pmic_attr_ptr get_swa_sequence_order[] =
+{
+ mss::attr::get_pmic0_swa_sequence_order,
+ mss::attr::get_pmic1_swa_sequence_order
+};
+static constexpr pmic_attr_ptr get_swb_sequence_order[] =
+{
+ mss::attr::get_pmic0_swb_sequence_order,
+ mss::attr::get_pmic1_swb_sequence_order
+};
+static constexpr pmic_attr_ptr get_swc_sequence_order[] =
+{
+ mss::attr::get_pmic0_swc_sequence_order,
+ mss::attr::get_pmic1_swc_sequence_order
+};
+static constexpr pmic_attr_ptr get_swd_sequence_order[] =
+{
+ mss::attr::get_pmic0_swd_sequence_order,
+ mss::attr::get_pmic1_swd_sequence_order
+};
+
+// Phase Combination
+static constexpr pmic_attr_ptr get_phase_comb[] =
+{
+ mss::attr::get_pmic0_phase_comb,
+ mss::attr::get_pmic1_phase_comb
+};
+
+// These arrays allow us to dynamically choose the right attribute getter at runtime based on the rail and mss::pmic::id
+static const pmic_attr_ptr* l_get_volt_setting[] =
+{
+ mss::pmic::get_swa_voltage_setting,
+ mss::pmic::get_swb_voltage_setting,
+ mss::pmic::get_swc_voltage_setting,
+ mss::pmic::get_swd_voltage_setting
+};
+
+static const pmic_attr_ptr* l_get_volt_range_select[] =
+{
+ mss::pmic::get_swa_voltage_range_select,
+ mss::pmic::get_swb_voltage_range_select,
+ mss::pmic::get_swc_voltage_range_select,
+ mss::pmic::get_swd_voltage_range_select
+};
+
+static const pmic_attr_ptr* l_get_volt_offset[] =
+{
+ mss::pmic::get_swa_voltage_offset,
+ mss::pmic::get_swb_voltage_offset,
+ mss::pmic::get_swc_voltage_offset,
+ mss::pmic::get_swd_voltage_offset
+};
+
+static const pmic_attr_ptr* l_get_volt_offset_direction[] =
+{
+ mss::pmic::get_swa_voltage_offset_direction,
+ mss::pmic::get_swb_voltage_offset_direction,
+ mss::pmic::get_swc_voltage_offset_direction,
+ mss::pmic::get_swd_voltage_offset_direction
+};
+
+// For output traces
+static const std::vector<const char*> PMIC_RAIL_NAMES = {"SWA", "SWB", "SWC", "SWD"};
+
+// Attribute setter FP type
+typedef fapi2::ReturnCode (*pmic_attr_setter_ptr)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
+ uint8_t i_value);
+
+// Voltage Setting
+static constexpr pmic_attr_setter_ptr set_swa_voltage_setting[] =
+{
+ mss::attr::set_pmic0_swa_voltage_setting,
+ mss::attr::set_pmic1_swa_voltage_setting
+};
+static constexpr pmic_attr_setter_ptr set_swb_voltage_setting[] =
+{
+ mss::attr::set_pmic0_swb_voltage_setting,
+ mss::attr::set_pmic1_swb_voltage_setting
+};
+static constexpr pmic_attr_setter_ptr set_swc_voltage_setting[] =
+{
+ mss::attr::set_pmic0_swc_voltage_setting,
+ mss::attr::set_pmic1_swc_voltage_setting
+};
+static constexpr pmic_attr_setter_ptr set_swd_voltage_setting[] =
+{
+ mss::attr::set_pmic0_swd_voltage_setting,
+ mss::attr::set_pmic1_swd_voltage_setting
+};
+
+// Voltage Range Select
+static constexpr pmic_attr_setter_ptr set_swa_voltage_range_select[] =
+{
+ mss::attr::set_pmic0_swa_voltage_range_select,
+ mss::attr::set_pmic1_swa_voltage_range_select
+};
+static constexpr pmic_attr_setter_ptr set_swb_voltage_range_select[] =
+{
+ mss::attr::set_pmic0_swb_voltage_range_select,
+ mss::attr::set_pmic1_swb_voltage_range_select
+};
+static constexpr pmic_attr_setter_ptr set_swc_voltage_range_select[] =
+{
+ mss::attr::set_pmic0_swc_voltage_range_select,
+ mss::attr::set_pmic1_swc_voltage_range_select
+};
+static constexpr pmic_attr_setter_ptr set_swd_voltage_range_select[] =
+{
+ mss::attr::set_pmic0_swd_voltage_range_select,
+ mss::attr::set_pmic1_swd_voltage_range_select
+};
+
+// Voltage Offset
+static constexpr pmic_attr_setter_ptr set_swa_voltage_offset[] =
+{
+ mss::attr::set_pmic0_swa_voltage_offset,
+ mss::attr::set_pmic1_swa_voltage_offset
+};
+static constexpr pmic_attr_setter_ptr set_swb_voltage_offset[] =
+{
+ mss::attr::set_pmic0_swb_voltage_offset,
+ mss::attr::set_pmic1_swb_voltage_offset
+};
+static constexpr pmic_attr_setter_ptr set_swc_voltage_offset[] =
+{
+ mss::attr::set_pmic0_swc_voltage_offset,
+ mss::attr::set_pmic1_swc_voltage_offset
+};
+static constexpr pmic_attr_setter_ptr set_swd_voltage_offset[] =
+{
+ mss::attr::set_pmic0_swd_voltage_offset,
+ mss::attr::set_pmic1_swd_voltage_offset
+};
+
+// Voltage Offset Direction
+static constexpr pmic_attr_setter_ptr set_swa_voltage_offset_direction[] =
+{
+ mss::attr::set_pmic0_swa_voltage_offset_direction,
+ mss::attr::set_pmic1_swa_voltage_offset_direction
+};
+static constexpr pmic_attr_setter_ptr set_swb_voltage_offset_direction[] =
+{
+ mss::attr::set_pmic0_swb_voltage_offset_direction,
+ mss::attr::set_pmic1_swb_voltage_offset_direction
+};
+static constexpr pmic_attr_setter_ptr set_swc_voltage_offset_direction[] =
+{
+ mss::attr::set_pmic0_swc_voltage_offset_direction,
+ mss::attr::set_pmic1_swc_voltage_offset_direction
+};
+static constexpr pmic_attr_setter_ptr set_swd_voltage_offset_direction[] =
+{
+ mss::attr::set_pmic0_swd_voltage_offset_direction,
+ mss::attr::set_pmic1_swd_voltage_offset_direction
+};
+
+// Sequence Delay
+static constexpr pmic_attr_setter_ptr set_swa_sequence_delay[] =
+{
+ mss::attr::set_pmic0_swa_sequence_delay,
+ mss::attr::set_pmic1_swa_sequence_delay
+};
+static constexpr pmic_attr_setter_ptr set_swb_sequence_delay[] =
+{
+ mss::attr::set_pmic0_swb_sequence_delay,
+ mss::attr::set_pmic1_swb_sequence_delay
+};
+static constexpr pmic_attr_setter_ptr set_swc_sequence_delay[] =
+{
+ mss::attr::set_pmic0_swc_sequence_delay,
+ mss::attr::set_pmic1_swc_sequence_delay
+};
+static constexpr pmic_attr_setter_ptr set_swd_sequence_delay[] =
+{
+ mss::attr::set_pmic0_swd_sequence_delay,
+ mss::attr::set_pmic1_swd_sequence_delay
+};
+
+// Sequence Order
+static constexpr pmic_attr_setter_ptr set_swa_sequence_order[] =
+{
+ mss::attr::set_pmic0_swa_sequence_order,
+ mss::attr::set_pmic1_swa_sequence_order
+};
+static constexpr pmic_attr_setter_ptr set_swb_sequence_order[] =
+{
+ mss::attr::set_pmic0_swb_sequence_order,
+ mss::attr::set_pmic1_swb_sequence_order
+};
+static constexpr pmic_attr_setter_ptr set_swc_sequence_order[] =
+{
+ mss::attr::set_pmic0_swc_sequence_order,
+ mss::attr::set_pmic1_swc_sequence_order
+};
+static constexpr pmic_attr_setter_ptr set_swd_sequence_order[] =
+{
+ mss::attr::set_pmic0_swd_sequence_order,
+ mss::attr::set_pmic1_swd_sequence_order
+};
+
+// Phase Combination
+static constexpr pmic_attr_setter_ptr set_phase_comb[] =
+{
+ mss::attr::set_pmic0_phase_comb,
+ mss::attr::set_pmic1_phase_comb
+};
+
+// TK - these will be needed in the next commit (pmic_bias).
+
+// These arrays allow us to dynamically choose the right attribute setter at runtime based on the rail and mss::pmic::id
+// static const pmic_attr_setter_ptr* l_set_volt_setting[] =
+// {
+// mss::pmic::set_swa_voltage_setting,
+// mss::pmic::set_swb_voltage_setting,
+// mss::pmic::set_swc_voltage_setting,
+// mss::pmic::set_swd_voltage_setting
+// };
+
+// static const pmic_attr_setter_ptr* l_set_volt_range_select[] =
+// {
+// mss::pmic::set_swa_voltage_range_select,
+// mss::pmic::set_swb_voltage_range_select,
+// mss::pmic::set_swc_voltage_range_select,
+// mss::pmic::set_swd_voltage_range_select
+// };
+
+// static const pmic_attr_setter_ptr* l_set_volt_offset[] =
+// {
+// mss::pmic::set_swa_voltage_offset,
+// mss::pmic::set_swb_voltage_offset,
+// mss::pmic::set_swc_voltage_offset,
+// mss::pmic::set_swd_voltage_offset
+// };
+
+// static const pmic_attr_setter_ptr* l_set_volt_offset_direction[] =
+// {
+// mss::pmic::set_swa_voltage_offset_direction,
+// mss::pmic::set_swb_voltage_offset_direction,
+// mss::pmic::set_swc_voltage_offset_direction,
+// mss::pmic::set_swd_voltage_offset_direction
+// };
+
+//-----------------------------------
+// SPD Biasing functions
+//-----------------------------------
+
+///
+/// @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);
+
+///
+/// @brief bias PMIC0 with spd settings for phase combination (SWA, SWB or SWA+SWB)
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_port - port target of PMIC
+/// @param[in] i_dimm_index - DIMM index for PMIC (0,1)
+/// @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,
+ const mss::pmic::id i_id);
+
+///
+/// @brief bias with SPD settings for voltage ranges
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_port port target of PMIC
+/// @param[in] i_dimm_index DIMM index for PMIC (0,1)
+/// @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,
+ const mss::pmic::id i_id);
+
+///
+/// @brief bias with SPD settings for startup sequence
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_port port target of PMIC
+/// @param[in] i_dimm_index DIMM index for PMIC (0,1)
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error
+///
+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,
+ const mss::pmic::id i_id);
+
+///
+/// @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);
+
+//-----------------------------------
+// Templated SPD Biasing functions
+//-----------------------------------
+
+///
+/// @brief bias with spd settings for voltages
+///
+/// @tparam V mss::pmic::vendor (TI/IDT)
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target of PMIC
+/// @param[in] i_dimm_index - DIMM index for PMIC (0,1)
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff no error
+///
+template <mss::pmic::vendor V>
+fapi2::ReturnCode bias_with_spd_voltages(
+ 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);
+
+///
+/// @brief Calcuate target voltage for PMIC
+///
+/// @param[in] i_dimm_target DIMM target of PMIC (holds the attributes)
+/// @param[in] i_id ID of pmic (0,1)
+/// @param[in] i_rail RAIL to calculate voltage for
+/// @param[out] o_volt_buffer output buffer
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+inline fapi2::ReturnCode calculate_voltage_write_buffer(
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target,
+ const mss::pmic::id i_id,
+ const uint8_t i_rail,
+ fapi2::buffer<uint8_t>& o_volt_buffer)
+{
+ uint8_t l_volt = 0;
+ uint8_t l_volt_offset = 0;
+ uint8_t l_volt_offset_direction = 0;
+
+ // Get the attributes corresponding to the rail and PMIC indices
+ FAPI_TRY(l_get_volt_setting[i_rail][i_id](i_dimm_target, l_volt));
+ FAPI_TRY(l_get_volt_offset[i_rail][i_id](i_dimm_target, l_volt_offset));
+ FAPI_TRY(l_get_volt_offset_direction[i_rail][i_id](i_dimm_target, l_volt_offset_direction));
+
+ o_volt_buffer = (l_volt_offset_direction == CONSTS::OFFSET_PLUS) ?
+ l_volt + l_volt_offset : l_volt - l_volt_offset;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Bias with spd voltages for IDT pmic
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target
+/// @param[in] i_id relative ID of PMIC (0/1)
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+template <>
+inline fapi2::ReturnCode bias_with_spd_voltages<mss::pmic::vendor::IDT>(
+ 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)
+{
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+
+ for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index)
+ {
+ fapi2::buffer<uint8_t> l_volt_buffer;
+ FAPI_TRY(calculate_voltage_write_buffer(i_dimm_target, i_id, l_rail_index, l_volt_buffer));
+
+ // Since we have unsigned integers, this should check both underflow and overflow
+ FAPI_ASSERT(l_volt_buffer <= CONSTS::MAX_VOLT_BITMAP,
+ fapi2::PMIC_VOLTAGE_OUT_OF_RANGE()
+ .set_TARGET(i_pmic_target)
+ .set_VOLTAGE_BITMAP(l_volt_buffer)
+ .set_RAIL(mss::pmic::VOLT_SETTING_REGS[l_rail_index]),
+ "Voltage out of range as determined by SPD voltage +/- offset for %s of %s",
+ PMIC_RAIL_NAMES[l_rail_index], mss::c_str(i_pmic_target) );
+
+ l_volt_buffer = l_volt_buffer << CONSTS::SHIFT_VOLTAGE_FOR_REG;
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, mss::pmic::VOLT_SETTING_REGS[l_rail_index], l_volt_buffer),
+ "Error writing address 0x%02hhX of PMIC %s", mss::pmic::VOLT_SETTING_REGS[l_rail_index], mss::c_str(i_pmic_target));
+
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Bias with spd voltages for TI pmic
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target
+/// @param[in] i_id relative ID of PMIC (0/1)
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+template <>
+inline fapi2::ReturnCode bias_with_spd_voltages<mss::pmic::vendor::TI>(
+ 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)
+{
+ using REGS = pmicRegs<mss::pmic::product::JEDEC_COMPLIANT>;
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+
+ for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index)
+ {
+ fapi2::buffer<uint8_t> l_volt_buffer;
+ FAPI_TRY(calculate_voltage_write_buffer(i_dimm_target, i_id, l_rail_index, l_volt_buffer));
+
+ bool l_overflow = false;
+
+ uint8_t l_volt_range_select = 0;
+ FAPI_TRY(l_get_volt_range_select[l_rail_index][i_id](i_dimm_target, l_volt_range_select));
+
+ // SWD supports a RANGE 1, but NOT SWA-C
+ if (l_rail_index == mss::pmic::rail::SWD)
+ {
+ // Can set range and voltage directly
+ fapi2::buffer<uint8_t> l_volt_range_buffer;
+
+ // 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));
+
+ l_volt_range_buffer.writeBit<FIELDS::SWD_VOLTAGE_RANGE>(l_volt_range_select);
+
+ // Write to PMIC
+ FAPI_TRY(mss::pmic::i2c::reg_write_reverse_buffer(i_pmic_target, REGS::R2B, l_volt_range_buffer));
+ }
+ else
+ {
+ // Check if the range is range 1, in which case we will need to convert to range 0 (thanks TI)
+ if (l_volt_range_select == CONSTS::RANGE_1)
+ {
+ // Convert from RANGE1 -> RANGE0
+
+ // Since both ranges are 5mV (at least they're supposed to be)
+ // we can just subtract the difference between range 1 and 0
+ // which is 600mV -> 800mV
+ // 200mV / 5 = 40
+ uint8_t l_old_voltage = uint8_t(l_volt_buffer);
+ l_volt_buffer = l_volt_buffer - CONSTS::CONVERT_RANGE1_TO_RANGE0;
+
+ // Check for overflow (the old voltage should be larger unless we rolled over)
+ if (l_old_voltage < l_volt_buffer)
+ {
+ // Not using an extra xml error for this as overflow implies PMIC_VOLTAGE_OUT_OF_RANGE anyway.
+ FAPI_ERR("Overflow ocurred when converting SPD Range 1 voltage to TI Range 0");
+ l_overflow = true;
+ }
+ }
+ }
+
+ // Check for overflow due to range conversion (SWA-C), but also due to overflow due to offset attributes
+ FAPI_ASSERT( (!l_overflow) && (l_volt_buffer <= CONSTS::MAX_VOLT_BITMAP),
+ fapi2::PMIC_VOLTAGE_OUT_OF_RANGE()
+ .set_TARGET(i_pmic_target)
+ .set_VOLTAGE_BITMAP(l_volt_buffer)
+ .set_RAIL(mss::pmic::VOLT_SETTING_REGS[l_rail_index]),
+ "Voltage out of range as determined by SPD voltage +/- offset for %s of %s",
+ PMIC_RAIL_NAMES[l_rail_index], mss::c_str(i_pmic_target) );
+
+ l_volt_buffer = l_volt_buffer << CONSTS::SHIFT_VOLTAGE_FOR_REG;
+ FAPI_TRY(mss::pmic::i2c::reg_write(i_pmic_target, mss::pmic::VOLT_SETTING_REGS[l_rail_index], l_volt_buffer),
+ "Error writing address 0x%02hhX of PMIC %s", mss::pmic::VOLT_SETTING_REGS[l_rail_index], mss::c_str(i_pmic_target));
+
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Bias PMIC from SPD settings per vendor
+///
+/// @tparam V mss::pmic::vendor (IDT/TI)
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target associated with PMIC
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+template <mss::pmic::vendor V>
+fapi2::ReturnCode bias_with_spd_settings(
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target);
+
+///
+/// @brief Bias IDT PMIC from SPD settings
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+template<>
+inline fapi2::ReturnCode bias_with_spd_settings<mss::pmic::vendor::IDT>(
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target)
+{
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+ // Unlock Vendor Region
+ FAPI_TRY(mss::pmic::unlock_vendor_region(i_pmic_target),
+ "Error unlocking vendor region on PMIC %s", mss::c_str(i_pmic_target));
+
+ {
+ // PMIC position/ID of the OCMB target. There could be 4 total, but we care about whether its PMIC0(2) or PMIC1(3)
+ const mss::pmic::id l_relative_pmic_id = static_cast<mss::pmic::id>(
+ mss::index(i_pmic_target) % CONSTS::NUM_UNIQUE_PMICS);
+
+ // Phase combination
+ FAPI_TRY(mss::pmic::bias_with_spd_phase_comb(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+
+ // Voltage ranges
+ FAPI_TRY(mss::pmic::bias_with_spd_volt_ranges(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+
+ // Voltages
+ FAPI_TRY(mss::pmic::bias_with_spd_voltages<mss::pmic::IDT>(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+
+ // Startup sequence
+ FAPI_TRY(mss::pmic::bias_with_spd_startup_seq(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+ }
+
+fapi_try_exit:
+ // Try to lock vendor region even in the case of an error in this function
+ return mss::pmic::lock_vendor_region(i_pmic_target, fapi2::current_err);
+}
+
+///
+/// @brief Bias TI PMIC from SPD settings
+///
+/// @param[in] i_pmic_target PMIC target
+/// @param[in] i_dimm_target DIMM target
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success
+///
+template<>
+inline fapi2::ReturnCode bias_with_spd_settings<mss::pmic::vendor::TI>(
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target)
+{
+ using CONSTS = mss::pmic::consts<mss::pmic::product::JEDEC_COMPLIANT>;
+ // Unlock Vendor Region
+ FAPI_TRY(mss::pmic::unlock_vendor_region(i_pmic_target),
+ "Error unlocking vendor region on PMIC %s", mss::c_str(i_pmic_target));
+
+ {
+ // PMIC position/ID of the OCMB target. There could be 4 total, but we care about whether its PMIC0(2) or PMIC1(3)
+ const mss::pmic::id l_relative_pmic_id = static_cast<mss::pmic::id>(
+ mss::index(i_pmic_target) % CONSTS::NUM_UNIQUE_PMICS);
+ // Phase combination
+ FAPI_TRY(mss::pmic::bias_with_spd_phase_comb(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+
+ // Voltages
+ // TI pmic only has range 0 for SWA-C. We need to convert anything SPD that says range 1 --> range 0
+ FAPI_TRY(mss::pmic::bias_with_spd_voltages<mss::pmic::TI>(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+
+ // Startup sequence
+ FAPI_TRY(mss::pmic::bias_with_spd_startup_seq(i_pmic_target, i_dimm_target, l_relative_pmic_id));
+ }
+
+fapi_try_exit:
+ // Try to lock vendor region even in the case of an error in this function
+ return mss::pmic::lock_vendor_region(i_pmic_target, fapi2::current_err);
+}
+
+//------------------- ENABLE FUNCTIONS-----------------//
+
+///
+/// @brief template function for the chip-specific enable functions
+///
+/// @tparam H module_height
+/// @param[in] i_pmic_target - the pmic target
+/// @param[in] i_dimm_target - the dimm target that the PMIC resides on
+/// @param[in] i_vendor_id - the vendor ID of the PMIC to bias
+/// @param[in] i_mode enable mode operation
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS if successful
+///
+template <mss::pmic::module_height H>
+fapi2::ReturnCode enable_chip(const fapi2::Target<fapi2::TargetType::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target,
+ const uint16_t i_vendor_id,
+ const mss::pmic::enable_mode i_mode);
+
+///
+/// @brief enable procedure for IDT PMIC and 1U or 2U DIMM
+///
+/// @param[in] i_pmic_target - the pmic_target
+/// @param[in] i_dimm_target - the dimm target that the PMIC resides on
+/// @param[in] i_vendor_id - the vendor ID of the PMIC to bias
+/// @param[in] i_mode enable mode operation
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS if successful
+///
+template <>
+inline fapi2::ReturnCode enable_chip<mss::pmic::module_height::HEIGHT_1U>(
+ const fapi2::Target<fapi2::TARGET_TYPE_PMIC>& i_pmic_target,
+ const fapi2::Target<fapi2::TargetType::TARGET_TYPE_DIMM>& i_dimm_target,
+ const uint16_t i_vendor_id,
+ const mss::pmic::enable_mode i_mode)
+{
+ if (i_mode == mss::pmic::enable_mode::SPD)
+ {
+ FAPI_INF("Setting PMIC settings from SPD");
+
+ // Make sure it is TI or IDT
+ FAPI_ASSERT((i_vendor_id == mss::pmic::vendor::IDT ||
+ i_vendor_id == mss::pmic::vendor::TI),
+ fapi2::PMIC_CHIP_NOT_RECOGNIZED()
+ .set_TARGET(i_pmic_target)
+ .set_VENDOR_ID(i_vendor_id),
+ "Unknown PMIC: %s with vendor ID 0x%04hhX",
+ mss::c_str(i_pmic_target),
+ uint8_t(i_vendor_id) );
+
+ if (i_vendor_id == mss::pmic::vendor::IDT)
+ {
+ FAPI_TRY(mss::pmic::bias_with_spd_settings<mss::pmic::vendor::IDT>(i_pmic_target, i_dimm_target),
+ "enable_chip<IDT, 1U/2U>: Error biasing PMIC %s with SPD settings",
+ mss::c_str(i_pmic_target));
+ }
+ else // assert done in pmic_enable.C that vendor is IDT or TI
+ {
+ FAPI_TRY(mss::pmic::bias_with_spd_settings<mss::pmic::vendor::TI>(i_pmic_target, i_dimm_target),
+ "enable_chip<TI, 1U/2U>: Error biasing PMIC %s with SPD settings",
+ mss::c_str(i_pmic_target));
+ }
+ }
+ else // manual mode
+ {
+ FAPI_INF("Using built-in PMIC settings (defaults or from pmic_update)");
+ }
+
+ // Execute VR Enable command
+ FAPI_TRY((mss::pmic::start_vr_enable(i_pmic_target)),
+ "enable_chip: Failed to start VR Enable for %s", mss::c_str(i_pmic_target));
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+}
+} // mss
#endif
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.C b/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.C
index c1403be9d..a10f772b6 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.C
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.C
@@ -30,17 +30,23 @@
// *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 Level: 2
// *HWP Consumed by: FSP:HB
#include <fapi2.H>
#include <pmic_enable.H>
+#include <generic/memory/lib/utils/find.H>
+#include <lib/utils/pmic_common_utils.H>
#include <lib/utils/pmic_enable_utils.H>
+#include <lib/utils/pmic_consts.H>
+#include <generic/memory/lib/utils/shared/mss_generic_consts.H>
+#include <generic/memory/lib/utils/c_str.H>
+#include <mss_generic_attribute_getters.H>
extern "C"
{
///
- /// @brief enable function for pmic module
+ /// @brief Enable function for pmic module. Calls appropriate enable func with matching DIMM target
/// @param[in] i_target ocmb target
/// @param[in] i_mode enable mode operation
/// @return FAPI2_RC_SUCCESS iff ok
@@ -48,7 +54,94 @@ extern "C"
fapi2::ReturnCode pmic_enable(const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_ocmb_target,
const mss::pmic::enable_mode i_mode)
{
- // TK - L1 implementation, function not filled in yet
+ auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(i_ocmb_target);
+ auto l_pmics = mss::find_targets<fapi2::TARGET_TYPE_PMIC>(i_ocmb_target);
+
+ // Sort by index (low to high) since find_targets may not return the correct order
+ std::sort(l_dimms.begin(), l_dimms.end(),
+ [] (const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& l_first_dimm,
+ const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& l_second_dimm) -> bool
+ {
+ return mss::index(l_first_dimm) < mss::index(l_second_dimm);
+ });
+
+ std::sort(l_pmics.begin(), l_pmics.end(),
+ [] (const fapi2::Target<fapi2::TARGET_TYPE_PMIC>& l_first_pmic,
+ const fapi2::Target<fapi2::TARGET_TYPE_PMIC>& l_second_pmic) -> bool
+ {
+ return mss::index(l_first_pmic) < mss::index(l_second_pmic);
+ });
+
+ // Start at PMIC0. If there was ever a weird case where there is a 4U dimm
+ // on the same OCMB as a 2U dimm (is this possible?),
+ // we would have 6 total PMICs. So, we need to keep
+ // track of where we left off for the last pmic we enabled
+ uint8_t l_pmic_index = 0;
+
+ // Not asserting vectors non-empty because there could be OCMBs without DIMMs on them
+ for (const auto& l_dimm : l_dimms)
+ {
+ // Get module height for DIMM to determine the number of PMICs we should be using
+ uint8_t l_module_height = 0;
+ FAPI_TRY(mss::attr::get_dram_module_height(l_dimm, l_module_height));
+
+ if (l_module_height == mss::pmic::module_height::HEIGHT_1U ||
+ l_module_height == mss::pmic::module_height::HEIGHT_2U)
+ {
+ // 1U and 2U are the same sequence, use 1U traits
+ using PMIC_TRAITS = mss::pmic::pmic_traits<mss::pmic::module_height::HEIGHT_1U>;
+
+ uint16_t l_vendor_id = 0;
+
+ // PMIC0 and PMIC1 of each DIMM
+ for (uint8_t l_current_pmic = 0; l_current_pmic < PMIC_TRAITS::PMICS_PER_DIMM; ++l_current_pmic)
+ {
+ const auto l_current_pmic_target = l_pmics[l_pmic_index + l_current_pmic];
+ // Get vendor ID
+ FAPI_TRY(mss::pmic::get_mfg_id[l_current_pmic](l_dimm, l_vendor_id));
+
+ // Poll to make sure PBULK reports good, then we can enable the chip and write/read registers
+ FAPI_TRY(mss::pmic::poll_for_pbulk_good(l_current_pmic_target),
+ "pmic_enable: poll for pbulk good either failed, or returned not good status on PMIC %s",
+ mss::c_str(l_current_pmic_target));
+
+ // Call the enable procedure
+ FAPI_TRY((mss::pmic::enable_chip
+ <mss::pmic::module_height::HEIGHT_1U>
+ (l_current_pmic_target, l_dimm, l_vendor_id, i_mode)),
+ "pmic_enable: Error enabling PMIC %s", mss::c_str(l_current_pmic_target));
+
+ }
+
+ // Increment by the number of PMICs that were enabled and move on to the next dimm
+ l_pmic_index += PMIC_TRAITS::PMICS_PER_DIMM;
+ }
+ else // 4U DIMM:
+ {
+ // Asserting out here as if we see a 4U at this point we shouldn't be able to proceed
+ // Ugly assert false, but we need the above else later so we will use this for now
+ FAPI_ASSERT(false,
+ fapi2::PMIC_DIMM_SPD_4U()
+ .set_TARGET(l_dimm),
+ "DIMM %s module height attribute identified as 4U. Not supported yet.",
+ mss::c_str(l_dimm));
+
+ // The enable algorithm will be:
+ // Load SPD for PMIC0 and PMIC1
+ // Broadcast enable both together
+
+ // Load SPD for PMIC2 and PMIC3 (which should be the same data as for PMIC0 and PMIC1)
+ // Broadcast and enable both together
+
+ // using PMIC_TRAITS = mss::pmic::pmic_traits<mss::pmic::module_height::HEIGHT_4U>;
+ // l_pmic_index += PMIC_TRAITS::PMICS_PER_DIMM;
+ }
+ }
+
return fapi2::FAPI2_RC_SUCCESS;
+
+ fapi_try_exit:
+ return fapi2::current_err;
}
-}
+
+} // extern C
diff --git a/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.H b/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.H
index 9175bd99c..62a0fe12f 100644
--- a/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.H
+++ b/src/import/chips/ocmb/common/procedures/hwp/pmic/pmic_enable.H
@@ -30,7 +30,7 @@
// *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 Level: 2
// *HWP Consumed by: FSP:HB
#ifndef __PMIC_ENABLE_H__
diff --git a/src/import/chips/ocmb/common/procedures/xml/error_info/pmic_errors.xml b/src/import/chips/ocmb/common/procedures/xml/error_info/pmic_errors.xml
index fea79e82c..6c2437728 100644
--- a/src/import/chips/ocmb/common/procedures/xml/error_info/pmic_errors.xml
+++ b/src/import/chips/ocmb/common/procedures/xml/error_info/pmic_errors.xml
@@ -44,4 +44,92 @@
</callout>
</hwpError>
+ <hwpError>
+ <rc>RC_PMIC_CHIP_NOT_RECOGNIZED</rc>
+ <description>
+ The PMIC identifier register contents did not match any known chip.
+ </description>
+ <ffdc>TARGET</ffdc>
+ <ffdc>VENDOR_ID</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>MEDIUM</priority>
+ </callout>
+ <callout>
+ <target>TARGET</target>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_PMIC_VOLTAGE_OUT_OF_RANGE</rc>
+ <description>
+ The voltage from the SPD and offset combination was out of range for the PMIC.
+ </description>
+ <ffdc>TARGET</ffdc>
+ <ffdc>VOLTAGE_BITMAP</ffdc>
+ <ffdc>RAIL</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>MEDIUM</priority>
+ </callout>
+ <callout>
+ <target>TARGET</target>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_PMIC_ORDER_OUT_OF_RANGE</rc>
+ <description>
+ The sequence order specified by the SPD was out of range for the PMIC (max 4)
+ </description>
+ <ffdc>TARGET</ffdc>
+ <ffdc>RAIL</ffdc>
+ <ffdc>ORDER</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>MEDIUM</priority>
+ </callout>
+ <callout>
+ <target>TARGET</target>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_PMIC_DELAY_OUT_OF_RANGE</rc>
+ <description>
+ The sequence delay specified by the SPD was out of range for the PMIC (max bitmap: 0b111)
+ </description>
+ <ffdc>TARGET</ffdc>
+ <ffdc>RAIL</ffdc>
+ <ffdc>DELAY</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>MEDIUM</priority>
+ </callout>
+ <callout>
+ <target>TARGET</target>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_PMIC_DIMM_SPD_4U</rc>
+ <description>
+ The module_height attribute SPD of this DIMM was read as 4U.
+ 4U is not supported yet for pmic_enable().
+ </description>
+ <ffdc>TARGET</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>MEDIUM</priority>
+ </callout>
+ <callout>
+ <target>TARGET</target>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
</hwpErrors>
diff --git a/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H b/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H
index 50b9063ff..90c361e94 100644
--- a/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H
+++ b/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H
@@ -180,6 +180,7 @@ enum generic_ffdc_codes
SET_DIMM_RANKS_CNFG = 0x1039,
DDIMM_RAWCARD_DECODE = 0x103a,
INIT_RANK_INFO = 0x103B,
+ BIAS_PMIC_FROM_SPD = 0x103C,
SET_DRAM_WIDTH = 0x1040,
SET_SI_VREF_DRAM_WR = 0x1041,
OpenPOWER on IntegriCloud