blob: 1dfdcb0bc8053453222bd684f66c268047562c72 [file] [log] [blame]
Brandon Wyman18a24d92022-04-19 22:48:34 +00001#include "config.h"
2
Brandon Wymana0f33ce2019-10-17 18:32:29 -05003#include "psu_manager.hpp"
4
5#include "utility.hpp"
6
Brandon Wymanb76ab242020-09-16 18:06:06 -05007#include <fmt/format.h>
8#include <sys/types.h>
9#include <unistd.h>
10
Shawn McCarney9252b7e2022-06-10 12:47:38 -050011#include <algorithm>
Brandon Wymanecbecbc2021-08-31 22:53:21 +000012#include <regex>
Shawn McCarney9252b7e2022-06-10 12:47:38 -050013#include <set>
Brandon Wymanecbecbc2021-08-31 22:53:21 +000014
Brandon Wymanaed1f752019-11-25 18:10:52 -060015using namespace phosphor::logging;
16
Brandon Wyman63ea78b2020-09-24 16:49:09 -050017namespace phosphor::power::manager
Brandon Wymana0f33ce2019-10-17 18:32:29 -050018{
Adriana Kobylakc9b05732022-03-19 15:15:10 +000019constexpr auto managerBusName = "xyz.openbmc_project.Power.PSUMonitor";
20constexpr auto objectManagerObjPath =
21 "/xyz/openbmc_project/power/power_supplies";
22constexpr auto powerSystemsInputsObjPath =
23 "/xyz/openbmc_project/power/power_supplies/chassis0/psus";
Brandon Wymana0f33ce2019-10-17 18:32:29 -050024
Brandon Wyman510acaa2020-11-05 18:32:04 -060025constexpr auto IBMCFFPSInterface =
26 "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
27constexpr auto i2cBusProp = "I2CBus";
28constexpr auto i2cAddressProp = "I2CAddress";
29constexpr auto psuNameProp = "Name";
B. J. Wyman681b2a32021-04-20 22:31:22 +000030constexpr auto presLineName = "NamedPresenceGpio";
Brandon Wyman510acaa2020-11-05 18:32:04 -060031
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060032constexpr auto supportedConfIntf =
33 "xyz.openbmc_project.Configuration.SupportedConfiguration";
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060034
Brandon Wymanc9e840e2022-05-10 20:48:41 +000035constexpr auto INPUT_HISTORY_SYNC_DELAY = 5;
Brandon Wyman18a24d92022-04-19 22:48:34 +000036
Brandon Wyman510acaa2020-11-05 18:32:04 -060037PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
Adriana Kobylakc9b05732022-03-19 15:15:10 +000038 bus(bus), powerSystemInputs(bus, powerSystemsInputsObjPath),
Brandon Wymanc3324422022-03-24 20:30:57 +000039 objectManager(bus, objectManagerObjPath),
40 historyManager(bus, "/org/open_power/sensors")
Brandon Wyman510acaa2020-11-05 18:32:04 -060041{
Brandon Wyman510acaa2020-11-05 18:32:04 -060042 // Subscribe to InterfacesAdded before doing a property read, otherwise
43 // the interface could be created after the read attempt but before the
44 // match is created.
45 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
46 bus,
47 sdbusplus::bus::match::rules::interfacesAdded() +
48 sdbusplus::bus::match::rules::sender(
49 "xyz.openbmc_project.EntityManager"),
50 std::bind(&PSUManager::entityManagerIfaceAdded, this,
51 std::placeholders::_1));
52 getPSUConfiguration();
53 getSystemProperties();
54
Adriana Kobylakc9b05732022-03-19 15:15:10 +000055 // Request the bus name before the analyze() function, which is the one that
56 // determines the brownout condition and sets the status d-bus property.
57 bus.request_name(managerBusName);
58
Brandon Wyman510acaa2020-11-05 18:32:04 -060059 using namespace sdeventplus;
60 auto interval = std::chrono::milliseconds(1000);
61 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
62 e, std::bind(&PSUManager::analyze, this), interval);
63
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +000064 validationTimer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
65 e, std::bind(&PSUManager::validateConfig, this));
66
Adriana Kobylakc0a07582021-10-13 15:52:25 +000067 try
68 {
69 powerConfigGPIO = createGPIO("power-config-full-load");
70 }
71 catch (const std::exception& e)
72 {
73 // Ignore error, GPIO may not be implemented in this system.
74 powerConfigGPIO = nullptr;
75 }
76
Brandon Wyman510acaa2020-11-05 18:32:04 -060077 // Subscribe to power state changes
78 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
79 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
80 bus,
81 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
82 POWER_IFACE),
83 [this](auto& msg) { this->powerStateChanged(msg); });
84
85 initialize();
86}
87
Brandon Wyman510acaa2020-11-05 18:32:04 -060088void PSUManager::getPSUConfiguration()
89{
90 using namespace phosphor::power::util;
91 auto depth = 0;
92 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
93
94 psus.clear();
95
96 // I should get a map of objects back.
97 // Each object will have a path, a service, and an interface.
98 // The interface should match the one passed into this function.
99 for (const auto& [path, services] : objects)
100 {
101 auto service = services.begin()->first;
102
103 if (path.empty() || service.empty())
104 {
105 continue;
106 }
107
108 // For each object in the array of objects, I want to get properties
109 // from the service, path, and interface.
110 auto properties =
111 getAllProperties(bus, path, IBMCFFPSInterface, service);
112
113 getPSUProperties(properties);
114 }
115
116 if (psus.empty())
117 {
118 // Interface or properties not found. Let the Interfaces Added callback
119 // process the information once the interfaces are added to D-Bus.
120 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
121 }
122}
123
124void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
125{
126 // From passed in properties, I want to get: I2CBus, I2CAddress,
127 // and Name. Create a power supply object, using Name to build the inventory
128 // path.
129 const auto basePSUInvPath =
130 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
131 uint64_t* i2cbus = nullptr;
132 uint64_t* i2caddr = nullptr;
133 std::string* psuname = nullptr;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000134 std::string* preslineptr = nullptr;
Brandon Wyman510acaa2020-11-05 18:32:04 -0600135
136 for (const auto& property : properties)
137 {
138 try
139 {
140 if (property.first == i2cBusProp)
141 {
142 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
143 }
144 else if (property.first == i2cAddressProp)
145 {
146 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
147 }
148 else if (property.first == psuNameProp)
149 {
150 psuname = std::get_if<std::string>(&properties[psuNameProp]);
151 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000152 else if (property.first == presLineName)
153 {
154 preslineptr =
155 std::get_if<std::string>(&properties[presLineName]);
156 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600157 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500158 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000159 {}
Brandon Wyman510acaa2020-11-05 18:32:04 -0600160 }
161
162 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
163 {
164 std::string invpath = basePSUInvPath;
165 invpath.push_back(psuname->back());
B. J. Wyman681b2a32021-04-20 22:31:22 +0000166 std::string presline = "";
Brandon Wyman510acaa2020-11-05 18:32:04 -0600167
168 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
169
B. J. Wyman681b2a32021-04-20 22:31:22 +0000170 if (nullptr != preslineptr)
171 {
172 presline = *preslineptr;
173 }
174
Brandon Wymanecbecbc2021-08-31 22:53:21 +0000175 auto invMatch =
176 std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) {
177 return psu->getInventoryPath() == invpath;
178 });
179 if (invMatch != psus.end())
180 {
181 // This power supply has the same inventory path as the one with
182 // information just added to D-Bus.
183 // Changes to GPIO line name unlikely, so skip checking.
184 // Changes to the I2C bus and address unlikely, as that would
185 // require corresponding device tree updates.
186 // Return out to avoid duplicate object creation.
187 return;
188 }
189
Brandon Wymanc3324422022-03-24 20:30:57 +0000190 constexpr auto driver = "ibm-cffps";
B. J. Wyman681b2a32021-04-20 22:31:22 +0000191 log<level::DEBUG>(
Brandon Wymanc3324422022-03-24 20:30:57 +0000192 fmt::format(
193 "make PowerSupply bus: {} addr: {} driver: {} presline: {}",
194 *i2cbus, *i2caddr, driver, presline)
B. J. Wyman681b2a32021-04-20 22:31:22 +0000195 .c_str());
196 auto psu = std::make_unique<PowerSupply>(bus, invpath, *i2cbus,
Brandon Wymanc3324422022-03-24 20:30:57 +0000197 *i2caddr, driver, presline);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600198 psus.emplace_back(std::move(psu));
Adriana Kobylak9ba38232021-11-16 20:27:45 +0000199
200 // Subscribe to power supply presence changes
201 auto presenceMatch = std::make_unique<sdbusplus::bus::match_t>(
202 bus,
203 sdbusplus::bus::match::rules::propertiesChanged(invpath,
204 INVENTORY_IFACE),
205 [this](auto& msg) { this->presenceChanged(msg); });
206 presenceMatches.emplace_back(std::move(presenceMatch));
Brandon Wyman510acaa2020-11-05 18:32:04 -0600207 }
208
209 if (psus.empty())
210 {
211 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
212 }
213}
214
Adriana Kobylake1074d82021-03-16 20:46:44 +0000215void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
216{
217 try
218 {
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000219 auto propIt = properties.find("SupportedType");
220 if (propIt == properties.end())
221 {
222 return;
223 }
224 const std::string* type = std::get_if<std::string>(&(propIt->second));
225 if ((type == nullptr) || (*type != "PowerSupply"))
226 {
227 return;
228 }
229
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000230 propIt = properties.find("SupportedModel");
231 if (propIt == properties.end())
232 {
233 return;
234 }
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000235 const std::string* model = std::get_if<std::string>(&(propIt->second));
236 if (model == nullptr)
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000237 {
238 return;
239 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000240
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000241 sys_properties sys;
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000242 propIt = properties.find("RedundantCount");
Adriana Kobylake1074d82021-03-16 20:46:44 +0000243 if (propIt != properties.end())
244 {
245 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
246 if (count != nullptr)
247 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000248 sys.powerSupplyCount = *count;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000249 }
250 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000251 propIt = properties.find("InputVoltage");
252 if (propIt != properties.end())
253 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000254 const std::vector<uint64_t>* voltage =
255 std::get_if<std::vector<uint64_t>>(&(propIt->second));
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000256 if (voltage != nullptr)
257 {
258 sys.inputVoltage = *voltage;
259 }
260 }
261
Adriana Kobylak886574c2021-11-01 18:22:28 +0000262 // The PowerConfigFullLoad is an optional property, default it to false
263 // since that's the default value of the power-config-full-load GPIO.
264 sys.powerConfigFullLoad = false;
265 propIt = properties.find("PowerConfigFullLoad");
266 if (propIt != properties.end())
267 {
268 const bool* fullLoad = std::get_if<bool>(&(propIt->second));
269 if (fullLoad != nullptr)
270 {
271 sys.powerConfigFullLoad = *fullLoad;
272 }
273 }
274
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000275 supportedConfigs.emplace(*model, sys);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000276 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500277 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000278 {}
Adriana Kobylake1074d82021-03-16 20:46:44 +0000279}
280
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600281void PSUManager::getSystemProperties()
282{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600283
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600284 try
285 {
286 util::DbusSubtree subtree =
287 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000288 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600289 {
290 throw std::runtime_error("Supported Configuration Not Found");
291 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600292
Adriana Kobylake1074d82021-03-16 20:46:44 +0000293 for (const auto& [objPath, services] : subtree)
294 {
295 std::string service = services.begin()->first;
296 if (objPath.empty() || service.empty())
297 {
298 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600299 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000300 auto properties = util::getAllProperties(
301 bus, objPath, supportedConfIntf, service);
302 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600303 }
304 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500305 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600306 {
307 // Interface or property not found. Let the Interfaces Added callback
308 // process the information once the interfaces are added to D-Bus.
309 }
310}
311
Brandon Wyman3e429132021-03-18 18:03:14 -0500312void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600313{
314 try
315 {
316 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000317 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600318 interfaces;
319 msg.read(objPath, interfaces);
320
321 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600322 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600323 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600324 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600325 }
326
Brandon Wyman510acaa2020-11-05 18:32:04 -0600327 itIntf = interfaces.find(IBMCFFPSInterface);
328 if (itIntf != interfaces.cend())
329 {
330 log<level::INFO>(
331 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
332 .c_str());
333 getPSUProperties(itIntf->second);
334 }
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000335
Brandon Wyman64e97752022-06-03 23:50:13 +0000336 updateMissingPSUs();
337
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000338 // Call to validate the psu configuration if the power is on and both
339 // the IBMCFFPSConnector and SupportedConfiguration interfaces have been
340 // processed
341 if (powerOn && !psus.empty() && !supportedConfigs.empty())
342 {
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000343 validationTimer->restartOnce(validationTimeout);
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000344 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600345 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500346 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600347 {
348 // Ignore, the property may be of a different type than expected.
349 }
350}
351
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500352void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
353{
354 int32_t state = 0;
355 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500356 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500357 msg.read(msgSensor, msgData);
358
359 // Check if it was the Present property that changed.
360 auto valPropMap = msgData.find("state");
361 if (valPropMap != msgData.end())
362 {
363 state = std::get<int32_t>(valPropMap->second);
364
365 // Power is on when state=1. Clear faults.
366 if (state)
367 {
368 powerOn = true;
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000369 validationTimer->restartOnce(validationTimeout);
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500370 clearFaults();
Brandon Wyman49b8ec42022-04-20 21:18:33 +0000371 syncHistory();
Adriana Kobylakc0a07582021-10-13 15:52:25 +0000372 setPowerConfigGPIO();
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500373 }
374 else
375 {
376 powerOn = false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000377 runValidateConfig = true;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500378 }
379 }
380}
381
Adriana Kobylak9ba38232021-11-16 20:27:45 +0000382void PSUManager::presenceChanged(sdbusplus::message::message& msg)
383{
384 std::string msgSensor;
385 std::map<std::string, std::variant<uint32_t, bool>> msgData;
386 msg.read(msgSensor, msgData);
387
388 // Check if it was the Present property that changed.
389 auto valPropMap = msgData.find(PRESENT_PROP);
390 if (valPropMap != msgData.end())
391 {
392 if (std::get<bool>(valPropMap->second))
393 {
394 // A PSU became present, force the PSU validation to run.
395 runValidateConfig = true;
396 validationTimer->restartOnce(validationTimeout);
397 }
398 }
399}
400
Brandon Wyman10fc6e82022-02-08 20:51:22 +0000401void PSUManager::setPowerSupplyError(const std::string& psuErrorString)
402{
403 using namespace sdbusplus::xyz::openbmc_project;
404 constexpr auto service = "org.openbmc.control.Power";
405 constexpr auto objPath = "/org/openbmc/control/power0";
406 constexpr auto interface = "org.openbmc.control.Power";
407 constexpr auto method = "setPowerSupplyError";
408
409 try
410 {
411 // Call D-Bus method to inform pseq of PSU error
412 auto methodMsg =
413 bus.new_method_call(service, objPath, interface, method);
414 methodMsg.append(psuErrorString);
415 auto callReply = bus.call(methodMsg);
416 }
417 catch (const std::exception& e)
418 {
419 log<level::INFO>(
420 fmt::format("Failed calling setPowerSupplyError due to error {}",
421 e.what())
422 .c_str());
423 }
424}
425
Brandon Wyman8b662882021-10-08 17:31:51 +0000426void PSUManager::createError(const std::string& faultName,
427 std::map<std::string, std::string>& additionalData)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500428{
429 using namespace sdbusplus::xyz::openbmc_project;
430 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
431 constexpr auto loggingCreateInterface =
432 "xyz.openbmc_project.Logging.Create";
433
434 try
435 {
Brandon Wyman8b662882021-10-08 17:31:51 +0000436 additionalData["_PID"] = std::to_string(getpid());
437
Brandon Wymanb76ab242020-09-16 18:06:06 -0500438 auto service =
439 util::getService(loggingObjectPath, loggingCreateInterface, bus);
440
441 if (service.empty())
442 {
443 log<level::ERR>("Unable to get logging manager service");
444 return;
445 }
446
447 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
448 loggingCreateInterface, "Create");
449
450 auto level = Logging::server::Entry::Level::Error;
451 method.append(faultName, level, additionalData);
452
453 auto reply = bus.call(method);
Brandon Wyman10fc6e82022-02-08 20:51:22 +0000454 setPowerSupplyError(faultName);
Brandon Wymanb76ab242020-09-16 18:06:06 -0500455 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500456 catch (const std::exception& e)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500457 {
458 log<level::ERR>(
459 fmt::format(
460 "Failed creating event log for fault {} due to error {}",
461 faultName, e.what())
462 .c_str());
463 }
464}
465
Brandon Wyman18a24d92022-04-19 22:48:34 +0000466void PSUManager::syncHistory()
467{
468 log<level::INFO>("Synchronize INPUT_HISTORY");
469
470 if (!syncHistoryGPIO)
471 {
472 syncHistoryGPIO = createGPIO(INPUT_HISTORY_SYNC_GPIO);
473 }
474 if (syncHistoryGPIO)
475 {
476 const std::chrono::milliseconds delay{INPUT_HISTORY_SYNC_DELAY};
477 syncHistoryGPIO->toggleLowHigh(delay);
478 for (auto& psu : psus)
479 {
480 psu->clearSyncHistoryRequired();
481 }
482 }
483
484 log<level::INFO>("Synchronize INPUT_HISTORY completed");
485}
486
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500487void PSUManager::analyze()
488{
Brandon Wyman18a24d92022-04-19 22:48:34 +0000489 auto syncHistoryRequired =
490 std::any_of(psus.begin(), psus.end(), [](const auto& psu) {
491 return psu->isSyncHistoryRequired();
492 });
493 if (syncHistoryRequired)
494 {
495 syncHistory();
496 }
497
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500498 for (auto& psu : psus)
499 {
500 psu->analyze();
501 }
502
Adriana Kobylake5b1e082022-03-02 15:37:32 +0000503 std::map<std::string, std::string> additionalData;
504
505 auto notPresentCount = decltype(psus.size())(
506 std::count_if(psus.begin(), psus.end(),
507 [](const auto& psu) { return !psu->isPresent(); }));
508
509 auto hasVINUVFaultCount = decltype(psus.size())(
510 std::count_if(psus.begin(), psus.end(),
511 [](const auto& psu) { return psu->hasVINUVFault(); }));
512
513 // The PSU D-Bus objects may not be available yet, so ignore if all
514 // PSUs are not present or the number of PSUs is still 0.
515 if ((psus.size() == (notPresentCount + hasVINUVFaultCount)) &&
516 (psus.size() != notPresentCount) && (psus.size() != 0))
517 {
518 // Brownout: All PSUs report an AC failure: At least one PSU reports
519 // AC loss VIN fault and the rest either report AC loss VIN fault as
520 // well or are not present.
521 additionalData["NOT_PRESENT_COUNT"] = std::to_string(notPresentCount);
522 additionalData["VIN_FAULT_COUNT"] = std::to_string(hasVINUVFaultCount);
523 setBrownout(additionalData);
524 }
525 else
526 {
527 // Brownout condition is not present or has been cleared
528 clearBrownout();
529 }
530
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600531 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500532 {
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600533 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500534 {
Brandon Wymanec0b8dc2021-10-08 21:49:43 +0000535 additionalData.clear();
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000536
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600537 if (!psu->isFaultLogged() && !psu->isPresent())
538 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000539 std::map<std::string, std::string> requiredPSUsData;
540 auto requiredPSUsPresent = hasRequiredPSUs(requiredPSUsData);
Shawn McCarney9252b7e2022-06-10 12:47:38 -0500541 if (!requiredPSUsPresent && isRequiredPSU(*psu))
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000542 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000543 additionalData.merge(requiredPSUsData);
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000544 // Create error for power supply missing.
545 additionalData["CALLOUT_INVENTORY_PATH"] =
546 psu->getInventoryPath();
547 additionalData["CALLOUT_PRIORITY"] = "H";
548 createError(
549 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
550 additionalData);
551 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600552 psu->setFaultLogged();
553 }
554 else if (!psu->isFaultLogged() && psu->isFaulted())
555 {
Brandon Wyman786b6f42021-10-12 20:21:41 +0000556 // Add STATUS_WORD and STATUS_MFR last response, in padded
557 // hexadecimal format.
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600558 additionalData["STATUS_WORD"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000559 fmt::format("{:#04x}", psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600560 additionalData["STATUS_MFR"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000561 fmt::format("{:#02x}", psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600562 // If there are faults being reported, they possibly could be
563 // related to a bug in the firmware version running on the power
564 // supply. Capture that data into the error as well.
565 additionalData["FW_VERSION"] = psu->getFWVersion();
566
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000567 if (psu->hasCommFault())
568 {
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000569 additionalData["STATUS_CML"] =
570 fmt::format("{:#02x}", psu->getStatusCML());
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000571 /* Attempts to communicate with the power supply have
572 * reached there limit. Create an error. */
573 additionalData["CALLOUT_DEVICE_PATH"] =
574 psu->getDevicePath();
575
576 createError(
577 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
578 additionalData);
579
580 psu->setFaultLogged();
581 }
582 else if ((psu->hasInputFault() || psu->hasVINUVFault()))
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600583 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000584 // Include STATUS_INPUT for input faults.
585 additionalData["STATUS_INPUT"] =
586 fmt::format("{:#02x}", psu->getStatusInput());
587
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600588 /* The power supply location might be needed if the input
589 * fault is due to a problem with the power supply itself.
590 * Include the inventory path with a call out priority of
591 * low.
592 */
593 additionalData["CALLOUT_INVENTORY_PATH"] =
594 psu->getInventoryPath();
595 additionalData["CALLOUT_PRIORITY"] = "L";
596 createError("xyz.openbmc_project.Power.PowerSupply.Error."
597 "InputFault",
598 additionalData);
599 psu->setFaultLogged();
600 }
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000601 else if (psu->hasPSKillFault())
602 {
603 createError(
604 "xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault",
605 additionalData);
606 psu->setFaultLogged();
607 }
Brandon Wyman6710ba22021-10-27 17:39:31 +0000608 else if (psu->hasVoutOVFault())
609 {
610 // Include STATUS_VOUT for Vout faults.
611 additionalData["STATUS_VOUT"] =
612 fmt::format("{:#02x}", psu->getStatusVout());
613
614 additionalData["CALLOUT_INVENTORY_PATH"] =
615 psu->getInventoryPath();
616
617 createError(
618 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
619 additionalData);
620
621 psu->setFaultLogged();
622 }
Brandon Wymanb10b3be2021-11-09 22:12:15 +0000623 else if (psu->hasIoutOCFault())
624 {
625 // Include STATUS_IOUT for Iout faults.
626 additionalData["STATUS_IOUT"] =
627 fmt::format("{:#02x}", psu->getStatusIout());
628
629 createError(
630 "xyz.openbmc_project.Power.PowerSupply.Error.IoutOCFault",
631 additionalData);
632
633 psu->setFaultLogged();
634 }
Brandon Wyman39ea02b2021-11-23 23:22:23 +0000635 else if (psu->hasVoutUVFault() || psu->hasPS12VcsFault() ||
636 psu->hasPSCS12VFault())
Brandon Wyman2cf46942021-10-28 19:09:16 +0000637 {
638 // Include STATUS_VOUT for Vout faults.
639 additionalData["STATUS_VOUT"] =
640 fmt::format("{:#02x}", psu->getStatusVout());
641
642 additionalData["CALLOUT_INVENTORY_PATH"] =
643 psu->getInventoryPath();
644
645 createError(
646 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
647 additionalData);
648
649 psu->setFaultLogged();
650 }
Brandon Wyman7ee4d7e2021-11-19 20:48:23 +0000651 // A fan fault should have priority over a temperature fault,
652 // since a failed fan may lead to a temperature problem.
653 else if (psu->hasFanFault())
654 {
655 // Include STATUS_TEMPERATURE and STATUS_FANS_1_2
656 additionalData["STATUS_TEMPERATURE"] =
657 fmt::format("{:#02x}", psu->getStatusTemperature());
658 additionalData["STATUS_FANS_1_2"] =
659 fmt::format("{:#02x}", psu->getStatusFans12());
660
661 additionalData["CALLOUT_INVENTORY_PATH"] =
662 psu->getInventoryPath();
663
664 createError(
665 "xyz.openbmc_project.Power.PowerSupply.Error.FanFault",
666 additionalData);
667
668 psu->setFaultLogged();
669 }
Brandon Wyman96893a42021-11-05 19:56:57 +0000670 else if (psu->hasTempFault())
671 {
672 // Include STATUS_TEMPERATURE for temperature faults.
673 additionalData["STATUS_TEMPERATURE"] =
674 fmt::format("{:#02x}", psu->getStatusTemperature());
675
676 additionalData["CALLOUT_INVENTORY_PATH"] =
677 psu->getInventoryPath();
678
679 createError(
680 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
681 additionalData);
682
683 psu->setFaultLogged();
684 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600685 else if (psu->hasMFRFault())
686 {
687 /* This can represent a variety of faults that result in
688 * calling out the power supply for replacement: Output
689 * OverCurrent, Output Under Voltage, and potentially other
690 * faults.
691 *
692 * Also plan on putting specific fault in AdditionalData,
693 * along with register names and register values
694 * (STATUS_WORD, STATUS_MFR, etc.).*/
695
696 additionalData["CALLOUT_INVENTORY_PATH"] =
697 psu->getInventoryPath();
698
699 createError(
700 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500701 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500702
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600703 psu->setFaultLogged();
704 }
Brandon Wyman2916ea52021-11-06 03:31:18 +0000705 else if (psu->hasPgoodFault())
706 {
707 /* POWER_GOOD# is not low, or OFF is on */
708 additionalData["CALLOUT_INVENTORY_PATH"] =
709 psu->getInventoryPath();
710
711 createError(
712 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
713 additionalData);
714
715 psu->setFaultLogged();
716 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500717 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500718 }
719 }
720}
721
Brandon Wyman64e97752022-06-03 23:50:13 +0000722void PSUManager::updateMissingPSUs()
723{
724 if (supportedConfigs.empty() || psus.empty())
725 {
726 return;
727 }
728
729 // Power supplies default to missing. If the power supply is present,
730 // the PowerSupply object will update the inventory Present property to
731 // true. If we have less than the required number of power supplies, and
732 // this power supply is missing, update the inventory Present property
733 // to false to indicate required power supply is missing. Avoid
734 // indicating power supply missing if not required.
735
736 auto presentCount =
737 std::count_if(psus.begin(), psus.end(),
738 [](const auto& psu) { return psu->isPresent(); });
739
740 for (const auto& config : supportedConfigs)
741 {
742 for (const auto& psu : psus)
743 {
744 auto psuModel = psu->getModelName();
745 auto psuShortName = psu->getShortName();
746 auto psuInventoryPath = psu->getInventoryPath();
747 auto relativeInvPath =
748 psuInventoryPath.substr(strlen(INVENTORY_OBJ_PATH));
749 auto psuPresent = psu->isPresent();
750 auto presProperty = false;
751 auto propReadFail = false;
752
753 try
754 {
755 presProperty = getPresence(bus, psuInventoryPath);
756 propReadFail = false;
757 }
758 catch (const sdbusplus::exception::exception& e)
759 {
760 propReadFail = true;
761 // Relying on property change or interface added to retry.
762 // Log an informational trace to the journal.
763 log<level::INFO>(
764 fmt::format("D-Bus property {} access failure exception",
765 psuInventoryPath)
766 .c_str());
767 }
768
769 if (psuModel.empty())
770 {
771 if (!propReadFail && (presProperty != psuPresent))
772 {
773 // We already have this property, and it is not false
774 // set Present to false
775 setPresence(bus, relativeInvPath, psuPresent, psuShortName);
776 }
777 continue;
778 }
779
780 if (config.first != psuModel)
781 {
782 continue;
783 }
784
785 if ((presentCount < config.second.powerSupplyCount) && !psuPresent)
786 {
787 setPresence(bus, relativeInvPath, psuPresent, psuShortName);
788 }
789 }
790 }
791}
792
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000793void PSUManager::validateConfig()
794{
Adriana Kobylakb23e4432022-04-01 14:22:47 +0000795 if (!runValidateConfig || supportedConfigs.empty() || psus.empty())
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000796 {
797 return;
798 }
799
Brandon Wyman9666ddf2022-04-27 21:53:14 +0000800 for (const auto& psu : psus)
801 {
802 if ((psu->hasInputFault() || psu->hasVINUVFault()))
803 {
804 // Do not try to validate if input voltage fault present.
805 validationTimer->restartOnce(validationTimeout);
806 return;
807 }
808 }
809
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000810 std::map<std::string, std::string> additionalData;
811 auto supported = hasRequiredPSUs(additionalData);
812 if (supported)
813 {
814 runValidateConfig = false;
815 return;
816 }
817
818 // Validation failed, create an error log.
819 // Return without setting the runValidateConfig flag to false because
820 // it may be that an additional supported configuration interface is
821 // added and we need to validate it to see if it matches this system.
822 createError("xyz.openbmc_project.Power.PowerSupply.Error.NotSupported",
823 additionalData);
824}
825
826bool PSUManager::hasRequiredPSUs(
827 std::map<std::string, std::string>& additionalData)
828{
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000829 std::string model{};
Adriana Kobylak523704d2021-09-21 15:55:41 +0000830 if (!validateModelName(model, additionalData))
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000831 {
Adriana Kobylak523704d2021-09-21 15:55:41 +0000832 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000833 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000834
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000835 auto presentCount =
836 std::count_if(psus.begin(), psus.end(),
837 [](const auto& psu) { return psu->isPresent(); });
838
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000839 // Validate the supported configurations. A system may support more than one
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000840 // power supply model configuration. Since all configurations need to be
841 // checked, the additional data would contain only the information of the
842 // last configuration that did not match.
843 std::map<std::string, std::string> tmpAdditionalData;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000844 for (const auto& config : supportedConfigs)
845 {
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000846 if (config.first != model)
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000847 {
848 continue;
849 }
Brandon Wyman64e97752022-06-03 23:50:13 +0000850
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000851 if (presentCount != config.second.powerSupplyCount)
852 {
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000853 tmpAdditionalData.clear();
854 tmpAdditionalData["EXPECTED_COUNT"] =
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000855 std::to_string(config.second.powerSupplyCount);
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000856 tmpAdditionalData["ACTUAL_COUNT"] = std::to_string(presentCount);
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000857 continue;
858 }
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000859
860 bool voltageValidated = true;
861 for (const auto& psu : psus)
862 {
863 if (!psu->isPresent())
864 {
865 // Only present PSUs report a valid input voltage
866 continue;
867 }
868
869 double actualInputVoltage;
870 int inputVoltage;
871 psu->getInputVoltage(actualInputVoltage, inputVoltage);
872
873 if (std::find(config.second.inputVoltage.begin(),
874 config.second.inputVoltage.end(),
875 inputVoltage) == config.second.inputVoltage.end())
876 {
877 tmpAdditionalData.clear();
878 tmpAdditionalData["ACTUAL_VOLTAGE"] =
879 std::to_string(actualInputVoltage);
880 for (const auto& voltage : config.second.inputVoltage)
881 {
882 tmpAdditionalData["EXPECTED_VOLTAGE"] +=
883 std::to_string(voltage) + " ";
884 }
885 tmpAdditionalData["CALLOUT_INVENTORY_PATH"] =
886 psu->getInventoryPath();
887
888 voltageValidated = false;
889 break;
890 }
891 }
892 if (!voltageValidated)
893 {
894 continue;
895 }
896
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000897 return true;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000898 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000899
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000900 additionalData.insert(tmpAdditionalData.begin(), tmpAdditionalData.end());
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000901 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000902}
903
Shawn McCarney9252b7e2022-06-10 12:47:38 -0500904unsigned int PSUManager::getRequiredPSUCount()
905{
906 unsigned int requiredCount{0};
907
908 // Verify we have the supported configuration and PSU information
909 if (!supportedConfigs.empty() && !psus.empty())
910 {
911 // Find PSU models. They should all be the same.
912 std::set<std::string> models{};
913 std::for_each(psus.begin(), psus.end(), [&models](const auto& psu) {
914 if (!psu->getModelName().empty())
915 {
916 models.insert(psu->getModelName());
917 }
918 });
919
920 // If exactly one model was found, find corresponding configuration
921 if (models.size() == 1)
922 {
923 const std::string& model = *(models.begin());
924 auto it = supportedConfigs.find(model);
925 if (it != supportedConfigs.end())
926 {
927 requiredCount = it->second.powerSupplyCount;
928 }
929 }
930 }
931
932 return requiredCount;
933}
934
935bool PSUManager::isRequiredPSU(const PowerSupply& psu)
936{
937 // Get required number of PSUs; if not found, we don't know if PSU required
938 unsigned int requiredCount = getRequiredPSUCount();
939 if (requiredCount == 0)
940 {
941 return false;
942 }
943
944 // If total PSU count <= the required count, all PSUs are required
945 if (psus.size() <= requiredCount)
946 {
947 return true;
948 }
949
950 // We don't currently get information from EntityManager about which PSUs
951 // are required, so we have to do some guesswork. First check if this PSU
952 // is present. If so, assume it is required.
953 if (psu.isPresent())
954 {
955 return true;
956 }
957
958 // This PSU is not present. Count the number of other PSUs that are
959 // present. If enough other PSUs are present, assume the specified PSU is
960 // not required.
961 unsigned int psuCount =
962 std::count_if(psus.begin(), psus.end(),
963 [](const auto& psu) { return psu->isPresent(); });
964 if (psuCount >= requiredCount)
965 {
966 return false;
967 }
968
969 // Check if this PSU was previously present. If so, assume it is required.
970 // We know it was previously present if it has a non-empty model name.
971 if (!psu.getModelName().empty())
972 {
973 return true;
974 }
975
976 // This PSU was never present. Count the number of other PSUs that were
977 // previously present. If including those PSUs is enough, assume the
978 // specified PSU is not required.
979 psuCount += std::count_if(psus.begin(), psus.end(), [](const auto& psu) {
980 return (!psu->isPresent() && !psu->getModelName().empty());
981 });
982 if (psuCount >= requiredCount)
983 {
984 return false;
985 }
986
987 // We still haven't found enough PSUs. Sort the inventory paths of PSUs
988 // that were never present. PSU inventory paths typically end with the PSU
989 // number (0, 1, 2, ...). Assume that lower-numbered PSUs are required.
990 std::vector<std::string> sortedPaths;
991 std::for_each(psus.begin(), psus.end(), [&sortedPaths](const auto& psu) {
992 if (!psu->isPresent() && psu->getModelName().empty())
993 {
994 sortedPaths.push_back(psu->getInventoryPath());
995 }
996 });
997 std::sort(sortedPaths.begin(), sortedPaths.end());
998
999 // Check if specified PSU is close enough to start of list to be required
1000 for (const auto& path : sortedPaths)
1001 {
1002 if (path == psu.getInventoryPath())
1003 {
1004 return true;
1005 }
1006 if (++psuCount >= requiredCount)
1007 {
1008 break;
1009 }
1010 }
1011
1012 // PSU was not close to start of sorted list; assume not required
1013 return false;
1014}
1015
Adriana Kobylak523704d2021-09-21 15:55:41 +00001016bool PSUManager::validateModelName(
1017 std::string& model, std::map<std::string, std::string>& additionalData)
1018{
1019 // Check that all PSUs have the same model name. Initialize the model
1020 // variable with the first PSU name found, then use it as a base to compare
Adriana Kobylakb70eae92022-01-20 22:09:56 +00001021 // against the rest of the PSUs and get its inventory path to use as callout
1022 // if needed.
Adriana Kobylak523704d2021-09-21 15:55:41 +00001023 model.clear();
Adriana Kobylakb70eae92022-01-20 22:09:56 +00001024 std::string modelInventoryPath{};
Adriana Kobylak523704d2021-09-21 15:55:41 +00001025 for (const auto& psu : psus)
1026 {
1027 auto psuModel = psu->getModelName();
1028 if (psuModel.empty())
1029 {
1030 continue;
1031 }
1032 if (model.empty())
1033 {
1034 model = psuModel;
Adriana Kobylakb70eae92022-01-20 22:09:56 +00001035 modelInventoryPath = psu->getInventoryPath();
Adriana Kobylak523704d2021-09-21 15:55:41 +00001036 continue;
1037 }
1038 if (psuModel != model)
1039 {
Adriana Kobylakb70eae92022-01-20 22:09:56 +00001040 if (supportedConfigs.find(model) != supportedConfigs.end())
1041 {
1042 // The base model is supported, callout the mismatched PSU. The
1043 // mismatched PSU may or may not be supported.
1044 additionalData["EXPECTED_MODEL"] = model;
1045 additionalData["ACTUAL_MODEL"] = psuModel;
1046 additionalData["CALLOUT_INVENTORY_PATH"] =
1047 psu->getInventoryPath();
1048 }
1049 else if (supportedConfigs.find(psuModel) != supportedConfigs.end())
1050 {
1051 // The base model is not supported, but the mismatched PSU is,
1052 // callout the base PSU.
1053 additionalData["EXPECTED_MODEL"] = psuModel;
1054 additionalData["ACTUAL_MODEL"] = model;
1055 additionalData["CALLOUT_INVENTORY_PATH"] = modelInventoryPath;
1056 }
1057 else
1058 {
1059 // The base model and the mismatched PSU are not supported or
1060 // could not be found in the supported configuration, callout
1061 // the mismatched PSU.
1062 additionalData["EXPECTED_MODEL"] = model;
1063 additionalData["ACTUAL_MODEL"] = psuModel;
1064 additionalData["CALLOUT_INVENTORY_PATH"] =
1065 psu->getInventoryPath();
1066 }
Adriana Kobylak523704d2021-09-21 15:55:41 +00001067 model.clear();
1068 return false;
1069 }
1070 }
1071 return true;
1072}
1073
Adriana Kobylakc0a07582021-10-13 15:52:25 +00001074void PSUManager::setPowerConfigGPIO()
1075{
1076 if (!powerConfigGPIO)
1077 {
1078 return;
1079 }
1080
1081 std::string model{};
1082 std::map<std::string, std::string> additionalData;
1083 if (!validateModelName(model, additionalData))
1084 {
1085 return;
1086 }
1087
1088 auto config = supportedConfigs.find(model);
1089 if (config != supportedConfigs.end())
1090 {
1091 // The power-config-full-load is an open drain GPIO. Set it to low (0)
1092 // if the supported configuration indicates that this system model
1093 // expects the maximum number of power supplies (full load set to true).
1094 // Else, set it to high (1), this is the default.
1095 auto powerConfigValue =
1096 (config->second.powerConfigFullLoad == true ? 0 : 1);
1097 auto flags = gpiod::line_request::FLAG_OPEN_DRAIN;
1098 powerConfigGPIO->write(powerConfigValue, flags);
1099 }
1100}
1101
Adriana Kobylake5b1e082022-03-02 15:37:32 +00001102void PSUManager::setBrownout(std::map<std::string, std::string>& additionalData)
1103{
1104 powerSystemInputs.status(sdbusplus::xyz::openbmc_project::State::Decorator::
1105 server::PowerSystemInputs::Status::Fault);
1106 if (!brownoutLogged)
1107 {
1108 if (powerOn)
1109 {
1110 createError(
1111 "xyz.openbmc_project.State.Shutdown.Power.Error.Blackout",
1112 additionalData);
1113 brownoutLogged = true;
1114 }
1115 }
1116}
1117
1118void PSUManager::clearBrownout()
1119{
1120 powerSystemInputs.status(sdbusplus::xyz::openbmc_project::State::Decorator::
1121 server::PowerSystemInputs::Status::Good);
1122 brownoutLogged = false;
1123}
1124
Brandon Wyman63ea78b2020-09-24 16:49:09 -05001125} // namespace phosphor::power::manager