Monitor for power cap changes when occ-control started

The object that is monitoring for power cap changes needs to get created
when occ-control is started so that changes are not missed.
Added validation check to ensure that the value sent to the OCC matches
the user configured value on dbus.

Verified on Raininer

Change-Id: I0d77f4569e5459ff58d6fc72147153133d2104a3
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/occ_manager.cpp b/occ_manager.cpp
index ea02cf3..78ac9f2 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -170,6 +170,13 @@
 #endif
             ));
 
+    // Create the power cap monitor object
+    if (!pcap)
+    {
+        pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
+            *statusObjects.back());
+    }
+
     if (statusObjects.back()->isMasterOcc())
     {
         log<level::INFO>(
@@ -181,6 +188,8 @@
 #ifdef POWER10
         // Set the master OCC on the PowerMode object
         pmode->setMasterOcc(path);
+        // Update power cap bounds
+        pcap->updatePcapBounds();
 #endif
     }
 
@@ -296,6 +305,9 @@
         statusObjects.emplace_back(
             std::make_unique<Status>(event, path.c_str(), *this));
     }
+    // The first device is master occ
+    pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
+        *statusObjects.front());
 #ifdef POWER10
     pmode = std::make_unique<powermode::PowerMode>(*this, powermode::PMODE_PATH,
                                                    powermode::PIPS_PATH);
@@ -1040,5 +1052,13 @@
     }
 }
 
+void Manager::updatePcapBounds() const
+{
+    if (pcap)
+    {
+        pcap->updatePcapBounds();
+    }
+}
+
 } // namespace occ
 } // namespace open_power
diff --git a/occ_manager.hpp b/occ_manager.hpp
index fa340b7..c06f6d3 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -7,6 +7,7 @@
 
 #include <libphal.H>
 #endif
+#include "powercap.hpp"
 #include "utils.hpp"
 #ifdef POWER10
 #include "powermode.hpp"
@@ -140,6 +141,9 @@
     void getAmbientData(bool& ambientValid, uint8_t& ambientTemp,
                         uint16_t& altitude) const;
 
+    /** @brief Notify pcap object to update bounds */
+    void updatePcapBounds() const;
+
   private:
     /** @brief Creates the OCC D-Bus objects.
      */
@@ -183,6 +187,9 @@
     /** @brief OCC Status objects */
     std::vector<std::unique_ptr<Status>> statusObjects;
 
+    /** @brief Power cap monitor and occ notification object */
+    std::unique_ptr<open_power::occ::powercap::PowerCap> pcap;
+
 #ifdef POWER10
     /** @brief Power mode monitor and notification object */
     std::unique_ptr<open_power::occ::powermode::PowerMode> pmode;
diff --git a/occ_pass_through.cpp b/occ_pass_through.cpp
index ea640ef..dd4e773 100644
--- a/occ_pass_through.cpp
+++ b/occ_pass_through.cpp
@@ -75,7 +75,7 @@
 {
     std::vector<uint8_t> response{};
 
-    log<level::DEBUG>(
+    log<level::INFO>(
         fmt::format("PassThrough::send() Sending 0x{:02X} command to OCC{}",
                     command.front(), occInstance)
             .c_str());
diff --git a/occ_status.cpp b/occ_status.cpp
index ff44c9d..95a7729 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -40,13 +40,8 @@
 
             if (device.master())
             {
-                if (!pcap)
-                {
-                    // Create the power cap monitor object for master OCC
-                    pcap = std::make_unique<powercap::PowerCap>(*this);
-                }
                 // Update powercap bounds from OCC
-                pcap->updatePcapBounds();
+                manager.updatePcapBounds();
             }
 
             // Call into Manager to let know that we have bound
diff --git a/occ_status.hpp b/occ_status.hpp
index 0d22c49..6a00676 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -227,9 +227,6 @@
     /** @brief OCC manager object */
     const Manager& manager;
 
-    /** @brief Power cap monitor and occ notification object */
-    std::unique_ptr<powercap::PowerCap> pcap;
-
 #ifdef POWER10
     /** @brief OCC PowerMode object */
     std::unique_ptr<powermode::PowerMode>& pmode;
diff --git a/powercap.cpp b/powercap.cpp
index cd11059..c95c279 100644
--- a/powercap.cpp
+++ b/powercap.cpp
@@ -19,7 +19,6 @@
 
 constexpr auto POWER_CAP_PROP = "PowerCap";
 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
-constexpr auto POWER_CAP_SOFT_MIN = "MinSoftPowerCapValue";
 constexpr auto POWER_CAP_HARD_MIN = "MinPowerCapValue";
 constexpr auto POWER_CAP_MAX = "MaxPowerCapValue";
 
@@ -30,29 +29,11 @@
 {
     // Build the hwmon string to write the power cap bounds
     fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"});
-    fs::path softMinName =
-        getPcapFilename(std::regex{"power\\d+_cap_min_soft$"});
     fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"});
 
     // Read the power cap bounds from sysfs files
     uint64_t cap;
-    uint32_t capSoftMin = 0, capHardMin = 0, capMax = INT_MAX;
-    std::ifstream softMinFile(softMinName, std::ios::in);
-    if (softMinFile)
-    {
-        softMinFile >> cap;
-        softMinFile.close();
-        // Convert to Watts
-        capSoftMin = cap / 1000000;
-    }
-    else
-    {
-        log<level::ERR>(
-            fmt::format(
-                "updatePcapBounds: unable to find pcap_min_soft file: {} (errno={})",
-                pcapBasePathname.c_str(), errno)
-                .c_str());
-    }
+    uint32_t capHardMin = 0, capMax = INT_MAX;
     std::ifstream minFile(minName, std::ios::in);
     if (minFile)
     {
@@ -87,7 +68,21 @@
     }
 
     // Save the bounds to dbus
-    updateDbusPcap(capSoftMin, capHardMin, capMax);
+    updateDbusPcap(capHardMin, capMax);
+
+    // Validate dbus and hwmon user caps match
+    const uint32_t dbusUserCap = getPcap();
+    const uint32_t hwmonUserCap = readUserCapHwmon();
+    if ((hwmonUserCap != 0) && (dbusUserCap != hwmonUserCap))
+    {
+        // User power cap is enabled, but does not match dbus
+        log<level::ERR>(
+            fmt::format(
+                "updatePcapBounds: user powercap mismatch (hwmon:{}W, bdus:{}W) - using dbus",
+                hwmonUserCap, dbusUserCap)
+                .c_str());
+        writeOcc(dbusUserCap);
+    }
 }
 
 uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled)
@@ -167,6 +162,8 @@
     return fs::path{};
 }
 
