blob: fef23e923b6f851e7d4927aafc38dda71b54d8c8 [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
19#include "Utils.hpp"
20#include "VariantVisitors.hpp"
21
Patrick Venture96e97db2019-10-31 13:44:38 -070022#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
25#include <sdbusplus/bus/match.hpp>
26
Yuan Li445efe32019-06-14 22:58:32 +080027#include <chrono>
Ed Tanous8a57ec02020-10-09 12:46:52 -070028#include <cmath>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <functional>
Yuan Li445efe32019-06-14 22:58:32 +080030#include <iostream>
31#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070032#include <memory>
Yuan Li445efe32019-06-14 22:58:32 +080033#include <numeric>
Patrick Venture96e97db2019-10-31 13:44:38 -070034#include <string>
Yuan Li445efe32019-06-14 22:58:32 +080035#include <vector>
36
James Feist38fb5982020-05-28 10:09:54 -070037extern "C"
38{
Yuan Li445efe32019-06-14 22:58:32 +080039#include <i2c/smbus.h>
40#include <linux/i2c-dev.h>
41}
42
43constexpr const bool debug = false;
44
Zev Weiss054aad82022-08-18 01:37:34 -070045constexpr const char* sensorType = "MCUTempSensor";
Yuan Li445efe32019-06-14 22:58:32 +080046static constexpr double mcuTempMaxReading = 0xFF;
47static constexpr double mcuTempMinReading = 0;
48
49boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
50
51MCUTempSensor::MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
Ed Tanous1f978632023-02-28 18:16:39 -080052 boost::asio::io_context& io,
Yuan Li445efe32019-06-14 22:58:32 +080053 const std::string& sensorName,
54 const std::string& sensorConfiguration,
55 sdbusplus::asio::object_server& objectServer,
56 std::vector<thresholds::Threshold>&& thresholdData,
57 uint8_t busId, uint8_t mcuAddress,
58 uint8_t tempReg) :
Zhikui Renda98f092021-11-01 09:41:08 -070059 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -070060 sensorConfiguration, "MCUTempSensor", false, false,
Zev Weiss34d7b972022-08-17 19:38:59 -070061 mcuTempMaxReading, mcuTempMinReading, conn),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050062 busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
James Feiste3338522020-09-15 15:40:30 -070063 objectServer(objectServer), waitTimer(io)
Yuan Li445efe32019-06-14 22:58:32 +080064{
65 sensorInterface = objectServer.add_interface(
66 "/xyz/openbmc_project/sensors/temperature/" + name,
67 "xyz.openbmc_project.Sensor.Value");
68
Jayashree Dhanapal56678082022-01-04 17:27:20 +053069 for (const auto& threshold : thresholds)
Yuan Li445efe32019-06-14 22:58:32 +080070 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053071 std::string interface = thresholds::getInterface(threshold.level);
72 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
73 objectServer.add_interface(
74 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
Yuan Li445efe32019-06-14 22:58:32 +080075 }
76 association = objectServer.add_interface(
77 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070078 association::interface);
Yuan Li445efe32019-06-14 22:58:32 +080079}
80
81MCUTempSensor::~MCUTempSensor()
82{
83 waitTimer.cancel();
Jayashree Dhanapal56678082022-01-04 17:27:20 +053084 for (const auto& iface : thresholdInterfaces)
85 {
86 objectServer.remove_interface(iface);
87 }
Yuan Li445efe32019-06-14 22:58:32 +080088 objectServer.remove_interface(sensorInterface);
89 objectServer.remove_interface(association);
90}
91
Ed Tanous201a1012024-04-03 18:07:28 -070092void MCUTempSensor::init()
Yuan Li445efe32019-06-14 22:58:32 +080093{
Andrei Kartashev39287412022-02-04 16:04:47 +030094 setInitialProperties(sensor_paths::unitDegreesC);
Yuan Li445efe32019-06-14 22:58:32 +080095 read();
96}
97
Ed Tanous201a1012024-04-03 18:07:28 -070098void MCUTempSensor::checkThresholds()
Yuan Li445efe32019-06-14 22:58:32 +080099{
100 thresholds::checkThresholds(this);
101}
102
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000103int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int32_t* pu32data) const
Yuan Li445efe32019-06-14 22:58:32 +0800104{
105 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
Yuan Li445efe32019-06-14 22:58:32 +0800106
Ed Tanous99c44092022-01-14 09:59:09 -0800107 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
108 int fd = open(i2cBus.c_str(), O_RDWR);
Yuan Li445efe32019-06-14 22:58:32 +0800109 if (fd < 0)
110 {
111 std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
112 << "\n";
113 return -1;
114 }
115
Ed Tanous99c44092022-01-14 09:59:09 -0800116 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800117 if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
118 {
119 std::cerr << " unable to set device address\n";
120 close(fd);
121 return -1;
122 }
123
124 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800125 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800126 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
127 {
128 std::cerr << " not support I2C_FUNCS\n";
129 close(fd);
130 return -1;
131 }
132
Ed Tanous2049bd22022-07-09 07:20:26 -0700133 if ((funcs & I2C_FUNC_SMBUS_READ_WORD_DATA) == 0U)
Yuan Li445efe32019-06-14 22:58:32 +0800134 {
135 std::cerr << " not support I2C_FUNC_SMBUS_READ_WORD_DATA\n";
136 close(fd);
137 return -1;
138 }
139
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000140 *pu32data = i2c_smbus_read_word_data(fd, regs);
Yuan Li445efe32019-06-14 22:58:32 +0800141 close(fd);
142
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000143 if (*pu32data < 0)
Yuan Li445efe32019-06-14 22:58:32 +0800144 {
145 std::cerr << " read word data failed at " << static_cast<int>(regs)
146 << "\n";
147 return -1;
148 }
149
150 return 0;
151}
152
Ed Tanous201a1012024-04-03 18:07:28 -0700153void MCUTempSensor::read()
Yuan Li445efe32019-06-14 22:58:32 +0800154{
155 static constexpr size_t pollTime = 1; // in seconds
156
Ed Tanous83db50c2023-03-01 10:20:24 -0800157 waitTimer.expires_after(std::chrono::seconds(pollTime));
Yuan Li445efe32019-06-14 22:58:32 +0800158 waitTimer.async_wait([this](const boost::system::error_code& ec) {
159 if (ec == boost::asio::error::operation_aborted)
160 {
161 return; // we're being cancelled
162 }
163 // read timer error
Ed Tanous8a57ec02020-10-09 12:46:52 -0700164 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800165 {
166 std::cerr << "timer error\n";
167 return;
168 }
Saitwal, Meghanf1169f72023-04-23 05:14:14 +0000169 int32_t temp = 0;
Yuan Li445efe32019-06-14 22:58:32 +0800170 int ret = getMCURegsInfoWord(tempReg, &temp);
171 if (ret >= 0)
172 {
173 double v = static_cast<double>(temp) / 1000;
174 if constexpr (debug)
175 {
James Feistb6c0b912019-07-09 12:21:44 -0700176 std::cerr << "Value update to " << v << "raw reading "
Yuan Li445efe32019-06-14 22:58:32 +0800177 << static_cast<int>(temp) << "\n";
178 }
179 updateValue(v);
180 }
181 else
182 {
183 std::cerr << "Invalid read getMCURegsInfoWord\n";
James Feist961bf092020-07-01 16:38:12 -0700184 incrementError();
Yuan Li445efe32019-06-14 22:58:32 +0800185 }
186 read();
187 });
188}
189
190void createSensors(
Ed Tanous1f978632023-02-28 18:16:39 -0800191 boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
Yuan Li445efe32019-06-14 22:58:32 +0800192 boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
193 sensors,
194 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
195{
196 if (!dbusConnection)
197 {
198 std::cerr << "Connection not created\n";
199 return;
200 }
201
202 dbusConnection->async_method_call(
203 [&io, &objectServer, &dbusConnection, &sensors](
204 boost::system::error_code ec, const ManagedObjectType& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -0700205 if (ec)
206 {
207 std::cerr << "Error contacting entity manager\n";
208 return;
209 }
Zev Weissd7c1f762022-08-12 18:21:02 -0700210 for (const auto& [path, interfaces] : resp)
Ed Tanousbb679322022-05-16 16:10:00 -0700211 {
Zev Weissd7c1f762022-08-12 18:21:02 -0700212 for (const auto& [intf, cfg] : interfaces)
Yuan Li445efe32019-06-14 22:58:32 +0800213 {
Zev Weiss054aad82022-08-18 01:37:34 -0700214 if (intf != configInterfaceName(sensorType))
Yuan Li445efe32019-06-14 22:58:32 +0800215 {
Ed Tanousbb679322022-05-16 16:10:00 -0700216 continue;
Yuan Li445efe32019-06-14 22:58:32 +0800217 }
Zev Weissd7c1f762022-08-12 18:21:02 -0700218 std::string name = loadVariant<std::string>(cfg, "Name");
Ed Tanousbb679322022-05-16 16:10:00 -0700219
220 std::vector<thresholds::Threshold> sensorThresholds;
Zev Weissd7c1f762022-08-12 18:21:02 -0700221 if (!parseThresholdsFromConfig(interfaces, sensorThresholds))
Ed Tanousbb679322022-05-16 16:10:00 -0700222 {
223 std::cerr << "error populating thresholds for " << name
224 << "\n";
225 }
226
Zev Weissd7c1f762022-08-12 18:21:02 -0700227 uint8_t busId = loadVariant<uint8_t>(cfg, "Bus");
228 uint8_t mcuAddress = loadVariant<uint8_t>(cfg, "Address");
229 uint8_t tempReg = loadVariant<uint8_t>(cfg, "Reg");
Ed Tanousbb679322022-05-16 16:10:00 -0700230
Patrick Williams779c96a2023-05-10 07:50:42 -0500231 std::string sensorClass = loadVariant<std::string>(cfg,
232 "Class");
Ed Tanousbb679322022-05-16 16:10:00 -0700233
234 if constexpr (debug)
235 {
Zev Weissd7c1f762022-08-12 18:21:02 -0700236 std::cerr << "Configuration parsed for \n\t" << intf << "\n"
Ed Tanousbb679322022-05-16 16:10:00 -0700237 << "with\n"
238 << "\tName: " << name << "\n"
239 << "\tBus: " << static_cast<int>(busId) << "\n"
240 << "\tAddress: " << static_cast<int>(mcuAddress)
241 << "\n"
242 << "\tReg: " << static_cast<int>(tempReg) << "\n"
243 << "\tClass: " << sensorClass << "\n";
244 }
245
246 auto& sensor = sensors[name];
247
248 sensor = std::make_unique<MCUTempSensor>(
Zev Weissd7c1f762022-08-12 18:21:02 -0700249 dbusConnection, io, name, path, objectServer,
Ed Tanousbb679322022-05-16 16:10:00 -0700250 std::move(sensorThresholds), busId, mcuAddress, tempReg);
251
252 sensor->init();
Yuan Li445efe32019-06-14 22:58:32 +0800253 }
Ed Tanousbb679322022-05-16 16:10:00 -0700254 }
Patrick Williams597e8422023-10-20 11:19:01 -0500255 },
JeffLin2c5a1f22022-10-05 15:19:09 +0800256 entityManagerName, "/xyz/openbmc_project/inventory",
257 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Yuan Li445efe32019-06-14 22:58:32 +0800258}
259
James Feistb6c0b912019-07-09 12:21:44 -0700260int main()
Yuan Li445efe32019-06-14 22:58:32 +0800261{
Ed Tanous1f978632023-02-28 18:16:39 -0800262 boost::asio::io_context io;
Yuan Li445efe32019-06-14 22:58:32 +0800263 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Ed Tanous14ed5e92022-07-12 15:50:23 -0700264 sdbusplus::asio::object_server objectServer(systemBus, true);
265 objectServer.add_manager("/xyz/openbmc_project/sensors");
266
Yuan Li445efe32019-06-14 22:58:32 +0800267 systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
Yuan Li445efe32019-06-14 22:58:32 +0800268
Ed Tanous83db50c2023-03-01 10:20:24 -0800269 boost::asio::post(
270 io, [&]() { createSensors(io, objectServer, sensors, systemBus); });
Yuan Li445efe32019-06-14 22:58:32 +0800271
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700272 boost::asio::steady_timer configTimer(io);
Yuan Li445efe32019-06-14 22:58:32 +0800273
Patrick Williams92f8f512022-07-22 19:26:55 -0500274 std::function<void(sdbusplus::message_t&)> eventHandler =
275 [&](sdbusplus::message_t&) {
Ed Tanous83db50c2023-03-01 10:20:24 -0800276 configTimer.expires_after(std::chrono::seconds(1));
Ed Tanousbb679322022-05-16 16:10:00 -0700277 // create a timer because normally multiple properties change
278 configTimer.async_wait([&](const boost::system::error_code& ec) {
279 if (ec == boost::asio::error::operation_aborted)
280 {
281 return; // we're being canceled
282 }
283 // config timer error
284 if (ec)
285 {
286 std::cerr << "timer error\n";
287 return;
288 }
289 createSensors(io, objectServer, sensors, systemBus);
290 if (sensors.empty())
291 {
292 std::cout << "Configuration not detected\n";
293 }
294 });
295 };
Yuan Li445efe32019-06-14 22:58:32 +0800296
Zev Weiss214d9712022-08-12 12:54:31 -0700297 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
298 setupPropertiesChangedMatches(
Zev Weiss054aad82022-08-18 01:37:34 -0700299 *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
Bruce Lee1263c3d2021-06-04 15:16:33 +0800300 setupManufacturingModeMatch(*systemBus);
Yuan Li445efe32019-06-14 22:58:32 +0800301 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700302 return 0;
Yuan Li445efe32019-06-14 22:58:32 +0800303}