blob: 16eeb9e2b165b32642b0807acf5b36d13ff86ae7 [file] [log] [blame]
Yuan Li445efe32019-06-14 22:58:32 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103016
17#include "MCUTempSensor.hpp"
18
Ed Tanouseacbfdd2024-04-04 12:00:24 -070019#include "SensorPaths.hpp"
20#include "Thresholds.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103021#include "Utils.hpp"
Ed Tanouseacbfdd2024-04-04 12:00:24 -070022#include "sensor.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103023
Ed Tanouseacbfdd2024-04-04 12:00:24 -070024#include <fcntl.h>
25#include <linux/i2c.h>
26#include <sys/ioctl.h>
27#include <unistd.h>
28
29#include <boost/asio/error.hpp>
30#include <boost/asio/io_context.hpp>
31#include <boost/asio/post.hpp>
32#include <boost/asio/steady_timer.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070033#include <boost/container/flat_map.hpp>
George Liuf2a2baa2025-02-20 17:32:51 +080034#include <phosphor-logging/lg2.hpp>
James Feist38fb5982020-05-28 10:09:54 -070035#include <sdbusplus/asio/connection.hpp>
36#include <sdbusplus/asio/object_server.hpp>
37#include <sdbusplus/bus/match.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070038#include <sdbusplus/message.hpp>
James Feist38fb5982020-05-28 10:09:54 -070039
Ed Tanouseacbfdd2024-04-04 12:00:24 -070040#include <array>
Yuan Li445efe32019-06-14 22:58:32 +080041#include <chrono>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070042#include <cstddef>
43#include <cstdint>
Patrick Venture96e97db2019-10-31 13:44:38 -070044#include <functional>
Patrick Venture96e97db2019-10-31 13:44:38 -070045#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070046#include <string>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070047#include <utility>
Yuan Li445efe32019-06-14 22:58:32 +080048#include <vector>
49
James Feist38fb5982020-05-28 10:09:54 -070050extern "C"
51{
Yuan Li445efe32019-06-14 22:58:32 +080052#include <i2c/smbus.h>
53#include <linux/i2c-dev.h>
54}
55
56constexpr const bool debug = false;
57
Zev Weiss054aad82022-08-18 01:37:34 -070058constexpr const char* sensorType = "MCUTempSensor";
Yuan Li445efe32019-06-14 22:58:32 +080059static constexpr double mcuTempMaxReading = 0xFF;
60static constexpr double mcuTempMinReading = 0;
61
62boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
63
Patrick Williams2aaf7172024-08-16 15:20:40 -040064MCUTempSensor::MCUTempSensor(
65 std::shared_ptr<sdbusplus::asio::connection>& conn,
66 boost::asio::io_context& io, const std::string& sensorName,
67 const std::string& sensorConfiguration,
68 sdbusplus::asio::object_server& objectServer,
69 std::vector<thresholds::Threshold>&& thresholdData, uint8_t busId,
70 uint8_t mcuAddress, uint8_t tempReg) :
Zhikui Renda98f092021-11-01 09:41:08 -070071 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -070072 sensorConfiguration, "MCUTempSensor", false, false,
Zev Weiss34d7b972022-08-17 19:38:59 -070073 mcuTempMaxReading, mcuTempMinReading, conn),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050074 busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
James Feiste3338522020-09-15 15:40:30 -070075 objectServer(objectServer), waitTimer(io)
Yuan Li445efe32019-06-14 22:58:32 +080076{
77 sensorInterface = objectServer.add_interface(
78 "/xyz/openbmc_project/sensors/temperature/" + name,
79 "xyz.openbmc_project.Sensor.Value");
80
Jayashree Dhanapal56678082022-01-04 17:27:20 +053081 for (const auto& threshold : thresholds)
Yuan Li445efe32019-06-14 22:58:32 +080082 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053083 std::string interface = thresholds::getInterface(threshold.level);
84 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
85 objectServer.add_interface(
86 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
Yuan Li445efe32019-06-14 22:58:32 +080087 }
88 association = objectServer.add_interface(
89 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070090 association::interface);
Yuan Li445efe32019-06-14 22:58:32 +080091}
92
93MCUTempSensor::~MCUTempSensor()
94{
95 waitTimer.cancel();
Jayashree Dhanapal56678082022-01-04 17:27:20 +053096 for (const auto& iface : thresholdInterfaces)
97 {
98 objectServer.remove_interface(iface);
99 }
Yuan Li445efe32019-06-14 22:58:32 +0800100 objectServer.remove_interface(sensorInterface);
101 objectServer.remove_interface(association);
102}
103
Ed Tanous201a1012024-04-03 18:07:28 -0700104void MCUTempSensor::init()
Yuan Li445efe32019-06-14 22:58:32 +0800105{
Andrei Kartashev39287412022-02-04 16:04:47 +0300106 setInitialProperties(sensor_paths::unitDegreesC);
Yuan Li445efe32019-06-14 22:58:32 +0800107 read();
108}
109
Ed Tanous201a1012024-04-03 18:07:28 -0700110void MCUTempSensor::checkThresholds()
Yuan Li445efe32019-06-14 22:58:32 +0800111{
112 thresholds::checkThresholds(this);
113}
114
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000115int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int32_t* pu32data) const
Yuan Li445efe32019-06-14 22:58:32 +0800116{
117 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
Yuan Li445efe32019-06-14 22:58:32 +0800118
Ed Tanous99c44092022-01-14 09:59:09 -0800119 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
120 int fd = open(i2cBus.c_str(), O_RDWR);
Yuan Li445efe32019-06-14 22:58:32 +0800121 if (fd < 0)
122 {
George Liuf2a2baa2025-02-20 17:32:51 +0800123 lg2::error("unable to open i2c device '{BUS}' err = '{ERR}'", "BUS",
124 i2cBus, "ERR", fd);
Yuan Li445efe32019-06-14 22:58:32 +0800125 return -1;
126 }
127
Ed Tanous99c44092022-01-14 09:59:09 -0800128 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800129 if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
130 {
George Liuf2a2baa2025-02-20 17:32:51 +0800131 lg2::error("unable to set device address");
Yuan Li445efe32019-06-14 22:58:32 +0800132 close(fd);
133 return -1;
134 }
135
136 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800137 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800138 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
139 {
George Liuf2a2baa2025-02-20 17:32:51 +0800140 lg2::error("not support I2C_FUNCS");
Yuan Li445efe32019-06-14 22:58:32 +0800141 close(fd);
142 return -1;
143 }
144
Ed Tanous2049bd22022-07-09 07:20:26 -0700145 if ((funcs & I2C_FUNC_SMBUS_READ_WORD_DATA) == 0U)
Yuan Li445efe32019-06-14 22:58:32 +0800146 {
George Liuf2a2baa2025-02-20 17:32:51 +0800147 lg2::error("not support I2C_FUNC_SMBUS_READ_WORD_DATA");
Yuan Li445efe32019-06-14 22:58:32 +0800148 close(fd);
149 return -1;
150 }
151
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000152 *pu32data = i2c_smbus_read_word_data(fd, regs);
Yuan Li445efe32019-06-14 22:58:32 +0800153 close(fd);
154
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000155 if (*pu32data < 0)
Yuan Li445efe32019-06-14 22:58:32 +0800156 {
George Liuf2a2baa2025-02-20 17:32:51 +0800157 lg2::error(" read word data failed at '{REGS}'", "REGS", regs);
Yuan Li445efe32019-06-14 22:58:32 +0800158 return -1;
159 }
160
161 return 0;
162}
163
Ed Tanous201a1012024-04-03 18:07:28 -0700164void MCUTempSensor::read()
Yuan Li445efe32019-06-14 22:58:32 +0800165{
166 static constexpr size_t pollTime = 1; // in seconds
167
Ed Tanous83db50c2023-03-01 10:20:24 -0800168 waitTimer.expires_after(std::chrono::seconds(pollTime));
Yuan Li445efe32019-06-14 22:58:32 +0800169 waitTimer.async_wait([this](const boost::system::error_code& ec) {
170 if (ec == boost::asio::error::operation_aborted)
171 {
172 return; // we're being cancelled
173 }
174 // read timer error
Ed Tanous8a57ec02020-10-09 12:46:52 -0700175 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800176 {
George Liuf2a2baa2025-02-20 17:32:51 +0800177 lg2::error("timer error");
Yuan Li445efe32019-06-14 22:58:32 +0800178 return;
179 }
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000180 int32_t temp = 0;
Yuan Li445efe32019-06-14 22:58:32 +0800181 int ret = getMCURegsInfoWord(tempReg, &temp);
182 if (ret >= 0)
183 {
184 double v = static_cast<double>(temp) / 1000;
185 if constexpr (debug)
186 {
George Liuf2a2baa2025-02-20 17:32:51 +0800187 lg2::error("Value update to '{VALUE}' raw reading '{RAW}'",
188 "VALUE", v, "RAW", temp);
Yuan Li445efe32019-06-14 22:58:32 +0800189 }
190 updateValue(v);
191 }
192 else
193 {
George Liuf2a2baa2025-02-20 17:32:51 +0800194 lg2::error("Invalid read getMCURegsInfoWord");
James Feist961bf092020-07-01 16:38:12 -0700195 incrementError();
Yuan Li445efe32019-06-14 22:58:32 +0800196 }
197 read();
198 });
199}
200
201void createSensors(
Ed Tanous1f978632023-02-28 18:16:39 -0800202 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
Yuan Li445efe32019-06-14 22:58:32 +0800203 boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
204 sensors,
205 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
206{
207 if (!dbusConnection)
208 {
George Liuf2a2baa2025-02-20 17:32:51 +0800209 lg2::error("Connection not created");
Yuan Li445efe32019-06-14 22:58:32 +0800210 return;
211 }
212
213 dbusConnection->async_method_call(
214 [&io, &objectServer, &dbusConnection, &sensors](
215 boost::system::error_code ec, const ManagedObjectType& resp) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400216 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800217 {
George Liuf2a2baa2025-02-20 17:32:51 +0800218 lg2::error("Error contacting entity manager");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400219 return;
Yuan Li445efe32019-06-14 22:58:32 +0800220 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400221 for (const auto& [path, interfaces] : resp)
222 {
223 for (const auto& [intf, cfg] : interfaces)
224 {
225 if (intf != configInterfaceName(sensorType))
226 {
227 continue;
228 }
229 std::string name = loadVariant<std::string>(cfg, "Name");
230
231 std::vector<thresholds::Threshold> sensorThresholds;
232 if (!parseThresholdsFromConfig(interfaces,
233 sensorThresholds))
234 {
George Liuf2a2baa2025-02-20 17:32:51 +0800235 lg2::error("error populating thresholds for '{NAME}'",
236 "NAME", name);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400237 }
238
239 uint8_t busId = loadVariant<uint8_t>(cfg, "Bus");
240 uint8_t mcuAddress = loadVariant<uint8_t>(cfg, "Address");
241 uint8_t tempReg = loadVariant<uint8_t>(cfg, "Reg");
242
243 std::string sensorClass =
244 loadVariant<std::string>(cfg, "Class");
245
246 if constexpr (debug)
247 {
George Liuf2a2baa2025-02-20 17:32:51 +0800248 lg2::error(
249 "Configuration parsed for '{INTERFACE}' with Name: {NAME}, Bus: {BUS}, "
250 "Address: {ADDRESS}, Reg: {REG}, Class: {CLASS}",
251 "INTERFACE", intf, "NAME", name, "BUS", busId,
252 "ADDRESS", mcuAddress, "REG", tempReg, "CLASS",
253 sensorClass);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400254 }
255
256 auto& sensor = sensors[name];
257
258 sensor = std::make_unique<MCUTempSensor>(
259 dbusConnection, io, name, path, objectServer,
260 std::move(sensorThresholds), busId, mcuAddress,
261 tempReg);
262
263 sensor->init();
264 }
265 }
266 },
JeffLin2c5a1f22022-10-05 15:19:09 +0800267 entityManagerName, "/xyz/openbmc_project/inventory",
268 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Yuan Li445efe32019-06-14 22:58:32 +0800269}
270
James Feistb6c0b912019-07-09 12:21:44 -0700271int main()
Yuan Li445efe32019-06-14 22:58:32 +0800272{
Ed Tanous1f978632023-02-28 18:16:39 -0800273 boost::asio::io_context io;
Yuan Li445efe32019-06-14 22:58:32 +0800274 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanous14ed5e92022-07-12 15:50:23 -0700275 sdbusplus::asio::object_server objectServer(systemBus, true);
276 objectServer.add_manager("/xyz/openbmc_project/sensors");
277
Yuan Li445efe32019-06-14 22:58:32 +0800278 systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
Yuan Li445efe32019-06-14 22:58:32 +0800279
Patrick Williams2aaf7172024-08-16 15:20:40 -0400280 boost::asio::post(io, [&]() {
281 createSensors(io, objectServer, sensors, systemBus);
282 });
Yuan Li445efe32019-06-14 22:58:32 +0800283
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700284 boost::asio::steady_timer configTimer(io);
Yuan Li445efe32019-06-14 22:58:32 +0800285
Patrick Williams92f8f512022-07-22 19:26:55 -0500286 std::function<void(sdbusplus::message_t&)> eventHandler =
287 [&](sdbusplus::message_t&) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400288 configTimer.expires_after(std::chrono::seconds(1));
289 // create a timer because normally multiple properties change
290 configTimer.async_wait([&](const boost::system::error_code& ec) {
291 if (ec == boost::asio::error::operation_aborted)
292 {
293 return; // we're being canceled
294 }
295 // config timer error
296 if (ec)
297 {
George Liuf2a2baa2025-02-20 17:32:51 +0800298 lg2::error("timer error");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400299 return;
300 }
301 createSensors(io, objectServer, sensors, systemBus);
302 if (sensors.empty())
303 {
George Liuf2a2baa2025-02-20 17:32:51 +0800304 lg2::info("Configuration not detected");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400305 }
306 });
307 };
Yuan Li445efe32019-06-14 22:58:32 +0800308
Zev Weiss214d9712022-08-12 12:54:31 -0700309 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
310 setupPropertiesChangedMatches(
Zev Weiss054aad82022-08-18 01:37:34 -0700311 *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
Bruce Lee1263c3d2021-06-04 15:16:33 +0800312 setupManufacturingModeMatch(*systemBus);
Yuan Li445efe32019-06-14 22:58:32 +0800313 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700314 return 0;
Yuan Li445efe32019-06-14 22:58:32 +0800315}