Add support for OEM Power Modes

- Allow mode to be set via PassThrough interface
- Allow non-customer OEM power modes to be persisted
- Persist any OEM power mode settings
- moved mode related code from Status to PowerMode object
- merged PowerIPS into PowerMode object

Tested on Everest and Rainier.
Setting mode through PassThrough/ce-login:
  busctl call org.open_power.OCC.Control /org/open_power/control/occ0 org.open_power.OCC.PassThrough SetMode yq 11 3600
Trace (via PassThrough interface)
  openpower-occ-control[4440]: PassThrough::setMode() Setting Power Mode 11 (data: 3600)
  openpower-occ-control[4440]: PowerMode::sendModeChange: SET_MODE(11,3600) command to OCC0 (9 bytes)
Trace (setting mode via GUI/Redfish):
  openpower-occ-control[4440]: Power Mode Change Requested: xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance
  openpower-occ-control[4440]: PowerMode::sendModeChange: SET_MODE(12,0) command to OCC0 (9 bytes)
Verified when system in any OEM mode that Redfish also reports OEM
Verified all modes are persisted across PM Complex resets and reboots

Change-Id: Idd0be05cb6fd74dbd0776145f212c49addd1c365
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/powermode.hpp b/powermode.hpp
index 0d337dc..5342c77 100644
--- a/powermode.hpp
+++ b/powermode.hpp
@@ -3,8 +3,14 @@
 #include "config.h"
 
 #ifdef POWER10
-#include "occ_status.hpp"
+#include "occ_command.hpp"
 
+#include <cereal/archives/json.hpp>
+//#include <cereal/archives/binary.hpp>
+#include <cereal/cereal.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/types/tuple.hpp>
+#include <cereal/types/vector.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/bus/match.hpp>
 
