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