Fill in voltage fault checking code

Fill in the code to check for a voltage fault.  This is where
the power sequencer device detects that one of its child
devices has a bad voltage.  A separate error log will be created
for each voltage rail that has a fault.  Each rail will only have
an error logged against it once for the lifetime of the object.

There will be support documentation that maps the failing rail
name to the hardware that the rail corresponds to.

Change-Id: I13380b9898613bf8e76d66a72e1fbe005f816dad
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/power-sequencer/ucd90160.cpp b/power-sequencer/ucd90160.cpp
index 7a69a64..704c4e0 100644
--- a/power-sequencer/ucd90160.cpp
+++ b/power-sequencer/ucd90160.cpp
@@ -21,6 +21,7 @@
 #include <xyz/openbmc_project/Sensor/Device/error.hpp>
 #include <xyz/openbmc_project/Control/Device/error.hpp>
 #include <xyz/openbmc_project/Power/Fault/error.hpp>
+#include "names_values.hpp"
 #include "ucd90160.hpp"
 
 namespace witherspoon
@@ -31,9 +32,11 @@
 using namespace std::string_literals;
 
 const auto CLEAR_LOGGED_FAULTS = "clear_logged_faults"s;
+const auto MFR_STATUS = "mfr_status"s;
 
 const auto DEVICE_NAME = "UCD90160"s;
 const auto DRIVER_NAME = "ucd9000"s;
+constexpr auto NUM_PAGES = 16;
 
 using namespace pmbus;
 using namespace phosphor::logging;
@@ -94,6 +97,16 @@
     }
 }
 
+uint16_t UCD90160::readStatusWord()
+{
+    return interface.read(STATUS_WORD, Type::Debug);
+}
+
+uint32_t UCD90160::readMFRStatus()
+{
+    return interface.read(MFR_STATUS, Type::DeviceDebug);
+}
+
 void UCD90160::clearFaults()
 {
     try
@@ -113,7 +126,52 @@
 
 bool UCD90160::checkVOUTFaults()
 {
-    return false;
+    bool errorCreated = false;
+    auto statusWord = readStatusWord();
+
+    //The status_word register has a summary bit to tell us
+    //if each page even needs to be checked
+    if (!(statusWord & status_word::VOUT_FAULT))
+    {
+        return errorCreated;
+    }
+
+    for (size_t page = 0; page < NUM_PAGES; page++)
+    {
+        if (isVoutFaultLogged(page))
+        {
+            continue;
+        }
+
+        auto statusVout = interface.insertPageNum(STATUS_VOUT, page);
+        uint8_t vout = interface.read(statusVout, Type::Debug);
+
+        //Any bit on is an error
+        if (vout)
+        {
+            auto& railNames = std::get<ucd90160::railNamesField>(
+                    deviceMap.find(getInstance())->second);
+            auto railName = railNames.at(page);
+
+            util::NamesValues nv;
+            nv.add("STATUS_WORD", statusWord);
+            nv.add("STATUS_VOUT", vout);
+            nv.add("MFR_STATUS", readMFRStatus());
+
+            using metadata = xyz::openbmc_project::Power::Fault::
+                    PowerSequencerVoltageFault;
+
+            report<PowerSequencerVoltageFault>(
+                    metadata::RAIL(page),
+                    metadata::RAIL_NAME(railName.c_str()),
+                    metadata::RAW_STATUS(nv.get().c_str()));
+
+            setVoutFaultLogged(page);
+            errorCreated = true;
+        }
+    }
+
+    return errorCreated;
 }
 
 bool UCD90160::checkPGOODFaults(bool polling)