blob: ba68bedb2dbb69fca6a3ba92212fe754ce73f20f [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*/
Ed Tanous8a57ec02020-10-09 12:46:52 -070016#include <MCUTempSensor.hpp>
17#include <Utils.hpp>
18#include <VariantVisitors.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070019#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070020#include <sdbusplus/asio/connection.hpp>
21#include <sdbusplus/asio/object_server.hpp>
22#include <sdbusplus/bus/match.hpp>
23
Yuan Li445efe32019-06-14 22:58:32 +080024#include <chrono>
Ed Tanous8a57ec02020-10-09 12:46:52 -070025#include <cmath>
Patrick Venture96e97db2019-10-31 13:44:38 -070026#include <functional>
Yuan Li445efe32019-06-14 22:58:32 +080027#include <iostream>
28#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <memory>
Yuan Li445efe32019-06-14 22:58:32 +080030#include <numeric>
Patrick Venture96e97db2019-10-31 13:44:38 -070031#include <string>
Yuan Li445efe32019-06-14 22:58:32 +080032#include <vector>
33
James Feist38fb5982020-05-28 10:09:54 -070034extern "C"
35{
Yuan Li445efe32019-06-14 22:58:32 +080036#include <i2c/smbus.h>
37#include <linux/i2c-dev.h>
38}
39
40constexpr const bool debug = false;
41
Zev Weiss054aad82022-08-18 01:37:34 -070042constexpr const char* sensorType = "MCUTempSensor";
Yuan Li445efe32019-06-14 22:58:32 +080043static constexpr double mcuTempMaxReading = 0xFF;
44static constexpr double mcuTempMinReading = 0;
45
46boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
47
48MCUTempSensor::MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
49 boost::asio::io_service& io,
50 const std::string& sensorName,
51 const std::string& sensorConfiguration,
52 sdbusplus::asio::object_server& objectServer,
53 std::vector<thresholds::Threshold>&& thresholdData,
54 uint8_t busId, uint8_t mcuAddress,
55 uint8_t tempReg) :
Zhikui Renda98f092021-11-01 09:41:08 -070056 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -070057 sensorConfiguration, "MCUTempSensor", false, false,
Zev Weiss34d7b972022-08-17 19:38:59 -070058 mcuTempMaxReading, mcuTempMinReading, conn),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050059 busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
James Feiste3338522020-09-15 15:40:30 -070060 objectServer(objectServer), waitTimer(io)
Yuan Li445efe32019-06-14 22:58:32 +080061{
62 sensorInterface = objectServer.add_interface(
63 "/xyz/openbmc_project/sensors/temperature/" + name,
64 "xyz.openbmc_project.Sensor.Value");
65
Jayashree Dhanapal56678082022-01-04 17:27:20 +053066 for (const auto& threshold : thresholds)
Yuan Li445efe32019-06-14 22:58:32 +080067 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053068 std::string interface = thresholds::getInterface(threshold.level);
69 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
70 objectServer.add_interface(
71 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
Yuan Li445efe32019-06-14 22:58:32 +080072 }
73 association = objectServer.add_interface(
74 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070075 association::interface);
Yuan Li445efe32019-06-14 22:58:32 +080076}
77
78MCUTempSensor::~MCUTempSensor()
79{
80 waitTimer.cancel();
Jayashree Dhanapal56678082022-01-04 17:27:20 +053081 for (const auto& iface : thresholdInterfaces)
82 {
83 objectServer.remove_interface(iface);
84 }
Yuan Li445efe32019-06-14 22:58:32 +080085 objectServer.remove_interface(sensorInterface);
86 objectServer.remove_interface(association);
87}
88
89void MCUTempSensor::init(void)
90{
Andrei Kartashev39287412022-02-04 16:04:47 +030091 setInitialProperties(sensor_paths::unitDegreesC);
Yuan Li445efe32019-06-14 22:58:32 +080092 read();
93}
94
95void MCUTempSensor::checkThresholds(void)
96{
97 thresholds::checkThresholds(this);
98}
99
Ed Tanous2049bd22022-07-09 07:20:26 -0700100int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int16_t* pu16data) const
Yuan Li445efe32019-06-14 22:58:32 +0800101{
102 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
Yuan Li445efe32019-06-14 22:58:32 +0800103
Ed Tanous99c44092022-01-14 09:59:09 -0800104 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
105 int fd = open(i2cBus.c_str(), O_RDWR);
Yuan Li445efe32019-06-14 22:58:32 +0800106 if (fd < 0)
107 {
108 std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
109 << "\n";
110 return -1;
111 }
112
Ed Tanous99c44092022-01-14 09:59:09 -0800113 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800114 if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
115 {
116 std::cerr << " unable to set device address\n";
117 close(fd);
118 return -1;
119 }
120
121 unsigned long funcs = 0;
Ed Tanous99c44092022-01-14 09:59:09 -0800122 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
Yuan Li445efe32019-06-14 22:58:32 +0800123 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
124 {
125 std::cerr << " not support I2C_FUNCS\n";
126 close(fd);
127 return -1;
128 }
129
Ed Tanous2049bd22022-07-09 07:20:26 -0700130 if ((funcs & I2C_FUNC_SMBUS_READ_WORD_DATA) == 0U)
Yuan Li445efe32019-06-14 22:58:32 +0800131 {
132 std::cerr << " not support I2C_FUNC_SMBUS_READ_WORD_DATA\n";
133 close(fd);
134 return -1;
135 }
136
137 *pu16data = i2c_smbus_read_word_data(fd, regs);
138 close(fd);
139
140 if (*pu16data < 0)
141 {
142 std::cerr << " read word data failed at " << static_cast<int>(regs)
143 << "\n";
144 return -1;
145 }
146
147 return 0;
148}
149
150void MCUTempSensor::read(void)
151{
152 static constexpr size_t pollTime = 1; // in seconds
153
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700154 waitTimer.expires_from_now(std::chrono::seconds(pollTime));
Yuan Li445efe32019-06-14 22:58:32 +0800155 waitTimer.async_wait([this](const boost::system::error_code& ec) {
156 if (ec == boost::asio::error::operation_aborted)
157 {
158 return; // we're being cancelled
159 }
160 // read timer error
Ed Tanous8a57ec02020-10-09 12:46:52 -0700161 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800162 {
163 std::cerr << "timer error\n";
164 return;
165 }
Ed Tanousa771f6a2022-01-14 09:36:51 -0800166 int16_t temp = 0;
Yuan Li445efe32019-06-14 22:58:32 +0800167 int ret = getMCURegsInfoWord(tempReg, &temp);
168 if (ret >= 0)
169 {
170 double v = static_cast<double>(temp) / 1000;
171 if constexpr (debug)
172 {
James Feistb6c0b912019-07-09 12:21:44 -0700173 std::cerr << "Value update to " << v << "raw reading "
Yuan Li445efe32019-06-14 22:58:32 +0800174 << static_cast<int>(temp) << "\n";
175 }
176 updateValue(v);
177 }
178 else
179 {
180 std::cerr << "Invalid read getMCURegsInfoWord\n";
James Feist961bf092020-07-01 16:38:12 -0700181 incrementError();
Yuan Li445efe32019-06-14 22:58:32 +0800182 }
183 read();
184 });
185}
186
187void createSensors(
188 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
189 boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
190 sensors,
191 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
192{
193 if (!dbusConnection)
194 {
195 std::cerr << "Connection not created\n";
196 return;
197 }
198
199 dbusConnection->async_method_call(
200 [&io, &objectServer, &dbusConnection, &sensors](
201 boost::system::error_code ec, const ManagedObjectType& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -0700202 if (ec)
203 {
204 std::cerr << "Error contacting entity manager\n";
205 return;
206 }
Zev Weissd7c1f762022-08-12 18:21:02 -0700207 for (const auto& [path, interfaces] : resp)
Ed Tanousbb679322022-05-16 16:10:00 -0700208 {
Zev Weissd7c1f762022-08-12 18:21:02 -0700209 for (const auto& [intf, cfg] : interfaces)
Yuan Li445efe32019-06-14 22:58:32 +0800210 {
Zev Weiss054aad82022-08-18 01:37:34 -0700211 if (intf != configInterfaceName(sensorType))
Yuan Li445efe32019-06-14 22:58:32 +0800212 {
Ed Tanousbb679322022-05-16 16:10:00 -0700213 continue;
Yuan Li445efe32019-06-14 22:58:32 +0800214 }
Zev Weissd7c1f762022-08-12 18:21:02 -0700215 std::string name = loadVariant<std::string>(cfg, "Name");
Ed Tanousbb679322022-05-16 16:10:00 -0700216
217 std::vector<thresholds::Threshold> sensorThresholds;
Zev Weissd7c1f762022-08-12 18:21:02 -0700218 if (!parseThresholdsFromConfig(interfaces, sensorThresholds))
Ed Tanousbb679322022-05-16 16:10:00 -0700219 {
220 std::cerr << "error populating thresholds for " << name
221 << "\n";
222 }
223
Zev Weissd7c1f762022-08-12 18:21:02 -0700224 uint8_t busId = loadVariant<uint8_t>(cfg, "Bus");
225 uint8_t mcuAddress = loadVariant<uint8_t>(cfg, "Address");
226 uint8_t tempReg = loadVariant<uint8_t>(cfg, "Reg");
Ed Tanousbb679322022-05-16 16:10:00 -0700227
228 std::string sensorClass =
Zev Weissd7c1f762022-08-12 18:21:02 -0700229 loadVariant<std::string>(cfg, "Class");
Ed Tanousbb679322022-05-16 16:10:00 -0700230
231 if constexpr (debug)
232 {
Zev Weissd7c1f762022-08-12 18:21:02 -0700233 std::cerr << "Configuration parsed for \n\t" << intf << "\n"
Ed Tanousbb679322022-05-16 16:10:00 -0700234 << "with\n"
235 << "\tName: " << name << "\n"
236 << "\tBus: " << static_cast<int>(busId) << "\n"
237 << "\tAddress: " << static_cast<int>(mcuAddress)
238 << "\n"
239 << "\tReg: " << static_cast<int>(tempReg) << "\n"
240 << "\tClass: " << sensorClass << "\n";
241 }
242
243 auto& sensor = sensors[name];
244
245 sensor = std::make_unique<MCUTempSensor>(
Zev Weissd7c1f762022-08-12 18:21:02 -0700246 dbusConnection, io, name, path, objectServer,
Ed Tanousbb679322022-05-16 16:10:00 -0700247 std::move(sensorThresholds), busId, mcuAddress, tempReg);
248
249 sensor->init();
Yuan Li445efe32019-06-14 22:58:32 +0800250 }
Ed Tanousbb679322022-05-16 16:10:00 -0700251 }
Yuan Li445efe32019-06-14 22:58:32 +0800252 },
253 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
254 "GetManagedObjects");
255}
256
James Feistb6c0b912019-07-09 12:21:44 -0700257int main()
Yuan Li445efe32019-06-14 22:58:32 +0800258{
259 boost::asio::io_service io;
260 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
261 systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
262 sdbusplus::asio::object_server objectServer(systemBus);
263
264 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
265
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700266 boost::asio::steady_timer configTimer(io);
Yuan Li445efe32019-06-14 22:58:32 +0800267
Patrick Williams92f8f512022-07-22 19:26:55 -0500268 std::function<void(sdbusplus::message_t&)> eventHandler =
269 [&](sdbusplus::message_t&) {
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700270 configTimer.expires_from_now(std::chrono::seconds(1));
Ed Tanousbb679322022-05-16 16:10:00 -0700271 // create a timer because normally multiple properties change
272 configTimer.async_wait([&](const boost::system::error_code& ec) {
273 if (ec == boost::asio::error::operation_aborted)
274 {
275 return; // we're being canceled
276 }
277 // config timer error
278 if (ec)
279 {
280 std::cerr << "timer error\n";
281 return;
282 }
283 createSensors(io, objectServer, sensors, systemBus);
284 if (sensors.empty())
285 {
286 std::cout << "Configuration not detected\n";
287 }
288 });
289 };
Yuan Li445efe32019-06-14 22:58:32 +0800290
Zev Weiss214d9712022-08-12 12:54:31 -0700291 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
292 setupPropertiesChangedMatches(
Zev Weiss054aad82022-08-18 01:37:34 -0700293 *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
Bruce Lee1263c3d2021-06-04 15:16:33 +0800294 setupManufacturingModeMatch(*systemBus);
Yuan Li445efe32019-06-14 22:58:32 +0800295 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700296 return 0;
Yuan Li445efe32019-06-14 22:58:32 +0800297}