Add PGOODMonitor class

This class checks that PGOOD comes on in the amount of
time specified.  If it doesn't, it will create an error log.

Future commits will analyze the power sequencer chip for failures
in this case so a better callout can be done.

Change-Id: Ia3679e5a7d36103f908b70aa0301cd012b0e7b20
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/power-sequencer/Makefile.am b/power-sequencer/Makefile.am
index 2aed038..27fc0d0 100644
--- a/power-sequencer/Makefile.am
+++ b/power-sequencer/Makefile.am
@@ -6,7 +6,8 @@
 
 witherspoon_pseq_monitor_SOURCES = \
 	argument.cpp \
-	main.cpp
+	main.cpp \
+	pgood_monitor.cpp
 
 witherspoon_pseq_monitor_LDADD = \
 	$(top_builddir)/libpower.la \
diff --git a/power-sequencer/pgood_monitor.cpp b/power-sequencer/pgood_monitor.cpp
new file mode 100644
index 0000000..c4c88ca
--- /dev/null
+++ b/power-sequencer/pgood_monitor.cpp
@@ -0,0 +1,153 @@
+/**
+ * Copyright © 2017 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Power/Fault/error.hpp>
+#include "elog-errors.hpp"
+#include "pgood_monitor.hpp"
+#include "utility.hpp"
+
+namespace witherspoon
+{
+namespace power
+{
+
+constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
+constexpr auto POWER_INTERFACE = "org.openbmc.control.Power";
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Power::Fault::Error;
+
+bool PGOODMonitor::pgoodPending()
+{
+    bool pending = false;
+    int32_t state = 0;
+    int32_t pgood = 0;
+
+    auto service = util::getService(POWER_OBJ_PATH,
+            POWER_INTERFACE,
+            bus);
+
+    util::getProperty<int32_t>(POWER_INTERFACE,
+            "pgood",
+            POWER_OBJ_PATH,
+            service,
+            bus,
+            pgood);
+
+    //When state = 1, system was switched on
+    util::getProperty<int32_t>(POWER_INTERFACE,
+            "state",
+            POWER_OBJ_PATH,
+            service,
+            bus,
+            state);
+
+    //On but no PGOOD
+    if (state && !pgood)
+    {
+        pending = true;
+    }
+
+    return pending;
+}
+
+
+void PGOODMonitor::exitEventLoop()
+{
+    auto r = sd_event_exit(event.get(), EXIT_SUCCESS);
+    if (r < 0)
+    {
+        log<level::ERR>("sd_event_exit failed",
+                entry("RC = %d", r));
+    }
+}
+
+void PGOODMonitor::analyze()
+{
+    //Timer callback.
+    //The timer expired before it was stopped.
+    //If PGOOD is still pending (it should be),
+    //then there is a real failure.
+
+    if (pgoodPending())
+    {
+        report<PowerOnFailure>();
+    }
+
+    //The pgood-wait service (with a longer timeout)
+    //will handle powering off the system.
+
+    exitEventLoop();
+}
+
+void PGOODMonitor::propertyChanged()
+{
+    //Multiple properties could have changed here.
+    //Keep things simple and just recheck the important ones.
+    if (!pgoodPending())
+    {
+        //PGOOD is on, or system is off, so we are done.
+        timer.stop();
+        exitEventLoop();
+    }
+}
+
+void PGOODMonitor::startListening()
+{
+    match = std::make_unique<sdbusplus::bus::match_t>(
+            bus,
+            sdbusplus::bus::match::rules::propertiesChanged(
+                POWER_OBJ_PATH,
+                POWER_INTERFACE),
+            [this](auto& msg){this->propertyChanged();});
+}
+
+int PGOODMonitor::run()
+{
+    try
+    {
+        startListening();
+
+        //If PGOOD came up before we got here, we're done.
+        //Otherwise if PGOOD doesn't get asserted before
+        //the timer expires, it's a failure.
+        if (!pgoodPending())
+        {
+            return EXIT_SUCCESS;
+        }
+
+        timer.start(interval);
+
+        auto r = sd_event_loop(event.get());
+        if (r < 0)
+        {
+            log<level::ERR>("sd_event_loop() failed",
+                    entry("ERROR=%d", r));
+        }
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        log<level::ERR>("Unexpected failure prevented PGOOD checking");
+    }
+
+    //Letting the service fail won't help anything, so don't do it.
+    return EXIT_SUCCESS;
+}
+
+
+}
+}
diff --git a/power-sequencer/pgood_monitor.hpp b/power-sequencer/pgood_monitor.hpp
new file mode 100644
index 0000000..611cac6
--- /dev/null
+++ b/power-sequencer/pgood_monitor.hpp
@@ -0,0 +1,121 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
+#include "event.hpp"
+#include "timer.hpp"
+
+namespace witherspoon
+{
+namespace power
+{
+
+/**
+ * @class PGOODMonitor
+ *
+ * Monitors PGOOD and creates an error if it doesn't come on in time.
+ *
+ * The run() function is designed to be called right after the
+ * power sequencer device is told to kick off a power on.
+ *
+ * Future commits will analyze the power sequencer chip for errors
+ * on a PGOOD fail.
+ */
+class PGOODMonitor
+{
+    public:
+
+        PGOODMonitor() = delete;
+        ~PGOODMonitor() = default;
+        PGOODMonitor(const PGOODMonitor&) = delete;
+        PGOODMonitor& operator=(const PGOODMonitor&) = delete;
+        PGOODMonitor(PGOODMonitor&&) = delete;
+        PGOODMonitor& operator=(PGOODMonitor&&) = delete;
+
+        /**
+         * Constructor
+         *
+         * @param[in] b - D-Bus bus object
+         * @param[in] e - event object
+         * @param[in] t - time to allow PGOOD to come up
+         */
+        PGOODMonitor(sdbusplus::bus::bus& b,
+                event::Event& e,
+                std::chrono::seconds& t) :
+            bus(b),
+            event(e),
+            interval(t),
+            timer(e, [this]() { this->analyze(); })
+            {
+            }
+
+        /**
+         * The timer callback.
+         *
+         * Creates a PGOOD failure error log.
+         */
+        void analyze();
+
+        /**
+         * Waits a specified amount of time for PGOOD to
+         * come on, and if it fails to come on in that time
+         * an error log will be created.
+         *
+         * @return - the return value from sd_event_loop()
+         */
+        int run();
+
+    private:
+
+        /**
+         * Enables the properties changed signal callback
+         * on the power object so we can tell when PGOOD
+         * comes on.
+         */
+        void startListening();
+
+        /**
+         * The callback function for the properties changed
+         * signal.
+         */
+        void propertyChanged();
+
+        /**
+         * Returns true if the system has been turned on
+         * but PGOOD isn't up yet.
+         */
+        bool pgoodPending();
+
+        /**
+         * Used to break out of the event loop in run()
+         */
+        void exitEventLoop();
+
+        /**
+         * The D-Bus object
+         */
+        sdbusplus::bus::bus& bus;
+
+        /**
+         * The match object for the properties changed signal
+         */
+        std::unique_ptr<sdbusplus::bus::match_t> match;
+
+        /**
+         * The sd_event structure used by the timer
+         */
+        event::Event& event;
+
+        /**
+         * The amount of time to wait for PGOOD to turn on
+         */
+        std::chrono::seconds interval;
+
+        /**
+         * The timer used to do the callback
+         */
+        Timer timer;
+};
+
+}
+}