Read default power mode & IPS parameters from DBus

If the power mode or idle power saver parameters have never been set,
they will be read from a set of default parameters that are on DBus.
This allows individual systems to have different default settings.
Mode/IPS parameters moved from Settings to OCC.Control

Code verified on Everest hardware with multiple scenarios including
host reboots, BMC reboots, OCC resets.

Change-Id: Id02613455db7f382791ff37c7dc420dbea1e0906
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/app.cpp b/app.cpp
index 0d6053c..94e00c4 100644
--- a/app.cpp
+++ b/app.cpp
@@ -4,6 +4,9 @@
 #include "occ_events.hpp"
 #include "occ_manager.hpp"
 #include "utils.hpp"
+#ifdef POWER10
+#include "powermode.hpp"
+#endif
 
 #include <org/open_power/OCC/Device/error.hpp>
 #include <phosphor-logging/elog.hpp>
@@ -38,6 +41,10 @@
 #ifdef READ_OCC_SENSORS
     sdbusplus::server::manager::manager objManagerXyz(bus, OCC_SENSORS_ROOT);
 #endif
+#ifdef POWER10
+    sdbusplus::server::manager::manager objManagerXyzControl(
+        bus, "/xyz/openbmc_project/control");
+#endif
     open_power::occ::Manager mgr(eventP);
 
     // Claim the bus since all the house keeping is done now
diff --git a/occ_manager.cpp b/occ_manager.cpp
index ee8575d..3160131 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -136,7 +136,9 @@
 #ifdef POWER10
     if (!pmode)
     {
-        pmode = std::make_unique<open_power::occ::powermode::PowerMode>(*this);
+        // Create the power mode object
+        pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
+            *this, powermode::PMODE_PATH, powermode::PIPS_PATH);
     }
 #endif
 
@@ -166,7 +168,7 @@
         if (!pcap)
         {
             pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
-                *statusObjects.front());
+                *statusObjects.back());
         }
 
 #ifdef POWER10
@@ -292,7 +294,9 @@
     pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
         *statusObjects.front(), occMasterName);
 #ifdef POWER10
-    pmode = std::make_unique<open_power::occ::powermode::PowerMode>(*this);
+    pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
+        *this, open_power::occ::powermode::PMODE_PATH,
+        open_power::occ::powermode::PIPS_PATH);
     // Set the master OCC on the PowerMode object
     pmode->setMasterOcc(path);
 #endif
diff --git a/occ_status.cpp b/occ_status.cpp
index 0cb9b46..5abf6dd 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -39,15 +39,15 @@
             lastState = 0;
 
             // Call into Manager to let know that we have bound
-            if (this->callBack)
+            if (this->managerCallBack)
             {
-                this->callBack(value);
+                this->managerCallBack(value);
             }
         }
         else
         {
 #ifdef POWER10
-            if (device.master())
+            if (pmode && device.master())
             {
                 // Prevent mode changes
                 pmode->setMasterActive(false);
@@ -60,9 +60,9 @@
 #endif
 
             // Call into Manager to let know that we will unbind.
-            if (this->callBack)
+            if (this->managerCallBack)
             {
-                this->callBack(value);
+                this->managerCallBack(value);
             }
 
             // Stop watching for errors
@@ -109,8 +109,11 @@
 void Status::deviceError()
 {
 #ifdef POWER10
-    // Prevent mode changes
-    pmode->setMasterActive(false);
+    if (pmode && device.master())
+    {
+        // Prevent mode changes
+        pmode->setMasterActive(false);
+    }
 #endif
 
     // This would deem OCC inactive
@@ -204,7 +207,7 @@
 #ifdef POWER10
             if (OccState(state) == OccState::ACTIVE)
             {
-                if (device.master())
+                if (pmode && device.master())
                 {
                     // Set the master OCC on the PowerMode object
                     pmode->setMasterOcc(path);
diff --git a/occ_status.hpp b/occ_status.hpp
index 32041c7..6dab7dd 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -86,7 +86,7 @@
            ) :
 
         Interface(utils::getBus(), getDbusPath(path).c_str(), true),
-        path(path), callBack(callBack), instance(getInstance(path)),
+        path(path), managerCallBack(callBack), instance(getInstance(path)),
         manager(managerRef),
 #ifdef POWER10
         pmode(powerModeRef),
@@ -206,7 +206,7 @@
     /** @brief Callback handler to be invoked during property change.
      *         This is a handler in Manager class
      */
-    std::function<void(bool)> callBack;
+    std::function<void(bool)> managerCallBack;
 
     /** @brief OCC instance number. Ex, 0,1, etc */
     unsigned int instance;
diff --git a/powercap.cpp b/powercap.cpp
index ffa65dd..2555d95 100644
--- a/powercap.cpp
+++ b/powercap.cpp
@@ -16,10 +16,6 @@
 constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
 
-constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
-constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
-constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
-
 constexpr auto POWER_CAP_PROP = "PowerCap";
 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
 
diff --git a/powermode.cpp b/powermode.cpp
index 9b9f3cb..fd8679d 100644
--- a/powermode.cpp
+++ b/powermode.cpp
@@ -18,18 +18,38 @@
 {
 
 using namespace phosphor::logging;
+using namespace std::literals::string_literals;
+
 using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
 
 // Set the Master OCC
-void PowerMode::setMasterOcc(const std::string& occPath)
+void PowerMode::setMasterOcc(const std::string& masterOccPath)
 {
-    path = occPath;
+    if (masterOccSet)
+    {
+        if (masterOccPath != path)
+        {
+            log<level::ERR>(
+                fmt::format(
+                    "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
+                    occInstance, masterOccPath)
+                    .c_str());
+            if (occCmd)
+            {
+                occCmd.reset();
+            }
+        }
+    }
+    path = masterOccPath;
     occInstance = path.back() - '0';
     log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})",
                                   occInstance, path.c_str())
                           .c_str());
-    occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
-                                                           path.c_str());
+    if (!occCmd)
+    {
+        occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
+                                                               path.c_str());
+    }
     masterOccSet = true;
 };
 
