summaryrefslogtreecommitdiffstats
path: root/src/import/generic
diff options
context:
space:
mode:
authorAlvin Wang <wangat@tw.ibm.com>2019-04-16 10:57:56 -0500
committerChristian R. Geddes <crgeddes@us.ibm.com>2019-04-30 11:53:20 -0500
commit4ce53f71e9fcf0c22fdb83af31b450db975df233 (patch)
tree0c85bb228692dd78965a54f78795af407ff7209d /src/import/generic
parent902e166a98fe9cce8dbf6dc69534f45499c0ab1d (diff)
downloadtalos-hostboot-4ce53f71e9fcf0c22fdb83af31b450db975df233.tar.gz
talos-hostboot-4ce53f71e9fcf0c22fdb83af31b450db975df233.zip
Move power_thermal lib to generic
Change-Id: I2851b7fa990d7e8c5a2d726b650b4e2fc11f3fe7 Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72525 Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Dev-Ready: STEPHEN GLANCY <sglancy@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: Jennifer A. Stofer <stofer@us.ibm.com> Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72845 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>
Diffstat (limited to 'src/import/generic')
-rw-r--r--src/import/generic/memory/lib/mss_generic_attribute_getters.H47
-rw-r--r--src/import/generic/memory/lib/utils/dimm/kind.H246
-rw-r--r--src/import/generic/memory/lib/utils/mss_math.H18
-rw-r--r--src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H584
-rw-r--r--src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H1204
-rw-r--r--src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H33
-rw-r--r--src/import/generic/memory/lib/utils/shared/mss_generic_consts.H8
-rw-r--r--src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml16
-rw-r--r--src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml132
-rw-r--r--src/import/generic/procedures/xml/error_info/generic_error.xml229
10 files changed, 2517 insertions, 0 deletions
diff --git a/src/import/generic/memory/lib/mss_generic_attribute_getters.H b/src/import/generic/memory/lib/mss_generic_attribute_getters.H
index c8fd30b63..eb791e7f3 100644
--- a/src/import/generic/memory/lib/mss_generic_attribute_getters.H
+++ b/src/import/generic/memory/lib/mss_generic_attribute_getters.H
@@ -2207,6 +2207,53 @@ fapi_try_exit:
}
///
+/// @brief ATTR_MEM_EFF_DRAM_MODULE_HEIGHT getter
+/// @param[in] const ref to the TARGET_TYPE_DIMM
+/// @param[out] uint8_t& reference to store the value
+/// @note Generated by gen_accessors.pl generate_mc_port_params
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+/// @note ARRAY[DIMM] DRAM Modlue Height Decodes SPD Byte 193
+///
+inline fapi2::ReturnCode get_dram_module_height(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
+ uint8_t& o_value)
+{
+ uint8_t l_value[2] = {};
+ const auto l_port = i_target.getParent<fapi2::TARGET_TYPE_MEM_PORT>();
+
+ FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MEM_EFF_DRAM_MODULE_HEIGHT, l_port, l_value) );
+ o_value = l_value[mss::index(i_target)];
+ return fapi2::current_err;
+
+fapi_try_exit:
+ FAPI_ERR("failed getting ATTR_MEM_EFF_DRAM_MODULE_HEIGHT: 0x%lx (target: %s)",
+ uint64_t(fapi2::current_err), mss::c_str(i_target));
+ return fapi2::current_err;
+}
+
+///
+/// @brief ATTR_MEM_EFF_DRAM_MODULE_HEIGHT getter
+/// @param[in] const ref to the TARGET_TYPE_MEM_PORT
+/// @param[out] uint8_t&[] array reference to store the value
+/// @note Generated by gen_accessors.pl generate_mc_port_params
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+/// @note ARRAY[DIMM] DRAM Modlue Height Decodes SPD Byte 193
+///
+inline fapi2::ReturnCode get_dram_module_height(const fapi2::Target<fapi2::TARGET_TYPE_MEM_PORT>& i_target,
+ uint8_t (&o_array)[2])
+{
+ uint8_t l_value[2] = {};
+
+ FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MEM_EFF_DRAM_MODULE_HEIGHT, i_target, l_value) );
+ memcpy(o_array, &l_value, 2);
+ return fapi2::current_err;
+
+fapi_try_exit:
+ FAPI_ERR("failed getting ATTR_MEM_EFF_DRAM_MODULE_HEIGHT: 0x%lx (target: %s)",
+ uint64_t(fapi2::current_err), mss::c_str(i_target));
+ return fapi2::current_err;
+}
+
+///
/// @brief ATTR_MEM_EFF_RCD_MFG_ID getter
/// @param[in] const ref to the TARGET_TYPE_DIMM
/// @param[out] uint16_t& reference to store the value
diff --git a/src/import/generic/memory/lib/utils/dimm/kind.H b/src/import/generic/memory/lib/utils/dimm/kind.H
index bcb29264d..aecec90b9 100644
--- a/src/import/generic/memory/lib/utils/dimm/kind.H
+++ b/src/import/generic/memory/lib/utils/dimm/kind.H
@@ -22,3 +22,249 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file dimm.H
+/// @brief Encapsulation for dimms of all types
+///
+// *HWP HWP Owner: Louis Stermole <stermole@us.ibm.com>
+// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
+// *HWP Team: Memory
+// *HWP Level: 3
+// *HWP Consumed by: HB:FSP
+
+#ifndef _MSS_DIMM_H_
+#define _MSS_DIMM_H_
+
+#include <fapi2.H>
+
+#include <generic/memory/lib/mss_generic_attribute_getters.H>
+#include <generic/memory/lib/utils/c_str.H>
+
+namespace mss
+{
+
+namespace dimm
+{
+
+///
+/// @class mss::dimm::kind
+/// @brief A class containing information about a dimm like ranks, density, configuration - what kind of dimm is it?
+///
+class kind
+{
+ public:
+
+ ///
+ /// @brief Generate a vector of DIMM kind from a vector of DIMM
+ /// @param[in] i_dimm a vector of DIMM
+ /// @return std::vector of dimm::kind relating to the DIMM passed in
+ ///
+ static std::vector<kind> vector(const std::vector<fapi2::Target<fapi2::TARGET_TYPE_DIMM>>& i_dimm)
+ {
+ std::vector<kind> l_kinds;
+
+ for (const auto& d : i_dimm)
+ {
+ l_kinds.push_back( kind(d) );
+ }
+
+ return l_kinds;
+ }
+
+ ///
+ /// @brief operator=() - assign kinds (needed to sort vectors of kinds)
+ /// @param[in] i_rhs the right hand side of the assignment statement
+ /// @return reference to this
+ ///
+ inline kind& operator=(const kind& i_rhs)
+ {
+ iv_target = i_rhs.iv_target;
+ iv_master_ranks = i_rhs.iv_master_ranks;
+ iv_total_ranks = i_rhs.iv_total_ranks;
+ iv_dram_density = i_rhs.iv_dram_density;
+ iv_dram_width = i_rhs.iv_dram_width;
+ iv_dram_generation = i_rhs.iv_dram_generation;
+ iv_dimm_type = i_rhs.iv_dimm_type;
+ iv_rows = i_rhs.iv_rows;
+ iv_size = i_rhs.iv_size;
+ iv_mfgid = i_rhs.iv_mfgid;
+ iv_stack_type = i_rhs.iv_stack_type;
+ iv_hybrid = i_rhs.iv_hybrid;
+ iv_hybrid_memory_type = i_rhs.iv_hybrid_memory_type;
+ iv_rcd_mfgid = i_rhs.iv_rcd_mfgid;
+ iv_module_height = i_rhs.iv_module_height;
+ return *this;
+ }
+
+ ///
+ /// @brief operator==() - are two kinds the same?
+ /// @param[in] i_rhs the right hand side of the comparison statement
+ /// @return bool true iff the two kind are of the same kind
+ /// @warning this does not compare the targets (iv_target,) just the values
+ /// Also does not compare the mfgid as that's not really part of the DIMM kind but is additional information
+ ///
+ inline bool operator==(const kind& i_rhs) const
+ {
+ return ((iv_master_ranks == i_rhs.iv_master_ranks) &&
+ (iv_total_ranks == i_rhs.iv_total_ranks) &&
+ (iv_dram_density == i_rhs.iv_dram_density) &&
+ (iv_dram_width == i_rhs.iv_dram_width) &&
+ (iv_dram_generation == i_rhs.iv_dram_generation) &&
+ (iv_dimm_type == i_rhs.iv_dimm_type) &&
+ (iv_rows == i_rhs.iv_rows) &&
+ (iv_size == i_rhs.iv_size) &&
+ (iv_stack_type == i_rhs.iv_stack_type) &&
+ (iv_hybrid == i_rhs.iv_hybrid) &&
+ (iv_hybrid_memory_type == i_rhs.iv_hybrid_memory_type) &&
+ (iv_rcd_mfgid == i_rhs.iv_rcd_mfgid) &&
+ (iv_module_height == i_rhs.iv_module_height));
+ }
+
+ ///
+ /// @brief operator!=() - are two kinds different?
+ /// @param[in] i_rhs the right hand side of the comparison statement
+ /// @return bool true iff the two kind are of different
+ /// @warning this does not compare the targets (iv_target,) just the values
+ ///
+ inline bool operator!=(const kind& i_rhs) const
+ {
+ return !(this->operator==(i_rhs));
+ }
+
+ ///
+ /// @brief Construct a dimm::kind data structure - information about the kind of DIMM this is
+ /// @param[in] i_target a DIMM target
+ ///
+ kind(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target):
+ iv_target(i_target)
+ {
+ FAPI_TRY( mss::attr::get_dram_gen(i_target, iv_dram_generation) );
+ FAPI_TRY( mss::attr::get_dimm_type(i_target, iv_dimm_type) );
+ FAPI_TRY( mss::attr::get_dram_density(i_target, iv_dram_density) );
+ FAPI_TRY( mss::attr::get_dram_width(i_target, iv_dram_width) );
+ FAPI_TRY( mss::attr::get_num_master_ranks_per_dimm(i_target, iv_master_ranks) );
+ FAPI_TRY( mss::attr::get_logical_ranks_per_dimm(i_target, iv_total_ranks) );
+ FAPI_TRY( mss::attr::get_dram_row_bits(i_target, iv_rows) );
+ FAPI_TRY( mss::attr::get_dimm_size(i_target, iv_size) );
+ FAPI_TRY( mss::attr::get_dram_mfg_id(i_target, iv_mfgid) );
+ FAPI_TRY( mss::attr::get_prim_stack_type( i_target, iv_stack_type) );
+ FAPI_TRY( mss::attr::get_hybrid( i_target, iv_hybrid ));
+ FAPI_TRY( mss::attr::get_hybrid_memory_type( i_target, iv_hybrid_memory_type ));
+ FAPI_TRY( mss::attr::get_rcd_mfg_id(i_target, iv_rcd_mfgid) );
+ FAPI_TRY( mss::attr::get_dram_module_height(i_target, iv_module_height) );
+ return;
+
+ fapi_try_exit:
+ // Not 100% sure what to do here ...
+ FAPI_ERR("error initializing DIMM structure: %s 0x%016lx", mss::c_str(i_target), uint64_t(fapi2::current_err));
+ fapi2::Assert(false);
+ }
+
+ ///
+ /// @brief Construct a DIMM kind used to identify this DIMM for tables.
+ /// @param[in] i_master_ranks number of master ranks on the DIMM
+ /// @param[in] i_total_ranks total number of ranks on the DIMM
+ /// @param[in] i_dram_density density of the DRAM
+ /// @param[in] i_dram_width width of the DRAM
+ /// @param[in] i_dram_generation DRAM generation
+ /// @param[in] i_dimm_type DIMM type (e.g. RDIMM)
+ /// @param[in] i_rows number of rows in the DRAM
+ /// @param[in] i_size the overal size of the DIMM in GB
+ /// @param[in] i_mfgid the dram manufacturer id of the dimm, defaulted to 0
+ /// @param[in] i_stack_type dram die type, single die package or 3DS
+ /// @param[in] i_hybrid, default not hybrid
+ /// @param[in] i_hybrid_memory_type, defult none
+ /// @param[in] i_rcd_mfgid dimm register and data buffer manufacturer id, default 0
+ /// @note can't be constexpr as fapi2::Target doesn't have a constexpr ctor.
+ ///
+ kind( const uint8_t i_master_ranks,
+ const uint8_t i_total_ranks,
+ const uint8_t i_dram_density,
+ const uint8_t i_dram_width,
+ const uint8_t i_dram_generation,
+ const uint8_t i_dimm_type,
+ const uint8_t i_rows,
+ const uint32_t i_size,
+ const uint16_t i_mfgid = 0,
+ const uint8_t i_stack_type = fapi2::ENUM_ATTR_EFF_PRIM_STACK_TYPE_SDP,
+ const uint8_t i_hybrid = fapi2::ENUM_ATTR_EFF_HYBRID_NOT_HYBRID,
+ const uint8_t i_hybrid_memory_type = fapi2::ENUM_ATTR_EFF_HYBRID_MEMORY_TYPE_NONE,
+ const uint16_t i_rcd_mfgid = 0,
+ const uint8_t i_module_height = 0
+ ):
+ iv_target(0),
+ iv_master_ranks(i_master_ranks),
+ iv_total_ranks(i_total_ranks),
+ iv_dram_density(i_dram_density),
+ iv_dram_width(i_dram_width),
+ iv_dram_generation(i_dram_generation),
+ iv_dimm_type(i_dimm_type),
+ iv_rows(i_rows),
+ // TK consider calculating size rather than requiring it be set.
+ iv_size(i_size),
+ iv_mfgid(i_mfgid),
+ iv_stack_type(i_stack_type),
+ iv_hybrid(i_hybrid),
+ iv_hybrid_memory_type(i_hybrid_memory_type),
+ iv_rcd_mfgid(i_rcd_mfgid),
+ iv_module_height(i_module_height)
+ {
+ // Bit of an idiot-check to be sure a hand-crafted dimm::kind make sense wrt slaves, masters, packages, etc.
+ // Both of these are checked in eff_config. If they are messed up, they should be caught there
+ if (iv_master_ranks > iv_total_ranks)
+ {
+ FAPI_ERR("Not enough total ranks? master: %d total: %d",
+ iv_master_ranks,
+ iv_total_ranks);
+ fapi2::Assert(false);
+ }
+
+ if ((iv_total_ranks % iv_master_ranks) != 0)
+ {
+ FAPI_ERR("total or master ranks seems incorrect. master: %d total: %d",
+ iv_master_ranks,
+ iv_total_ranks);
+ fapi2::Assert(false);
+ }
+ }
+
+ fapi2::Target<fapi2::TARGET_TYPE_DIMM> iv_target;
+ uint8_t iv_master_ranks;
+ uint8_t iv_total_ranks;
+ uint8_t iv_dram_density;
+ uint8_t iv_dram_width;
+ uint8_t iv_dram_generation;
+ uint8_t iv_dimm_type;
+ uint8_t iv_rows;
+ uint32_t iv_size;
+ uint16_t iv_mfgid;
+ uint8_t iv_stack_type;
+ uint8_t iv_hybrid;
+ uint8_t iv_hybrid_memory_type;
+ uint16_t iv_rcd_mfgid;
+ uint8_t iv_module_height;
+
+ ///
+ /// @brief equal_config
+ /// @param[in] i_input_compare the i_kind to compare against
+ /// @return bool true iff the two kind are of the same kind for xlate purposes
+ /// @warning this does not compare the targets (iv_target,), mfgid, prim_stack_type nor hybrid type
+ ///
+ inline bool equal_config(const kind& i_input_compare) const
+ {
+ return ((iv_master_ranks == i_input_compare.iv_master_ranks) &&
+ (iv_total_ranks == i_input_compare.iv_total_ranks) &&
+ (iv_dram_density == i_input_compare.iv_dram_density) &&
+ (iv_dram_width == i_input_compare.iv_dram_width) &&
+ (iv_dram_generation == i_input_compare.iv_dram_generation) &&
+ (iv_dimm_type == i_input_compare.iv_dimm_type) &&
+ (iv_rows == i_input_compare.iv_rows) &&
+ (iv_size == i_input_compare.iv_size));
+ }
+};
+
+}
+
+}
+#endif
diff --git a/src/import/generic/memory/lib/utils/mss_math.H b/src/import/generic/memory/lib/utils/mss_math.H
index ebb3da8e3..65fc0f898 100644
--- a/src/import/generic/memory/lib/utils/mss_math.H
+++ b/src/import/generic/memory/lib/utils/mss_math.H
@@ -108,6 +108,24 @@ fapi_try_exit:
}
+///
+/// @brief Determines if the double has decimal digits and adds 1 and rounds if true
+/// @param[in] i_val the double to be rounded up if trialing digits
+/// @return the input value rounded up to the next whole digit
+/// @note Called in p9_mss_bulk_pwr_throttles
+///
+inline uint32_t round_up(const double i_val)
+{
+ //convert to uint to truncate decimals and convert back to double for comparison
+ uint32_t temp = uint32_t (i_val);
+
+ //if not equal, lost something from truncating, so add 1
+ temp += (temp == i_val) ? 0 : 1;
+
+ //Truncate final value
+ return temp;
+}
+
}// mss
#endif
diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H b/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H
index 7e2c47406..1c6365d37 100644
--- a/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H
+++ b/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H
@@ -22,3 +22,587 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file gen_decoder.H
+/// @brief Decoder for ATTR_MSS_MRW_PWR_CURVE_SLOPE and _INTERCEPT and THERMAL_POWER_LIMIT
+///
+// *HWP HWP Owner: Louis Stermole <stermole@us.ibm.com>
+// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com>
+// *HWP Team: Memory
+// *HWP Level: 3
+// *HWP Consumed by: FSP:HB
+
+#ifndef _MSS_GEN_POWER_DECODER__
+#define _MSS_GEN_POWER_DECODER__
+
+#include <fapi2.H>
+#include <generic/memory/lib/utils/count_dimm.H>
+#include <generic/memory/lib/utils/power_thermal/gen_throttle_traits.H>
+#include <generic/memory/lib/utils/shared/mss_generic_consts.H>
+#include <generic/memory/lib/mss_generic_attribute_getters.H>
+
+namespace mss
+{
+
+namespace power_thermal
+{
+
+constexpr uint32_t ANY_SIZE = 0xFFFFFFFF;
+constexpr uint8_t ANY_TYPE = 0xFF;
+constexpr uint8_t ANY_GEN = 0xFF;
+constexpr uint8_t ANY_WIDTH = 0xFF;
+constexpr uint8_t ANY_DENSITY = 0xFF;
+constexpr uint8_t ANY_STACK_TYPE = 0xFF;
+constexpr uint16_t ANY_MFGID = 0xFFFF;
+constexpr uint8_t ANY_HEIGHT = 0xFF;
+constexpr uint8_t ANY_PORT = 0xFF;
+
+//Currently needs to be in sorted order for lookup to work
+static const std::vector< std::pair<uint32_t , uint8_t> > DIMM_SIZE_MAP =
+{
+ {4, 0b0000},
+ {8, 0b0001},
+ {16, 0b0010},
+ {32, 0b0011},
+ {64, 0b0100},
+ {128, 0b0101},
+ {256, 0b0110},
+ {512, 0b0111},
+ {ANY_SIZE, 0b1111}
+};
+
+
+
+static const std::vector< std::pair<uint8_t , uint8_t> > DRAM_GEN_MAP =
+{
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_EMPTY, 0b00},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_DDR3, 0b01},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_DDR4, 0b10},
+ {ANY_GEN, 0b11}
+};
+
+static const std::vector <std::pair<uint8_t, uint8_t> > DRAM_WIDTH_MAP =
+{
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X4, 0b000},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X8, 0b001},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X16, 0b010},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X32, 0b011},
+ {ANY_WIDTH, 0b111}
+};
+
+static const std::vector< std::pair<uint8_t , uint8_t> > DRAM_DENSITY_MAP =
+{
+ {4, 0b000},
+ {8, 0b001},
+ {16, 0b010},
+ {32, 0b011},
+ {64, 0b100},
+ {ANY_DENSITY, 0b111}
+};
+
+static const std::vector <std::pair<uint8_t, uint8_t> > DRAM_STACK_TYPE_MAP =
+{
+ {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_SDP, 0b00},
+ {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_DDP_QDP, 0b01},
+ {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_3DS, 0b10},
+ {ANY_STACK_TYPE, 0b11}
+};
+
+//Note, the first entries of the pairs need to be in sorted order!!
+static const std::vector <std::pair<uint16_t, uint8_t> > DRAM_MFGID_MAP =
+{
+ //Kingston
+ {0x0198, 0b011},
+ //A-data
+ {0x04CB, 0b101},
+ //Micron
+ {0x802C, 0b000},
+ //HYNIX
+ {0x80AD, 0b001},
+ //SAMSUNG
+ {0x80CE, 0b010},
+ //Innodisk
+ {0x86F1, 0b100},
+ // ANY
+ {ANY_MFGID, 0b111}
+};
+
+static const std::vector< std::pair<uint8_t , uint8_t> > DIMM_MODULE_HEIGHT_MAP =
+{
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_1U, 0b00},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_2U, 0b01},
+ {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_4U, 0b10},
+ {ANY_HEIGHT, 0b11}
+};
+
+static const std::vector < std::pair< uint8_t, uint8_t> > DIMMS_PORT_MAP =
+{
+ //Num dimms per MCA, only 1 or 2 possible options. 0 is no-op
+ {1, 0b00},
+ {2, 0b01},
+ {ANY_PORT, 0b11}
+};
+
+// Forward declaration
+template<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode generate_wildcard_mask(const uint32_t i_hash, uint32_t& o_mask);
+
+
+///
+///@brief a compare functor for the decoder::find_attr functions below
+/// @tparam MC mss::mc_type
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE>
+struct is_match
+{
+ ///
+ ///@brief functor constructor
+ ///@param[in] i_gen_key the class object's constructed hash for the installed dimm, to be compared with the attr array
+ ///
+ is_match(const uint32_t i_gen_key) : iv_gen_key(i_gen_key) {}
+ const fapi2::buffer<uint32_t> iv_gen_key;
+
+ ///
+ ///@brief Boolean compare used for find_if function
+ ///
+ bool operator()(const uint64_t i_hash)
+ {
+ // l_this_hash is the first half of the i_hash's bits
+ const uint32_t l_this_hash = i_hash >> 32;
+ uint32_t l_wildcard_mask = 0;
+
+ // Get wildcard mask. If the decoding fails(value to key), we should continue
+ generate_wildcard_mask<MC>(l_this_hash, l_wildcard_mask);
+
+ // Mask the wildcard bits
+ return ((l_this_hash | l_wildcard_mask) == (iv_gen_key | l_wildcard_mask));
+ }
+};
+
+///
+/// @brief Encode the attribute into a bit encoding
+/// @tparam S *ATTR*_SIZE enum used for fapi2::buffer position
+/// @tparam L *ATTR*_LEN enum used for fapi2::buffer position
+/// @tparam T integral type of key
+/// @tparam OT fapi2::buffer of some integral type
+/// @param[in] i_target the DIMM the encoding is for
+/// @param[in] i_attr the attribute key being used for the encoding
+/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value, sorted
+/// @param[out] o_buf the fapi2::buffer where the encoding is going into
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup
+///
+template<size_t S, size_t L, typename T, typename OT>
+inline fapi2::ReturnCode encode ( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
+ const T& i_attr,
+ const std::vector<std::pair<T, OT> >& i_map,
+ fapi2::buffer<uint32_t>& o_buf)
+{
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+ //used to hold result from vector pair lookup
+ OT l_encoding = 0;
+
+ //Failing out if we don't find an encoding. All suported types should be encoded above
+ FAPI_ASSERT( mss::find_value_from_key (i_map, i_attr, l_encoding),
+ fapi2::MSS_POWER_THERMAL_ENCODE_ERROR()
+ .set_ATTR(i_attr)
+ .set_DIMM_TARGET(i_target),
+ "Couldn't find encoding for power thermal encode for value: %x target: %s", i_attr, mss::c_str(i_target));
+ o_buf.insertFromRight<S, L>(l_encoding);
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Decode the attribute into a bit encoding
+/// @tparam S DRAM_GEN_SIZE enum used for fapi2::buffer position
+/// @tparam L DRAM_GEN_LEN enum used for fapi2::buffer position
+/// @tparam T integral type of key
+/// @tparam OT fapi2::buffer of some integral type
+/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value
+/// @param[in] i_buf the fapi2::buffer that has the encoding to parse
+/// @param[out] o_attr the attribute value from the encoding is going
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup
+///
+template<size_t S, size_t L, typename T, typename OT>
+inline fapi2::ReturnCode decode (const std::vector<std::pair<T, OT> >& i_map,
+ fapi2::buffer<uint32_t>& i_buf,
+ T& o_attr )
+{
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+ //used to hold result from vector pair lookup
+ OT l_encoding = 0;
+ i_buf.extractToRight<S, L>(l_encoding);
+
+ //Failing out if we don't find an decoding. All suported types should be encoded above
+ FAPI_ASSERT( mss::find_key_from_value (i_map, l_encoding, o_attr),
+ fapi2::MSS_POWER_THERMAL_DECODE_ERROR()
+ .set_ATTR(l_encoding),
+ "Couldn't find encoding for power thermal decode");
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+
+///
+/// @brief generates wildcard mask for the hash value
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+/// @tparam MC mss::mc_type
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_hash The encoded value
+/// @param[out] o_mask The wildcard mask
+///
+
+template<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode generate_wildcard_mask(const uint32_t i_hash, uint32_t& o_mask)
+{
+
+ fapi2::buffer<uint32_t> l_mask;
+ fapi2::buffer<uint32_t> l_hash = i_hash;
+ uint8_t l_uint8_buf = 0;
+ uint16_t l_uint16_buf = 0;
+ uint32_t l_uint32_buf = 0;
+
+ //DIMM_SIZE wildcard
+ FAPI_TRY(( decode<TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN>
+ (DIMM_SIZE_MAP, l_hash, l_uint32_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DIMM_SIZE", l_hash );
+
+ if(ANY_SIZE == l_uint32_buf)
+ {
+ l_mask.setBit(TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN);
+ }
+
+ //DRAM_GEN wildcard
+ FAPI_TRY(( decode<TT::DRAM_GEN_START, TT::DRAM_GEN_LEN>
+ (DRAM_GEN_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DRAM_GEN", l_hash);
+
+ if(ANY_GEN == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DRAM_GEN_START, TT::DRAM_GEN_LEN);
+ }
+
+ //DIMM_TYPE wildcard
+ FAPI_TRY(( decode<TT::DIMM_TYPE_START, TT::DIMM_TYPE_LEN>
+ (TT::DIMM_TYPE_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DIMM_TYPE", l_hash);
+
+ if(ANY_TYPE == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DIMM_TYPE_START, TT::DIMM_TYPE_LEN);
+ }
+
+ //DRAM_WIDTH wildcard
+ FAPI_TRY(( decode<TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN>
+ (DRAM_WIDTH_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DRAM_WIDTH", l_hash);
+
+ if(ANY_WIDTH == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN);
+ }
+
+ //DRAM_DENSITY wildcard
+ FAPI_TRY(( decode<TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN>
+ (DRAM_DENSITY_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DRAM_DENSITY", l_hash);
+
+ if(ANY_DENSITY == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN);
+ }
+
+ //DRAM_STACK_TYPE wildcard
+ FAPI_TRY(( decode<TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN>
+ (DRAM_STACK_TYPE_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DRAM_STACK_TYPE", l_hash);
+
+ if(ANY_STACK_TYPE == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN);
+ }
+
+ //DRAM_MFGID wildcard
+ FAPI_TRY(( decode<TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN>
+ (DRAM_MFGID_MAP, l_hash, l_uint16_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DRAM_MFG_ID", l_hash);
+
+ if(ANY_MFGID == l_uint16_buf)
+ {
+ l_mask.setBit(TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN);
+ }
+
+
+ if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_MCS)
+ {
+ //NUM DROPS PER PORT wildcard
+ FAPI_TRY(( decode<TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN>
+ (DIMMS_PORT_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DIMMS_PER_PORT", l_hash);
+
+ if(ANY_PORT == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN);
+ }
+ }
+
+ if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_OCMB_CHIP)
+ {
+ //MODUEL HEIGHT wildcard
+ FAPI_TRY(( decode<TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN>
+ (DIMM_MODULE_HEIGHT_MAP, l_hash, l_uint8_buf)),
+ "Failed to generate power thermal decoding for %s val %d",
+ "DIMMS_MODULE_HEIGHT", l_hash);
+
+ if(ANY_HEIGHT == l_uint8_buf)
+ {
+ l_mask.setBit(TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN);
+ }
+ }
+
+ o_mask = l_mask;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+
+///
+/// @class decoder
+/// @brief Decodes the power curve and thermal power limit attributes for eff_config_thermal
+/// @tparam MC mss::mc_type
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>>
+class decoder
+{
+ public:
+
+ //IVs for all of the attributes per MCS
+ const mss::dimm::kind iv_kind;
+
+ //Left in here rather than calculating during encode for testing
+ uint8_t iv_dimms_per_port;
+
+ //Power thermal attributes, both total and vddr versions will be used in eff_config_thermal
+ uint16_t iv_vddr_slope = 0;
+ uint16_t iv_vddr_intercept = 0;
+ uint16_t iv_total_slope = 0;
+ uint16_t iv_total_intercept = 0;
+
+ // Valid for OCMB only
+ uint32_t iv_power_limit = 0 ;
+
+ uint32_t iv_thermal_power_limit = 0;
+
+ //Generated key, used to decode all three power curve attributes
+ fapi2::buffer<uint32_t> iv_gen_key;
+
+ ///
+ /// @brief Constructor
+ /// @param[in] dimm::kind to call power thermal stuff on
+ ///
+ decoder( const mss::dimm::kind& i_kind):
+ iv_kind(i_kind)
+ {
+ iv_dimms_per_port = mss::count_dimm (find_target<TT::PORT_TARGET_TYPE>(iv_kind.iv_target));
+ };
+
+ //
+ // @brief Default destructor
+ //
+ ~decoder() = default;
+
+ ///
+ /// @brief generates the 32 bit encoding for the power curve attributes
+ /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+ /// @note populates iv_gen_key
+ ///
+ fapi2::ReturnCode generate_encoding ();
+
+ ///
+ /// @brief Finds a value for the power curve slope attributes by matching the generated hashes
+ /// @param[in] i_array is a vector of the attribute values
+ /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+ /// @note populates iv_vddr_slope, iv_total_slope
+ ///
+ fapi2::ReturnCode find_slope (const std::vector< const std::vector<uint64_t>* >& i_slope);
+
+ ///
+ /// @brief Finds a value for power curve intercept attributes by matching the generated hashes
+ /// @param[in] i_array is a vector of the attribute values
+ /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+ /// @note populates iv_vddr_intercept, iv_total_intercept
+ ///
+ fapi2::ReturnCode find_intercept (const std::vector< const std::vector<uint64_t>* >& i_intercept);
+
+ ///
+ /// @brief Finds a value from ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT and stores in iv variable
+ /// @param[in] i_array is a vector of the attribute values
+ /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+ /// @note populates iv_thermal_power_limit
+ ///
+ fapi2::ReturnCode find_thermal_power_limit (const std::vector< const std::vector<uint64_t>* >& i_thermal_limits);
+
+ ///
+ /// @brief Helper function to find the value from attribute
+ /// @tparam FIELD_START the field start offset inside attribute
+ /// @tparam FIELD_LEN the field length to extract
+ /// @tparam FUNCTION the function of the field
+ /// @tparam OT output type
+ /// @param[in] i_array is a vector of the attribute values
+ /// @param[in] i_attr_description the attribute description
+ /// @param[out] o_value the output value
+ /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+ /// @note populates iv_thermal_power_limit
+ ///
+ template<size_t FIELD_START, size_t FIELD_LEN, generic_ffdc_codes FUNCTION, typename OT>
+ fapi2::ReturnCode get_power_thermal_value(const std::vector<uint64_t>& i_array,
+ const char* const i_attr_description,
+ OT& o_value);
+};
+
+///
+/// @brief generates the 32 bit encoding for the power curve attributes
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+/// @note populates iv_gen_keys
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode decoder<MC, TT>::generate_encoding()
+{
+ //DIMM_SIZE
+ FAPI_TRY(( encode<TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN>
+ (iv_kind.iv_target, iv_kind.iv_size, DIMM_SIZE_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DIMM_SIZE", iv_kind.iv_size, mss::c_str(iv_kind.iv_target) );
+
+ //DRAM_GEN
+ FAPI_TRY(( encode<TT::DRAM_GEN_START, TT::DRAM_GEN_LEN>
+ (iv_kind.iv_target, iv_kind.iv_dram_generation, DRAM_GEN_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DRAM_GEN", iv_kind.iv_dram_generation, mss::c_str(iv_kind.iv_target) );
+
+ //DIMM_TYPE
+ FAPI_TRY(( encode<TT::DIMM_TYPE_START, TT::DIMM_TYPE_LEN>
+ (iv_kind.iv_target, iv_kind.iv_dimm_type, TT::DIMM_TYPE_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DIMM_TYPE", iv_kind.iv_dimm_type, mss::c_str(iv_kind.iv_target) );
+
+ //DRAM WIDTH
+ FAPI_TRY(( encode<TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN>
+ (iv_kind.iv_target, iv_kind.iv_dram_width, DRAM_WIDTH_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DRAM_WIDTH", iv_kind.iv_dram_width, mss::c_str(iv_kind.iv_target) );
+
+ //DRAM DENSITY
+ FAPI_TRY(( encode<TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN>
+ (iv_kind.iv_target, iv_kind.iv_dram_density, DRAM_DENSITY_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DRAM_DENSITY", iv_kind.iv_dram_density, mss::c_str(iv_kind.iv_target) );
+
+ //DRAM STACK TYPE
+ FAPI_TRY(( encode<TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN>
+ (iv_kind.iv_target, iv_kind.iv_stack_type, DRAM_STACK_TYPE_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DRAM_STACK_TYPE", iv_kind.iv_stack_type, mss::c_str(iv_kind.iv_target) );
+
+ //DRAM MFG ID
+ FAPI_TRY(( encode<TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN>
+ (iv_kind.iv_target, iv_kind.iv_mfgid, DRAM_MFGID_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DRAM_MFG_ID", iv_kind.iv_mfgid, mss::c_str(iv_kind.iv_target) );
+
+ if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_MCS)
+ {
+ //NUM DROPS PER PORT
+ FAPI_TRY(( encode<TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN>
+ (iv_kind.iv_target, iv_dimms_per_port, DIMMS_PORT_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DIMMS_PER_PORT", iv_dimms_per_port, mss::c_str(iv_kind.iv_target) );
+ }
+
+ if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_OCMB_CHIP)
+ {
+ //DIMM_MODULE_HEIGHT
+ FAPI_TRY(( encode<TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN>
+ (iv_kind.iv_target, iv_kind.iv_module_height, DIMM_MODULE_HEIGHT_MAP, iv_gen_key)),
+ "Failed to generate power thermal encoding for %s val %d on target: %s",
+ "DIMM_MODULE_HEIGHT", iv_kind.iv_module_height, mss::c_str(iv_kind.iv_target) );
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find the value from attribute
+/// @param[in] i_array is a vector of the attribute values
+/// @param[in] i_attr_description the attribute description
+/// @param[out] o_value the output value
+/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful
+/// @note populates iv_thermal_power_limit
+///
+template<mss::mc_type MC, typename TT>
+template<size_t FIELD_START, size_t FIELD_LEN, generic_ffdc_codes FUNCTION, typename OT>
+fapi2::ReturnCode decoder<MC, TT>::get_power_thermal_value(const std::vector<uint64_t>& i_array,
+ const char* const i_attr_description,
+ OT& o_value)
+{
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // Find iterator to matching key (if it exists)
+ auto l_value_iterator = std::find_if(i_array.begin(),
+ i_array.end(),
+ is_match<>(iv_gen_key));
+
+ FAPI_ASSERT(l_value_iterator != i_array.end(),
+ fapi2::MSS_NO_POWER_THERMAL_ATTR_FOUND()
+ .set_GENERATED_KEY(iv_gen_key)
+ .set_FUNCTION(FUNCTION)
+ .set_DIMM_TARGET(iv_kind.iv_target)
+ .set_SIZE(iv_kind.iv_size)
+ .set_DRAM_GEN(iv_kind.iv_dram_generation)
+ .set_DIMM_TYPE(iv_kind.iv_dimm_type)
+ .set_DRAM_WIDTH( iv_kind.iv_dram_width)
+ .set_DRAM_DENSITY(iv_kind.iv_dram_density)
+ .set_STACK_TYPE(iv_kind.iv_stack_type)
+ .set_MFGID(iv_kind.iv_mfgid)
+ .set_MODULE_HEIGHT(iv_kind.iv_module_height),
+ "Couldn't find %s value for generated key:0x%08lx, for target %s. "
+ "DIMM values for generated key are "
+ "size is %d, gen is %d, type is %d, width is %d, density %d, stack %d, mfgid %d, dimms %d, height %d",
+ i_attr_description,
+ iv_gen_key,
+ mss::c_str(iv_kind.iv_target),
+ iv_kind.iv_size,
+ iv_kind.iv_dram_generation,
+ iv_kind.iv_dimm_type,
+ iv_kind.iv_dram_width,
+ iv_kind.iv_dram_density,
+ iv_kind.iv_stack_type,
+ iv_kind.iv_mfgid,
+ iv_dimms_per_port,
+ iv_kind.iv_module_height
+ );
+ {
+ const fapi2::buffer<uint64_t> l_temp(*l_value_iterator);
+ l_temp.extractToRight<FIELD_START, FIELD_LEN>(o_value);
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+} // power_thermal
+} // mss
+#endif
diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H
index 4d99317bf..d90c6bb01 100644
--- a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H
+++ b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H
@@ -22,3 +22,1207 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+///
+/// @file gen_throttle.H
+/// @brief throttle API
+///
+
+// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com>
+// *HWP HWP Backup: Louis Stermole <stermole@us.ibm.com>
+// *HWP Team: Memory
+// *HWP Level: 3
+// *HWP Consumed by: FSP:HB
+
+#ifndef _MSS_GEN_POWER_THROTTLE_
+#define _MSS_GEN_POWER_THROTTLE_
+
+#include <fapi2.H>
+#include <generic/memory/lib/utils/shared/mss_generic_consts.H>
+#include <generic/memory/lib/utils/power_thermal/gen_throttle_traits.H>
+#include <generic/memory/lib/utils/count_dimm.H>
+#include <generic/memory/lib/mss_generic_system_attribute_getters.H>
+#include <generic/memory/lib/mss_generic_attribute_setters.H>
+#include <generic/memory/lib/utils/mss_math.H>
+#include <generic/memory/lib/utils/pos.H>
+
+namespace mss
+{
+
+namespace power_thermal
+{
+
+///
+/// @brief throttle constants used in the power_thermal functions
+///
+enum throttle_const : size_t
+{
+ /// Dram data bus utilization is bus utilization / 4
+ DRAM_BUS_UTILS = 4,
+
+ /// 10000 to convert to and from c%
+ UTIL_CONVERSION = 10000,
+
+ /// Conversion to percentage
+ PERCENT_CONVERSION = 100,
+
+};
+
+///
+/// @brief Calculate N (address operations) allowed within a window of M DRAM clocks
+/// @param[in] i_databus_util databus utilization percentage (e.g. 5% = 5)
+/// @param[in] i_num_dram_clocks window of M DRAM clocks
+/// @return number of throttled commands allowed
+/// @note Uses N/M Throttling.
+/// Equation: N = (DRAM data bus utilization * M) / (4 * 10000)
+///
+inline uint32_t throttled_cmds(const uint32_t i_databus_util, const uint32_t i_num_dram_clocks)
+{
+ constexpr uint64_t l_divisor = DRAM_BUS_UTILS * UTIL_CONVERSION;
+ const uint64_t l_dividend = i_databus_util * i_num_dram_clocks;
+ const uint64_t l_result = l_dividend / l_divisor;
+
+ //Make sure N is not equal to 0, or we brick the dram until reboot
+ return ((l_result == 0) ? 1 : l_result);
+}
+
+///
+/// @brief Calculate the port databus utilization based off of N throttles and M dram clocks
+/// @tparam MC mss::mc_type
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @tparam T output type
+/// @param[in] i_n_throttles N (address operations) allowed within a window of M DRAM clocks
+/// @param[in] i_num_dram_clocks window of M DRAM clocks
+/// @param[out] o_calc_util
+/// @return FAPI2_RC_SUCCESS iff method was a success
+/// @note Uses N/M Throttling.
+/// @note DRAM databus utilization = N * 4 * 10000 / M
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>, typename T>
+fapi2::ReturnCode calc_util_from_throttles(const uint16_t i_n_throttles,
+ const uint32_t i_num_dram_clocks,
+ T& o_calc_util)
+{
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+ constexpr uint32_t l_multiplier = DRAM_BUS_UTILS * UTIL_CONVERSION;
+ const uint64_t l_calc_util_uint64 = static_cast<uint64_t>((static_cast<double>(i_n_throttles) * l_multiplier) /
+ i_num_dram_clocks);
+ FAPI_ASSERT( (i_num_dram_clocks != 0),
+ fapi2::MSS_M_DRAM_CLOCKS_EQUALS_ZERO(),
+ "ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero");
+
+ o_calc_util = (static_cast<double>(i_n_throttles) * l_multiplier) / i_num_dram_clocks;
+
+ // Best way to check for overflow if o_calc_util can be a double?
+ // If o_calc_util overflows, the value inside will be below the expected outcome
+ // So compare o_calc_util with the calculated value, but store calculated value in largest storage
+ // Compare ">=" because o_calc_util can be a double, and so we can't compare just equality due to truncation
+ FAPI_ASSERT( o_calc_util >= l_calc_util_uint64,
+ fapi2::MSS_OUTPUT_OVERFLOW_CALC_UTIL()
+ .set_RESULT(o_calc_util),
+ "Overflow of output variable in calc_util_from_throttles throttles: %d, multiplier %d, dram_clocks %d",
+ i_n_throttles,
+ l_multiplier,
+ i_num_dram_clocks);
+
+ // Check for the minimum
+ if(o_calc_util < TT::MIN_UTIL)
+ {
+ FAPI_INF("Calculated utilization (%f) is less than the minimum utilization: %lu. Setting to minimum value",
+ o_calc_util, TT::MIN_UTIL);
+ o_calc_util = TT::MIN_UTIL;
+ }
+
+ FAPI_INF("In calc_util_from_throttles, calculated %f for output utilization from throttles:%d, dram_clocks%d",
+ o_calc_util, i_n_throttles, i_num_dram_clocks);
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Perform thermal calculations as part of the effective configuration
+/// @tparam MC mss::mc_type
+/// @tparam T the fapi2 target type of the target
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_target the MCS target in which the runtime throttles will be reset
+/// @return FAPI2_RC_SUCCESS iff ok
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode restore_runtime_throttles( const fapi2::Target<T>& i_target )
+{
+
+ uint32_t l_max_databus = 0;
+ uint32_t l_throttle_m_clocks = 0;
+
+ FAPI_TRY( mss::attr::get_mrw_mem_m_dram_clocks(l_throttle_m_clocks) );
+ FAPI_TRY( mss::attr::get_mrw_max_dram_databus_util(l_max_databus) );
+
+ //Set runtime throttles to unthrottled value, using max dram utilization and M throttle
+ //Do I need to check to see if any DIMMS configured on the port?
+ for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target))
+ {
+ uint16_t l_run_throttle = 0;
+
+ if (mss::count_dimm (l_port) != 0)
+ {
+ l_run_throttle = mss::power_thermal::throttled_cmds (l_max_databus, l_throttle_m_clocks);
+ }
+
+ FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_port( l_port, l_run_throttle) );
+ FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_slot( l_port, l_run_throttle) );
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values
+/// @tparam MC mss::mc_type
+/// @tparam T the fapi2 target type of the target
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_target the MCS target in which the runtime throttles will be set
+/// @return FAPI2_RC_SUCCESS iff ok
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode update_runtime_throttle(const fapi2::Target<T>& i_target)
+
+{
+
+ if (mss::count_dimm(i_target) == 0)
+ {
+ return fapi2::FAPI2_RC_SUCCESS;
+ }
+
+ for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target))
+ {
+
+ uint16_t l_run_slot = 0;
+ uint16_t l_run_port = 0;
+ uint16_t l_calc_slot = 0;
+ uint16_t l_calc_port = 0;
+
+ FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot));
+ FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port));
+ FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_slot(l_port, l_calc_slot));
+ FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_port(l_port, l_calc_port));
+
+ //Choose the worst case between runtime and calculated throttles
+ //Have to make sure the calc_slot isn't equal to 0 though
+ l_run_slot = (l_calc_slot != 0) ?
+ std::min(l_run_slot, l_calc_slot) : l_run_slot;
+ l_run_port = (l_calc_port != 0) ?
+ std::min(l_run_port, l_calc_port) : l_run_port;
+
+ FAPI_INF("New runtime throttles for %s for slot are %d, port are %d for %s",
+ mss::c_str(l_port),
+ l_run_slot,
+ l_run_port,
+ mss::c_str(l_port));
+
+ FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port) );
+ FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot) );
+ }
+
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+
+
+///
+/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values
+/// @tparam MC mss::mc_type
+/// @tparam T the fapi2 target type of the target
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_target the MCS target in which the runtime throttles will be set
+/// @return FAPI2_RC_SUCCESS iff ok
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode update_runtime_throttles(const std::vector< fapi2::Target<T> >& i_targets)
+
+{
+ for (const auto& l_mc : i_targets)
+ {
+ FAPI_TRY(update_runtime_throttle(l_mc));
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+
+///
+/// @class throttle
+/// @brief Determine power_thermal throttles for memory
+/// @tparam MC mss::mc_type
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>>
+class throttle
+{
+ private:
+ ///
+ /// @brief Calculate the power (cW) of inputs and the power curve
+ /// @tparam T the type of i_util and return value
+ /// @param[in] i_util the databus utilization that the power will be based on
+ /// @param[in] l_pos the dimm position for the power value being calculated.
+ /// @return Integral type T
+ ///
+ template<typename T>
+ inline T calc_power (const T i_util, const size_t i_pos, fapi2::ReturnCode& o_rc ) const
+ {
+ o_rc = fapi2::FAPI2_RC_SUCCESS;
+ FAPI_ASSERT( (i_pos < TT::DIMMS_PER_PORT),
+ fapi2::MSS_POWER_THERMAL_DIMM_INDEX_OUT_OF_BOUND()
+ .set_INPUT_SIZE(i_pos)
+ .set_MAX_SIZE(TT::DIMMS_PER_PORT),
+ "The dimm is index is out of bound for the port index: %d, max: %d for port %s",
+ i_pos, TT::DIMMS_PER_PORT, mss::c_str(iv_target) );
+
+ return ((i_util / UTIL_CONVERSION) * iv_pwr_slope[i_pos]) + iv_pwr_int[i_pos];
+
+ fapi_try_exit:
+ o_rc = fapi2::current_err;
+ return 0;
+ }
+
+ ///
+ /// @brief Raise the o_value by the percent passed in
+ /// @param[in] i_uplift the percent the o_Value should be raised by
+ /// @param[out] o_value the value that will be modified
+ ///
+ inline void calc_power_uplift (const uint8_t i_uplift, double& o_value) const
+ {
+ o_value *= (1 + (static_cast<double>(i_uplift) / PERCENT_CONVERSION));
+ }
+
+ public:
+ const fapi2::Target<TT::PORT_TARGET_TYPE>& iv_target;
+
+ uint32_t iv_databus_port_max;
+
+ uint8_t iv_power_uplift_idle;
+ uint8_t iv_power_uplift;
+
+ uint16_t iv_runtime_n_slot;
+ uint16_t iv_runtime_n_port;
+ uint32_t iv_m_clocks;
+ uint32_t iv_dimm_thermal_limit[TT::DIMMS_PER_PORT] = {};
+ uint16_t iv_pwr_slope[TT::DIMMS_PER_PORT] = {};
+ uint16_t iv_pwr_int[TT::DIMMS_PER_PORT] = {};
+ uint16_t iv_n_slot;
+ uint16_t iv_n_port;
+ uint32_t iv_port_power_limit;
+ uint32_t iv_calc_port_maxpower;
+
+ //default ctor deleted
+ throttle() = delete;
+
+ ///
+ /// @brief Constructor
+ /// @param[in] i_target port target to call power thermal stuff on
+ /// @param[out] o_rc fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ctor was successful
+ ///
+ throttle( const fapi2::Target<TT::PORT_TARGET_TYPE>& i_port, fapi2::ReturnCode& o_rc);
+
+ //
+ // @brief Destructor
+ //
+ ~throttle() = default;
+
+ ///
+ /// @brief Calculates the min and max power usage for a port
+ /// @param[in] i_idle_util the utilization of the databus in idle mode
+ /// @param[in] i_max_util the utilization of the port at maximum possible (mrw or calculated)
+ /// @param[out] o_port_power_idle max value of port power in cW
+ /// @param[out] o_port_power_max max value of port power in cW
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ /// @note Called twice in p9_mss_bulk_pwr_throttles
+ ///
+ fapi2::ReturnCode calc_port_power( const double i_idle_util [TT::DIMMS_PER_PORT],
+ const double i_max_util [TT::DIMMS_PER_PORT],
+ double& o_port_power_idle,
+ double& o_port_power_max) const;
+ ///
+ /// @brief Calculates max and min power usages based off of DIMM power curves
+ /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw)
+ /// @param[in] i_port_power_calc_idle double of the port's power consumption at idle
+ /// @param[out] o_dimm_power_idle array of dimm power in cW
+ /// @param[out] o_dimm_power_max array of dimm power in cW
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @note used for the thermal throttles
+ ///
+ fapi2::ReturnCode calc_dimm_power(const double i_databus_idle,
+ const double i_databus_max,
+ double o_dimm_power_idle [TT::DIMMS_PER_PORT],
+ double o_dimm_power_max [TT::DIMMS_PER_PORT]) const;
+
+ ///
+ /// @brief Calculate the power curve in order to calculate databus utilization
+ /// @param[in] i_power_idle double of the port's power consumption at idle
+ /// @param[in] i_power_max double of the port's power consumption at max utilization
+ /// @param[out] o_power_slope
+ /// @param[out] o_power_int
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @note Power curve needed to calculate the utilization
+ ///
+ fapi2::ReturnCode calc_power_curve(const double i_power_idle,
+ const double i_power_max,
+ uint32_t& o_power_slope,
+ uint32_t& o_power_int) const;
+ ///
+ /// @brief Calculate the databus utilization given the power curve
+ /// @param[in] i_slope the slope of power curve
+ /// @param[in] i_int the intercept of power curve
+ /// @param[in] i_power_limit either iv_port_power_limit or thermal_power_limit depending on throttle type
+ /// @param[out] o_port_util the port's databus utilization
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @note Chooses worst case between the maximum allowed databus utilization and the calculated value
+ ///
+ void calc_util_usage(const uint32_t i_slope,
+ const uint32_t i_int,
+ const uint32_t i_power_limit,
+ double& o_util) const;
+ ///
+ /// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower
+ /// @param[in] i_util_port pass in the calculated port databus utilization
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+ ///
+ fapi2::ReturnCode calc_slots_and_power (const double i_util_port);
+
+ ///
+ /// @brief calculated the output power estimate from the calculated N throttle
+ /// @param[in] i_n_slot the N throttle per slot
+ /// @param[in] i_n_port the N throttle per port
+ /// @param[out] o_power the calculated power
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ ///
+ fapi2::ReturnCode calc_power_from_n (const uint16_t i_n_slot, const uint16_t i_n_port, uint32_t& o_power) const;
+
+ ///
+ /// @brief Converts the port maximum databus util to a dimm level based on powerslopes and dimms installed
+ /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw)
+ /// @param[out] o_databus_dimm_max array of dimm utilization values
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @used to calculate the port power based off of DIMM power curves
+ ///
+ fapi2::ReturnCode calc_databus( const double i_databus_port_max,
+ double o_databus_dimm_max [TT::DIMMS_PER_PORT]);
+ ///
+ /// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed
+ /// @param[in] i_util_slot databus utilization for the slot
+ /// @param[in] i_util_port databus utilization for the port
+ /// @param[out] o_util_dimm_max array of dimm utilization values
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+ /// @note determines worst case utilization per dimms, takes into account port and combine slot throttles
+ ///
+ fapi2::ReturnCode calc_split_util(
+ const double i_util_slot,
+ const double i_util_port,
+ double o_util_dimm_max [TT::DIMMS_PER_PORT]) const;
+
+
+ ///
+ /// @brief Calculate ATTR_MSS_CHANNEL_PAIR_MAXPOWER and ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT,
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @note determines the throttle levels based off of the port's power curve, max databus utilization,
+ /// and memwat target.
+ /// @note currently sets the slot and port throttles to the same value
+ ///
+ fapi2::ReturnCode power_regulator_throttles ();
+
+ ///
+ /// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT,
+ /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+ /// @note Called in p9_mss_bulk_pwr_throttles
+ /// @note Sets the throttle levels based off of the dimm's thermal limits
+ /// @note both DIMM's on a port are set to the same throttle level
+ ///
+ fapi2::ReturnCode thermal_throttles ();
+};
+
+
+///
+/// @brief Constructor
+/// @tparam MC mss::mc_type
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_target MCS target to call power thermal stuff on
+/// @param[out] o_rc, a return code which determines the success of the constructor
+///
+template<mss::mc_type MC, typename TT>
+throttle<MC, TT>::throttle( const fapi2::Target<TT::PORT_TARGET_TYPE>& i_port, fapi2::ReturnCode& o_rc) :
+ iv_target(i_port),
+ iv_databus_port_max(0),
+ iv_runtime_n_slot(0),
+ iv_runtime_n_port(0),
+ iv_n_slot(0),
+ iv_n_port(0),
+ iv_port_power_limit(0),
+ iv_calc_port_maxpower(0)
+{
+ FAPI_TRY( mss::attr::get_mrw_max_dram_databus_util(iv_databus_port_max), "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_mrw_dimm_power_curve_percent_uplift(iv_power_uplift), "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_mrw_dimm_power_curve_percent_uplift_idle(iv_power_uplift_idle), "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_dimm_thermal_limit( iv_target, iv_dimm_thermal_limit), "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_total_pwr_intercept( iv_target, iv_pwr_int), "%s Error in throttle ctor", mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_total_pwr_slope( iv_target, iv_pwr_slope), "%s Error in throttle ctor", mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_runtime_mem_throttled_n_commands_per_slot(iv_target, iv_runtime_n_slot ),
+ "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_runtime_mem_throttled_n_commands_per_port(iv_target, iv_runtime_n_port ),
+ "%s Error in throttle ctor",
+ mss::c_str(i_port) );
+ FAPI_TRY( mss::attr::get_mrw_mem_m_dram_clocks(iv_m_clocks), "%s Error in throttle ctor", mss::c_str(i_port) );
+
+ //Port power limit = sum of dimm power limits
+ for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) )
+ {
+ uint32_t l_dimm_limit = 0;
+ FAPI_TRY( mss::attr::get_mem_watt_target( l_dimm, l_dimm_limit) );
+ iv_port_power_limit += l_dimm_limit;
+ }
+
+ FAPI_INF("Setting up throttle for target %s, Values are: max databus is %d, uplifts are %d %d, runtime throttles are %d %d for %s",
+ mss::c_str(iv_target),
+ iv_databus_port_max,
+ iv_power_uplift,
+ iv_power_uplift_idle,
+ iv_runtime_n_slot,
+ iv_runtime_n_port,
+ mss::c_str(iv_target));
+
+ FAPI_INF("The dimm power limit is %d, dram clocks are %d, dimm power curve slopes are %d %d for %s",
+ iv_port_power_limit,
+ iv_m_clocks,
+ iv_pwr_slope[0],
+ iv_pwr_slope[1],
+ mss::c_str(iv_target));
+
+ FAPI_INF("DIMM power curve intercepts are %d %d, DIMM power thermal limits are %d %d for %s",
+ iv_pwr_int[0],
+ iv_pwr_int[1],
+ iv_dimm_thermal_limit[0],
+ iv_dimm_thermal_limit[1],
+ mss::c_str(iv_target));
+
+ FAPI_ASSERT( (iv_databus_port_max != 0),
+ fapi2::MSS_NO_DATABUS_UTILIZATION()
+ .set_PORT_DATABUS_UTIL(iv_databus_port_max)
+ .set_DIMM_COUNT(mss::count_dimm(iv_target)),
+ "Failed to get max databus utilization for target %s",
+ mss::c_str(iv_target));
+
+ FAPI_ASSERT( (iv_port_power_limit != 0),
+ fapi2::MSS_NO_PORT_POWER_LIMIT()
+ .set_COUNT_DIMMS( mss::count_dimm(iv_target))
+ .set_PORT_POWER_LIMIT( iv_port_power_limit),
+ "Error calculating port_power_limit on target %s with %d DIMMs installed",
+ mss::c_str(iv_target),
+ iv_port_power_limit);
+
+ //Checking to make sure all of the attributes are valid
+ for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) )
+ {
+ const auto l_pos = mss::index(l_dimm);
+ FAPI_ASSERT( (iv_pwr_int[l_pos] != 0),
+ fapi2::MSS_POWER_INTERCEPT_NOT_SET(),
+ "The attribute ATTR_MSS_TOTAL_PWR_INTERCEPT equals 0 for %s",
+ mss::c_str(l_dimm));
+
+ FAPI_ASSERT( (iv_pwr_slope[l_pos] != 0),
+ fapi2::MSS_POWER_SLOPE_NOT_SET(),
+ "The attribute ATTR_MSS_TOTAL_PWR_SLOPE equals 0 for %s",
+ mss::c_str(l_dimm));
+ }
+
+fapi_try_exit:
+ o_rc = fapi2::current_err;
+ return;
+}
+
+///
+/// @brief Set ATTR_MSS_CHANNEL_PAIR_MAXPOWER, ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and _PER_PORT
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @note determines the throttle levels based off of the port's power curve,
+/// @note the _per_slot throttles are set to the _per_port values
+/// @note throttles are all equalized and set to the worst case value
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::power_regulator_throttles ()
+{
+ double l_port_power_calc_idle = 0;
+ double l_port_power_calc_max = 0;
+ uint32_t l_port_power_slope = 0;
+ uint32_t l_port_power_int = 0;
+ double l_calc_util_port = 0;
+ double l_databus_dimm_max[TT::DIMMS_PER_PORT] = {};
+ double l_calc_databus_port_idle[TT::DIMMS_PER_PORT] = {TT::IDLE_UTIL, TT::IDLE_UTIL};
+
+ FAPI_INF("Starting power regulator throttles for %s", mss::c_str(iv_target));
+
+ //Decide utilization for each dimm based off of dimm count and power slopes
+ FAPI_TRY( calc_databus(iv_databus_port_max, l_databus_dimm_max),
+ "Failed to calculate each DIMMs' percentage of dram databus utilization for target %s, max port databus is %d",
+ mss::c_str(iv_target),
+ iv_databus_port_max);
+
+ //Use the dimm utilizations and dimm power slopes to calculate port min and max power
+ FAPI_TRY( calc_port_power(l_calc_databus_port_idle,
+ l_databus_dimm_max,
+ l_port_power_calc_idle,
+ l_port_power_calc_max),
+ "Failed to calculate the max and idle power for port %s",
+ mss::c_str(iv_target));
+
+ FAPI_INF("POWER throttles: %s max port power is %f", mss::c_str(iv_target), l_port_power_calc_max);
+
+ //Calculate the power curve slope and intercept using the port's min and max power values
+ FAPI_TRY(calc_power_curve(l_port_power_calc_idle,
+ l_port_power_calc_max,
+ l_port_power_slope,
+ l_port_power_int),
+ "Failed to calculate the power curve for port %s, calculated port power max is %d, idle is %d",
+ mss::c_str(iv_target),
+ l_port_power_calc_max,
+ l_port_power_calc_idle);
+
+ FAPI_INF("%s POWER Port power limit is %d", mss::c_str(iv_target), iv_port_power_limit);
+ //Calculate the port's utilization to get under watt target using the port's calculated slopes
+ calc_util_usage(l_port_power_slope,
+ l_port_power_int,
+ iv_port_power_limit,
+ l_calc_util_port);
+
+ FAPI_INF("%s POWER calc util port is %f", mss::c_str(iv_target), l_calc_util_port);
+
+ //Calculate the new slot values and the max power value for the port
+ FAPI_TRY( calc_slots_and_power( l_calc_util_port),
+ "%s Error calculating the final throttles and power values for target with passed in port utilization %d",
+ mss::c_str(iv_target),
+ l_calc_util_port);
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower
+/// @param[in] i_util_port pass in the calculated port databus utilization
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_slots_and_power (const double i_util_port)
+{
+ //Calculate the Port N throttles
+ iv_n_port = power_thermal::throttled_cmds(i_util_port, iv_m_clocks);
+
+ //Set iv_n_slot to the lower value between the slot runtime and iv_n_port
+ iv_n_slot = (iv_runtime_n_slot != 0) ? std::min (iv_n_port, iv_runtime_n_slot) : iv_n_port;
+
+ //Choose the lowest value of the runtime and the calculated
+ iv_n_port = (iv_runtime_n_port != 0) ? std::min (iv_n_port, iv_runtime_n_port) : iv_n_port;
+
+ //Use the throttle value to calculate the power that gets to exactly that value
+ FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower));
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and PER_PORT
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @note Sets the throttle levels based off of the dimm's thermal limits
+/// @note both DIMM's on a port are set to the same throttle level
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::thermal_throttles ()
+{
+ double l_dimm_power_idle [TT::DIMMS_PER_PORT] = {};
+ double l_dimm_power_max [TT::DIMMS_PER_PORT] = {};
+ uint32_t l_dimm_power_slope [TT::DIMMS_PER_PORT] = {};
+ uint32_t l_dimm_power_int [TT::DIMMS_PER_PORT] = {};
+ double l_calc_util [TT::DIMMS_PER_PORT] = {};
+ const auto l_count = count_dimm (iv_target);
+
+ //Calculate the dimm power range for each dimm at max utilization for each
+ FAPI_TRY( calc_dimm_power(TT::IDLE_UTIL,
+ iv_databus_port_max,
+ l_dimm_power_idle,
+ l_dimm_power_max));
+
+ //Let's calculate the N throttle for each DIMM
+ for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) )
+ {
+ uint16_t l_temp_n_slot = 0;
+ const uint8_t l_pos = mss::index(l_dimm);
+ //Calculate the power curve taking the thermal limit into account
+ FAPI_TRY( calc_power_curve(l_dimm_power_idle[l_pos],
+ l_dimm_power_max[l_pos],
+ l_dimm_power_slope[l_pos],
+ l_dimm_power_int[l_pos]),
+ "Failed to calculate the power curve for dimm %s, calculated dimm power curve slope is %d, intercept %d",
+ mss::c_str(l_dimm),
+ l_dimm_power_slope[l_pos],
+ l_dimm_power_int[l_pos]);
+
+ //Calculate the databus utilization at the calculated power curve
+ calc_util_usage(l_dimm_power_slope[l_pos],
+ l_dimm_power_int[l_pos],
+ iv_dimm_thermal_limit[l_pos],
+ l_calc_util[l_pos]);
+
+ FAPI_INF("THERMAL throttles: %s dram databus utilization is %f for %s", mss::c_str(l_dimm), l_calc_util[l_pos],
+ mss::c_str(l_dimm));
+
+ l_temp_n_slot = power_thermal::throttled_cmds (l_calc_util[l_pos], iv_m_clocks);
+
+ //Set to the min between the two value
+ //If iv_n_slot == 0 (so uninitialized), set it to the calculated slot value
+ //The l_n_slot value can't be equal to 0 because there's a dimm installed
+ if ((l_temp_n_slot < iv_n_slot) || (iv_n_slot == 0))
+ {
+ iv_n_slot = l_temp_n_slot;
+ }
+ }
+
+ //Set to lowest value between calculated and runtime
+ FAPI_INF("THERMAL throttles: runtime slot is %d, calc n slot is %d for %s", iv_runtime_n_slot, iv_n_slot,
+ mss::c_str(iv_target));
+ //Taking the min of the SLOT * (# of dimms on the port) and the iv_runtime_port throttle value
+ //Thermal throttling happens after the POWER calculations. the iv_runtime_n_port value shouldn't be set to 0
+ iv_n_port = std::min(iv_runtime_n_port, static_cast<uint16_t>(iv_n_slot * l_count));
+ iv_n_port = (iv_n_port == 0) ? TT::MIN_THROTTLE : iv_n_port;
+
+ iv_n_slot = std::min(iv_n_slot, iv_runtime_n_slot);
+ iv_n_slot = (iv_n_slot == 0) ? TT::MIN_THROTTLE : iv_n_slot;
+
+ //Now time to get and set iv_calc_port_max from the calculated N throttle
+ FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower),
+ "Failed to calculate the final max port maxpower. Slot throttle value is %d, port value is %d for %s",
+ iv_n_slot,
+ iv_n_port,
+ mss::c_str(iv_target));
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ FAPI_ERR("Error calculating mss::power_thermal::thermal_throttles() for %s", mss::c_str(iv_target));
+ return fapi2::current_err;
+}
+
+///
+/// @brief Calculates the min and max power usage for a port based off of power curves and utilizations
+/// @param[in] i_idle_util the utilization of the databus in idle mode (0% most likely)
+/// @param[in] i_max_util the utilization of the dimm at maximum possible percentage (mrw or calculated)
+/// @param[out] o_port_power_idle max value of port power in cW
+/// @param[out] o_port_power_max max value of port power in cW
+/// @return fapi2::FAPI2_RC_SUCCESS iff the method was a success
+/// @note Called twice in p9_mss_bulk_pwr_throttles
+/// @note uses dimm power curves from class variables
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_port_power(const double i_idle_util [TT::DIMMS_PER_PORT],
+ const double i_max_util [TT::DIMMS_PER_PORT],
+ double& o_port_power_idle,
+ double& o_port_power_max) const
+{
+ //Playing it safe
+ o_port_power_idle = 0;
+ o_port_power_max = 0;
+ fapi2::ReturnCode l_rc;
+
+ //Calculate the port power curve info by summing the dimms on the port
+ for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) )
+ {
+ const auto l_pos = mss::index(l_dimm);
+ //Printing as decimals because HB messes up floats
+ FAPI_INF("%s max dram databus for DIMM in pos %d is %d, databus for idle is %d",
+ mss::c_str(iv_target),
+ l_pos,
+ static_cast<uint64_t>( i_max_util[l_pos]),
+ static_cast<uint64_t>( i_idle_util[l_pos]) );
+ //Sum up the dimm's power to calculate the port power curve
+ o_port_power_idle += calc_power(i_idle_util[l_pos], l_pos, l_rc);
+ FAPI_TRY(l_rc, "calc_power failed");
+ o_port_power_max += calc_power(i_max_util[l_pos], l_pos, l_rc);
+ FAPI_TRY(l_rc, "calc_power failed");
+ }
+
+ //Raise the powers by the uplift percent
+ calc_power_uplift(iv_power_uplift_idle, o_port_power_idle);
+ calc_power_uplift(iv_power_uplift, o_port_power_max);
+
+ FAPI_ASSERT( (o_port_power_max > 0),
+ fapi2::MSS_NO_PORT_POWER()
+ .set_COUNT_DIMMS(mss::count_dimm(iv_target))
+ .set_MAX_UTILIZATION_DIMM_0(i_max_util[0])
+ .set_MAX_UTILIZATION_DIMM_1(i_max_util[1]),
+ "No Port Power limit was calculated for %s, %d DIMMs installed, utilizations: DIMM 0 %d, DIMM 1 %d",
+ mss::c_str(iv_target),
+ mss::count_dimm(iv_target),
+ i_max_util[0],
+ i_max_util[1]);
+
+ //FAPI_ASSERTs don't set the current err to good
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Calculates max and min power usages based off of DIMM power curves
+/// @param[in] i_databus_idle idle databus utilization (either calculated or mrw)
+/// @param[in] i_databus_max max databus utilization (either calculated or mrw)
+/// @param[out] o_dimm_power_idle array of dimm power in cW
+/// @param[out] o_dimm_power_max array of dimm power in cW
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @note used for the thermal throttles
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_dimm_power(const double i_databus_idle,
+ const double i_databus_max,
+ double o_dimm_power_idle [TT::DIMMS_PER_PORT],
+ double o_dimm_power_max [TT::DIMMS_PER_PORT]) const
+{
+ for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) )
+ {
+ fapi2::ReturnCode l_rc;
+ const uint8_t l_pos = mss::index(l_dimm);
+ o_dimm_power_idle[l_pos] = calc_power(i_databus_idle, l_pos, l_rc);
+ FAPI_TRY(l_rc, "calc_power failed");
+ o_dimm_power_max[l_pos] = calc_power(i_databus_max, l_pos, l_rc);
+ FAPI_TRY(l_rc, "calc_power failed");
+
+ //Raise the powers by the uplift percent
+ calc_power_uplift(iv_power_uplift_idle, o_dimm_power_idle[l_pos]);
+ calc_power_uplift(iv_power_uplift, o_dimm_power_max[l_pos]);
+
+ FAPI_INF("Calc_dimm_power: dimm (%d) power max is %f, %f for dimm slope of %d, intercept of %d for %s",
+ l_pos,
+ o_dimm_power_max[l_pos],
+ o_dimm_power_max[l_pos],
+ iv_pwr_slope[l_pos],
+ iv_pwr_int[l_pos],
+ mss::c_str(l_dimm));
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ FAPI_INF("Error calculating mss::power_thermal::calc_dimm_power for %s", mss::c_str(iv_target));
+ return fapi2::current_err;
+}
+
+///
+/// @brief Calculate the port power curve in order to calculate the port utilization
+/// @param[in] i_power_idle double of the port's power consumption at idle
+/// @param[in] i_power_max double of the port's power consumption at max utilization
+/// @param[out] o_slope
+/// @param[out] o_int
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @note Port power curve needed to calculate the port utilization
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_power_curve(const double i_power_idle,
+ const double i_power_max,
+ uint32_t& o_slope,
+ uint32_t& o_int) const
+{
+ auto l_min_util = TT::MIN_UTIL;
+ const double l_divisor = ((static_cast<double>(iv_databus_port_max) / UTIL_CONVERSION) - TT::IDLE_UTIL);
+ FAPI_ASSERT ((l_divisor > 0),
+ fapi2::MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO()
+ .set_PORT_DATABUS_UTIL(iv_databus_port_max)
+ .set_UTIL_CONVERSION(UTIL_CONVERSION)
+ .set_IDLE_UTIL(l_min_util)
+ .set_RESULT(l_divisor),
+ "Calculated zero for the divisor in calc_power_curve on target %s",
+ mss::c_str(iv_target) );
+
+ o_slope = (i_power_max - i_power_idle) / l_divisor;
+ o_int = i_power_idle - (o_slope * TT::IDLE_UTIL);
+ FAPI_INF("Calc_power_curve: power idle is %f, max is %f, slope is %d, int is %d for %s",
+ i_power_idle,
+ i_power_max,
+ o_slope,
+ o_int,
+ mss::c_str(iv_target));
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ FAPI_INF("Error calculating mss::power_thermal::calc_power_curve for %s", mss::c_str(iv_target));
+ return fapi2::current_err;
+
+}
+
+///
+/// @brief Calculate the databus utilization given the power curve
+/// @param[in] i_slope
+/// @param[in] i_int
+/// @param[in] i_power_limit either the port_power_limit or the dimm thermal power limit
+/// @param[out] o_port_util the port's databus utilization
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @note Chooses worst case between the maximum allowed databus utilization and the calculated value
+///
+template<mss::mc_type MC, typename TT>
+void throttle<MC, TT>::calc_util_usage(const uint32_t i_slope,
+ const uint32_t i_int,
+ const uint32_t i_power_limit,
+ double& o_util) const
+{
+ o_util = ((static_cast<double>(i_power_limit) - i_int) / i_slope ) * UTIL_CONVERSION;
+
+ //Cast to uint32 for edge case where it has decimals
+ o_util = (static_cast<uint32_t>(o_util) < iv_databus_port_max) ? static_cast<uint32_t>(o_util) : iv_databus_port_max;
+
+ // Check for the minimum threshnold and update if need be
+ if(o_util < TT::MIN_UTIL)
+ {
+ FAPI_INF("Calculated utilization (%lu) is less than the minimum utilization: %lu. Setting to minimum value for %s",
+ o_util,
+ TT::MIN_UTIL, mss::c_str(iv_target));
+ o_util = TT::MIN_UTIL;
+ }
+}
+
+///
+/// @brief calculated the output power estimate from the calculated N throttle
+/// @param[in] i_n_slot the throttle per slot in terms of N commands
+/// @param[in] i_n_port the throttle per port in terms of N commands
+/// @param[out] o_power the calculated power
+/// @return fapi2::ReturnCode iff it was a success
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_power_from_n (const uint16_t i_n_slot,
+ const uint16_t i_n_port,
+ uint32_t& o_power) const
+{
+ double l_calc_util_port = 0;
+ double l_calc_util_slot = 0;
+ double l_calc_databus_port_max[TT::DIMMS_PER_PORT] = {};
+ double l_calc_databus_port_idle[TT::DIMMS_PER_PORT] = {};
+ double l_port_power_max = 0;
+ double l_port_power_idle = 0;
+
+ FAPI_TRY( calc_util_from_throttles(i_n_slot, iv_m_clocks, l_calc_util_slot),
+ "%s Error calculating utilization from slot throttle %d and mem clocks %d",
+ mss::c_str(iv_target),
+ i_n_slot,
+ iv_m_clocks);
+ FAPI_TRY( calc_util_from_throttles(i_n_port, iv_m_clocks, l_calc_util_port),
+ "%s Error calculating utilization from port throttle %d and mem clocks %d",
+ mss::c_str(iv_target),
+ i_n_port,
+ iv_m_clocks);
+
+ //Determine the utilization for each DIMM that will maximize the port power
+ FAPI_TRY( calc_split_util(l_calc_util_slot, l_calc_util_port, l_calc_databus_port_max),
+ "Error splitting the utilization for target %s with slot utilizatio %d and port util %d",
+ mss::c_str(iv_target),
+ l_calc_util_slot,
+ l_calc_util_port);
+
+ FAPI_TRY( calc_port_power(l_calc_databus_port_idle,
+ l_calc_databus_port_max,
+ l_port_power_idle,
+ l_port_power_max),
+ "Error calculating the port power value for %s. Slot value is %d, port value is %d",
+ mss::c_str(iv_target),
+ i_n_slot,
+ i_n_port);
+
+ o_power = mss::round_up (l_port_power_max);
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Converts the port maximum databus to a dimm level based on powerslopes and dimms installed
+/// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw)
+/// @param[out] o_databus_dimm_max array of dimm utilization values
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+/// @note Called in p9_mss_bulk_pwr_throttles
+/// @used to calculate the port power based off of DIMM power curves
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_databus (const double i_databus_port_max,
+ double o_databus_dimm_max [TT::DIMMS_PER_PORT])
+{
+ const uint8_t l_count_dimms = count_dimm(iv_target);
+
+ //No work for no dimms
+ if (l_count_dimms == 0)
+ {
+ return fapi2::FAPI2_RC_SUCCESS;
+ }
+
+ for (const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target))
+ {
+ //Left early if count_dimms == 0
+ o_databus_dimm_max[mss::index(l_dimm)] = i_databus_port_max / l_count_dimms;
+ }
+
+ //If the power slopes aren't equal, set the dimm with the highest power slope
+ //Should be correct even if only one DIMM is installed
+ if (iv_pwr_slope[0] != iv_pwr_slope[1])
+ {
+ o_databus_dimm_max[0] = (iv_pwr_slope[0] > iv_pwr_slope[1]) ? i_databus_port_max : 0;
+ o_databus_dimm_max[1] = (iv_pwr_slope[1] > iv_pwr_slope[0]) ? i_databus_port_max : 0;
+ }
+
+ //Make sure both are not 0
+ FAPI_ASSERT ( (o_databus_dimm_max[0] != 0) || (o_databus_dimm_max[1] != 0),
+ fapi2::MSS_NO_DATABUS_UTILIZATION()
+ .set_PORT_DATABUS_UTIL(i_databus_port_max)
+ .set_DIMM_COUNT(l_count_dimms),
+ "Failed to calculated databus utilization for target %s",
+ mss::c_str(iv_target));
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed
+/// @param[in] i_util_slot databus utilization for the slot
+/// @param[in] i_util_port databus utilization for the port
+/// @param[out] o_util_dimm_max array of dimm utilization values
+/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK
+/// @note determines worst case utilization per dimms, takes into account port and combine slot throttles
+/// @note used in calculating the port power, not for calculating the slot and port utilization
+///
+template<mss::mc_type MC, typename TT>
+fapi2::ReturnCode throttle<MC, TT>::calc_split_util(
+ const double i_util_slot,
+ const double i_util_port,
+ double o_util_dimm_max [TT::DIMMS_PER_PORT]) const
+{
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+ const uint8_t l_count_dimms = count_dimm (iv_target);
+ //The total utilization to be used is limited by either what the port can allow or what the dimms can use
+ FAPI_ASSERT( (i_util_slot <= i_util_port),
+ fapi2::MSS_SLOT_UTIL_EXCEEDS_PORT()
+ .set_SLOT_UTIL(i_util_slot)
+ .set_PORT_UTIL(i_util_port),
+ "The slot utilization (%f) exceeds the port's utilization (%f) for %s",
+ i_util_slot,
+ i_util_port,
+ mss::c_str(iv_target));
+
+ if (l_count_dimms == 0)
+ {
+ return fapi2::FAPI2_RC_SUCCESS;
+ }
+
+ //assumptions slot <= port, l_count_dimms <=2
+ if (i_util_slot * l_count_dimms > i_util_port)
+ {
+ FAPI_INF("In mss::power_thermal::calc_split i_util_slot is %f, i_util_port is %f, l_count_dimms is %d for %s",
+ i_util_slot,
+ i_util_port,
+ l_count_dimms,
+ mss::c_str(iv_target));
+ const uint8_t l_high_pos = (iv_pwr_slope[0] >= iv_pwr_slope[1]) ? 0 : 1;
+
+ //Highest power_slope gets the higher utilization
+ o_util_dimm_max[l_high_pos] = std::min(i_util_slot, i_util_port);
+ //Set the other dimm to the left over utilization (i_util_port - i_util_slot)
+ o_util_dimm_max[(!l_high_pos)] = (l_count_dimms == TT::DIMMS_PER_PORT) ? (i_util_port - o_util_dimm_max[l_high_pos]) :
+ 0;
+
+ FAPI_INF("Split utilization for target %s, DIMM in %d gets %f, DIMM in %d gets %f for %s",
+ mss::c_str(iv_target),
+ l_high_pos,
+ o_util_dimm_max[l_high_pos],
+ !l_high_pos,
+ o_util_dimm_max[!l_high_pos],
+ mss::c_str(iv_target));
+ }
+ else
+ {
+ //If only 1 dimm, i_util_port == i_util_slot
+ //If 2 dimms, 2*i_util_slot <= i_util_pot
+ //Either way, limit utilization by the slot value
+ for (const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target))
+ {
+ const size_t l_pos = mss::index(l_dimm);
+ o_util_dimm_max[l_pos] = i_util_slot;
+ }
+ }
+
+ //make sure both are not 0
+ FAPI_ASSERT ( (o_util_dimm_max[0] != 0) || (o_util_dimm_max[1] != 0),
+ fapi2::MSS_NO_DATABUS_UTILIZATION()
+ .set_PORT_DATABUS_UTIL(i_util_port)
+ .set_DIMM_COUNT(mss::count_dimm(iv_target)),
+ "Failed to calculated util utilization for target %s",
+ mss::c_str(iv_target));
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+
+///
+/// @brief Equalize the throttles and estimated power at those throttle levels
+/// @tparam MC mss::mc_type
+/// @tparam T the fapi2 MC target type of the target
+/// @tparam TT throttle_traits throttle traits for the given mc_type
+/// @param[in] i_targets vector of MCS targets all on the same VDDR domain
+/// @param[in] i_throttle_type denotes if this was done for POWER (VMEM) or THERMAL (VMEM+VPP) throttles
+/// @param[out] o_exceeded_power vector of MCA targets where the estimated power exceeded the maximum allowed
+/// @return FAPI2_RC_SUCCESS iff ok
+/// @note sets the throttles and power to the worst case
+/// Called by p9_mss_bulk_pwr_throttles and by p9_mss_utils_to_throttle (so by IPL or by OCC)
+///
+template<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>>
+fapi2::ReturnCode equalize_throttles (const std::vector< fapi2::Target<T> >& i_targets,
+ const throttle_type i_throttle_type,
+ std::vector< fapi2::Target<TT::PORT_TARGET_TYPE> >& o_exceeded_power)
+
+{
+ o_exceeded_power.clear();
+
+ //Set to max values so every compare will change to min value
+ uint16_t l_min_slot = ~(0);
+ uint16_t l_min_port = ~(0);
+
+ //Loop through all of the MC targets to find the worst case throttle value (lowest) for the slot and port
+ for (const auto& l_mc : i_targets)
+ {
+ for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(l_mc))
+ {
+ uint16_t l_calc_slot = 0;
+ uint16_t l_calc_port = 0;
+ uint16_t l_run_slot = 0;
+ uint16_t l_run_port = 0;
+
+ if (mss::count_dimm(l_port) == 0)
+ {
+ continue;
+ }
+
+ FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_slot(l_port, l_calc_slot));
+ FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_port(l_port, l_calc_port));
+ FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot));
+ FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port));
+
+ //Find the smaller of the three values (calculated slot, runtime slot, and min slot)
+ l_min_slot = (l_calc_slot != 0) ? std::min( std::min (l_calc_slot, l_run_slot),
+ l_min_slot) : l_min_slot;
+ l_min_port = (l_calc_port != 0) ? std::min( std::min( l_calc_port, l_run_port),
+ l_min_port) : l_min_port;
+ }
+ }
+
+ FAPI_INF("Calculated min slot is %d, min port is %d for the system", l_min_slot, l_min_port);
+
+ //Now set every port to have those values
+ {
+ for (const auto& l_mc : i_targets)
+ {
+ for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(l_mc))
+ {
+ uint16_t l_fin_slot = 0;
+ uint16_t l_fin_port = 0;
+ uint32_t l_fin_power = 0;;
+
+ if (mss::count_dimm(l_port) == 0)
+ {
+ continue;
+ }
+
+ // Declaring above to avoid fapi2 jump
+ uint64_t l_power_limit = 0;
+
+ l_fin_slot = l_min_slot;
+ l_fin_port = l_min_port;
+
+ //Need to create throttle object for each mca in order to get dimm configuration and power curves
+ //To calculate the slot/port utilization and total port power consumption
+ fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS;
+
+ const auto l_dummy = mss::power_thermal::throttle<MC>(l_port, l_rc);
+ FAPI_TRY(l_rc, "Failed creating a throttle object in equalize_throttles for %s", mss::c_str(l_port));
+
+ FAPI_TRY( l_dummy.calc_power_from_n(l_fin_slot, l_fin_port, l_fin_power),
+ "Failed calculating the power value for throttles: slot %d, port %d for target %s",
+ l_fin_slot,
+ l_fin_port,
+ mss::c_str(l_port));
+
+ // You may ask why this is not a variable within the throttle struct
+ // It's because POWER throttling is on a per port basis while the THERMAL throttle is per dimm
+ // Didn't feel like adding a variable just for this check
+ l_power_limit = (i_throttle_type == throttle_type::POWER) ?
+ l_dummy.iv_port_power_limit : (l_dummy.iv_dimm_thermal_limit[0] + l_dummy.iv_dimm_thermal_limit[1]);
+
+ FAPI_INF("%s Calculated power is %d, limit is %ld", mss::c_str(l_port), l_fin_power, l_power_limit);
+
+ //If there's an error with calculating port power, the wrong watt target was passed in
+ //Returns an error but doesn't deconfigure anything. Calling function can log if it wants to
+ //Called by OCC and by p9_mss_eff_config_thermal, thus different ways for error handling
+ //Continue setting throttles to prevent a possible throttle == 0
+ //The error will be the last bad port found
+ if (l_fin_power > l_power_limit)
+ {
+ //Need this because of pos traits and templating stuff
+ uint64_t l_fail = mss::fapi_pos(l_port);
+ //Set the failing port. OCC just needs one failing port, doesn't need all of them
+ FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_MEM_PORT_POS_OF_FAIL_THROTTLE,
+ fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(),
+ l_fail) );
+
+ FAPI_ASSERT_NOEXIT( false,
+ fapi2::MSS_CALC_PORT_POWER_EXCEEDS_MAX()
+ .set_CALCULATED_PORT_POWER(l_fin_power)
+ .set_MAX_POWER_ALLOWED(l_power_limit)
+ .set_PORT_POS(mss::pos(l_port))
+ .set_PORT_TARGET(l_port),
+ "Error calculating the final port power value for target %s, calculated power is %d, max value can be %d",
+ mss::c_str(l_port),
+ l_fin_power,
+ l_power_limit);
+
+ o_exceeded_power.push_back(l_port);
+ }
+
+ FAPI_INF("%s Final throttles values for slot %d, for port %d, power value %d",
+ mss::c_str(l_port),
+ l_fin_port,
+ l_fin_slot,
+ l_fin_power);
+
+ //Even if there's an error, still calculate and set the throttles.
+ //OCC will set to safemode if there's an error
+ //Better to set the throttles than leave them 0, and potentially brick the memory
+ FAPI_TRY( mss::attr::set_mem_throttled_n_commands_per_port( l_port, l_fin_port) );
+ FAPI_TRY( mss::attr::set_mem_throttled_n_commands_per_slot( l_port, l_fin_slot) );
+ FAPI_TRY( mss::attr::set_port_maxpower( l_port, l_fin_power) );
+ }
+ }
+ }
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ FAPI_ERR("Error equalizing memory throttles");
+ return fapi2::current_err;
+}
+
+} //ns power_thermal
+}// mss
+
+#endif
diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H
index 17758adf1..d6d30fa66 100644
--- a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H
+++ b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H
@@ -22,3 +22,36 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file gen_throttle_traits.H
+/// @brief Contains throttle traits information
+///
+// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com>
+// *HWP FW Owner: Andre Marin <aamarin@us.ibm.com>
+// *HWP Team: Memory
+// *HWP Level: 2
+// *HWP Consumed by: CI
+
+#ifndef _GEN_THROTTLE_TRAITS_H_
+#define _GEN_THROTTLE_TRAITS_H_
+
+#include <fapi2.H>
+#include <generic/memory/lib/utils/shared/mss_generic_consts.H>
+
+namespace mss
+{
+
+namespace power_thermal
+{
+
+///
+/// @class Traits for throttle
+/// @tparam MC mss::mc_type memory controller type
+///
+template< mss::mc_type MC = DEFAULT_MC_TYPE >
+class throttle_traits;
+
+} //ns power_thermal
+} // ns mss
+#endif
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 2dee8fa53..275cb9e82 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
@@ -104,6 +104,9 @@ enum conversions
NIBBLES_PER_BYTE = 2,
BITS_PER_NIBBLE = 4,
BITS_PER_BYTE = 8,
+
+ // Used by exp_decoder.C for dA to cA
+ DECI_TO_CENTI = 10,
};
enum generic_sizes
@@ -200,6 +203,11 @@ enum generic_ffdc_codes
SET_PRIM_BUS_WIDTH = 0x1069,
SET_PRIM_DIE_COUNT = 0x1070,
SET_DRAM_DENSITY = 0x1071,
+
+ // Power thermal functions
+ POWER_LIMIT = 0x1072,
+ SLOPE = 1073,
+ INTERCEPT = 1074,
};
///
diff --git a/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml b/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml
index 1b2e69ea4..60cf77d35 100644
--- a/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml
+++ b/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml
@@ -621,6 +621,22 @@
</attribute>
<attribute>
+ <id>ATTR_MEM_EFF_DRAM_MODULE_HEIGHT</id>
+ <targetType>TARGET_TYPE_MEM_PORT</targetType>
+ <description>
+ ARRAY[DIMM]
+ DRAM Modlue Height
+ Decodes SPD Byte 193
+ </description>
+ <enum>1U = 0, 2U = 1, 4U = 2 </enum>
+ <initToZero></initToZero>
+ <valueType>uint8</valueType>
+ <writeable/>
+ <array>2</array>
+ <mssAccessorName>dram_module_height</mssAccessorName>
+ </attribute>
+
+ <attribute>
<id>ATTR_MEM_EFF_RCD_MFG_ID</id>
<targetType>TARGET_TYPE_MEM_PORT</targetType>
<description>
diff --git a/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml b/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml
index 31ac9b4c0..0230aa2e4 100644
--- a/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml
+++ b/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml
@@ -606,4 +606,136 @@
<mssAccessorName>mrw_supported_dram_width</mssAccessorName>
</attribute>
+ <attribute>
+ <id>ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT</id>
+ <targetType>TARGET_TYPE_SYSTEM</targetType>
+ <description>
+ Machine Readable Workbook Thermal Memory Power Limit
+ Used to calculate throttles to be at or under the power limit
+ Per DIMM basis
+ KEY (0-21): In order
+ DIMM_SIZE = bits 0-3,
+ DIMM_GEN = 4-5,
+ DIMM_TYPE = 6-8,
+ DIMM_WIDTH = 9-11,
+ DIMM_DENSITY = 12-14,
+ DIMM_STACK_TYPE = 15-16,
+ DRAM_MFGID = 17-19,
+ DIMM_HEIGHT = 20-21,
+ Bits 22-32: Not used
+ VALUE (bits 32-47) in cW:
+ OCMB+DRAM thermal power limit per DIMM = 32-47
+ </description>
+ <valueType>uint64</valueType>
+ <mssUnits>cW</mssUnits>
+ <default>0xfffffc07940000</default>
+ <array>25</array>
+ <platInit/>
+ <mssAccessorName>mrw_ocmb_thermal_memory_power_limit</mssAccessorName>
+ </attribute>
+
+ <attribute>
+ <id>ATTR_MSS_MRW_OCMB_PWR_SLOPE</id>
+ <targetType>TARGET_TYPE_SYSTEM</targetType>
+ <description>
+ Machine Readable Workbook Power Curve Slope for DIMM
+ Used to get the OCMB+DRAM power curve for each DIMM
+ Per DIMM basis
+ KEY (0-21): In order
+ DIMM_SIZE = bits 0-3,
+ DIMM_GEN = 4-5,
+ DIMM_TYPE = 6-8,
+ DIMM_WIDTH = 9-11,
+ DIMM_DENSITY = 12-14,
+ DIMM_STACK_TYPE = 15-16,
+ DRAM_MFGID = 17-19,
+ DIMM_HEIGHT = 20-21,
+ Bits 22-32: Not used
+ VALUE (bits 32-47) in cW/utilization:
+ OCMB+DRAM thermal power limit per DIMM = 32-47
+ </description>
+ <valueType>uint64</valueType>
+ <mssUnits>cW</mssUnits>
+ <default>0xfffffc00044C0000</default>
+ <array>50</array>
+ <platInit/>
+ <mssAccessorName>mrw_ocmb_pwr_slope</mssAccessorName>
+ </attribute>
+
+ <attribute>
+ <id>ATTR_MSS_MRW_OCMB_PWR_INTERCEPT</id>
+ <targetType>TARGET_TYPE_SYSTEM</targetType>
+ <description>
+ Machine Readable Workbook Power Curve Intercept for DIMM
+ Used to get the OCMB+DRAM power curve for each DIMM
+ Per DIMM basis
+ KEY (0-21): In order
+ DIMM_SIZE = bits 0-3,
+ DIMM_GEN = 4-5,
+ DIMM_TYPE = 6-8,
+ DIMM_WIDTH = 9-11,
+ DIMM_DENSITY = 12-14,
+ DIMM_STACK_TYPE = 15-16,
+ DRAM_MFGID = 17-19,
+ DIMM_HEIGHT = 20-21,
+ Bits 22-32: Not used
+ VALUE (bits 32-47) in cW/utilization:
+ OCMB+DRAM thermal power limit per DIMM = 32-47
+ </description>
+ <valueType>uint64</valueType>
+ <mssUnits>cW/utilization</mssUnits>
+ <default>0xfffffc00044C0000</default>
+ <array>50</array>
+ <platInit/>
+ <mssAccessorName>mrw_ocmb_pwr_intercept</mssAccessorName>
+ </attribute>
+
+ <attribute>
+ <id>ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT</id>
+ <targetType>TARGET_TYPE_SYSTEM</targetType>
+ <description>
+ Machine Readable Workbook Power Curve Intercept and limit for DIMM
+ Used to get the PMIC power curve and limit for each DIMM
+ Per DIMM basis
+ KEY (0-21): In order
+ DIMM_SIZE = bits 0-3,
+ DIMM_GEN = 4-5,
+ DIMM_TYPE = 6-8,
+ DIMM_WIDTH = 9-11,
+ DIMM_DENSITY = 12-14,
+ DIMM_STACK_TYPE = 15-16,
+ DRAM_MFGID = 17-19,
+ DIMM_HEIGHT = 20-21,
+ Bits 22-32: Not used
+ VALUE (bits 32-39): Current limit (dA)
+ VALUE (bits 40-51): Current slope (cA/utilization)
+ VALUE (bits 52-63): Current intercept (cA)
+ </description>
+ <valueType>uint64</valueType>
+ <mssUnits>dA, cA/utilization, cA</mssUnits>
+ <default>0xfffffc0000000000</default>
+ <array>25</array>
+ <platInit/>
+ <mssAccessorName>mrw_ocmb_current_curve_with_limit</mssAccessorName>
+ </attribute>
+
+ <attribute>
+ <id>ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL</id>
+ <targetType>TARGET_TYPE_SYSTEM</targetType>
+ <description>
+ Machine Readable Workbook value for safe mode dram data bus utilization in centi percent (c%).
+ Set to below optimum value/ rate.
+ On a per port basis
+ Also used for emergency mode throttle
+ Used to thermally protect the system in all supported environmental conditions when OCC is not functional
+ Consumer: thermal_init, initfile
+ Default to 2500 c%%
+ </description>
+ <valueType>uint32</valueType>
+ <default>0x000009C4</default>
+ <platInit/>
+ <initToZero/>
+ <mssAccessorName>mrw_safemode_dram_databus_util</mssAccessorName>
+ </attribute>
+
</attributes>
diff --git a/src/import/generic/procedures/xml/error_info/generic_error.xml b/src/import/generic/procedures/xml/error_info/generic_error.xml
index cec60fdfb..78d852568 100644
--- a/src/import/generic/procedures/xml/error_info/generic_error.xml
+++ b/src/import/generic/procedures/xml/error_info/generic_error.xml
@@ -440,4 +440,233 @@
</callout>
</hwpError>
+ <hwpError>
+ <rc>RC_MSS_POWER_INTERCEPT_NOT_SET</rc>
+ <description>
+ The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0
+ </description>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_POWER_SLOPE_NOT_SET</rc>
+ <description>
+ The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0
+ </description>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_NO_DATABUS_UTILIZATION</rc>
+ <description>
+ There are 2 DIMMS on the port but both have 0 databus utilization
+ </description>
+ <ffdc>PORT_DATABUS_UTIL</ffdc>
+ <ffdc>DIMM_COUNT</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO</rc>
+ <description>
+ Denominator equals 0
+ </description>
+ <ffdc>PORT_DATABUS_UTIL</ffdc>
+ <ffdc>UTIL_CONVERSION</ffdc>
+ <ffdc>IDLE_UTIL</ffdc>
+ <ffdc>RESULT</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_NO_PORT_POWER_LIMIT</rc>
+ <description>
+ Got 0 when calculating port power limit.
+ Either no dimms or attribute MEM_WATT_TARGET wasn't set
+ </description>
+ <ffdc>COUNT_DIMMS</ffdc>
+ <ffdc>PORT_POWER_LIMIT</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_NO_PORT_POWER</rc>
+ <description>
+ Got 0 when calculating port power limits using the DIMMs databus utilization
+ </description>
+ <ffdc>COUNT_DIMMS</ffdc>
+ <ffdc>MAX_UTILIZATION_DIMM_0</ffdc>
+ <ffdc>MAX_UTILIZATION_DIMM_1</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_M_DRAM_CLOCKS_EQUALS_ZERO</rc>
+ <description>
+ ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero
+ </description>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_CALC_PORT_POWER_EXCEEDS_MAX</rc>
+ <description>
+ The calculated port power from equalizing throttles exceeds the maximum allowed power
+ </description>
+ <ffdc>CALCULATED_PORT_POWER</ffdc>
+ <ffdc>MAX_POWER_ALLOWED</ffdc>
+ <ffdc>PORT_POS</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ <callout>
+ <childTargets>
+ <parent>PORT_TARGET</parent>
+ <childType>TARGET_TYPE_DIMM</childType>
+ </childTargets>
+ <priority>MEDIUM</priority>
+ </callout>
+ <deconfigure>
+ <childTargets>
+ <parent>PORT_TARGET</parent>
+ <childType>TARGET_TYPE_DIMM</childType>
+ </childTargets>
+ </deconfigure>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_SLOT_UTIL_EXCEEDS_PORT</rc>
+ <description>
+ The memory throttle per slot (DIMM) exceeds the allowed throttle for the port
+ </description>
+ <ffdc>SLOT_UTIL</ffdc>
+ <ffdc>PORT_UTIL</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_OUTPUT_OVERFLOW_CALC_UTIL</rc>
+ <description>
+ Type of output variable is not large enough for the calculations
+ </description>
+ <ffdc>RESULT</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_POWER_THERMAL_DECODE_ERROR</rc>
+ <description>
+ There was no match or value found in decoding the power thermal attributes
+ </description>
+ <ffdc>DIMM_TARGET</ffdc>
+ <ffdc>ATTR</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_MRW_SAFEMODE_UTIL_THROTTLE_NOT_SUPPORTED</rc>
+ <description>
+ The MRW safemode utilization that is less than the minimum utilization supported. Check ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL.
+ </description>
+ <ffdc>MRW_SAFEMODE_UTIL</ffdc>
+ <ffdc>MIN_UTIL_VALUE</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_NO_POWER_THERMAL_ATTR_FOUND</rc>
+ <description>
+ There was no match or value found in decoding the power thermal attributes
+ </description>
+ <ffdc>GENERATED_KEY</ffdc>
+ <ffdc>FUNCTION</ffdc>
+ <ffdc>DIMM_TARGET</ffdc>
+ <ffdc>SIZE</ffdc>
+ <ffdc>DRAM_GEN</ffdc>
+ <ffdc>DIMM_TYPE</ffdc>
+ <ffdc>DRAM_WIDTH</ffdc>
+ <ffdc>DRAM_DENSITY</ffdc>
+ <ffdc>STACK_TYPE</ffdc>
+ <ffdc>MFGID</ffdc>
+ <ffdc>MODULE_HEIGHT</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_POWER_THERMAL_ENCODE_ERROR</rc>
+ <description>
+ There was no match or value found in encoding the power thermal attributes
+ </description>
+ <ffdc>DIMM_TARGET</ffdc>
+ <ffdc>ATTR</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT</rc>
+ <description>
+ The attributes vectors size is incorrect for find_xxx functions
+ </description>
+ <ffdc>FUNCTION</ffdc>
+ <ffdc>INPUT_SIZE</ffdc>
+ <ffdc>EXPECTED_SIZE</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
+ <hwpError>
+ <rc>RC_MSS_POWER_THERMAL_DIMM_INDEX_OUT_OF_BOUND</rc>
+ <description>
+ The dimm index is out of bound for the port
+ </description>
+ <ffdc>INPUT_SIZE</ffdc>
+ <ffdc>MAX_SIZE</ffdc>
+ <callout>
+ <procedure>CODE</procedure>
+ <priority>HIGH</priority>
+ </callout>
+ </hwpError>
+
</hwpErrors>
OpenPOWER on IntegriCloud