pseq: Add GPIO formatting for UCD90160

Add support for formatting GPIO values when a pgood error is detected by
a UCD90160.

Write the GPIO names and values to the journal and store them in the
error log additional data.

This formatting support already exists for the UCD90320.

Tested:
* Test where pgood error occurs while chassis is powering on
  * Verify correct GPIO values are captured
  * Verify GPIO names and values are written to journal
  * Verify GPIO names and values are stored in error log
* Test where pgood error occurs after chassis is powered on
  * Verify correct GPIO values are captured
  * Verify correct GPIO names and values are written to journal
  * Verify correct GPIO names and values are stored in error log
* Test where the number of GPIO values is unexpected
  * Verify all values stored in additional data with no names
  * Verify all values written to the journal with no names

Change-Id: I9a761670bd8af020deb7f85d59d409ad6b5d5b2c
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/ucd90160_monitor.cpp b/phosphor-power-sequencer/src/ucd90160_monitor.cpp
index f933230..a75a6fc 100644
--- a/phosphor-power-sequencer/src/ucd90160_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90160_monitor.cpp
@@ -16,12 +16,69 @@
 
 #include "ucd90160_monitor.hpp"
 
+#include <fmt/format.h>
+#include <fmt/ranges.h>
+
+#include <phosphor-logging/log.hpp>
+
+#include <algorithm>
+#include <array>
+#include <span>
+
 namespace phosphor::power::sequencer
 {
 
+using namespace phosphor::logging;
+
+// Names of the UCD90160 GPIOs.  The array indices correspond to the Pin IDs
+// defined in the UCD90160 PMBus interface documentation.  These Pin IDs are the
+// same as the libgpiod line offsets used to obtain the GPIO values.
+static constexpr std::array<const char*, 26> gpioNames = {
+    "FPWM1_GPIO5", "FPWM2_GPIO6",  "FPWM3_GPIO7",  "FPWM4_GPIO8",
+    "FPWM5_GPIO9", "FPWM6_GPIO10", "FPWM7_GPIO11", "FPWM8_GPIO12",
+    "GPI1_PWM1",   "GPI2_PWM2",    "GPI3_PWM3",    "GPI4_PWM4",
+    "GPIO14",      "GPIO15",       "TDO_GPIO20",   "TCK_GPIO19",
+    "TMS_GPIO22",  "TDI_GPIO21",   "GPIO1",        "GPIO2",
+    "GPIO3",       "GPIO4",        "GPIO13",       "GPIO16",
+    "GPIO17",      "GPIO18"};
+
 UCD90160Monitor::UCD90160Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
                                  std::uint16_t i2cAddress) :
     UCD90xMonitor(bus, i2cBus, i2cAddress, "UCD90160", 16)
 {}
 
+void UCD90160Monitor::formatGpioValues(
+    const std::vector<int>& values, unsigned int numberLines,
+    std::map<std::string, std::string>& additionalData) const
+{
+    // Verify the expected number of GPIO values were passed in
+    if ((values.size() == gpioNames.size()) &&
+        (numberLines == gpioNames.size()))
+    {
+        // Store GPIO names and values in additional data and journal.
+        // Use groups of GPIOs in journal to minimize number of entries.
+        unsigned int groupSize{4};
+        for (unsigned int i = 0; i < gpioNames.size(); ++i)
+        {
+            additionalData.emplace(gpioNames[i], std::to_string(values[i]));
+            if ((i % groupSize) == 0)
+            {
+                unsigned int gpiosLeft = gpioNames.size() - i;
+                unsigned int count = std::min(groupSize, gpiosLeft);
+                log<level::INFO>(
+                    fmt::format("GPIO values: {}: {}",
+                                std::span{gpioNames.begin() + i, count},
+                                std::span{values.begin() + i, count})
+                        .c_str());
+            }
+        }
+    }
+    else
+    {
+        // Unexpected number of GPIO values.  Store without names.
+        additionalData.emplace("GPIO_VALUES", fmt::format("{}", values));
+        log<level::INFO>(fmt::format("GPIO values: {}", values).c_str());
+    }
+}
+
 } // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90160_monitor.hpp b/phosphor-power-sequencer/src/ucd90160_monitor.hpp
index 31d4567..00a3cd8 100644
--- a/phosphor-power-sequencer/src/ucd90160_monitor.hpp
+++ b/phosphor-power-sequencer/src/ucd90160_monitor.hpp
@@ -5,6 +5,9 @@
 #include <sdbusplus/bus.hpp>
 
 #include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
 
 namespace phosphor::power::sequencer
 {
@@ -32,6 +35,12 @@
      */
     UCD90160Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
                     std::uint16_t i2cAddress);
+
+  protected:
+    /** @copydoc UCD90xMonitor::formatGpioValues() */
+    void formatGpioValues(
+        const std::vector<int>& values, unsigned int numberLines,
+        std::map<std::string, std::string>& additionalData) const override;
 };
 
 } // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90x_monitor.cpp b/phosphor-power-sequencer/src/ucd90x_monitor.cpp
index 2dc4675..f36d734 100644
--- a/phosphor-power-sequencer/src/ucd90x_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90x_monitor.cpp
@@ -287,8 +287,12 @@
     log<level::INFO>(
         fmt::format("GPIO chip number of lines: {}", numberLines).c_str());
 
-    // Workaround libgpiod bulk line maximum by getting values from individual
-    // lines
+    // Read GPIO values.  Work around libgpiod bulk line maximum by getting
+    // values from individual lines.  The libgpiod line offsets are the same as
+    // the Pin IDs defined in the UCD90xxx PMBus interface documentation.  These
+    // Pin IDs are different from the pin numbers on the chip.  For example, on
+    // the UCD90160, "FPWM1/GPIO5" is Pin ID/line offset 0, but it is pin number
+    // 17 on the chip.
     std::vector<int> values;
     try
     {