Add I2C OCC support for P8 systems
P8 system uses I2C OCC and it uses different driver for occ-hwmon.
Add `--enable-i2c-occ` configure option to enable the support.
It searches i2c device names in sysfs to get all occ-hwmon devices and
use the i2c device name to bind/unbind the driver.
The occ control object path for I2C OCC hwmon becomes something like
/org/open_power/control/3_0050, where 3_0050 is the i2c address.
Change-Id: I8b9d8d4429c563528dc88fb2679b265c53d7a2d5
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/Makefile.am b/Makefile.am
index aecbebb..d71d6e5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,6 +18,7 @@
powercap.cpp \
org/open_power/OCC/Device/error.cpp \
occ_finder.cpp \
+ i2c_occ.cpp \
utils.cpp
BUILT_SOURCES = org/open_power/OCC/Device/error.hpp \
diff --git a/configure.ac b/configure.ac
index 254cc77..b121f55 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,17 @@
AX_CXX_COMPILE_STDCXX_14([noext])
AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+AC_ARG_ENABLE([i2c-occ],
+ AS_HELP_STRING([--enable-i2c-occ], [Enable I2C OCC support])
+)
+AS_IF([test "x$enable_i2c_occ" == "xyes"],
+ AC_MSG_NOTICE([Enabling I2C OCC])
+ [
+ cpp_flags="-DI2C_OCC"
+ ]
+ AC_SUBST([CPPFLAGS], [$cpp_flags])
+)
+
AC_ARG_VAR(OCC_CONTROL_BUSNAME, [The Dbus busname to own])
AS_IF([test "x$OCC_CONTROL_BUSNAME" == "x"], [OCC_CONTROL_BUSNAME="org.open_power.OCC.Control"])
AC_DEFINE_UNQUOTED([OCC_CONTROL_BUSNAME], ["$OCC_CONTROL_BUSNAME"], [The DBus busname to own])
@@ -86,12 +97,21 @@
AC_DEFINE_UNQUOTED([OCC_MASTER_NAME], ["$OCC_MASTER_NAME"], [The OCC master object name])
AC_ARG_VAR(OCC_HWMON_PATH, [The OCC hwmon path])
-AS_IF([test "x$OCC_HWMON_PATH" == "x"], [OCC_HWMON_PATH="/sys/bus/platform/drivers/occ-hwmon/"])
-AC_DEFINE_UNQUOTED([OCC_HWMON_PATH], ["$OCC_HWMON_PATH"], [The OCC hwmon path])
-
AC_ARG_VAR(DEV_PATH, [The device path])
-AS_IF([test "x$DEV_PATH" == "x"], [DEV_PATH="/sys/bus/platform/devices/"])
+AC_ARG_VAR(I2C_OCC_DEVICE_NAME, [The device name of i2c occ hwmon])
+AS_IF([test "x$enable_i2c_occ" == "xyes"],
+ # If enable_2c_occ is defined, define occ hwmon path for I2C and its driver's name
+ AS_IF([test "x$OCC_HWMON_PATH" == "x"], [OCC_HWMON_PATH="/sys/bus/i2c/drivers/occ-hwmon/"])
+ AS_IF([test "x$DEV_PATH" == "x"], [DEV_PATH="/sys/bus/i2c/devices"])
+ 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_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_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])
AC_ARG_VAR(FSI_SCAN_FILE, [The File to write for initiating FSI rescan])
AS_IF([test "x$FSI_SCAN_FILE" == "x"], [FSI_SCAN_FILE="/sys/devices/platform/gpio-fsi/fsi0/slave@00:00/00:00:00:0a/fsi1/rescan"])
diff --git a/i2c_occ.cpp b/i2c_occ.cpp
new file mode 100644
index 0000000..ffbb844
--- /dev/null
+++ b/i2c_occ.cpp
@@ -0,0 +1,73 @@
+#include <algorithm>
+#include <fstream>
+
+#include "config.h"
+#include "i2c_occ.hpp"
+
+#ifdef I2C_OCC
+
+namespace i2c_occ
+{
+
+namespace fs = std::experimental::filesystem;
+
+// The device name's length, e.g. "p8-occ-hwmon"
+constexpr auto DEVICE_NAME_LENGTH = 12;
+
+// static assert to make sure the i2c occ device name is expected
+static_assert(sizeof(I2C_OCC_DEVICE_NAME) -1 == DEVICE_NAME_LENGTH);
+
+std::string getFileContent(const fs::path& f)
+{
+ std::string ret(DEVICE_NAME_LENGTH, 0);
+ std::ifstream ifs(f.c_str(), std::ios::binary);
+ if (ifs.is_open())
+ {
+ ifs.read(&ret[0], DEVICE_NAME_LENGTH);
+ ret.resize(ifs.gcount());
+ }
+ return ret;
+}
+
+std::vector<std::string> getOccHwmonDevices(const char* path)
+{
+ std::vector<std::string> result{};
+
+ if (fs::is_directory(path))
+ {
+ for (auto & p : fs::directory_iterator(path))
+ {
+ // Check if a device's name is "p8-occ-hwmon"
+ auto f = p / "name";
+ auto str = getFileContent(f);
+ if (str == I2C_OCC_DEVICE_NAME)
+ {
+ result.emplace_back(p.path().filename());
+ }
+ }
+ std::sort(result.begin(), result.end());
+ }
+ return result;
+}
+
+void i2cToDbus(std::string& path)
+{
+ std::replace(path.begin(), path.end(), '-', '_');
+}
+
+void dbusToI2c(std::string& path)
+{
+ std::replace(path.begin(), path.end(), '_', '-');
+}
+
+std::string getI2cDeviceName(const std::string& dbusPath)
+{
+ auto name = fs::path(dbusPath).filename().string();
+ dbusToI2c(name);
+ return name;
+}
+
+} // namespace i2c_occ
+
+#endif
+
diff --git a/i2c_occ.hpp b/i2c_occ.hpp
new file mode 100644
index 0000000..31b6721
--- /dev/null
+++ b/i2c_occ.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <experimental/filesystem>
+#include <vector>
+
+#ifdef I2C_OCC
+
+namespace i2c_occ
+{
+
+namespace fs = std::experimental::filesystem;
+
+/** @brief Get file content
+ *
+ * Get at most NAME_LENGTH bytes of content from file. If the file is smaller
+ * than NAME_LENGTH bytes, return the valid parts.
+ *
+ * @param[in] f - The path of file
+ *
+ * @return The string of file content
+ */
+std::string getFileContent(const fs::path& f);
+
+/** @brief Find all devices of occ hwmon
+ *
+ * It iterates in path, finds all occ hwmon devices
+ *
+ * E.g. If "path/3-0050/name" exists and its content is "p8-occ-hwmon",
+ * "3-0050" is returned.
+ *
+ * @param[in] path - The path to search
+ *
+ * @return A vector of strings containing the occ hwmon device path
+ */
+std::vector<std::string> getOccHwmonDevices(const char* path);
+
+/** @brief Convert i2c name to DBus path
+ *
+ * It converts '-' to '_' so that it becomes a valid DBus path.
+ * E.g. 3-0050 converts to 3_0050
+ *
+ * @param[in,out] path - The i2c name to convert
+ */
+void i2cToDbus(std::string& name);
+
+/** @brief Convert DBus path to i2c name
+ *
+ * It converts '_' to '_' so that it becomes a valid i2c name
+ * E.g. 3_0050 converts to 3-0050
+ *
+ * @param[in,out] path - The DBus path to convert
+ */
+void dbusToI2c(std::string& path);
+
+/** @brief Get i2c name from full DBus path
+ *
+ * It extract the i2c name from the full DBus path.
+ * E.g. /org/open_power/control/3_0050 returns "3-0050"
+ *
+ * @param[in] dbusPath - The full DBus path
+ *
+ * @return The i2c name
+ */
+std::string getI2cDeviceName(const std::string& dbusPath);
+
+} // namespace i2c_occ
+
+#endif
+
diff --git a/occ_device.hpp b/occ_device.hpp
index 6538597..a2b4e85 100644
--- a/occ_device.hpp
+++ b/occ_device.hpp
@@ -5,6 +5,8 @@
#include "occ_events.hpp"
#include "occ_errors.hpp"
#include "config.h"
+
+
namespace open_power
{
namespace occ
@@ -34,7 +36,11 @@
Device(EventPtr& event,
const std::string& name,
std::function<void()> callBack = nullptr) :
+#ifdef I2C_OCC
+ config(name),
+#else
config(name + '-' + "dev0"),
+#endif
errorFile(fs::path(config) / "occ_error"),
error(event, errorFile, callBack)
{
diff --git a/occ_manager.hpp b/occ_manager.hpp
index e79ac3c..7bb1042 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -12,6 +12,7 @@
#include "occ_status.hpp"
#include "occ_finder.hpp"
#include "config.h"
+#include "i2c_occ.hpp"
namespace sdbusRule = sdbusplus::bus::match::rules;
namespace open_power
@@ -43,6 +44,10 @@
bus(bus),
event(event)
{
+#ifdef I2C_OCC
+ // I2C OCC status objects are initialized directly
+ initStatusObjects();
+#else
// Check if CPU inventory exists already.
auto occs = open_power::occ::finder::get(bus);
if (occs.empty())
@@ -67,6 +72,7 @@
createObjects(occ);
}
}
+#endif
}
private:
@@ -187,6 +193,31 @@
/** @brief Number of OCCs that are bound */
uint8_t activeCount = 0;
+
+#ifdef I2C_OCC
+ /** @brief Init Status objects for I2C OCC devices
+ *
+ * It iterates in /sys/bus/i2c/devices, finds all occ hwmon devices
+ * and creates status objects.
+ */
+ void initStatusObjects()
+ {
+ // Make sure we have a valid path string
+ static_assert(sizeof(DEV_PATH) != 0);
+
+ auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
+ for (auto& name : deviceNames)
+ {
+ i2c_occ::i2cToDbus(name);
+ auto path = fs::path(OCC_CONTROL_ROOT) / name;
+ statusObjects.emplace_back(
+ std::make_unique<Status>(
+ bus,
+ event,
+ path.c_str()));
+ }
+ }
+#endif
};
} // namespace occ
diff --git a/occ_status.hpp b/occ_status.hpp
index 2e12806..23b2080 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -7,6 +7,8 @@
#include <org/open_power/Control/Host/server.hpp>
#include "occ_events.hpp"
#include "occ_device.hpp"
+#include "i2c_occ.hpp"
+
namespace open_power
{
namespace occ
@@ -59,7 +61,11 @@
callBack(callBack),
instance(((this->path.back() - '0'))),
device(event,
+#ifdef I2C_OCC
+ i2c_occ::getI2cDeviceName(path),
+#else
name + std::to_string(instance + 1),
+#endif
std::bind(&Status::deviceErrorHandler, this)),
hostControlSignal(
bus,
diff --git a/test/Makefile.am b/test/Makefile.am
index d644de8..cd879f5 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -20,5 +20,6 @@
$(top_builddir)/occ_status.o \
$(top_builddir)/occ_device.o \
$(top_builddir)/occ_errors.o \
+ $(top_builddir)/i2c_occ.o \
$(top_builddir)/utils.o \
$(top_builddir)/org/open_power/OCC/Device/error.o