xref: /openbmc/dbus-sensors/src/mcu/MCUTempSensor.cpp (revision f2a2baae26ed85ef2096046f8457e5e0fbb1fb3f)
1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2019 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu //      http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu 
17d7be555eSGeorge Liu #include "MCUTempSensor.hpp"
18d7be555eSGeorge Liu 
19d7be555eSGeorge Liu #include "SensorPaths.hpp"
20d7be555eSGeorge Liu #include "Thresholds.hpp"
21d7be555eSGeorge Liu #include "Utils.hpp"
22d7be555eSGeorge Liu #include "sensor.hpp"
23d7be555eSGeorge Liu 
24d7be555eSGeorge Liu #include <fcntl.h>
25d7be555eSGeorge Liu #include <linux/i2c.h>
26d7be555eSGeorge Liu #include <sys/ioctl.h>
27d7be555eSGeorge Liu #include <unistd.h>
28d7be555eSGeorge Liu 
29d7be555eSGeorge Liu #include <boost/asio/error.hpp>
30d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
31d7be555eSGeorge Liu #include <boost/asio/post.hpp>
32d7be555eSGeorge Liu #include <boost/asio/steady_timer.hpp>
33d7be555eSGeorge Liu #include <boost/container/flat_map.hpp>
34*f2a2baaeSGeorge Liu #include <phosphor-logging/lg2.hpp>
35d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
36d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
37d7be555eSGeorge Liu #include <sdbusplus/bus/match.hpp>
38d7be555eSGeorge Liu #include <sdbusplus/message.hpp>
39d7be555eSGeorge Liu 
40d7be555eSGeorge Liu #include <array>
41d7be555eSGeorge Liu #include <chrono>
42d7be555eSGeorge Liu #include <cstddef>
43d7be555eSGeorge Liu #include <cstdint>
44d7be555eSGeorge Liu #include <functional>
45d7be555eSGeorge Liu #include <memory>
46d7be555eSGeorge Liu #include <string>
47d7be555eSGeorge Liu #include <utility>
48d7be555eSGeorge Liu #include <vector>
49d7be555eSGeorge Liu 
50d7be555eSGeorge Liu extern "C"
51d7be555eSGeorge Liu {
52d7be555eSGeorge Liu #include <i2c/smbus.h>
53d7be555eSGeorge Liu #include <linux/i2c-dev.h>
54d7be555eSGeorge Liu }
55d7be555eSGeorge Liu 
56d7be555eSGeorge Liu constexpr const bool debug = false;
57d7be555eSGeorge Liu 
58d7be555eSGeorge Liu constexpr const char* sensorType = "MCUTempSensor";
59d7be555eSGeorge Liu static constexpr double mcuTempMaxReading = 0xFF;
60d7be555eSGeorge Liu static constexpr double mcuTempMinReading = 0;
61d7be555eSGeorge Liu 
62d7be555eSGeorge Liu boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
63d7be555eSGeorge Liu 
MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & 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)64d7be555eSGeorge Liu MCUTempSensor::MCUTempSensor(
65d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& conn,
66d7be555eSGeorge Liu     boost::asio::io_context& io, const std::string& sensorName,
67d7be555eSGeorge Liu     const std::string& sensorConfiguration,
68d7be555eSGeorge Liu     sdbusplus::asio::object_server& objectServer,
69d7be555eSGeorge Liu     std::vector<thresholds::Threshold>&& thresholdData, uint8_t busId,
70d7be555eSGeorge Liu     uint8_t mcuAddress, uint8_t tempReg) :
71d7be555eSGeorge Liu     Sensor(escapeName(sensorName), std::move(thresholdData),
72d7be555eSGeorge Liu            sensorConfiguration, "MCUTempSensor", false, false,
73d7be555eSGeorge Liu            mcuTempMaxReading, mcuTempMinReading, conn),
74d7be555eSGeorge Liu     busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
75d7be555eSGeorge Liu     objectServer(objectServer), waitTimer(io)
76d7be555eSGeorge Liu {
77d7be555eSGeorge Liu     sensorInterface = objectServer.add_interface(
78d7be555eSGeorge Liu         "/xyz/openbmc_project/sensors/temperature/" + name,
79d7be555eSGeorge Liu         "xyz.openbmc_project.Sensor.Value");
80d7be555eSGeorge Liu 
81d7be555eSGeorge Liu     for (const auto& threshold : thresholds)
82d7be555eSGeorge Liu     {
83d7be555eSGeorge Liu         std::string interface = thresholds::getInterface(threshold.level);
84d7be555eSGeorge Liu         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
85d7be555eSGeorge Liu             objectServer.add_interface(
86d7be555eSGeorge Liu                 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
87d7be555eSGeorge Liu     }
88d7be555eSGeorge Liu     association = objectServer.add_interface(
89d7be555eSGeorge Liu         "/xyz/openbmc_project/sensors/temperature/" + name,
90d7be555eSGeorge Liu         association::interface);
91d7be555eSGeorge Liu }
92d7be555eSGeorge Liu 
~MCUTempSensor()93d7be555eSGeorge Liu MCUTempSensor::~MCUTempSensor()
94d7be555eSGeorge Liu {
95d7be555eSGeorge Liu     waitTimer.cancel();
96d7be555eSGeorge Liu     for (const auto& iface : thresholdInterfaces)
97d7be555eSGeorge Liu     {
98d7be555eSGeorge Liu         objectServer.remove_interface(iface);
99d7be555eSGeorge Liu     }
100d7be555eSGeorge Liu     objectServer.remove_interface(sensorInterface);
101d7be555eSGeorge Liu     objectServer.remove_interface(association);
102d7be555eSGeorge Liu }
103d7be555eSGeorge Liu 
init()104d7be555eSGeorge Liu void MCUTempSensor::init()
105d7be555eSGeorge Liu {
106d7be555eSGeorge Liu     setInitialProperties(sensor_paths::unitDegreesC);
107d7be555eSGeorge Liu     read();
108d7be555eSGeorge Liu }
109d7be555eSGeorge Liu 
checkThresholds()110d7be555eSGeorge Liu void MCUTempSensor::checkThresholds()
111d7be555eSGeorge Liu {
112d7be555eSGeorge Liu     thresholds::checkThresholds(this);
113d7be555eSGeorge Liu }
114d7be555eSGeorge Liu 
getMCURegsInfoWord(uint8_t regs,int32_t * pu32data) const115d7be555eSGeorge Liu int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int32_t* pu32data) const
116d7be555eSGeorge Liu {
117d7be555eSGeorge Liu     std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
118d7be555eSGeorge Liu 
119d7be555eSGeorge Liu     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
120d7be555eSGeorge Liu     int fd = open(i2cBus.c_str(), O_RDWR);
121d7be555eSGeorge Liu     if (fd < 0)
122d7be555eSGeorge Liu     {
123*f2a2baaeSGeorge Liu         lg2::error("unable to open i2c device '{BUS}' err = '{ERR}'", "BUS",
124*f2a2baaeSGeorge Liu                    i2cBus, "ERR", fd);
125d7be555eSGeorge Liu         return -1;
126d7be555eSGeorge Liu     }
127d7be555eSGeorge Liu 
128d7be555eSGeorge Liu     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
129d7be555eSGeorge Liu     if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
130d7be555eSGeorge Liu     {
131*f2a2baaeSGeorge Liu         lg2::error("unable to set device address");
132d7be555eSGeorge Liu         close(fd);
133d7be555eSGeorge Liu         return -1;
134d7be555eSGeorge Liu     }
135d7be555eSGeorge Liu 
136d7be555eSGeorge Liu     unsigned long funcs = 0;
137d7be555eSGeorge Liu     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
138d7be555eSGeorge Liu     if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
139d7be555eSGeorge Liu     {
140*f2a2baaeSGeorge Liu         lg2::error("not support I2C_FUNCS");
141d7be555eSGeorge Liu         close(fd);
142d7be555eSGeorge Liu         return -1;
143d7be555eSGeorge Liu     }
144d7be555eSGeorge Liu 
145d7be555eSGeorge Liu     if ((funcs & I2C_FUNC_SMBUS_READ_WORD_DATA) == 0U)
146d7be555eSGeorge Liu     {
147*f2a2baaeSGeorge Liu         lg2::error("not support I2C_FUNC_SMBUS_READ_WORD_DATA");
148d7be555eSGeorge Liu         close(fd);
149d7be555eSGeorge Liu         return -1;
150d7be555eSGeorge Liu     }
151d7be555eSGeorge Liu 
152d7be555eSGeorge Liu     *pu32data = i2c_smbus_read_word_data(fd, regs);
153d7be555eSGeorge Liu     close(fd);
154d7be555eSGeorge Liu 
155d7be555eSGeorge Liu     if (*pu32data < 0)
156d7be555eSGeorge Liu     {
157*f2a2baaeSGeorge Liu         lg2::error(" read word data failed at '{REGS}'", "REGS", regs);
158d7be555eSGeorge Liu         return -1;
159d7be555eSGeorge Liu     }
160d7be555eSGeorge Liu 
161d7be555eSGeorge Liu     return 0;
162d7be555eSGeorge Liu }
163d7be555eSGeorge Liu 
read()164d7be555eSGeorge Liu void MCUTempSensor::read()
165d7be555eSGeorge Liu {
166d7be555eSGeorge Liu     static constexpr size_t pollTime = 1; // in seconds
167d7be555eSGeorge Liu 
168d7be555eSGeorge Liu     waitTimer.expires_after(std::chrono::seconds(pollTime));
169d7be555eSGeorge Liu     waitTimer.async_wait([this](const boost::system::error_code& ec) {
170d7be555eSGeorge Liu         if (ec == boost::asio::error::operation_aborted)
171d7be555eSGeorge Liu         {
172d7be555eSGeorge Liu             return; // we're being cancelled
173d7be555eSGeorge Liu         }
174d7be555eSGeorge Liu         // read timer error
175d7be555eSGeorge Liu         if (ec)
176d7be555eSGeorge Liu         {
177*f2a2baaeSGeorge Liu             lg2::error("timer error");
178d7be555eSGeorge Liu             return;
179d7be555eSGeorge Liu         }
180d7be555eSGeorge Liu         int32_t temp = 0;
181d7be555eSGeorge Liu         int ret = getMCURegsInfoWord(tempReg, &temp);
182d7be555eSGeorge Liu         if (ret >= 0)
183d7be555eSGeorge Liu         {
184d7be555eSGeorge Liu             double v = static_cast<double>(temp) / 1000;
185d7be555eSGeorge Liu             if constexpr (debug)
186d7be555eSGeorge Liu             {
187*f2a2baaeSGeorge Liu                 lg2::error("Value update to '{VALUE}' raw reading '{RAW}'",
188*f2a2baaeSGeorge Liu                            "VALUE", v, "RAW", temp);
189d7be555eSGeorge Liu             }
190d7be555eSGeorge Liu             updateValue(v);
191d7be555eSGeorge Liu         }
192d7be555eSGeorge Liu         else
193d7be555eSGeorge Liu         {
194*f2a2baaeSGeorge Liu             lg2::error("Invalid read getMCURegsInfoWord");
195d7be555eSGeorge Liu             incrementError();
196d7be555eSGeorge Liu         }
197d7be555eSGeorge Liu         read();
198d7be555eSGeorge Liu     });
199d7be555eSGeorge Liu }
200d7be555eSGeorge Liu 
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::unique_ptr<MCUTempSensor>> & sensors,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)201d7be555eSGeorge Liu void createSensors(
202d7be555eSGeorge Liu     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
203d7be555eSGeorge Liu     boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
204d7be555eSGeorge Liu         sensors,
205d7be555eSGeorge Liu     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
206d7be555eSGeorge Liu {
207d7be555eSGeorge Liu     if (!dbusConnection)
208d7be555eSGeorge Liu     {
209*f2a2baaeSGeorge Liu         lg2::error("Connection not created");
210d7be555eSGeorge Liu         return;
211d7be555eSGeorge Liu     }
212d7be555eSGeorge Liu 
213d7be555eSGeorge Liu     dbusConnection->async_method_call(
214d7be555eSGeorge Liu         [&io, &objectServer, &dbusConnection, &sensors](
215d7be555eSGeorge Liu             boost::system::error_code ec, const ManagedObjectType& resp) {
216d7be555eSGeorge Liu             if (ec)
217d7be555eSGeorge Liu             {
218*f2a2baaeSGeorge Liu                 lg2::error("Error contacting entity manager");
219d7be555eSGeorge Liu                 return;
220d7be555eSGeorge Liu             }
221d7be555eSGeorge Liu             for (const auto& [path, interfaces] : resp)
222d7be555eSGeorge Liu             {
223d7be555eSGeorge Liu                 for (const auto& [intf, cfg] : interfaces)
224d7be555eSGeorge Liu                 {
225d7be555eSGeorge Liu                     if (intf != configInterfaceName(sensorType))
226d7be555eSGeorge Liu                     {
227d7be555eSGeorge Liu                         continue;
228d7be555eSGeorge Liu                     }
229d7be555eSGeorge Liu                     std::string name = loadVariant<std::string>(cfg, "Name");
230d7be555eSGeorge Liu 
231d7be555eSGeorge Liu                     std::vector<thresholds::Threshold> sensorThresholds;
232d7be555eSGeorge Liu                     if (!parseThresholdsFromConfig(interfaces,
233d7be555eSGeorge Liu                                                    sensorThresholds))
234d7be555eSGeorge Liu                     {
235*f2a2baaeSGeorge Liu                         lg2::error("error populating thresholds for '{NAME}'",
236*f2a2baaeSGeorge Liu                                    "NAME", name);
237d7be555eSGeorge Liu                     }
238d7be555eSGeorge Liu 
239d7be555eSGeorge Liu                     uint8_t busId = loadVariant<uint8_t>(cfg, "Bus");
240d7be555eSGeorge Liu                     uint8_t mcuAddress = loadVariant<uint8_t>(cfg, "Address");
241d7be555eSGeorge Liu                     uint8_t tempReg = loadVariant<uint8_t>(cfg, "Reg");
242d7be555eSGeorge Liu 
243d7be555eSGeorge Liu                     std::string sensorClass =
244d7be555eSGeorge Liu                         loadVariant<std::string>(cfg, "Class");
245d7be555eSGeorge Liu 
246d7be555eSGeorge Liu                     if constexpr (debug)
247d7be555eSGeorge Liu                     {
248*f2a2baaeSGeorge Liu                         lg2::error(
249*f2a2baaeSGeorge Liu                             "Configuration parsed for '{INTERFACE}' with Name: {NAME}, Bus: {BUS}, "
250*f2a2baaeSGeorge Liu                             "Address: {ADDRESS}, Reg: {REG}, Class: {CLASS}",
251*f2a2baaeSGeorge Liu                             "INTERFACE", intf, "NAME", name, "BUS", busId,
252*f2a2baaeSGeorge Liu                             "ADDRESS", mcuAddress, "REG", tempReg, "CLASS",
253*f2a2baaeSGeorge Liu                             sensorClass);
254d7be555eSGeorge Liu                     }
255d7be555eSGeorge Liu 
256d7be555eSGeorge Liu                     auto& sensor = sensors[name];
257d7be555eSGeorge Liu 
258d7be555eSGeorge Liu                     sensor = std::make_unique<MCUTempSensor>(
259d7be555eSGeorge Liu                         dbusConnection, io, name, path, objectServer,
260d7be555eSGeorge Liu                         std::move(sensorThresholds), busId, mcuAddress,
261d7be555eSGeorge Liu                         tempReg);
262d7be555eSGeorge Liu 
263d7be555eSGeorge Liu                     sensor->init();
264d7be555eSGeorge Liu                 }
265d7be555eSGeorge Liu             }
266d7be555eSGeorge Liu         },
267d7be555eSGeorge Liu         entityManagerName, "/xyz/openbmc_project/inventory",
268d7be555eSGeorge Liu         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
269d7be555eSGeorge Liu }
270d7be555eSGeorge Liu 
main()271d7be555eSGeorge Liu int main()
272d7be555eSGeorge Liu {
273d7be555eSGeorge Liu     boost::asio::io_context io;
274d7be555eSGeorge Liu     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
275d7be555eSGeorge Liu     sdbusplus::asio::object_server objectServer(systemBus, true);
276d7be555eSGeorge Liu     objectServer.add_manager("/xyz/openbmc_project/sensors");
277d7be555eSGeorge Liu 
278d7be555eSGeorge Liu     systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
279d7be555eSGeorge Liu 
280d7be555eSGeorge Liu     boost::asio::post(io, [&]() {
281d7be555eSGeorge Liu         createSensors(io, objectServer, sensors, systemBus);
282d7be555eSGeorge Liu     });
283d7be555eSGeorge Liu 
284d7be555eSGeorge Liu     boost::asio::steady_timer configTimer(io);
285d7be555eSGeorge Liu 
286d7be555eSGeorge Liu     std::function<void(sdbusplus::message_t&)> eventHandler =
287d7be555eSGeorge Liu         [&](sdbusplus::message_t&) {
288d7be555eSGeorge Liu             configTimer.expires_after(std::chrono::seconds(1));
289d7be555eSGeorge Liu             // create a timer because normally multiple properties change
290d7be555eSGeorge Liu             configTimer.async_wait([&](const boost::system::error_code& ec) {
291d7be555eSGeorge Liu                 if (ec == boost::asio::error::operation_aborted)
292d7be555eSGeorge Liu                 {
293d7be555eSGeorge Liu                     return; // we're being canceled
294d7be555eSGeorge Liu                 }
295d7be555eSGeorge Liu                 // config timer error
296d7be555eSGeorge Liu                 if (ec)
297d7be555eSGeorge Liu                 {
298*f2a2baaeSGeorge Liu                     lg2::error("timer error");
299d7be555eSGeorge Liu                     return;
300d7be555eSGeorge Liu                 }
301d7be555eSGeorge Liu                 createSensors(io, objectServer, sensors, systemBus);
302d7be555eSGeorge Liu                 if (sensors.empty())
303d7be555eSGeorge Liu                 {
304*f2a2baaeSGeorge Liu                     lg2::info("Configuration not detected");
305d7be555eSGeorge Liu                 }
306d7be555eSGeorge Liu             });
307d7be555eSGeorge Liu         };
308d7be555eSGeorge Liu 
309d7be555eSGeorge Liu     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
310d7be555eSGeorge Liu         setupPropertiesChangedMatches(
311d7be555eSGeorge Liu             *systemBus, std::to_array<const char*>({sensorType}), eventHandler);
312d7be555eSGeorge Liu     setupManufacturingModeMatch(*systemBus);
313d7be555eSGeorge Liu     io.run();
314d7be555eSGeorge Liu     return 0;
315d7be555eSGeorge Liu }
316