@@ -48,12 +68,11 @@
         SysPwrMode newMode = convertStringToMode(propVal);
         if (newMode != SysPwrMode::NO_CHANGE)
         {
-            // DBus mode changed, get rid of any OEM mode if set
-            persistedData.purge();
+            // Update persisted data with new mode
+            persistedData.updateMode(newMode, 0);
 
             log<level::INFO>(
-                fmt::format("Power Mode Change Requested: {}", propVal)
-                    .c_str());
+                fmt::format("DBus Power Mode Changed: {}", propVal).c_str());
 
             // Send mode change to OCC
             sendModeChange();
@@ -62,7 +81,7 @@
 }
 
 // Called from OCC PassThrough interface (via CE login / BMC command line)
-bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t modeData)
+bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
 {
     if (updateDbusMode(newMode) == false)
     {
@@ -70,19 +89,14 @@
         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);
+    // Save mode
+    persistedData.updateMode(newMode, oemModeData);
 
-        // Send mode change to OCC
-        if (sendModeChange() != CmdStatus::SUCCESS)
-        {
-            // Mode change failed
-            return false;
-        }
+    // Send mode change to OCC
+    if (sendModeChange() != CmdStatus::SUCCESS)
+    {
+        // Mode change failed
+        return false;
     }
 
     return true;
@@ -125,7 +139,6 @@
 // Check if Hypervisor target is PowerVM
 bool 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";
@@ -156,44 +169,81 @@
     return powerVmTarget;
 }
 
-// Get the requested power mode from DBus
-SysPwrMode PowerMode::getDbusMode()
+// Initialize persistent data and return true if successful
+bool PowerMode::initPersistentData()
 {
-    using namespace open_power::occ::powermode;
-    SysPwrMode currentMode;
-
-    // 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))
+    if (!persistedData.modeAvailable())
     {
-        log<level::ERR>(
-            fmt::format(
-                "PowerMode::getDbusMode Invalid power mode found on DBus: {}",
-                currentMode)
+        // Read the default mode
+        SysPwrMode currentMode;
+        if (!getDefaultMode(currentMode))
+        {
+            // Unable to read defaults
+            return false;
+        }
+        log<level::INFO>(
+            fmt::format("PowerMode::initPersistentData: Using default mode: {}",
+                        currentMode)
                 .c_str());
-        currentMode = SysPwrMode::NO_CHANGE;
+
+        // Save default mode as current mode
+        persistedData.updateMode(currentMode, 0);
+
+        // Write default mode to DBus
+        updateDbusMode(currentMode);
     }
 
-    return currentMode;
+    if (!persistedData.ipsAvailable())
+    {
+        // Read the default IPS parameters
+        bool ipsEnabled;
+        uint8_t enterUtil, exitUtil;
+        uint16_t enterTime, exitTime;
+        if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
+                                exitTime))
+        {
+            // Unable to read defaults
+            return false;
+        }
+        log<level::INFO>(
+            fmt::format(
+                "PowerMode::initPersistentData: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
+                ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
+                .c_str());
+
+        // Save IPS
+        persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
+                                exitTime);
+
+        // Write default IPS data to DBus
+        updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
+    }
+    return true;
+}
+
+// Get the requested power mode and return true if successful
+bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
+{
+    currentMode = SysPwrMode::NO_CHANGE;
+    oemModeData = 0;
+
+    if (!persistedData.getMode(currentMode, oemModeData))
+    {
+        // Persistent data not initialized, read defaults and update DBus
+        if (!initPersistentData())
+        {
+            // Unable to read defaults from entity manager yet
+            return false;
+        }
+        return persistedData.getMode(currentMode, oemModeData);
+    }
+
+    return true;
 }
 
 // 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))
     {
@@ -205,25 +255,25 @@
         return false;
     }
 
