Add Bus class to reset OCC bus driver

For some systems, the OCC is accessed over the SBEFIFO, acting as a bus.
If the SBE experiences a failure, the SBEFIFO driver is unable to
recover. Therefore, the OCC control application  must force a reset of
the SBEFIFO driver when the OCC goes active.

Add a Bus class and reset method to unbind and bind the appropriate
SBEFIFO devices when the OCC devices are bound.

Testing: Powered on the system successfully. Injected SBE error through
a putscom. Without this fix, I intermittently saw SBEFIFO errors on the
BMC after the injection. With the fix, I saw no errors despite ~20
iterations.

Resolves openbmc/openbmc#3156

Change-Id: I0f9a230c57d0a3a7b59a874f62cdb1d93c6dcdfb
Signed-off-by: Eddie James <eajames@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index b883f94..fddf9e7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,7 @@
 	occ_pass_through.hpp \
 	occ_status.hpp \
 	powercap.hpp \
+        occ_bus.hpp \
 	occ_errors.hpp \
 	occ_events.hpp \
 	occ_finder.hpp \
@@ -21,6 +22,7 @@
 	occ_pass_through.cpp \
 	occ_manager.cpp \
 	occ_status.cpp \
+        occ_bus.cpp \
 	occ_device.cpp \
 	occ_errors.cpp \
 	occ_presence.cpp \
diff --git a/configure.ac b/configure.ac
index 149e0d0..7ac868c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,7 @@
 AS_IF([test "x$OCC_MASTER_NAME" == "x"], [OCC_MASTER_NAME="occ-hwmon.1"])
 AC_DEFINE_UNQUOTED([OCC_MASTER_NAME], ["$OCC_MASTER_NAME"], [The OCC master object name])
 
+AC_ARG_VAR(OCC_BUS_PATH, [The OCC bus device path])
 AC_ARG_VAR(OCC_HWMON_PATH, [The OCC hwmon path])
 AC_ARG_VAR(DEV_PATH, [The device path])
 AC_ARG_VAR(I2C_OCC_DEVICE_NAME, [The device name of i2c occ hwmon])
@@ -115,9 +116,11 @@
     AS_IF([test "x$I2C_OCC_DEVICE_NAME" == "x"], [I2C_OCC_DEVICE_NAME="p8-occ-hwmon"]),
 
     # Else, define occ hwmon path for FSI
+    AS_IF([test "x$OCC_BUS_PATH" == "x"], [OCC_BUS_PATH="/sys/bus/fsi/drivers/sbefifo/"])
     AS_IF([test "x$OCC_HWMON_PATH" == "x"], [OCC_HWMON_PATH="/sys/bus/platform/drivers/occ-hwmon/"])
     AS_IF([test "x$DEV_PATH" == "x"], [DEV_PATH="/sys/bus/platform/devices/"]),
 )
+AC_DEFINE_UNQUOTED([OCC_BUS_PATH], ["$OCC_BUS_PATH"], [The OCC bus device path])
 AC_DEFINE_UNQUOTED([OCC_HWMON_PATH], ["$OCC_HWMON_PATH"], [The OCC hwmon path])
 AC_DEFINE_UNQUOTED([DEV_PATH], ["$DEV_PATH"], [The device path])
 AC_DEFINE_UNQUOTED([I2C_OCC_DEVICE_NAME], ["$I2C_OCC_DEVICE_NAME"], [The device name of i2c occ hwmon])
