From 1dab92e705f6f8c893a09ff250eab1a762f7e88f Mon Sep 17 00:00:00 2001 From: Mark Pizzutillo Date: Mon, 15 Apr 2019 16:08:22 -0400 Subject: Add PMIC enable procedure code and UTs Change-Id: Iac5cd8016efa705be6512b842e0e793eb3d4c5fa Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/74639 Tested-by: FSP CI Jenkins Tested-by: Jenkins Server Tested-by: Hostboot CI Tested-by: HWSV CI Reviewed-by: Louis Stermole Reviewed-by: STEPHEN GLANCY Reviewed-by: Thi N. Tran Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/77132 Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Reviewed-by: Christian R. Geddes --- src/import/chips/ocmb/common/include/pmic_regs.H | 130 ++++ .../chips/ocmb/common/include/pmic_regs_fld.H | 53 ++ .../common/procedures/hwp/pmic/lib/i2c/i2c_pmic.H | 1 - .../hwp/pmic/lib/utils/pmic_common_utils.C | 125 ++++ .../hwp/pmic/lib/utils/pmic_common_utils.H | 104 +++ .../procedures/hwp/pmic/lib/utils/pmic_consts.H | 154 +++- .../hwp/pmic/lib/utils/pmic_enable_utils.C | 316 +++++++++ .../hwp/pmic/lib/utils/pmic_enable_utils.H | 784 ++++++++++++++++++++- .../ocmb/common/procedures/hwp/pmic/pmic_enable.C | 101 ++- .../ocmb/common/procedures/hwp/pmic/pmic_enable.H | 2 +- .../procedures/xml/error_info/pmic_errors.xml | 88 +++ .../memory/lib/utils/shared/mss_generic_consts.H | 1 + 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 +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#ifndef __PMIC_REGS__ +#define __PMIC_REGS__ + +#include + +/// +/// @brief Registers for PMIC devices +/// @class pmicRegs +/// @tparam P pmic_product +/// +template +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 +{ + 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 +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#ifndef __PMIC_REGS_FLD__ +#define __PMIC_REGS_FLD__ + +#include + +/// +/// @brief Register fields for PMIC devices +/// @class pmicFields +/// @tparam P pmic_product +/// +template +struct pmicFields; + +/// +/// @brief Fields for JEDEC_COMPLIANT PMICs +/// +template<> +struct pmicFields +{ + 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 +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#include +#include +#include +#include +#include +#include +#include +#include + +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& i_pmic_target) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs; + using FIELDS = pmicFields; + + // 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 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() == + mss::pmic::consts::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& i_pmic_target) +{ + using REGS = pmicRegs; + using CONSTS = mss::pmic::consts; + + // Unlock + const fapi2::buffer l_password_low(CONSTS::VENDOR_PASSWORD_LOW); + const fapi2::buffer l_password_high(CONSTS::VENDOR_PASSWORD_HIGH); + const fapi2::buffer 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& i_pmic_target, + const fapi2::ReturnCode i_rc) +{ + using REGS = pmicRegs; + using CONSTS = mss::pmic::consts; + + // Lock vendor region, password registers are cleared automatically + const fapi2::buffer l_lock_code(CONSTS::LOCK_VENDOR_REGION); + const fapi2::buffer 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 +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#ifndef __PMIC_COMMON_UTILS_H__ +#define __PMIC_COMMON_UTILS_H__ + +#include +#include +#include +#include +#include + +namespace mss +{ +namespace pmic +{ + +// Attribute getter pointer for manufacturer/vendor ID +typedef fapi2::ReturnCode (*mfg_id_attr_ptr)(const fapi2::Target& 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; +using FIELDS = pmicFields; +using CONSTS = mss::pmic::consts;; + +// 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& 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& 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& 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 +struct consts; + +/// +/// @brief constants for JEDEC_COMPLIANT PMICS +/// +template<> +struct consts +{ + 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 +struct pmic_traits; + +/// +/// @brief pmic traits for 1U dimms +/// +template <> +struct pmic_traits +{ + static constexpr uint8_t PMICS_PER_DIMM = 2; +}; + +/// +/// @brief pmic traits for 2U dimms +/// +template <> +struct pmic_traits +{ + static constexpr uint8_t PMICS_PER_DIMM = 2; +}; + +/// +/// @brief pmic traits for 4U dimms +/// +template <> +struct pmic_traits +{ + 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 +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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& i_pmic_target) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs; + using FIELDS = pmicFields; + + + fapi2::buffer l_programmable_mode_buffer; + fapi2::buffer 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( + mss::pmic::consts::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(); + + 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& i_pmic_target, + const fapi2::Target& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using FIELDS = pmicFields; + using REGS = pmicRegs; + + uint8_t l_phase_comb = 0; + fapi2::buffer 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(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& i_pmic_target, + const fapi2::Target& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using FIELDS = pmicFields; + using REGS = pmicRegs; + + 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 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(l_swa_range); + l_volt_range_buffer.writeBit(l_swb_range); + l_volt_range_buffer.writeBit(l_swc_range); + l_volt_range_buffer.writeBit(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& i_pmic_target, + const fapi2::Target& i_dimm_target, + const mss::pmic::id i_id) +{ + static constexpr auto J = mss::pmic::product::JEDEC_COMPLIANT; + using REGS = pmicRegs; + using CONSTS = mss::pmic::consts; + + // 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 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& 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; + using FIELDS = pmicFields; + using CONSTS = mss::pmic::consts; + + // 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 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 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 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(); + 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 // *HWP HWP Backup: Louis Stermole // *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 +#include +#include +#include +#include +#include +#include +#include +#include +#include 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& 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 PMIC_RAIL_NAMES = {"SWA", "SWB", "SWC", "SWD"}; + +// Attribute setter FP type +typedef fapi2::ReturnCode (*pmic_attr_setter_ptr)(const fapi2::Target& 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& 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& i_pmic_target, + const fapi2::Target& 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& i_pmic_target, + const fapi2::Target& 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& i_pmic_target, + const fapi2::Target& 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& 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 +fapi2::ReturnCode bias_with_spd_voltages( + const fapi2::Target& i_pmic_target, + const fapi2::Target& 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& i_dimm_target, + const mss::pmic::id i_id, + const uint8_t i_rail, + fapi2::buffer& 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( + const fapi2::Target& i_pmic_target, + const fapi2::Target& i_dimm_target, + const mss::pmic::id i_id) +{ + using CONSTS = mss::pmic::consts; + + for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index) + { + fapi2::buffer 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( + const fapi2::Target& i_pmic_target, + const fapi2::Target& i_dimm_target, + const mss::pmic::id i_id) +{ + using REGS = pmicRegs; + using CONSTS = mss::pmic::consts; + + for (uint8_t l_rail_index = mss::pmic::rail::SWA; l_rail_index <= mss::pmic::rail::SWD; ++l_rail_index) + { + fapi2::buffer 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 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(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 +fapi2::ReturnCode bias_with_spd_settings( + const fapi2::Target& i_pmic_target, + const fapi2::Target& 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( + const fapi2::Target& i_pmic_target, + const fapi2::Target& i_dimm_target) +{ + using CONSTS = mss::pmic::consts; + // 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::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(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( + const fapi2::Target& i_pmic_target, + const fapi2::Target& i_dimm_target) +{ + using CONSTS = mss::pmic::consts; + // 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::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(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 +fapi2::ReturnCode enable_chip(const fapi2::Target& i_pmic_target, + const fapi2::Target& 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( + const fapi2::Target& i_pmic_target, + const fapi2::Target& 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(i_pmic_target, i_dimm_target), + "enable_chip: 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(i_pmic_target, i_dimm_target), + "enable_chip: 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 // *HWP HWP Backup: Louis Stermole // *HWP Team: Memory -// *HWP Level: 1 +// *HWP Level: 2 // *HWP Consumed by: FSP:HB #include #include +#include +#include #include +#include +#include +#include +#include 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& i_ocmb_target, const mss::pmic::enable_mode i_mode) { - // TK - L1 implementation, function not filled in yet + auto l_dimms = mss::find_targets(i_ocmb_target); + auto l_pmics = mss::find_targets(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& l_first_dimm, + const fapi2::Target& 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& l_first_pmic, + const fapi2::Target& 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; + + 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 + + (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; + // 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 // *HWP HWP Backup: Louis Stermole // *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 @@ + + RC_PMIC_CHIP_NOT_RECOGNIZED + + The PMIC identifier register contents did not match any known chip. + + TARGET + VENDOR_ID + + CODE + MEDIUM + + + TARGET + HIGH + + + + + RC_PMIC_VOLTAGE_OUT_OF_RANGE + + The voltage from the SPD and offset combination was out of range for the PMIC. + + TARGET + VOLTAGE_BITMAP + RAIL + + CODE + MEDIUM + + + TARGET + HIGH + + + + + RC_PMIC_ORDER_OUT_OF_RANGE + + The sequence order specified by the SPD was out of range for the PMIC (max 4) + + TARGET + RAIL + ORDER + + CODE + MEDIUM + + + TARGET + HIGH + + + + + RC_PMIC_DELAY_OUT_OF_RANGE + + The sequence delay specified by the SPD was out of range for the PMIC (max bitmap: 0b111) + + TARGET + RAIL + DELAY + + CODE + MEDIUM + + + TARGET + HIGH + + + + + RC_PMIC_DIMM_SPD_4U + + The module_height attribute SPD of this DIMM was read as 4U. + 4U is not supported yet for pmic_enable(). + + TARGET + + CODE + MEDIUM + + + TARGET + HIGH + + + 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, -- cgit v1.2.1