-    // Mode::PowerMode dBusMode;
-    std::string dBusMode;
+    // Convert mode for DBus
+    ModeInterface::PowerMode dBusMode;
     switch (newMode)
     {
         case SysPwrMode::STATIC:
-            dBusMode = PMODE_INTERFACE + ".PowerMode.Static"s;
+            dBusMode = Mode::PowerMode::Static;
             break;
         case SysPwrMode::POWER_SAVING:
-            dBusMode = PMODE_INTERFACE + ".PowerMode.PowerSaving"s;
+            dBusMode = Mode::PowerMode::PowerSaving;
             break;
         case SysPwrMode::MAX_PERF:
-            dBusMode = PMODE_INTERFACE + ".PowerMode.MaximumPerformance"s;
+            dBusMode = Mode::PowerMode::MaximumPerformance;
             break;
         default:
-            dBusMode = PMODE_INTERFACE + ".PowerMode.OEM"s;
+            dBusMode = Mode::PowerMode::OEM;
     }
 
-    utils::setProperty(PMODE_PATH, PMODE_INTERFACE, POWER_MODE_PROP,
-                       std::move(dBusMode));
+    // true = skip update signal
+    ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
 
     return true;
 }
@@ -248,14 +298,9 @@
         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();
-    }
+    SysPwrMode newMode;
+    uint16_t oemModeData = 0;
+    getMode(newMode, oemModeData);
 
     if (VALID_POWER_MODE_SETTING(newMode) ||
         VALID_OEM_POWER_MODE_SETTING(newMode))
@@ -268,13 +313,13 @@
         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
+        cmd.push_back(oemModeData >> 8);   // Mode Data (Freq Point)
+        cmd.push_back(oemModeData & 0xFF); //
+        cmd.push_back(0x00);               // reserved
         log<level::INFO>(
             fmt::format(
                 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
-                newMode, modeData, occInstance, cmd.size())
+                newMode, oemModeData, occInstance, cmd.size())
                 .c_str());
         status = occCmd->send(cmd, rsp);
         if (status == CmdStatus::SUCCESS)
@@ -340,10 +385,17 @@
         ipsProperties{};
     msg.read(interface, ipsProperties);
 
+    // Read persisted values
+    bool ipsEnabled;
+    uint8_t enterUtil, exitUtil;
+    uint16_t enterTime, exitTime;
+    getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
+
+    // Check for any changed data
     auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
     if (ipsEntry != ipsProperties.end())
     {
-        const auto ipsEnabled = std::get<bool>(ipsEntry->second);
+        ipsEnabled = std::get<bool>(ipsEntry->second);
         log<level::INFO>(
             fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
                 .c_str());
@@ -352,7 +404,7 @@
     ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
     if (ipsEntry != ipsProperties.end())
     {
-        const auto enterUtil = std::get<uint8_t>(ipsEntry->second);
+        enterUtil = std::get<uint8_t>(ipsEntry->second);
         log<level::INFO>(
             fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
                 .c_str());
@@ -362,7 +414,7 @@
     if (ipsEntry != ipsProperties.end())
     {
         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
-        const auto enterTime =
+        enterTime =
             std::chrono::duration_cast<std::chrono::seconds>(ms).count();
         log<level::INFO>(
             fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
@@ -372,7 +424,7 @@
     ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
     if (ipsEntry != ipsProperties.end())
     {
-        const auto exitUtil = std::get<uint8_t>(ipsEntry->second);
+        exitUtil = std::get<uint8_t>(ipsEntry->second);
         log<level::INFO>(
             fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
                 .c_str());
@@ -382,8 +434,7 @@
     if (ipsEntry != ipsProperties.end())
     {
         std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
-        const auto exitTime =
-            std::chrono::duration_cast<std::chrono::seconds>(ms).count();
+        exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
         log<level::INFO>(
             fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
                 .c_str());
@@ -392,118 +443,43 @@
 
     if (parmsChanged)
     {
-        // Trigger mode change to OCC
+        // Update persistant data with new DBus values
+        persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
+                                exitTime);
+
+        // Trigger IPS data to get sent to the OCC
         sendIpsData();
     }
 
     return;
 }
 
-/** @brief Get the Idle Power Saver properties
- * @return true if IPS is enabled
+/** @brief Get the Idle Power Saver properties from persisted data
+ * @return true if IPS parameters were read
  */
-bool PowerMode::getIPSParms(uint8_t& enterUtil, uint16_t& enterTime,
-                            uint8_t& exitUtil, uint16_t& exitTime)
+bool PowerMode::getIPSParms(bool& ipsEnabled, 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)
+    ipsEnabled = true; // Enabled
+    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{};
+    if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
+                              exitTime))
+    {
+        // Persistent data not initialized, read defaults and update DBus
+        if (!initPersistentData())
+        {
+            // Unable to read defaults from entity manager yet
+            return false;
+        }
 
-    // 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());
+        persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
+                             exitTime);
     }
 
     if (enterUtil > exitUtil)
@@ -516,14 +492,30 @@
         enterUtil = exitUtil;
     }
 
