pseq: Add GPIOs to power control application

Add power control GPIOs for power on / off control and chassis power
good monitoring. Use libgpiod and make neccessary meson changes.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: I4f44552e855547fea466a98cb71acdb315aa26f5
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index f3e0d26..adb8a3f 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -9,6 +9,7 @@
     'power_control.cpp',
     'power_interface.cpp',
     dependencies: [
+        libgpiodcxx,
         phosphor_logging,
         sdbusplus,
         sdeventplus,
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index 931142d..2680409 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -20,19 +20,22 @@
 
 #include <fmt/format.h>
 
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
-#include <sdbusplus/bus.hpp>
-#include <sdeventplus/event.hpp>
-#include <sdeventplus/utility/timer.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
 
-#include <chrono>
 #include <exception>
+#include <string>
 
 using namespace phosphor::logging;
 
 namespace phosphor::power::sequencer
 {
 
+const std::string powerControlLineName = "power-chassis-control";
+const std::string pgoodLineName = "power-chassis-good";
+
 PowerControl::PowerControl(sdbusplus::bus::bus& bus,
                            const sdeventplus::Event& event) :
     PowerObject{bus, POWER_OBJ_PATH, true},
@@ -41,6 +44,7 @@
 {
     // Obtain dbus service name
     bus.request_name(POWER_IFACE);
+    setUpGpio();
 }
 
 int PowerControl::getPgood() const
@@ -59,7 +63,37 @@
 }
 
 void PowerControl::pollPgood()
-{}
+{
+    if (inStateTransition)
+    {
+        const auto now = std::chrono::steady_clock::now();
+        if (now > pgoodTimeoutTime)
+        {
+            log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
+            inStateTransition = false;
+            return;
+        }
+    }
+
+    int pgoodState = pgoodLine.get_value();
+    if (pgoodState != pgood)
+    {
+        pgood = pgoodState;
+        if (pgoodState == 0)
+        {
+            emitPowerLostSignal();
+        }
+        else
+        {
+            emitPowerGoodSignal();
+        }
+        emitPropertyChangedSignal("pgood");
+    }
+    if (pgoodState == state)
+    {
+        inStateTransition = false;
+    }
+}
 
 void PowerControl::setPgoodTimeout(int t)
 {
@@ -80,8 +114,46 @@
     }
 
     log<level::INFO>(fmt::format("setState: {}", s).c_str());
+    powerControlLine.request(
+        {"phosphor-power-control", gpiod::line_request::DIRECTION_OUTPUT, 0});
+    powerControlLine.set_value(s);
+    powerControlLine.release();
+
+    pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
+    inStateTransition = true;
     state = s;
     emitPropertyChangedSignal("state");
 }
 
+void PowerControl::setUpGpio()
+{
+    pgoodLine = gpiod::find_line(pgoodLineName);
+    if (!pgoodLine)
+    {
+        std::string errorString =
+            fmt::format("GPIO line name not found: {}", pgoodLineName);
+        log<level::ERR>(errorString.c_str());
+        report<
+            sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+        throw std::runtime_error(errorString);
+    }
+    powerControlLine = gpiod::find_line(powerControlLineName);
+    if (!powerControlLine)
+    {
+        std::string errorString =
+            fmt::format("GPIO line name not found: {}", powerControlLineName);
+        log<level::ERR>(errorString.c_str());
+        report<
+            sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+        throw std::runtime_error(errorString);
+    }
+
+    pgoodLine.request(
+        {"phosphor-power-control", gpiod::line_request::DIRECTION_INPUT, 0});
+    int pgoodState = pgoodLine.get_value();
+    pgood = pgoodState;
+    state = pgoodState;
+    log<level::INFO>(fmt::format("Pgood state: {}", pgoodState).c_str());
+}
+
 } // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/power_control.hpp b/phosphor-power-sequencer/src/power_control.hpp
index 248431a..4359074 100644
--- a/phosphor-power-sequencer/src/power_control.hpp
+++ b/phosphor-power-sequencer/src/power_control.hpp
@@ -2,9 +2,11 @@
 
 #include "power_interface.hpp"
 
+#include <gpiod.hpp>
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/message.hpp>
 #include <sdbusplus/server/object.hpp>
+#include <sdeventplus/clock.hpp>
 #include <sdeventplus/event.hpp>
 #include <sdeventplus/utility/timer.hpp>
 
@@ -59,23 +61,43 @@
     sdbusplus::bus::bus& bus;
 
     /**
+     * Indicates if a state transistion is taking place
+     */
+    bool inStateTransition{false};
+
+    /**
      * Power good
      */
     int pgood{0};
 
     /**
+     * GPIO line object for chassis power good
+     */
+    gpiod::line pgoodLine;
+
+    /**
      * Power good timeout constant
      */
     static constexpr std::chrono::seconds pgoodTimeout{
         std::chrono::seconds(10)};
 
     /**
+     * Point in time at which power good timeout will take place
+     */
+    std::chrono::time_point<std::chrono::steady_clock> pgoodTimeoutTime;
+
+    /**
      * Poll interval constant
      */
     static constexpr std::chrono::milliseconds pollInterval{
         std::chrono::milliseconds(3000)};
 
     /**
+     * GPIO line object for power-on / power-off control
+     */
+    gpiod::line powerControlLine;
+
+    /**
      * Power state
      */
     int state{0};
@@ -94,6 +116,11 @@
      * Polling method for monitoring the system power good
      */
     void pollPgood();
+
+    /**
+     * Set up GPIOs
+     */
+    void setUpGpio();
 };
 
 } // namespace phosphor::power::sequencer