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