+// Write the user power cap to sysfs.
+// This will trigger the driver to send the cap to the OCC
 void PowerCap::writeOcc(uint32_t pcapValue)
 {
     // Build the hwmon string to write the user power cap
@@ -204,6 +201,44 @@
     return;
 }
 
+// Read the current user power cap from sysfs.
+uint32_t PowerCap::readUserCapHwmon()
+{
+    uint32_t userCap = 0;
+
+    // Get the sysfs filename for the user power cap
+    fs::path userCapName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
+    if (userCapName.empty())
+    {
+        log<level::ERR>(
+            fmt::format(
+                "readUserCapHwmon: Could not find a power cap file to read: {})",
+                pcapBasePathname.c_str())
+                .c_str());
+        return 0;
+    }
+
+    // Open the sysfs file and read the power cap
+    uint64_t cap;
+    std::ifstream file(userCapName, std::ios::in);
+    if (file)
+    {
+        file >> cap;
+        file.close();
+        // Convert to Watts
+        userCap = cap / 1000000;
+    }
+    else
+    {
+        log<level::ERR>(
+            fmt::format("readUserCapHwmon: Failed reading {} (errno={})",
+                        userCapName.c_str(), errno)
+                .c_str());
+    }
+
+    return userCap;
+}
+
 void PowerCap::pcapChanged(sdbusplus::message::message& msg)
 {
     if (!occStatus.occActive())
@@ -265,27 +300,12 @@
 }
 
 // Update the Power Cap bounds on DBus
-bool PowerCap::updateDbusPcap(uint32_t softMin, uint32_t hardMin, uint32_t max)
+bool PowerCap::updateDbusPcap(uint32_t hardMin, uint32_t max)
 {
     bool complete = true;
 
     try
     {
-        utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_SOFT_MIN,
-                           softMin);
-    }
-    catch (const sdbusplus::exception::exception& e)
-    {
-        log<level::ERR>(
-            fmt::format(
-                "updateDbusPcap: Failed to set SOFT PCAP to {}W due to {}",
-                softMin, e.what())
-                .c_str());
-        complete = false;
-    }
-
-    try
-    {
         utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_HARD_MIN,
                            hardMin);
     }
diff --git a/powercap.hpp b/powercap.hpp
index 34df45f..6d611db 100644
--- a/powercap.hpp
+++ b/powercap.hpp
@@ -93,6 +93,12 @@
      */
     void writeOcc(uint32_t pcapValue);
 
+    /** @brief Read the user power cap from sysfs
+     *
+     * @return User power cap value in Watts or 0 if disabled
+     */
+    uint32_t readUserCapHwmon();
+
     /**
      * @brief Returns the filename to use for the user power cap
      *
@@ -116,13 +122,12 @@
 
     /** @brief Update the power cap bounds on DBus
      *
-     * @param[in]  softMin - soft minimum power cap in Watts
      * @param[in]  hardMin - hard minimum power cap in Watts
      * @param[in]  pcapMax - maximum power cap in Watts
      *
      * @return true if all parms were written successfully
      */
-    bool updateDbusPcap(uint32_t softMin, uint32_t hardMin, uint32_t pcapMax);
+    bool updateDbusPcap(uint32_t hardMin, uint32_t pcapMax);
 };
 
 } // namespace powercap