Add support for OEM Power Modes
- Allow mode to be set via PassThrough interface
- Allow non-customer OEM power modes to be persisted
- Persist any OEM power mode settings
- moved mode related code from Status to PowerMode object
- merged PowerIPS into PowerMode object
Tested on Everest and Rainier.
Setting mode through PassThrough/ce-login:
busctl call org.open_power.OCC.Control /org/open_power/control/occ0 org.open_power.OCC.PassThrough SetMode yq 11 3600
Trace (via PassThrough interface)
openpower-occ-control[4440]: PassThrough::setMode() Setting Power Mode 11 (data: 3600)
openpower-occ-control[4440]: PowerMode::sendModeChange: SET_MODE(11,3600) command to OCC0 (9 bytes)
Trace (setting mode via GUI/Redfish):
openpower-occ-control[4440]: Power Mode Change Requested: xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance
openpower-occ-control[4440]: PowerMode::sendModeChange: SET_MODE(12,0) command to OCC0 (9 bytes)
Verified when system in any OEM mode that Redfish also reports OEM
Verified all modes are persisted across PM Complex resets and reboots
Change-Id: Idd0be05cb6fd74dbd0776145f212c49addd1c365
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/meson.build b/meson.build
index 2f7a8f6..843a9a7 100644
--- a/meson.build
+++ b/meson.build
@@ -20,6 +20,8 @@
conf_data.set_quoted('OCC_NAME', 'occ')
conf_data.set_quoted('OCC_MASTER_NAME', 'occ-hwmon.1')
conf_data.set_quoted('OCC_DEV_PATH', '/dev/occ')
+conf_data.set_quoted('CPU_SUBPATH', '/xyz/openbmc_project/inventory/system/chassis/motherboard')
+conf_data.set_quoted('OCC_CONTROL_PERSIST_PATH', '/var/lib/openpower-occ-control')
conf_data.set('MAX_CPUS', get_option('max-cpus'))
conf_data.set('OCC_CPU_TEMP_SENSOR_TYPE', 0xC0)
diff --git a/occ_command.hpp b/occ_command.hpp
index ff8347b..aee4c58 100644
--- a/occ_command.hpp
+++ b/occ_command.hpp
@@ -45,10 +45,11 @@
enum class SysPwrMode
{
NO_CHANGE = 0,
- DISABLE = 0x01, // Disable / Static Base Frequencey
+ STATIC = 0x01, // Static Base Frequencey
SFP = 0x03, // Static Frequency Point (requires freqPt)
SAFE = 0x04, // reported when system is in SAFE mode (not settable)
POWER_SAVING = 0x05, // Static Power Saving
+ MAX_FREQ = 0x09, // Maximum Frequency (per chip)
DYNAMIC_PERF = 0x0A, // Dynamic / Balanced Performance
FFO = 0x0B, // Fixed Frequency Override (requires freqPt)
MAX_PERF = 0x0C // Maximum Performance
@@ -56,8 +57,11 @@
// Only some of the SysPwrModes are currently supported and allowed to be set
#define VALID_POWER_MODE_SETTING(mode) \
- ((mode == SysPwrMode::DISABLE) || (mode == SysPwrMode::POWER_SAVING) || \
+ ((mode == SysPwrMode::STATIC) || (mode == SysPwrMode::POWER_SAVING) || \
(mode == SysPwrMode::DYNAMIC_PERF) || (mode == SysPwrMode::MAX_PERF))
+#define VALID_OEM_POWER_MODE_SETTING(mode) \
+ ((mode == SysPwrMode::SFP) || (mode == SysPwrMode::FFO) || \
+ (mode == SysPwrMode::MAX_FREQ))
enum class RspStatus
{
diff --git a/occ_manager.cpp b/occ_manager.cpp
index 9b6f1c5..d24aa68 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -13,6 +13,7 @@
#include <chrono>
#include <cmath>
#include <filesystem>
+#include <fstream>
#include <regex>
namespace open_power
@@ -132,11 +133,11 @@
{
auto path = fs::path(OCC_CONTROL_ROOT) / occ;
- passThroughObjects.emplace_back(
- std::make_unique<PassThrough>(path.c_str()));
-
statusObjects.emplace_back(std::make_unique<Status>(
event, path.c_str(), *this,
+#ifdef POWER10
+ pmode,
+#endif
std::bind(std::mem_fn(&Manager::statusCallBack), this,
std::placeholders::_1)
#ifdef PLDM
@@ -146,27 +147,37 @@
#endif
));
- // Create the power cap monitor object for master occ (0)
- if (!pcap)
+ if (statusObjects.back()->isMasterOcc())
{
- pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
- *statusObjects.front());
- }
+ log<level::INFO>(
+ fmt::format("Manager::createObjects(): OCC{} is the master",
+ statusObjects.back()->getOccInstanceID())
+ .c_str());
+ _pollTimer->setEnabled(false);
+
+ // Create the power cap monitor object for master OCC
+ if (!pcap)
+ {
+ pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
+ *statusObjects.front());
+ }
#ifdef POWER10
- // Create the power mode monitor object for master occ (0)
- if (!pmode)
- {
- pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
- *statusObjects.front());
- }
- // Create the idle power saver monitor object for master occ (0)
- if (!pips)
- {
- pips = std::make_unique<open_power::occ::powermode::PowerIPS>(
- *statusObjects.front());
- }
+ // Create the power mode object for master OCC
+ if (!pmode)
+ {
+ pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
+ *this, path.c_str());
+ }
#endif
+ }
+
+ passThroughObjects.emplace_back(std::make_unique<PassThrough>(path.c_str()
+#ifdef POWER10
+ ,
+ pmode
+#endif
+ ));
}
void Manager::statusCallBack(bool status)
@@ -214,9 +225,8 @@
if (!_pollTimer->isEnabled())
{
log<level::INFO>(
- fmt::format(
- "Manager::statusCallBack(): {} OCCs will be polled every {} seconds",
- activeCount, pollInterval)
+ fmt::format("Manager: OCCs will be polled every {} seconds",
+ pollInterval)
.c_str());
// Send poll and start OCC poll timer
@@ -280,9 +290,7 @@
*statusObjects.front(), occMasterName);
#ifdef POWER10
pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
- *statusObjects.front());
- pips = std::make_unique<open_power::occ::powermode::PowerIPS>(
- *statusObjects.front());
+ *this, path.c_str());
#endif
}
#endif
@@ -1013,7 +1021,8 @@
else
{
log<level::INFO>(
- fmt::format("validateOccMaster: OCC{} is master", masterInstance)
+ fmt::format("validateOccMaster: OCC{} is master of {} OCCs",
+ masterInstance, activeCount)
.c_str());
}
}
diff --git a/occ_manager.hpp b/occ_manager.hpp
index e9a469c..9a29b8f 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -190,9 +190,6 @@
#ifdef POWER10
/** @brief Power mode monitor and notification object */
std::unique_ptr<open_power::occ::powermode::PowerMode> pmode;
-
- /** @brief Idle Power Saver monitor and notification object */
- std::unique_ptr<open_power::occ::powermode::PowerIPS> pips;
#endif
/** @brief sbdbusplus match objects */
diff --git a/occ_pass_through.cpp b/occ_pass_through.cpp
index db133b1..6cf6374 100644
--- a/occ_pass_through.cpp
+++ b/occ_pass_through.cpp
@@ -22,8 +22,21 @@
namespace occ
{
-PassThrough::PassThrough(const char* path) :
- Iface(utils::getBus(), path), path(path),
+using namespace phosphor::logging;
+using namespace sdbusplus::org::open_power::OCC::Device::Error;
+
+PassThrough::PassThrough(
+ const char* path
+#ifdef POWER10
+ ,
+ std::unique_ptr<open_power::occ::powermode::PowerMode>& powerModeRef
+#endif
+ ) :
+ Iface(utils::getBus(), path),
+ path(path),
+#ifdef POWER10
+ pmode(powerModeRef),
+#endif
devicePath(OCC_DEV_PATH + std::to_string((this->path.back() - '0') + 1)),
occInstance(this->path.back() - '0'),
activeStatusSignal(
@@ -60,9 +73,6 @@
std::vector<uint8_t> PassThrough::send(std::vector<uint8_t> command)
{
- using namespace phosphor::logging;
- using namespace sdbusplus::org::open_power::OCC::Device::Error;
-
std::vector<uint8_t> response{};
log<level::DEBUG>(
@@ -100,6 +110,48 @@
return response;
}
+bool PassThrough::setMode(const uint8_t mode, const uint16_t modeData)
+{
+#ifdef POWER10
+ SysPwrMode newMode = SysPwrMode(mode);
+
+ if ((!VALID_POWER_MODE_SETTING(newMode)) &&
+ (!VALID_OEM_POWER_MODE_SETTING(newMode)))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PassThrough::setMode() Unsupported mode {} requested (0x{:04X})",
+ newMode, modeData)
+ .c_str());
+ return false;
+ }
+
+ if (((newMode == SysPwrMode::FFO) || (newMode == SysPwrMode::SFP)) &&
+ (modeData == 0))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PassThrough::setMode() Mode {} requires non-zero frequency point.",
+ newMode)
+ .c_str());
+ return false;
+ }
+
+ log<level::INFO>(
+ fmt::format("PassThrough::setMode() Setting Power Mode {} (data: {})",
+ newMode, modeData)
+ .c_str());
+ return pmode->setMode(newMode, modeData);
+#else
+ log<level::DEBUG>(
+ fmt::format(
+ "PassThrough::setMode() No support to setting Power Mode {} (data: {})",
+ mode, modeData)
+ .c_str());
+ return false;
+#endif
+}
+
// Called at OCC Status change signal
void PassThrough::activeStatusEvent(sdbusplus::message::message& msg)
{
diff --git a/occ_pass_through.hpp b/occ_pass_through.hpp
index a1e1e91..52a8f71 100644
--- a/occ_pass_through.hpp
+++ b/occ_pass_through.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "occ_command.hpp"
+#include "powermode.hpp"
#include "utils.hpp"
#include <fmt/core.h>
@@ -39,7 +40,13 @@
/** @brief Ctor to put pass-through d-bus object on the bus
* @param[in] path - Path to attach at
*/
- explicit PassThrough(const char* path);
+ explicit PassThrough(
+ const char* path
+#ifdef POWER10
+ ,
+ std::unique_ptr<open_power::occ::powermode::PowerMode>& powerModeRef
+#endif
+ );
/** @brief Pass through command to OCC from dbus
* @param[in] command - command to pass-through
@@ -53,10 +60,24 @@
*/
std::vector<std::uint8_t> send(std::vector<std::uint8_t> command);
+ /** @brief Set a Power Mode
+ *
+ * @param[in] mode - desired System Power Mode
+ * @param[in] modeData - data associated some Power Modes
+ *
+ * @returns true if mode change was accepted
+ */
+ bool setMode(const uint8_t mode, const uint16_t modeData);
+
private:
/** @brief Pass-through occ path on the bus */
std::string path;
+#ifdef POWER10
+ /** @brief OCC PowerMode object */
+ std::unique_ptr<open_power::occ::powermode::PowerMode>& pmode;
+#endif
+
/** @brief OCC device path
* For now, here is the hard-coded mapping until
* the udev rule is in.
diff --git a/occ_status.cpp b/occ_status.cpp
index a2ab6ab..1da440c 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -46,13 +46,12 @@
}
else
{
- // Call into Manager to let know that we will unbind.
- if (this->callBack)
- {
- this->callBack(value);
- }
-
#ifdef POWER10
+ if (device.master())
+ {
+ // Prevent mode changes
+ pmode->setMasterActive(false);
+ }
if (safeStateDelayTimer.isEnabled())
{
// stop safe delay timer
@@ -60,6 +59,12 @@
}
#endif
+ // Call into Manager to let know that we will unbind.
+ if (this->callBack)
+ {
+ this->callBack(value);
+ }
+
// Stop watching for errors
removeErrorWatch();
@@ -103,6 +108,11 @@
// Callback handler when a device error is reported.
void Status::deviceError()
{
+#ifdef POWER10
+ // Prevent mode changes
+ pmode->setMasterActive(false);
+#endif
+
// This would deem OCC inactive
this->occActive(false);
@@ -196,6 +206,9 @@
{
if (device.master())
{
+ // Prevent mode changes
+ pmode->setMasterActive();
+
// Special processing by master OCC when it goes active
occsWentActive();
}
@@ -238,189 +251,12 @@
}
#ifdef POWER10
-// Check if Hypervisor target is PowerVM
-bool Status::isPowerVM()
-{
- using namespace open_power::occ::powermode;
- namespace Hyper = sdbusplus::com::ibm::Host::server;
- constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
- constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
- constexpr auto HYPE_PROP = "Target";
-
- bool powerVmTarget = false;
-
- // This will throw exception on failure
- auto& bus = utils::getBus();
- auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
- auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
- "org.freedesktop.DBus.Properties", "Get");
- method.append(HYPE_INTERFACE, HYPE_PROP);
- auto reply = bus.call(method);
-
- std::variant<std::string> hyperEntryValue;
- reply.read(hyperEntryValue);
- auto propVal = std::get<std::string>(hyperEntryValue);
- if (Hyper::Target::convertHypervisorFromString(propVal) ==
- Hyper::Target::Hypervisor::PowerVM)
- {
- powerVmTarget = true;
- }
-
- log<level::DEBUG>(
- fmt::format("Status::isPowerVM returning {}", powerVmTarget).c_str());
-
- return powerVmTarget;
-}
-
-// Get the requested power mode
-SysPwrMode Status::getMode()
-{
- using namespace open_power::occ::powermode;
- SysPwrMode pmode = SysPwrMode::NO_CHANGE;
-
- // This will throw exception on failure
- auto& bus = utils::getBus();
- auto service = utils::getService(PMODE_PATH, PMODE_INTERFACE);
- auto method = bus.new_method_call(service.c_str(), PMODE_PATH,
- "org.freedesktop.DBus.Properties", "Get");
- method.append(PMODE_INTERFACE, POWER_MODE_PROP);
- auto reply = bus.call(method);
-
- std::variant<std::string> stateEntryValue;
- reply.read(stateEntryValue);
- auto propVal = std::get<std::string>(stateEntryValue);
- pmode = powermode::convertStringToMode(propVal);
-
- log<level::DEBUG>(
- fmt::format("Status::getMode returning {}", pmode).c_str());
-
- return pmode;
-}
-
-// Get the requested power mode
-bool Status::getIPSParms(uint8_t& enterUtil, uint16_t& enterTime,
- uint8_t& exitUtil, uint16_t& exitTime)
-{
- using namespace open_power::occ::powermode;
- // Defaults:
- bool ipsEnabled = false; // Disabled
- enterUtil = 8; // Enter Utilization (8%)
- enterTime = 240; // Enter Delay Time (240s)
- exitUtil = 12; // Exit Utilization (12%)
- exitTime = 10; // Exit Delay Time (10s)
-
- std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
- ipsProperties{};
-
- // Get all IPS properties from DBus
- try
- {
- auto& bus = utils::getBus();
- auto service = utils::getService(PIPS_PATH, PIPS_INTERFACE);
- auto method =
- bus.new_method_call(service.c_str(), PIPS_PATH,
- "org.freedesktop.DBus.Properties", "GetAll");
- method.append(PIPS_INTERFACE);
- auto reply = bus.call(method);
- reply.read(ipsProperties);
- }
- catch (const sdbusplus::exception::exception& e)
- {
- log<level::ERR>(
- fmt::format(
- "Unable to read Idle Power Saver parameters so it will be disabled: {}",
- e.what())
- .c_str());
- return ipsEnabled;
- }
-
- auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
- if (ipsEntry != ipsProperties.end())
- {
- ipsEnabled = std::get<bool>(ipsEntry->second);
- }
- else
- {
- log<level::ERR>(
- fmt::format("Status::getIPSParms could not find property: {}",
- IPS_ENABLED_PROP)
- .c_str());
- }
-
- ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
- if (ipsEntry != ipsProperties.end())
- {
- enterUtil = std::get<uint8_t>(ipsEntry->second);
- }
- else
- {
- log<level::ERR>(
- fmt::format("Status::getIPSParms could not find property: {}",
- IPS_ENTER_UTIL)
- .c_str());
- }
-
- ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
- if (ipsEntry != ipsProperties.end())
- {
- std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
- enterTime =
- std::chrono::duration_cast<std::chrono::seconds>(ms).count();
- }
- else
- {
- log<level::ERR>(
- fmt::format("Status::getIPSParms could not find property: {}",
- IPS_ENTER_TIME)
- .c_str());
- }
-
- ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
- if (ipsEntry != ipsProperties.end())
- {
- exitUtil = std::get<uint8_t>(ipsEntry->second);
- }
- else
- {
- log<level::ERR>(
- fmt::format("Status::getIPSParms could not find property: {}",
- IPS_EXIT_UTIL)
- .c_str());
- }
-
- ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
- if (ipsEntry != ipsProperties.end())
- {
- std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
- exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
- }
- else
- {
- log<level::ERR>(
- fmt::format("Status::getIPSParms could not find property: {}",
- IPS_EXIT_TIME)
- .c_str());
- }
-
- if (enterUtil > exitUtil)
- {
- log<level::ERR>(
- fmt::format(
- "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
- enterUtil, exitUtil)
- .c_str());
- enterUtil = exitUtil;
- }
-
- return ipsEnabled;
-}
-
// Special processing that needs to happen once the OCCs change to ACTIVE state
void Status::occsWentActive()
{
CmdStatus status = CmdStatus::SUCCESS;
- status = sendModeChange();
+ status = pmode->sendModeChange();
if (status != CmdStatus::SUCCESS)
{
log<level::ERR>(
@@ -430,7 +266,7 @@
.c_str());
}
- status = sendIpsData();
+ status = pmode->sendIpsData();
if (status != CmdStatus::SUCCESS)
{
log<level::ERR>(
@@ -441,189 +277,6 @@
}
}
-// Send mode change request to the master OCC
-CmdStatus Status::sendModeChange()
-{
- CmdStatus status = CmdStatus::FAILURE;
-
- if (!device.master())
- {
- log<level::ERR>(
- fmt::format(
- "Status::sendModeChange: MODE CHANGE does not get sent to slave OCC{}",
- instance)
- .c_str());
- return status;
- }
- if (!isPowerVM())
- {
- // Mode change is only supported on PowerVM systems
- log<level::DEBUG>(
- "Status::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
- return CmdStatus::SUCCESS;
- }
-
- const SysPwrMode newMode = getMode();
-
- if (VALID_POWER_MODE_SETTING(newMode))
- {
- std::vector<std::uint8_t> cmd, rsp;
- cmd.reserve(9);
- cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
- cmd.push_back(0x00); // Data Length (2 bytes)
- cmd.push_back(0x06);
- cmd.push_back(0x30); // Data (Version)
- cmd.push_back(uint8_t(OccState::NO_CHANGE));
- cmd.push_back(uint8_t(newMode));
- cmd.push_back(0x00); // Mode Data (Freq Point)
- cmd.push_back(0x00); //
- cmd.push_back(0x00); // reserved
- log<level::INFO>(
- fmt::format(
- "Status::sendModeChange: SET_MODE({}) command to OCC{} ({} bytes)",
- newMode, instance, cmd.size())
- .c_str());
- status = occCmd.send(cmd, rsp);
- if (status == CmdStatus::SUCCESS)
- {
- if (rsp.size() == 5)
- {
- if (RspStatus::SUCCESS != RspStatus(rsp[2]))
- {
- log<level::ERR>(
- fmt::format(
- "Status::sendModeChange: SET MODE failed with status 0x{:02X}",
- rsp[2])
- .c_str());
- dump_hex(rsp);
- status = CmdStatus::FAILURE;
- }
- }
- else
- {
- log<level::ERR>(
- "Status::sendModeChange: INVALID SET MODE response");
- dump_hex(rsp);
- status = CmdStatus::FAILURE;
- }
- }
- else
- {
- if (status == CmdStatus::OPEN_FAILURE)
- {
- log<level::INFO>("Status::sendModeChange: OCC not active yet");
- status = CmdStatus::SUCCESS;
- }
- else
- {
- log<level::ERR>("Status::sendModeChange: SET_MODE FAILED!");
- }
- }
- }
- else
- {
- log<level::ERR>(
- fmt::format(
- "Status::sendModeChange: Unable to set power mode to {}",
- newMode)
- .c_str());
- status = CmdStatus::FAILURE;
- }
-
- return status;
-}
-
-// Send Idle Power Saver config data to the master OCC
-CmdStatus Status::sendIpsData()
-{
- CmdStatus status = CmdStatus::FAILURE;
-
- if (!device.master())
- {
- log<level::ERR>(
- fmt::format(
- "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent to slave OCC{}",
- instance)
- .c_str());
- return status;
- }
- if (!isPowerVM())
- {
- // Idle Power Saver data is only supported on PowerVM systems
- log<level::DEBUG>(
- "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
- return CmdStatus::SUCCESS;
- }
-
- uint8_t enterUtil, exitUtil;
- uint16_t enterTime, exitTime;
- const bool ipsEnabled =
- getIPSParms(enterUtil, enterTime, exitUtil, exitTime);
-
- log<level::INFO>(
- fmt::format(
- "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
- ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
- .c_str());
-
- std::vector<std::uint8_t> cmd, rsp;
- cmd.reserve(12);
- cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
- cmd.push_back(0x00); // Data Length (2 bytes)
- cmd.push_back(0x09); //
- cmd.push_back(0x11); // Config Format: IPS Settings
- cmd.push_back(0x00); // Version
- cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
- cmd.push_back(enterTime >> 8); // Enter Delay Time
- cmd.push_back(enterTime & 0xFF); //
- cmd.push_back(enterUtil); // Enter Utilization
- cmd.push_back(exitTime >> 8); // Exit Delay Time
- cmd.push_back(exitTime & 0xFF); //
- cmd.push_back(exitUtil); // Exit Utilization
- log<level::INFO>(fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] "
- "command to OCC{} ({} bytes)",
- instance, cmd.size())
- .c_str());
- status = occCmd.send(cmd, rsp);
- if (status == CmdStatus::SUCCESS)
- {
- if (rsp.size() == 5)
- {
- if (RspStatus::SUCCESS != RspStatus(rsp[2]))
- {
- log<level::ERR>(
- fmt::format(
- "Status::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
- rsp[2])
- .c_str());
- dump_hex(rsp);
- status = CmdStatus::FAILURE;
- }
- }
- else
- {
- log<level::ERR>(
- "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
- dump_hex(rsp);
- status = CmdStatus::FAILURE;
- }
- }
- else
- {
- if (status == CmdStatus::OPEN_FAILURE)
- {
- log<level::INFO>("Status::sendIpsData: OCC not active yet");
- status = CmdStatus::SUCCESS;
- }
- else
- {
- log<level::ERR>("Status::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
- }
- }
-
- return status;
-}
-
// Send Ambient and Altitude to the OCC
CmdStatus Status::sendAmbient(const uint8_t inTemp, const uint16_t inAltitude)
{
diff --git a/occ_status.hpp b/occ_status.hpp
index e8e4530..32041c7 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -5,6 +5,7 @@
#include "occ_command.hpp"
#include "occ_device.hpp"
#include "occ_events.hpp"
+#include "powermode.hpp"
#include "utils.hpp"
#include <org/open_power/Control/Host/server.hpp>
@@ -74,6 +75,9 @@
* protocol
*/
Status(EventPtr& event, const char* path, Manager& managerRef,
+#ifdef POWER10
+ std::unique_ptr<open_power::occ::powermode::PowerMode>& powerModeRef,
+#endif
std::function<void(bool)> callBack = nullptr
#ifdef PLDM
,
@@ -84,6 +88,9 @@
Interface(utils::getBus(), getDbusPath(path).c_str(), true),
path(path), callBack(callBack), instance(getInstance(path)),
manager(managerRef),
+#ifdef POWER10
+ pmode(powerModeRef),
+#endif
device(event,
#ifdef I2C_OCC
fs::path(DEV_PATH) / i2c_occ::getI2cDeviceName(path),
@@ -180,16 +187,6 @@
/** @brief Handle additional tasks when the OCCs reach active state */
void occsWentActive();
- /** @brief Send mode change command to the master OCC
- * @return SUCCESS on success
- */
- CmdStatus sendModeChange();
-
- /** @brief Send Idle Power Saver config data to the master OCC
- * @return SUCCESS on success
- */
- CmdStatus sendIpsData();
-
/** @brief Send Ambient & Altitude data to OCC
*
* @param[in] ambient - temperature to send (0xFF will force read
@@ -223,6 +220,11 @@
/** @brief OCC manager object */
const Manager& manager;
+#ifdef POWER10
+ /** @brief OCC PowerMode object */
+ std::unique_ptr<open_power::occ::powermode::PowerMode>& pmode;
+#endif
+
/** @brief OCC device object to do bind and unbind */
Device device;
@@ -262,22 +264,6 @@
}
#ifdef POWER10
- /** @brief Query the current Hypervisor target
- * @return true if the current Hypervisor target is PowerVM
- */
- bool isPowerVM();
-
- /** @brief Get the requested power mode property
- * @return Power mode
- */
- SysPwrMode getMode();
-
- /** @brief Get the Idle Power Saver properties
- * @return true if IPS is enabled
- */
- bool getIPSParms(uint8_t& enterUtil, uint16_t& enterTime, uint8_t& exitUtil,
- uint16_t& exitTime);
-
/**
* @brief Timer that is started when OCC is detected to be in safe mode
*/
diff --git a/powermode.cpp b/powermode.cpp
index e87b921..04a9787 100644
--- a/powermode.cpp
+++ b/powermode.cpp
@@ -1,10 +1,13 @@
+#include "powermode.hpp"
+
#include <fmt/core.h>
+#include <com/ibm/Host/Target/server.hpp>
#include <phosphor-logging/log.hpp>
-#include <powermode.hpp>
#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
#include <cassert>
+#include <fstream>
#include <regex>
namespace open_power
@@ -17,13 +20,10 @@
using namespace phosphor::logging;
using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
+// Called when DBus power mode gets changed
void PowerMode::modeChanged(sdbusplus::message::message& msg)
{
- if (!occStatus.occActive())
- {
- // Nothing to do
- return;
- }
+ SysPwrMode newMode = SysPwrMode::NO_CHANGE;
std::map<std::string, std::variant<std::string>> properties{};
std::string interface;
@@ -34,22 +34,51 @@
{
auto modeEntryValue = modeEntry->second;
propVal = std::get<std::string>(modeEntryValue);
-
- if (convertStringToMode(propVal) != SysPwrMode::NO_CHANGE)
+ newMode = convertStringToMode(propVal);
+ if (newMode != SysPwrMode::NO_CHANGE)
{
+ // DBus mode changed, get rid of any OEM mode if set
+ persistedData.purge();
+
log<level::INFO>(
fmt::format("Power Mode Change Requested: {}", propVal)
.c_str());
- // Trigger mode change to OCC
- occStatus.sendModeChange();
+ // Send mode change to OCC
+ sendModeChange();
+ }
+ }
+}
+
+// Called from OCC PassThrough interface (via CE login / BMC command line)
+bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t modeData)
+{
+ if (updateDbusMode(newMode) == false)
+ {
+ // Unsupported mode
+ return false;
+ }
+
+ // If new mode is valid customer mode, the DBus update will trigger the mode
+ // change request to OCC. For OEM modes, the request will be sent here.
+ if (VALID_OEM_POWER_MODE_SETTING(newMode))
+ {
+ // Save OEM mode
+ persistedData.writeModeFile(newMode, modeData);
+
+ // Send mode change to OCC
+ if (sendModeChange() != CmdStatus::SUCCESS)
+ {
+ // Mode change failed
+ return false;
}
}
- return;
+ return true;
}
// Convert PowerMode string to OCC SysPwrMode
+// Returns NO_CHANGE if OEM or unsupported mode
SysPwrMode convertStringToMode(const std::string& i_modeString)
{
SysPwrMode pmode = SysPwrMode::NO_CHANGE;
@@ -65,22 +94,230 @@
}
else if (mode == Mode::PowerMode::Static)
{
- pmode = SysPwrMode::DISABLE;
+ pmode = SysPwrMode::STATIC;
}
else
{
- log<level::ERR>(
- fmt::format("convertStringToMode: Invalid Power Mode specified: {}",
- i_modeString)
- .c_str());
+ if (mode != Mode::PowerMode::OEM)
+ {
+ log<level::ERR>(
+ fmt::format(
+ "convertStringToMode: Invalid Power Mode specified: {}",
+ i_modeString)
+ .c_str());
+ }
}
return pmode;
}
-void PowerIPS::ipsChanged(sdbusplus::message::message& msg)
+// Check if Hypervisor target is PowerVM
+bool isPowerVM()
{
- if (!occStatus.occActive())
+ using namespace open_power::occ::powermode;
+ namespace Hyper = sdbusplus::com::ibm::Host::server;
+ constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
+ constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
+ constexpr auto HYPE_PROP = "Target";
+
+ bool powerVmTarget = false;
+
+ // This will throw exception on failure
+ auto& bus = utils::getBus();
+ auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
+ auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(HYPE_INTERFACE, HYPE_PROP);
+ auto reply = bus.call(method);
+
+ std::variant<std::string> hyperEntryValue;
+ reply.read(hyperEntryValue);
+ auto propVal = std::get<std::string>(hyperEntryValue);
+ if (Hyper::Target::convertHypervisorFromString(propVal) ==
+ Hyper::Target::Hypervisor::PowerVM)
+ {
+ powerVmTarget = true;
+ }
+
+ log<level::DEBUG>(
+ fmt::format("isPowerVM returning {}", powerVmTarget).c_str());
+
+ return powerVmTarget;
+}
+
+// Get the requested power mode from DBus
+SysPwrMode PowerMode::getDbusMode()
+{
+ using namespace open_power::occ::powermode;
+ SysPwrMode currentMode = SysPwrMode::NO_CHANGE;
+
+ // This will throw exception on failure
+ auto& bus = utils::getBus();
+ auto service = utils::getService(PMODE_PATH, PMODE_INTERFACE);
+ auto method = bus.new_method_call(service.c_str(), PMODE_PATH,
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(PMODE_INTERFACE, POWER_MODE_PROP);
+ auto reply = bus.call(method);
+
+ std::variant<std::string> stateEntryValue;
+ reply.read(stateEntryValue);
+ auto propVal = std::get<std::string>(stateEntryValue);
+
+ currentMode = powermode::convertStringToMode(propVal);
+ if (!VALID_POWER_MODE_SETTING(currentMode))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PowerMode::getDbusMode Invalid power mode found on DBus: {}",
+ currentMode)
+ .c_str());
+ currentMode = SysPwrMode::NO_CHANGE;
+ }
+
+ return currentMode;
+}
+
+// Set the power mode on DBus
+bool PowerMode::updateDbusMode(const SysPwrMode newMode)
+{
+ using namespace open_power::occ::powermode;
+ using namespace std::literals::string_literals;
+
+ if (!VALID_POWER_MODE_SETTING(newMode) &&
+ !VALID_OEM_POWER_MODE_SETTING(newMode))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PowerMode::updateDbusMode - Requested power mode not supported: {}",
+ newMode)
+ .c_str());
+ return false;
+ }
+
+ // Mode::PowerMode dBusMode;
+ std::string dBusMode;
+ switch (newMode)
+ {
+ case SysPwrMode::STATIC:
+ dBusMode = PMODE_INTERFACE + ".PowerMode.Static"s;
+ break;
+ case SysPwrMode::POWER_SAVING:
+ dBusMode = PMODE_INTERFACE + ".PowerMode.PowerSaving"s;
+ break;
+ case SysPwrMode::MAX_PERF:
+ dBusMode = PMODE_INTERFACE + ".PowerMode.MaximumPerformance"s;
+ break;
+ default:
+ dBusMode = PMODE_INTERFACE + ".PowerMode.OEM"s;
+ }
+
+ utils::setProperty(PMODE_PATH, PMODE_INTERFACE, POWER_MODE_PROP,
+ std::move(dBusMode));
+
+ return true;
+}
+
+// Send mode change request to the master OCC
+CmdStatus PowerMode::sendModeChange()
+{
+ CmdStatus status = CmdStatus::FAILURE;
+
+ if (!masterActive)
+ {
+ // Nothing to do
+ log<level::DEBUG>("PowerMode::sendModeChange: MODE CHANGE not enabled");
+ return CmdStatus::SUCCESS;
+ }
+
+ if (!isPowerVM())
+ {
+ // Mode change is only supported on PowerVM systems
+ log<level::DEBUG>(
+ "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
+ return CmdStatus::SUCCESS;
+ }
+
+ // Use OEM power mode if it was set
+ SysPwrMode newMode = SysPwrMode::NO_CHANGE;
+ uint16_t modeData = 0;
+ if (persistedData.getOemMode(newMode, modeData) == false)
+ {
+ // Read customer power mode from Dbus
+ newMode = getDbusMode();
+ }
+
+ if (VALID_POWER_MODE_SETTING(newMode) ||
+ VALID_OEM_POWER_MODE_SETTING(newMode))
+ {
+ std::vector<std::uint8_t> cmd, rsp;
+ cmd.reserve(9);
+ cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
+ cmd.push_back(0x00); // Data Length (2 bytes)
+ cmd.push_back(0x06);
+ cmd.push_back(0x30); // Data (Version)
+ cmd.push_back(uint8_t(OccState::NO_CHANGE));
+ cmd.push_back(uint8_t(newMode));
+ cmd.push_back(modeData >> 8); // Mode Data (Freq Point)
+ cmd.push_back(modeData & 0xFF); //
+ cmd.push_back(0x00); // reserved
+ log<level::INFO>(
+ fmt::format(
+ "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
+ newMode, modeData, occInstance, cmd.size())
+ .c_str());
+ status = occCmd.send(cmd, rsp);
+ if (status == CmdStatus::SUCCESS)
+ {
+ if (rsp.size() == 5)
+ {
+ if (RspStatus::SUCCESS != RspStatus(rsp[2]))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
+ rsp[2])
+ .c_str());
+ dump_hex(rsp);
+ status = CmdStatus::FAILURE;
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ "PowerMode::sendModeChange: INVALID SET MODE response");
+ dump_hex(rsp);
+ status = CmdStatus::FAILURE;
+ }
+ }
+ else
+ {
+ if (status == CmdStatus::OPEN_FAILURE)
+ {
+ // OCC not active yet
+ status = CmdStatus::SUCCESS;
+ }
+ else
+ {
+ log<level::ERR>("PowerMode::sendModeChange: SET_MODE FAILED!");
+ }
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PowerMode::sendModeChange: Unable to set power mode to {}",
+ newMode)
+ .c_str());
+ status = CmdStatus::FAILURE;
+ }
+
+ return status;
+}
+
+void PowerMode::ipsChanged(sdbusplus::message::message& msg)
+{
+ if (!masterActive)
{
// Nothing to do
return;
@@ -145,12 +382,308 @@
if (parmsChanged)
{
// Trigger mode change to OCC
- occStatus.sendIpsData();
+ sendIpsData();
}
return;
}
+/** @brief Get the Idle Power Saver properties
+ * @return true if IPS is enabled
+ */
+bool PowerMode::getIPSParms(uint8_t& enterUtil, uint16_t& enterTime,
+ uint8_t& exitUtil, uint16_t& exitTime)
+{
+ using namespace open_power::occ::powermode;
+ // Defaults:
+ bool ipsEnabled = false; // Disabled
+ enterUtil = 8; // Enter Utilization (8%)
+ enterTime = 240; // Enter Delay Time (240s)
+ exitUtil = 12; // Exit Utilization (12%)
+ exitTime = 10; // Exit Delay Time (10s)
+
+ std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
+ ipsProperties{};
+
+ // Get all IPS properties from DBus
+ try
+ {
+ auto& bus = utils::getBus();
+ auto service = utils::getService(PIPS_PATH, PIPS_INTERFACE);
+ auto method =
+ bus.new_method_call(service.c_str(), PIPS_PATH,
+ "org.freedesktop.DBus.Properties", "GetAll");
+ method.append(PIPS_INTERFACE);
+ auto reply = bus.call(method);
+ reply.read(ipsProperties);
+ }
+ catch (const sdbusplus::exception::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format(
+ "Unable to read Idle Power Saver parameters so it will be disabled: {}",
+ e.what())
+ .c_str());
+ return ipsEnabled;
+ }
+
+ auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
+ if (ipsEntry != ipsProperties.end())
+ {
+ ipsEnabled = std::get<bool>(ipsEntry->second);
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("PowerMode::getIPSParms could not find property: {}",
+ IPS_ENABLED_PROP)
+ .c_str());
+ }
+
+ ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
+ if (ipsEntry != ipsProperties.end())
+ {
+ enterUtil = std::get<uint8_t>(ipsEntry->second);
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("PowerMode::getIPSParms could not find property: {}",
+ IPS_ENTER_UTIL)
+ .c_str());
+ }
+
+ ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
+ if (ipsEntry != ipsProperties.end())
+ {
+ std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
+ enterTime =
+ std::chrono::duration_cast<std::chrono::seconds>(ms).count();
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("PowerMode::getIPSParms could not find property: {}",
+ IPS_ENTER_TIME)
+ .c_str());
+ }
+
+ ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
+ if (ipsEntry != ipsProperties.end())
+ {
+ exitUtil = std::get<uint8_t>(ipsEntry->second);
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("PowerMode::getIPSParms could not find property: {}",
+ IPS_EXIT_UTIL)
+ .c_str());
+ }
+
+ ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
+ if (ipsEntry != ipsProperties.end())
+ {
+ std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
+ exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("PowerMode::getIPSParms could not find property: {}",
+ IPS_EXIT_TIME)
+ .c_str());
+ }
+
+ if (enterUtil > exitUtil)
+ {
+ log<level::ERR>(
+ fmt::format(
+ "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
+ enterUtil, exitUtil)
+ .c_str());
+ enterUtil = exitUtil;
+ }
+
+ return ipsEnabled;
+}
+
+// Send Idle Power Saver config data to the master OCC
+CmdStatus PowerMode::sendIpsData()
+{
+ CmdStatus status = CmdStatus::FAILURE;
+
+ if (!masterActive)
+ {
+ // Nothing to do
+ return CmdStatus::SUCCESS;
+ }
+
+ if (!isPowerVM())
+ {
+ // Idle Power Saver data is only supported on PowerVM systems
+ log<level::DEBUG>(
+ "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
+ return CmdStatus::SUCCESS;
+ }
+
+ uint8_t enterUtil, exitUtil;
+ uint16_t enterTime, exitTime;
+ const bool ipsEnabled =
+ getIPSParms(enterUtil, enterTime, exitUtil, exitTime);
+
+ log<level::INFO>(
+ fmt::format(
+ "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
+ ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
+ .c_str());
+
+ std::vector<std::uint8_t> cmd, rsp;
+ cmd.reserve(12);
+ cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
+ cmd.push_back(0x00); // Data Length (2 bytes)
+ cmd.push_back(0x09); //
+ cmd.push_back(0x11); // Config Format: IPS Settings
+ cmd.push_back(0x00); // Version
+ cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
+ cmd.push_back(enterTime >> 8); // Enter Delay Time
+ cmd.push_back(enterTime & 0xFF); //
+ cmd.push_back(enterUtil); // Enter Utilization
+ cmd.push_back(exitTime >> 8); // Exit Delay Time
+ cmd.push_back(exitTime & 0xFF); //
+ cmd.push_back(exitUtil); // Exit Utilization
+ log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
+ "command to OCC{} ({} bytes)",
+ occInstance, cmd.size())
+ .c_str());
+ status = occCmd.send(cmd, rsp);
+ if (status == CmdStatus::SUCCESS)
+ {
+ if (rsp.size() == 5)
+ {
+ if (RspStatus::SUCCESS != RspStatus(rsp[2]))
+ {
+ log<level::ERR>(
+ fmt::format(
+ "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
+ rsp[2])
+ .c_str());
+ dump_hex(rsp);
+ status = CmdStatus::FAILURE;
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
+ dump_hex(rsp);
+ status = CmdStatus::FAILURE;
+ }
+ }
+ else
+ {
+ if (status == CmdStatus::OPEN_FAILURE)
+ {
+ // OCC not active yet
+ status = CmdStatus::SUCCESS;
+ }
+ else
+ {
+ log<level::ERR>(
+ "PowerMode::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
+ }
+ }
+
+ return status;
+}
+
+inline void OccPersistData::print()
+{
+ log<level::DEBUG>(
+ fmt::format(
+ "OccPersistData: OEM Mode: 0x{:02X}, OEM Mode Freq: {} (0x{:04X})",
+ oemData.oemMode, oemData.oemModeFreq, oemData.oemModeFreq)
+ .c_str());
+}
+
+// Saves the OEM mode data in the filesystem using cereal.
+void OccPersistData::save()
+{
+ std::filesystem::path opath =
+ std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / oemModeFilename;
+
+ if (!std::filesystem::exists(opath.parent_path()))
+ {
+ std::filesystem::create_directory(opath.parent_path());
+ }
+
+ log<level::DEBUG>(
+ fmt::format("OccPersistData::save: Writing OEM persisted data to {}",
+ opath.c_str())
+ .c_str());
+ print();
+
+ std::ofstream stream{opath.c_str()};
+ cereal::JSONOutputArchive oarchive{stream};
+
+ oarchive(oemData);
+}
+
+// Loads the OEM mode data in the filesystem using cereal.
+void OccPersistData::load()
+{
+
+ std::filesystem::path ipath =
+ std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / oemModeFilename;
+
+ if (!std::filesystem::exists(ipath))
+ {
+ return;
+ }
+
+ log<level::DEBUG>(
+ fmt::format("OccPersistData::load: Reading OEM persisted data from {}",
+ ipath.c_str())
+ .c_str());
+ try
+ {
+ std::ifstream stream{ipath.c_str()};
+ cereal::JSONInputArchive iarchive(stream);
+ iarchive(oemData);
+
+ oemSet = true;
+ }
+ catch (const std::exception& e)
+ {
+ auto error = errno;
+ log<level::ERR>(
+ fmt::format("OccPersistData::load: failed to read {}, errno={}",
+ ipath.c_str(), error)
+ .c_str());
+ }
+
+ print();
+}
+
+void OccPersistData::purge()
+{
+ std::filesystem::path opath =
+ std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / oemModeFilename;
+
+ if (!std::filesystem::exists(opath))
+ {
+ return;
+ }
+
+ print();
+ log<level::DEBUG>("OccPersistData::purge() Removing OEM data");
+
+ oemSet = false;
+ oemData.oemMode = SysPwrMode::NO_CHANGE;
+ oemData.oemModeFreq = 0x0000;
+ remove(opath.c_str());
+}
+
} // namespace powermode
} // namespace occ
diff --git a/powermode.hpp b/powermode.hpp
index 0d337dc..5342c77 100644
--- a/powermode.hpp
+++ b/powermode.hpp
@@ -3,8 +3,14 @@
#include "config.h"
#ifdef POWER10
-#include "occ_status.hpp"
+#include "occ_command.hpp"
+#include <cereal/archives/json.hpp>
+//#include <cereal/archives/binary.hpp>
+#include <cereal/cereal.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/types/tuple.hpp>
+#include <cereal/types/vector.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
@@ -14,6 +20,9 @@
{
namespace occ
{
+
+class Manager;
+
namespace powermode
{
@@ -30,6 +39,11 @@
constexpr auto IPS_EXIT_UTIL = "ExitUtilizationPercent";
constexpr auto IPS_EXIT_TIME = "ExitDwellTime";
+/** @brief Query the current Hypervisor target
+ * @return true if the current Hypervisor target is PowerVM
+ */
+bool isPowerVM();
+
/** @brief Convert power mode string to OCC SysPwrMode value
*
* @param[in] i_modeString - power mode string
@@ -38,6 +52,93 @@
*/
SysPwrMode convertStringToMode(const std::string& i_modeString);
+struct OemModeData
+{
+ SysPwrMode oemMode = SysPwrMode::NO_CHANGE;
+ uint16_t oemModeFreq = 0x0000;
+
+ /** @brief Function specifying data to archive for cereal.
+ */
+ template <class Archive>
+ void serialize(Archive& archive)
+ {
+ archive(oemMode, oemModeFreq);
+ }
+};
+
+/** @class OccPersistData
+ * @brief Provides persistent container to store data for OCC
+ *
+ * Data is stored via cereal
+ */
+class OccPersistData
+{
+ public:
+ ~OccPersistData() = default;
+ OccPersistData(const OccPersistData&) = default;
+ OccPersistData& operator=(const OccPersistData&) = default;
+ OccPersistData(OccPersistData&&) = default;
+ OccPersistData& operator=(OccPersistData&&) = default;
+
+ /** @brief Loads any saved OEM mode data */
+ OccPersistData()
+ {
+ load();
+ }
+
+ /** @brief Save Power Mode data to persistent file
+ *
+ * @param[in] newMode - desired OEM Power Mode
+ * @param[in] modeData - data required by some OEM Power Modes
+ */
+ void writeModeFile(const SysPwrMode newMode, const uint16_t modeData)
+ {
+ oemData.oemMode = newMode;
+ oemData.oemModeFreq = modeData;
+ oemSet = true;
+ save();
+ }
+
+ /** @brief Return the OEM Power Mode and frequency if enabled
+ *
+ * @param[out] newMode - OEM mode (if set, else data not changed)
+ * @param[out] oemFreq - Frequency data for OEM mode
+ *
+ * @returns true if OEM mode was set
+ */
+ bool getOemMode(SysPwrMode& mode, uint16_t& freq) const
+ {
+ if (!oemSet)
+ {
+ return false;
+ }
+
+ mode = oemData.oemMode;
+ freq = oemData.oemModeFreq;
+ return true;
+ }
+
+ /** @brief Saves the Power Mode data in the filesystem using cereal. */
+ void save();
+
+ /** @brief Removes the OEM mode data. */
+ void purge();
+
+ inline void print();
+
+ private:
+ static constexpr auto oemModeFilename = "oemModeData";
+
+ /** @brief true if an OEM Power Mode was set */
+ bool oemSet = false;
+
+ /** @brief OEM Power Mode data */
+ OemModeData oemData;
+
+ /** @brief Loads the OEM mode data in the filesystem using cereal. */
+ void load();
+};
+
/** @class PowerMode
* @brief Monitors for changes to the power mode and notifies occ
*
@@ -55,16 +156,67 @@
* If a change is detected, and the occ is active, then this object will
* notify the OCC of the change.
*
- * @param[in] occStatus - The occ status object
+ * @param[in] managerRef -
+ * @param[in] path -
*/
- explicit PowerMode(Status& occStatus) :
- occStatus(occStatus),
+ explicit PowerMode(Manager& managerRef, const char* path) :
+ manager(managerRef), path(path), occInstance(this->path.back() - '0'),
+ occCmd(occInstance, path),
pmodeMatch(utils::getBus(),
sdbusplus::bus::match::rules::propertiesChanged(
PMODE_PATH, PMODE_INTERFACE),
- [this](auto& msg) { this->modeChanged(msg); }){};
+ [this](auto& msg) { this->modeChanged(msg); }),
+ ipsMatch(utils::getBus(),
+ sdbusplus::bus::match::rules::propertiesChanged(
+ PIPS_PATH, PIPS_INTERFACE),
+ [this](auto& msg) { this->ipsChanged(msg); }),
+ masterActive(false){};
+
+ bool setMode(const SysPwrMode newMode, const uint16_t modedata);
+
+ /** @brief Send mode change command to the master OCC
+ * @return SUCCESS on success
+ */
+ CmdStatus sendModeChange();
+
+ /** @brief Send Idle Power Saver config data to the master OCC
+ * @return SUCCESS on success
+ */
+ CmdStatus sendIpsData();
+
+ /** @brief Notify object of master OCC state. If not acitve, no
+ * commands will be sent to the master OCC
+ *
+ * @param[in] isActive - true when master OCC is active
+ */
+ void setMasterActive(const bool isActive = true)
+ {
+ masterActive = isActive;
+ };
private:
+ /** @brief OCC manager object */
+ const Manager& manager;
+
+ /** @brief Pass-through occ path on the bus */
+ std::string path;
+
+ /** @brief OCC instance number */
+ int occInstance;
+
+ /** @brief Object to send commands to the OCC */
+ OccCommand occCmd;
+
+ /** @brief Used to subscribe to dbus pmode property changes **/
+ sdbusplus::bus::match_t pmodeMatch;
+
+ /** @brief Used to subscribe to dbus IPS property changes **/
+ sdbusplus::bus::match_t ipsMatch;
+
+ OccPersistData persistedData;
+
+ bool masterActive;
+
/** @brief Callback for pmode setting changes
*
* Process change and inform OCC
@@ -74,47 +226,33 @@
*/
void modeChanged(sdbusplus::message::message& msg);
- /* @brief OCC Status object */
- Status& occStatus;
-
- /** @brief Used to subscribe to dbus pmode property changes **/
- sdbusplus::bus::match_t pmodeMatch;
-};
-
-class PowerIPS
-{
- public:
- /** @brief PowerIPS object to inform occ of changes to Idle Power Saver
- * parms
- *
- * This object will monitor for changes to the Idle Power Saver settings.
- * If a change is detected, and the occ is active, then this object will
- * notify the OCC of the change.
- *
- * @param[in] occStatus - The occ status object
+ /** @brief Get the current power mode property from DBus
+ * @return Power mode
*/
- explicit PowerIPS(Status& occStatus) :
- occStatus(occStatus),
- ipsMatch(utils::getBus(),
- sdbusplus::bus::match::rules::propertiesChanged(
- PIPS_PATH, PIPS_INTERFACE),
- [this](auto& msg) { this->ipsChanged(msg); }){};
+ SysPwrMode getDbusMode();
- private:
+ /** @brief Update the power mode property on DBus
+ *
+ * @param[in] newMode - desired power mode
+ *
+ * @return true on success
+ */
+ bool updateDbusMode(const SysPwrMode newMode);
+
/** @brief Callback for IPS setting changes
*
* Process change and inform OCC
*
- * @param[in] msg - Data associated with IPS change signal
+ * @param[in] msg - Data associated with IPS change signal
*
*/
void ipsChanged(sdbusplus::message::message& msg);
- /* @brief OCC Status object */
- Status& occStatus;
-
- /** @brief Used to subscribe to dbus IPS property changes **/
- sdbusplus::bus::match_t ipsMatch;
+ /** @brief Get the Idle Power Saver properties from DBus
+ * @return true if IPS is enabled
+ */
+ bool getIPSParms(uint8_t& enterUtil, uint16_t& enterTime, uint8_t& exitUtil,
+ uint16_t& exitTime);
};
} // namespace powermode
diff --git a/utils.cpp b/utils.cpp
index 0b983b4..b44d477 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -1,5 +1,7 @@
#include "utils.hpp"
+#include <fmt/core.h>
+
#include <phosphor-logging/elog-errors.hpp>
#include <sdbusplus/bus.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
@@ -67,6 +69,41 @@
return value;
}
+/**
+ * @brief Sets a given object's property value
+ *
+ * @param[in] object - Name of the object containing the property
+ * @param[in] interface - Interface name containing the property
+ * @param[in] property - Property name
+ * @param[in] value - Property value
+ */
+void setProperty(const std::string& objectPath, const std::string& interface,
+ const std::string& propertyName, std::string&& value)
+{
+ using namespace std::literals::string_literals;
+ std::variant<std::string> varValue(std::forward<std::string>(value));
+
+ auto& bus = getBus();
+ auto service = getService(objectPath, interface);
+ if (service.empty())
+ {
+ return;
+ }
+
+ auto method = bus.new_method_call(service.c_str(), objectPath.c_str(),
+ DBUS_PROPERTY_IFACE, "Set");
+ method.append(interface, propertyName, varValue);
+
+ auto reply = bus.call(method);
+ if (reply.is_method_error())
+ {
+ log<level::ERR>(
+ fmt::format("util::setProperty: Failed to set property {}",
+ propertyName)
+ .c_str());
+ }
+}
+
std::vector<std::string>
getSubtreePaths(const std::vector<std::string>& interfaces,
const std::string& path)
diff --git a/utils.hpp b/utils.hpp
index 9e6c19b..4a57700 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -54,6 +54,17 @@
const std::string& interface,
const std::string& propertyName);
+/**
+ * @brief Sets a given object's property value
+ *
+ * @param[in] object - Name of the object containing the property
+ * @param[in] interface - Interface name containing the property
+ * @param[in] property - Property name
+ * @param[in] value - Property value
+ */
+void setProperty(const std::string& objectPath, const std::string& interface,
+ const std::string& propertyName, std::string&& value);
+
/** @brief Get subtree paths
*
* @param[in] interfaces - D-Bus interfaces