-    return ipsEnabled;
+    return true;
+}
+
+// Set the Idle Power Saver data on DBus
+bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
+                              const uint16_t enterTime, const uint8_t exitUtil,
+                              const uint16_t exitTime)
+{
+    // true = skip update signal
+    IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
+    IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
+    // Convert time from seconds to ms
+    uint64_t msTime = enterTime * 1000;
+    IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
+    IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
+    msTime = exitTime * 1000;
+    IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
+
+    return true;
 }
 
 // Send Idle Power Saver config data to the master OCC
 CmdStatus PowerMode::sendIpsData()
 {
-    CmdStatus status;
-
     if (!masterActive || !masterOccSet)
     {
         // Nothing to do
@@ -538,10 +530,10 @@
         return CmdStatus::SUCCESS;
     }
 
+    bool ipsEnabled;
     uint8_t enterUtil, exitUtil;
     uint16_t enterTime, exitTime;
-    const bool ipsEnabled =
-        getIPSParms(enterUtil, enterTime, exitUtil, exitTime);
+    getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
 
     log<level::INFO>(
         fmt::format(
@@ -567,7 +559,7 @@
                                  "command to OCC{} ({} bytes)",
                                  occInstance, cmd.size())
                          .c_str());
-    status = occCmd->send(cmd, rsp);
+    CmdStatus status = occCmd->send(cmd, rsp);
     if (status == CmdStatus::SUCCESS)
     {
         if (rsp.size() == 5)
@@ -608,20 +600,33 @@
     return status;
 }
 
-inline void OccPersistData::print()
+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());
+    if (modeData.modeInitialized)
+    {
+        log<level::INFO>(
+            fmt::format(
+                "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
+                modeData.mode, modeData.oemModeData, modeData.oemModeData)
+                .c_str());
+    }
+    if (modeData.ipsInitialized)
+    {
+        log<level::INFO>(
+            fmt::format(
+                "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
+                modeData.ipsEnabled, modeData.ipsEnterUtil,
+                modeData.ipsEnterTime, modeData.ipsExitUtil,
+                modeData.ipsExitTime)
+                .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;
+        std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
 
     if (!std::filesystem::exists(opath.parent_path()))
     {
@@ -629,15 +634,16 @@
     }
 
     log<level::DEBUG>(
-        fmt::format("OccPersistData::save: Writing OEM persisted data to {}",
-                    opath.c_str())
+        fmt::format(
+            "OccPersistData::save: Writing Power Mode persisted data to {}",
+            opath.c_str())
             .c_str());
     print();
 
     std::ofstream stream{opath.c_str()};
     cereal::JSONOutputArchive oarchive{stream};
 
-    oarchive(oemData);
+    oarchive(modeData);
 }
 
 // Loads the OEM mode data in the filesystem using cereal.
@@ -645,24 +651,25 @@
 {
 
     std::filesystem::path ipath =
-        std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / oemModeFilename;
+        std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
 
     if (!std::filesystem::exists(ipath))
     {
+        modeData.modeInitialized = false;
+        modeData.ipsInitialized = false;
         return;
     }
 
     log<level::DEBUG>(
-        fmt::format("OccPersistData::load: Reading OEM persisted data from {}",
-                    ipath.c_str())
+        fmt::format(
+            "OccPersistData::load: Reading Power Mode persisted data from {}",
+            ipath.c_str())
             .c_str());
     try
     {
         std::ifstream stream{ipath.c_str()};
         cereal::JSONInputArchive iarchive(stream);
-        iarchive(oemData);
-
-        oemSet = true;
+        iarchive(modeData);
     }
     catch (const std::exception& e)
     {
@@ -671,28 +678,185 @@
             fmt::format("OccPersistData::load: failed to read {}, errno={}",
                         ipath.c_str(), error)
                 .c_str());
+        modeData.modeInitialized = false;
+        modeData.ipsInitialized = false;
     }
 
     print();
 }
 
-void OccPersistData::purge()
+// Called when PowerModeProperties defaults are available on DBus
+void PowerMode::defaultsReady(sdbusplus::message::message& msg)
 {
-    std::filesystem::path opath =
-        std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / oemModeFilename;
+    std::map<std::string, std::variant<std::string>> properties{};
+    std::string interface;
+    msg.read(interface, properties);
 
-    if (!std::filesystem::exists(opath))
+    // If persistent data exists, then don't need to read defaults
+    if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
     {
-        return;
+        log<level::INFO>(
+            fmt::format(
+                "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
+                persistedData.modeAvailable() ? 'y' : 'n',
+                persistedData.modeAvailable() ? 'y' : 'n')
+                .c_str());
+
+        // Read default power mode defaults and update DBus
+        initPersistentData();
+    }
+}
+
+// Get the default power mode from DBus and return true if success
+bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
+{
+    try
+    {
+        auto& bus = utils::getBus();
+        std::string path = "/";
+        std::string service =
+            utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
+        auto method =
+            bus.new_method_call(service.c_str(), path.c_str(),
+                                "org.freedesktop.DBus.Properties", "Get");
+        method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
+        auto reply = bus.call(method);
+
+        std::variant<std::string> stateEntryValue;
+        reply.read(stateEntryValue);
+        auto propVal = std::get<std::string>(stateEntryValue);
+
+        const std::string fullModeString =
+            PMODE_INTERFACE + ".PowerMode."s + propVal;
+        defaultMode = powermode::convertStringToMode(fullModeString);
+        if (!VALID_POWER_MODE_SETTING(defaultMode))
+        {
+            log<level::ERR>(
+                fmt::format(
+                    "PowerMode::getDefaultMode: Invalid default power mode found: {}",
+                    defaultMode)
+                    .c_str());
+            // If default was read but not valid, use Max Performance
+            defaultMode = SysPwrMode::MAX_PERF;
+            return true;
+        }
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>(
+            fmt::format("Unable to read Default Power Mode: {}", e.what())
+                .c_str());
+        return false;
     }
 
-    print();
-    log<level::DEBUG>("OccPersistData::purge() Removing OEM data");
+    return true;
+}
 
