1dfc7ec73SVishwanatha Subbanna #include "config.h"
2dfc7ec73SVishwanatha Subbanna 
394df8c90SGunnar Mills #include "occ_manager.hpp"
494df8c90SGunnar Mills 
594df8c90SGunnar Mills #include "i2c_occ.hpp"
6bb895cb8SChicago Duan #include "occ_dbus.hpp"
74b82f3e3SChris Cain #include "occ_errors.hpp"
894df8c90SGunnar Mills #include "utils.hpp"
994df8c90SGunnar Mills 
10b5ca1015SGeorge Liu #include <phosphor-logging/elog-errors.hpp>
11b5ca1015SGeorge Liu #include <phosphor-logging/log.hpp>
12b5ca1015SGeorge Liu #include <xyz/openbmc_project/Common/error.hpp>
13b5ca1015SGeorge Liu 
14d267cec2SMatt Spinler #include <chrono>
15bb895cb8SChicago Duan #include <cmath>
16bcef3b48SGeorge Liu #include <filesystem>
1736f9cdedSChris Cain #include <fstream>
18bb895cb8SChicago Duan #include <regex>
1994df8c90SGunnar Mills 
20dfc7ec73SVishwanatha Subbanna namespace open_power
21dfc7ec73SVishwanatha Subbanna {
22dfc7ec73SVishwanatha Subbanna namespace occ
23dfc7ec73SVishwanatha Subbanna {
24dfc7ec73SVishwanatha Subbanna 
258b8abeedSMatt Spinler constexpr uint32_t fruTypeNotAvailable = 0xFF;
26a26f1527SMatt Spinler constexpr auto fruTypeSuffix = "fru_type";
27a26f1527SMatt Spinler constexpr auto faultSuffix = "fault";
28a26f1527SMatt Spinler constexpr auto inputSuffix = "input";
29ace67d85SMatt Spinler constexpr auto maxSuffix = "max";
308b8abeedSMatt Spinler 
311718fd8bSChris Cain const auto HOST_ON_FILE = "/run/openbmc/host@0-on";
321718fd8bSChris Cain 
33a8857c50SChris Cain using namespace phosphor::logging;
34a7b74dc3SChris Cain using namespace std::literals::chrono_literals;
35a8857c50SChris Cain 
36a26f1527SMatt Spinler template <typename T>
37a26f1527SMatt Spinler T readFile(const std::string& path)
38a26f1527SMatt Spinler {
39a26f1527SMatt Spinler     std::ifstream ifs;
40a26f1527SMatt Spinler     ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit |
41a26f1527SMatt Spinler                    std::ifstream::eofbit);
42a26f1527SMatt Spinler     T data;
43a26f1527SMatt Spinler 
44a26f1527SMatt Spinler     try
45a26f1527SMatt Spinler     {
46a26f1527SMatt Spinler         ifs.open(path);
47a26f1527SMatt Spinler         ifs >> data;
48a26f1527SMatt Spinler         ifs.close();
49a26f1527SMatt Spinler     }
50a26f1527SMatt Spinler     catch (const std::exception& e)
51a26f1527SMatt Spinler     {
52a26f1527SMatt Spinler         auto err = errno;
53a26f1527SMatt Spinler         throw std::system_error(err, std::generic_category());
54a26f1527SMatt Spinler     }
55a26f1527SMatt Spinler 
56a26f1527SMatt Spinler     return data;
57a26f1527SMatt Spinler }
58a26f1527SMatt Spinler 
59c33171bbSChris Cain // findAndCreateObjects():
60c33171bbSChris Cain // Takes care of getting the required objects created and
61c33171bbSChris Cain // finds the available devices/processors.
62c33171bbSChris Cain // (function is called everytime the discoverTimer expires)
63c33171bbSChris Cain // - create the PowerMode object to control OCC modes
64c33171bbSChris Cain // - create statusObjects for each OCC device found
65c33171bbSChris Cain // - waits for OCC Active sensors PDRs to become available
66c33171bbSChris Cain // - restart discoverTimer if all data is not available yet
67dfc7ec73SVishwanatha Subbanna void Manager::findAndCreateObjects()
68dfc7ec73SVishwanatha Subbanna {
69d267cec2SMatt Spinler #ifndef POWER10
70dfc7ec73SVishwanatha Subbanna     for (auto id = 0; id < MAX_CPUS; ++id)
71dfc7ec73SVishwanatha Subbanna     {
7230417a15SDeepak Kodihalli         // Create one occ per cpu
7330417a15SDeepak Kodihalli         auto occ = std::string(OCC_NAME) + std::to_string(id);
74dfc7ec73SVishwanatha Subbanna         createObjects(occ);
75dfc7ec73SVishwanatha Subbanna     }
76d267cec2SMatt Spinler #else
77613dc90dSChris Cain     if (!pmode)
78613dc90dSChris Cain     {
79613dc90dSChris Cain         // Create the power mode object
80613dc90dSChris Cain         pmode = std::make_unique<powermode::PowerMode>(
81613dc90dSChris Cain             *this, powermode::PMODE_PATH, powermode::PIPS_PATH, event);
82613dc90dSChris Cain     }
83613dc90dSChris Cain 
841718fd8bSChris Cain     if (!fs::exists(HOST_ON_FILE))
851718fd8bSChris Cain     {
86bae4d07eSChris Cain         static bool statusObjCreated = false;
87bae4d07eSChris Cain         if (!statusObjCreated)
88bae4d07eSChris Cain         {
89d267cec2SMatt Spinler             // Create the OCCs based on on the /dev/occX devices
90d267cec2SMatt Spinler             auto occs = findOCCsInDev();
91d267cec2SMatt Spinler 
92d267cec2SMatt Spinler             if (occs.empty() || (prevOCCSearch.size() != occs.size()))
93d267cec2SMatt Spinler             {
94d267cec2SMatt Spinler                 // Something changed or no OCCs yet, try again in 10s.
95d267cec2SMatt Spinler                 // Note on the first pass prevOCCSearch will be empty,
96d267cec2SMatt Spinler                 // so there will be at least one delay to give things
97d267cec2SMatt Spinler                 // a chance to settle.
98d267cec2SMatt Spinler                 prevOCCSearch = occs;
99d267cec2SMatt Spinler 
100bae4d07eSChris Cain                 log<level::INFO>(
10148002498SPatrick Williams                     std::format(
102bae4d07eSChris Cain                         "Manager::findAndCreateObjects(): Waiting for OCCs (currently {})",
103bae4d07eSChris Cain                         occs.size())
104bae4d07eSChris Cain                         .c_str());
105bae4d07eSChris Cain 
106d267cec2SMatt Spinler                 discoverTimer->restartOnce(10s);
107d267cec2SMatt Spinler             }
108d267cec2SMatt Spinler             else
109d267cec2SMatt Spinler             {
110bae4d07eSChris Cain                 // All OCCs appear to be available, create status objects
111d267cec2SMatt Spinler 
112d267cec2SMatt Spinler                 // createObjects requires OCC0 first.
113d267cec2SMatt Spinler                 std::sort(occs.begin(), occs.end());
114d267cec2SMatt Spinler 
115bae4d07eSChris Cain                 log<level::INFO>(
11648002498SPatrick Williams                     std::format(
117bae4d07eSChris Cain                         "Manager::findAndCreateObjects(): Creating {} OCC Status Objects",
118bae4d07eSChris Cain                         occs.size())
119bae4d07eSChris Cain                         .c_str());
120d267cec2SMatt Spinler                 for (auto id : occs)
121d267cec2SMatt Spinler                 {
122d267cec2SMatt Spinler                     createObjects(std::string(OCC_NAME) + std::to_string(id));
123d267cec2SMatt Spinler                 }
124bae4d07eSChris Cain                 statusObjCreated = true;
1256d8f37a2SChris Cain                 waitingForAllOccActiveSensors = true;
126c86d80faSChris Cain 
127c86d80faSChris Cain                 // Find/update the processor path associated with each OCC
128c86d80faSChris Cain                 for (auto& obj : statusObjects)
129c86d80faSChris Cain                 {
130c86d80faSChris Cain                     obj->updateProcAssociation();
131c86d80faSChris Cain                 }
132bae4d07eSChris Cain             }
133bae4d07eSChris Cain         }
134bae4d07eSChris Cain 
1356d8f37a2SChris Cain         if (statusObjCreated && waitingForAllOccActiveSensors)
136bae4d07eSChris Cain         {
137bae4d07eSChris Cain             static bool tracedHostWait = false;
138bae4d07eSChris Cain             if (utils::isHostRunning())
139bae4d07eSChris Cain             {
140bae4d07eSChris Cain                 if (tracedHostWait)
141bae4d07eSChris Cain                 {
142bae4d07eSChris Cain                     log<level::INFO>(
143bae4d07eSChris Cain                         "Manager::findAndCreateObjects(): Host is running");
144bae4d07eSChris Cain                     tracedHostWait = false;
145bae4d07eSChris Cain                 }
146bae4d07eSChris Cain                 checkAllActiveSensors();
147bae4d07eSChris Cain             }
148bae4d07eSChris Cain             else
149bae4d07eSChris Cain             {
150bae4d07eSChris Cain                 if (!tracedHostWait)
151bae4d07eSChris Cain                 {
152bae4d07eSChris Cain                     log<level::INFO>(
153bae4d07eSChris Cain                         "Manager::findAndCreateObjects(): Waiting for host to start");
154bae4d07eSChris Cain                     tracedHostWait = true;
155bae4d07eSChris Cain                 }
156bae4d07eSChris Cain                 discoverTimer->restartOnce(30s);
1577651c06bSChris Cain #ifdef PLDM
158c33171bbSChris Cain                 if (throttlePldmTraceTimer->isEnabled())
1597651c06bSChris Cain                 {
1607651c06bSChris Cain                     // Host is no longer running, disable throttle timer and
1617651c06bSChris Cain                     // make sure traces are not throttled
1627651c06bSChris Cain                     log<level::INFO>(
1637651c06bSChris Cain                         "findAndCreateObjects(): disabling sensor timer");
164c33171bbSChris Cain                     throttlePldmTraceTimer->setEnabled(false);
1657651c06bSChris Cain                     pldmHandle->setTraceThrottle(false);
1667651c06bSChris Cain                 }
1677651c06bSChris Cain #endif
168bae4d07eSChris Cain             }
169d267cec2SMatt Spinler         }
1701718fd8bSChris Cain     }
1711718fd8bSChris Cain     else
1721718fd8bSChris Cain     {
1731718fd8bSChris Cain         log<level::INFO>(
17448002498SPatrick Williams             std::format(
1751718fd8bSChris Cain                 "Manager::findAndCreateObjects(): Waiting for {} to complete...",
1761718fd8bSChris Cain                 HOST_ON_FILE)
1771718fd8bSChris Cain                 .c_str());
1781718fd8bSChris Cain         discoverTimer->restartOnce(10s);
1791718fd8bSChris Cain     }
180d267cec2SMatt Spinler #endif
181d267cec2SMatt Spinler }
182d267cec2SMatt Spinler 
183bae4d07eSChris Cain #ifdef POWER10
184bae4d07eSChris Cain // Check if all occActive sensors are available
185bae4d07eSChris Cain void Manager::checkAllActiveSensors()
186bae4d07eSChris Cain {
187bae4d07eSChris Cain     static bool allActiveSensorAvailable = false;
188bae4d07eSChris Cain     static bool tracedSensorWait = false;
189082a6ca7SChris Cain     static bool waitingForHost = false;
190082a6ca7SChris Cain 
191082a6ca7SChris Cain     if (open_power::occ::utils::isHostRunning())
192082a6ca7SChris Cain     {
193082a6ca7SChris Cain         if (waitingForHost)
194082a6ca7SChris Cain         {
195082a6ca7SChris Cain             waitingForHost = false;
196082a6ca7SChris Cain             log<level::INFO>("checkAllActiveSensors(): Host is now running");
197082a6ca7SChris Cain         }
198bae4d07eSChris Cain 
199bae4d07eSChris Cain         // Start with the assumption that all are available
200bae4d07eSChris Cain         allActiveSensorAvailable = true;
201bae4d07eSChris Cain         for (auto& obj : statusObjects)
202bae4d07eSChris Cain         {
203082a6ca7SChris Cain             if ((!obj->occActive()) && (!obj->getPldmSensorReceived()))
204bae4d07eSChris Cain             {
205bd551de3SChris Cain                 auto instance = obj->getOccInstanceID();
206bd551de3SChris Cain                 // Check if sensor was queued while waiting for discovery
207bd551de3SChris Cain                 auto match = queuedActiveState.find(instance);
208bd551de3SChris Cain                 if (match != queuedActiveState.end())
209bd551de3SChris Cain                 {
2107f89e4d1SChris Cain                     queuedActiveState.erase(match);
211bd551de3SChris Cain                     log<level::INFO>(
21248002498SPatrick Williams                         std::format(
213bd551de3SChris Cain                             "checkAllActiveSensors(): OCC{} is ACTIVE (queued)",
214bd551de3SChris Cain                             instance)
215bd551de3SChris Cain                             .c_str());
216bd551de3SChris Cain                     obj->occActive(true);
217bd551de3SChris Cain                 }
218bd551de3SChris Cain                 else
219bd551de3SChris Cain                 {
220bae4d07eSChris Cain                     allActiveSensorAvailable = false;
221bae4d07eSChris Cain                     if (!tracedSensorWait)
222bae4d07eSChris Cain                     {
223bae4d07eSChris Cain                         log<level::INFO>(
22448002498SPatrick Williams                             std::format(
225bd551de3SChris Cain                                 "checkAllActiveSensors(): Waiting on OCC{} Active sensor",
226bd551de3SChris Cain                                 instance)
227bae4d07eSChris Cain                                 .c_str());
228bae4d07eSChris Cain                         tracedSensorWait = true;
229755af102SChris Cain #ifdef PLDM
230c33171bbSChris Cain                         // Make sure PLDM traces are not throttled
231755af102SChris Cain                         pldmHandle->setTraceThrottle(false);
232c33171bbSChris Cain                         // Start timer to throttle PLDM traces when timer
233755af102SChris Cain                         // expires
234c33171bbSChris Cain                         onPldmTimeoutCreatePel = false;
235c33171bbSChris Cain                         throttlePldmTraceTimer->restartOnce(5min);
236755af102SChris Cain #endif
237bae4d07eSChris Cain                     }
238fb0a5c3cSPatrick Williams #ifdef PLDM
239bae4d07eSChris Cain                     pldmHandle->checkActiveSensor(obj->getOccInstanceID());
240fb0a5c3cSPatrick Williams #endif
241bae4d07eSChris Cain                     break;
242bae4d07eSChris Cain                 }
243bae4d07eSChris Cain             }
244bd551de3SChris Cain         }
2457f89e4d1SChris Cain     }
246082a6ca7SChris Cain     else
247082a6ca7SChris Cain     {
248082a6ca7SChris Cain         if (!waitingForHost)
249082a6ca7SChris Cain         {
250082a6ca7SChris Cain             waitingForHost = true;
251082a6ca7SChris Cain             log<level::INFO>(
252082a6ca7SChris Cain                 "checkAllActiveSensors(): Waiting for host to start");
2537651c06bSChris Cain #ifdef PLDM
254c33171bbSChris Cain             if (throttlePldmTraceTimer->isEnabled())
2557651c06bSChris Cain             {
2567651c06bSChris Cain                 // Host is no longer running, disable throttle timer and
2577651c06bSChris Cain                 // make sure traces are not throttled
2587651c06bSChris Cain                 log<level::INFO>(
2597651c06bSChris Cain                     "checkAllActiveSensors(): disabling sensor timer");
260c33171bbSChris Cain                 throttlePldmTraceTimer->setEnabled(false);
2617651c06bSChris Cain                 pldmHandle->setTraceThrottle(false);
2627651c06bSChris Cain             }
2637651c06bSChris Cain #endif
264082a6ca7SChris Cain         }
265082a6ca7SChris Cain     }
266bae4d07eSChris Cain 
267bae4d07eSChris Cain     if (allActiveSensorAvailable)
268bae4d07eSChris Cain     {
269bae4d07eSChris Cain         // All sensors were found, disable the discovery timer
2707f89e4d1SChris Cain         if (discoverTimer->isEnabled())
2717f89e4d1SChris Cain         {
272f55f91acSChris Cain             discoverTimer->setEnabled(false);
2737f89e4d1SChris Cain         }
274755af102SChris Cain #ifdef PLDM
275c33171bbSChris Cain         if (throttlePldmTraceTimer->isEnabled())
276755af102SChris Cain         {
277755af102SChris Cain             // Disable throttle timer and make sure traces are not throttled
278c33171bbSChris Cain             throttlePldmTraceTimer->setEnabled(false);
279755af102SChris Cain             pldmHandle->setTraceThrottle(false);
280755af102SChris Cain         }
281755af102SChris Cain #endif
2827f89e4d1SChris Cain         if (waitingForAllOccActiveSensors)
2837f89e4d1SChris Cain         {
284bae4d07eSChris Cain             log<level::INFO>(
285bd551de3SChris Cain                 "checkAllActiveSensors(): OCC Active sensors are available");
2867f89e4d1SChris Cain             waitingForAllOccActiveSensors = false;
2877f89e4d1SChris Cain         }
2887f89e4d1SChris Cain         queuedActiveState.clear();
289bae4d07eSChris Cain         tracedSensorWait = false;
290bae4d07eSChris Cain     }
291bae4d07eSChris Cain     else
292bae4d07eSChris Cain     {
293bae4d07eSChris Cain         // Not all sensors were available, so keep waiting
294bae4d07eSChris Cain         if (!tracedSensorWait)
295bae4d07eSChris Cain         {
296bae4d07eSChris Cain             log<level::INFO>(
297bd551de3SChris Cain                 "checkAllActiveSensors(): Waiting for OCC Active sensors to become available");
298bae4d07eSChris Cain             tracedSensorWait = true;
299bae4d07eSChris Cain         }
300bd551de3SChris Cain         discoverTimer->restartOnce(10s);
301bae4d07eSChris Cain     }
302bae4d07eSChris Cain }
303bae4d07eSChris Cain #endif
304bae4d07eSChris Cain 
305d267cec2SMatt Spinler std::vector<int> Manager::findOCCsInDev()
306d267cec2SMatt Spinler {
307d267cec2SMatt Spinler     std::vector<int> occs;
308d267cec2SMatt Spinler     std::regex expr{R"(occ(\d+)$)"};
309d267cec2SMatt Spinler 
310d267cec2SMatt Spinler     for (auto& file : fs::directory_iterator("/dev"))
311d267cec2SMatt Spinler     {
312d267cec2SMatt Spinler         std::smatch match;
313d267cec2SMatt Spinler         std::string path{file.path().string()};
314d267cec2SMatt Spinler         if (std::regex_search(path, match, expr))
315d267cec2SMatt Spinler         {
316d267cec2SMatt Spinler             auto num = std::stoi(match[1].str());
317d267cec2SMatt Spinler 
318d267cec2SMatt Spinler             // /dev numbering starts at 1, ours starts at 0.
319d267cec2SMatt Spinler             occs.push_back(num - 1);
320d267cec2SMatt Spinler         }
321d267cec2SMatt Spinler     }
322d267cec2SMatt Spinler 
323d267cec2SMatt Spinler     return occs;
324dfc7ec73SVishwanatha Subbanna }
325dfc7ec73SVishwanatha Subbanna 
326af40808fSPatrick Williams int Manager::cpuCreated(sdbusplus::message_t& msg)
327dfc7ec73SVishwanatha Subbanna {
328bcef3b48SGeorge Liu     namespace fs = std::filesystem;
329dfc7ec73SVishwanatha Subbanna 
330dfc7ec73SVishwanatha Subbanna     sdbusplus::message::object_path o;
331dfc7ec73SVishwanatha Subbanna     msg.read(o);
332dfc7ec73SVishwanatha Subbanna     fs::path cpuPath(std::string(std::move(o)));
333dfc7ec73SVishwanatha Subbanna 
334dfc7ec73SVishwanatha Subbanna     auto name = cpuPath.filename().string();
335dfc7ec73SVishwanatha Subbanna     auto index = name.find(CPU_NAME);
336dfc7ec73SVishwanatha Subbanna     name.replace(index, std::strlen(CPU_NAME), OCC_NAME);
337dfc7ec73SVishwanatha Subbanna 
338dfc7ec73SVishwanatha Subbanna     createObjects(name);
339dfc7ec73SVishwanatha Subbanna 
340dfc7ec73SVishwanatha Subbanna     return 0;
341dfc7ec73SVishwanatha Subbanna }
342dfc7ec73SVishwanatha Subbanna 
343dfc7ec73SVishwanatha Subbanna void Manager::createObjects(const std::string& occ)
344dfc7ec73SVishwanatha Subbanna {
345dfc7ec73SVishwanatha Subbanna     auto path = fs::path(OCC_CONTROL_ROOT) / occ;
346dfc7ec73SVishwanatha Subbanna 
34794df8c90SGunnar Mills     statusObjects.emplace_back(std::make_unique<Status>(
348f3b7514eSGeorge Liu         event, path.c_str(), *this,
34936f9cdedSChris Cain #ifdef POWER10
35036f9cdedSChris Cain         pmode,
35136f9cdedSChris Cain #endif
35294df8c90SGunnar Mills         std::bind(std::mem_fn(&Manager::statusCallBack), this,
353373af757SSheldon Bailey                   std::placeholders::_1, std::placeholders::_2)
35400325238STom Joseph #ifdef PLDM
35500325238STom Joseph             ,
35600325238STom Joseph         std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
35700325238STom Joseph                   std::placeholders::_1)
35800325238STom Joseph #endif
35900325238STom Joseph             ));
360dfc7ec73SVishwanatha Subbanna 
36140501a23SChris Cain     // Create the power cap monitor object
36240501a23SChris Cain     if (!pcap)
36340501a23SChris Cain     {
36440501a23SChris Cain         pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
36540501a23SChris Cain             *statusObjects.back());
36640501a23SChris Cain     }
36740501a23SChris Cain 
36836f9cdedSChris Cain     if (statusObjects.back()->isMasterOcc())
36936f9cdedSChris Cain     {
37036f9cdedSChris Cain         log<level::INFO>(
37148002498SPatrick Williams             std::format("Manager::createObjects(): OCC{} is the master",
37236f9cdedSChris Cain                         statusObjects.back()->getOccInstanceID())
37336f9cdedSChris Cain                 .c_str());
37436f9cdedSChris Cain         _pollTimer->setEnabled(false);
37536f9cdedSChris Cain 
37678e86012SChris Cain #ifdef POWER10
3776fa848a9SChris Cain         // Set the master OCC on the PowerMode object
3786fa848a9SChris Cain         pmode->setMasterOcc(path);
37978e86012SChris Cain #endif
380dfc7ec73SVishwanatha Subbanna     }
381dfc7ec73SVishwanatha Subbanna 
382*d7542c83SPatrick Williams     passThroughObjects.emplace_back(std::make_unique<PassThrough>(
383*d7542c83SPatrick Williams         path.c_str()
38436f9cdedSChris Cain #ifdef POWER10
38536f9cdedSChris Cain             ,
38636f9cdedSChris Cain         pmode
38736f9cdedSChris Cain #endif
38836f9cdedSChris Cain         ));
38936f9cdedSChris Cain }
39036f9cdedSChris Cain 
391373af757SSheldon Bailey void Manager::statusCallBack(instanceID instance, bool status)
392dfc7ec73SVishwanatha Subbanna {
393a7b74dc3SChris Cain     if (status == true)
394a7b74dc3SChris Cain     {
395a7b74dc3SChris Cain         // OCC went active
396a7b74dc3SChris Cain         ++activeCount;
397dae2d940SEddie James 
398a7b74dc3SChris Cain #ifdef POWER10
399a7b74dc3SChris Cain         if (activeCount == 1)
400a7b74dc3SChris Cain         {
401a7b74dc3SChris Cain             // First OCC went active (allow some time for all OCCs to go active)
402bd551de3SChris Cain             waitForAllOccsTimer->restartOnce(60s);
403a7b74dc3SChris Cain         }
404a7b74dc3SChris Cain #endif
405a7b74dc3SChris Cain 
406dae2d940SEddie James         if (activeCount == statusObjects.size())
407dae2d940SEddie James         {
408a7b74dc3SChris Cain #ifdef POWER10
409a7b74dc3SChris Cain             // All OCCs are now running
410a7b74dc3SChris Cain             if (waitForAllOccsTimer->isEnabled())
411dae2d940SEddie James             {
412a7b74dc3SChris Cain                 // stop occ wait timer
413a7b74dc3SChris Cain                 waitForAllOccsTimer->setEnabled(false);
414dae2d940SEddie James             }
415a7b74dc3SChris Cain #endif
416a7b74dc3SChris Cain 
417a7b74dc3SChris Cain             // Verify master OCC and start presence monitor
418a7b74dc3SChris Cain             validateOccMaster();
419dae2d940SEddie James         }
420a8857c50SChris Cain 
421a7b74dc3SChris Cain         // Start poll timer if not already started
422a7b74dc3SChris Cain         if (!_pollTimer->isEnabled())
423a8857c50SChris Cain         {
424b5ca1015SGeorge Liu             log<level::INFO>(
42548002498SPatrick Williams                 std::format("Manager: OCCs will be polled every {} seconds",
42636f9cdedSChris Cain                             pollInterval)
427a8857c50SChris Cain                     .c_str());
428a8857c50SChris Cain 
429a8857c50SChris Cain             // Send poll and start OCC poll timer
430a8857c50SChris Cain             pollerTimerExpired();
431a8857c50SChris Cain         }
432a7b74dc3SChris Cain     }
433a7b74dc3SChris Cain     else
434a8857c50SChris Cain     {
435a7b74dc3SChris Cain         // OCC went away
436082a6ca7SChris Cain         if (activeCount > 0)
437082a6ca7SChris Cain         {
438a7b74dc3SChris Cain             --activeCount;
439082a6ca7SChris Cain         }
440082a6ca7SChris Cain         else
441082a6ca7SChris Cain         {
442082a6ca7SChris Cain             log<level::ERR>(
44348002498SPatrick Williams                 std::format("OCC{} disabled, but currently no active OCCs",
444082a6ca7SChris Cain                             instance)
445082a6ca7SChris Cain                     .c_str());
446082a6ca7SChris Cain         }
447a7b74dc3SChris Cain 
448a7b74dc3SChris Cain         if (activeCount == 0)
449a7b74dc3SChris Cain         {
450a7b74dc3SChris Cain             // No OCCs are running
451a7b74dc3SChris Cain 
452a8857c50SChris Cain             // Stop OCC poll timer
453a7b74dc3SChris Cain             if (_pollTimer->isEnabled())
454a7b74dc3SChris Cain             {
455b5ca1015SGeorge Liu                 log<level::INFO>(
456b5ca1015SGeorge Liu                     "Manager::statusCallBack(): OCCs are not running, stopping poll timer");
457a8857c50SChris Cain                 _pollTimer->setEnabled(false);
458a7b74dc3SChris Cain             }
459a7b74dc3SChris Cain 
460a7b74dc3SChris Cain #ifdef POWER10
461a7b74dc3SChris Cain             // stop wait timer
462a7b74dc3SChris Cain             if (waitForAllOccsTimer->isEnabled())
463a7b74dc3SChris Cain             {
464a7b74dc3SChris Cain                 waitForAllOccsTimer->setEnabled(false);
465a7b74dc3SChris Cain             }
466a7b74dc3SChris Cain #endif
467373af757SSheldon Bailey         }
46853f68148SMatt Spinler #ifdef READ_OCC_SENSORS
469a7b74dc3SChris Cain         // Clear OCC sensors
470c8dd4599SSheldon Bailey         setSensorValueToNaN(instance);
47153f68148SMatt Spinler #endif
472a8857c50SChris Cain     }
473bae4d07eSChris Cain 
474bae4d07eSChris Cain #ifdef POWER10
475bae4d07eSChris Cain     if (waitingForAllOccActiveSensors)
476bae4d07eSChris Cain     {
4776d8f37a2SChris Cain         if (utils::isHostRunning())
4786d8f37a2SChris Cain         {
479bae4d07eSChris Cain             checkAllActiveSensors();
480bae4d07eSChris Cain         }
4816d8f37a2SChris Cain     }
482bae4d07eSChris Cain #endif
483dfc7ec73SVishwanatha Subbanna }
484dfc7ec73SVishwanatha Subbanna 
485dfc7ec73SVishwanatha Subbanna #ifdef I2C_OCC
486dfc7ec73SVishwanatha Subbanna void Manager::initStatusObjects()
487dfc7ec73SVishwanatha Subbanna {
488dfc7ec73SVishwanatha Subbanna     // Make sure we have a valid path string
489dfc7ec73SVishwanatha Subbanna     static_assert(sizeof(DEV_PATH) != 0);
490dfc7ec73SVishwanatha Subbanna 
491dfc7ec73SVishwanatha Subbanna     auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH);
492dfc7ec73SVishwanatha Subbanna     for (auto& name : deviceNames)
493dfc7ec73SVishwanatha Subbanna     {
494dfc7ec73SVishwanatha Subbanna         i2c_occ::i2cToDbus(name);
495b5259a1eSLei YU         name = std::string(OCC_NAME) + '_' + name;
496dfc7ec73SVishwanatha Subbanna         auto path = fs::path(OCC_CONTROL_ROOT) / name;
497dfc7ec73SVishwanatha Subbanna         statusObjects.emplace_back(
498f3b7514eSGeorge Liu             std::make_unique<Status>(event, path.c_str(), *this));
499dfc7ec73SVishwanatha Subbanna     }
50040501a23SChris Cain     // The first device is master occ
50140501a23SChris Cain     pcap = std::make_unique<open_power::occ::powercap::PowerCap>(
50240501a23SChris Cain         *statusObjects.front());
50378e86012SChris Cain #ifdef POWER10
5045d66a0aaSChris Cain     pmode = std::make_unique<powermode::PowerMode>(*this, powermode::PMODE_PATH,
5055d66a0aaSChris Cain                                                    powermode::PIPS_PATH);
5066fa848a9SChris Cain     // Set the master OCC on the PowerMode object
5076fa848a9SChris Cain     pmode->setMasterOcc(path);
50878e86012SChris Cain #endif
509dfc7ec73SVishwanatha Subbanna }
510dfc7ec73SVishwanatha Subbanna #endif
511dfc7ec73SVishwanatha Subbanna 
512815f9f55STom Joseph #ifdef PLDM
513cbad219eSEddie James void Manager::sbeTimeout(unsigned int instance)
514cbad219eSEddie James {
5152a751d70SEddie James     auto obj = std::find_if(statusObjects.begin(), statusObjects.end(),
5162a751d70SEddie James                             [instance](const auto& obj) {
5172a751d70SEddie James                                 return instance == obj->getOccInstanceID();
5182a751d70SEddie James                             });
5192a751d70SEddie James 
520cb018dafSEddie James     if (obj != statusObjects.end() && (*obj)->occActive())
5212a751d70SEddie James     {
522bae4d07eSChris Cain         log<level::INFO>(
52348002498SPatrick Williams             std::format("SBE timeout, requesting HRESET (OCC{})", instance)
524bae4d07eSChris Cain                 .c_str());
525cbad219eSEddie James 
526cbad219eSEddie James         setSBEState(instance, SBE_STATE_NOT_USABLE);
527cbad219eSEddie James 
528cbad219eSEddie James         pldmHandle->sendHRESET(instance);
529cbad219eSEddie James     }
5302a751d70SEddie James }
531cbad219eSEddie James 
532815f9f55STom Joseph bool Manager::updateOCCActive(instanceID instance, bool status)
533815f9f55STom Joseph {
5347e374fb4SChris Cain     auto obj = std::find_if(statusObjects.begin(), statusObjects.end(),
5357e374fb4SChris Cain                             [instance](const auto& obj) {
5367e374fb4SChris Cain                                 return instance == obj->getOccInstanceID();
5377e374fb4SChris Cain                             });
5387e374fb4SChris Cain 
539082a6ca7SChris Cain     const bool hostRunning = open_power::occ::utils::isHostRunning();
5407e374fb4SChris Cain     if (obj != statusObjects.end())
5417e374fb4SChris Cain     {
542082a6ca7SChris Cain         if (!hostRunning && (status == true))
5437e374fb4SChris Cain         {
5447e374fb4SChris Cain             log<level::WARNING>(
54548002498SPatrick Williams                 std::format(
546082a6ca7SChris Cain                     "updateOCCActive: Host is not running yet (OCC{} active={}), clearing sensor received",
5477e374fb4SChris Cain                     instance, status)
5487e374fb4SChris Cain                     .c_str());
549082a6ca7SChris Cain             (*obj)->setPldmSensorReceived(false);
550082a6ca7SChris Cain             if (!waitingForAllOccActiveSensors)
551082a6ca7SChris Cain             {
552082a6ca7SChris Cain                 log<level::INFO>(
553082a6ca7SChris Cain                     "updateOCCActive: Waiting for Host and all OCC Active Sensors");
554082a6ca7SChris Cain                 waitingForAllOccActiveSensors = true;
555082a6ca7SChris Cain             }
556755af102SChris Cain #ifdef POWER10
557082a6ca7SChris Cain             discoverTimer->restartOnce(30s);
558755af102SChris Cain #endif
559082a6ca7SChris Cain             return false;
560082a6ca7SChris Cain         }
561082a6ca7SChris Cain         else
562082a6ca7SChris Cain         {
56348002498SPatrick Williams             log<level::INFO>(std::format("updateOCCActive: OCC{} active={}",
564082a6ca7SChris Cain                                          instance, status)
565082a6ca7SChris Cain                                  .c_str());
566082a6ca7SChris Cain             (*obj)->setPldmSensorReceived(true);
567082a6ca7SChris Cain             return (*obj)->occActive(status);
568082a6ca7SChris Cain         }
569082a6ca7SChris Cain     }
570082a6ca7SChris Cain     else
571082a6ca7SChris Cain     {
572082a6ca7SChris Cain         if (hostRunning)
573082a6ca7SChris Cain         {
574082a6ca7SChris Cain             log<level::WARNING>(
57548002498SPatrick Williams                 std::format(
576082a6ca7SChris Cain                     "updateOCCActive: No status object to update for OCC{} (active={})",
577082a6ca7SChris Cain                     instance, status)
578082a6ca7SChris Cain                     .c_str());
579082a6ca7SChris Cain         }
580082a6ca7SChris Cain         else
581082a6ca7SChris Cain         {
582082a6ca7SChris Cain             if (status == true)
583082a6ca7SChris Cain             {
584082a6ca7SChris Cain                 log<level::WARNING>(
58548002498SPatrick Williams                     std::format(
586082a6ca7SChris Cain                         "updateOCCActive: No status objects and Host is not running yet (OCC{} active={})",
587082a6ca7SChris Cain                         instance, status)
588082a6ca7SChris Cain                         .c_str());
589082a6ca7SChris Cain             }
590082a6ca7SChris Cain         }
591bd551de3SChris Cain         if (status == true)
592bd551de3SChris Cain         {
593bd551de3SChris Cain             // OCC went active
594bd551de3SChris Cain             queuedActiveState.insert(instance);
595bd551de3SChris Cain         }
596bd551de3SChris Cain         else
597bd551de3SChris Cain         {
598bd551de3SChris Cain             auto match = queuedActiveState.find(instance);
599bd551de3SChris Cain             if (match != queuedActiveState.end())
600bd551de3SChris Cain             {
601bd551de3SChris Cain                 // OCC was disabled
602bd551de3SChris Cain                 queuedActiveState.erase(match);
603bd551de3SChris Cain             }
604bd551de3SChris Cain         }
6057e374fb4SChris Cain         return false;
6067e374fb4SChris Cain     }
607815f9f55STom Joseph }
608cbad219eSEddie James 
60931a2f13aSSheldon Bailey // Called upon pldm event To set powermode Safe Mode State for system.
61031a2f13aSSheldon Bailey void Manager::updateOccSafeMode(bool safeMode)
61131a2f13aSSheldon Bailey {
61231a2f13aSSheldon Bailey #ifdef POWER10
61331a2f13aSSheldon Bailey     pmode->updateDbusSafeMode(safeMode);
61431a2f13aSSheldon Bailey #endif
615c86d80faSChris Cain     // Update the processor throttle status on dbus
616c86d80faSChris Cain     for (auto& obj : statusObjects)
617c86d80faSChris Cain     {
618c86d80faSChris Cain         obj->updateThrottle(safeMode, THROTTLED_SAFE);
619c86d80faSChris Cain     }
62031a2f13aSSheldon Bailey }
62131a2f13aSSheldon Bailey 
622cbad219eSEddie James void Manager::sbeHRESETResult(instanceID instance, bool success)
623cbad219eSEddie James {
624cbad219eSEddie James     if (success)
625cbad219eSEddie James     {
626bae4d07eSChris Cain         log<level::INFO>(
62748002498SPatrick Williams             std::format("HRESET succeeded (OCC{})", instance).c_str());
628cbad219eSEddie James 
629cbad219eSEddie James         setSBEState(instance, SBE_STATE_BOOTED);
630cbad219eSEddie James 
631cbad219eSEddie James         return;
632cbad219eSEddie James     }
633cbad219eSEddie James 
634cbad219eSEddie James     setSBEState(instance, SBE_STATE_FAILED);
635cbad219eSEddie James 
636cbad219eSEddie James     if (sbeCanDump(instance))
637cbad219eSEddie James     {
638bae4d07eSChris Cain         log<level::INFO>(
63948002498SPatrick Williams             std::format("HRESET failed (OCC{}), triggering SBE dump", instance)
640bae4d07eSChris Cain                 .c_str());
641cbad219eSEddie James 
642cbad219eSEddie James         auto& bus = utils::getBus();
643cbad219eSEddie James         uint32_t src6 = instance << 16;
644cbad219eSEddie James         uint32_t logId =
645cbad219eSEddie James             FFDC::createPEL("org.open_power.Processor.Error.SbeChipOpTimeout",
646cbad219eSEddie James                             src6, "SBE command timeout");
647cbad219eSEddie James 
648cbad219eSEddie James         try
649cbad219eSEddie James         {
650f3a4a69fSGeorge Liu             constexpr auto interface = "xyz.openbmc_project.Dump.Create";
651f3a4a69fSGeorge Liu             constexpr auto function = "CreateDump";
652f3a4a69fSGeorge Liu 
653*d7542c83SPatrick Williams             std::string service =
654*d7542c83SPatrick Williams                 utils::getService(OP_DUMP_OBJ_PATH, interface);
6551173b2b1SDhruvaraj Subhashchandran             auto method = bus.new_method_call(service.c_str(), OP_DUMP_OBJ_PATH,
6561173b2b1SDhruvaraj Subhashchandran                                               interface, function);
657cbad219eSEddie James 
658cbad219eSEddie James             std::map<std::string, std::variant<std::string, uint64_t>>
659cbad219eSEddie James                 createParams{
660cbad219eSEddie James                     {"com.ibm.Dump.Create.CreateParameters.ErrorLogId",
661cbad219eSEddie James                      uint64_t(logId)},
662cbad219eSEddie James                     {"com.ibm.Dump.Create.CreateParameters.DumpType",
663cbad219eSEddie James                      "com.ibm.Dump.Create.DumpType.SBE"},
664cbad219eSEddie James                     {"com.ibm.Dump.Create.CreateParameters.FailingUnitId",
665cbad219eSEddie James                      uint64_t(instance)},
666cbad219eSEddie James                 };
667cbad219eSEddie James 
668cbad219eSEddie James             method.append(createParams);
669cbad219eSEddie James 
670cbad219eSEddie James             auto response = bus.call(method);
671cbad219eSEddie James         }
672af40808fSPatrick Williams         catch (const sdbusplus::exception_t& e)
673cbad219eSEddie James         {
674cbad219eSEddie James             constexpr auto ERROR_DUMP_DISABLED =
675cbad219eSEddie James                 "xyz.openbmc_project.Dump.Create.Error.Disabled";
676cbad219eSEddie James             if (e.name() == ERROR_DUMP_DISABLED)
677cbad219eSEddie James             {
678cbad219eSEddie James                 log<level::INFO>("Dump is disabled, skipping");
679cbad219eSEddie James             }
680cbad219eSEddie James             else
681cbad219eSEddie James             {
682cbad219eSEddie James                 log<level::ERR>("Dump failed");
683cbad219eSEddie James             }
684cbad219eSEddie James         }
685cbad219eSEddie James     }
686cbad219eSEddie James }
687cbad219eSEddie James 
688cbad219eSEddie James bool Manager::sbeCanDump(unsigned int instance)
689cbad219eSEddie James {
690cbad219eSEddie James     struct pdbg_target* proc = getPdbgTarget(instance);
691cbad219eSEddie James 
692cbad219eSEddie James     if (!proc)
693cbad219eSEddie James     {
694cbad219eSEddie James         // allow the dump in the error case
695cbad219eSEddie James         return true;
696cbad219eSEddie James     }
697cbad219eSEddie James 
698cbad219eSEddie James     try
699cbad219eSEddie James     {
700cbad219eSEddie James         if (!openpower::phal::sbe::isDumpAllowed(proc))
701cbad219eSEddie James         {
702cbad219eSEddie James             return false;
703cbad219eSEddie James         }
704cbad219eSEddie James 
705cbad219eSEddie James         if (openpower::phal::pdbg::isSbeVitalAttnActive(proc))
706cbad219eSEddie James         {
707cbad219eSEddie James             return false;
708cbad219eSEddie James         }
709cbad219eSEddie James     }
710cbad219eSEddie James     catch (openpower::phal::exception::SbeError& e)
711cbad219eSEddie James     {
712cbad219eSEddie James         log<level::INFO>("Failed to query SBE state");
713cbad219eSEddie James     }
714cbad219eSEddie James 
715cbad219eSEddie James     // allow the dump in the error case
716cbad219eSEddie James     return true;
717cbad219eSEddie James }
718cbad219eSEddie James 
719cbad219eSEddie James void Manager::setSBEState(unsigned int instance, enum sbe_state state)
720cbad219eSEddie James {
721cbad219eSEddie James     struct pdbg_target* proc = getPdbgTarget(instance);
722cbad219eSEddie James 
723cbad219eSEddie James     if (!proc)
724cbad219eSEddie James     {
725cbad219eSEddie James         return;
726cbad219eSEddie James     }
727cbad219eSEddie James 
728cbad219eSEddie James     try
729cbad219eSEddie James     {
730cbad219eSEddie James         openpower::phal::sbe::setState(proc, state);
731cbad219eSEddie James     }
732cbad219eSEddie James     catch (const openpower::phal::exception::SbeError& e)
733cbad219eSEddie James     {
734cbad219eSEddie James         log<level::ERR>("Failed to set SBE state");
735cbad219eSEddie James     }
736cbad219eSEddie James }
737cbad219eSEddie James 
738cbad219eSEddie James struct pdbg_target* Manager::getPdbgTarget(unsigned int instance)
739cbad219eSEddie James {
740cbad219eSEddie James     if (!pdbgInitialized)
741cbad219eSEddie James     {
742cbad219eSEddie James         try
743cbad219eSEddie James         {
744cbad219eSEddie James             openpower::phal::pdbg::init();
745cbad219eSEddie James             pdbgInitialized = true;
746cbad219eSEddie James         }
747cbad219eSEddie James         catch (const openpower::phal::exception::PdbgError& e)
748cbad219eSEddie James         {
749cbad219eSEddie James             log<level::ERR>("pdbg initialization failed");
750cbad219eSEddie James             return nullptr;
751cbad219eSEddie James         }
752cbad219eSEddie James     }
753cbad219eSEddie James 
754cbad219eSEddie James     struct pdbg_target* proc = nullptr;
755cbad219eSEddie James     pdbg_for_each_class_target("proc", proc)
756cbad219eSEddie James     {
757cbad219eSEddie James         if (pdbg_target_index(proc) == instance)
758cbad219eSEddie James         {
759cbad219eSEddie James             return proc;
760cbad219eSEddie James         }
761cbad219eSEddie James     }
762cbad219eSEddie James 
763cbad219eSEddie James     log<level::ERR>("Failed to get pdbg target");
764cbad219eSEddie James     return nullptr;
765cbad219eSEddie James }
766815f9f55STom Joseph #endif
767815f9f55STom Joseph 
768a8857c50SChris Cain void Manager::pollerTimerExpired()
769a8857c50SChris Cain {
770a8857c50SChris Cain     if (!_pollTimer)
771a8857c50SChris Cain     {
772a8857c50SChris Cain         log<level::ERR>(
773a8857c50SChris Cain             "Manager::pollerTimerExpired() ERROR: Timer not defined");
774a8857c50SChris Cain         return;
775a8857c50SChris Cain     }
776a8857c50SChris Cain 
777a8857c50SChris Cain     for (auto& obj : statusObjects)
778a8857c50SChris Cain     {
779a7b74dc3SChris Cain         if (!obj->occActive())
780a7b74dc3SChris Cain         {
781a7b74dc3SChris Cain             // OCC is not running yet
782a7b74dc3SChris Cain #ifdef READ_OCC_SENSORS
7835d66a0aaSChris Cain             auto id = obj->getOccInstanceID();
784c8dd4599SSheldon Bailey             setSensorValueToNaN(id);
785a7b74dc3SChris Cain #endif
786a7b74dc3SChris Cain             continue;
787a7b74dc3SChris Cain         }
788a7b74dc3SChris Cain 
789a8857c50SChris Cain         // Read sysfs to force kernel to poll OCC
790a8857c50SChris Cain         obj->readOccState();
791bb895cb8SChicago Duan 
792bb895cb8SChicago Duan #ifdef READ_OCC_SENSORS
793bb895cb8SChicago Duan         // Read occ sensor values
7945d66a0aaSChris Cain         getSensorValues(obj);
795bb895cb8SChicago Duan #endif
796a8857c50SChris Cain     }
797a8857c50SChris Cain 
798a7b74dc3SChris Cain     if (activeCount > 0)
799a7b74dc3SChris Cain     {
800a8857c50SChris Cain         // Restart OCC poll timer
801a8857c50SChris Cain         _pollTimer->restartOnce(std::chrono::seconds(pollInterval));
802a8857c50SChris Cain     }
803a7b74dc3SChris Cain     else
804a7b74dc3SChris Cain     {
805a7b74dc3SChris Cain         // No OCCs running, so poll timer will not be restarted
806a7b74dc3SChris Cain         log<level::INFO>(
80748002498SPatrick Williams             std::format(
808a7b74dc3SChris Cain                 "Manager::pollerTimerExpired: poll timer will not be restarted")
809a7b74dc3SChris Cain                 .c_str());
810a7b74dc3SChris Cain     }
811a7b74dc3SChris Cain }
812a8857c50SChris Cain 
813bb895cb8SChicago Duan #ifdef READ_OCC_SENSORS
814ae157b68SChris Cain void Manager::readTempSensors(const fs::path& path, uint32_t occInstance)
815bb895cb8SChicago Duan {
816818cc8d7SMatt Spinler     // There may be more than one sensor with the same FRU type
817818cc8d7SMatt Spinler     // and label so make two passes: the first to read the temps
818818cc8d7SMatt Spinler     // from sysfs, and the second to put them on D-Bus after
819818cc8d7SMatt Spinler     // resolving any conflicts.
820818cc8d7SMatt Spinler     std::map<std::string, double> sensorData;
821818cc8d7SMatt Spinler 
822bb895cb8SChicago Duan     std::regex expr{"temp\\d+_label$"}; // Example: temp5_label
823bb895cb8SChicago Duan     for (auto& file : fs::directory_iterator(path))
824bb895cb8SChicago Duan     {
825bb895cb8SChicago Duan         if (!std::regex_search(file.path().string(), expr))
826bb895cb8SChicago Duan         {
827bb895cb8SChicago Duan             continue;
828bb895cb8SChicago Duan         }
829bb895cb8SChicago Duan 
830a26f1527SMatt Spinler         uint32_t labelValue{0};
831a26f1527SMatt Spinler 
832a26f1527SMatt Spinler         try
833a26f1527SMatt Spinler         {
834a26f1527SMatt Spinler             labelValue = readFile<uint32_t>(file.path());
835a26f1527SMatt Spinler         }
836a26f1527SMatt Spinler         catch (const std::system_error& e)
837a26f1527SMatt Spinler         {
838a26f1527SMatt Spinler             log<level::DEBUG>(
83948002498SPatrick Williams                 std::format("readTempSensors: Failed reading {}, errno = {}",
840a26f1527SMatt Spinler                             file.path().string(), e.code().value())
841a26f1527SMatt Spinler                     .c_str());
842bb895cb8SChicago Duan             continue;
843bb895cb8SChicago Duan         }
844bb895cb8SChicago Duan 
845bb895cb8SChicago Duan         const std::string& tempLabel = "label";
846bb895cb8SChicago Duan         const std::string filePathString = file.path().string().substr(
847bb895cb8SChicago Duan             0, file.path().string().length() - tempLabel.length());
848a26f1527SMatt Spinler 
849a26f1527SMatt Spinler         uint32_t fruTypeValue{0};
850a26f1527SMatt Spinler         try
851bb895cb8SChicago Duan         {
852a26f1527SMatt Spinler             fruTypeValue = readFile<uint32_t>(filePathString + fruTypeSuffix);
853a26f1527SMatt Spinler         }
854a26f1527SMatt Spinler         catch (const std::system_error& e)
855a26f1527SMatt Spinler         {
856bb895cb8SChicago Duan             log<level::DEBUG>(
85748002498SPatrick Williams                 std::format("readTempSensors: Failed reading {}, errno = {}",
858a26f1527SMatt Spinler                             filePathString + fruTypeSuffix, e.code().value())
859bb895cb8SChicago Duan                     .c_str());
860bb895cb8SChicago Duan             continue;
861bb895cb8SChicago Duan         }
862bb895cb8SChicago Duan 
863*d7542c83SPatrick Williams         std::string sensorPath =
864*d7542c83SPatrick Williams             OCC_SENSORS_ROOT + std::string("/temperature/");
865bb895cb8SChicago Duan 
866ace67d85SMatt Spinler         std::string dvfsTempPath;
867ace67d85SMatt Spinler 
868bb895cb8SChicago Duan         if (fruTypeValue == VRMVdd)
869bb895cb8SChicago Duan         {
870*d7542c83SPatrick Williams             sensorPath.append(
871*d7542c83SPatrick Williams                 "vrm_vdd" + std::to_string(occInstance) + "_temp");
872bb895cb8SChicago Duan         }
873ace67d85SMatt Spinler         else if (fruTypeValue == processorIoRing)
874ace67d85SMatt Spinler         {
875*d7542c83SPatrick Williams             sensorPath.append(
876*d7542c83SPatrick Williams                 "proc" + std::to_string(occInstance) + "_ioring_temp");
877ace67d85SMatt Spinler             dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/proc" +
878ae157b68SChris Cain                            std::to_string(occInstance) + "_ioring_dvfs_temp";
879ace67d85SMatt Spinler         }
880bb895cb8SChicago Duan         else
881bb895cb8SChicago Duan         {
88214d1402dSMatt Spinler             uint16_t type = (labelValue & 0xFF000000) >> 24;
88314d1402dSMatt Spinler             uint16_t instanceID = labelValue & 0x0000FFFF;
884bb895cb8SChicago Duan 
885bb895cb8SChicago Duan             if (type == OCC_DIMM_TEMP_SENSOR_TYPE)
886bb895cb8SChicago Duan             {
8878b8abeedSMatt Spinler                 if (fruTypeValue == fruTypeNotAvailable)
8888b8abeedSMatt Spinler                 {
8898b8abeedSMatt Spinler                     // Not all DIMM related temps are available to read
8908b8abeedSMatt Spinler                     // (no _input file in this case)
8918b8abeedSMatt Spinler                     continue;
8928b8abeedSMatt Spinler                 }
893bb895cb8SChicago Duan                 auto iter = dimmTempSensorName.find(fruTypeValue);
894bb895cb8SChicago Duan                 if (iter == dimmTempSensorName.end())
895bb895cb8SChicago Duan                 {
896b5ca1015SGeorge Liu                     log<level::ERR>(
89748002498SPatrick Williams                         std::format(
898b5ca1015SGeorge Liu                             "readTempSensors: Fru type error! fruTypeValue = {}) ",
899bb895cb8SChicago Duan                             fruTypeValue)
900bb895cb8SChicago Duan                             .c_str());
901bb895cb8SChicago Duan                     continue;
902bb895cb8SChicago Duan                 }
903bb895cb8SChicago Duan 
904*d7542c83SPatrick Williams                 sensorPath.append(
905*d7542c83SPatrick Williams                     "dimm" + std::to_string(instanceID) + iter->second);
906ad8f4524SMatt Spinler 
907ad8f4524SMatt Spinler                 dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/" +
908ad8f4524SMatt Spinler                                dimmDVFSSensorName.at(fruTypeValue);
909bb895cb8SChicago Duan             }
910bb895cb8SChicago Duan             else if (type == OCC_CPU_TEMP_SENSOR_TYPE)
911bb895cb8SChicago Duan             {
912ace67d85SMatt Spinler                 if (fruTypeValue == processorCore)
913bb895cb8SChicago Duan                 {
914ff7afd98SMatt Spinler                     // The OCC reports small core temps, of which there are
915ff7afd98SMatt Spinler                     // two per big core.  All current P10 systems are in big
916ff7afd98SMatt Spinler                     // core mode, so use a big core name.
917ff7afd98SMatt Spinler                     uint16_t coreNum = instanceID / 2;
918ff7afd98SMatt Spinler                     uint16_t tempNum = instanceID % 2;
919ae157b68SChris Cain                     sensorPath.append("proc" + std::to_string(occInstance) +
920ae157b68SChris Cain                                       "_core" + std::to_string(coreNum) + "_" +
921ff7afd98SMatt Spinler                                       std::to_string(tempNum) + "_temp");
922ace67d85SMatt Spinler 
923ae157b68SChris Cain                     dvfsTempPath =
924ae157b68SChris Cain                         std::string{OCC_SENSORS_ROOT} + "/temperature/proc" +
925ae157b68SChris Cain                         std::to_string(occInstance) + "_core_dvfs_temp";
926bb895cb8SChicago Duan                 }
927bb895cb8SChicago Duan                 else
928bb895cb8SChicago Duan                 {
929bb895cb8SChicago Duan                     continue;
930bb895cb8SChicago Duan                 }
931bb895cb8SChicago Duan             }
932ace67d85SMatt Spinler             else
933ace67d85SMatt Spinler             {
934ace67d85SMatt Spinler                 continue;
935ace67d85SMatt Spinler             }
936ace67d85SMatt Spinler         }
937ace67d85SMatt Spinler 
938ace67d85SMatt Spinler         // The dvfs temp file only needs to be read once per chip per type.
939ace67d85SMatt Spinler         if (!dvfsTempPath.empty() &&
940ace67d85SMatt Spinler             !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath))
941ace67d85SMatt Spinler         {
942ace67d85SMatt Spinler             try
943ace67d85SMatt Spinler             {
944ace67d85SMatt Spinler                 auto dvfsValue = readFile<double>(filePathString + maxSuffix);
945ace67d85SMatt Spinler 
946ace67d85SMatt Spinler                 dbus::OccDBusSensors::getOccDBus().setDvfsTemp(
947ace67d85SMatt Spinler                     dvfsTempPath, dvfsValue * std::pow(10, -3));
948ace67d85SMatt Spinler             }
949ace67d85SMatt Spinler             catch (const std::system_error& e)
950ace67d85SMatt Spinler             {
951ace67d85SMatt Spinler                 log<level::DEBUG>(
95248002498SPatrick Williams                     std::format(
953ace67d85SMatt Spinler                         "readTempSensors: Failed reading {}, errno = {}",
954ace67d85SMatt Spinler                         filePathString + maxSuffix, e.code().value())
955ace67d85SMatt Spinler                         .c_str());
956ace67d85SMatt Spinler             }
957ace67d85SMatt Spinler         }
958bb895cb8SChicago Duan 
959a26f1527SMatt Spinler         uint32_t faultValue{0};
960a26f1527SMatt Spinler         try
961bb895cb8SChicago Duan         {
962a26f1527SMatt Spinler             faultValue = readFile<uint32_t>(filePathString + faultSuffix);
963a26f1527SMatt Spinler         }
964a26f1527SMatt Spinler         catch (const std::system_error& e)
965a26f1527SMatt Spinler         {
966a26f1527SMatt Spinler             log<level::DEBUG>(
96748002498SPatrick Williams                 std::format("readTempSensors: Failed reading {}, errno = {}",
968a26f1527SMatt Spinler                             filePathString + faultSuffix, e.code().value())
969a26f1527SMatt Spinler                     .c_str());
970a26f1527SMatt Spinler             continue;
971a26f1527SMatt Spinler         }
972bb895cb8SChicago Duan 
973ae157b68SChris Cain         double tempValue{0};
974ae157b68SChris Cain         // NOTE: if OCC sends back 0xFF, kernal sets this fault value to 1.
975bb895cb8SChicago Duan         if (faultValue != 0)
976bb895cb8SChicago Duan         {
977ae157b68SChris Cain             tempValue = std::numeric_limits<double>::quiet_NaN();
978ae157b68SChris Cain         }
979ae157b68SChris Cain         else
980818cc8d7SMatt Spinler         {
981ae157b68SChris Cain             // Read the temperature
982a26f1527SMatt Spinler             try
983bb895cb8SChicago Duan             {
984a26f1527SMatt Spinler                 tempValue = readFile<double>(filePathString + inputSuffix);
985a26f1527SMatt Spinler             }
986a26f1527SMatt Spinler             catch (const std::system_error& e)
987a26f1527SMatt Spinler             {
988a26f1527SMatt Spinler                 log<level::DEBUG>(
98948002498SPatrick Williams                     std::format(
990ae157b68SChris Cain                         "readTempSensors: Failed reading {}, errno = {}",
991a26f1527SMatt Spinler                         filePathString + inputSuffix, e.code().value())
992a26f1527SMatt Spinler                         .c_str());
993cd0940b5SSheldon Bailey 
994cd0940b5SSheldon Bailey                 // if errno == EAGAIN(Resource temporarily unavailable) then set
995ae157b68SChris Cain                 // temp to 0, to avoid using old temp, and affecting FAN
996ae157b68SChris Cain                 // Control.
997cd0940b5SSheldon Bailey                 if (e.code().value() == EAGAIN)
998cd0940b5SSheldon Bailey                 {
999cd0940b5SSheldon Bailey                     tempValue = 0;
1000cd0940b5SSheldon Bailey                 }
1001cd0940b5SSheldon Bailey                 // else the errno would be something like
1002cd0940b5SSheldon Bailey                 //     EBADF(Bad file descriptor)
1003cd0940b5SSheldon Bailey                 // or ENOENT(No such file or directory)
1004cd0940b5SSheldon Bailey                 else
1005cd0940b5SSheldon Bailey                 {
1006a26f1527SMatt Spinler                     continue;
1007a26f1527SMatt Spinler                 }
1008cd0940b5SSheldon Bailey             }
1009ae157b68SChris Cain         }
1010bb895cb8SChicago Duan 
1011818cc8d7SMatt Spinler         // If this object path already has a value, only overwite
1012818cc8d7SMatt Spinler         // it if the previous one was an NaN or a smaller value.
1013818cc8d7SMatt Spinler         auto existing = sensorData.find(sensorPath);
1014818cc8d7SMatt Spinler         if (existing != sensorData.end())
1015818cc8d7SMatt Spinler         {
1016ae157b68SChris Cain             // Multiple sensors found for this FRU type
1017ae157b68SChris Cain             if ((std::isnan(existing->second) && (tempValue == 0)) ||
1018ae157b68SChris Cain                 ((existing->second == 0) && std::isnan(tempValue)))
1019ae157b68SChris Cain             {
1020ae157b68SChris Cain                 // One of the redundant sensors has failed (0xFF/nan), and the
1021ae157b68SChris Cain                 // other sensor has no reading (0), so set the FRU to NaN to
1022ae157b68SChris Cain                 // force fan increase
1023ae157b68SChris Cain                 tempValue = std::numeric_limits<double>::quiet_NaN();
1024ae157b68SChris Cain                 existing->second = tempValue;
1025ae157b68SChris Cain             }
1026818cc8d7SMatt Spinler             if (std::isnan(existing->second) || (tempValue > existing->second))
1027818cc8d7SMatt Spinler             {
1028818cc8d7SMatt Spinler                 existing->second = tempValue;
1029818cc8d7SMatt Spinler             }
1030818cc8d7SMatt Spinler         }
1031818cc8d7SMatt Spinler         else
1032818cc8d7SMatt Spinler         {
1033ae157b68SChris Cain             // First sensor for this FRU type
1034818cc8d7SMatt Spinler             sensorData[sensorPath] = tempValue;
1035818cc8d7SMatt Spinler         }
1036818cc8d7SMatt Spinler     }
1037bb895cb8SChicago Duan 
1038818cc8d7SMatt Spinler     // Now publish the values on D-Bus.
1039818cc8d7SMatt Spinler     for (const auto& [objectPath, value] : sensorData)
1040818cc8d7SMatt Spinler     {
1041818cc8d7SMatt Spinler         dbus::OccDBusSensors::getOccDBus().setValue(objectPath,
1042818cc8d7SMatt Spinler                                                     value * std::pow(10, -3));
1043bb895cb8SChicago Duan 
1044818cc8d7SMatt Spinler         dbus::OccDBusSensors::getOccDBus().setOperationalStatus(
1045818cc8d7SMatt Spinler             objectPath, !std::isnan(value));
1046818cc8d7SMatt Spinler 
1047818cc8d7SMatt Spinler         if (existingSensors.find(objectPath) == existingSensors.end())
10486fa848a9SChris Cain         {
10495d66a0aaSChris Cain             dbus::OccDBusSensors::getOccDBus().setChassisAssociation(
1050818cc8d7SMatt Spinler                 objectPath);
10516fa848a9SChris Cain         }
10526fa848a9SChris Cain 
1053ae157b68SChris Cain         existingSensors[objectPath] = occInstance;
1054bb895cb8SChicago Duan     }
1055bb895cb8SChicago Duan }
1056bb895cb8SChicago Duan 
1057bb895cb8SChicago Duan std::optional<std::string>
1058bb895cb8SChicago Duan     Manager::getPowerLabelFunctionID(const std::string& value)
1059bb895cb8SChicago Duan {
1060bb895cb8SChicago Duan     // If the value is "system", then the FunctionID is "system".
1061bb895cb8SChicago Duan     if (value == "system")
1062bb895cb8SChicago Duan     {
1063bb895cb8SChicago Duan         return value;
1064bb895cb8SChicago Duan     }
1065bb895cb8SChicago Duan 
1066bb895cb8SChicago Duan     // If the value is not "system", then the label value have 3 numbers, of
1067bb895cb8SChicago Duan     // which we only care about the middle one:
1068bb895cb8SChicago Duan     // <sensor id>_<function id>_<apss channel>
1069bb895cb8SChicago Duan     // eg: The value is "0_10_5" , then the FunctionID is "10".
1070bb895cb8SChicago Duan     if (value.find("_") == std::string::npos)
1071bb895cb8SChicago Duan     {
1072bb895cb8SChicago Duan         return std::nullopt;
1073bb895cb8SChicago Duan     }
1074bb895cb8SChicago Duan 
1075bb895cb8SChicago Duan     auto powerLabelValue = value.substr((value.find("_") + 1));
1076bb895cb8SChicago Duan 
1077bb895cb8SChicago Duan     if (powerLabelValue.find("_") == std::string::npos)
1078bb895cb8SChicago Duan     {
1079bb895cb8SChicago Duan         return std::nullopt;
1080bb895cb8SChicago Duan     }
1081bb895cb8SChicago Duan 
1082bb895cb8SChicago Duan     return powerLabelValue.substr(0, powerLabelValue.find("_"));
1083bb895cb8SChicago Duan }
1084bb895cb8SChicago Duan 
1085bb895cb8SChicago Duan void Manager::readPowerSensors(const fs::path& path, uint32_t id)
1086bb895cb8SChicago Duan {
1087bb895cb8SChicago Duan     std::regex expr{"power\\d+_label$"}; // Example: power5_label
1088bb895cb8SChicago Duan     for (auto& file : fs::directory_iterator(path))
1089bb895cb8SChicago Duan     {
1090bb895cb8SChicago Duan         if (!std::regex_search(file.path().string(), expr))
1091bb895cb8SChicago Duan         {
1092bb895cb8SChicago Duan             continue;
1093bb895cb8SChicago Duan         }
1094bb895cb8SChicago Duan 
1095a26f1527SMatt Spinler         std::string labelValue;
1096a26f1527SMatt Spinler         try
1097a26f1527SMatt Spinler         {
1098a26f1527SMatt Spinler             labelValue = readFile<std::string>(file.path());
1099a26f1527SMatt Spinler         }
1100a26f1527SMatt Spinler         catch (const std::system_error& e)
1101a26f1527SMatt Spinler         {
1102a26f1527SMatt Spinler             log<level::DEBUG>(
110348002498SPatrick Williams                 std::format("readPowerSensors: Failed reading {}, errno = {}",
1104a26f1527SMatt Spinler                             file.path().string(), e.code().value())
1105a26f1527SMatt Spinler                     .c_str());
1106bb895cb8SChicago Duan             continue;
1107bb895cb8SChicago Duan         }
1108bb895cb8SChicago Duan 
1109bb895cb8SChicago Duan         auto functionID = getPowerLabelFunctionID(labelValue);
1110bb895cb8SChicago Duan         if (functionID == std::nullopt)
1111bb895cb8SChicago Duan         {
1112bb895cb8SChicago Duan             continue;
1113bb895cb8SChicago Duan         }
1114bb895cb8SChicago Duan 
1115bb895cb8SChicago Duan         const std::string& tempLabel = "label";
1116bb895cb8SChicago Duan         const std::string filePathString = file.path().string().substr(
1117bb895cb8SChicago Duan             0, file.path().string().length() - tempLabel.length());
1118bb895cb8SChicago Duan 
1119bb895cb8SChicago Duan         std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/");
1120bb895cb8SChicago Duan 
1121bb895cb8SChicago Duan         auto iter = powerSensorName.find(*functionID);
1122bb895cb8SChicago Duan         if (iter == powerSensorName.end())
1123bb895cb8SChicago Duan         {
1124bb895cb8SChicago Duan             continue;
1125bb895cb8SChicago Duan         }
1126bb895cb8SChicago Duan         sensorPath.append(iter->second);
1127bb895cb8SChicago Duan 
1128a26f1527SMatt Spinler         double tempValue{0};
1129a26f1527SMatt Spinler 
1130a26f1527SMatt Spinler         try
1131bb895cb8SChicago Duan         {
1132a26f1527SMatt Spinler             tempValue = readFile<double>(filePathString + inputSuffix);
1133a26f1527SMatt Spinler         }
1134a26f1527SMatt Spinler         catch (const std::system_error& e)
1135bb895cb8SChicago Duan         {
1136a26f1527SMatt Spinler             log<level::DEBUG>(
113748002498SPatrick Williams                 std::format("readPowerSensors: Failed reading {}, errno = {}",
1138a26f1527SMatt Spinler                             filePathString + inputSuffix, e.code().value())
1139a26f1527SMatt Spinler                     .c_str());
1140bb895cb8SChicago Duan             continue;
1141bb895cb8SChicago Duan         }
1142bb895cb8SChicago Duan 
11435d66a0aaSChris Cain         dbus::OccDBusSensors::getOccDBus().setUnit(
1144d84a8335SChris Cain             sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts");
1145d84a8335SChris Cain 
11465d66a0aaSChris Cain         dbus::OccDBusSensors::getOccDBus().setValue(
1147bb895cb8SChicago Duan             sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3));
1148bb895cb8SChicago Duan 
1149*d7542c83SPatrick Williams         dbus::OccDBusSensors::getOccDBus().setOperationalStatus(
1150*d7542c83SPatrick Williams             sensorPath, true);
1151bb895cb8SChicago Duan 
11525901abdaSMatt Spinler         if (existingSensors.find(sensorPath) == existingSensors.end())
11535901abdaSMatt Spinler         {
11545d66a0aaSChris Cain             dbus::OccDBusSensors::getOccDBus().setChassisAssociation(
11555d66a0aaSChris Cain                 sensorPath);
11565901abdaSMatt Spinler         }
11575901abdaSMatt Spinler 
1158bb895cb8SChicago Duan         existingSensors[sensorPath] = id;
1159bb895cb8SChicago Duan     }
1160bb895cb8SChicago Duan     return;
1161bb895cb8SChicago Duan }
1162bb895cb8SChicago Duan 
1163c8dd4599SSheldon Bailey void Manager::setSensorValueToNaN(uint32_t id) const
1164bb895cb8SChicago Duan {
1165bb895cb8SChicago Duan     for (const auto& [sensorPath, occId] : existingSensors)
1166bb895cb8SChicago Duan     {
1167bb895cb8SChicago Duan         if (occId == id)
1168bb895cb8SChicago Duan         {
11695d66a0aaSChris Cain             dbus::OccDBusSensors::getOccDBus().setValue(
1170bb895cb8SChicago Duan                 sensorPath, std::numeric_limits<double>::quiet_NaN());
1171c8dd4599SSheldon Bailey 
1172*d7542c83SPatrick Williams             dbus::OccDBusSensors::getOccDBus().setOperationalStatus(
1173*d7542c83SPatrick Williams                 sensorPath, true);
1174bb895cb8SChicago Duan         }
1175bb895cb8SChicago Duan     }
1176bb895cb8SChicago Duan     return;
1177bb895cb8SChicago Duan }
1178bb895cb8SChicago Duan 
1179373af757SSheldon Bailey void Manager::setSensorValueToNonFunctional(uint32_t id) const
1180373af757SSheldon Bailey {
1181373af757SSheldon Bailey     for (const auto& [sensorPath, occId] : existingSensors)
1182373af757SSheldon Bailey     {
1183373af757SSheldon Bailey         if (occId == id)
1184373af757SSheldon Bailey         {
1185373af757SSheldon Bailey             dbus::OccDBusSensors::getOccDBus().setValue(
1186373af757SSheldon Bailey                 sensorPath, std::numeric_limits<double>::quiet_NaN());
1187373af757SSheldon Bailey 
1188*d7542c83SPatrick Williams             dbus::OccDBusSensors::getOccDBus().setOperationalStatus(
1189*d7542c83SPatrick Williams                 sensorPath, false);
1190373af757SSheldon Bailey         }
1191373af757SSheldon Bailey     }
1192373af757SSheldon Bailey     return;
1193373af757SSheldon Bailey }
1194373af757SSheldon Bailey 
11955d66a0aaSChris Cain void Manager::getSensorValues(std::unique_ptr<Status>& occ)
1196bb895cb8SChicago Duan {
1197e2d0a43cSChris Cain     static bool tracedError[8] = {0};
1198e2d0a43cSChris Cain     const fs::path sensorPath = occ->getHwmonPath();
11995d66a0aaSChris Cain     const uint32_t id = occ->getOccInstanceID();
1200bb895cb8SChicago Duan 
1201e2d0a43cSChris Cain     if (fs::exists(sensorPath))
1202e2d0a43cSChris Cain     {
1203bb895cb8SChicago Duan         // Read temperature sensors
1204e2d0a43cSChris Cain         readTempSensors(sensorPath, id);
1205bb895cb8SChicago Duan 
12065d66a0aaSChris Cain         if (occ->isMasterOcc())
1207bb895cb8SChicago Duan         {
1208bb895cb8SChicago Duan             // Read power sensors
1209e2d0a43cSChris Cain             readPowerSensors(sensorPath, id);
1210e2d0a43cSChris Cain         }
1211e2d0a43cSChris Cain         tracedError[id] = false;
1212e2d0a43cSChris Cain     }
1213e2d0a43cSChris Cain     else
1214e2d0a43cSChris Cain     {
1215e2d0a43cSChris Cain         if (!tracedError[id])
1216e2d0a43cSChris Cain         {
1217e2d0a43cSChris Cain             log<level::ERR>(
121848002498SPatrick Williams                 std::format(
1219e2d0a43cSChris Cain                     "Manager::getSensorValues: OCC{} sensor path missing: {}",
1220e2d0a43cSChris Cain                     id, sensorPath.c_str())
1221e2d0a43cSChris Cain                     .c_str());
1222e2d0a43cSChris Cain             tracedError[id] = true;
1223e2d0a43cSChris Cain         }
1224bb895cb8SChicago Duan     }
1225bb895cb8SChicago Duan 
1226bb895cb8SChicago Duan     return;
1227bb895cb8SChicago Duan }
1228bb895cb8SChicago Duan #endif
122917257673SChris Cain 
123017257673SChris Cain // Read the altitude from DBus
123117257673SChris Cain void Manager::readAltitude()
123217257673SChris Cain {
123317257673SChris Cain     static bool traceAltitudeErr = true;
123417257673SChris Cain 
123517257673SChris Cain     utils::PropertyValue altitudeProperty{};
123617257673SChris Cain     try
123717257673SChris Cain     {
123817257673SChris Cain         altitudeProperty = utils::getProperty(ALTITUDE_PATH, ALTITUDE_INTERFACE,
123917257673SChris Cain                                               ALTITUDE_PROP);
124017257673SChris Cain         auto sensorVal = std::get<double>(altitudeProperty);
124117257673SChris Cain         if (sensorVal < 0xFFFF)
124217257673SChris Cain         {
124317257673SChris Cain             if (sensorVal < 0)
124417257673SChris Cain             {
124517257673SChris Cain                 altitude = 0;
124617257673SChris Cain             }
124717257673SChris Cain             else
124817257673SChris Cain             {
124917257673SChris Cain                 // Round to nearest meter
125017257673SChris Cain                 altitude = uint16_t(sensorVal + 0.5);
125117257673SChris Cain             }
125248002498SPatrick Williams             log<level::DEBUG>(std::format("readAltitude: sensor={} ({}m)",
125317257673SChris Cain                                           sensorVal, altitude)
125417257673SChris Cain                                   .c_str());
125517257673SChris Cain             traceAltitudeErr = true;
125617257673SChris Cain         }
125717257673SChris Cain         else
125817257673SChris Cain         {
125917257673SChris Cain             if (traceAltitudeErr)
126017257673SChris Cain             {
126117257673SChris Cain                 traceAltitudeErr = false;
126217257673SChris Cain                 log<level::DEBUG>(
126348002498SPatrick Williams                     std::format("Invalid altitude value: {}", sensorVal)
126417257673SChris Cain                         .c_str());
126517257673SChris Cain             }
126617257673SChris Cain         }
126717257673SChris Cain     }
1268af40808fSPatrick Williams     catch (const sdbusplus::exception_t& e)
126917257673SChris Cain     {
127017257673SChris Cain         if (traceAltitudeErr)
127117257673SChris Cain         {
127217257673SChris Cain             traceAltitudeErr = false;
127317257673SChris Cain             log<level::INFO>(
127448002498SPatrick Williams                 std::format("Unable to read Altitude: {}", e.what()).c_str());
127517257673SChris Cain         }
127617257673SChris Cain         altitude = 0xFFFF; // not available
127717257673SChris Cain     }
127817257673SChris Cain }
127917257673SChris Cain 
128017257673SChris Cain // Callback function when ambient temperature changes
1281af40808fSPatrick Williams void Manager::ambientCallback(sdbusplus::message_t& msg)
128217257673SChris Cain {
128317257673SChris Cain     double currentTemp = 0;
128417257673SChris Cain     uint8_t truncatedTemp = 0xFF;
128517257673SChris Cain     std::string msgSensor;
128617257673SChris Cain     std::map<std::string, std::variant<double>> msgData;
128717257673SChris Cain     msg.read(msgSensor, msgData);
128817257673SChris Cain 
128917257673SChris Cain     auto valPropMap = msgData.find(AMBIENT_PROP);
129017257673SChris Cain     if (valPropMap == msgData.end())
129117257673SChris Cain     {
129217257673SChris Cain         log<level::DEBUG>("ambientCallback: Unknown ambient property changed");
129317257673SChris Cain         return;
129417257673SChris Cain     }
129517257673SChris Cain     currentTemp = std::get<double>(valPropMap->second);
129617257673SChris Cain     if (std::isnan(currentTemp))
129717257673SChris Cain     {
129817257673SChris Cain         truncatedTemp = 0xFF;
129917257673SChris Cain     }
130017257673SChris Cain     else
130117257673SChris Cain     {
130217257673SChris Cain         if (currentTemp < 0)
130317257673SChris Cain         {
130417257673SChris Cain             truncatedTemp = 0;
130517257673SChris Cain         }
130617257673SChris Cain         else
130717257673SChris Cain         {
130817257673SChris Cain             // Round to nearest degree C
130917257673SChris Cain             truncatedTemp = uint8_t(currentTemp + 0.5);
131017257673SChris Cain         }
131117257673SChris Cain     }
131217257673SChris Cain 
131317257673SChris Cain     // If ambient changes, notify OCCs
131417257673SChris Cain     if (truncatedTemp != ambient)
131517257673SChris Cain     {
131617257673SChris Cain         log<level::DEBUG>(
131748002498SPatrick Williams             std::format("ambientCallback: Ambient change from {} to {}C",
131817257673SChris Cain                         ambient, currentTemp)
131917257673SChris Cain                 .c_str());
132017257673SChris Cain 
132117257673SChris Cain         ambient = truncatedTemp;
132217257673SChris Cain         if (altitude == 0xFFFF)
132317257673SChris Cain         {
132417257673SChris Cain             // No altitude yet, try reading again
132517257673SChris Cain             readAltitude();
132617257673SChris Cain         }
132717257673SChris Cain 
132817257673SChris Cain         log<level::DEBUG>(
132948002498SPatrick Williams             std::format("ambientCallback: Ambient: {}C, altitude: {}m", ambient,
133017257673SChris Cain                         altitude)
133117257673SChris Cain                 .c_str());
133217257673SChris Cain #ifdef POWER10
133317257673SChris Cain         // Send ambient and altitude to all OCCs
133417257673SChris Cain         for (auto& obj : statusObjects)
133517257673SChris Cain         {
133617257673SChris Cain             if (obj->occActive())
133717257673SChris Cain             {
133817257673SChris Cain                 obj->sendAmbient(ambient, altitude);
133917257673SChris Cain             }
134017257673SChris Cain         }
134117257673SChris Cain #endif // POWER10
134217257673SChris Cain     }
134317257673SChris Cain }
134417257673SChris Cain 
134517257673SChris Cain // return the current ambient and altitude readings
134617257673SChris Cain void Manager::getAmbientData(bool& ambientValid, uint8_t& ambientTemp,
134717257673SChris Cain                              uint16_t& altitudeValue) const
134817257673SChris Cain {
134917257673SChris Cain     ambientValid = true;
135017257673SChris Cain     ambientTemp = ambient;
135117257673SChris Cain     altitudeValue = altitude;
135217257673SChris Cain 
135317257673SChris Cain     if (ambient == 0xFF)
135417257673SChris Cain     {
135517257673SChris Cain         ambientValid = false;
135617257673SChris Cain     }
135717257673SChris Cain }
135817257673SChris Cain 
1359a7b74dc3SChris Cain #ifdef POWER10
13607f89e4d1SChris Cain // Called when waitForAllOccsTimer expires
13617f89e4d1SChris Cain // After the first OCC goes active, this timer will be started (60 seconds)
1362a7b74dc3SChris Cain void Manager::occsNotAllRunning()
1363a7b74dc3SChris Cain {
1364a7b74dc3SChris Cain     if (activeCount != statusObjects.size())
1365a7b74dc3SChris Cain     {
1366a7b74dc3SChris Cain         // Not all OCCs went active
1367a7b74dc3SChris Cain         log<level::WARNING>(
136848002498SPatrick Williams             std::format(
1369a7b74dc3SChris Cain                 "occsNotAllRunning: Active OCC count ({}) does not match expected count ({})",
1370a7b74dc3SChris Cain                 activeCount, statusObjects.size())
1371a7b74dc3SChris Cain                 .c_str());
13727f89e4d1SChris Cain         // Procs may be garded, so may be expected
1373a7b74dc3SChris Cain     }
1374a7b74dc3SChris Cain 
1375a7b74dc3SChris Cain     validateOccMaster();
1376a7b74dc3SChris Cain }
1377755af102SChris Cain 
1378755af102SChris Cain #ifdef PLDM
1379c33171bbSChris Cain // Called when throttlePldmTraceTimer expires.
1380a19bd428SChris Cain // If this timer expires, that indicates there are no OCC active sensor PDRs
1381c33171bbSChris Cain // found which will trigger pldm traces to be throttled.
1382c33171bbSChris Cain // The second time this timer expires, a PEL will get created.
1383c33171bbSChris Cain void Manager::throttlePldmTraceExpired()
1384755af102SChris Cain {
13857651c06bSChris Cain     if (utils::isHostRunning())
13867651c06bSChris Cain     {
1387c33171bbSChris Cain         if (!onPldmTimeoutCreatePel)
1388c33171bbSChris Cain         {
1389755af102SChris Cain             // Throttle traces
1390755af102SChris Cain             pldmHandle->setTraceThrottle(true);
1391c33171bbSChris Cain             // Restart timer to log a PEL when timer expires
1392c33171bbSChris Cain             onPldmTimeoutCreatePel = true;
1393c33171bbSChris Cain             throttlePldmTraceTimer->restartOnce(40min);
1394c33171bbSChris Cain         }
1395c33171bbSChris Cain         else
1396c33171bbSChris Cain         {
1397c33171bbSChris Cain             log<level::ERR>(
1398c33171bbSChris Cain                 "throttlePldmTraceExpired(): OCC active sensors still not available!");
13994b82f3e3SChris Cain             // Create PEL
14004b82f3e3SChris Cain             createPldmSensorPEL();
14014b82f3e3SChris Cain         }
1402c33171bbSChris Cain     }
14037651c06bSChris Cain     else
14047651c06bSChris Cain     {
14057651c06bSChris Cain         // Make sure traces are not throttled
14067651c06bSChris Cain         pldmHandle->setTraceThrottle(false);
14077651c06bSChris Cain         log<level::INFO>(
1408c33171bbSChris Cain             "throttlePldmTraceExpired(): host it not running ignoring sensor timer");
14097651c06bSChris Cain     }
14107651c06bSChris Cain }
14114b82f3e3SChris Cain 
14124b82f3e3SChris Cain void Manager::createPldmSensorPEL()
14134b82f3e3SChris Cain {
14144b82f3e3SChris Cain     Error::Descriptor d = Error::Descriptor(MISSING_OCC_SENSORS_PATH);
14154b82f3e3SChris Cain     std::map<std::string, std::string> additionalData;
14164b82f3e3SChris Cain 
14174b82f3e3SChris Cain     additionalData.emplace("_PID", std::to_string(getpid()));
14184b82f3e3SChris Cain 
14194b82f3e3SChris Cain     log<level::INFO>(
14204b82f3e3SChris Cain         std::format(
14214b82f3e3SChris Cain             "createPldmSensorPEL(): Unable to find PLDM sensors for the OCCs")
14224b82f3e3SChris Cain             .c_str());
14234b82f3e3SChris Cain 
14244b82f3e3SChris Cain     auto& bus = utils::getBus();
14254b82f3e3SChris Cain 
14264b82f3e3SChris Cain     try
14274b82f3e3SChris Cain     {
14284b82f3e3SChris Cain         FFDCFiles ffdc;
14294b82f3e3SChris Cain         // Add occ-control journal traces to PEL FFDC
14304b82f3e3SChris Cain         auto occJournalFile =
14314b82f3e3SChris Cain             FFDC::addJournalEntries(ffdc, "openpower-occ-control", 40);
14324b82f3e3SChris Cain 
14334b82f3e3SChris Cain         static constexpr auto loggingObjectPath =
14344b82f3e3SChris Cain             "/xyz/openbmc_project/logging";
14354b82f3e3SChris Cain         static constexpr auto opLoggingInterface = "org.open_power.Logging.PEL";
1436*d7542c83SPatrick Williams         std::string service =
1437*d7542c83SPatrick Williams             utils::getService(loggingObjectPath, opLoggingInterface);
1438*d7542c83SPatrick Williams         auto method =
1439*d7542c83SPatrick Williams             bus.new_method_call(service.c_str(), loggingObjectPath,
1440*d7542c83SPatrick Williams                                 opLoggingInterface, "CreatePELWithFFDCFiles");
14414b82f3e3SChris Cain 
14421c3349e4SChris Cain         // Set level to Warning (Predictive).
14434b82f3e3SChris Cain         auto level =
14444b82f3e3SChris Cain             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
14454b82f3e3SChris Cain                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
14461c3349e4SChris Cain                     Warning);
14474b82f3e3SChris Cain 
14484b82f3e3SChris Cain         method.append(d.path, level, additionalData, ffdc);
14494b82f3e3SChris Cain         bus.call(method);
14504b82f3e3SChris Cain     }
14514b82f3e3SChris Cain     catch (const sdbusplus::exception_t& e)
14524b82f3e3SChris Cain     {
14534b82f3e3SChris Cain         log<level::ERR>(
14544b82f3e3SChris Cain             std::format("Failed to create MISSING_OCC_SENSORS PEL: {}",
14554b82f3e3SChris Cain                         e.what())
14564b82f3e3SChris Cain                 .c_str());
14574b82f3e3SChris Cain     }
1458755af102SChris Cain }
1459755af102SChris Cain #endif // PLDM
1460a7b74dc3SChris Cain #endif // POWER10
1461a7b74dc3SChris Cain 
1462a7b74dc3SChris Cain // Verify single master OCC and start presence monitor
1463a7b74dc3SChris Cain void Manager::validateOccMaster()
1464a7b74dc3SChris Cain {
1465a7b74dc3SChris Cain     int masterInstance = -1;
1466a7b74dc3SChris Cain     for (auto& obj : statusObjects)
1467a7b74dc3SChris Cain     {
1468bd551de3SChris Cain         auto instance = obj->getOccInstanceID();
1469bae4d07eSChris Cain #ifdef POWER10
1470bae4d07eSChris Cain         if (!obj->occActive())
1471bae4d07eSChris Cain         {
1472bae4d07eSChris Cain             if (utils::isHostRunning())
1473bae4d07eSChris Cain             {
1474bd551de3SChris Cain                 // Check if sensor was queued while waiting for discovery
1475bd551de3SChris Cain                 auto match = queuedActiveState.find(instance);
1476bd551de3SChris Cain                 if (match != queuedActiveState.end())
1477bd551de3SChris Cain                 {
14787f89e4d1SChris Cain                     queuedActiveState.erase(match);
1479bd551de3SChris Cain                     log<level::INFO>(
148048002498SPatrick Williams                         std::format(
1481bd551de3SChris Cain                             "validateOccMaster: OCC{} is ACTIVE (queued)",
1482bd551de3SChris Cain                             instance)
1483bd551de3SChris Cain                             .c_str());
1484bd551de3SChris Cain                     obj->occActive(true);
1485bd551de3SChris Cain                 }
1486bd551de3SChris Cain                 else
1487bd551de3SChris Cain                 {
1488bae4d07eSChris Cain                     // OCC does not appear to be active yet, check active sensor
1489fb0a5c3cSPatrick Williams #ifdef PLDM
1490bd551de3SChris Cain                     pldmHandle->checkActiveSensor(instance);
1491fb0a5c3cSPatrick Williams #endif
1492bae4d07eSChris Cain                     if (obj->occActive())
1493bae4d07eSChris Cain                     {
1494bae4d07eSChris Cain                         log<level::INFO>(
149548002498SPatrick Williams                             std::format(
1496bae4d07eSChris Cain                                 "validateOccMaster: OCC{} is ACTIVE after reading sensor",
1497bd551de3SChris Cain                                 instance)
1498bae4d07eSChris Cain                                 .c_str());
1499bae4d07eSChris Cain                     }
1500bae4d07eSChris Cain                 }
1501bd551de3SChris Cain             }
1502bae4d07eSChris Cain             else
1503bae4d07eSChris Cain             {
1504bae4d07eSChris Cain                 log<level::WARNING>(
150548002498SPatrick Williams                     std::format(
1506bae4d07eSChris Cain                         "validateOccMaster: HOST is not running (OCC{})",
1507bd551de3SChris Cain                         instance)
1508bae4d07eSChris Cain                         .c_str());
1509bae4d07eSChris Cain                 return;
1510bae4d07eSChris Cain             }
1511bae4d07eSChris Cain         }
1512bae4d07eSChris Cain #endif // POWER10
1513bae4d07eSChris Cain 
1514a7b74dc3SChris Cain         if (obj->isMasterOcc())
1515a7b74dc3SChris Cain         {
15165d66a0aaSChris Cain             obj->addPresenceWatchMaster();
15175d66a0aaSChris Cain 
1518a7b74dc3SChris Cain             if (masterInstance == -1)
1519a7b74dc3SChris Cain             {
1520bd551de3SChris Cain                 masterInstance = instance;
1521a7b74dc3SChris Cain             }
1522a7b74dc3SChris Cain             else
1523a7b74dc3SChris Cain             {
1524a7b74dc3SChris Cain                 log<level::ERR>(
152548002498SPatrick Williams                     std::format(
1526a7b74dc3SChris Cain                         "validateOccMaster: Multiple OCC masters! ({} and {})",
1527bd551de3SChris Cain                         masterInstance, instance)
1528a7b74dc3SChris Cain                         .c_str());
1529a7b74dc3SChris Cain                 // request reset
15309789e71fSEddie James                 obj->deviceError(Error::Descriptor(PRESENCE_ERROR_PATH));
1531a7b74dc3SChris Cain             }
1532a7b74dc3SChris Cain         }
1533a7b74dc3SChris Cain     }
1534bae4d07eSChris Cain 
1535a7b74dc3SChris Cain     if (masterInstance < 0)
1536a7b74dc3SChris Cain     {
1537bae4d07eSChris Cain         log<level::ERR>(
153848002498SPatrick Williams             std::format("validateOccMaster: Master OCC not found! (of {} OCCs)",
1539bae4d07eSChris Cain                         statusObjects.size())
1540bae4d07eSChris Cain                 .c_str());
1541a7b74dc3SChris Cain         // request reset
15429789e71fSEddie James         statusObjects.front()->deviceError(
15439789e71fSEddie James             Error::Descriptor(PRESENCE_ERROR_PATH));
1544a7b74dc3SChris Cain     }
1545a7b74dc3SChris Cain     else
1546a7b74dc3SChris Cain     {
1547a7b74dc3SChris Cain         log<level::INFO>(
154848002498SPatrick Williams             std::format("validateOccMaster: OCC{} is master of {} OCCs",
154936f9cdedSChris Cain                         masterInstance, activeCount)
1550a7b74dc3SChris Cain                 .c_str());
155131a2f13aSSheldon Bailey #ifdef POWER10
155231a2f13aSSheldon Bailey         pmode->updateDbusSafeMode(false);
155331a2f13aSSheldon Bailey #endif
1554a7b74dc3SChris Cain     }
1555a7b74dc3SChris Cain }
1556a7b74dc3SChris Cain 
155740501a23SChris Cain void Manager::updatePcapBounds() const
155840501a23SChris Cain {
155940501a23SChris Cain     if (pcap)
156040501a23SChris Cain     {
156140501a23SChris Cain         pcap->updatePcapBounds();
156240501a23SChris Cain     }
156340501a23SChris Cain }
156440501a23SChris Cain 
1565dfc7ec73SVishwanatha Subbanna } // namespace occ
1566dfc7ec73SVishwanatha Subbanna } // namespace open_power
1567