xref: /openbmc/openpower-occ-control/powermode.cpp (revision cde7bea3d41e51204dad61761d46af70b34a787a)
1 #include "powermode.hpp"
2 
3 #include <fmt/core.h>
4 
5 #include <com/ibm/Host/Target/server.hpp>
6 #include <phosphor-logging/log.hpp>
7 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
8 
9 #include <cassert>
10 #include <fstream>
11 #include <regex>
12 
13 namespace open_power
14 {
15 namespace occ
16 {
17 namespace powermode
18 {
19 
20 using namespace phosphor::logging;
21 using namespace std::literals::string_literals;
22 
23 using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
24 
25 // Set the Master OCC
26 void PowerMode::setMasterOcc(const std::string& masterOccPath)
27 {
28     if (masterOccSet)
29     {
30         if (masterOccPath != path)
31         {
32             log<level::ERR>(
33                 fmt::format(
34                     "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
35                     occInstance, masterOccPath)
36                     .c_str());
37             if (occCmd)
38             {
39                 occCmd.reset();
40             }
41         }
42     }
43     path = masterOccPath;
44     occInstance = path.back() - '0';
45     log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})",
46                                   occInstance, path.c_str())
47                           .c_str());
48     if (!occCmd)
49     {
50         occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
51                                                                path.c_str());
52     }
53     masterOccSet = true;
54 };
55 
56 // Called when DBus power mode gets changed
57 void PowerMode::modeChanged(sdbusplus::message::message& msg)
58 {
59     std::map<std::string, std::variant<std::string>> properties{};
60     std::string interface;
61     std::string propVal;
62     msg.read(interface, properties);
63     const auto modeEntry = properties.find(POWER_MODE_PROP);
64     if (modeEntry != properties.end())
65     {
66         auto modeEntryValue = modeEntry->second;
67         propVal = std::get<std::string>(modeEntryValue);
68         SysPwrMode newMode = convertStringToMode(propVal);
69         if (newMode != SysPwrMode::NO_CHANGE)
70         {
71             // Update persisted data with new mode
72             persistedData.updateMode(newMode, 0);
73 
74             log<level::INFO>(
75                 fmt::format("DBus Power Mode Changed: {}", propVal).c_str());
76 
77             // Send mode change to OCC
78             sendModeChange();
79         }
80     }
81 }
82 
83 // Called from OCC PassThrough interface (via CE login / BMC command line)
84 bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
85 {
86     if (updateDbusMode(newMode) == false)
87     {
88         // Unsupported mode
89         return false;
90     }
91 
92     // Save mode
93     persistedData.updateMode(newMode, oemModeData);
94 
95     // Send mode change to OCC
96     if (sendModeChange() != CmdStatus::SUCCESS)
97     {
98         // Mode change failed
99         return false;
100     }
101 
102     return true;
103 }
104 
105 // Convert PowerMode string to OCC SysPwrMode
106 // Returns NO_CHANGE if OEM or unsupported mode
107 SysPwrMode convertStringToMode(const std::string& i_modeString)
108 {
109     SysPwrMode pmode = SysPwrMode::NO_CHANGE;
110 
111     Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString);
112     if (mode == Mode::PowerMode::MaximumPerformance)
113     {
114         pmode = SysPwrMode::MAX_PERF;
115     }
116     else if (mode == Mode::PowerMode::PowerSaving)
117     {
118         pmode = SysPwrMode::POWER_SAVING;
119     }
120     else if (mode == Mode::PowerMode::Static)
121     {
122         pmode = SysPwrMode::STATIC;
123     }
124     else
125     {
126         if (mode != Mode::PowerMode::OEM)
127         {
128             log<level::ERR>(
129                 fmt::format(
130                     "convertStringToMode: Invalid Power Mode specified: {}",
131                     i_modeString)
132                     .c_str());
133         }
134     }
135 
136     return pmode;
137 }
138 
139 // Check if Hypervisor target is PowerVM
140 bool isPowerVM()
141 {
142     namespace Hyper = sdbusplus::com::ibm::Host::server;
143     constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
144     constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
145     constexpr auto HYPE_PROP = "Target";
146 
147     bool powerVmTarget = false;
148 
149     // This will throw exception on failure
150     auto& bus = utils::getBus();
151     auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
152     auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
153                                       "org.freedesktop.DBus.Properties", "Get");
154     method.append(HYPE_INTERFACE, HYPE_PROP);
155     auto reply = bus.call(method);
156 
157     std::variant<std::string> hyperEntryValue;
158     reply.read(hyperEntryValue);
159     auto propVal = std::get<std::string>(hyperEntryValue);
160     if (Hyper::Target::convertHypervisorFromString(propVal) ==
161         Hyper::Target::Hypervisor::PowerVM)
162     {
163         powerVmTarget = true;
164     }
165 
166     log<level::DEBUG>(
167         fmt::format("isPowerVM returning {}", powerVmTarget).c_str());
168 
169     return powerVmTarget;
170 }
171 
172 // Initialize persistent data and return true if successful
173 bool PowerMode::initPersistentData()
174 {
175     if (!persistedData.modeAvailable())
176     {
177         // Read the default mode
178         SysPwrMode currentMode;
179         if (!getDefaultMode(currentMode))
180         {
181             // Unable to read defaults
182             return false;
183         }
184         log<level::INFO>(
185             fmt::format("PowerMode::initPersistentData: Using default mode: {}",
186                         currentMode)
187                 .c_str());
188 
189         // Save default mode as current mode
190         persistedData.updateMode(currentMode, 0);
191 
192         // Write default mode to DBus
193         updateDbusMode(currentMode);
194     }
195 
196     if (!persistedData.ipsAvailable())
197     {
198         // Read the default IPS parameters, write persistent file and update
199         // DBus
200         return useDefaultIPSParms();
201     }
202     return true;
203 }
204 
205 // Get the requested power mode and return true if successful
206 bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
207 {
208     currentMode = SysPwrMode::NO_CHANGE;
209     oemModeData = 0;
210 
211     if (!persistedData.getMode(currentMode, oemModeData))
212     {
213         // Persistent data not initialized, read defaults and update DBus
214         if (!initPersistentData())
215         {
216             // Unable to read defaults from entity manager yet
217             return false;
218         }
219         return persistedData.getMode(currentMode, oemModeData);
220     }
221 
222     return true;
223 }
224 
225 // Set the power mode on DBus
226 bool PowerMode::updateDbusMode(const SysPwrMode newMode)
227 {
228     if (!VALID_POWER_MODE_SETTING(newMode) &&
229         !VALID_OEM_POWER_MODE_SETTING(newMode))
230     {
231         log<level::ERR>(
232             fmt::format(
233                 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
234                 newMode)
235                 .c_str());
236         return false;
237     }
238 
239     // Convert mode for DBus
240     ModeInterface::PowerMode dBusMode;
241     switch (newMode)
242     {
243         case SysPwrMode::STATIC:
244             dBusMode = Mode::PowerMode::Static;
245             break;
246         case SysPwrMode::POWER_SAVING:
247             dBusMode = Mode::PowerMode::PowerSaving;
248             break;
249         case SysPwrMode::MAX_PERF:
250             dBusMode = Mode::PowerMode::MaximumPerformance;
251             break;
252         default:
253             dBusMode = Mode::PowerMode::OEM;
254     }
255 
256     // true = skip update signal
257     ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
258 
259     return true;
260 }
261 
262 // Send mode change request to the master OCC
263 CmdStatus PowerMode::sendModeChange()
264 {
265     CmdStatus status;
266 
267     if (!masterActive || !masterOccSet)
268     {
269         // Nothing to do
270         log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
271         return CmdStatus::SUCCESS;
272     }
273 
274     if (!isPowerVM())
275     {
276         // Mode change is only supported on PowerVM systems
277         log<level::DEBUG>(
278             "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
279         return CmdStatus::SUCCESS;
280     }
281 
282     SysPwrMode newMode;
283     uint16_t oemModeData = 0;
284     getMode(newMode, oemModeData);
285 
286     if (VALID_POWER_MODE_SETTING(newMode) ||
287         VALID_OEM_POWER_MODE_SETTING(newMode))
288     {
289         std::vector<std::uint8_t> cmd, rsp;
290         cmd.reserve(9);
291         cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
292         cmd.push_back(0x00); // Data Length (2 bytes)
293         cmd.push_back(0x06);
294         cmd.push_back(0x30); // Data (Version)
295         cmd.push_back(uint8_t(OccState::NO_CHANGE));
296         cmd.push_back(uint8_t(newMode));
297         cmd.push_back(oemModeData >> 8);   // Mode Data (Freq Point)
298         cmd.push_back(oemModeData & 0xFF); //
299         cmd.push_back(0x00);               // reserved
300         log<level::INFO>(
301             fmt::format(
302                 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
303                 newMode, oemModeData, occInstance, cmd.size())
304                 .c_str());
305         status = occCmd->send(cmd, rsp);
306         if (status == CmdStatus::SUCCESS)
307         {
308             if (rsp.size() == 5)
309             {
310                 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
311                 {
312                     log<level::ERR>(
313                         fmt::format(
314                             "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
315                             rsp[2])
316                             .c_str());
317                     dump_hex(rsp);
318                     status = CmdStatus::FAILURE;
319                 }
320             }
321             else
322             {
323                 log<level::ERR>(
324                     "PowerMode::sendModeChange: INVALID SET MODE response");
325                 dump_hex(rsp);
326                 status = CmdStatus::FAILURE;
327             }
328         }
329         else
330         {
331             if (status == CmdStatus::OPEN_FAILURE)
332             {
333                 // OCC not active yet
334                 status = CmdStatus::SUCCESS;
335             }
336             else
337             {
338                 log<level::ERR>("PowerMode::sendModeChange: SET_MODE FAILED!");
339             }
340         }
341     }
342     else
343     {
344         log<level::ERR>(
345             fmt::format(
346                 "PowerMode::sendModeChange: Unable to set power mode to {}",
347                 newMode)
348                 .c_str());
349         status = CmdStatus::FAILURE;
350     }
351 
352     return status;
353 }
354 
355 void PowerMode::ipsChanged(sdbusplus::message::message& msg)
356 {
357     bool parmsChanged = false;
358     std::string interface;
359     std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
360         ipsProperties{};
361     msg.read(interface, ipsProperties);
362 
363     // Read persisted values
364     bool ipsEnabled;
365     uint8_t enterUtil, exitUtil;
366     uint16_t enterTime, exitTime;
367     getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
368 
369     // Check for any changed data
370     auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
371     if (ipsEntry != ipsProperties.end())
372     {
373         ipsEnabled = std::get<bool>(ipsEntry->second);
374         log<level::INFO>(
375             fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
376                 .c_str());
377         parmsChanged = true;
378     }
379     ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
380     if (ipsEntry != ipsProperties.end())
381     {
382         enterUtil = std::get<uint8_t>(ipsEntry->second);
383         log<level::INFO>(
384             fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
385                 .c_str());
386         parmsChanged = true;
387     }
388     ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
389     if (ipsEntry != ipsProperties.end())
390     {
391         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
392         enterTime =
393             std::chrono::duration_cast<std::chrono::seconds>(ms).count();
394         log<level::INFO>(
395             fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
396                 .c_str());
397         parmsChanged = true;
398     }
399     ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
400     if (ipsEntry != ipsProperties.end())
401     {
402         exitUtil = std::get<uint8_t>(ipsEntry->second);
403         log<level::INFO>(
404             fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
405                 .c_str());
406         parmsChanged = true;
407     }
408     ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
409     if (ipsEntry != ipsProperties.end())
410     {
411         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
412         exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
413         log<level::INFO>(
414             fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
415                 .c_str());
416         parmsChanged = true;
417     }
418 
419     if (parmsChanged)
420     {
421         if (exitUtil == 0)
422         {
423             // Setting the exitUtil to 0 will force restoring the default IPS
424             // parmeters (0 is not valid exit utilization)
425             log<level::INFO>(
426                 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
427             // Read the default IPS parameters, write persistent file and update
428             // DBus
429             useDefaultIPSParms();
430         }
431         else
432         {
433             // Update persistant data with new DBus values
434             persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
435                                     exitTime);
436         }
437 
438         // Trigger IPS data to get sent to the OCC
439         sendIpsData();
440     }
441 
442     return;
443 }
444 
445 /** @brief Get the Idle Power Saver properties from persisted data
446  * @return true if IPS parameters were read
447  */
448 bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
449                             uint16_t& enterTime, uint8_t& exitUtil,
450                             uint16_t& exitTime)
451 {
452     // Defaults:
453     ipsEnabled = true; // Enabled
454     enterUtil = 8;     // Enter Utilization (8%)
455     enterTime = 240;   // Enter Delay Time (240s)
456     exitUtil = 12;     // Exit Utilization (12%)
457     exitTime = 10;     // Exit Delay Time (10s)
458 
459     if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
460                               exitTime))
461     {
462         // Persistent data not initialized, read defaults and update DBus
463         if (!initPersistentData())
464         {
465             // Unable to read defaults from entity manager yet
466             return false;
467         }
468 
469         persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
470                              exitTime);
471     }
472 
473     if (enterUtil > exitUtil)
474     {
475         log<level::ERR>(
476             fmt::format(
477                 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
478                 enterUtil, exitUtil)
479                 .c_str());
480         enterUtil = exitUtil;
481     }
482 
483     return true;
484 }
485 
486 // Set the Idle Power Saver data on DBus
487 bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
488                               const uint16_t enterTime, const uint8_t exitUtil,
489                               const uint16_t exitTime)
490 {
491     // true = skip update signal
492     IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
493     IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
494     // Convert time from seconds to ms
495     uint64_t msTime = enterTime * 1000;
496     IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
497     IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
498     msTime = exitTime * 1000;
499     IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
500 
501     return true;
502 }
503 
504 // Send Idle Power Saver config data to the master OCC
505 CmdStatus PowerMode::sendIpsData()
506 {
507     if (!masterActive || !masterOccSet)
508     {
509         // Nothing to do
510         return CmdStatus::SUCCESS;
511     }
512 
513     if (!isPowerVM())
514     {
515         // Idle Power Saver data is only supported on PowerVM systems
516         log<level::DEBUG>(
517             "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
518         return CmdStatus::SUCCESS;
519     }
520 
521     bool ipsEnabled;
522     uint8_t enterUtil, exitUtil;
523     uint16_t enterTime, exitTime;
524     getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
525 
526     log<level::INFO>(
527         fmt::format(
528             "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
529             ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
530             .c_str());
531 
532     std::vector<std::uint8_t> cmd, rsp;
533     cmd.reserve(12);
534     cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
535     cmd.push_back(0x00);               // Data Length (2 bytes)
536     cmd.push_back(0x09);               //
537     cmd.push_back(0x11);               // Config Format: IPS Settings
538     cmd.push_back(0x00);               // Version
539     cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
540     cmd.push_back(enterTime >> 8);     // Enter Delay Time
541     cmd.push_back(enterTime & 0xFF);   //
542     cmd.push_back(enterUtil);          // Enter Utilization
543     cmd.push_back(exitTime >> 8);      // Exit Delay Time
544     cmd.push_back(exitTime & 0xFF);    //
545     cmd.push_back(exitUtil);           // Exit Utilization
546     log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
547                                  "command to OCC{} ({} bytes)",
548                                  occInstance, cmd.size())
549                          .c_str());
550     CmdStatus status = occCmd->send(cmd, rsp);
551     if (status == CmdStatus::SUCCESS)
552     {
553         if (rsp.size() == 5)
554         {
555             if (RspStatus::SUCCESS != RspStatus(rsp[2]))
556             {
557                 log<level::ERR>(
558                     fmt::format(
559                         "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
560                         rsp[2])
561                         .c_str());
562                 dump_hex(rsp);
563                 status = CmdStatus::FAILURE;
564             }
565         }
566         else
567         {
568             log<level::ERR>(
569                 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
570             dump_hex(rsp);
571             status = CmdStatus::FAILURE;
572         }
573     }
574     else
575     {
576         if (status == CmdStatus::OPEN_FAILURE)
577         {
578             // OCC not active yet
579             status = CmdStatus::SUCCESS;
580         }
581         else
582         {
583             log<level::ERR>(
584                 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
585         }
586     }
587 
588     return status;
589 }
590 
591 // Print the current values
592 void OccPersistData::print()
593 {
594     if (modeData.modeInitialized)
595     {
596         log<level::INFO>(
597             fmt::format(
598                 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
599                 modeData.mode, modeData.oemModeData, modeData.oemModeData)
600                 .c_str());
601     }
602     if (modeData.ipsInitialized)
603     {
604         log<level::INFO>(
605             fmt::format(
606                 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
607                 modeData.ipsEnabled, modeData.ipsEnterUtil,
608                 modeData.ipsEnterTime, modeData.ipsExitUtil,
609                 modeData.ipsExitTime)
610                 .c_str());
611     }
612 }
613 
614 // Saves the OEM mode data in the filesystem using cereal.
615 void OccPersistData::save()
616 {
617     std::filesystem::path opath =
618         std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
619 
620     if (!std::filesystem::exists(opath.parent_path()))
621     {
622         std::filesystem::create_directory(opath.parent_path());
623     }
624 
625     log<level::DEBUG>(
626         fmt::format(
627             "OccPersistData::save: Writing Power Mode persisted data to {}",
628             opath.c_str())
629             .c_str());
630     // print();
631 
632     std::ofstream stream{opath.c_str()};
633     cereal::JSONOutputArchive oarchive{stream};
634 
635     oarchive(modeData);
636 }
637 
638 // Loads the OEM mode data in the filesystem using cereal.
639 void OccPersistData::load()
640 {
641 
642     std::filesystem::path ipath =
643         std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
644 
645     if (!std::filesystem::exists(ipath))
646     {
647         modeData.modeInitialized = false;
648         modeData.ipsInitialized = false;
649         return;
650     }
651 
652     log<level::DEBUG>(
653         fmt::format(
654             "OccPersistData::load: Reading Power Mode persisted data from {}",
655             ipath.c_str())
656             .c_str());
657     try
658     {
659         std::ifstream stream{ipath.c_str()};
660         cereal::JSONInputArchive iarchive(stream);
661         iarchive(modeData);
662     }
663     catch (const std::exception& e)
664     {
665         auto error = errno;
666         log<level::ERR>(
667             fmt::format("OccPersistData::load: failed to read {}, errno={}",
668                         ipath.c_str(), error)
669                 .c_str());
670         modeData.modeInitialized = false;
671         modeData.ipsInitialized = false;
672     }
673 
674     // print();
675 }
676 
677 // Called when PowerModeProperties defaults are available on DBus
678 void PowerMode::defaultsReady(sdbusplus::message::message& msg)
679 {
680     std::map<std::string, std::variant<std::string>> properties{};
681     std::string interface;
682     msg.read(interface, properties);
683 
684     // If persistent data exists, then don't need to read defaults
685     if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
686     {
687         log<level::INFO>(
688             fmt::format(
689                 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
690                 persistedData.modeAvailable() ? 'y' : 'n',
691                 persistedData.modeAvailable() ? 'y' : 'n')
692                 .c_str());
693 
694         // Read default power mode defaults and update DBus
695         initPersistentData();
696     }
697 }
698 
699 // Get the default power mode from DBus and return true if success
700 bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
701 {
702     try
703     {
704         auto& bus = utils::getBus();
705         std::string path = "/";
706         std::string service =
707             utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
708         auto method =
709             bus.new_method_call(service.c_str(), path.c_str(),
710                                 "org.freedesktop.DBus.Properties", "Get");
711         method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
712         auto reply = bus.call(method);
713 
714         std::variant<std::string> stateEntryValue;
715         reply.read(stateEntryValue);
716         auto propVal = std::get<std::string>(stateEntryValue);
717 
718         const std::string fullModeString =
719             PMODE_INTERFACE + ".PowerMode."s + propVal;
720         defaultMode = powermode::convertStringToMode(fullModeString);
721         if (!VALID_POWER_MODE_SETTING(defaultMode))
722         {
723             log<level::ERR>(
724                 fmt::format(
725                     "PowerMode::getDefaultMode: Invalid default power mode found: {}",
726                     defaultMode)
727                     .c_str());
728             // If default was read but not valid, use Max Performance
729             defaultMode = SysPwrMode::MAX_PERF;
730             return true;
731         }
732     }
733     catch (const sdbusplus::exception::exception& e)
734     {
735         log<level::ERR>(
736             fmt::format("Unable to read Default Power Mode: {}", e.what())
737                 .c_str());
738         return false;
739     }
740 
741     return true;
742 }
743 
744 /* Get the default Idle Power Saver properties and return true if successful */
745 bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
746                                    uint16_t& enterTime, uint8_t& exitUtil,
747                                    uint16_t& exitTime)
748 {
749     // Defaults:
750     ipsEnabled = true; // Enabled
751     enterUtil = 8;     // Enter Utilization (8%)
752     enterTime = 240;   // Enter Delay Time (240s)
753     exitUtil = 12;     // Exit Utilization (12%)
754     exitTime = 10;     // Exit Delay Time (10s)
755 
756     std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
757         ipsProperties{};
758 
759     // Get all IPS properties from DBus
760     try
761     {
762         auto& bus = utils::getBus();
763         std::string path = "/";
764         std::string service =
765             utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
766         auto method =
767             bus.new_method_call(service.c_str(), path.c_str(),
768                                 "org.freedesktop.DBus.Properties", "GetAll");
769         method.append(PMODE_DEFAULT_INTERFACE);
770         auto reply = bus.call(method);
771         reply.read(ipsProperties);
772     }
773     catch (const sdbusplus::exception::exception& e)
774     {
775         log<level::ERR>(
776             fmt::format(
777                 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
778                 e.what())
779                 .c_str());
780         return false;
781     }
782 
783     auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
784     if (ipsEntry != ipsProperties.end())
785     {
786         ipsEnabled = std::get<bool>(ipsEntry->second);
787     }
788     else
789     {
790         log<level::ERR>(
791             "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
792     }
793 
794     ipsEntry = ipsProperties.find("EnterUtilizationPercent");
795     if (ipsEntry != ipsProperties.end())
796     {
797         enterUtil = std::get<uint64_t>(ipsEntry->second);
798     }
799     else
800     {
801         log<level::ERR>(
802             "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
803     }
804 
805     ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
806     if (ipsEntry != ipsProperties.end())
807     {
808         enterTime = std::get<uint64_t>(ipsEntry->second);
809     }
810     else
811     {
812         log<level::ERR>(
813             "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
814     }
815 
816     ipsEntry = ipsProperties.find("ExitUtilizationPercent");
817     if (ipsEntry != ipsProperties.end())
818     {
819         exitUtil = std::get<uint64_t>(ipsEntry->second);
820     }
821     else
822     {
823         log<level::ERR>(
824             "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
825     }
826 
827     ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
828     if (ipsEntry != ipsProperties.end())
829     {
830         exitTime = std::get<uint64_t>(ipsEntry->second);
831     }
832     else
833     {
834         log<level::ERR>(
835             "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
836     }
837 
838     if (enterUtil > exitUtil)
839     {
840         log<level::ERR>(
841             fmt::format(
842                 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
843                 enterUtil, exitUtil)
844                 .c_str());
845         enterUtil = exitUtil;
846     }
847 
848     return true;
849 }
850 
851 /* Read default IPS parameters, save them to the persistent file and update
852  DBus. Return true if successful */
853 bool PowerMode::useDefaultIPSParms()
854 {
855     // Read the default IPS parameters
856     bool ipsEnabled;
857     uint8_t enterUtil, exitUtil;
858     uint16_t enterTime, exitTime;
859     if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
860                             exitTime))
861     {
862         // Unable to read defaults
863         return false;
864     }
865     log<level::INFO>(
866         fmt::format(
867             "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
868             ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
869             .c_str());
870 
871     // Save IPS parms to the persistent file
872     persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
873                             exitTime);
874 
875     // Write IPS parms to DBus
876     return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
877 }
878 
879 } // namespace powermode
880 
881 } // namespace occ
882 
883 } // namespace open_power
884