Add support for power supply off when it should be on

If the power good bit indicates false, or the UNIT_IS_OFF bit is on,
create an error log and attach STATUS_WORD, STATUS_INPUT, STATUS_VOUT,
STATUS_IOUT, and MFR_SPECIFIC values to the metadata. The combination of
those PMBus command results should give an indication as to why the
power supply has turned off.

Change-Id: I692a8fdeac3fe208a5eb70964db7b5094cfb587c
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/power-supply/power_supply.cpp b/power-supply/power_supply.cpp
index 1510753..467144b 100644
--- a/power-supply/power_supply.cpp
+++ b/power-supply/power_supply.cpp
@@ -90,7 +90,12 @@
     {
         if (present)
         {
-            auto curUVFault = pmbusIntf.readBit(VIN_UV_FAULT, Type::Hwmon);
+            std::uint16_t statusWord = 0;
+            std::uint8_t  statusInput = 0;
+
+            // Read the 2 byte STATUS_WORD value to check for faults.
+            statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
+
             //TODO: 3 consecutive reads should be performed.
             // If 3 consecutive reads are seen, log the fault.
             // Driver gives cached value, read once a second.
@@ -98,63 +103,96 @@
             // If count reaches 3, we have fault. If count reaches 0, fault is
             // cleared.
 
-            auto curInputFault = pmbusIntf.readBit(INPUT_FAULT_WARN,
-                                                   Type::Hwmon);
-
-            if (curUVFault != vinUVFault)
+            if ((statusWord & status_word::VIN_UV_FAULT) && !vinUVFault)
             {
-                vinUVFault = curUVFault;
+                vinUVFault = true;
 
-                if (curUVFault)
-                {
-                    std::uint16_t statusWord = 0;
-                    statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
+                util::NamesValues nv;
+                nv.add("STATUS_WORD", statusWord);
 
-                    util::NamesValues nv;
-                    nv.add("STATUS_WORD", statusWord);
+                using metadata = xyz::openbmc_project::Power::Fault::
+                        PowerSupplyUnderVoltageFault;
 
-                    using metadata = xyz::openbmc_project::Power::Fault::
-                            PowerSupplyUnderVoltageFault;
+                report<PowerSupplyUnderVoltageFault>(
+                        metadata::RAW_STATUS(nv.get().c_str()));
 
-                    report<PowerSupplyUnderVoltageFault>(
-                            metadata::RAW_STATUS(nv.get().c_str()));
-
-                    vinUVFault = true;
-                }
-                else
-                {
-                    log<level::INFO>("VIN_UV_FAULT cleared",
-                                     entry("POWERSUPPLY=%s",
-                                           inventoryPath.c_str()));
-                }
-
+                vinUVFault = true;
+            }
+            else
+            {
+                vinUVFault = false;
+                log<level::INFO>("VIN_UV_FAULT cleared",
+                                 entry("POWERSUPPLY=%s",
+                                 inventoryPath.c_str()));
             }
 
-            if (curInputFault != inputFault)
+            if ((statusWord & status_word::INPUT_FAULT_WARN) && !inputFault)
             {
-                if (curInputFault)
-                {
-                    std::uint16_t statusWord = 0;
-                    std::uint8_t  statusInput = 0;
+                inputFault = true;
 
-                    statusWord = pmbusIntf.read(STATUS_WORD, Type::Debug);
+                statusInput = pmbusIntf.read(STATUS_INPUT, Type::Debug);
+
+                util::NamesValues nv;
+                nv.add("STATUS_WORD", statusWord);
+                nv.add("STATUS_INPUT", statusInput);
+
+                using metadata = xyz::openbmc_project::Power::Fault::
+                        PowerSupplyInputFault;
+
+                report<PowerSupplyInputFault>(metadata::RAW_STATUS(
+                                                      nv.get().c_str()));
+            }
+            else
+            {
+                if ((inputFault) &&
+                    !(statusWord & status_word::INPUT_FAULT_WARN))
+                {
+                    inputFault = false;
+
                     statusInput = pmbusIntf.read(STATUS_INPUT, Type::Debug);
 
+                    log<level::INFO>("INPUT_FAULT_WARN cleared",
+                                     entry("POWERSUPPLY=%s",
+                                             inventoryPath.c_str()),
+                                     entry("STATUS_WORD=0x%04X", statusWord),
+                                     entry("STATUS_INPUT=0x%02X", statusInput));
+                }
+            }
+
+            if (powerOn)
+            {
+                // Check PG# and UNIT_IS_OFF
+                if (((statusWord & status_word::POWER_GOOD_NEGATED) ||
+                     (statusWord & status_word::UNIT_IS_OFF)) &&
+                    !powerOnFault)
+                {
+                    std::uint8_t  statusVout = 0;
+                    std::uint8_t  statusIout = 0;
+                    std::uint8_t  statusMFR  = 0;
+
+                    statusInput = pmbusIntf.read(STATUS_INPUT, Type::Debug);
+                    auto status0Vout = pmbusIntf.insertPageNum(STATUS_VOUT, 0);
+                    statusVout = pmbusIntf.read(status0Vout, Type::Debug);
+                    statusIout = pmbusIntf.read(STATUS_IOUT, Type::Debug);
+                    statusMFR = pmbusIntf.read(STATUS_MFR, Type::Debug);
+
                     util::NamesValues nv;
                     nv.add("STATUS_WORD", statusWord);
                     nv.add("STATUS_INPUT", statusInput);
+                    nv.add("STATUS_VOUT", statusVout);
+                    nv.add("STATUS_IOUT", statusIout);
+                    nv.add("MFR_SPECIFIC", statusMFR);
 
                     using metadata = xyz::openbmc_project::Power::Fault::
-                            PowerSupplyInputFault;
+                            PowerSupplyShouldBeOn;
 
-                    report<PowerSupplyInputFault>(
-                            metadata::RAW_STATUS(nv.get().c_str()));
+                    // A power supply is OFF (or pgood low) but should be on.
+                    report<PowerSupplyShouldBeOn>(
+                            metadata::RAW_STATUS(nv.get().c_str()),
+                            metadata::CALLOUT_INVENTORY_PATH(
+                                    inventoryPath.c_str()));
 
-                    inputFault = true;
-                }
-                else
-                {
-                    inputFault = false;
+                    powerOnFault = true;
                 }
 
             }
@@ -239,6 +277,7 @@
             readFailLogged = false;
             vinUVFault = false;
             inputFault = false;
+            powerOnFault = false;
             powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
         }
         else
diff --git a/power-supply/power_supply.hpp b/power-supply/power_supply.hpp
index 317c20b..e9bd9f3 100644
--- a/power-supply/power_supply.hpp
+++ b/power-supply/power_supply.hpp
@@ -96,6 +96,9 @@
         /** @brief True if the power is on. */
         bool powerOn = false;
 
+        /** @brief True if power on fault has been detected/reported. */
+        bool powerOnFault = false;
+
         /** @brief The sd_event structure used by the power on timer. */
         event::Event& event;
 
@@ -119,9 +122,7 @@
         /** @brief Used to subscribe to D-Bus power on state changes **/
         std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
 
-        /**
-         * @brief Has a PMBus read failure already been logged?
-         */
+        /** @brief Has a PMBus read failure already been logged? */
         bool readFailLogged = false;
 
         /**
@@ -140,7 +141,8 @@
          */
         bool inputFault = false;
 
-        /** @brief Callback for inventory property changes
+        /**
+         * @brief Callback for inventory property changes
          *
          * Process change of Present property for power supply.
          *
@@ -166,7 +168,9 @@
          */
         void updatePowerState();
 
-        /** @brief Callback for power state property changes
+        /**
+         * @brief Callback for power state property changes
+         *
          * Process changes to the powered on stat property for the system.
          *
          * @param[in] msg - Data associated with the power state signal