@@ -14,6 +20,9 @@
 {
 namespace occ
 {
+
+class Manager;
+
 namespace powermode
 {
 
@@ -30,6 +39,11 @@
 constexpr auto IPS_EXIT_UTIL = "ExitUtilizationPercent";
 constexpr auto IPS_EXIT_TIME = "ExitDwellTime";
 
+/** @brief Query the current Hypervisor target
+ * @return true if the current Hypervisor target is PowerVM
+ */
+bool isPowerVM();
+
 /** @brief Convert power mode string to OCC SysPwrMode value
  *
  * @param[in] i_modeString - power mode string
@@ -38,6 +52,93 @@
  */
 SysPwrMode convertStringToMode(const std::string& i_modeString);
 
+struct OemModeData
+{
+    SysPwrMode oemMode = SysPwrMode::NO_CHANGE;
+    uint16_t oemModeFreq = 0x0000;
+
+    /** @brief Function specifying data to archive for cereal.
+     */
+    template <class Archive>
+    void serialize(Archive& archive)
+    {
+        archive(oemMode, oemModeFreq);
+    }
+};
+
+/** @class OccPersistData
+ *  @brief Provides persistent container to store data for OCC
+ *
+ * Data is stored via cereal
+ */
+class OccPersistData
+{
+  public:
+    ~OccPersistData() = default;
+    OccPersistData(const OccPersistData&) = default;
+    OccPersistData& operator=(const OccPersistData&) = default;
+    OccPersistData(OccPersistData&&) = default;
+    OccPersistData& operator=(OccPersistData&&) = default;
+
+    /** @brief Loads any saved OEM mode data */
+    OccPersistData()
+    {
+        load();
+    }
+
+    /** @brief Save Power Mode data to persistent file
+     *
+     *  @param[in] newMode - desired OEM Power Mode
+     *  @param[in] modeData - data required by some OEM Power Modes
+     */
+    void writeModeFile(const SysPwrMode newMode, const uint16_t modeData)
+    {
+        oemData.oemMode = newMode;
+        oemData.oemModeFreq = modeData;
+        oemSet = true;
+        save();
+    }
+
+    /** @brief Return the OEM Power Mode and frequency if enabled
+     *
+     *  @param[out] newMode - OEM mode (if set, else data not changed)
+     *  @param[out] oemFreq - Frequency data for OEM mode
+     *
+     *  @returns true if OEM mode was set
+     */
+    bool getOemMode(SysPwrMode& mode, uint16_t& freq) const
+    {
+        if (!oemSet)
+        {
+            return false;
+        }
+
+        mode = oemData.oemMode;
+        freq = oemData.oemModeFreq;
+        return true;
+    }
+
+    /** @brief Saves the Power Mode data in the filesystem using cereal. */
+    void save();
+
+    /** @brief Removes the OEM mode data. */
+    void purge();
+
+    inline void print();
+
+  private:
+    static constexpr auto oemModeFilename = "oemModeData";
+
+    /** @brief true if an OEM Power Mode was set */
+    bool oemSet = false;
+
+    /** @brief OEM Power Mode data */
+    OemModeData oemData;
+
+    /** @brief Loads the OEM mode data in the filesystem using cereal. */
+    void load();
+};
+
 /** @class PowerMode
  *  @brief Monitors for changes to the power mode and notifies occ
  *
@@ -55,16 +156,67 @@
      * If a change is detected, and the occ is active, then this object will
      * notify the OCC of the change.
      *
-     * @param[in] occStatus - The occ status object
+     * @param[in] managerRef -
+     * @param[in] path -
      */
-    explicit PowerMode(Status& occStatus) :
-        occStatus(occStatus),
+    explicit PowerMode(Manager& managerRef, const char* path) :
+        manager(managerRef), path(path), occInstance(this->path.back() - '0'),
+        occCmd(occInstance, path),
         pmodeMatch(utils::getBus(),
                    sdbusplus::bus::match::rules::propertiesChanged(
                        PMODE_PATH, PMODE_INTERFACE),
-                   [this](auto& msg) { this->modeChanged(msg); }){};
+                   [this](auto& msg) { this->modeChanged(msg); }),
+        ipsMatch(utils::getBus(),
+                 sdbusplus::bus::match::rules::propertiesChanged(
+                     PIPS_PATH, PIPS_INTERFACE),
+                 [this](auto& msg) { this->ipsChanged(msg); }),
+        masterActive(false){};
+
+    bool setMode(const SysPwrMode newMode, const uint16_t modedata);
+
+    /** @brief Send mode change command to the master OCC
+     *  @return SUCCESS on success
+     */
+    CmdStatus sendModeChange();
+
+    /** @brief Send Idle Power Saver config data to the master OCC
+     *  @return SUCCESS on success
+     */
+    CmdStatus sendIpsData();
+
+    /** @brief Notify object of master OCC state.  If not acitve, no
+     * commands will be sent to the master OCC
+     *
+     * @param[in]  isActive - true when master OCC is active
+     */
+    void setMasterActive(const bool isActive = true)
+    {
+        masterActive = isActive;
+    };
 
   private:
+    /** @brief OCC manager object */
+    const Manager& manager;
+
+    /** @brief Pass-through occ path on the bus */
+    std::string path;
+
+    /** @brief OCC instance number */
+    int occInstance;
+
+    /** @brief Object to send commands to the OCC */
+    OccCommand occCmd;
+
+    /** @brief Used to subscribe to dbus pmode property changes **/
+    sdbusplus::bus::match_t pmodeMatch;
+
+    /** @brief Used to subscribe to dbus IPS property changes **/
+    sdbusplus::bus::match_t ipsMatch;
+
+    OccPersistData persistedData;
+
+    bool masterActive;
+
     /** @brief Callback for pmode setting changes
      *
      * Process change and inform OCC
@@ -74,47 +226,33 @@
      */
     void modeChanged(sdbusplus::message::message& msg);
 
-    /* @brief OCC Status object */
-    Status& occStatus;
-
-    /** @brief Used to subscribe to dbus pmode property changes **/
-    sdbusplus::bus::match_t pmodeMatch;
-};
-
-class PowerIPS
-{
-  public:
-    /** @brief PowerIPS object to inform occ of changes to Idle Power Saver
-     * parms
-     *
-     * This object will monitor for changes to the Idle Power Saver settings.
-     * If a change is detected, and the occ is active, then this object will
-     * notify the OCC of the change.
-     *
-     * @param[in] occStatus - The occ status object
+    /** @brief Get the current power mode property from DBus
+     * @return Power mode
      */
-    explicit PowerIPS(Status& occStatus) :
-        occStatus(occStatus),
-        ipsMatch(utils::getBus(),
-                 sdbusplus::bus::match::rules::propertiesChanged(
-                     PIPS_PATH, PIPS_INTERFACE),
-                 [this](auto& msg) { this->ipsChanged(msg); }){};
+    SysPwrMode getDbusMode();
 
-  private:
+    /** @brief Update the power mode property on DBus
+     *
+     * @param[in]  newMode - desired power mode
+     *
+     * @return true on success
+     */
+    bool updateDbusMode(const SysPwrMode newMode);
+
     /** @brief Callback for IPS setting changes
      *
      * Process change and inform OCC
      *
-     * @param[in]  msg       - Data associated with IPS change signal
+     * @param[in]  msg - Data associated with IPS change signal
      *
      */
     void ipsChanged(sdbusplus::message::message& msg);
 
-    /* @brief OCC Status object */
-    Status& occStatus;
-
-    /** @brief Used to subscribe to dbus IPS property changes **/
-    sdbusplus::bus::match_t ipsMatch;
+    /** @brief Get the Idle Power Saver properties from DBus
+     * @return true if IPS is enabled
+     */
+    bool getIPSParms(uint8_t& enterUtil, uint16_t& enterTime, uint8_t& exitUtil,
+                     uint16_t& exitTime);
 };
 
 } // namespace powermode