blob: 69f01eaaefd7965c6f79614d7273e13de30f5663 [file] [log] [blame]
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +05301#include "config.h"
2
Gunnar Mills94df8c92018-09-14 14:50:03 -05003#include "occ_manager.hpp"
4
5#include "i2c_occ.hpp"
Chicago Duanbb895cb2021-06-18 19:37:16 +08006#include "occ_dbus.hpp"
Gunnar Mills94df8c92018-09-14 14:50:03 -05007#include "utils.hpp"
8
Chicago Duanbb895cb2021-06-18 19:37:16 +08009#include <cmath>
Gunnar Mills94df8c92018-09-14 14:50:03 -050010#include <experimental/filesystem>
11#include <phosphor-logging/elog-errors.hpp>
12#include <phosphor-logging/log.hpp>
Chicago Duanbb895cb2021-06-18 19:37:16 +080013#include <regex>
Gunnar Mills94df8c92018-09-14 14:50:03 -050014#include <xyz/openbmc_project/Common/error.hpp>
15
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053016namespace open_power
17{
18namespace occ
19{
20
Matt Spinler8b8abee2021-08-25 15:18:21 -050021constexpr uint32_t fruTypeNotAvailable = 0xFF;
Matt Spinlera26f1522021-08-25 15:50:20 -050022constexpr auto fruTypeSuffix = "fru_type";
23constexpr auto faultSuffix = "fault";
24constexpr auto inputSuffix = "input";
Matt Spinler8b8abee2021-08-25 15:18:21 -050025
Chris Caina8857c52021-01-27 11:53:05 -060026using namespace phosphor::logging;
27
Matt Spinlera26f1522021-08-25 15:50:20 -050028template <typename T>
29T readFile(const std::string& path)
30{
31 std::ifstream ifs;
32 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
33 std::ifstream::eofbit);
34 T data;
35
36 try
37 {
38 ifs.open(path);
39 ifs >> data;
40 ifs.close();
41 }
42 catch (const std::exception& e)
43 {
44 auto err = errno;
45 throw std::system_error(err, std::generic_category());
46 }
47
48 return data;
49}
50
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053051void Manager::findAndCreateObjects()
52{
Deepak Kodihalli370f06b2017-10-25 04:26:07 -050053 for (auto id = 0; id < MAX_CPUS; ++id)
54 {
Deepak Kodihalli30417a12017-12-04 00:54:01 -060055 // Create one occ per cpu
56 auto occ = std::string(OCC_NAME) + std::to_string(id);
57 createObjects(occ);
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053058 }
59}
60
61int Manager::cpuCreated(sdbusplus::message::message& msg)
62{
63 namespace fs = std::experimental::filesystem;
64
65 sdbusplus::message::object_path o;
66 msg.read(o);
67 fs::path cpuPath(std::string(std::move(o)));
68
69 auto name = cpuPath.filename().string();
70 auto index = name.find(CPU_NAME);
71 name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
72
73 createObjects(name);
74
75 return 0;
76}
77
78void Manager::createObjects(const std::string& occ)
79{
80 auto path = fs::path(OCC_CONTROL_ROOT) / occ;
81
82 passThroughObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +080083 std::make_unique<PassThrough>(path.c_str()));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053084
Gunnar Mills94df8c92018-09-14 14:50:03 -050085 statusObjects.emplace_back(std::make_unique<Status>(
George Liuf3b75142021-06-10 11:22:50 +080086 event, path.c_str(), *this,
Gunnar Mills94df8c92018-09-14 14:50:03 -050087 std::bind(std::mem_fn(&Manager::statusCallBack), this,
Tom Joseph00325232020-07-29 17:51:48 +053088 std::placeholders::_1)
89#ifdef PLDM
90 ,
91 std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
92 std::placeholders::_1)
93#endif
94 ));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053095
96 // Create the power cap monitor object for master occ (0)
97 if (!pcap)
98 {
99 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800100 *statusObjects.front());
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530101 }
Chris Cain78e86012021-03-04 16:15:31 -0600102
103#ifdef POWER10
104 // Create the power mode monitor object for master occ (0)
105 if (!pmode)
106 {
107 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
108 *statusObjects.front());
109 }
110#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530111}
112
113void Manager::statusCallBack(bool status)
114{
Gunnar Mills94df8c92018-09-14 14:50:03 -0500115 using InternalFailure =
116 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530117
118 // At this time, it won't happen but keeping it
119 // here just in case something changes in the future
120 if ((activeCount == 0) && (!status))
121 {
122 log<level::ERR>("Invalid update on OCCActive");
123 elog<InternalFailure>();
124 }
125
126 activeCount += status ? 1 : -1;
Eddie Jamesdae2d942017-12-20 10:50:03 -0600127
128 // Only start presence detection if all the OCCs are bound
129 if (activeCount == statusObjects.size())
130 {
Gunnar Mills94df8c92018-09-14 14:50:03 -0500131 for (auto& obj : statusObjects)
Eddie Jamesdae2d942017-12-20 10:50:03 -0600132 {
133 obj->addPresenceWatchMaster();
134 }
135 }
Chris Caina8857c52021-01-27 11:53:05 -0600136
137 if ((!_pollTimer->isEnabled()) && (activeCount > 0))
138 {
139 log<level::INFO>(fmt::format("Manager::statusCallBack(): {} OCCs will "
140 "be polled every {} seconds",
141 activeCount, pollInterval)
142 .c_str());
143
144 // Send poll and start OCC poll timer
145 pollerTimerExpired();
146 }
147 else if ((_pollTimer->isEnabled()) && (activeCount == 0))
148 {
149 // Stop OCC poll timer
150 log<level::INFO>("Manager::statusCallBack(): OCCs are not running, "
151 "stopping poll timer");
152 _pollTimer->setEnabled(false);
Matt Spinler53f68142021-08-25 15:47:31 -0500153
154#ifdef READ_OCC_SENSORS
155 for (auto& obj : statusObjects)
156 {
157 setSensorValueToNaN(obj->getOccInstanceID());
158 }
159#endif
Chris Caina8857c52021-01-27 11:53:05 -0600160 }
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530161}
162
163#ifdef I2C_OCC
164void Manager::initStatusObjects()
165{
166 // Make sure we have a valid path string
167 static_assert(sizeof(DEV_PATH) != 0);
168
169 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
Lei YU41470e52017-11-30 16:03:50 +0800170 auto occMasterName = deviceNames.front();
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530171 for (auto& name : deviceNames)
172 {
173 i2c_occ::i2cToDbus(name);
Lei YUb5259a12017-09-01 16:22:40 +0800174 name = std::string(OCC_NAME) + '_' + name;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530175 auto path = fs::path(OCC_CONTROL_ROOT) / name;
176 statusObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +0800177 std::make_unique<Status>(event, path.c_str(), *this));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530178 }
Lei YU41470e52017-11-30 16:03:50 +0800179 // The first device is master occ
180 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800181 *statusObjects.front(), occMasterName);
Chris Cain78e86012021-03-04 16:15:31 -0600182#ifdef POWER10
183 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
184 *statusObjects.front());
185#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530186}
187#endif
188
Tom Joseph815f9f52020-07-27 12:12:13 +0530189#ifdef PLDM
190bool Manager::updateOCCActive(instanceID instance, bool status)
191{
192 return (statusObjects[instance])->occActive(status);
193}
194#endif
195
Chris Caina8857c52021-01-27 11:53:05 -0600196void Manager::pollerTimerExpired()
197{
198 if (activeCount == 0)
199 {
200 // No OCCs running, so poll timer will not be restarted
201 log<level::INFO>("Manager::pollerTimerExpire(): No OCCs running, poll "
202 "timer not restarted");
203 }
204
205 if (!_pollTimer)
206 {
207 log<level::ERR>(
208 "Manager::pollerTimerExpired() ERROR: Timer not defined");
209 return;
210 }
211
212 for (auto& obj : statusObjects)
213 {
214 // Read sysfs to force kernel to poll OCC
215 obj->readOccState();
Chicago Duanbb895cb2021-06-18 19:37:16 +0800216
217#ifdef READ_OCC_SENSORS
218 // Read occ sensor values
219 auto id = obj->getOccInstanceID();
220 if (!obj->occActive())
221 {
222 // Occ not activated
223 setSensorValueToNaN(id);
224 continue;
225 }
226 getSensorValues(id, obj->isMasterOcc());
227#endif
Chris Caina8857c52021-01-27 11:53:05 -0600228 }
229
230 // Restart OCC poll timer
231 _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
232}
233
Chicago Duanbb895cb2021-06-18 19:37:16 +0800234#ifdef READ_OCC_SENSORS
235void Manager::readTempSensors(const fs::path& path, uint32_t id)
236{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800237 std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
238 for (auto& file : fs::directory_iterator(path))
239 {
240 if (!std::regex_search(file.path().string(), expr))
241 {
242 continue;
243 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800244
Matt Spinlera26f1522021-08-25 15:50:20 -0500245 uint32_t labelValue{0};
246
247 try
248 {
249 labelValue = readFile<uint32_t>(file.path());
250 }
251 catch (const std::system_error& e)
252 {
253 log<level::DEBUG>(
254 fmt::format("readTempSensors: Failed reading {}, errno = {}",
255 file.path().string(), e.code().value())
256 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800257 continue;
258 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800259
260 const std::string& tempLabel = "label";
261 const std::string filePathString = file.path().string().substr(
262 0, file.path().string().length() - tempLabel.length());
Matt Spinlera26f1522021-08-25 15:50:20 -0500263
264 uint32_t fruTypeValue{0};
265 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800266 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500267 fruTypeValue = readFile<uint32_t>(filePathString + fruTypeSuffix);
268 }
269 catch (const std::system_error& e)
270 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800271 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500272 fmt::format("readTempSensors: Failed reading {}, errno = {}",
273 filePathString + fruTypeSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800274 .c_str());
275 continue;
276 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800277
278 std::string sensorPath =
279 OCC_SENSORS_ROOT + std::string("/temperature/");
280
281 if (fruTypeValue == VRMVdd)
282 {
283 sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
284 }
285 else
286 {
Matt Spinler14d14022021-08-25 15:38:29 -0500287 uint16_t type = (labelValue & 0xFF000000) >> 24;
288 uint16_t instanceID = labelValue & 0x0000FFFF;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800289
290 if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
291 {
Matt Spinler8b8abee2021-08-25 15:18:21 -0500292 if (fruTypeValue == fruTypeNotAvailable)
293 {
294 // Not all DIMM related temps are available to read
295 // (no _input file in this case)
296 continue;
297 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800298 auto iter = dimmTempSensorName.find(fruTypeValue);
299 if (iter == dimmTempSensorName.end())
300 {
301 log<level::ERR>(fmt::format("readTempSensors: Fru type "
302 "error! fruTypeValue = {}) ",
303 fruTypeValue)
304 .c_str());
305 continue;
306 }
307
308 sensorPath.append("dimm" + std::to_string(instanceID) +
309 iter->second);
310 }
311 else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
312 {
313 if (fruTypeValue != processorCore)
314 {
Matt Spinlerabbcfc52021-08-25 15:22:22 -0500315 // TODO: support IO ring temp
Chicago Duanbb895cb2021-06-18 19:37:16 +0800316 continue;
317 }
318
319 sensorPath.append("proc" + std::to_string(id) + "_core" +
320 std::to_string(instanceID) + "_temp");
321 }
322 else
323 {
324 continue;
325 }
326 }
327
Matt Spinlera26f1522021-08-25 15:50:20 -0500328 uint32_t faultValue{0};
329 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800330 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500331 faultValue = readFile<uint32_t>(filePathString + faultSuffix);
332 }
333 catch (const std::system_error& e)
334 {
335 log<level::DEBUG>(
336 fmt::format("readTempSensors: Failed reading {}, errno = {}",
337 filePathString + faultSuffix, e.code().value())
338 .c_str());
339 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800340 }
341
Matt Spinlera26f1522021-08-25 15:50:20 -0500342 if (faultValue != 0)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800343 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800344 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
Matt Spinlera26f1522021-08-25 15:50:20 -0500345 sensorPath, std::numeric_limits<double>::quiet_NaN());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800346
347 open_power::occ::dbus::OccDBusSensors::getOccDBus()
Matt Spinlera26f1522021-08-25 15:50:20 -0500348 .setOperationalStatus(sensorPath, false);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800349
Matt Spinlera26f1522021-08-25 15:50:20 -0500350 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800351 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500352
353 double tempValue{0};
354
355 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800356 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500357 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800358 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500359 catch (const std::system_error& e)
360 {
361 log<level::DEBUG>(
362 fmt::format("readTempSensors: Failed reading {}, errno = {}",
363 filePathString + inputSuffix, e.code().value())
364 .c_str());
365 continue;
366 }
367
368 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
369 sensorPath, tempValue * std::pow(10, -3));
370
371 open_power::occ::dbus::OccDBusSensors::getOccDBus()
372 .setOperationalStatus(sensorPath, true);
373
374 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800375 }
376 return;
377}
378
379std::optional<std::string>
380 Manager::getPowerLabelFunctionID(const std::string& value)
381{
382 // If the value is "system", then the FunctionID is "system".
383 if (value == "system")
384 {
385 return value;
386 }
387
388 // If the value is not "system", then the label value have 3 numbers, of
389 // which we only care about the middle one:
390 // <sensor id>_<function id>_<apss channel>
391 // eg: The value is "0_10_5" , then the FunctionID is "10".
392 if (value.find("_") == std::string::npos)
393 {
394 return std::nullopt;
395 }
396
397 auto powerLabelValue = value.substr((value.find("_") + 1));
398
399 if (powerLabelValue.find("_") == std::string::npos)
400 {
401 return std::nullopt;
402 }
403
404 return powerLabelValue.substr(0, powerLabelValue.find("_"));
405}
406
407void Manager::readPowerSensors(const fs::path& path, uint32_t id)
408{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800409 std::regex expr{"power\\d+_label$"}; // Example: power5_label
410 for (auto& file : fs::directory_iterator(path))
411 {
412 if (!std::regex_search(file.path().string(), expr))
413 {
414 continue;
415 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800416
Matt Spinlera26f1522021-08-25 15:50:20 -0500417 std::string labelValue;
418 try
419 {
420 labelValue = readFile<std::string>(file.path());
421 }
422 catch (const std::system_error& e)
423 {
424 log<level::DEBUG>(
425 fmt::format("readPowerSensors: Failed reading {}, errno = {}",
426 file.path().string(), e.code().value())
427 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800428 continue;
429 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800430
431 auto functionID = getPowerLabelFunctionID(labelValue);
432 if (functionID == std::nullopt)
433 {
434 continue;
435 }
436
437 const std::string& tempLabel = "label";
438 const std::string filePathString = file.path().string().substr(
439 0, file.path().string().length() - tempLabel.length());
440
441 std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
442
443 auto iter = powerSensorName.find(*functionID);
444 if (iter == powerSensorName.end())
445 {
446 continue;
447 }
448 sensorPath.append(iter->second);
449
Matt Spinlera26f1522021-08-25 15:50:20 -0500450 double tempValue{0};
451
452 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800453 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500454 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800455 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500456 catch (const std::system_error& e)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800457 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800458 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500459 fmt::format("readTempSensors: Failed reading {}, errno = {}",
460 filePathString + inputSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800461 .c_str());
Matt Spinlera26f1522021-08-25 15:50:20 -0500462 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800463 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500464
465 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
466 sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
467
468 open_power::occ::dbus::OccDBusSensors::getOccDBus()
469 .setOperationalStatus(sensorPath, true);
470
471 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800472 }
473 return;
474}
475
476void Manager::setSensorValueToNaN(uint32_t id)
477{
478 for (const auto& [sensorPath, occId] : existingSensors)
479 {
480 if (occId == id)
481 {
482 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
483 sensorPath, std::numeric_limits<double>::quiet_NaN());
484 }
485 }
486 return;
487}
488
489void Manager::getSensorValues(uint32_t id, bool masterOcc)
490{
491 const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1);
492
493 fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"};
494
495 // Need to get the hwmonXX directory name, there better only be 1 dir
496 assert(std::distance(fs::directory_iterator(fileName),
497 fs::directory_iterator{}) == 1);
498 // Now set our path to this full path, including this hwmonXX directory
499 fileName = fs::path(*fs::directory_iterator(fileName));
500
501 // Read temperature sensors
502 readTempSensors(fileName, id);
503
504 if (masterOcc)
505 {
506 // Read power sensors
507 readPowerSensors(fileName, id);
508 }
509
510 return;
511}
512#endif
513
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530514} // namespace occ
515} // namespace open_power