Add CFM Limit Set / Get commands
These commands can be used to set the Min / Max
CFM limits.
Tested: Used bios to change cfm limits and they
were persisted.
Change-Id: Id806acb9c13b32228d1fe86baf436e756f32d212
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index df6b007..cd51038 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -28,6 +28,8 @@
cmdGetShutdownPolicy = 0x62,
cmdSetFanConfig = 0x89,
cmdGetFanConfig = 0x8a,
+ cmdSetFscParameter = 0x90,
+ cmdGetFscParameter = 0x91,
cmdGetChassisIdentifier = 0x92,
cmdGetProcessorErrConfig = 0x9A,
cmdSetProcessorErrConfig = 0x9B,
@@ -298,4 +300,12 @@
// reserved [5:3]
performAcousSelect = 2
// reserved [1:0]
+};
+
+enum class setFscParamFlags : uint8_t
+{
+ tcontrol = 0x1,
+ pwmOffset = 0x2,
+ maxPwm = 0x3,
+ cfm = 0x4
};
\ No newline at end of file
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index 455c61c..c45727b 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -846,6 +846,167 @@
return IPMI_CC_OK;
}
+constexpr const char* cfmLimitSettingPath =
+ "/xyz/openbmc_project/control/cfm_limit";
+constexpr const char* cfmLimitIface = "xyz.openbmc_project.Control.CFMLimit";
+
+ipmi_ret_t ipmiOEMSetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+ constexpr const size_t disableLimiting = 0x0;
+
+ if (*dataLen < 2)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ uint8_t* req = static_cast<uint8_t*>(request);
+
+ if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
+ {
+ if (*dataLen != 3)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ *dataLen = 0;
+
+ uint16_t cfm = req[1] | (static_cast<uint16_t>(req[2]) << 8);
+
+ // must be greater than 50 based on eps
+ if (cfm < 50 && cfm != disableLimiting)
+ {
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+
+ try
+ {
+ ipmi::setDbusProperty(dbus, settingsBusName, cfmLimitSettingPath,
+ cfmLimitIface, "Limit",
+ static_cast<double>(cfm));
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: can't set cfm setting!",
+ phosphor::logging::entry("ERR=%s", e.what()));
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ return IPMI_CC_OK;
+ }
+ else
+ {
+ // todo other command parts possibly
+ // tcontrol is handled in peci now
+ // fan speed offset not implemented yet
+ // domain pwm limit not implemented
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+}
+
+ipmi_ret_t ipmiOEMGetFscParameter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+ ipmi_request_t request,
+ ipmi_response_t response,
+ ipmi_data_len_t dataLen,
+ ipmi_context_t context)
+{
+
+ if (*dataLen < 1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMGetFscParameter: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+
+ uint8_t* req = static_cast<uint8_t*>(request);
+
+ if (*req == static_cast<uint8_t>(setFscParamFlags::cfm))
+ {
+
+ /*
+ DataLen should be 1, but host is sending us an extra bit. As the
+ previous behavior didn't seem to prevent this, ignore the check for now.
+
+ if (*dataLen != 1)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMGetFscParameter: invalid input len!");
+ *dataLen = 0;
+ return IPMI_CC_REQ_DATA_LEN_INVALID;
+ }
+ */
+ Value cfmLimit;
+ Value cfmMaximum;
+ try
+ {
+ cfmLimit = ipmi::getDbusProperty(dbus, settingsBusName,
+ cfmLimitSettingPath, cfmLimitIface,
+ "Limit");
+ cfmMaximum = ipmi::getDbusProperty(
+ dbus, "xyz.openbmc_project.ExitAirTempSensor",
+ "/xyz/openbmc_project/control/MaxCFM", cfmLimitIface, "Limit");
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: can't get cfm setting!",
+ phosphor::logging::entry("ERR=%s", e.what()));
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ auto cfmLim = std::get_if<double>(&cfmLimit);
+ if (cfmLim == nullptr ||
+ *cfmLim > std::numeric_limits<uint16_t>::max() || *cfmLim < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: cfm limit out of range!");
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+
+ auto cfmMax = std::get_if<double>(&cfmMaximum);
+ if (cfmMax == nullptr ||
+ *cfmMax > std::numeric_limits<uint16_t>::max() || *cfmMax < 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "ipmiOEMSetFscParameter: cfm max out of range!");
+ *dataLen = 0;
+ return IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ *cfmLim = std::floor(*cfmLim + 0.5);
+ *cfmMax = std::floor(*cfmMax + 0.5);
+ uint16_t resp = static_cast<uint16_t>(*cfmLim);
+ uint16_t* ptr = static_cast<uint16_t*>(response);
+ ptr[0] = resp;
+ resp = static_cast<uint16_t>(*cfmMax);
+ ptr[1] = resp;
+
+ *dataLen = 4;
+
+ return IPMI_CC_OK;
+ }
+ else
+ {
+ // todo other command parts possibly
+ // tcontrol is handled in peci now
+ // fan speed offset not implemented yet
+ // domain pwm limit not implemented
+ *dataLen = 0;
+ return IPMI_CC_PARM_OUT_OF_RANGE;
+ }
+}
+
static void registerOEMFunctions(void)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
@@ -919,6 +1080,16 @@
static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetFanConfig),
NULL, ipmiOEMGetFanConfig, PRIVILEGE_USER);
+ ipmiPrintAndRegister(netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnIntelOEMGeneralCmd::cmdSetFscParameter),
+ NULL, ipmiOEMSetFscParameter, PRIVILEGE_USER);
+
+ ipmiPrintAndRegister(netfnIntcOEMGeneral,
+ static_cast<ipmi_cmd_t>(
+ IPMINetfnIntelOEMGeneralCmd::cmdGetFscParameter),
+ NULL, ipmiOEMGetFscParameter, PRIVILEGE_USER);
+
ipmiPrintAndRegister(
netfnIntcOEMGeneral,
static_cast<ipmi_cmd_t>(IPMINetfnIntelOEMGeneralCmd::cmdGetLEDStatus),