Use gpioplus for specifying gpio gating
A GPIO can control whether a hwmon sensor is readable. This module
allows one to specify whether a sensor is gated and by what GPIO. This
is often the case for battery voltages, such that the battery isn't
drained constantly by being left open.
For each sensor where you need GPIO locking:
GPIOCHIP_in1=0
GPIO_in1=53
such that GPIOCHIP is the gpiochip: /sys/bus/gpio/devices/gpiochip{id}
such that GPIO is the line offset.
the value used to unlock the sensor via gpio is 1
after 1 is written to the gpio, it pauses for 500ms
Tested: Verified the failure case for invalid gpio fields. Verified
correct behavior on two platforms.
Change-Id: I2fa12848972075cad0e0f69c0bfa6382e15d4f50
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/Makefile.am b/Makefile.am
index 7e1cccb..c0a30ea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,11 +10,13 @@
-lstdc++fs \
$(SDBUSPLUS_LIBS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
- $(PHOSPHOR_LOGGING_LIBS)
+ $(PHOSPHOR_LOGGING_LIBS) \
+ $(GPIOPLUS_LIBS)
libhwmon_la_CXXFLAGS = \
$(SDBUSPLUS_CFLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
- $(PHOSPHOR_LOGGING_CFLAGS)
+ $(PHOSPHOR_LOGGING_CFLAGS) \
+ $(GPIOPLUS_CFLAGS)
libhwmon_la_SOURCES = \
argument.cpp \
@@ -27,6 +29,7 @@
timer.cpp \
hwmon.cpp \
hwmonio.cpp \
- sensor.cpp
+ sensor.cpp \
+ gpio_handle.cpp
SUBDIRS = . msl test tools
diff --git a/configure.ac b/configure.ac
index fea80cd..36d10ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,6 +16,7 @@
PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus], [], [AC_MSG_ERROR(["sdbusplus required and not found."])])
PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces], [], [AC_MSG_ERROR(["phosphor-dbus-interfaces required and not found."])])
PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging], [], [AC_MSG_ERROR(["phosphor-logging required and not found."])])
+PKG_CHECK_MODULES([GPIOPLUS], [gpioplus],, [AC_MSG_ERROR([Could not find gpioplus...openbmc/gpioplus package required])])
AX_PTHREAD([], [AC_MSG_ERROR(["pthread required and not found"])])
# Checks for typedefs, structures, and compiler characteristics.
diff --git a/gpio_handle.cpp b/gpio_handle.cpp
new file mode 100644
index 0000000..2a46ae2
--- /dev/null
+++ b/gpio_handle.cpp
@@ -0,0 +1,55 @@
+#include "gpio_handle.hpp"
+
+#include <cstdlib>
+#include <gpioplus/chip.hpp>
+#include <gpioplus/handle.hpp>
+#include <memory>
+#include <phosphor-logging/log.hpp>
+#include <string>
+
+namespace gpio
+{
+
+using namespace phosphor::logging;
+
+std::unique_ptr<gpioplus::Handle> BuildGpioHandle(const std::string& gpiochip,
+ const std::string& line)
+{
+ char *gpioEnd, *lineEnd;
+ unsigned long chipId = std::strtoul(gpiochip.c_str(), &gpioEnd, 10);
+ unsigned long lineOffset = std::strtoul(line.c_str(), &lineEnd, 10);
+
+ if (!gpioEnd || gpioEnd != &gpiochip.c_str()[gpiochip.length()])
+ {
+ log<level::ERR>("Unable to handle giochip entry",
+ entry("GPIOCHIP=%s", gpiochip.c_str()));
+ return nullptr;
+ }
+
+ if (!lineEnd || lineEnd != &line.c_str()[line.length()])
+ {
+ log<level::ERR>("Unable to handle line entry",
+ entry("LINE=%s", line.c_str()));
+ return nullptr;
+ }
+
+ try
+ {
+ gpioplus::Chip chip(chipId);
+ gpioplus::HandleFlags flags(chip.getLineInfo(lineOffset).flags);
+ flags.output = true;
+ std::vector<gpioplus::Handle::Line> lines = {
+ {static_cast<uint32_t>(lineOffset), 0}};
+
+ return std::make_unique<gpioplus::Handle>(chip, lines, flags,
+ "phosphor-hwmon");
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Unable to set up GPIO handle",
+ entry("ERROR=%s", e.what()));
+ return nullptr;
+ }
+}
+
+} // namespace gpio
diff --git a/gpio_handle.hpp b/gpio_handle.hpp
new file mode 100644
index 0000000..f7d4a5e
--- /dev/null
+++ b/gpio_handle.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <gpioplus/handle.hpp>
+#include <memory>
+#include <string>
+
+namespace gpio
+{
+
+/**
+ * Method called to validate inputs and create a GpioHandle.
+ *
+ * @param[in] gpiochip - gpiochip id as string, e.g. "0", or "1"
+ * @param[in] line - gpio line offset as string.
+ * @return A gpioplus::Handle on success nullptr on failure.
+ */
+std::unique_ptr<gpioplus::Handle> BuildGpioHandle(const std::string& gpiochip,
+ const std::string& line);
+
+} // namespace gpio
diff --git a/mainloop.cpp b/mainloop.cpp
index cf5baaf..096e1d4 100644
--- a/mainloop.cpp
+++ b/mainloop.cpp
@@ -387,11 +387,17 @@
// Retry for up to a second if device is busy
// or has a transient error.
+ std::unique_ptr<sensor::Sensor>& sensor =
+ sensorObjects[i.first];
+
+ sensor->unlockGpio();
value = ioAccess.read(i.first.first, i.first.second, input,
hwmonio::retries, hwmonio::delay);
- value = sensorObjects[i.first]->adjustValue(value);
+ sensor->lockGpio();
+
+ value = sensor->adjustValue(value);
for (auto& iface : obj)
{
diff --git a/sensor.cpp b/sensor.cpp
index bc5f34d..e68f7a7 100644
--- a/sensor.cpp
+++ b/sensor.cpp
@@ -3,6 +3,7 @@
#include "sensor.hpp"
#include "env.hpp"
+#include "gpio_handle.hpp"
#include "hwmon.hpp"
#include "sensorset.hpp"
#include "sysfs.hpp"
@@ -10,18 +11,34 @@
#include <cstring>
#include <experimental/filesystem>
#include <phosphor-logging/elog-errors.hpp>
+#include <thread>
+#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Sensor/Device/error.hpp>
namespace sensor
{
using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Sensor::Sensor(const SensorSet::key_type& sensor,
const hwmonio::HwmonIO& ioAccess, const std::string& devPath) :
sensor(sensor),
ioAccess(ioAccess), devPath(devPath)
{
+ auto chip = env::getEnv("GPIOCHIP", sensor);
+ auto access = env::getEnv("GPIO", sensor);
+ if (!access.empty() && !chip.empty())
+ {
+ handle = gpio::BuildGpioHandle(chip, access);
+
+ if (!handle)
+ {
+ log<level::ERR>("Unable to set up gpio locking");
+ elog<InternalFailure>();
+ }
+ }
+
auto gain = env::getEnv("GAIN", sensor);
if (!gain.empty())
{
@@ -110,11 +127,15 @@
// its status is functional, read the input value.
if (!statusIface || (statusIface && statusIface->functional()))
{
+ unlockGpio();
+
// Retry for up to a second if device is busy
// or has a transient error.
val = ioAccess.read(sensor.first, sensor.second, hwmon::entry::cinput,
std::get<size_t>(retryIO),
std::get<std::chrono::milliseconds>(retryIO));
+
+ lockGpio();
val = adjustValue(val);
}
@@ -199,4 +220,21 @@
return iface;
}
+void Sensor::unlockGpio()
+{
+ if (handle)
+ {
+ handle->setValues({1});
+ std::this_thread::sleep_for(pause);
+ }
+}
+
+void Sensor::lockGpio()
+{
+ if (handle)
+ {
+ handle->setValues({0});
+ }
+}
+
} // namespace sensor
diff --git a/sensor.hpp b/sensor.hpp
index 7005ea9..3f38c33 100644
--- a/sensor.hpp
+++ b/sensor.hpp
@@ -4,6 +4,8 @@
#include "sensorset.hpp"
#include "types.hpp"
+#include <chrono>
+#include <gpioplus/handle.hpp>
#include <unordered_set>
namespace sensor
@@ -101,6 +103,16 @@
*/
std::shared_ptr<StatusObject> addStatus(ObjectInfo& info);
+ /**
+ * @brief Unlock the gpio, set to high if relevant.
+ */
+ void unlockGpio();
+
+ /**
+ * @brief Lock the gpio, set to low if relevant.
+ */
+ void lockGpio();
+
private:
/** @brief Sensor object's identifiers */
SensorSet::key_type sensor;
@@ -113,6 +125,12 @@
/** @brief Structure for storing sensor adjustments */
valueAdjust sensorAdjusts;
+
+ /** @brief Optional pointer to GPIO handle. */
+ std::unique_ptr<gpioplus::Handle> handle;
+
+ /** @brief default pause after unlocking gpio. */
+ static constexpr std::chrono::milliseconds pause{500};
};
} // namespace sensor