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/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