pseq: Support "GPIOs only" power sequencer device

Add support for a "GPIOs only" power sequencer device to the
phosphor-power-sequencer application.

If this device type is specified in the JSON configuration file, then
the application will only use the named GPIOs to power the device on/off
and read the power good signal. No attempt will be made to communicate
with the device otherwise. No pgood fault isolation will be performed.

This device type is useful for simple systems that do not require pgood
fault isolation. It is also useful as a temporary solution when
performing early bring-up work on a new system.

Tested:
* Ran automated tests

Change-Id: Ib5ba9a9c136dd5f5e853372f881f9e227f01a6bd
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/config_file_parser.cpp b/phosphor-power-sequencer/src/config_file_parser.cpp
index 2a5cc26..07a92c3 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -17,6 +17,7 @@
 #include "config_file_parser.hpp"
 
 #include "config_file_parser_error.hpp"
+#include "gpios_only_device.hpp"
 #include "json_parser_utils.hpp"
 #include "ucd90160_device.hpp"
 #include "ucd90320_device.hpp"
@@ -333,11 +334,19 @@
     std::string type = parseString(typeElement, false, variables);
     ++propertyCount;
 
-    // Required i2c_interface property
-    const json& i2cInterfaceElement =
-        getRequiredProperty(element, "i2c_interface");
-    auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
-    ++propertyCount;
+    // i2c_interface property is required for some device types
+    uint8_t bus{0};
+    uint16_t address{0};
+    auto i2cInterfaceIt = element.find("i2c_interface");
+    if (i2cInterfaceIt != element.end())
+    {
+        std::tie(bus, address) = parseI2CInterface(*i2cInterfaceIt, variables);
+        ++propertyCount;
+    }
+    else if (type != GPIOsOnlyDevice::deviceName)
+    {
+        throw std::invalid_argument{"Required property missing: i2c_interface"};
+    }
 
     // Required power_control_gpio_name property
     const json& powerControlGPIONameElement =
@@ -353,11 +362,18 @@
         parseString(powerGoodGPIONameElement, false, variables);
     ++propertyCount;
 
-    // Required rails property
-    const json& railsElement = getRequiredProperty(element, "rails");
-    std::vector<std::unique_ptr<Rail>> rails =
-        parseRailArray(railsElement, variables);
-    ++propertyCount;
+    // rails property is required for some device types
+    std::vector<std::unique_ptr<Rail>> rails{};
+    auto railsIt = element.find("rails");
+    if (railsIt != element.end())
+    {
+        rails = parseRailArray(*railsIt, variables);
+        ++propertyCount;
+    }
+    else if (type != GPIOsOnlyDevice::deviceName)
+    {
+        throw std::invalid_argument{"Required property missing: rails"};
+    }
 
     // Verify no invalid properties exist
     verifyPropertyCount(element, propertyCount);
@@ -374,6 +390,11 @@
             bus, address, powerControlGPIOName, powerGoodGPIOName,
             std::move(rails), services);
     }
+    else if (type == GPIOsOnlyDevice::deviceName)
+    {
+        return std::make_unique<GPIOsOnlyDevice>(powerControlGPIOName,
+                                                 powerGoodGPIOName);
+    }
     throw std::invalid_argument{"Invalid power sequencer type: " + type};
 }