blob: ee8575dd2f2904db558980e4e5d6413ffc4f8b60 [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
George Liub5ca1012021-09-10 12:53:11 +08009#include <phosphor-logging/elog-errors.hpp>
10#include <phosphor-logging/log.hpp>
11#include <xyz/openbmc_project/Common/error.hpp>
12
Matt Spinlerd267cec2021-09-01 14:49:19 -050013#include <chrono>
Chicago Duanbb895cb2021-06-18 19:37:16 +080014#include <cmath>
George Liubcef3b42021-09-10 12:39:02 +080015#include <filesystem>
Chris Cain36f9cde2021-11-22 11:18:21 -060016#include <fstream>
Chicago Duanbb895cb2021-06-18 19:37:16 +080017#include <regex>
Gunnar Mills94df8c92018-09-14 14:50:03 -050018
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053019namespace open_power
20{
21namespace occ
22{
23
Matt Spinler8b8abee2021-08-25 15:18:21 -050024constexpr uint32_t fruTypeNotAvailable = 0xFF;
Matt Spinlera26f1522021-08-25 15:50:20 -050025constexpr auto fruTypeSuffix = "fru_type";
26constexpr auto faultSuffix = "fault";
27constexpr auto inputSuffix = "input";
Matt Spinlerace67d82021-10-18 13:41:57 -050028constexpr auto maxSuffix = "max";
Matt Spinler8b8abee2021-08-25 15:18:21 -050029
Chris Caina8857c52021-01-27 11:53:05 -060030using namespace phosphor::logging;
Chris Caina7b74dc2021-11-10 17:03:43 -060031using namespace std::literals::chrono_literals;
Chris Caina8857c52021-01-27 11:53:05 -060032
Matt Spinlera26f1522021-08-25 15:50:20 -050033template <typename T>
34T readFile(const std::string& path)
35{
36 std::ifstream ifs;
37 ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
38 std::ifstream::eofbit);
39 T data;
40
41 try
42 {
43 ifs.open(path);
44 ifs >> data;
45 ifs.close();
46 }
47 catch (const std::exception& e)
48 {
49 auto err = errno;
50 throw std::system_error(err, std::generic_category());
51 }
52
53 return data;
54}
55
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053056void Manager::findAndCreateObjects()
57{
Matt Spinlerd267cec2021-09-01 14:49:19 -050058#ifndef POWER10
Deepak Kodihalli370f06b2017-10-25 04:26:07 -050059 for (auto id = 0; id < MAX_CPUS; ++id)
60 {
Deepak Kodihalli30417a12017-12-04 00:54:01 -060061 // Create one occ per cpu
62 auto occ = std::string(OCC_NAME) + std::to_string(id);
63 createObjects(occ);
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +053064 }
Matt Spinlerd267cec2021-09-01 14:49:19 -050065#else
66 // Create the OCCs based on on the /dev/occX devices
67 auto occs = findOCCsInDev();
68
69 if (occs.empty() || (prevOCCSearch.size() != occs.size()))
70 {
71 // Something changed or no OCCs yet, try again in 10s.
72 // Note on the first pass prevOCCSearch will be empty,
73 // so there will be at least one delay to give things
74 // a chance to settle.
75 prevOCCSearch = occs;
76
Matt Spinlerd267cec2021-09-01 14:49:19 -050077 discoverTimer->restartOnce(10s);
78 }
79 else
80 {
81 discoverTimer.reset();
82
83 // createObjects requires OCC0 first.
84 std::sort(occs.begin(), occs.end());
85
86 for (auto id : occs)
87 {
88 createObjects(std::string(OCC_NAME) + std::to_string(id));
89 }
90 }
91#endif
92}
93
94std::vector<int> Manager::findOCCsInDev()
95{
96 std::vector<int> occs;
97 std::regex expr{R"(occ(\d+)$)"};
98
99 for (auto& file : fs::directory_iterator("/dev"))
100 {
101 std::smatch match;
102 std::string path{file.path().string()};
103 if (std::regex_search(path, match, expr))
104 {
105 auto num = std::stoi(match[1].str());
106
107 // /dev numbering starts at 1, ours starts at 0.
108 occs.push_back(num - 1);
109 }
110 }
111
112 return occs;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530113}
114
115int Manager::cpuCreated(sdbusplus::message::message& msg)
116{
George Liubcef3b42021-09-10 12:39:02 +0800117 namespace fs = std::filesystem;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530118
119 sdbusplus::message::object_path o;
120 msg.read(o);
121 fs::path cpuPath(std::string(std::move(o)));
122
123 auto name = cpuPath.filename().string();
124 auto index = name.find(CPU_NAME);
125 name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
126
127 createObjects(name);
128
129 return 0;
130}
131
132void Manager::createObjects(const std::string& occ)
133{
134 auto path = fs::path(OCC_CONTROL_ROOT) / occ;
135
Chris Cain6fa848a2022-01-24 14:54:38 -0600136#ifdef POWER10
137 if (!pmode)
138 {
139 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(*this);
140 }
141#endif
142
Gunnar Mills94df8c92018-09-14 14:50:03 -0500143 statusObjects.emplace_back(std::make_unique<Status>(
George Liuf3b75142021-06-10 11:22:50 +0800144 event, path.c_str(), *this,
Chris Cain36f9cde2021-11-22 11:18:21 -0600145#ifdef POWER10
146 pmode,
147#endif
Gunnar Mills94df8c92018-09-14 14:50:03 -0500148 std::bind(std::mem_fn(&Manager::statusCallBack), this,
Tom Joseph00325232020-07-29 17:51:48 +0530149 std::placeholders::_1)
150#ifdef PLDM
151 ,
152 std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
153 std::placeholders::_1)
154#endif
155 ));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530156
Chris Cain36f9cde2021-11-22 11:18:21 -0600157 if (statusObjects.back()->isMasterOcc())
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530158 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600159 log<level::INFO>(
160 fmt::format("Manager::createObjects(): OCC{} is the master",
161 statusObjects.back()->getOccInstanceID())
162 .c_str());
163 _pollTimer->setEnabled(false);
164
165 // Create the power cap monitor object for master OCC
166 if (!pcap)
167 {
168 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
169 *statusObjects.front());
170 }
Chris Cain78e86012021-03-04 16:15:31 -0600171
172#ifdef POWER10
Chris Cain6fa848a2022-01-24 14:54:38 -0600173 // Set the master OCC on the PowerMode object
174 pmode->setMasterOcc(path);
Chris Cain78e86012021-03-04 16:15:31 -0600175#endif
Chris Cain36f9cde2021-11-22 11:18:21 -0600176 }
177
178 passThroughObjects.emplace_back(std::make_unique<PassThrough>(path.c_str()
179#ifdef POWER10
180 ,
181 pmode
182#endif
183 ));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530184}
185
186void Manager::statusCallBack(bool status)
187{
Gunnar Mills94df8c92018-09-14 14:50:03 -0500188 using InternalFailure =
189 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530190
191 // At this time, it won't happen but keeping it
192 // here just in case something changes in the future
193 if ((activeCount == 0) && (!status))
194 {
195 log<level::ERR>("Invalid update on OCCActive");
196 elog<InternalFailure>();
197 }
198
Chris Caina7b74dc2021-11-10 17:03:43 -0600199 if (status == true)
Eddie Jamesdae2d942017-12-20 10:50:03 -0600200 {
Chris Caina7b74dc2021-11-10 17:03:43 -0600201 // OCC went active
202 ++activeCount;
203
204#ifdef POWER10
205 if (activeCount == 1)
Eddie Jamesdae2d942017-12-20 10:50:03 -0600206 {
Chris Caina7b74dc2021-11-10 17:03:43 -0600207 // First OCC went active (allow some time for all OCCs to go active)
208 waitForAllOccsTimer->restartOnce(30s);
Matt Spinler53f68142021-08-25 15:47:31 -0500209 }
210#endif
Chris Caina7b74dc2021-11-10 17:03:43 -0600211
212 if (activeCount == statusObjects.size())
213 {
214#ifdef POWER10
215 // All OCCs are now running
216 if (waitForAllOccsTimer->isEnabled())
217 {
218 // stop occ wait timer
219 waitForAllOccsTimer->setEnabled(false);
220 }
221#endif
222
223 // Verify master OCC and start presence monitor
224 validateOccMaster();
225 }
226
227 // Start poll timer if not already started
228 if (!_pollTimer->isEnabled())
229 {
230 log<level::INFO>(
Chris Cain36f9cde2021-11-22 11:18:21 -0600231 fmt::format("Manager: OCCs will be polled every {} seconds",
232 pollInterval)
Chris Caina7b74dc2021-11-10 17:03:43 -0600233 .c_str());
234
235 // Send poll and start OCC poll timer
236 pollerTimerExpired();
237 }
238 }
239 else
240 {
241 // OCC went away
242 --activeCount;
243
244 if (activeCount == 0)
245 {
246 // No OCCs are running
247
248 // Stop OCC poll timer
249 if (_pollTimer->isEnabled())
250 {
251 log<level::INFO>(
252 "Manager::statusCallBack(): OCCs are not running, stopping poll timer");
253 _pollTimer->setEnabled(false);
254 }
255
256#ifdef POWER10
257 // stop wait timer
258 if (waitForAllOccsTimer->isEnabled())
259 {
260 waitForAllOccsTimer->setEnabled(false);
261 }
262#endif
263
264#ifdef READ_OCC_SENSORS
265 // Clear OCC sensors
266 for (auto& obj : statusObjects)
267 {
268 setSensorValueToNaN(obj->getOccInstanceID());
269 }
270#endif
271 }
Chris Caina8857c52021-01-27 11:53:05 -0600272 }
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530273}
274
275#ifdef I2C_OCC
276void Manager::initStatusObjects()
277{
278 // Make sure we have a valid path string
279 static_assert(sizeof(DEV_PATH) != 0);
280
281 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
Lei YU41470e52017-11-30 16:03:50 +0800282 auto occMasterName = deviceNames.front();
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530283 for (auto& name : deviceNames)
284 {
285 i2c_occ::i2cToDbus(name);
Lei YUb5259a12017-09-01 16:22:40 +0800286 name = std::string(OCC_NAME) + '_' + name;
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530287 auto path = fs::path(OCC_CONTROL_ROOT) / name;
288 statusObjects.emplace_back(
George Liuf3b75142021-06-10 11:22:50 +0800289 std::make_unique<Status>(event, path.c_str(), *this));
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530290 }
Lei YU41470e52017-11-30 16:03:50 +0800291 // The first device is master occ
292 pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
George Liuf3b75142021-06-10 11:22:50 +0800293 *statusObjects.front(), occMasterName);
Chris Cain78e86012021-03-04 16:15:31 -0600294#ifdef POWER10
Chris Cain6fa848a2022-01-24 14:54:38 -0600295 pmode = std::make_unique<open_power::occ::powermode::PowerMode>(*this);
296 // Set the master OCC on the PowerMode object
297 pmode->setMasterOcc(path);
Chris Cain78e86012021-03-04 16:15:31 -0600298#endif
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +0530299}
300#endif
301
Tom Joseph815f9f52020-07-27 12:12:13 +0530302#ifdef PLDM
Eddie Jamescbad2192021-10-07 09:39:39 -0500303void Manager::sbeTimeout(unsigned int instance)
304{
305 log<level::INFO>("SBE timeout, requesting HRESET",
306 entry("SBE=%d", instance));
307
308 setSBEState(instance, SBE_STATE_NOT_USABLE);
309
310 pldmHandle->sendHRESET(instance);
311}
312
Tom Joseph815f9f52020-07-27 12:12:13 +0530313bool Manager::updateOCCActive(instanceID instance, bool status)
314{
315 return (statusObjects[instance])->occActive(status);
316}
Eddie Jamescbad2192021-10-07 09:39:39 -0500317
318void Manager::sbeHRESETResult(instanceID instance, bool success)
319{
320 if (success)
321 {
322 log<level::INFO>("HRESET succeeded", entry("SBE=%d", instance));
323
324 setSBEState(instance, SBE_STATE_BOOTED);
325
326 return;
327 }
328
329 setSBEState(instance, SBE_STATE_FAILED);
330
331 if (sbeCanDump(instance))
332 {
Eddie Jamescbad2192021-10-07 09:39:39 -0500333 log<level::INFO>("HRESET failed, triggering SBE dump",
334 entry("SBE=%d", instance));
335
336 auto& bus = utils::getBus();
337 uint32_t src6 = instance << 16;
338 uint32_t logId =
339 FFDC::createPEL("org.open_power.Processor.Error.SbeChipOpTimeout",
340 src6, "SBE command timeout");
341
342 try
343 {
George Liuf3a4a692021-12-28 13:59:51 +0800344 constexpr auto path = "/org/openpower/dump";
345 constexpr auto interface = "xyz.openbmc_project.Dump.Create";
346 constexpr auto function = "CreateDump";
347
Eddie Jamescbad2192021-10-07 09:39:39 -0500348 std::string service = utils::getService(path, interface);
349 auto method =
350 bus.new_method_call(service.c_str(), path, interface, function);
351
352 std::map<std::string, std::variant<std::string, uint64_t>>
353 createParams{
354 {"com.ibm.Dump.Create.CreateParameters.ErrorLogId",
355 uint64_t(logId)},
356 {"com.ibm.Dump.Create.CreateParameters.DumpType",
357 "com.ibm.Dump.Create.DumpType.SBE"},
358 {"com.ibm.Dump.Create.CreateParameters.FailingUnitId",
359 uint64_t(instance)},
360 };
361
362 method.append(createParams);
363
364 auto response = bus.call(method);
365 }
366 catch (const sdbusplus::exception::exception& e)
367 {
368 constexpr auto ERROR_DUMP_DISABLED =
369 "xyz.openbmc_project.Dump.Create.Error.Disabled";
370 if (e.name() == ERROR_DUMP_DISABLED)
371 {
372 log<level::INFO>("Dump is disabled, skipping");
373 }
374 else
375 {
376 log<level::ERR>("Dump failed");
377 }
378 }
379 }
380}
381
382bool Manager::sbeCanDump(unsigned int instance)
383{
384 struct pdbg_target* proc = getPdbgTarget(instance);
385
386 if (!proc)
387 {
388 // allow the dump in the error case
389 return true;
390 }
391
392 try
393 {
394 if (!openpower::phal::sbe::isDumpAllowed(proc))
395 {
396 return false;
397 }
398
399 if (openpower::phal::pdbg::isSbeVitalAttnActive(proc))
400 {
401 return false;
402 }
403 }
404 catch (openpower::phal::exception::SbeError& e)
405 {
406 log<level::INFO>("Failed to query SBE state");
407 }
408
409 // allow the dump in the error case
410 return true;
411}
412
413void Manager::setSBEState(unsigned int instance, enum sbe_state state)
414{
415 struct pdbg_target* proc = getPdbgTarget(instance);
416
417 if (!proc)
418 {
419 return;
420 }
421
422 try
423 {
424 openpower::phal::sbe::setState(proc, state);
425 }
426 catch (const openpower::phal::exception::SbeError& e)
427 {
428 log<level::ERR>("Failed to set SBE state");
429 }
430}
431
432struct pdbg_target* Manager::getPdbgTarget(unsigned int instance)
433{
434 if (!pdbgInitialized)
435 {
436 try
437 {
438 openpower::phal::pdbg::init();
439 pdbgInitialized = true;
440 }
441 catch (const openpower::phal::exception::PdbgError& e)
442 {
443 log<level::ERR>("pdbg initialization failed");
444 return nullptr;
445 }
446 }
447
448 struct pdbg_target* proc = nullptr;
449 pdbg_for_each_class_target("proc", proc)
450 {
451 if (pdbg_target_index(proc) == instance)
452 {
453 return proc;
454 }
455 }
456
457 log<level::ERR>("Failed to get pdbg target");
458 return nullptr;
459}
Tom Joseph815f9f52020-07-27 12:12:13 +0530460#endif
461
Chris Caina8857c52021-01-27 11:53:05 -0600462void Manager::pollerTimerExpired()
463{
Chris Caina8857c52021-01-27 11:53:05 -0600464 if (!_pollTimer)
465 {
466 log<level::ERR>(
467 "Manager::pollerTimerExpired() ERROR: Timer not defined");
468 return;
469 }
470
471 for (auto& obj : statusObjects)
472 {
Chris Caina7b74dc2021-11-10 17:03:43 -0600473#ifdef READ_OCC_SENSORS
474 auto id = obj->getOccInstanceID();
475#endif
476 if (!obj->occActive())
477 {
478 // OCC is not running yet
479#ifdef READ_OCC_SENSORS
480 setSensorValueToNaN(id);
481#endif
482 continue;
483 }
484
Chris Caina8857c52021-01-27 11:53:05 -0600485 // Read sysfs to force kernel to poll OCC
486 obj->readOccState();
Chicago Duanbb895cb2021-06-18 19:37:16 +0800487
488#ifdef READ_OCC_SENSORS
489 // Read occ sensor values
Chicago Duanbb895cb2021-06-18 19:37:16 +0800490 getSensorValues(id, obj->isMasterOcc());
491#endif
Chris Caina8857c52021-01-27 11:53:05 -0600492 }
493
Chris Caina7b74dc2021-11-10 17:03:43 -0600494 if (activeCount > 0)
495 {
496 // Restart OCC poll timer
497 _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
498 }
499 else
500 {
501 // No OCCs running, so poll timer will not be restarted
502 log<level::INFO>(
503 fmt::format(
504 "Manager::pollerTimerExpired: poll timer will not be restarted")
505 .c_str());
506 }
Chris Caina8857c52021-01-27 11:53:05 -0600507}
508
Chicago Duanbb895cb2021-06-18 19:37:16 +0800509#ifdef READ_OCC_SENSORS
510void Manager::readTempSensors(const fs::path& path, uint32_t id)
511{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800512 std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
513 for (auto& file : fs::directory_iterator(path))
514 {
515 if (!std::regex_search(file.path().string(), expr))
516 {
517 continue;
518 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800519
Matt Spinlera26f1522021-08-25 15:50:20 -0500520 uint32_t labelValue{0};
521
522 try
523 {
524 labelValue = readFile<uint32_t>(file.path());
525 }
526 catch (const std::system_error& e)
527 {
528 log<level::DEBUG>(
529 fmt::format("readTempSensors: Failed reading {}, errno = {}",
530 file.path().string(), e.code().value())
531 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800532 continue;
533 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800534
535 const std::string& tempLabel = "label";
536 const std::string filePathString = file.path().string().substr(
537 0, file.path().string().length() - tempLabel.length());
Matt Spinlera26f1522021-08-25 15:50:20 -0500538
539 uint32_t fruTypeValue{0};
540 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800541 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500542 fruTypeValue = readFile<uint32_t>(filePathString + fruTypeSuffix);
543 }
544 catch (const std::system_error& e)
545 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800546 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500547 fmt::format("readTempSensors: Failed reading {}, errno = {}",
548 filePathString + fruTypeSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800549 .c_str());
550 continue;
551 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800552
553 std::string sensorPath =
554 OCC_SENSORS_ROOT + std::string("/temperature/");
555
Matt Spinlerace67d82021-10-18 13:41:57 -0500556 std::string dvfsTempPath;
557
Chicago Duanbb895cb2021-06-18 19:37:16 +0800558 if (fruTypeValue == VRMVdd)
559 {
560 sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp");
561 }
Matt Spinlerace67d82021-10-18 13:41:57 -0500562 else if (fruTypeValue == processorIoRing)
563 {
564 sensorPath.append("proc" + std::to_string(id) + "_ioring_temp");
565 dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/proc" +
566 std::to_string(id) + "_ioring_dvfs_temp";
567 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800568 else
569 {
Matt Spinler14d14022021-08-25 15:38:29 -0500570 uint16_t type = (labelValue & 0xFF000000) >> 24;
571 uint16_t instanceID = labelValue & 0x0000FFFF;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800572
573 if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
574 {
Matt Spinler8b8abee2021-08-25 15:18:21 -0500575 if (fruTypeValue == fruTypeNotAvailable)
576 {
577 // Not all DIMM related temps are available to read
578 // (no _input file in this case)
579 continue;
580 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800581 auto iter = dimmTempSensorName.find(fruTypeValue);
582 if (iter == dimmTempSensorName.end())
583 {
George Liub5ca1012021-09-10 12:53:11 +0800584 log<level::ERR>(
585 fmt::format(
586 "readTempSensors: Fru type error! fruTypeValue = {}) ",
587 fruTypeValue)
588 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800589 continue;
590 }
591
592 sensorPath.append("dimm" + std::to_string(instanceID) +
593 iter->second);
594 }
595 else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
596 {
Matt Spinlerace67d82021-10-18 13:41:57 -0500597 if (fruTypeValue == processorCore)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800598 {
Matt Spinlerace67d82021-10-18 13:41:57 -0500599 // The OCC reports small core temps, of which there are
600 // two per big core. All current P10 systems are in big
601 // core mode, so use a big core name.
602 uint16_t coreNum = instanceID / 2;
603 uint16_t tempNum = instanceID % 2;
604 sensorPath.append("proc" + std::to_string(id) + "_core" +
605 std::to_string(coreNum) + "_" +
606 std::to_string(tempNum) + "_temp");
607
608 dvfsTempPath = std::string{OCC_SENSORS_ROOT} +
609 "/temperature/proc" + std::to_string(id) +
610 "_core_dvfs_temp";
611 }
612 else
613 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800614 continue;
615 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800616 }
617 else
618 {
619 continue;
620 }
621 }
622
Matt Spinlerace67d82021-10-18 13:41:57 -0500623 // The dvfs temp file only needs to be read once per chip per type.
624 if (!dvfsTempPath.empty() &&
625 !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath))
626 {
627 try
628 {
629 auto dvfsValue = readFile<double>(filePathString + maxSuffix);
630
631 dbus::OccDBusSensors::getOccDBus().setDvfsTemp(
632 dvfsTempPath, dvfsValue * std::pow(10, -3));
633 }
634 catch (const std::system_error& e)
635 {
636 log<level::DEBUG>(
637 fmt::format(
638 "readTempSensors: Failed reading {}, errno = {}",
639 filePathString + maxSuffix, e.code().value())
640 .c_str());
641 }
642 }
643
Matt Spinlera26f1522021-08-25 15:50:20 -0500644 uint32_t faultValue{0};
645 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800646 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500647 faultValue = readFile<uint32_t>(filePathString + faultSuffix);
648 }
649 catch (const std::system_error& e)
650 {
651 log<level::DEBUG>(
652 fmt::format("readTempSensors: Failed reading {}, errno = {}",
653 filePathString + faultSuffix, e.code().value())
654 .c_str());
655 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800656 }
657
Matt Spinlera26f1522021-08-25 15:50:20 -0500658 if (faultValue != 0)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800659 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800660 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
Matt Spinlera26f1522021-08-25 15:50:20 -0500661 sensorPath, std::numeric_limits<double>::quiet_NaN());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800662
663 open_power::occ::dbus::OccDBusSensors::getOccDBus()
Matt Spinlera26f1522021-08-25 15:50:20 -0500664 .setOperationalStatus(sensorPath, false);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800665
Matt Spinlera26f1522021-08-25 15:50:20 -0500666 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800667 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500668
669 double tempValue{0};
670
671 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800672 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500673 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800674 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500675 catch (const std::system_error& e)
676 {
677 log<level::DEBUG>(
678 fmt::format("readTempSensors: Failed reading {}, errno = {}",
679 filePathString + inputSuffix, e.code().value())
680 .c_str());
681 continue;
682 }
683
684 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
685 sensorPath, tempValue * std::pow(10, -3));
686
687 open_power::occ::dbus::OccDBusSensors::getOccDBus()
688 .setOperationalStatus(sensorPath, true);
689
Chris Cain6fa848a2022-01-24 14:54:38 -0600690 // At this point, the sensor will be created for sure.
691 if (existingSensors.find(sensorPath) == existingSensors.end())
692 {
693 open_power::occ::dbus::OccDBusSensors::getOccDBus()
694 .setChassisAssociation(sensorPath);
695 }
696
Matt Spinlera26f1522021-08-25 15:50:20 -0500697 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800698 }
699 return;
700}
701
702std::optional<std::string>
703 Manager::getPowerLabelFunctionID(const std::string& value)
704{
705 // If the value is "system", then the FunctionID is "system".
706 if (value == "system")
707 {
708 return value;
709 }
710
711 // If the value is not "system", then the label value have 3 numbers, of
712 // which we only care about the middle one:
713 // <sensor id>_<function id>_<apss channel>
714 // eg: The value is "0_10_5" , then the FunctionID is "10".
715 if (value.find("_") == std::string::npos)
716 {
717 return std::nullopt;
718 }
719
720 auto powerLabelValue = value.substr((value.find("_") + 1));
721
722 if (powerLabelValue.find("_") == std::string::npos)
723 {
724 return std::nullopt;
725 }
726
727 return powerLabelValue.substr(0, powerLabelValue.find("_"));
728}
729
730void Manager::readPowerSensors(const fs::path& path, uint32_t id)
731{
Chicago Duanbb895cb2021-06-18 19:37:16 +0800732 std::regex expr{"power\\d+_label$"}; // Example: power5_label
733 for (auto& file : fs::directory_iterator(path))
734 {
735 if (!std::regex_search(file.path().string(), expr))
736 {
737 continue;
738 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800739
Matt Spinlera26f1522021-08-25 15:50:20 -0500740 std::string labelValue;
741 try
742 {
743 labelValue = readFile<std::string>(file.path());
744 }
745 catch (const std::system_error& e)
746 {
747 log<level::DEBUG>(
748 fmt::format("readPowerSensors: Failed reading {}, errno = {}",
749 file.path().string(), e.code().value())
750 .c_str());
Chicago Duanbb895cb2021-06-18 19:37:16 +0800751 continue;
752 }
Chicago Duanbb895cb2021-06-18 19:37:16 +0800753
754 auto functionID = getPowerLabelFunctionID(labelValue);
755 if (functionID == std::nullopt)
756 {
757 continue;
758 }
759
760 const std::string& tempLabel = "label";
761 const std::string filePathString = file.path().string().substr(
762 0, file.path().string().length() - tempLabel.length());
763
764 std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
765
766 auto iter = powerSensorName.find(*functionID);
767 if (iter == powerSensorName.end())
768 {
769 continue;
770 }
771 sensorPath.append(iter->second);
772
Matt Spinlera26f1522021-08-25 15:50:20 -0500773 double tempValue{0};
774
775 try
Chicago Duanbb895cb2021-06-18 19:37:16 +0800776 {
Matt Spinlera26f1522021-08-25 15:50:20 -0500777 tempValue = readFile<double>(filePathString + inputSuffix);
Chicago Duanbb895cb2021-06-18 19:37:16 +0800778 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500779 catch (const std::system_error& e)
Chicago Duanbb895cb2021-06-18 19:37:16 +0800780 {
Chicago Duanbb895cb2021-06-18 19:37:16 +0800781 log<level::DEBUG>(
Matt Spinlera26f1522021-08-25 15:50:20 -0500782 fmt::format("readTempSensors: Failed reading {}, errno = {}",
783 filePathString + inputSuffix, e.code().value())
Chicago Duanbb895cb2021-06-18 19:37:16 +0800784 .c_str());
Matt Spinlera26f1522021-08-25 15:50:20 -0500785 continue;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800786 }
Matt Spinlera26f1522021-08-25 15:50:20 -0500787
Chris Caind84a8332022-01-13 08:58:45 -0600788 open_power::occ::dbus::OccDBusSensors::getOccDBus().setUnit(
789 sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts");
790
Matt Spinlera26f1522021-08-25 15:50:20 -0500791 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
792 sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
793
794 open_power::occ::dbus::OccDBusSensors::getOccDBus()
795 .setOperationalStatus(sensorPath, true);
796
Matt Spinler5901abd2021-09-23 13:50:03 -0500797 if (existingSensors.find(sensorPath) == existingSensors.end())
798 {
799 open_power::occ::dbus::OccDBusSensors::getOccDBus()
800 .setChassisAssociation(sensorPath);
801 }
802
Matt Spinlera26f1522021-08-25 15:50:20 -0500803 existingSensors[sensorPath] = id;
Chicago Duanbb895cb2021-06-18 19:37:16 +0800804 }
805 return;
806}
807
808void Manager::setSensorValueToNaN(uint32_t id)
809{
810 for (const auto& [sensorPath, occId] : existingSensors)
811 {
812 if (occId == id)
813 {
814 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue(
815 sensorPath, std::numeric_limits<double>::quiet_NaN());
816 }
817 }
818 return;
819}
820
821void Manager::getSensorValues(uint32_t id, bool masterOcc)
822{
823 const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1);
824
825 fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"};
826
827 // Need to get the hwmonXX directory name, there better only be 1 dir
828 assert(std::distance(fs::directory_iterator(fileName),
829 fs::directory_iterator{}) == 1);
830 // Now set our path to this full path, including this hwmonXX directory
831 fileName = fs::path(*fs::directory_iterator(fileName));
832
833 // Read temperature sensors
834 readTempSensors(fileName, id);
835
836 if (masterOcc)
837 {
838 // Read power sensors
839 readPowerSensors(fileName, id);
840 }
841
842 return;
843}
844#endif
Chris Cain17257672021-10-22 13:41:03 -0500845
846// Read the altitude from DBus
847void Manager::readAltitude()
848{
849 static bool traceAltitudeErr = true;
850
851 utils::PropertyValue altitudeProperty{};
852 try
853 {
854 altitudeProperty = utils::getProperty(ALTITUDE_PATH, ALTITUDE_INTERFACE,
855 ALTITUDE_PROP);
856 auto sensorVal = std::get<double>(altitudeProperty);
857 if (sensorVal < 0xFFFF)
858 {
859 if (sensorVal < 0)
860 {
861 altitude = 0;
862 }
863 else
864 {
865 // Round to nearest meter
866 altitude = uint16_t(sensorVal + 0.5);
867 }
868 log<level::DEBUG>(fmt::format("readAltitude: sensor={} ({}m)",
869 sensorVal, altitude)
870 .c_str());
871 traceAltitudeErr = true;
872 }
873 else
874 {
875 if (traceAltitudeErr)
876 {
877 traceAltitudeErr = false;
878 log<level::DEBUG>(
879 fmt::format("Invalid altitude value: {}", sensorVal)
880 .c_str());
881 }
882 }
883 }
884 catch (const sdbusplus::exception::exception& e)
885 {
886 if (traceAltitudeErr)
887 {
888 traceAltitudeErr = false;
889 log<level::INFO>(
890 fmt::format("Unable to read Altitude: {}", e.what()).c_str());
891 }
892 altitude = 0xFFFF; // not available
893 }
894}
895
896// Callback function when ambient temperature changes
897void Manager::ambientCallback(sdbusplus::message::message& msg)
898{
899 double currentTemp = 0;
900 uint8_t truncatedTemp = 0xFF;
901 std::string msgSensor;
902 std::map<std::string, std::variant<double>> msgData;
903 msg.read(msgSensor, msgData);
904
905 auto valPropMap = msgData.find(AMBIENT_PROP);
906 if (valPropMap == msgData.end())
907 {
908 log<level::DEBUG>("ambientCallback: Unknown ambient property changed");
909 return;
910 }
911 currentTemp = std::get<double>(valPropMap->second);
912 if (std::isnan(currentTemp))
913 {
914 truncatedTemp = 0xFF;
915 }
916 else
917 {
918 if (currentTemp < 0)
919 {
920 truncatedTemp = 0;
921 }
922 else
923 {
924 // Round to nearest degree C
925 truncatedTemp = uint8_t(currentTemp + 0.5);
926 }
927 }
928
929 // If ambient changes, notify OCCs
930 if (truncatedTemp != ambient)
931 {
932 log<level::DEBUG>(
933 fmt::format("ambientCallback: Ambient change from {} to {}C",
934 ambient, currentTemp)
935 .c_str());
936
937 ambient = truncatedTemp;
938 if (altitude == 0xFFFF)
939 {
940 // No altitude yet, try reading again
941 readAltitude();
942 }
943
944 log<level::DEBUG>(
945 fmt::format("ambientCallback: Ambient: {}C, altitude: {}m", ambient,
946 altitude)
947 .c_str());
948#ifdef POWER10
949 // Send ambient and altitude to all OCCs
950 for (auto& obj : statusObjects)
951 {
952 if (obj->occActive())
953 {
954 obj->sendAmbient(ambient, altitude);
955 }
956 }
957#endif // POWER10
958 }
959}
960
961// return the current ambient and altitude readings
962void Manager::getAmbientData(bool& ambientValid, uint8_t& ambientTemp,
963 uint16_t& altitudeValue) const
964{
965 ambientValid = true;
966 ambientTemp = ambient;
967 altitudeValue = altitude;
968
969 if (ambient == 0xFF)
970 {
971 ambientValid = false;
972 }
973}
974
Chris Caina7b74dc2021-11-10 17:03:43 -0600975#ifdef POWER10
976void Manager::occsNotAllRunning()
977{
Chris Cain6fa848a2022-01-24 14:54:38 -0600978 // Function will also gets called when occ-control app gets
979 // restarted. (occ active sensors do not change, so the Status
980 // object does not call Manager back for all OCCs)
Chris Caina7b74dc2021-11-10 17:03:43 -0600981
982 if (activeCount != statusObjects.size())
983 {
984 // Not all OCCs went active
985 log<level::WARNING>(
986 fmt::format(
987 "occsNotAllRunning: Active OCC count ({}) does not match expected count ({})",
988 activeCount, statusObjects.size())
989 .c_str());
990 // Procs may be garded, so may not need reset.
991 }
992
993 validateOccMaster();
994}
995#endif // POWER10
996
997// Verify single master OCC and start presence monitor
998void Manager::validateOccMaster()
999{
1000 int masterInstance = -1;
1001 for (auto& obj : statusObjects)
1002 {
1003 obj->addPresenceWatchMaster();
1004 if (obj->isMasterOcc())
1005 {
1006 if (masterInstance == -1)
1007 {
1008 masterInstance = obj->getOccInstanceID();
1009 }
1010 else
1011 {
1012 log<level::ERR>(
1013 fmt::format(
1014 "validateOccMaster: Multiple OCC masters! ({} and {})",
1015 masterInstance, obj->getOccInstanceID())
1016 .c_str());
1017 // request reset
1018 obj->deviceError();
1019 }
1020 }
1021 }
1022 if (masterInstance < 0)
1023 {
1024 log<level::ERR>("validateOccMaster: Master OCC not found!");
1025 // request reset
1026 statusObjects.front()->deviceError();
1027 }
1028 else
1029 {
1030 log<level::INFO>(
Chris Cain36f9cde2021-11-22 11:18:21 -06001031 fmt::format("validateOccMaster: OCC{} is master of {} OCCs",
1032 masterInstance, activeCount)
Chris Caina7b74dc2021-11-10 17:03:43 -06001033 .c_str());
1034 }
1035}
1036
Vishwanatha Subbannadfc7ec72017-09-07 18:18:01 +05301037} // namespace occ
1038} // namespace open_power