blob: 6d2424a183dd298becf42e82f2c8934c169b22f7 [file] [log] [blame]
Chris Cain36f9cde2021-11-22 11:18:21 -06001#include "powermode.hpp"
2
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05003#include <fcntl.h>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05004#include <sys/ioctl.h>
Chris Cain78e86012021-03-04 16:15:31 -06005
Chris Cain52cce032023-02-09 15:38:31 -06006#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -06007#include <com/ibm/Host/Target/server.hpp>
Chris Cain52cce032023-02-09 15:38:31 -06008#endif
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05009#include <org/open_power/OCC/Device/error.hpp>
Ben Tyner3576d652022-05-22 18:05:53 -050010#include <phosphor-logging/elog-errors.hpp>
Chris Cain37abe9b2024-10-31 17:20:31 -050011#include <phosphor-logging/lg2.hpp>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050012#include <xyz/openbmc_project/Common/error.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060013#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
14
George Liub5ca1012021-09-10 12:53:11 +080015#include <cassert>
Chris Cain36f9cde2021-11-22 11:18:21 -060016#include <fstream>
George Liub5ca1012021-09-10 12:53:11 +080017#include <regex>
18
Chris Cain78e86012021-03-04 16:15:31 -060019namespace open_power
20{
21namespace occ
22{
23namespace powermode
24{
25
26using namespace phosphor::logging;
Chris Cain1be43372021-12-09 19:29:37 -060027using namespace std::literals::string_literals;
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050028using namespace sdbusplus::org::open_power::OCC::Device::Error;
Chris Cain1be43372021-12-09 19:29:37 -060029
Chris Cain78e86012021-03-04 16:15:31 -060030using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
31
Ben Tyner3576d652022-05-22 18:05:53 -050032using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
Chris Cain30040d92024-06-19 16:50:34 -050033
34// List of all Power Modes that are currently supported (and in Redfish)
35#define VALID_POWER_MODE_SETTING(mode) \
36 ((mode == SysPwrMode::STATIC) || (mode == SysPwrMode::POWER_SAVING) || \
37 (mode == SysPwrMode::BALANCED_PERF) || (mode == SysPwrMode::MAX_PERF) || \
38 (mode == SysPwrMode::EFF_FAVOR_POWER) || \
39 (mode == SysPwrMode::EFF_FAVOR_PERF))
40// List of OEM Power Modes that are currently supported
41#define VALID_OEM_POWER_MODE_SETTING(mode) \
42 ((mode == SysPwrMode::SFP) || (mode == SysPwrMode::FFO) || \
43 (mode == SysPwrMode::MAX_FREQ) || \
44 (mode == SysPwrMode::NON_DETERMINISTIC))
Chris Cain1fe436d2024-10-10 09:41:03 -050045// List of all Power Modes that disable IPS
46#define IS_ECO_MODE(mode) \
47 ((mode == SysPwrMode::EFF_FAVOR_POWER) || \
48 (mode == SysPwrMode::EFF_FAVOR_PERF))
Chris Cain30040d92024-06-19 16:50:34 -050049
50// Constructor
51PowerMode::PowerMode(const Manager& managerRef, const char* modePath,
52 const char* ipsPath, EventPtr& event) :
53 ModeInterface(utils::getBus(), modePath,
54 ModeInterface::action::emit_no_signals),
Chris Cain30040d92024-06-19 16:50:34 -050055 manager(managerRef),
56 ipsMatch(utils::getBus(),
57 sdbusplus::bus::match::rules::propertiesChanged(PIPS_PATH,
58 PIPS_INTERFACE),
59 [this](auto& msg) { this->ipsChanged(msg); }),
60 defaultsUpdateMatch(
61 utils::getBus(),
62 sdbusplus::bus::match::rules::propertiesChangedNamespace(
63 "/xyz/openbmc_project/inventory", PMODE_DEFAULT_INTERFACE),
64 [this](auto& msg) { this->defaultsReady(msg); }),
Chris Cain1fe436d2024-10-10 09:41:03 -050065 masterOccSet(false), masterActive(false), ipsObjectPath(ipsPath),
66 event(event)
Chris Cain30040d92024-06-19 16:50:34 -050067{
68 // Get supported power modes from entity manager
69 if (false == getSupportedModes())
70 {
71 // Did not find them so use default customer modes
72 using Mode =
73 sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
74 // Update power modes that will be allowed by the Redfish interface
Patrick Williamsd7542c82024-08-16 15:20:28 -040075 ModeInterface::allowedPowerModes(
76 {Mode::PowerMode::Static, Mode::PowerMode::MaximumPerformance,
77 Mode::PowerMode::PowerSaving});
Chris Cain30040d92024-06-19 16:50:34 -050078 }
79
80 SysPwrMode currentMode;
81 uint16_t oemModeData = 0;
82 // Read the persisted power mode
83 if (getMode(currentMode, oemModeData))
84 {
85 // Validate persisted mode is supported
86 if (isValidMode(currentMode))
87 {
Chris Cain1fe436d2024-10-10 09:41:03 -050088 // Update power mode on DBus and create IPS object if allowed
Chris Cain30040d92024-06-19 16:50:34 -050089 updateDbusMode(currentMode);
90 }
91 else
92 {
Chris Cain37abe9b2024-10-31 17:20:31 -050093 lg2::error("PowerMode: Persisted power mode ({MODE}/{DATA}) is not "
94 "valid. Reading system default mode",
95 "MODE", currentMode, "DATA", oemModeData);
Chris Cain30040d92024-06-19 16:50:34 -050096 persistedData.invalidateMode();
97 // Read default power mode
98 initPersistentData();
99 }
100 }
Chris Cain30040d92024-06-19 16:50:34 -0500101};
Ben Tyner3576d652022-05-22 18:05:53 -0500102
Chris Cain1fe436d2024-10-10 09:41:03 -0500103void PowerMode::createIpsObject()
104{
105 if (!ipsObject)
106 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500107 lg2::info("createIpsObject: Creating IPS object");
Chris Cain1fe436d2024-10-10 09:41:03 -0500108 ipsObject =
109 std::make_unique<IpsInterface>(utils::getBus(), ipsObjectPath);
110
111 uint8_t enterUtil, exitUtil;
112 uint16_t enterTime, exitTime;
113 bool ipsEnabled;
114 // Read the persisted Idle Power Saver parametres
115 if (getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime))
116 {
117 // Update Idle Power Saver parameters on DBus
118 updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
119 }
120
121 // Starts watching for IPS state changes.
122 addIpsWatch(true);
123
124 needToSendIpsData = true;
125 }
126}
127
128void PowerMode::removeIpsObject()
129{
130 if (ipsObject)
131 {
132 // Stop watching for IPS state changes.
133 removeIpsWatch();
134
Chris Cain37abe9b2024-10-31 17:20:31 -0500135 lg2::info("removeIpsObject: Deleting IPS object");
Chris Cain1fe436d2024-10-10 09:41:03 -0500136 ipsObject.reset(nullptr);
137 }
138 needToSendIpsData = false;
139}
140
Chris Cain6fa848a2022-01-24 14:54:38 -0600141// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -0600142void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -0600143{
Chris Cain1be43372021-12-09 19:29:37 -0600144 if (masterOccSet)
145 {
146 if (masterOccPath != path)
147 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500148 lg2::error(
149 "PowerMode::setMasterOcc: Master changed (was OCC{INST}, {PATH})",
150 "INST", occInstance, "PATH", masterOccPath);
Chris Cain1be43372021-12-09 19:29:37 -0600151 if (occCmd)
152 {
153 occCmd.reset();
154 }
155 }
156 }
157 path = masterOccPath;
Chris Cain6fa848a2022-01-24 14:54:38 -0600158 occInstance = path.back() - '0';
Chris Cain37abe9b2024-10-31 17:20:31 -0500159 lg2::debug("PowerMode::setMasterOcc(OCC{INST}, {PATH})", "INST",
160 occInstance, "PATH", path);
Chris Cain1be43372021-12-09 19:29:37 -0600161 if (!occCmd)
162 {
Patrick Williamsd7542c82024-08-16 15:20:28 -0400163 occCmd = std::make_unique<open_power::occ::OccCommand>(
164 occInstance, path.c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600165 }
Chris Cain6fa848a2022-01-24 14:54:38 -0600166 masterOccSet = true;
167};
168
Ben Tyner3576d652022-05-22 18:05:53 -0500169// Set the state of power mode lock. Writing persistent data via dbus method.
170bool PowerMode::powerModeLock()
171{
Chris Cain37abe9b2024-10-31 17:20:31 -0500172 lg2::info("PowerMode::powerModeLock: locking mode change");
Ben Tyner3576d652022-05-22 18:05:53 -0500173 persistedData.updateModeLock(true); // write persistent data
174 return true;
175}
176
177// Get the state of power mode. Reading persistent data via dbus method.
178bool PowerMode::powerModeLockStatus()
179{
180 bool status = persistedData.getModeLock(); // read persistent data
Chris Cain37abe9b2024-10-31 17:20:31 -0500181 lg2::info("PowerMode::powerModeLockStatus: {STATUS}", "STATUS",
182 status ? "locked" : "unlocked");
Ben Tyner3576d652022-05-22 18:05:53 -0500183 return status;
184}
185
Chris Cain36f9cde2021-11-22 11:18:21 -0600186// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -0600187bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -0600188{
Ben Tyner3576d652022-05-22 18:05:53 -0500189 if (persistedData.getModeLock())
190 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500191 lg2::info("PowerMode::setMode: mode change blocked");
Ben Tyner3576d652022-05-22 18:05:53 -0500192 return false;
193 }
194
Chris Cain36f9cde2021-11-22 11:18:21 -0600195 if (updateDbusMode(newMode) == false)
196 {
197 // Unsupported mode
198 return false;
199 }
200
Chris Cain1be43372021-12-09 19:29:37 -0600201 // Save mode
202 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600203
Chris Cain1be43372021-12-09 19:29:37 -0600204 // Send mode change to OCC
205 if (sendModeChange() != CmdStatus::SUCCESS)
206 {
207 // Mode change failed
208 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600209 }
210
Chris Cain36f9cde2021-11-22 11:18:21 -0600211 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600212}
213
Chris Cain30040d92024-06-19 16:50:34 -0500214// Convert PowerMode value to occ-control internal SysPwrMode
215// Returns SysPwrMode::NO_CHANGE if mode not valid
216SysPwrMode getInternalMode(const Mode::PowerMode& mode)
217{
218 if (mode == Mode::PowerMode::MaximumPerformance)
219 {
220 return SysPwrMode::MAX_PERF;
221 }
222 else if (mode == Mode::PowerMode::PowerSaving)
223 {
224 return SysPwrMode::POWER_SAVING;
225 }
226 else if (mode == Mode::PowerMode::Static)
227 {
228 return SysPwrMode::STATIC;
229 }
230 else if (mode == Mode::PowerMode::EfficiencyFavorPower)
231 {
232 return SysPwrMode::EFF_FAVOR_POWER;
233 }
234 else if (mode == Mode::PowerMode::EfficiencyFavorPerformance)
235 {
236 return SysPwrMode::EFF_FAVOR_PERF;
237 }
238 else if (mode == Mode::PowerMode::BalancedPerformance)
239 {
240 return SysPwrMode::BALANCED_PERF;
241 }
242
Chris Cain37abe9b2024-10-31 17:20:31 -0500243 lg2::warning("getInternalMode: Invalid PowerMode specified");
Chris Cain30040d92024-06-19 16:50:34 -0500244 return SysPwrMode::NO_CHANGE;
245}
246
Chris Cain78e86012021-03-04 16:15:31 -0600247// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600248// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600249SysPwrMode convertStringToMode(const std::string& i_modeString)
250{
Chris Cain30040d92024-06-19 16:50:34 -0500251 SysPwrMode newMode = SysPwrMode::NO_CHANGE;
252 try
Chris Cain78e86012021-03-04 16:15:31 -0600253 {
Chris Cain30040d92024-06-19 16:50:34 -0500254 Mode::PowerMode newPMode =
255 Mode::convertPowerModeFromString(i_modeString);
256 newMode = getInternalMode(newPMode);
Chris Cain78e86012021-03-04 16:15:31 -0600257 }
Chris Cain30040d92024-06-19 16:50:34 -0500258 catch (const std::exception& e)
Chris Cain78e86012021-03-04 16:15:31 -0600259 {
Chris Cain30040d92024-06-19 16:50:34 -0500260 // Strip off prefix to to search OEM modes not part of Redfish
261 auto prefix = PMODE_INTERFACE + ".PowerMode."s;
262 std::string shortMode = i_modeString;
263 std::string::size_type index = i_modeString.find(prefix);
264 if (index != std::string::npos)
265 {
266 shortMode.erase(0, prefix.length());
267 }
268 if (shortMode == "FFO")
269 {
270 newMode = SysPwrMode::FFO;
271 }
272 else if (shortMode == "SFP")
273 {
274 newMode = SysPwrMode::SFP;
275 }
276 else if (shortMode == "MaxFrequency")
277 {
278 newMode = SysPwrMode::MAX_FREQ;
279 }
280 else if (shortMode == "NonDeterministic")
281 {
282 newMode = SysPwrMode::NON_DETERMINISTIC;
283 }
284 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600285 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500286 lg2::error(
287 "convertStringToMode: Invalid Power Mode: {MODE} ({DATA})",
288 "MODE", shortMode, "DATA", e.what());
Chris Cain36f9cde2021-11-22 11:18:21 -0600289 }
Chris Cain78e86012021-03-04 16:15:31 -0600290 }
Chris Cain30040d92024-06-19 16:50:34 -0500291 return newMode;
Chris Cain78e86012021-03-04 16:15:31 -0600292}
293
Chris Cain36f9cde2021-11-22 11:18:21 -0600294// Check if Hypervisor target is PowerVM
295bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500296{
Chris Cain52cce032023-02-09 15:38:31 -0600297 bool powerVmTarget = true;
298#ifdef POWERVM_CHECK
Chris Cain36f9cde2021-11-22 11:18:21 -0600299 namespace Hyper = sdbusplus::com::ibm::Host::server;
300 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
301 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
302 constexpr auto HYPE_PROP = "Target";
303
Chris Cain36f9cde2021-11-22 11:18:21 -0600304 // This will throw exception on failure
305 auto& bus = utils::getBus();
306 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
307 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
308 "org.freedesktop.DBus.Properties", "Get");
309 method.append(HYPE_INTERFACE, HYPE_PROP);
310 auto reply = bus.call(method);
311
312 std::variant<std::string> hyperEntryValue;
313 reply.read(hyperEntryValue);
314 auto propVal = std::get<std::string>(hyperEntryValue);
315 if (Hyper::Target::convertHypervisorFromString(propVal) ==
316 Hyper::Target::Hypervisor::PowerVM)
317 {
318 powerVmTarget = true;
319 }
320
Chris Cain37abe9b2024-10-31 17:20:31 -0500321 lg2::debug("isPowerVM returning {VAL}", "VAL", powerVmTarget);
Chris Cain52cce032023-02-09 15:38:31 -0600322#endif
Chris Cain36f9cde2021-11-22 11:18:21 -0600323
324 return powerVmTarget;
325}
326
Chris Cain1be43372021-12-09 19:29:37 -0600327// Initialize persistent data and return true if successful
328bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600329{
Chris Cain1be43372021-12-09 19:29:37 -0600330 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600331 {
Chris Cain1be43372021-12-09 19:29:37 -0600332 // Read the default mode
333 SysPwrMode currentMode;
334 if (!getDefaultMode(currentMode))
335 {
336 // Unable to read defaults
337 return false;
338 }
Chris Cain37abe9b2024-10-31 17:20:31 -0500339 lg2::info("PowerMode::initPersistentData: Using default mode: {MODE}",
340 "MODE", currentMode);
Chris Cain1be43372021-12-09 19:29:37 -0600341
342 // Save default mode as current mode
343 persistedData.updateMode(currentMode, 0);
344
Chris Cain1fe436d2024-10-10 09:41:03 -0500345 // Write default mode to DBus and create IPS object if allowed
Chris Cain1be43372021-12-09 19:29:37 -0600346 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600347 }
348
Chris Cain1be43372021-12-09 19:29:37 -0600349 if (!persistedData.ipsAvailable())
350 {
Chris Caincde7bea2022-01-28 15:54:24 -0600351 // Read the default IPS parameters, write persistent file and update
352 // DBus
353 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600354 }
355 return true;
356}
357
358// Get the requested power mode and return true if successful
359bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
360{
361 currentMode = SysPwrMode::NO_CHANGE;
362 oemModeData = 0;
363
364 if (!persistedData.getMode(currentMode, oemModeData))
365 {
366 // Persistent data not initialized, read defaults and update DBus
367 if (!initPersistentData())
368 {
369 // Unable to read defaults from entity manager yet
370 return false;
371 }
372 return persistedData.getMode(currentMode, oemModeData);
373 }
374
375 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600376}
377
Chris Cain1fe436d2024-10-10 09:41:03 -0500378// Set the power mode on DBus and create IPS object if allowed/needed
Chris Cain36f9cde2021-11-22 11:18:21 -0600379bool PowerMode::updateDbusMode(const SysPwrMode newMode)
380{
Chris Cain30040d92024-06-19 16:50:34 -0500381 if (!isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600382 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500383 lg2::error(
384 "PowerMode::updateDbusMode - Requested power mode not supported: {MODE}",
385 "MODE", newMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600386 return false;
387 }
388
Chris Cain30040d92024-06-19 16:50:34 -0500389 ModeInterface::PowerMode dBusMode = Mode::PowerMode::OEM;
390 if (customerModeList.contains(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600391 {
Chris Cain30040d92024-06-19 16:50:34 -0500392 // Convert mode for DBus
393 switch (newMode)
394 {
395 case SysPwrMode::STATIC:
396 dBusMode = Mode::PowerMode::Static;
397 break;
398 case SysPwrMode::POWER_SAVING:
399 dBusMode = Mode::PowerMode::PowerSaving;
400 break;
401 case SysPwrMode::MAX_PERF:
402 dBusMode = Mode::PowerMode::MaximumPerformance;
403 break;
404 case SysPwrMode::EFF_FAVOR_POWER:
405 dBusMode = Mode::PowerMode::EfficiencyFavorPower;
406 break;
407 case SysPwrMode::EFF_FAVOR_PERF:
408 dBusMode = Mode::PowerMode::EfficiencyFavorPerformance;
409 break;
410 case SysPwrMode::BALANCED_PERF:
411 dBusMode = Mode::PowerMode::BalancedPerformance;
412 break;
413 default:
414 break;
415 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600416 }
Chris Cain30040d92024-06-19 16:50:34 -0500417 // else return OEM mode
Chris Cain36f9cde2021-11-22 11:18:21 -0600418
Chris Cain1fe436d2024-10-10 09:41:03 -0500419 // Determine if IPS is allowed and create/remove as needed
420 if (IS_ECO_MODE(newMode))
421 {
422 removeIpsObject();
423 }
424 else
425 {
426 createIpsObject();
427 }
428
Chris Cain30040d92024-06-19 16:50:34 -0500429 ModeInterface::powerMode(dBusMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600430
431 return true;
432}
433
434// Send mode change request to the master OCC
435CmdStatus PowerMode::sendModeChange()
436{
Chris Cain6fa848a2022-01-24 14:54:38 -0600437 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600438
Chris Cain1be43372021-12-09 19:29:37 -0600439 SysPwrMode newMode;
440 uint16_t oemModeData = 0;
441 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600442
Chris Cain30040d92024-06-19 16:50:34 -0500443 if (isValidMode(newMode))
Chris Cain36f9cde2021-11-22 11:18:21 -0600444 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500445 if (IS_ECO_MODE(newMode))
446 {
447 // Customer no longer able to enable IPS
448 removeIpsObject();
449 }
450 else
451 {
452 // Customer now able to enable IPS
453 if (!ipsObject)
454 {
455 createIpsObject();
456 }
Chris Caina21c4712025-03-04 15:58:27 -0600457 else
458 {
459 if (!watching)
460 {
461 // Starts watching for IPS state changes.
462 addIpsWatch(true);
463 }
464 }
Chris Cain1fe436d2024-10-10 09:41:03 -0500465 }
466
467 if (!masterActive || !masterOccSet)
468 {
469 // Nothing to do until OCC goes active
Chris Cain37abe9b2024-10-31 17:20:31 -0500470 lg2::debug("PowerMode::sendModeChange: OCC master not active");
Chris Cain1fe436d2024-10-10 09:41:03 -0500471 return CmdStatus::SUCCESS;
472 }
473
474 if (!isPowerVM())
475 {
476 // Mode change is only supported on PowerVM systems
Chris Cain37abe9b2024-10-31 17:20:31 -0500477 lg2::debug(
Chris Cain1fe436d2024-10-10 09:41:03 -0500478 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
479 return CmdStatus::SUCCESS;
480 }
481
Chris Cain36f9cde2021-11-22 11:18:21 -0600482 std::vector<std::uint8_t> cmd, rsp;
483 cmd.reserve(9);
484 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
485 cmd.push_back(0x00); // Data Length (2 bytes)
486 cmd.push_back(0x06);
487 cmd.push_back(0x30); // Data (Version)
488 cmd.push_back(uint8_t(OccState::NO_CHANGE));
489 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600490 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
491 cmd.push_back(oemModeData & 0xFF); //
492 cmd.push_back(0x00); // reserved
Chris Cain37abe9b2024-10-31 17:20:31 -0500493 lg2::info(
494 "PowerMode::sendModeChange: SET_MODE({MODE},{DATA}) command to OCC{INST} ({LEN} bytes)",
495 "MODE", uint8_t(newMode), "DATA", oemModeData, "INST", occInstance,
496 "LEN", cmd.size());
Chris Cain6fa848a2022-01-24 14:54:38 -0600497 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600498 if (status == CmdStatus::SUCCESS)
499 {
500 if (rsp.size() == 5)
501 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500502 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
503 {
504 if (needToSendIpsData)
505 {
506 // Successful mode change and IPS is now allowed, so
507 // send IPS config
508 sendIpsData();
509 }
510 }
511 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600512 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500513 lg2::error(
514 "PowerMode::sendModeChange: SET MODE failed with status {STATUS}",
515 "STATUS", lg2::hex, rsp[2]);
Chris Cain36f9cde2021-11-22 11:18:21 -0600516 dump_hex(rsp);
517 status = CmdStatus::FAILURE;
518 }
519 }
520 else
521 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500522 lg2::error(
Chris Cain36f9cde2021-11-22 11:18:21 -0600523 "PowerMode::sendModeChange: INVALID SET MODE response");
524 dump_hex(rsp);
525 status = CmdStatus::FAILURE;
526 }
527 }
528 else
529 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500530 lg2::error(
531 "PowerMode::sendModeChange: SET_MODE FAILED with status={STATUS}",
532 "STATUS", lg2::hex, uint8_t(status));
Chris Cain36f9cde2021-11-22 11:18:21 -0600533 }
534 }
535 else
536 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500537 lg2::error(
538 "PowerMode::sendModeChange: Unable to set power mode to {MODE}",
539 "MODE", newMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600540 status = CmdStatus::FAILURE;
541 }
542
543 return status;
544}
545
Chris Cain1fe436d2024-10-10 09:41:03 -0500546// Handle IPS changed event (from GUI/Redfish)
Patrick Williamsaf408082022-07-22 19:26:54 -0500547void PowerMode::ipsChanged(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600548{
Chris Cain1d51da22021-09-21 14:13:41 -0500549 bool parmsChanged = false;
550 std::string interface;
551 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
552 ipsProperties{};
553 msg.read(interface, ipsProperties);
554
Chris Cain1be43372021-12-09 19:29:37 -0600555 // Read persisted values
556 bool ipsEnabled;
557 uint8_t enterUtil, exitUtil;
558 uint16_t enterTime, exitTime;
559 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
560
Chris Cain1fe436d2024-10-10 09:41:03 -0500561 if (!ipsObject)
562 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500563 lg2::warning(
Chris Cain1fe436d2024-10-10 09:41:03 -0500564 "ipsChanged: Idle Power Saver can not be modified in an ECO power mode");
565 return;
566 }
567
Chris Cain1be43372021-12-09 19:29:37 -0600568 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500569 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
570 if (ipsEntry != ipsProperties.end())
571 {
Chris Cain1be43372021-12-09 19:29:37 -0600572 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain37abe9b2024-10-31 17:20:31 -0500573 lg2::info("Idle Power Saver change: Enabled={STAT}", "STAT",
574 ipsEnabled);
Chris Cain1d51da22021-09-21 14:13:41 -0500575 parmsChanged = true;
576 }
577 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
578 if (ipsEntry != ipsProperties.end())
579 {
Chris Cain1be43372021-12-09 19:29:37 -0600580 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain37abe9b2024-10-31 17:20:31 -0500581 lg2::info("Idle Power Saver change: Enter Util={UTIL}%", "UTIL",
582 enterUtil);
Chris Cain1d51da22021-09-21 14:13:41 -0500583 parmsChanged = true;
584 }
585 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
586 if (ipsEntry != ipsProperties.end())
587 {
588 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600589 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500590 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain37abe9b2024-10-31 17:20:31 -0500591 lg2::info("Idle Power Saver change: Enter Time={TIME}sec", "TIME",
592 enterTime);
Chris Cain1d51da22021-09-21 14:13:41 -0500593 parmsChanged = true;
594 }
595 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
596 if (ipsEntry != ipsProperties.end())
597 {
Chris Cain1be43372021-12-09 19:29:37 -0600598 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain37abe9b2024-10-31 17:20:31 -0500599 lg2::info("Idle Power Saver change: Exit Util={UTIL}%", "UTIL",
600 exitUtil);
Chris Cain1d51da22021-09-21 14:13:41 -0500601 parmsChanged = true;
602 }
603 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
604 if (ipsEntry != ipsProperties.end())
605 {
606 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600607 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain37abe9b2024-10-31 17:20:31 -0500608 lg2::info("Idle Power Saver change: Exit Time={TIME}sec", "TIME",
609 exitTime);
Chris Cain1d51da22021-09-21 14:13:41 -0500610 parmsChanged = true;
611 }
612
613 if (parmsChanged)
614 {
Chris Caincde7bea2022-01-28 15:54:24 -0600615 if (exitUtil == 0)
616 {
617 // Setting the exitUtil to 0 will force restoring the default IPS
618 // parmeters (0 is not valid exit utilization)
Chris Cain37abe9b2024-10-31 17:20:31 -0500619 lg2::info(
Chris Caincde7bea2022-01-28 15:54:24 -0600620 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
621 // Read the default IPS parameters, write persistent file and update
622 // DBus
623 useDefaultIPSParms();
624 }
625 else
626 {
627 // Update persistant data with new DBus values
628 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
629 exitTime);
630 }
Chris Cain1be43372021-12-09 19:29:37 -0600631
632 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600633 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500634 }
635
636 return;
637}
638
Chris Cain1be43372021-12-09 19:29:37 -0600639/** @brief Get the Idle Power Saver properties from persisted data
640 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600641 */
Chris Cain1be43372021-12-09 19:29:37 -0600642bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
643 uint16_t& enterTime, uint8_t& exitUtil,
644 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600645{
Chris Cain36f9cde2021-11-22 11:18:21 -0600646 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600647 ipsEnabled = true; // Enabled
648 enterUtil = 8; // Enter Utilization (8%)
649 enterTime = 240; // Enter Delay Time (240s)
650 exitUtil = 12; // Exit Utilization (12%)
651 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600652
Chris Cain1be43372021-12-09 19:29:37 -0600653 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
654 exitTime))
655 {
656 // Persistent data not initialized, read defaults and update DBus
657 if (!initPersistentData())
658 {
659 // Unable to read defaults from entity manager yet
660 return false;
661 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600662
Chris Cain1be43372021-12-09 19:29:37 -0600663 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
664 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600665 }
666
667 if (enterUtil > exitUtil)
668 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500669 lg2::error(
670 "ERROR: Idle Power Saver Enter Utilization ({ENTER}%) is > Exit Utilization ({EXIT}%) - using Exit for both",
671 "ENTER", enterUtil, "EXIT", exitUtil);
Chris Cain36f9cde2021-11-22 11:18:21 -0600672 enterUtil = exitUtil;
673 }
674
Chris Cain1be43372021-12-09 19:29:37 -0600675 return true;
676}
677
678// Set the Idle Power Saver data on DBus
679bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
680 const uint16_t enterTime, const uint8_t exitUtil,
681 const uint16_t exitTime)
682{
Chris Cain1fe436d2024-10-10 09:41:03 -0500683 if (ipsObject)
684 {
685 // true = skip update signal
686 ipsObject->setPropertyByName(IPS_ENABLED_PROP, enabled, true);
687 ipsObject->setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
688 // Convert time from seconds to ms
689 uint64_t msTime = enterTime * 1000;
690 ipsObject->setPropertyByName(IPS_ENTER_TIME, msTime, true);
691 ipsObject->setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
692 msTime = exitTime * 1000;
693 ipsObject->setPropertyByName(IPS_EXIT_TIME, msTime, true);
694 }
695 else
696 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500697 lg2::warning("updateDbusIPS: No IPS object was found");
Chris Cain1fe436d2024-10-10 09:41:03 -0500698 }
Chris Cain1be43372021-12-09 19:29:37 -0600699
700 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600701}
702
703// Send Idle Power Saver config data to the master OCC
704CmdStatus PowerMode::sendIpsData()
705{
Chris Cain6fa848a2022-01-24 14:54:38 -0600706 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600707 {
708 // Nothing to do
709 return CmdStatus::SUCCESS;
710 }
711
712 if (!isPowerVM())
713 {
714 // Idle Power Saver data is only supported on PowerVM systems
Chris Cain37abe9b2024-10-31 17:20:31 -0500715 lg2::debug(
Chris Cain36f9cde2021-11-22 11:18:21 -0600716 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
717 return CmdStatus::SUCCESS;
718 }
719
Chris Cain1fe436d2024-10-10 09:41:03 -0500720 if (!ipsObject)
721 {
722 // Idle Power Saver data is not available in Eco Modes
Chris Cain37abe9b2024-10-31 17:20:31 -0500723 lg2::info(
Chris Cain1fe436d2024-10-10 09:41:03 -0500724 "PowerMode::sendIpsData: Skipping IPS data due to being in an ECO Power Mode");
725 return CmdStatus::SUCCESS;
726 }
727
Chris Cain1be43372021-12-09 19:29:37 -0600728 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600729 uint8_t enterUtil, exitUtil;
730 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600731 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600732
Chris Cain37abe9b2024-10-31 17:20:31 -0500733 lg2::info(
734 "Idle Power Saver Parameters: enabled:{ENABLE}, enter:{EUTIL}%/{ETIME}s, exit:{XUTIL}%/{XTIME}s",
735 "ENABLE", ipsEnabled, "EUTIL", enterUtil, "ETIME", enterTime, "XUTIL",
736 exitUtil, "XTIME", exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600737
738 std::vector<std::uint8_t> cmd, rsp;
739 cmd.reserve(12);
740 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
741 cmd.push_back(0x00); // Data Length (2 bytes)
742 cmd.push_back(0x09); //
743 cmd.push_back(0x11); // Config Format: IPS Settings
744 cmd.push_back(0x00); // Version
745 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
746 cmd.push_back(enterTime >> 8); // Enter Delay Time
747 cmd.push_back(enterTime & 0xFF); //
748 cmd.push_back(enterUtil); // Enter Utilization
749 cmd.push_back(exitTime >> 8); // Exit Delay Time
750 cmd.push_back(exitTime & 0xFF); //
751 cmd.push_back(exitUtil); // Exit Utilization
Chris Cain37abe9b2024-10-31 17:20:31 -0500752 lg2::info("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
753 "command to OCC{INST} ({LEN} bytes)",
754 "INST", occInstance, "LEN", cmd.size());
Chris Cain1be43372021-12-09 19:29:37 -0600755 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600756 if (status == CmdStatus::SUCCESS)
757 {
758 if (rsp.size() == 5)
759 {
Chris Cain1fe436d2024-10-10 09:41:03 -0500760 if (RspStatus::SUCCESS == RspStatus(rsp[2]))
761 {
762 needToSendIpsData = false;
763 }
764 else
Chris Cain36f9cde2021-11-22 11:18:21 -0600765 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500766 lg2::error(
767 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status {STATUS}",
768 "STATUS", lg2::hex, rsp[2]);
Chris Cain36f9cde2021-11-22 11:18:21 -0600769 dump_hex(rsp);
770 status = CmdStatus::FAILURE;
771 }
772 }
773 else
774 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500775 lg2::error(
Chris Cain36f9cde2021-11-22 11:18:21 -0600776 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
777 dump_hex(rsp);
778 status = CmdStatus::FAILURE;
779 }
780 }
781 else
782 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500783 lg2::error(
784 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={STATUS}",
785 "STATUS", lg2::hex, uint8_t(status));
Chris Cain36f9cde2021-11-22 11:18:21 -0600786 }
787
788 return status;
789}
790
Chris Caincde7bea2022-01-28 15:54:24 -0600791// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600792void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600793{
Chris Cain1be43372021-12-09 19:29:37 -0600794 if (modeData.modeInitialized)
795 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500796 lg2::info(
797 "OccPersistData: Mode: {MODE}, OEM Mode Data: {DATA} ({DATAHEX} Locked{LOCK})",
798 "MODE", lg2::hex, uint8_t(modeData.mode), "DATA",
799 modeData.oemModeData, "DATAHEX", lg2::hex, modeData.oemModeData,
800 "LOCK", modeData.modeLocked);
Chris Cain1be43372021-12-09 19:29:37 -0600801 }
802 if (modeData.ipsInitialized)
803 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500804 lg2::info(
805 "OccPersistData: IPS enabled:{ENABLE}, enter:{EUTIL}%/{ETIME}s, exit:{XUTIL}%/{XTIME}s",
806 "ENABLE", modeData.ipsEnabled, "EUTIL", modeData.ipsEnterUtil,
807 "ETIME", modeData.ipsEnterTime, "XUTIL", modeData.ipsExitUtil,
808 "XTIME", modeData.ipsExitTime);
Chris Cain1be43372021-12-09 19:29:37 -0600809 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600810}
811
812// Saves the OEM mode data in the filesystem using cereal.
813void OccPersistData::save()
814{
815 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600816 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600817
818 if (!std::filesystem::exists(opath.parent_path()))
819 {
820 std::filesystem::create_directory(opath.parent_path());
821 }
822
Chris Cain37abe9b2024-10-31 17:20:31 -0500823 lg2::debug(
824 "OccPersistData::save: Writing Power Mode persisted data to {FILE}",
825 "FILE", opath);
Chris Caincde7bea2022-01-28 15:54:24 -0600826 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600827
828 std::ofstream stream{opath.c_str()};
829 cereal::JSONOutputArchive oarchive{stream};
830
Chris Cain1be43372021-12-09 19:29:37 -0600831 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600832}
833
834// Loads the OEM mode data in the filesystem using cereal.
835void OccPersistData::load()
836{
Chris Cain36f9cde2021-11-22 11:18:21 -0600837 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600838 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600839
840 if (!std::filesystem::exists(ipath))
841 {
Chris Cain1be43372021-12-09 19:29:37 -0600842 modeData.modeInitialized = false;
843 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600844 return;
845 }
846
Chris Cain37abe9b2024-10-31 17:20:31 -0500847 lg2::debug(
848 "OccPersistData::load: Reading Power Mode persisted data from {FILE}",
849 "FILE", ipath);
Chris Cain36f9cde2021-11-22 11:18:21 -0600850 try
851 {
852 std::ifstream stream{ipath.c_str()};
853 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600854 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600855 }
856 catch (const std::exception& e)
857 {
858 auto error = errno;
Chris Cain37abe9b2024-10-31 17:20:31 -0500859 lg2::error("OccPersistData::load: failed to read {FILE}, errno={ERR}",
860 "FILE", ipath, "ERR", error);
Chris Cain1be43372021-12-09 19:29:37 -0600861 modeData.modeInitialized = false;
862 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600863 }
864
Chris Caincde7bea2022-01-28 15:54:24 -0600865 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600866}
867
Chris Cain1be43372021-12-09 19:29:37 -0600868// Called when PowerModeProperties defaults are available on DBus
Patrick Williamsaf408082022-07-22 19:26:54 -0500869void PowerMode::defaultsReady(sdbusplus::message_t& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600870{
Chris Cain1be43372021-12-09 19:29:37 -0600871 std::map<std::string, std::variant<std::string>> properties{};
872 std::string interface;
873 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600874
Chris Cain30040d92024-06-19 16:50:34 -0500875 if (persistedData.modeAvailable())
876 {
877 // Validate persisted mode is supported
878 SysPwrMode pMode = SysPwrMode::NO_CHANGE;
879 uint16_t oemModeData = 0;
880 persistedData.getMode(pMode, oemModeData);
881 if (!isValidMode(pMode))
882 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500883 lg2::error(
884 "defaultsReady: Persisted power mode ({MODE}/{DATA}) is not valid. Reading system default mode",
885 "MODE", pMode, "DATA", oemModeData);
Chris Cain30040d92024-06-19 16:50:34 -0500886 persistedData.invalidateMode();
887 }
888 }
889
Chris Cain1be43372021-12-09 19:29:37 -0600890 // If persistent data exists, then don't need to read defaults
891 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600892 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500893 lg2::info(
894 "Default PowerModeProperties are now available (persistent modeAvail={MAVAIL}, ipsAvail={IAVAIL})",
895 "MAVAIL", persistedData.modeAvailable(), "IAVAIL",
896 persistedData.ipsAvailable());
Chris Cain1be43372021-12-09 19:29:37 -0600897
898 // Read default power mode defaults and update DBus
899 initPersistentData();
900 }
901}
902
903// Get the default power mode from DBus and return true if success
904bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
905{
906 try
907 {
908 auto& bus = utils::getBus();
909 std::string path = "/";
910 std::string service =
911 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -0400912 auto method =
913 bus.new_method_call(service.c_str(), path.c_str(),
914 "org.freedesktop.DBus.Properties", "Get");
Chris Cain1be43372021-12-09 19:29:37 -0600915 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
916 auto reply = bus.call(method);
917
918 std::variant<std::string> stateEntryValue;
919 reply.read(stateEntryValue);
920 auto propVal = std::get<std::string>(stateEntryValue);
921
Patrick Williamsd7542c82024-08-16 15:20:28 -0400922 const std::string fullModeString =
923 PMODE_INTERFACE + ".PowerMode."s + propVal;
Chris Cain1be43372021-12-09 19:29:37 -0600924 defaultMode = powermode::convertStringToMode(fullModeString);
925 if (!VALID_POWER_MODE_SETTING(defaultMode))
926 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500927 lg2::error("PowerMode::getDefaultMode: Invalid "
928 "default power mode found: {MODE}",
929 "MODE", defaultMode);
Chris Cain1be43372021-12-09 19:29:37 -0600930 // If default was read but not valid, use Max Performance
931 defaultMode = SysPwrMode::MAX_PERF;
932 return true;
933 }
934 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500935 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600936 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500937 lg2::error("Unable to read Default Power Mode: {ERR}", "ERR", e.what());
Chris Cain1be43372021-12-09 19:29:37 -0600938 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600939 }
940
Chris Cain1be43372021-12-09 19:29:37 -0600941 return true;
942}
Chris Cain36f9cde2021-11-22 11:18:21 -0600943
Chris Cain1be43372021-12-09 19:29:37 -0600944/* Get the default Idle Power Saver properties and return true if successful */
945bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
946 uint16_t& enterTime, uint8_t& exitUtil,
947 uint16_t& exitTime)
948{
949 // Defaults:
950 ipsEnabled = true; // Enabled
951 enterUtil = 8; // Enter Utilization (8%)
952 enterTime = 240; // Enter Delay Time (240s)
953 exitUtil = 12; // Exit Utilization (12%)
954 exitTime = 10; // Exit Delay Time (10s)
955
956 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
957 ipsProperties{};
958
959 // Get all IPS properties from DBus
960 try
961 {
962 auto& bus = utils::getBus();
963 std::string path = "/";
964 std::string service =
965 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -0400966 auto method =
967 bus.new_method_call(service.c_str(), path.c_str(),
968 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain1be43372021-12-09 19:29:37 -0600969 method.append(PMODE_DEFAULT_INTERFACE);
970 auto reply = bus.call(method);
971 reply.read(ipsProperties);
972 }
Patrick Williamsaf408082022-07-22 19:26:54 -0500973 catch (const sdbusplus::exception_t& e)
Chris Cain1be43372021-12-09 19:29:37 -0600974 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500975 lg2::error(
976 "Unable to read Default Idle Power Saver parameters so it will be disabled: {ERR}",
977 "ERR", e.what());
Chris Cain1be43372021-12-09 19:29:37 -0600978 return false;
979 }
980
981 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
982 if (ipsEntry != ipsProperties.end())
983 {
984 ipsEnabled = std::get<bool>(ipsEntry->second);
985 }
986 else
987 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500988 lg2::error(
Chris Cain1be43372021-12-09 19:29:37 -0600989 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
990 }
991
992 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
993 if (ipsEntry != ipsProperties.end())
994 {
995 enterUtil = std::get<uint64_t>(ipsEntry->second);
996 }
997 else
998 {
Chris Cain37abe9b2024-10-31 17:20:31 -0500999 lg2::error(
Chris Cain1be43372021-12-09 19:29:37 -06001000 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
1001 }
1002
1003 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
1004 if (ipsEntry != ipsProperties.end())
1005 {
1006 enterTime = std::get<uint64_t>(ipsEntry->second);
1007 }
1008 else
1009 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001010 lg2::error(
Chris Cain1be43372021-12-09 19:29:37 -06001011 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
1012 }
1013
1014 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
1015 if (ipsEntry != ipsProperties.end())
1016 {
1017 exitUtil = std::get<uint64_t>(ipsEntry->second);
1018 }
1019 else
1020 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001021 lg2::error(
Chris Cain1be43372021-12-09 19:29:37 -06001022 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
1023 }
1024
1025 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
1026 if (ipsEntry != ipsProperties.end())
1027 {
1028 exitTime = std::get<uint64_t>(ipsEntry->second);
1029 }
1030 else
1031 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001032 lg2::error(
Chris Cain1be43372021-12-09 19:29:37 -06001033 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
1034 }
1035
1036 if (enterUtil > exitUtil)
1037 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001038 lg2::error(
1039 "ERROR: Default Idle Power Saver Enter Utilization ({ENTER}%) is > Exit Utilization ({EXIT}%) - using Exit for both",
1040 "ENTER", enterUtil, "EXIT", exitUtil);
Chris Cain1be43372021-12-09 19:29:37 -06001041 enterUtil = exitUtil;
1042 }
1043
1044 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -06001045}
1046
Chris Caincde7bea2022-01-28 15:54:24 -06001047/* Read default IPS parameters, save them to the persistent file and update
1048 DBus. Return true if successful */
1049bool PowerMode::useDefaultIPSParms()
1050{
1051 // Read the default IPS parameters
1052 bool ipsEnabled;
1053 uint8_t enterUtil, exitUtil;
1054 uint16_t enterTime, exitTime;
1055 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
1056 exitTime))
1057 {
1058 // Unable to read defaults
1059 return false;
1060 }
Chris Cain37abe9b2024-10-31 17:20:31 -05001061 lg2::info("PowerMode::useDefaultIPSParms: Using default IPS parms: "
1062 "Enabled: {ENABLE}, EnterUtil: {EUTIL}%, EnterTime: {ETIME}s, "
1063 "ExitUtil: {XUTIL}%, ExitTime: {XTIME}s",
1064 "ENABLE", ipsEnabled, "EUTIL", enterUtil, "ETIME", enterTime,
1065 "XUTIL", exitUtil, "XTIME", exitTime);
Chris Caincde7bea2022-01-28 15:54:24 -06001066
1067 // Save IPS parms to the persistent file
1068 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
1069 exitTime);
1070
1071 // Write IPS parms to DBus
1072 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
1073}
1074
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001075// Starts to watch for IPS active state changes.
1076bool PowerMode::openIpsFile()
1077{
1078 bool rc = true;
1079 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
1080 const int open_errno = errno;
1081 if (fd < 0)
1082 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001083 lg2::error("openIpsFile Error({ERR})={STR} : File={FILE}", "ERR",
1084 open_errno, "STR", strerror(open_errno), "FILE",
1085 ipsStatusFile);
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001086
1087 close(fd);
1088
1089 using namespace sdbusplus::org::open_power::OCC::Device::Error;
1090 report<OpenFailure>(
1091 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1092 CALLOUT_ERRNO(open_errno),
1093 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
1094 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1095
1096 // We are no longer watching the error
Chris Cain1fe436d2024-10-10 09:41:03 -05001097 if (ipsObject)
1098 {
1099 ipsObject->active(false);
1100 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001101
1102 watching = false;
1103 rc = false;
1104 // NOTE: this will leave the system not reporting IPS active state to
1105 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1106 }
1107 return rc;
1108}
1109
1110// Starts to watch for IPS active state changes.
1111void PowerMode::addIpsWatch(bool poll)
1112{
1113 // open file and register callback on file if we are not currently watching,
1114 // and if poll=true, and if we are the master.
1115 if ((!watching) && poll)
1116 {
1117 // Open the file
1118 if (openIpsFile())
1119 {
1120 // register the callback handler which sets 'watching'
1121 registerIpsStatusCallBack();
1122 }
1123 }
1124}
1125
1126// Stops watching for IPS active state changes.
1127void PowerMode::removeIpsWatch()
1128{
1129 // NOTE: we want to remove event, close file, and IPS active false no
1130 // matter what the 'watching' flags is set to.
1131
1132 // We are no longer watching the error
Chris Cain1fe436d2024-10-10 09:41:03 -05001133 if (ipsObject)
1134 {
1135 ipsObject->active(false);
1136 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001137
1138 watching = false;
1139
1140 // Close file
1141 close(fd);
1142
1143 // clears sourcePtr in the event source.
1144 eventSource.reset();
1145}
1146
1147// Attaches the FD to event loop and registers the callback handler
1148void PowerMode::registerIpsStatusCallBack()
1149{
1150 decltype(eventSource.get()) sourcePtr = nullptr;
1151
1152 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
1153 ipsStatusCallBack, this);
1154 if (r < 0)
1155 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001156 lg2::error("sd_event_add_io: Error({ERR})={STR} : File={FILE}", "ERR",
1157 r, "STR", strerror(-r), "FILE", ipsStatusFile);
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001158
1159 using InternalFailure =
1160 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
1161 report<InternalFailure>();
1162
1163 removeIpsWatch();
1164 // NOTE: this will leave the system not reporting IPS active state to
1165 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1166 }
1167 else
1168 {
1169 // puts sourcePtr in the event source.
1170 eventSource.reset(sourcePtr);
1171 // Set we are watching the error
1172 watching = true;
1173 }
1174}
1175
1176// Static function to redirect to non static analyze event function to be
1177// able to read file and push onto dBus.
1178int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
1179 uint32_t /*revents*/, void* userData)
1180{
1181 auto pmode = static_cast<PowerMode*>(userData);
1182 pmode->analyzeIpsEvent();
1183 return 0;
1184}
1185
1186// Function to Read SysFs file change on IPS state and push on dBus.
1187void PowerMode::analyzeIpsEvent()
1188{
1189 // Need to seek to START, else the poll returns immediately telling
1190 // there is data to be read. if not done this floods the system.
1191 auto r = lseek(fd, 0, SEEK_SET);
1192 const int open_errno = errno;
1193 if (r < 0)
1194 {
1195 // NOTE: upon file access error we can not just re-open file, we have to
1196 // remove and add to watch.
1197 removeIpsWatch();
1198 addIpsWatch(true);
1199 }
1200
1201 // if we are 'watching' that is the file seek, or the re-open passed.. we
1202 // can read the data
1203 if (watching)
1204 {
1205 // This file gets created when polling OCCs. A value or length of 0 is
1206 // deemed success. That means we would disable IPS active on dbus.
1207 char data;
1208 bool ipsState = false;
1209 const auto len = read(fd, &data, sizeof(data));
1210 const int readErrno = errno;
1211 if (len <= 0)
1212 {
1213 removeIpsWatch();
1214
Chris Cain37abe9b2024-10-31 17:20:31 -05001215 lg2::error(
1216 "IPS state Read Error({ERR})={STR} : File={FILE} : len={LEN}",
1217 "ERR", readErrno, "STR", strerror(readErrno), "FILE",
1218 ipsStatusFile, "LEN", len);
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001219
1220 report<ReadFailure>(
1221 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1222 CALLOUT_ERRNO(readErrno),
1223 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1224 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1225
1226 // NOTE: this will leave the system not reporting IPS active state
1227 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1228 // again.
1229 }
1230 else
1231 {
1232 // Data returned in ASCII.
1233 // convert to integer. atoi()
1234 // from OCC_P10_FW_Interfaces spec
1235 // Bit 6: IPS active 1 indicates enabled.
1236 // Bit 7: IPS enabled. 1 indicates enabled.
1237 // mask off bit 6 --> & 0x02
1238 // Shift left one bit and store as bool. >> 1
1239 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1240 }
1241
1242 // This will only set IPS active dbus if different than current.
Chris Cain1fe436d2024-10-10 09:41:03 -05001243 if (ipsObject)
1244 {
1245 ipsObject->active(ipsState);
1246 }
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001247 }
1248 else
1249 {
1250 removeIpsWatch();
1251
1252 // If the Retry did not get to "watching = true" we already have an
1253 // error log, just post trace.
Chris Cain37abe9b2024-10-31 17:20:31 -05001254 lg2::error("Retry on File seek Error({ERR})={STR} : File={FILE}", "ERR",
1255 open_errno, "STR", strerror(open_errno), "FILE",
1256 ipsStatusFile);
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001257
1258 // NOTE: this will leave the system not reporting IPS active state to
1259 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1260 }
1261
1262 return;
1263}
Ben Tyner3576d652022-05-22 18:05:53 -05001264
1265// overrides read/write to powerMode dbus property.
Chris Cain30040d92024-06-19 16:50:34 -05001266Mode::PowerMode PowerMode::powerMode(Mode::PowerMode requestedMode)
Ben Tyner3576d652022-05-22 18:05:53 -05001267{
1268 if (persistedData.getModeLock())
1269 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001270 lg2::info("PowerMode::powerMode: mode property change blocked");
Ben Tyner3576d652022-05-22 18:05:53 -05001271 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
1272 "mode change not allowed due to lock"));
Ben Tyner3576d652022-05-22 18:05:53 -05001273 }
1274 else
1275 {
Chris Cain30040d92024-06-19 16:50:34 -05001276 // Verify requested mode is allowed
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05001277
Chris Cain30040d92024-06-19 16:50:34 -05001278 // Convert PowerMode to internal SysPwrMode
1279 SysPwrMode newMode = getInternalMode(requestedMode);
1280 if (newMode != SysPwrMode::NO_CHANGE)
1281 {
1282 // Validate it is an allowed customer mode
1283 if (customerModeList.contains(newMode))
1284 {
1285 // Update persisted data with new mode
1286 persistedData.updateMode(newMode, 0);
1287
Chris Cain37abe9b2024-10-31 17:20:31 -05001288 lg2::info("DBus PowerMode Changed: {MODE}", "MODE",
1289 convertPowerModeToString(requestedMode));
Chris Cain30040d92024-06-19 16:50:34 -05001290
1291 // Send mode change to OCC
1292 sendModeChange();
1293 }
1294 else
1295 {
1296 // Not Allowed
Chris Cain37abe9b2024-10-31 17:20:31 -05001297 lg2::error(
1298 "PowerMode change not allowed. {MODE} is not in AllowedPowerModes",
1299 "MODE", convertPowerModeToString(requestedMode));
Chris Cain30040d92024-06-19 16:50:34 -05001300 elog<NotAllowed>(
1301 xyz::openbmc_project::Common::NotAllowed::REASON(
1302 "PowerMode value not allowed"));
1303 }
1304 }
1305 else
1306 {
1307 // Value is not valid
1308 using InvalidArgument =
1309 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
1310 using Argument = xyz::openbmc_project::Common::InvalidArgument;
Chris Cain37abe9b2024-10-31 17:20:31 -05001311 lg2::error(
1312 "PowerMode not valid. {MODE} is not in AllowedPowerModes",
1313 "MODE", convertPowerModeToString(requestedMode));
Chris Cain30040d92024-06-19 16:50:34 -05001314 elog<InvalidArgument>(Argument::ARGUMENT_NAME("PowerMode"),
1315 Argument::ARGUMENT_VALUE("INVALID MODE"));
1316 }
1317 }
1318
1319 // All elog<> calls will cause trap (so code will not make it here)
1320
1321 return Mode::powerMode(requestedMode);
1322}
1323
1324/* Set dbus property to SAFE mode(true) or clear(false) only if different
1325 */
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001326void PowerMode::updateDbusSafeMode(const bool safeModeReq)
1327{
Chris Cain37abe9b2024-10-31 17:20:31 -05001328 lg2::debug("PowerMode:updateDbusSafeMode: Update dbus state ({STATE})",
1329 "STATE", safeModeReq);
Sheldon Bailey31a2f132022-05-20 11:31:52 -05001330
1331 // Note; this function checks and only updates if different.
1332 Mode::safeMode(safeModeReq);
1333}
1334
Chris Cain30040d92024-06-19 16:50:34 -05001335// Get the supported power modes from DBus and return true if success
1336bool PowerMode::getSupportedModes()
1337{
1338 bool foundCustomerMode = false;
1339 using ModePropertyVariants =
1340 std::variant<bool, uint8_t, uint16_t, std::vector<std::string>>;
1341 std::map<std::string, ModePropertyVariants> powerModeProperties{};
1342
1343 // Get all power mode properties from DBus
1344 try
1345 {
1346 auto& bus = utils::getBus();
1347 std::string path = "/";
1348 std::string service =
1349 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
Patrick Williamsd7542c82024-08-16 15:20:28 -04001350 auto method =
1351 bus.new_method_call(service.c_str(), path.c_str(),
1352 "org.freedesktop.DBus.Properties", "GetAll");
Chris Cain30040d92024-06-19 16:50:34 -05001353 method.append(PMODE_DEFAULT_INTERFACE);
1354 auto reply = bus.call(method);
1355 reply.read(powerModeProperties);
1356 }
1357 catch (const sdbusplus::exception_t& e)
1358 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001359 lg2::error("Unable to read PowerModeProperties: {ERR}", "ERR",
1360 e.what());
Chris Cain30040d92024-06-19 16:50:34 -05001361 return false;
1362 }
1363
1364 // Determine if system suports EcoModes
1365 auto ecoSupport = powerModeProperties.find("EcoModeSupport");
1366 if (ecoSupport != powerModeProperties.end())
1367 {
1368 ecoModeSupport = std::get<bool>(ecoSupport->second);
Chris Cain37abe9b2024-10-31 17:20:31 -05001369 lg2::info("getSupportedModes(): ecoModeSupport: {SUPPORT}", "SUPPORT",
1370 ecoModeSupport);
Chris Cain30040d92024-06-19 16:50:34 -05001371 }
1372
1373 // Determine what customer modes are supported
1374 using PMode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
1375 std::set<PMode::PowerMode> modesToAllow;
1376 auto custList = powerModeProperties.find("CustomerModes");
1377 if (custList != powerModeProperties.end())
1378 {
1379 auto modeList = std::get<std::vector<std::string>>(custList->second);
1380 for (auto mode : modeList)
1381 {
1382 // Ensure mode is valid
Patrick Williamsd7542c82024-08-16 15:20:28 -04001383 const std::string fullModeString =
1384 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain37abe9b2024-10-31 17:20:31 -05001385 lg2::info("getSupportedModes(): {MODE}", "MODE", mode);
Chris Cain30040d92024-06-19 16:50:34 -05001386 SysPwrMode modeValue =
1387 powermode::convertStringToMode(fullModeString);
1388 if (VALID_POWER_MODE_SETTING(modeValue))
1389 {
1390 if (!foundCustomerMode)
1391 {
1392 // Start with empty list
1393 customerModeList.clear();
1394 foundCustomerMode = true;
1395 }
1396 // Add mode to list
1397 std::optional<PMode::PowerMode> cMode =
1398 PMode::convertStringToPowerMode(fullModeString);
1399 if (cMode)
1400 modesToAllow.insert(cMode.value());
1401 customerModeList.insert(modeValue);
1402 }
1403 else
1404 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001405 lg2::error(
1406 "getSupportedModes(): Ignoring unsupported customer mode {MODE}",
1407 "MODE", mode);
Chris Cain30040d92024-06-19 16:50:34 -05001408 }
1409 }
1410 }
1411 if (foundCustomerMode)
1412 {
1413 ModeInterface::allowedPowerModes(modesToAllow);
1414 }
1415
1416 // Determine what OEM modes are supported
1417 auto oemList = powerModeProperties.find("OemModes");
1418 if (oemList != powerModeProperties.end())
1419 {
1420 bool foundValidMode = false;
1421 auto OmodeList = std::get<std::vector<std::string>>(oemList->second);
1422 for (auto mode : OmodeList)
1423 {
1424 // Ensure mode is valid
Patrick Williamsd7542c82024-08-16 15:20:28 -04001425 const std::string fullModeString =
1426 PMODE_INTERFACE + ".PowerMode."s + mode;
Chris Cain30040d92024-06-19 16:50:34 -05001427 SysPwrMode modeValue =
1428 powermode::convertStringToMode(fullModeString);
1429 if (VALID_POWER_MODE_SETTING(modeValue) ||
1430 VALID_OEM_POWER_MODE_SETTING(modeValue))
1431 {
1432 if (!foundValidMode)
1433 {
1434 // Start with empty list
1435 oemModeList.clear();
1436 foundValidMode = true;
1437 }
1438 // Add mode to list
1439 oemModeList.insert(modeValue);
1440 }
1441 else
1442 {
Chris Cain37abe9b2024-10-31 17:20:31 -05001443 lg2::error(
1444 "getSupportedModes(): Ignoring unsupported OEM mode {MODE}",
1445 "MODE", mode);
Chris Cain30040d92024-06-19 16:50:34 -05001446 }
1447 }
1448 }
1449
1450 return foundCustomerMode;
1451}
1452
1453bool PowerMode::isValidMode(const SysPwrMode mode)
1454{
1455 if (customerModeList.contains(mode) || oemModeList.contains(mode))
1456 {
1457 return true;
1458 }
1459 return false;
1460}
1461
Chris Cain78e86012021-03-04 16:15:31 -06001462} // namespace powermode
1463
1464} // namespace occ
1465
1466} // namespace open_power