pseq: Observe minimum power off time

In order to give hardware components an appropriate time to discharge
and prevent thrashing during brownouts, enforce a minimum power off
time. A 25 second minimum delay will be enforced after power off before
the next power on. A minimum of 15 seconds must pass after cold start
until a power on will start.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ib0cd0bcda1ca492548ee7912bab9a46181337098
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index 7f6ef9f..9ed3878 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -19,6 +19,7 @@
 #include "types.hpp"
 #include "ucd90320_monitor.hpp"
 
+#include <fmt/chrono.h>
 #include <fmt/format.h>
 
 #include <phosphor-logging/elog-errors.hpp>
@@ -49,6 +50,7 @@
                   "xyz.openbmc_project.EntityManager"),
           std::bind(&PowerControl::interfacesAddedHandler, this,
                     std::placeholders::_1)},
+    powerOnAllowedTime{std::chrono::steady_clock::now() + minimumColdStartTime},
     timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
 {
     // Obtain dbus service name
@@ -243,6 +245,21 @@
         // other BMC applications time to complete power off processing
         std::this_thread::sleep_for(std::chrono::seconds(2));
     }
+    else
+    {
+        // If minimum power off time has not passed, wait
+        if (powerOnAllowedTime > std::chrono::steady_clock::now())
+        {
+            log<level::INFO>(
+                fmt::format(
+                    "Waiting {} seconds until power on allowed",
+                    std::chrono::duration_cast<std::chrono::seconds>(
+                        powerOnAllowedTime - std::chrono::steady_clock::now())
+                        .count())
+                    .c_str());
+        }
+        std::this_thread::sleep_until(powerOnAllowedTime);
+    }
 
     log<level::INFO>(fmt::format("setState: {}", s).c_str());
     powerControlLine.request(
@@ -250,6 +267,13 @@
     powerControlLine.set_value(s);
     powerControlLine.release();
 
+    if (s == 0)
+    {
+        // Set a minimum amount of time to wait before next power on
+        powerOnAllowedTime =
+            std::chrono::steady_clock::now() + minimumPowerOffTime;
+    }
+
     pgoodTimeoutTime = std::chrono::steady_clock::now() + timeout;
     inStateTransition = true;
     state = s;
diff --git a/phosphor-power-sequencer/src/power_control.hpp b/phosphor-power-sequencer/src/power_control.hpp
index 560a5f9..0670876 100644
--- a/phosphor-power-sequencer/src/power_control.hpp
+++ b/phosphor-power-sequencer/src/power_control.hpp
@@ -98,6 +98,16 @@
     sdbusplus::bus::match_t match;
 
     /**
+     * Minimum time from cold start to power on constant
+     */
+    static constexpr std::chrono::seconds minimumColdStartTime{15};
+
+    /**
+     * Minimum time from power off to power on constant
+     */
+    static constexpr std::chrono::seconds minimumPowerOffTime{25};
+
+    /**
      * Power good
      */
     int pgood{0};
@@ -110,8 +120,7 @@
     /**
      * Power good timeout constant
      */
-    static constexpr std::chrono::seconds pgoodTimeout{
-        std::chrono::seconds(10)};
+    static constexpr std::chrono::seconds pgoodTimeout{10};
 
     /**
      * Point in time at which power good timeout will take place
@@ -121,8 +130,7 @@
     /**
      * Poll interval constant
      */
-    static constexpr std::chrono::milliseconds pollInterval{
-        std::chrono::milliseconds(3000)};
+    static constexpr std::chrono::milliseconds pollInterval{3000};
 
     /**
      * GPIO line object for power-on / power-off control
@@ -130,6 +138,11 @@
     gpiod::line powerControlLine;
 
     /**
+     * Point in time at which minumum power off time will have passed
+     */
+    std::chrono::time_point<std::chrono::steady_clock> powerOnAllowedTime;
+
+    /**
      * Power supply error.  Cleared at power on.
      */
     std::string powerSupplyError;