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/occ_status.cpp b/occ_status.cpp
index a2ab6ab..1da440c 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -46,13 +46,12 @@
         }
         else
         {
-            // Call into Manager to let know that we will unbind.
-            if (this->callBack)
-            {
-                this->callBack(value);
-            }
-
 #ifdef POWER10
+            if (device.master())
+            {
+                // Prevent mode changes
+                pmode->setMasterActive(false);
+            }
             if (safeStateDelayTimer.isEnabled())
             {
                 // stop safe delay timer
@@ -60,6 +59,12 @@
             }
 #endif
 
+            // Call into Manager to let know that we will unbind.
+            if (this->callBack)
+            {
+                this->callBack(value);
+            }
+
             // Stop watching for errors
             removeErrorWatch();
 
@@ -103,6 +108,11 @@
 // Callback handler when a device error is reported.
 void Status::deviceError()
 {
+#ifdef POWER10
+    // Prevent mode changes
+    pmode->setMasterActive(false);
+#endif
+
     // This would deem OCC inactive
     this->occActive(false);
 
@@ -196,6 +206,9 @@
             {
                 if (device.master())
                 {
+                    // Prevent mode changes
+                    pmode->setMasterActive();
+
                     // Special processing by master OCC when it goes active
                     occsWentActive();
                 }
@@ -238,189 +251,12 @@
 }
 
 #ifdef POWER10
-// Check if Hypervisor target is PowerVM
-bool Status::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";
-    constexpr auto HYPE_PROP = "Target";
-
-    bool powerVmTarget = false;
-
-    // This will throw exception on failure
-    auto& bus = utils::getBus();
-    auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
-    auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
-                                      "org.freedesktop.DBus.Properties", "Get");
-    method.append(HYPE_INTERFACE, HYPE_PROP);
-    auto reply = bus.call(method);
-
-    std::variant<std::string> hyperEntryValue;
-    reply.read(hyperEntryValue);
-    auto propVal = std::get<std::string>(hyperEntryValue);
-    if (Hyper::Target::convertHypervisorFromString(propVal) ==
-        Hyper::Target::Hypervisor::PowerVM)
-    {
-        powerVmTarget = true;
-    }
-
-    log<level::DEBUG>(
-        fmt::format("Status::isPowerVM returning {}", powerVmTarget).c_str());
-
-    return powerVmTarget;
-}
-
-// Get the requested power mode
-SysPwrMode Status::getMode()
-{
-    using namespace open_power::occ::powermode;
-    SysPwrMode pmode = SysPwrMode::NO_CHANGE;
-
-    // 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);
-    pmode = powermode::convertStringToMode(propVal);
-
-    log<level::DEBUG>(
-        fmt::format("Status::getMode returning {}", pmode).c_str());
-
-    return pmode;
-}
-
-// Get the requested power mode
-bool Status::getIPSParms(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)
-
-    std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
-        ipsProperties{};
-
-    // 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("Status::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("Status::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("Status::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("Status::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("Status::getIPSParms could not find property: {}",
-                        IPS_EXIT_TIME)
-                .c_str());
-    }
-
-    if (enterUtil > exitUtil)
-    {
-        log<level::ERR>(
-            fmt::format(
-                "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
-                enterUtil, exitUtil)
-                .c_str());
-        enterUtil = exitUtil;
-    }
-
-    return ipsEnabled;
-}
-
 // Special processing that needs to happen once the OCCs change to ACTIVE state
 void Status::occsWentActive()
 {
     CmdStatus status = CmdStatus::SUCCESS;
 
-    status = sendModeChange();
+    status = pmode->sendModeChange();
     if (status != CmdStatus::SUCCESS)
     {
         log<level::ERR>(
@@ -430,7 +266,7 @@
                 .c_str());
     }
 
-    status = sendIpsData();
+    status = pmode->sendIpsData();
     if (status != CmdStatus::SUCCESS)
     {
         log<level::ERR>(
@@ -441,189 +277,6 @@
     }
 }
 
-// Send mode change request to the master OCC
-CmdStatus Status::sendModeChange()
-{
-    CmdStatus status = CmdStatus::FAILURE;
-
-    if (!device.master())
-    {
-        log<level::ERR>(
-            fmt::format(
-                "Status::sendModeChange: MODE CHANGE does not get sent to slave OCC{}",
-                instance)
-                .c_str());
-        return status;
-    }
-    if (!isPowerVM())
-    {
-        // Mode change is only supported on PowerVM systems
-        log<level::DEBUG>(
-            "Status::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
-        return CmdStatus::SUCCESS;
-    }
-
-    const SysPwrMode newMode = getMode();
-
-    if (VALID_POWER_MODE_SETTING(newMode))
-    {
-        std::vector<std::uint8_t> cmd, rsp;
-        cmd.reserve(9);
-        cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
-        cmd.push_back(0x00); // Data Length (2 bytes)
-        cmd.push_back(0x06);
-        cmd.push_back(0x30); // Data (Version)
-        cmd.push_back(uint8_t(OccState::NO_CHANGE));
-        cmd.push_back(uint8_t(newMode));
-        cmd.push_back(0x00); // Mode Data (Freq Point)
-        cmd.push_back(0x00); //
-        cmd.push_back(0x00); // reserved
-        log<level::INFO>(
-            fmt::format(
-                "Status::sendModeChange: SET_MODE({}) command to OCC{} ({} bytes)",
-                newMode, instance, cmd.size())
-                .c_str());
-        status = occCmd.send(cmd, rsp);
-        if (status == CmdStatus::SUCCESS)
-        {
-            if (rsp.size() == 5)
-            {
-                if (RspStatus::SUCCESS != RspStatus(rsp[2]))
-                {
-                    log<level::ERR>(
-                        fmt::format(
-                            "Status::sendModeChange: SET MODE failed with status 0x{:02X}",
-                            rsp[2])
-                            .c_str());
-                    dump_hex(rsp);
-                    status = CmdStatus::FAILURE;
-                }
-            }
-            else
-            {
-                log<level::ERR>(
-                    "Status::sendModeChange: INVALID SET MODE response");
-                dump_hex(rsp);
-                status = CmdStatus::FAILURE;
-            }
-        }
-        else
-        {
-            if (status == CmdStatus::OPEN_FAILURE)
-            {
-                log<level::INFO>("Status::sendModeChange: OCC not active yet");
-                status = CmdStatus::SUCCESS;
-            }
-            else
-            {
-                log<level::ERR>("Status::sendModeChange: SET_MODE FAILED!");
-            }
-        }
-    }
-    else
-    {
-        log<level::ERR>(
-            fmt::format(
-                "Status::sendModeChange: Unable to set power mode to {}",
-                newMode)
-                .c_str());
-        status = CmdStatus::FAILURE;
-    }
-
-    return status;
-}
-
-// Send Idle Power Saver config data to the master OCC
-CmdStatus Status::sendIpsData()
-{
-    CmdStatus status = CmdStatus::FAILURE;
-
-    if (!device.master())
-    {
-        log<level::ERR>(
-            fmt::format(
-                "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent to slave OCC{}",
-                instance)
-                .c_str());
-        return status;
-    }
-    if (!isPowerVM())
-    {
-        // Idle Power Saver data is only supported on PowerVM systems
-        log<level::DEBUG>(
-            "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
-        return CmdStatus::SUCCESS;
-    }
-
-    uint8_t enterUtil, exitUtil;
-    uint16_t enterTime, exitTime;
-    const bool ipsEnabled =
-        getIPSParms(enterUtil, enterTime, exitUtil, exitTime);
-
-    log<level::INFO>(
-        fmt::format(
-            "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
-            ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
-            .c_str());
-
-    std::vector<std::uint8_t> cmd, rsp;
-    cmd.reserve(12);
-    cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
-    cmd.push_back(0x00);               // Data Length (2 bytes)
-    cmd.push_back(0x09);               //
-    cmd.push_back(0x11);               // Config Format: IPS Settings
-    cmd.push_back(0x00);               // Version
-    cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
-    cmd.push_back(enterTime >> 8);     // Enter Delay Time
-    cmd.push_back(enterTime & 0xFF);   //
-    cmd.push_back(enterUtil);          // Enter Utilization
-    cmd.push_back(exitTime >> 8);      // Exit Delay Time
-    cmd.push_back(exitTime & 0xFF);    //
-    cmd.push_back(exitUtil);           // Exit Utilization
-    log<level::INFO>(fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] "
-                                 "command to OCC{} ({} bytes)",
-                                 instance, cmd.size())
-                         .c_str());
-    status = occCmd.send(cmd, rsp);
-    if (status == CmdStatus::SUCCESS)
-    {
-        if (rsp.size() == 5)
-        {
-            if (RspStatus::SUCCESS != RspStatus(rsp[2]))
-            {
-                log<level::ERR>(
-                    fmt::format(
-                        "Status::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
-                        rsp[2])
-                        .c_str());
-                dump_hex(rsp);
-                status = CmdStatus::FAILURE;
-            }
-        }
-        else
-        {
-            log<level::ERR>(
-                "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
-            dump_hex(rsp);
-            status = CmdStatus::FAILURE;
-        }
-    }
-    else
-    {
-        if (status == CmdStatus::OPEN_FAILURE)
-        {
-            log<level::INFO>("Status::sendIpsData: OCC not active yet");
-            status = CmdStatus::SUCCESS;
-        }
-        else
-        {
-            log<level::ERR>("Status::sendIpsData: SET_CFG_DATA[IPS] FAILED!");
-        }
-    }
-
-    return status;
-}
-
 // Send Ambient and Altitude to the OCC
 CmdStatus Status::sendAmbient(const uint8_t inTemp, const uint16_t inAltitude)
 {