-    oemSet = false;
-    oemData.oemMode = SysPwrMode::NO_CHANGE;
-    oemData.oemModeFreq = 0x0000;
-    remove(opath.c_str());
+/* Get the default Idle Power Saver properties and return true if successful */
+bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
+                                   uint16_t& enterTime, uint8_t& exitUtil,
+                                   uint16_t& exitTime)
+{
+    // Defaults:
+    ipsEnabled = true; // Enabled
+    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, uint16_t, uint64_t>>
+        ipsProperties{};
+
+    // Get all IPS properties from DBus
+    try
+    {
+        auto& bus = utils::getBus();
+        std::string path = "/";
+        std::string service =
+            utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
+        auto method =
+            bus.new_method_call(service.c_str(), path.c_str(),
+                                "org.freedesktop.DBus.Properties", "GetAll");
+        method.append(PMODE_DEFAULT_INTERFACE);
+        auto reply = bus.call(method);
+        reply.read(ipsProperties);
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>(
+            fmt::format(
+                "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
+                e.what())
+                .c_str());
+        return false;
+    }
+
+    auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
+    if (ipsEntry != ipsProperties.end())
+    {
+        ipsEnabled = std::get<bool>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
+    }
+
+    ipsEntry = ipsProperties.find("EnterUtilizationPercent");
+    if (ipsEntry != ipsProperties.end())
+    {
+        enterUtil = std::get<uint64_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
+    }
+
+    ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
+    if (ipsEntry != ipsProperties.end())
+    {
+        enterTime = std::get<uint64_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
+    }
+
+    ipsEntry = ipsProperties.find("ExitUtilizationPercent");
+    if (ipsEntry != ipsProperties.end())
+    {
+        exitUtil = std::get<uint64_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
+    }
+
+    ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
+    if (ipsEntry != ipsProperties.end())
+    {
+        exitTime = std::get<uint64_t>(ipsEntry->second);
+    }
+    else
+    {
+        log<level::ERR>(
+            "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
+    }
+
+    if (enterUtil > exitUtil)
+    {
+        log<level::ERR>(
+            fmt::format(
+                "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
+                enterUtil, exitUtil)
+                .c_str());
+        enterUtil = exitUtil;
+    }
+
+    return true;
 }
 
 } // namespace powermode
diff --git a/powermode.hpp b/powermode.hpp
index 0ec22a5..b212184 100644
--- a/powermode.hpp
+++ b/powermode.hpp
@@ -13,6 +13,8 @@
 #include <cereal/types/vector.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/bus/match.hpp>
+#include <xyz/openbmc_project/Control/Power/IdlePowerSaver/server.hpp>
+#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
 
 #include <filesystem>
 