diff --git a/occ_bus.cpp b/occ_bus.cpp
new file mode 100644
index 0000000..27be21b
--- /dev/null
+++ b/occ_bus.cpp
@@ -0,0 +1,21 @@
+#include "occ_device.hpp"
+#include "occ_bus.hpp"
+
+namespace open_power
+{
+namespace occ
+{
+
+fs::path Bus::bindPath = fs::path(OCC_BUS_PATH) / "bind";
+fs::path Bus::unBindPath = fs::path(OCC_BUS_PATH) / "unbind";
+
+void Bus::reset() const
+{
+#ifndef I2C_OCC
+    Device::write(unBindPath, config);
+    Device::write(bindPath, config);
+#endif
+}
+
+} // namespace occ
+} // namespace open_power
diff --git a/occ_bus.hpp b/occ_bus.hpp
new file mode 100644
index 0000000..bdb4fa4
--- /dev/null
+++ b/occ_bus.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <string>
+#include <experimental/filesystem>
+
+namespace open_power
+{
+namespace occ
+{
+
+namespace fs = std::experimental::filesystem;
+
+/** @class Bus
+ *  @brief Manages the bus on which the OCC device exists
+ */
+class Bus
+{
+    public:
+        Bus() = delete;
+        ~Bus() = default;
+        Bus(const Bus&) = delete;
+        Bus& operator=(const Bus&) = delete;
+        Bus(Bus&&) = default;
+        Bus& operator=(Bus&&) = default;
+
+        /** @brief Constructs the Bus object
+         *
+         *  @param[in] instance - OCC device index
+         */
+        explicit Bus(int instance) :
+            config("0" + std::to_string(instance) + ":0" +
+                std::to_string(instance) + ":00:06")
+        {
+            // Nothing to do here
+        }
+
+        void reset() const;
+
+    private:
+        /** @brief Config value to be used to do bind and unbind */
+        const std::string config;
+
+        /**  @brief To bind the device to the OCC's bus driver, do:
+         *
+         *    Write 0x:0x:00:06 to: /sys/bus/fsi/drivers/sbefifo/bind
+         */
+        static fs::path bindPath;
+
+        /**  @brief To un-bind the dvice from the OCC's bus driver, do:
+         *    Write 0x:0x:00:06 to: /sys/bus/fsi/drivers/sbefifo/unbind
+         */
+        static fs::path unBindPath;
+};
+
+} // namespace occ
+} // namespace open_power
diff --git a/occ_device.hpp b/occ_device.hpp
index efa46dc..85abde6 100644
--- a/occ_device.hpp
+++ b/occ_device.hpp
@@ -2,6 +2,7 @@
 
 #include <fstream>
 #include <experimental/filesystem>
+#include "occ_bus.hpp"
 #include "occ_events.hpp"
 #include "occ_errors.hpp"
 #include "occ_presence.hpp"
@@ -34,16 +35,20 @@
          *  @param[in] event    - Unique ptr reference to sd_event
          *  @param[in] name     - OCC instance name
          *  @param[in] manager  - OCC manager instance
+         *  @param[in] status   - OCC status instance
+         *  @param[in] instance - OCC device index
          *  @param[in] callback - Optional callback on errors
          */
         Device(EventPtr& event,
                const std::string& name,
                const Manager& manager,
                Status& status,
+               int instance,
                std::function<void(bool)> callBack = nullptr) :
             config(name),
             errorFile(fs::path(config) / "occ_error"),
             statusObject(status),
+            busObject(instance),
             error(event, errorFile, callBack),
             presence(event,
                      fs::path(config) / "occs_present",
@@ -74,6 +79,9 @@
         /** @brief Binds device to the OCC driver */
         inline void bind()
         {
+            // Reset this OCC's bus driver
+            busObject.reset();
+
             // Bind the device
             return write(bindPath, config);
         }
@@ -128,6 +136,21 @@
             }
         }
 
+        /** @brief file writer to achieve bind and unbind
+         *
+         *  @param[in] filename - Name of file to be written
+         *  @param[in] data     - Data to be written to
+         *  @return             - None
+         */
+        static void write(const fs::path& fileName, const std::string& data)
+        {
+            // If there is an error, move the exception all the way up
+            std::ofstream file(fileName, std::ios::out);
+            file << data;
+            file.close();
+            return;
+        }
+
     private:
         /** @brief Config value to be used to do bind and unbind */
         const std::string config;
@@ -149,6 +172,9 @@
         /**  Store the associated Status instance */
         Status& statusObject;
 
+        /**  Store the associated Bus instance */
+        const Bus busObject;
+
         /** Abstraction of error monitoring */
         Error error;
 
@@ -160,21 +186,6 @@
         Error throttleProcPower;
         Error throttleMemTemp;
 
-        /** @brief file writer to achieve bind and unbind
-         *
-         *  @param[in] filename - Name of file to be written
-         *  @param[in] data     - Data to be written to
-         *  @return             - None
-         */
-        void write(const fs::path& fileName, const std::string& data)
-        {
-            // If there is an error, move the exception all the way up
-            std::ofstream file(fileName, std::ios::out);
-            file << data;
-            file.close();
-            return;
-        }
-
         /** @brief Returns if device represents the master OCC */
         bool master() const;
 
diff --git a/occ_status.hpp b/occ_status.hpp
index f14bd7d..579f29d 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -74,6 +74,7 @@
 #endif
                      manager,
                      *this,
+                     instance,
                      std::bind(std::mem_fn(&Status::deviceErrorHandler), this,
                                std::placeholders::_1)),
               hostControlSignal(