pseq: Add pgood fault detection to Rail class

Add power good (pgood) fault detection to the Rail class in the
phosphor-power-sequencer application.

Implement the checking defined in the JSON configuration file:
* Check for fault bits set in STATUS_VOUT
* Check for a GPIO with the wrong value
* Check if the output voltage (READ_VOUT) is below the undervoltage
  limit (VOUT_UV_FAULT_LIMIT)

If a pgood fault is detected, capture the relevant debug information
such as the rail name and STATUS_WORD value.

Tested:
* Added gtests for the new code
* Ran all gtests and verified they passed

Change-Id: I09c3ed6c504fe907a7854a4ac462a2bc4a8b806f
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/rail.hpp b/phosphor-power-sequencer/src/rail.hpp
index a0115e5..5a49e13 100644
--- a/phosphor-power-sequencer/src/rail.hpp
+++ b/phosphor-power-sequencer/src/rail.hpp
@@ -15,10 +15,15 @@
  */
 #pragma once
 
+#include "power_sequencer_device.hpp"
+#include "services.hpp"
+
 #include <cstdint>
+#include <map>
 #include <optional>
 #include <stdexcept>
 #include <string>
+#include <vector>
 
 namespace phosphor::power::sequencer
 {
@@ -93,7 +98,7 @@
         compareVoltageToLimit{compareVoltageToLimit}, gpio{gpio}
     {
         // If checking STATUS_VOUT or output voltage, verify PAGE was specified
-        if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
+        if ((checkStatusVout || compareVoltageToLimit) && !page)
         {
             throw std::invalid_argument{"PMBus PAGE is required"};
         }
@@ -172,8 +177,158 @@
         return gpio;
     }
 
+    /**
+     * Returns whether the rail is present.
+     *
+     * Returns true if no inventory path was specified for presence detection.
+     *
+     * @param services System services like hardware presence and the journal
+     * @return true if rail is present, false otherwise
+     */
+    bool isPresent(Services& services);
+
+    /**
+     * Returns the value of the PMBus STATUS_WORD command for the rail.
+     *
+     * Reads the value from the specified device.  The returned value is in
+     * host-endian order.
+     *
+     * Throws an exception if the value could not be obtained.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @return STATUS_WORD value
+     */
+    uint16_t getStatusWord(PowerSequencerDevice& device);
+
+    /**
+     * Returns the value of the PMBus STATUS_VOUT command for the rail.
+     *
+     * Reads the value from the specified device.
+     *
+     * Throws an exception if the value could not be obtained.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @return STATUS_VOUT value
+     */
+    uint8_t getStatusVout(PowerSequencerDevice& device);
+
+    /**
+     * Returns the value of the PMBus READ_VOUT command for the rail.
+     *
+     * Reads the value from the specified device.  The returned value is in
+     * volts.
+     *
+     * Throws an exception if the value could not be obtained.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @return READ_VOUT value in volts
+     */
+    double getReadVout(PowerSequencerDevice& device);
+
+    /**
+     * Returns the value of the PMBus VOUT_UV_FAULT_LIMIT command for the rail.
+     *
+     * Reads the value from the specified device.  The returned value is in
+     * volts.
+     *
+     * Throws an exception if the value could not be obtained.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @return VOUT_UV_FAULT_LIMIT value in volts
+     */
+    double getVoutUVFaultLimit(PowerSequencerDevice& device);
+
+    /**
+     * Returns whether a pgood (power good) fault has occurred on the rail.
+     *
+     * Throws an exception if an error occurs while trying to obtain the rail
+     * status.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @param services System services like hardware presence and the journal
+     * @param gpioValues GPIO values obtained from the device (if any)
+     * @param additionalData Additional data to include in an error log if this
+     *                       method returns true
+     * @return true if a pgood fault was found on the rail, false otherwise
+     */
+    bool hasPgoodFault(PowerSequencerDevice& device, Services& services,
+                       const std::vector<int>& gpioValues,
+                       std::map<std::string, std::string>& additionalData);
+
   private:
     /**
+     * Verifies that a PMBus PAGE number is defined for the rail.
+     *
+     * Throws an exception if a PAGE number is not defined.
+     */
+    void verifyHasPage();
+
+    /**
+     * Returns whether the PMBus STATUS_VOUT command indicates a pgood fault
+     * has occurred on the rail.
+     *
+     * Throws an exception if an error occurs while trying to obtain the rail
+     * status.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @param services System services like hardware presence and the journal
+     * @param additionalData Additional data to include in an error log if this
+     *                       method returns true
+     * @return true if a pgood fault was found on the rail, false otherwise
+     */
+    bool hasPgoodFaultStatusVout(
+        PowerSequencerDevice& device, Services& services,
+        std::map<std::string, std::string>& additionalData);
+
+    /**
+     * Returns whether a GPIO value indicates a pgood fault has occurred on the
+     * rail.
+     *
+     * Throws an exception if an error occurs while trying to obtain the rail
+     * status.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @param gpioValues GPIO values obtained from the device (if any)
+     * @param additionalData Additional data to include in an error log if this
+     *                       method returns true
+     * @return true if a pgood fault was found on the rail, false otherwise
+     */
+    bool hasPgoodFaultGPIO(Services& services,
+                           const std::vector<int>& gpioValues,
+                           std::map<std::string, std::string>& additionalData);
+
+    /**
+     * Returns whether the output voltage is below the undervoltage limit
+     * indicating a pgood fault has occurred on the rail.
+     *
+     * Throws an exception if an error occurs while trying to obtain the rail
+     * status.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @param services System services like hardware presence and the journal
+     * @param additionalData Additional data to include in an error log if this
+     *                       method returns true
+     * @return true if a pgood fault was found on the rail, false otherwise
+     */
+    bool hasPgoodFaultOutputVoltage(
+        PowerSequencerDevice& device, Services& services,
+        std::map<std::string, std::string>& additionalData);
+
+    /**
+     * Store pgood fault debug data in the specified additional data map.
+     *
+     * Stores data that is relevant regardless of which method was used to
+     * detect the pgood fault.
+     *
+     * @param device Power sequencer device that enables and monitors the rail
+     * @param services System services like hardware presence and the journal
+     * @param additionalData Additional data to include in an error log
+     */
+    void storePgoodFaultDebugData(
+        PowerSequencerDevice& device, Services& services,
+        std::map<std::string, std::string>& additionalData);
+
+    /**
      * Unique name for the rail.
      */
     std::string name{};