blob: 0b1ec10d6c07d7f8742e697634af976c72dab98a [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;
22
Chris Caina8857c52021-01-27 11:53:05 -060023using namespace phosphor::logging;
24
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053025void Manager::findAndCreateObjects()
26{
Deepak Kodihalli370f06b2017-10-25 04:26:07 -050027 for (auto id = 0; id < MAX_CPUS; ++id)
28 {
Deepak Kodihalli30417a12017-12-04 00:54:01 -060029 // Create one occ per cpu
30 auto occ = std::string(OCC_NAME) + std::to_string(id);
31 createObjects(occ);
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053032 }
33}
34
35int Manager::cpuCreated(sdbusplus::message::message& msg)
36{
37 namespace fs = std::experimental::filesystem;
38
39 sdbusplus::message::object_path o;
40 msg.read(o);
41 fs::path cpuPath(std::string(std::move(o)));
42
43 auto name = cpuPath.filename().string();
44 auto index = name.find(CPU_NAME);
45 name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
46
47 createObjects(name);
48
49 return 0;
50}
51
52void Manager::createObjects(const std::string& occ)
53{
54 auto path = fs::path(OCC_CONTROL_ROOT) / occ;
55
56 passThroughObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +080057 std::make_unique<PassThrough>(path.c_str()));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053058
Gunnar Mills94df8c92018-09-14 14:50:03 -050059 statusObjects.emplace_back(std::make_unique<Status>(
George Liuf3b75142021-06-10 11:22:50 +080060 event, path.c_str(), *this,
Gunnar Mills94df8c92018-09-14 14:50:03 -050061 std::bind(std::mem_fn(&Manager::statusCallBack), this,
Tom Joseph00325232020-07-29 17:51:48 +053062 std::placeholders::_1)
63#ifdef PLDM
64 ,
65 std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
66 std::placeholders::_1)
67#endif
68 ));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053069
70 // Create the power cap monitor object for master occ (0)
71 if (!pcap)
72 {
73 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +080074 *statusObjects.front());
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053075 }
Chris Cain78e86012021-03-04 16:15:31 -060076
77#ifdef POWER10
78 // Create the power mode monitor object for master occ (0)
79 if (!pmode)
80 {
81 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
82 *statusObjects.front());
83 }
84#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053085}
86
87void Manager::statusCallBack(bool status)
88{
Gunnar Mills94df8c92018-09-14 14:50:03 -050089 using InternalFailure =
90 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053091
92 // At this time, it won't happen but keeping it
93 // here just in case something changes in the future
94 if ((activeCount == 0) && (!status))
95 {
96 log<level::ERR>("Invalid update on OCCActive");
97 elog<InternalFailure>();
98 }
99
100 activeCount += status ? 1 : -1;
Eddie Jamesdae2d942017-12-20 10:50:03 -0600101
102 // Only start presence detection if all the OCCs are bound
103 if (activeCount == statusObjects.size())
104 {
Gunnar Mills94df8c92018-09-14 14:50:03 -0500105 for (auto& obj : statusObjects)
Eddie Jamesdae2d942017-12-20 10:50:03 -0600106 {
107 obj->addPresenceWatchMaster();
108 }
109 }
Chris Caina8857c52021-01-27 11:53:05 -0600110
111 if ((!_pollTimer->isEnabled()) && (activeCount > 0))
112 {
113 log<level::INFO>(fmt::format("Manager::statusCallBack(): {} OCCs will "
114 "be polled every {} seconds",
115 activeCount, pollInterval)
116 .c_str());
117
118 // Send poll and start OCC poll timer
119 pollerTimerExpired();
120 }
121 else if ((_pollTimer->isEnabled()) && (activeCount == 0))
122 {
123 // Stop OCC poll timer
124 log<level::INFO>("Manager::statusCallBack(): OCCs are not running, "
125 "stopping poll timer");
126 _pollTimer->setEnabled(false);
Matt Spinler53f68142021-08-25 15:47:31 -0500127
128#ifdef READ_OCC_SENSORS
129 for (auto& obj : statusObjects)
130 {
131 setSensorValueToNaN(obj->getOccInstanceID());
132 }
133#endif
Chris Caina8857c52021-01-27 11:53:05 -0600134 }
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530135}
136
137#ifdef I2C_OCC
138void Manager::initStatusObjects()
139{
140 // Make sure we have a valid path string
141 static_assert(sizeof(DEV_PATH) != 0);
142
143 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
Lei YU41470e52017-11-30 16:03:50 +0800144 auto occMasterName = deviceNames.front();
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530145 for (auto& name : deviceNames)
146 {
147 i2c_occ::i2cToDbus(name);
Lei YUb5259a12017-09-01 16:22:40 +0800148 name = std::string(OCC_NAME) + '_' + name;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530149 auto path = fs::path(OCC_CONTROL_ROOT) / name;
150 statusObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +0800151 std::make_unique<Status>(event, path.c_str(), *this));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530152 }
Lei YU41470e52017-11-30 16:03:50 +0800153 // The first device is master occ
154 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800155 *statusObjects.front(), occMasterName);
Chris Cain78e86012021-03-04 16:15:31 -0600156#ifdef POWER10
157 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(
158 *statusObjects.front());
159#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530160}
161#endif
162
Tom Joseph815f9f52020-07-27 12:12:13 +0530163#ifdef PLDM
164bool Manager::updateOCCActive(instanceID instance, bool status)
165{
166 return (statusObjects[instance])->occActive(status);
167}
168#endif
169
Chris Caina8857c52021-01-27 11:53:05 -0600170void Manager::pollerTimerExpired()
171{
172 if (activeCount == 0)
173 {
174 // No OCCs running, so poll timer will not be restarted
175 log<level::INFO>("Manager::pollerTimerExpire(): No OCCs running, poll "
176 "timer not restarted");
177 }
178
179 if (!_pollTimer)
180 {
181 log<level::ERR>(
182 "Manager::pollerTimerExpired() ERROR: Timer not defined");
183 return;
184 }
185
186 for (auto& obj : statusObjects)
187 {
188 // Read sysfs to force kernel to poll OCC
189 obj->readOccState();
Chicago Duanbb895cb2021-06-18 19:37:16 +0800190
191#ifdef READ_OCC_SENSORS
192 // Read occ sensor values
193 auto id = obj->getOccInstanceID();
194 if (!obj->occActive())
195 {
196 // Occ not activated
197 setSensorValueToNaN(id);
198 continue;
199 }
200 getSensorValues(id, obj->isMasterOcc());
201#endif
Chris Caina8857c52021-01-27 11:53:05 -0600202 }
203
204 // Restart OCC poll timer
205 _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
206}
207
Chicago Duanbb895cb2021-06-18 19:37:16 +0800208#ifdef READ_OCC_SENSORS
209void Manager::readTempSensors(const fs::path& path, uint32_t id)
210{
211 const int open_errno = errno;
212 std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
213 for (auto& file : fs::directory_iterator(path))
214 {
215 if (!std::regex_search(file.path().string(), expr))
216 {
217 continue;
218 }
219 std::ifstream fileOpen(file.path(), std::ios::in);
220 if (!fileOpen)
221 {
222 // If not able to read, OCC may be offline
223 log<level::DEBUG>(
224 fmt::format("readTempSensors: open failed(errno = {}) ",
225 open_errno)
226 .c_str());
227
228 continue;
229 }
Matt Spinler14d14022021-08-25 15:38:29 -0500230 uint32_t labelValue{0};
Chicago Duanbb895cb2021-06-18 19:37:16 +0800231 fileOpen >> labelValue;
232 fileOpen.close();
233
234 const std::string& tempLabel = "label";
235 const std::string filePathString = file.path().string().substr(
236 0, file.path().string().length() - tempLabel.length());
237 std::ifstream fruTypeFile(filePathString + "fru_type", std::ios::in);
238 if (!fruTypeFile)
239 {
240 // If not able to read, OCC may be offline
241 log<level::DEBUG>(
242 fmt::format("readTempSensors: open failed(errno = {}) ",
243 open_errno)
244 .c_str());
245 continue;
246 }
247 uint32_t fruTypeValue{0};
248 fruTypeFile >> fruTypeValue;
249 fruTypeFile.close();
250
251 std::string sensorPath =
252 OCC_SENSORS_ROOT + std::string("/temperature/");
253
254 if (fruTypeValue == VRMVdd)
255 {
256 sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
257 }
258 else
259 {
Matt Spinler14d14022021-08-25 15:38:29 -0500260 uint16_t type = (labelValue & 0xFF000000) >> 24;
261 uint16_t instanceID = labelValue & 0x0000FFFF;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800262
263 if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
264 {
Matt Spinler8b8abee2021-08-25 15:18:21 -0500265 if (fruTypeValue == fruTypeNotAvailable)
266 {
267 // Not all DIMM related temps are available to read
268 // (no _input file in this case)
269 continue;
270 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800271 auto iter = dimmTempSensorName.find(fruTypeValue);
272 if (iter == dimmTempSensorName.end())
273 {
274 log<level::ERR>(fmt::format("readTempSensors: Fru type "
275 "error! fruTypeValue = {}) ",
276 fruTypeValue)
277 .c_str());
278 continue;
279 }
280
281 sensorPath.append("dimm" + std::to_string(instanceID) +
282 iter->second);
283 }
284 else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
285 {
286 if (fruTypeValue != processorCore)
287 {
Matt Spinlerabbcfc52021-08-25 15:22:22 -0500288 // TODO: support IO ring temp
Chicago Duanbb895cb2021-06-18 19:37:16 +0800289 continue;
290 }
291
292 sensorPath.append("proc" + std::to_string(id) + "_core" +
293 std::to_string(instanceID) + "_temp");
294 }
295 else
296 {
297 continue;
298 }
299 }
300
301 std::ifstream faultPathFile(filePathString + "fault", std::ios::in);
302 if (faultPathFile)
303 {
304 uint32_t faultValue;
305 faultPathFile >> faultValue;
306 faultPathFile.close();
307
308 if (faultValue != 0)
309 {
310 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
311 sensorPath, std::numeric_limits<double>::quiet_NaN());
312
313 open_power::occ::dbus::OccDBusSensors::getOccDBus()
314 .setOperationalStatus(sensorPath, false);
315
316 continue;
317 }
318 }
319
320 std::ifstream inputFile(filePathString + "input", std::ios::in);
321 if (inputFile)
322 {
323 double tempValue;
324 inputFile >> tempValue;
325
326 inputFile.close();
327
328 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
329 sensorPath, tempValue * std::pow(10, -3));
330
331 open_power::occ::dbus::OccDBusSensors::getOccDBus()
332 .setOperationalStatus(sensorPath, true);
333
334 existingSensors[sensorPath] = id;
335 }
336 else
337 {
338 // If not able to read, OCC may be offline
339 log<level::DEBUG>(
340 fmt::format("readTempSensors: open failed(errno = {}) ",
341 open_errno)
342 .c_str());
343 }
344 }
345 return;
346}
347
348std::optional<std::string>
349 Manager::getPowerLabelFunctionID(const std::string& value)
350{
351 // If the value is "system", then the FunctionID is "system".
352 if (value == "system")
353 {
354 return value;
355 }
356
357 // If the value is not "system", then the label value have 3 numbers, of
358 // which we only care about the middle one:
359 // <sensor id>_<function id>_<apss channel>
360 // eg: The value is "0_10_5" , then the FunctionID is "10".
361 if (value.find("_") == std::string::npos)
362 {
363 return std::nullopt;
364 }
365
366 auto powerLabelValue = value.substr((value.find("_") + 1));
367
368 if (powerLabelValue.find("_") == std::string::npos)
369 {
370 return std::nullopt;
371 }
372
373 return powerLabelValue.substr(0, powerLabelValue.find("_"));
374}
375
376void Manager::readPowerSensors(const fs::path& path, uint32_t id)
377{
378 const int open_errno = errno;
379 std::regex expr{"power\\d+_label$"}; // Example: power5_label
380 for (auto& file : fs::directory_iterator(path))
381 {
382 if (!std::regex_search(file.path().string(), expr))
383 {
384 continue;
385 }
386 std::ifstream fileOpen(file.path(), std::ios::in);
387 if (!fileOpen)
388 {
389 // If not able to read, OCC may be offline
390 log<level::DEBUG>(
391 fmt::format("readPowerSensors: open failed(errno = {}) ",
392 open_errno)
393 .c_str());
394
395 continue;
396 }
397 std::string labelValue;
398 fileOpen >> labelValue;
399 fileOpen.close();
400
401 auto functionID = getPowerLabelFunctionID(labelValue);
402 if (functionID == std::nullopt)
403 {
404 continue;
405 }
406
407 const std::string& tempLabel = "label";
408 const std::string filePathString = file.path().string().substr(
409 0, file.path().string().length() - tempLabel.length());
410
411 std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
412
413 auto iter = powerSensorName.find(*functionID);
414 if (iter == powerSensorName.end())
415 {
416 continue;
417 }
418 sensorPath.append(iter->second);
419
420 std::ifstream faultPathFile(filePathString + "fault", std::ios::in);
421 if (faultPathFile)
422 {
423 uint32_t faultValue{0};
424 faultPathFile >> faultValue;
425 faultPathFile.close();
426
427 if (faultValue != 0)
428 {
429 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
430 sensorPath, std::numeric_limits<double>::quiet_NaN());
431
432 open_power::occ::dbus::OccDBusSensors::getOccDBus()
433 .setOperationalStatus(sensorPath, false);
434
435 continue;
436 }
437 }
438
439 std::ifstream inputFile(filePathString + "input", std::ios::in);
440 if (inputFile)
441 {
442 double tempValue;
443 inputFile >> tempValue;
444 inputFile.close();
445
446 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
447 sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
448
449 open_power::occ::dbus::OccDBusSensors::getOccDBus()
450 .setOperationalStatus(sensorPath, true);
451
452 existingSensors[sensorPath] = id;
453 }
454 else
455 {
456 // If not able to read, OCC may be offline
457 log<level::DEBUG>(
458 fmt::format("readPowerSensors: open failed(errno = {}) ",
459 open_errno)
460 .c_str());
461 }
462 }
463 return;
464}
465
466void Manager::setSensorValueToNaN(uint32_t id)
467{
468 for (const auto& [sensorPath, occId] : existingSensors)
469 {
470 if (occId == id)
471 {
472 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
473 sensorPath, std::numeric_limits<double>::quiet_NaN());
474 }
475 }
476 return;
477}
478
479void Manager::getSensorValues(uint32_t id, bool masterOcc)
480{
481 const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1);
482
483 fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"};
484
485 // Need to get the hwmonXX directory name, there better only be 1 dir
486 assert(std::distance(fs::directory_iterator(fileName),
487 fs::directory_iterator{}) == 1);
488 // Now set our path to this full path, including this hwmonXX directory
489 fileName = fs::path(*fs::directory_iterator(fileName));
490
491 // Read temperature sensors
492 readTempSensors(fileName, id);
493
494 if (masterOcc)
495 {
496 // Read power sensors
497 readPowerSensors(fileName, id);
498 }
499
500 return;
501}
502#endif
503
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530504} // namespace occ
505} // namespace open_power