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