psu-ng: Log error on brownout condition

Per design document:
https://github.com/openbmc/docs/blob/master/designs/power-recovery.md#brownout

The BMC must log an error indicating the brownout event has occurred.

Check for a brownout condition where all PSUs are either not present or
report an AC loss VIN fault. The error log would be the same as a
Blackout condition. Keep track if the error was created via a variable
so that the error is not created multiple times. Clear the variable once
the brownout condition is no longer detected or when then system is
powered off so that it gets logged on the next power on.

Tested: On Rainier 2S2U simulation where there are 4 PSUs slots but only
two power supplies connected, inject a VIN fault on the two present PSUs
and verify an error log for AC loss 110000AC is created:

Jan 31 16:57:37 p10bmc phosphor-psu-monitor[963]: INPUT fault:
STATUS_WORD = 0x2848, STATUS_MFR_SPECIFIC = 0x0, STATUS_INPUT = 0x38
Jan 31 16:57:37 p10bmc phosphor-psu-monitor[963]: VIN_UV fault:
STATUS_WORD = 0x2848, STATUS_MFR_SPECIFIC = 0x0, STATUS_INPUT = 0x38
Jan 31 16:57:38 p10bmc phosphor-psu-monitor[963]: INPUT fault:
STATUS_WORD = 0x2848, STATUS_MFR_SPECIFIC = 0x0, STATUS_INPUT = 0x38
Jan 31 16:57:38 p10bmc phosphor-psu-monitor[963]: VIN_UV fault:
STATUS_WORD = 0x2848, STATUS_MFR_SPECIFIC = 0x0, STATUS_INPUT = 0x38
Jan 31 16:57:38 p10bmc phosphor-log-manager[305]: Created PEL 0x50000007
(BMC ID 7) with SRC 110000AC

Change-Id: I7760b59a02ef2afc81bd7807c7896183d99a66ec
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 085f059..198e87f 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -353,6 +353,7 @@
         {
             powerOn = false;
             runValidateConfig = true;
+            brownoutLogged = false;
         }
     }
 }
@@ -425,11 +426,19 @@
     if (powerOn)
     {
         std::map<std::string, std::string> additionalData;
+        auto hasVINUVFaultCount = decltype(psus.size())(0);
 
         for (auto& psu : psus)
         {
             additionalData.clear();
 
+            // Check for brownout condition: PSU reports AC loss VIN fault or is
+            // not present.
+            if (!psu->isPresent() || psu->hasVINUVFault())
+            {
+                hasVINUVFaultCount++;
+            }
+
             if (!psu->isFaultLogged() && !psu->isPresent())
             {
                 std::map<std::string, std::string> requiredPSUsData;
@@ -612,6 +621,23 @@
                 }
             }
         }
+
+        if (hasVINUVFaultCount == psus.size())
+        {
+            // Brownout: All PSUs report AC loss VIN fault or are not present
+            if (!brownoutLogged)
+            {
+                createError(
+                    "xyz.openbmc_project.State.Shutdown.Power.Error.Blackout",
+                    additionalData);
+                brownoutLogged = true;
+            }
+        }
+        else
+        {
+            // Brownout condition is not present or has been cleared
+            brownoutLogged = false;
+        }
     }
 }
 
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 6c5775e..6314438 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -186,6 +186,9 @@
     /** @brief True if the power is on. */
     bool powerOn = false;
 
+    /** @brief True if an error for a brownout has already been logged. */
+    bool brownoutLogged = false;
+
     /** @brief Used as part of subscribing to power on state changes*/
     std::string powerService;