@@ -25,6 +27,10 @@
 
 namespace powermode
 {
+namespace Base = sdbusplus::xyz::openbmc_project::Control::Power::server;
+using ModeInterface = sdbusplus::server::object::object<Base::Mode>;
+using IpsInterface = sdbusplus::server::object::object<Base::IdlePowerSaver>;
+using namespace std::literals::string_literals;
 
 constexpr auto PMODE_PATH = "/xyz/openbmc_project/control/host0/power_mode";
 constexpr auto PMODE_INTERFACE = "xyz.openbmc_project.Control.Power.Mode";
@@ -39,6 +45,9 @@
 constexpr auto IPS_EXIT_UTIL = "ExitUtilizationPercent";
 constexpr auto IPS_EXIT_TIME = "ExitDwellTime";
 
+const auto PMODE_DEFAULT_INTERFACE =
+    "xyz.openbmc_project.Configuration.PowerModeProperties"s;
+
 /** @brief Query the current Hypervisor target
  * @return true if the current Hypervisor target is PowerVM
  */
@@ -52,17 +61,25 @@
  */
 SysPwrMode convertStringToMode(const std::string& i_modeString);
 
-struct OemModeData
+struct PowerModeData
 {
-    SysPwrMode oemMode = SysPwrMode::NO_CHANGE;
-    uint16_t oemModeFreq = 0x0000;
+    bool modeInitialized = false;
+    SysPwrMode mode = SysPwrMode::NO_CHANGE;
+    uint16_t oemModeData = 0x0000;
+    bool ipsInitialized = false;
+    bool ipsEnabled = true;
+    uint8_t ipsEnterUtil = 0;
+    uint16_t ipsEnterTime = 0;
+    uint8_t ipsExitUtil = 0;
+    uint16_t ipsExitTime = 0;
 
     /** @brief Function specifying data to archive for cereal.
      */
     template <class Archive>
     void serialize(Archive& archive)
     {
-        archive(oemMode, oemModeFreq);
+        archive(modeInitialized, mode, oemModeData, ipsInitialized, ipsEnabled,
+                ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime);
     }
 };
 
@@ -80,7 +97,7 @@
     OccPersistData(OccPersistData&&) = default;
     OccPersistData& operator=(OccPersistData&&) = default;
 
-    /** @brief Loads any saved OEM mode data */
+    /** @brief Loads any saved power mode data */
     OccPersistData()
     {
         load();
@@ -88,52 +105,107 @@
 
     /** @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
+     *  @param[in] newMode - desired System Power Mode
+     *  @param[in] oemModeData - data required by some OEM Power Modes
      */
-    void writeModeFile(const SysPwrMode newMode, const uint16_t modeData)
+    void updateMode(const SysPwrMode newMode, const uint16_t oemModeData)
     {
-        oemData.oemMode = newMode;
-        oemData.oemModeFreq = modeData;
-        oemSet = true;
+        modeData.mode = newMode;
+        modeData.oemModeData = oemModeData;
+        modeData.modeInitialized = true;
         save();
     }
 
-    /** @brief Return the OEM Power Mode and frequency if enabled
+    /** @brief Write Idle Power Saver parameters to persistent file
      *
-     *  @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
+     *  @param[in] enabled - Idle Power Save status (true = enabled)
+     *  @param[in] enterUtil - IPS Enter Utilization (%)
+     *  @param[in] enterTime - IPS Enter Time (seconds)
+     *  @param[in] exitUtil - IPS Exit Utilization (%)
+     *  @param[in] exitTime - IPS Exit Time (seconds)
      */
-    bool getOemMode(SysPwrMode& mode, uint16_t& freq) const
+    void updateIPS(const bool enabled, const uint8_t enterUtil,
+                   const uint16_t enterTime, const uint8_t exitUtil,
+                   const uint16_t exitTime)
     {
-        if (!oemSet)
+        modeData.ipsEnabled = enabled;
+        modeData.ipsEnterUtil = enterUtil;
+        modeData.ipsEnterTime = enterTime;
+        modeData.ipsExitUtil = exitUtil;
+        modeData.ipsExitTime = exitTime;
+        modeData.ipsInitialized = true;
+        save();
+    }
+
+    /** @brief Return the Power Mode and mode data
+     *
+     *  @param[out] mode - current system power mode
+     *  @param[out] oemModeData - frequency data for some OEM mode
+     *
+     *  @returns true if mode was available
+     */
+    bool getMode(SysPwrMode& mode, uint16_t& oemModeData) const
+    {
+        if (!modeData.modeInitialized)
         {
             return false;
         }
 
-        mode = oemData.oemMode;
-        freq = oemData.oemModeFreq;
+        mode = modeData.mode;
+        oemModeData = modeData.oemModeData;
         return true;
     }
 
+    /** @brief Get the Idle Power Saver properties from DBus
+     *
+     *  @param[out] enabled - Idle Power Save status (true = enabled)
+     *  @param[out] enterUtil - IPS Enter Utilization (%)
+     *  @param[out] enterTime - IPS Enter Time (seconds)
+     *  @param[out] exitUtil - IPS Exit Utilization (%)
+     *  @param[out] exitTime - IPS Exit Time (seconds)
+     *
+     * @return true if parameters were read successfully
+     */
+    bool getIPS(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
+                uint8_t& exitUtil, uint16_t& exitTime)
+    {
+        if (!modeData.ipsInitialized)
+        {
+            return false;
+        }
+
+        enabled = modeData.ipsEnabled;
+        enterUtil = modeData.ipsEnterUtil;
+        enterTime = modeData.ipsEnterTime;
+        exitUtil = modeData.ipsExitUtil;
+        exitTime = modeData.ipsExitTime;
+        return true;
+    }
+
+    /** @brief Return true if the power mode is available */
+    bool modeAvailable()
+    {
+        return (modeData.modeInitialized);
+    }
+
+    /** @brief Return true if the power mode is available */
+    bool ipsAvailable()
+    {
+        return (modeData.ipsInitialized);
+    }
+
     /** @brief Saves the Power Mode data in the filesystem using cereal. */
     void save();
 
-    /** @brief Removes the OEM mode data. */
-    void purge();
-
-    inline void print();
+    /** @brief Trace the Power Mode and IPS parameters. */
+    void print();
 
   private:
-    static constexpr auto oemModeFilename = "oemModeData";
+    /** @brief Power Mode data filename to store persistent data */
+    static constexpr auto powerModeFilename = "powerModeData";
 
-    /** @brief true if an OEM Power Mode was set */
-    bool oemSet = false;
-
-    /** @brief OEM Power Mode data */
-    OemModeData oemData;
+    /** @brief Power Mode data object to be persisted */
+    PowerModeData modeData;
 
     /** @brief Loads the OEM mode data in the filesystem using cereal. */
     void load();
@@ -147,7 +219,7 @@
  *  the power mode to the OCC if the mode is changed while the occ is active.
  */
 
-class PowerMode
+class PowerMode : public ModeInterface, public IpsInterface
 {
   public:
     /** @brief PowerMode object to inform occ of changes to mode
@@ -156,11 +228,14 @@
      * If a change is detected, and the occ is active, then this object will
      * notify the OCC of the change.
      *
-     * @param[in] managerRef -
-     * @param[in] path -
+     * @param[in] managerRef - manager object reference
+     * @param[in] modePath - Power Mode dbus path
+     * @param[in] ipsPath - Idle Power Saver dbus path
      */
-    explicit PowerMode(const Manager& managerRef) :
-        manager(managerRef), occInstance(0),
+    explicit PowerMode(const Manager& managerRef, const char* modePath,
+                       const char* ipsPath) :
+        ModeInterface(utils::getBus(), modePath, false),
+        IpsInterface(utils::getBus(), ipsPath, false), manager(managerRef),
         pmodeMatch(utils::getBus(),
                    sdbusplus::bus::match::rules::propertiesChanged(
                        PMODE_PATH, PMODE_INTERFACE),
@@ -169,9 +244,44 @@
                  sdbusplus::bus::match::rules::propertiesChanged(
                      PIPS_PATH, PIPS_INTERFACE),
                  [this](auto& msg) { this->ipsChanged(msg); }),
-        masterOccSet(false), masterActive(false){};
+        defaultsUpdateMatch(
+            utils::getBus(),
+            sdbusplus::bus::match::rules::propertiesChangedNamespace(
+                "/xyz/openbmc_project/inventory", PMODE_DEFAULT_INTERFACE),
+            [this](auto& msg) { this->defaultsReady(msg); }),
+        masterOccSet(false), masterActive(false)
+    {
+        // restore Power Mode to DBus
+        SysPwrMode currentMode;
+        uint16_t oemModeData = 0;
+        if (getMode(currentMode, oemModeData))
+        {
+            updateDbusMode(currentMode);
+        }
+        // restore Idle Power Saver parameters to DBus
+        uint8_t enterUtil, exitUtil;
+        uint16_t enterTime, exitTime;
+        bool ipsEnabled;
+        if (getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime))
+        {
+            updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
+        }
+    };
 
-    bool setMode(const SysPwrMode newMode, const uint16_t modedata);
+    /** @brief Initialize the persistent data with default values
+     *
+     * @return true if initialization completed
+     */
+    bool initPersistentData();
+
+    /** @brief Set the current power mode property
+     *
+     * @param[in] newMode     - desired system power mode
+     * @param[in] oemModeData - data required by some OEM Power Modes
+     *
+     * @return true if mode accepted
+     */
+    bool setMode(const SysPwrMode newMode, const uint16_t oemModeData);
 
     /** @brief Send mode change command to the master OCC
      *  @return SUCCESS on success
@@ -218,6 +328,9 @@
     /** @brief Used to subscribe to dbus IPS property changes **/
     sdbusplus::bus::match_t ipsMatch;
 
+    /** @brief Used to subscribe to dbus defaults property changes **/
+    sdbusplus::bus::match_t defaultsUpdateMatch;
+
     OccPersistData persistedData;
 
     /** @brief True when the master OCC has been established */
@@ -235,10 +348,14 @@
      */
     void modeChanged(sdbusplus::message::message& msg);
 
-    /** @brief Get the current power mode property from DBus
-     * @return Power mode
+    /** @brief Get the current power mode property
+     *
+     * @param[out] currentMode - current system power mode
+     * @param[out] oemModeData - frequency data for some OEM mode
+     *
+     * @return true if data read successfully
      */
-    SysPwrMode getDbusMode();
+    bool getMode(SysPwrMode& currentMode, uint16_t& oemModeData);
 
     /** @brief Update the power mode property on DBus
      *
@@ -257,11 +374,60 @@
      */
     void ipsChanged(sdbusplus::message::message& msg);
 
-    /** @brief Get the Idle Power Saver properties from DBus
-     * @return true if IPS is enabled
+    /** @brief Get the Idle Power Saver properties
+     *
+     *  @param[out] enabled - Idle Power Save status (true = enabled)
+     *  @param[out] enterUtil - IPS Enter Utilization (%)
+     *  @param[out] enterTime - IPS Enter Time (seconds)
+     *  @param[out] exitUtil - IPS Exit Utilization (%)
+     *  @param[out] exitTime - IPS Exit Time (seconds)
+     *
+     * @return true if data read successfully
      */
-    bool getIPSParms(uint8_t& enterUtil, uint16_t& enterTime, uint8_t& exitUtil,
-                     uint16_t& exitTime);
+    bool getIPSParms(bool& enabled, uint8_t& enterUtil, uint16_t& enterTime,
+                     uint8_t& exitUtil, uint16_t& exitTime);
+
+    /** Update the Idle Power Saver data on DBus
+     *
+     *  @param[in] enabled - Idle Power Save status (true = enabled)
+     *  @param[in] enterUtil - IPS Enter Utilization (%)
+     *  @param[in] enterTime - IPS Enter Time (seconds)
+     *  @param[in] exitUtil - IPS Exit Utilization (%)
+     *  @param[in] exitTime - IPS Exit Time (seconds)
+     *
+     *  @return true if parameters were set successfully
+     */
+    bool updateDbusIPS(const bool enabled, const uint8_t enterUtil,
+                       const uint16_t enterTime, const uint8_t exitUtil,
+                       const uint16_t exitTime);
+
+    /** @brief Callback for entity manager default changes
+     *
+     * Called when PowerModeProperties defaults are available
+     */
+    void defaultsReady(sdbusplus::message::message& msg);
+
+    /** @brief Get the default power mode property for this system type
+     *
+     * @param[out] defaultMode - default system power mode
+     *
+     * @return true if data read successfully
+     */
+    bool getDefaultMode(SysPwrMode& defaultMode);
+
+    /** @brief Get the default Idle Power Saver properties for this system type
+     *
+     *  @param[out] enabled - Idle Power Save status (true = enabled)
+     *  @param[out] enterUtil - IPS Enter Utilization (%)
+     *  @param[out] enterTime - IPS Enter Time (seconds)
+     *  @param[out] exitUtil - IPS Exit Utilization (%)
+     *  @param[out] exitTime - IPS Exit Time (seconds)
+     *
+     *  @return true if parameters were read successfully
+     */
+    bool getDefaultIPSParms(bool& enabled, uint8_t& enterUtil,
+                            uint16_t& enterTime, uint8_t& exitUtil,
+                            uint16_t& exitTime);
 };
 
 } // namespace powermode
