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