Support temp sensor on MCU which is available from I2C
Chassis MCU exposes register to report temperature for the peripheral board.
The MCU access is configurable as below:
{
"Bus": $bus,
"Address": "0xAA",
"Reg": "0xBB",
"Class": "MCUTemp",
"Name": "MCU Temp",
"Thresholds": [
xxx
]
"Type": "MCUTempSensor"
}
Tested:
The below interface was found under temperature sensor namespace:
└─/xyz/openbmc_project/sensors/temperature/MCU_Temp
Run ipmitool sensor and check below from list:
MCU Temp | 30.175 | degrees C | cr | na | 0.000 | 5.000 | 110.000 | 115.000 | na
Change-Id: I8d54455ccc39ea4f60a5b4aee5c68be092d39a72
Signed-off-by: Yuan Li <yuan.li@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8279694..af1a41c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@
option (DISABLE_HWMON_TEMP "Disable installing hwmon temp sensor" OFF)
option (DISABLE_INTRUSION "Disable installing intrusion sensor" OFF)
option (DISABLE_IPMB "Disable installing IPMB sensor" OFF)
+option (DISABLE_MCUTEMP "Disable installing MCU temperature sensor" OFF)
option (DISABLE_PSU "Disable installing PSU sensor" OFF)
include ("cmake/HunterGate.cmake")
@@ -42,6 +43,8 @@
set (IPMB_SRC_FILES src/Utils.cpp src/Thresholds.cpp)
+set (MCUTEMP_SRC_FILES src/Utils.cpp src/Thresholds.cpp)
+
set (PSU_SRC_FILES src/Utils.cpp src/PSUSensor.cpp src/Thresholds.cpp
src/PwmSensor.cpp src/PSUEvent.cpp)
@@ -151,6 +154,11 @@
add_dependencies (ipmbsensor sdbusplus-project)
target_link_libraries (ipmbsensor ${SENSOR_LINK_LIBS})
+add_executable (mcutempsensor src/MCUTempSensor.cpp ${MCUTEMP_SRC_FILES})
+add_dependencies (mcutempsensor sdbusplus-project)
+target_link_libraries (mcutempsensor ${SENSOR_LINK_LIBS})
+target_link_libraries (mcutempsensor i2c)
+
add_executable (psusensor src/PSUSensorMain.cpp ${PSU_SRC_FILES})
add_dependencies (psusensor sdbusplus-project)
target_link_libraries (psusensor ${SENSOR_LINK_LIBS})
@@ -163,6 +171,7 @@
add_dependencies (hwmontempsensor ${EXTERNAL_PACKAGES})
add_dependencies (intrusionsensor ${EXTERNAL_PACKAGES})
add_dependencies (ipmbsensor ${EXTERNAL_PACKAGES})
+ add_dependencies (mcutempsensor ${EXTERNAL_PACKAGES})
add_dependencies (psusensor ${EXTERNAL_PACKAGES})
endif ()
@@ -223,6 +232,13 @@
DESTINATION ${SERVICE_FILE_INSTALL_DIR})
endif ()
+if (NOT DISABLE_MCUTEMP)
+ install (TARGETS mcutempsensor DESTINATION bin)
+ install (FILES
+ ${SERVICE_FILE_SRC_DIR}/xyz.openbmc_project.mcutempsensor.service
+ DESTINATION ${SERVICE_FILE_INSTALL_DIR})
+endif ()
+
if (NOT DISABLE_PSU)
install (TARGETS psusensor DESTINATION bin)
install (FILES
diff --git a/include/MCUTempSensor.hpp b/include/MCUTempSensor.hpp
new file mode 100644
index 0000000..5dd3c03
--- /dev/null
+++ b/include/MCUTempSensor.hpp
@@ -0,0 +1,33 @@
+#pragma once
+#include "sensor.hpp"
+
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/container/flat_map.hpp>
+#include <chrono>
+#include <limits>
+#include <vector>
+
+struct MCUTempSensor : public Sensor
+{
+ MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io, const std::string& name,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholds,
+ uint8_t busId, uint8_t mcuAddress, uint8_t tempReg);
+ ~MCUTempSensor();
+
+ void checkThresholds(void) override;
+ void read(void);
+ void init(void);
+
+ uint8_t busId;
+ uint8_t mcuAddress;
+ uint8_t tempReg;
+
+ private:
+ int getMCURegsInfoWord(uint8_t regs, int16_t* pu16data);
+ sdbusplus::asio::object_server& objectServer;
+ std::shared_ptr<sdbusplus::asio::connection> dbusConnection;
+ boost::asio::deadline_timer waitTimer;
+};
diff --git a/service_files/xyz.openbmc_project.mcutempsensor.service b/service_files/xyz.openbmc_project.mcutempsensor.service
new file mode 100644
index 0000000..33a98f2
--- /dev/null
+++ b/service_files/xyz.openbmc_project.mcutempsensor.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=MCU Temp Sensor
+StopWhenUnneeded=false
+
+[Service]
+Restart=always
+RestartSec=5
+ExecStart=/usr/bin/mcutempsensor
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/src/MCUTempSensor.cpp b/src/MCUTempSensor.cpp
new file mode 100644
index 0000000..fd45327
--- /dev/null
+++ b/src/MCUTempSensor.cpp
@@ -0,0 +1,312 @@
+/*
+// Copyright (c) 2019 Intel 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 "MCUTempSensor.hpp"
+
+#include "Utils.hpp"
+#include "VariantVisitors.hpp"
+
+#include <math.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <chrono>
+#include <iostream>
+#include <limits>
+#include <numeric>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <vector>
+
+extern "C" {
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+}
+
+constexpr const bool debug = false;
+
+constexpr const char* configInterface =
+ "xyz.openbmc_project.Configuration.MCUTempSensor";
+static constexpr double mcuTempMaxReading = 0xFF;
+static constexpr double mcuTempMinReading = 0;
+
+boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
+
+MCUTempSensor::MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ boost::asio::io_service& io,
+ const std::string& sensorName,
+ const std::string& sensorConfiguration,
+ sdbusplus::asio::object_server& objectServer,
+ std::vector<thresholds::Threshold>&& thresholdData,
+ uint8_t busId, uint8_t mcuAddress,
+ uint8_t tempReg) :
+ Sensor(boost::replace_all_copy(sensorName, " ", "_"),
+ std::move(thresholdData), sensorConfiguration,
+ "xyz.openbmc_project.Configuration.ExitAirTemp", mcuTempMaxReading,
+ mcuTempMinReading),
+ objectServer(objectServer), dbusConnection(conn), waitTimer(io),
+ busId(busId), mcuAddress(mcuAddress), tempReg(tempReg)
+{
+ sensorInterface = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Value");
+
+ if (thresholds::hasWarningInterface(thresholds))
+ {
+ thresholdInterfaceWarning = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Warning");
+ }
+ if (thresholds::hasCriticalInterface(thresholds))
+ {
+ thresholdInterfaceCritical = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "xyz.openbmc_project.Sensor.Threshold.Critical");
+ }
+ association = objectServer.add_interface(
+ "/xyz/openbmc_project/sensors/temperature/" + name,
+ "org.openbmc.Associations");
+}
+
+MCUTempSensor::~MCUTempSensor()
+{
+ waitTimer.cancel();
+ objectServer.remove_interface(thresholdInterfaceWarning);
+ objectServer.remove_interface(thresholdInterfaceCritical);
+ objectServer.remove_interface(sensorInterface);
+ objectServer.remove_interface(association);
+}
+
+void MCUTempSensor::init(void)
+{
+ setInitialProperties(dbusConnection);
+ read();
+}
+
+void MCUTempSensor::checkThresholds(void)
+{
+ thresholds::checkThresholds(this);
+}
+
+int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int16_t* pu16data)
+{
+ std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
+ int fd = open(i2cBus.c_str(), O_RDWR);
+ size_t i = 0;
+
+ if (fd < 0)
+ {
+ std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
+ << "\n";
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
+ {
+ std::cerr << " unable to set device address\n";
+ close(fd);
+ return -1;
+ }
+
+ unsigned long funcs = 0;
+ if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
+ {
+ std::cerr << " not support I2C_FUNCS\n";
+ close(fd);
+ return -1;
+ }
+
+ if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
+ {
+ std::cerr << " not support I2C_FUNC_SMBUS_READ_WORD_DATA\n";
+ close(fd);
+ return -1;
+ }
+
+ *pu16data = i2c_smbus_read_word_data(fd, regs);
+ close(fd);
+
+ if (*pu16data < 0)
+ {
+ std::cerr << " read word data failed at " << static_cast<int>(regs)
+ << "\n";
+ return -1;
+ }
+
+ return 0;
+}
+
+void MCUTempSensor::read(void)
+{
+ static constexpr size_t pollTime = 1; // in seconds
+
+ waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
+ waitTimer.async_wait([this](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being cancelled
+ }
+ // read timer error
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ int16_t temp;
+ int ret = getMCURegsInfoWord(tempReg, &temp);
+ if (ret >= 0)
+ {
+ double v = static_cast<double>(temp) / 1000;
+ if constexpr (debug)
+ {
+ std::cerr << "Value update to " << (double)v << "raw reading "
+ << static_cast<int>(temp) << "\n";
+ }
+ updateValue(v);
+ }
+ else
+ {
+ std::cerr << "Invalid read getMCURegsInfoWord\n";
+ updateValue(-1);
+ }
+ read();
+ });
+}
+
+void createSensors(
+ boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
+ boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
+ sensors,
+ std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+ if (!dbusConnection)
+ {
+ std::cerr << "Connection not created\n";
+ return;
+ }
+
+ dbusConnection->async_method_call(
+ [&io, &objectServer, &dbusConnection, &sensors](
+ boost::system::error_code ec, const ManagedObjectType& resp) {
+ if (ec)
+ {
+ std::cerr << "Error contacting entity manager\n";
+ return;
+ }
+ for (const auto& pathPair : resp)
+ {
+ for (const auto& entry : pathPair.second)
+ {
+ if (entry.first != configInterface)
+ {
+ continue;
+ }
+ std::string name =
+ loadVariant<std::string>(entry.second, "Name");
+
+ std::vector<thresholds::Threshold> sensorThresholds;
+ if (!parseThresholdsFromConfig(pathPair.second,
+ sensorThresholds))
+ {
+ std::cerr << "error populating thresholds for " << name
+ << "\n";
+ }
+
+ uint8_t busId = loadVariant<uint8_t>(entry.second, "Bus");
+
+ uint8_t mcuAddress =
+ loadVariant<uint8_t>(entry.second, "Address");
+
+ uint8_t tempReg = loadVariant<uint8_t>(entry.second, "Reg");
+
+ std::string sensorClass =
+ loadVariant<std::string>(entry.second, "Class");
+
+ if constexpr (debug)
+ {
+ std::cerr
+ << "Configuration parsed for \n\t" << entry.first
+ << "\n"
+ << "with\n"
+ << "\tName: " << name << "\n"
+ << "\tBus: " << static_cast<int>(busId) << "\n"
+ << "\tAddress: " << static_cast<int>(mcuAddress)
+ << "\n"
+ << "\tReg: " << static_cast<int>(tempReg) << "\n"
+ << "\tClass: " << sensorClass << "\n";
+ }
+
+ auto& sensor = sensors[name];
+
+ sensor = std::make_unique<MCUTempSensor>(
+ dbusConnection, io, name, pathPair.first, objectServer,
+ std::move(sensorThresholds), busId, mcuAddress,
+ tempReg);
+
+ sensor->init();
+ }
+ }
+ },
+ entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+}
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+ systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
+ sdbusplus::asio::object_server objectServer(systemBus);
+
+ io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
+
+ boost::asio::deadline_timer configTimer(io);
+
+ std::function<void(sdbusplus::message::message&)> eventHandler =
+ [&](sdbusplus::message::message& message) {
+ configTimer.expires_from_now(boost::posix_time::seconds(1));
+ // create a timer because normally multiple properties change
+ configTimer.async_wait([&](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ return; // we're being canceled
+ }
+ // config timer error
+ else if (ec)
+ {
+ std::cerr << "timer error\n";
+ return;
+ }
+ createSensors(io, objectServer, sensors, systemBus);
+ if (sensors.empty())
+ {
+ std::cout << "Configuration not detected\n";
+ }
+ });
+ };
+
+ sdbusplus::bus::match::match configMatch(
+ static_cast<sdbusplus::bus::bus&>(*systemBus),
+ "type='signal',member='PropertiesChanged',"
+ "path_namespace='" +
+ std::string(inventoryPath) +
+ "',"
+ "arg0namespace='" +
+ configInterface + "'",
+ eventHandler);
+
+ io.run();
+}