diff --git a/utils.cpp b/utils.cpp
index b44d477..e9ccc88 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -72,9 +72,9 @@
 /**
  * @brief Sets a given object's property value
  *
- * @param[in] object - Name of the object containing the property
+ * @param[in] objectPath - Name of the object containing the property
  * @param[in] interface - Interface name containing the property
- * @param[in] property - Property name
+ * @param[in] propertyName - Property name
  * @param[in] value - Property value
  */
 void setProperty(const std::string& objectPath, const std::string& interface,
@@ -83,23 +83,34 @@
     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())
+    try
     {
-        return;
+        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());
+        }
     }
-
-    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())
+    catch (const std::exception& e)
     {
+        auto error = errno;
         log<level::ERR>(
-            fmt::format("util::setProperty: Failed to set property {}",
-                        propertyName)
+            fmt::format("setProperty: failed to Set {}, errno={}, what={}",
+                        propertyName.c_str(), error, e.what())
                 .c_str());
     }
 }
@@ -120,6 +131,54 @@
 
     return paths;
 }
+
+// Get the service and object path for an interface
+std::string getServiceUsingSubTree(const std::string& interface,
+                                   std::string& path)
+{
+    using Path = std::string;
+    using Intf = std::string;
+    using Serv = std::string;
+    using Intfs = std::vector<Intf>;
+    using Objects = std::map<Path, std::map<Serv, Intfs>>;
+    Serv service;
+    Objects rspObjects;
+
+    auto& bus = getBus();
+    auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
+                                      MAPPER_IFACE, "GetSubTree");
+    method.append(path, 0, std::vector{interface});
+
+    auto mapperResponseMsg = bus.call(method);
+    mapperResponseMsg.read(rspObjects);
+    if (rspObjects.empty())
+    {
+        log<level::ERR>(
+            fmt::format(
+                "util::getServiceUsingSubTree: Failed getSubTree({},0,{})",
+                path.c_str(), interface)
+                .c_str());
+    }
+    else
+    {
+        path = rspObjects.begin()->first;
+        if (!rspObjects.begin()->second.empty())
+        {
+            service = rspObjects.begin()->second.begin()->first;
+        }
+        else
+        {
+            log<level::ERR>(
+                fmt::format(
+                    "getServiceUsingSubTree: service not found for interface {} (path={})",
+                    interface, path.c_str())
+                    .c_str());
+        }
+    }
+
+    return service;
+}
+
 } // namespace utils
 } // namespace occ
 } // namespace open_power
diff --git a/utils.hpp b/utils.hpp
index 4a57700..2b4b897 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -78,6 +78,17 @@
     getSubtreePaths(const std::vector<std::string>& interfaces,
                     const std::string& path = "/");
 
+/**
+ * @brief Get the D-Bus service and object path for an interface
+ *
+ * @param[in] interface - D-Bus interface name
+ * @param[in,out] path  - D-Bus object path
+ *
+ * @return D-Bus service name
+ */
+std::string getServiceUsingSubTree(const std::string& interface,
+                                   std::string& path);
+
 } // namespace utils
 } // namespace occ
 } // namespace open_power