psu-ng: Change logic to clear brownout

The chassis component only recognizes brownout clearing and performs
auto power restart when the chassis state is off. Update the power
supply application to conform to this expectation.

Tested with a power level disturbance (PLD) hardware tester through a
matrix of varying durations and voltage percentages.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: I9938e9b3e2edc3954cc14b330a517889317b4477
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index c68e9be..c965892 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -509,19 +509,20 @@
         // Remember that this PSU has seen an AC fault
         acFault = AC_FAULT_LIMIT;
     }
-
-    if (vinUVFault &&
-        !(statusWord & phosphor::pmbus::status_word::VIN_UV_FAULT))
+    else
     {
-        log<level::INFO>(
-            fmt::format("{} VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
-                        "STATUS_MFR_SPECIFIC = {:#04x}, "
-                        "STATUS_INPUT = {:#04x}",
-                        shortName, statusWord, statusMFR, statusInput)
-                .c_str());
-        vinUVFault = 0;
+        if (vinUVFault != 0)
+        {
+            log<level::INFO>(
+                fmt::format("{} VIN_UV fault cleared: STATUS_WORD = {:#06x}, "
+                            "STATUS_MFR_SPECIFIC = {:#04x}, "
+                            "STATUS_INPUT = {:#04x}",
+                            shortName, statusWord, statusMFR, statusInput)
+                    .c_str());
+            vinUVFault = 0;
+        }
         // No AC fail, decrement counter
-        if (acFault)
+        if (acFault != 0)
         {
             --acFault;
         }
@@ -615,7 +616,7 @@
 
                 clearFaultFlags();
                 // No AC fail, decrement counter
-                if (acFault)
+                if (acFault != 0)
                 {
                     --acFault;
                 }
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 242632b..13d9356 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -8,6 +8,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <xyz/openbmc_project/State/Chassis/server.hpp>
+
 #include <algorithm>
 #include <regex>
 #include <set>
@@ -558,49 +560,6 @@
     log<level::INFO>("Synchronize INPUT_HISTORY completed");
 }
 
-bool PSUManager::isBrownout(std::map<std::string, std::string>& additionalData)
-{
-    size_t presentCount = 0;
-    size_t notPresentCount = 0;
-    size_t acFailedCount = 0;
-    size_t pgoodFailedCount = 0;
-    for (const auto& psu : psus)
-    {
-        if (psu->isPresent())
-        {
-            ++presentCount;
-            if (psu->hasACFault())
-            {
-                ++acFailedCount;
-            }
-            else if (psu->hasPgoodFault())
-            {
-                ++pgoodFailedCount;
-            }
-        }
-        else
-        {
-            ++notPresentCount;
-        }
-    }
-
-    // In brownout if at least one PS has seen an AC fail and all present PSUs
-    // have an AC or pgood failure. Note an AC fail is only set if at least one
-    // PSU is present.
-    bool isBrownout =
-        acFailedCount && (presentCount == (acFailedCount + pgoodFailedCount));
-    if (isBrownout)
-    {
-        additionalData.emplace("NOT_PRESENT_COUNT",
-                               std::to_string(notPresentCount));
-        additionalData.emplace("VIN_FAULT_COUNT",
-                               std::to_string(acFailedCount));
-        additionalData.emplace("PGOOD_FAULT_COUNT",
-                               std::to_string(pgoodFailedCount));
-    }
-    return isBrownout;
-}
-
 void PSUManager::analyze()
 {
     auto syncHistoryRequired =
@@ -617,19 +576,7 @@
         psu->analyze();
     }
 
-    std::map<std::string, std::string> additionalData;
-
-    // Only issue brownout failure if chassis pgood has failed and PSUs indicate
-    // AC failure
-    if (powerFaultOccurring && isBrownout(additionalData))
-    {
-        setBrownout(additionalData);
-    }
-    else
-    {
-        // Brownout condition is not present or has been cleared
-        clearBrownout();
-    }
+    analyzeBrownout();
 
     // Only perform individual PSU analysis if power is on and a brownout has
     // not already been logged
@@ -637,7 +584,7 @@
     {
         for (auto& psu : psus)
         {
-            additionalData.clear();
+            std::map<std::string, std::string> additionalData;
 
             if (!psu->isFaultLogged() && !psu->isPresent())
             {
@@ -826,6 +773,110 @@
     }
 }
 
+void PSUManager::analyzeBrownout()
+{
+    // Count number of power supplies failing
+    size_t presentCount = 0;
+    size_t notPresentCount = 0;
+    size_t acFailedCount = 0;
+    size_t pgoodFailedCount = 0;
+    for (const auto& psu : psus)
+    {
+        if (psu->isPresent())
+        {
+            ++presentCount;
+            if (psu->hasACFault())
+            {
+                ++acFailedCount;
+            }
+            else if (psu->hasPgoodFault())
+            {
+                ++pgoodFailedCount;
+            }
+        }
+        else
+        {
+            ++notPresentCount;
+        }
+    }
+
+    // Only issue brownout failure if chassis pgood has failed, it has not
+    // already been logged, at least one PSU has seen an AC fail, and all
+    // present PSUs have an AC or pgood failure. Note an AC fail is only set if
+    // at least one PSU is present.
+    if (powerFaultOccurring && !brownoutLogged && acFailedCount &&
+        (presentCount == (acFailedCount + pgoodFailedCount)))
+    {
+        // Indicate that the system is in a brownout condition by creating an
+        // error log and setting the PowerSystemInputs status property to Fault.
+        powerSystemInputs.status(
+            sdbusplus::xyz::openbmc_project::State::Decorator::server::
+                PowerSystemInputs::Status::Fault);
+
+        std::map<std::string, std::string> additionalData;
+        additionalData.emplace("NOT_PRESENT_COUNT",
+                               std::to_string(notPresentCount));
+        additionalData.emplace("VIN_FAULT_COUNT",
+                               std::to_string(acFailedCount));
+        additionalData.emplace("PGOOD_FAULT_COUNT",
+                               std::to_string(pgoodFailedCount));
+        log<level::INFO>(
+            fmt::format(
+                "Brownout detected, not present count: {}, AC fault count {}, pgood fault count: {}",
+                notPresentCount, acFailedCount, pgoodFailedCount)
+                .c_str());
+
+        createError("xyz.openbmc_project.State.Shutdown.Power.Error.Blackout",
+                    additionalData);
+        brownoutLogged = true;
+    }
+    else
+    {
+        // If a brownout was previously logged but at least one PSU is not
+        // currently in AC fault, determine if the brownout condition can be
+        // cleared
+        if (brownoutLogged && (acFailedCount < presentCount))
+        {
+            // Chassis only recognizes the PowerSystemInputs change when it is
+            // off
+            try
+            {
+                using PowerState = sdbusplus::xyz::openbmc_project::State::
+                    server::Chassis::PowerState;
+                PowerState currentPowerState;
+                util::getProperty<PowerState>(
+                    "xyz.openbmc_project.State.Chassis", "CurrentPowerState",
+                    "/xyz/openbmc_project/state/chassis0",
+                    "xyz.openbmc_project.State.Chassis", bus,
+                    currentPowerState);
+
+                if (currentPowerState == PowerState::Off)
+                {
+                    // Indicate that the system is no longer in a brownout
+                    // condition by setting the PowerSystemInputs status
+                    // property to Good.
+                    log<level::INFO>(
+                        fmt::format(
+                            "Brownout cleared, not present count: {}, AC fault count {}, pgood fault count: {}",
+                            notPresentCount, acFailedCount, pgoodFailedCount)
+                            .c_str());
+                    powerSystemInputs.status(
+                        sdbusplus::xyz::openbmc_project::State::Decorator::
+                            server::PowerSystemInputs::Status::Good);
+                    brownoutLogged = false;
+                }
+            }
+            catch (const std::exception& e)
+            {
+                log<level::ERR>(
+                    fmt::format("Error trying to clear brownout, error: {}",
+                                e.what())
+                        .c_str());
+            }
+        }
+    }
+}
+
 void PSUManager::updateMissingPSUs()
 {
     if (supportedConfigs.empty() || psus.empty())
@@ -1208,27 +1259,4 @@
     }
 }
 
-void PSUManager::setBrownout(std::map<std::string, std::string>& additionalData)
-{
-    powerSystemInputs.status(sdbusplus::xyz::openbmc_project::State::Decorator::
-                                 server::PowerSystemInputs::Status::Fault);
-    if (!brownoutLogged)
-    {
-        if (powerOn)
-        {
-            createError(
-                "xyz.openbmc_project.State.Shutdown.Power.Error.Blackout",
-                additionalData);
-            brownoutLogged = true;
-        }
-    }
-}
-
-void PSUManager::clearBrownout()
-{
-    powerSystemInputs.status(sdbusplus::xyz::openbmc_project::State::Decorator::
-                                 server::PowerSystemInputs::Status::Good);
-    brownoutLogged = false;
-}
-
 } // namespace phosphor::power::manager
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 5d2dce3..2ee61a6 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -178,6 +178,12 @@
      */
     void analyze();
 
+    /**
+     * @brief Analyze the set of the power supplies for a brownout failure. Log
+     * error when necessary, clear brownout condition when window has passed.
+     */
+    void analyzeBrownout();
+
     /** @brief True if the power is on. */
     bool powerOn = false;
 
@@ -317,28 +323,6 @@
     void setPowerConfigGPIO();
 
     /**
-     * @brief Determine if system is in brownout failure
-     * @param additionalData AdditionalData property of the error log entry
-     * @return true if system is in brownout failure, false otherwise.
-     */
-    bool isBrownout(std::map<std::string, std::string>& additionalData);
-
-    /**
-     * @brief Indicate that the system is in a brownout condition by creating an
-     * error log and setting the PowerSystemInputs status property to Fault.
-     *
-     * @param[in] additionalData - Contains debug information on the number of
-     *            PSUs in fault state or not present.
-     */
-    void setBrownout(std::map<std::string, std::string>& additionalData);
-
-    /**
-     * @brief Indicate that the system is no longer in a brownout condition by
-     * setting the PowerSystemInputs status property to Good.
-     */
-    void clearBrownout();
-
-    /**
      * @brief Map of supported PSU configurations that include the model name
      * and their properties.
      */