blob: 257fdfb42fcfe294cd9bfe3de4ac5ecc119df7bf [file] [log] [blame]
Brandon Wymana0f33ce2019-10-17 18:32:29 -05001#include "psu_manager.hpp"
2
3#include "utility.hpp"
4
Brandon Wymanb76ab242020-09-16 18:06:06 -05005#include <fmt/format.h>
6#include <sys/types.h>
7#include <unistd.h>
8
Brandon Wymanecbecbc2021-08-31 22:53:21 +00009#include <regex>
10
Brandon Wymanaed1f752019-11-25 18:10:52 -060011using namespace phosphor::logging;
12
Brandon Wyman63ea78b2020-09-24 16:49:09 -050013namespace phosphor::power::manager
Brandon Wymana0f33ce2019-10-17 18:32:29 -050014{
15
Brandon Wyman510acaa2020-11-05 18:32:04 -060016constexpr auto IBMCFFPSInterface =
17 "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
18constexpr auto i2cBusProp = "I2CBus";
19constexpr auto i2cAddressProp = "I2CAddress";
20constexpr auto psuNameProp = "Name";
B. J. Wyman681b2a32021-04-20 22:31:22 +000021constexpr auto presLineName = "NamedPresenceGpio";
Brandon Wyman510acaa2020-11-05 18:32:04 -060022
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060023constexpr auto supportedConfIntf =
24 "xyz.openbmc_project.Configuration.SupportedConfiguration";
Adriana Kobylak9bab9e12021-02-24 15:32:03 -060025
Brandon Wyman510acaa2020-11-05 18:32:04 -060026PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
27 bus(bus)
28{
Brandon Wyman510acaa2020-11-05 18:32:04 -060029 // Subscribe to InterfacesAdded before doing a property read, otherwise
30 // the interface could be created after the read attempt but before the
31 // match is created.
32 entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
33 bus,
34 sdbusplus::bus::match::rules::interfacesAdded() +
35 sdbusplus::bus::match::rules::sender(
36 "xyz.openbmc_project.EntityManager"),
37 std::bind(&PSUManager::entityManagerIfaceAdded, this,
38 std::placeholders::_1));
39 getPSUConfiguration();
40 getSystemProperties();
41
42 using namespace sdeventplus;
43 auto interval = std::chrono::milliseconds(1000);
44 timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
45 e, std::bind(&PSUManager::analyze, this), interval);
46
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +000047 validationTimer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
48 e, std::bind(&PSUManager::validateConfig, this));
49
Adriana Kobylakc0a07582021-10-13 15:52:25 +000050 try
51 {
52 powerConfigGPIO = createGPIO("power-config-full-load");
53 }
54 catch (const std::exception& e)
55 {
56 // Ignore error, GPIO may not be implemented in this system.
57 powerConfigGPIO = nullptr;
58 }
59
Brandon Wyman510acaa2020-11-05 18:32:04 -060060 // Subscribe to power state changes
61 powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
62 powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
63 bus,
64 sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
65 POWER_IFACE),
66 [this](auto& msg) { this->powerStateChanged(msg); });
67
68 initialize();
69}
70
Brandon Wyman510acaa2020-11-05 18:32:04 -060071void PSUManager::getPSUConfiguration()
72{
73 using namespace phosphor::power::util;
74 auto depth = 0;
75 auto objects = getSubTree(bus, "/", IBMCFFPSInterface, depth);
76
77 psus.clear();
78
79 // I should get a map of objects back.
80 // Each object will have a path, a service, and an interface.
81 // The interface should match the one passed into this function.
82 for (const auto& [path, services] : objects)
83 {
84 auto service = services.begin()->first;
85
86 if (path.empty() || service.empty())
87 {
88 continue;
89 }
90
91 // For each object in the array of objects, I want to get properties
92 // from the service, path, and interface.
93 auto properties =
94 getAllProperties(bus, path, IBMCFFPSInterface, service);
95
96 getPSUProperties(properties);
97 }
98
99 if (psus.empty())
100 {
101 // Interface or properties not found. Let the Interfaces Added callback
102 // process the information once the interfaces are added to D-Bus.
103 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
104 }
105}
106
107void PSUManager::getPSUProperties(util::DbusPropertyMap& properties)
108{
109 // From passed in properties, I want to get: I2CBus, I2CAddress,
110 // and Name. Create a power supply object, using Name to build the inventory
111 // path.
112 const auto basePSUInvPath =
113 "/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply";
114 uint64_t* i2cbus = nullptr;
115 uint64_t* i2caddr = nullptr;
116 std::string* psuname = nullptr;
B. J. Wyman681b2a32021-04-20 22:31:22 +0000117 std::string* preslineptr = nullptr;
Brandon Wyman510acaa2020-11-05 18:32:04 -0600118
119 for (const auto& property : properties)
120 {
121 try
122 {
123 if (property.first == i2cBusProp)
124 {
125 i2cbus = std::get_if<uint64_t>(&properties[i2cBusProp]);
126 }
127 else if (property.first == i2cAddressProp)
128 {
129 i2caddr = std::get_if<uint64_t>(&properties[i2cAddressProp]);
130 }
131 else if (property.first == psuNameProp)
132 {
133 psuname = std::get_if<std::string>(&properties[psuNameProp]);
134 }
B. J. Wyman681b2a32021-04-20 22:31:22 +0000135 else if (property.first == presLineName)
136 {
137 preslineptr =
138 std::get_if<std::string>(&properties[presLineName]);
139 }
Brandon Wyman510acaa2020-11-05 18:32:04 -0600140 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500141 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000142 {}
Brandon Wyman510acaa2020-11-05 18:32:04 -0600143 }
144
145 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty()))
146 {
147 std::string invpath = basePSUInvPath;
148 invpath.push_back(psuname->back());
B. J. Wyman681b2a32021-04-20 22:31:22 +0000149 std::string presline = "";
Brandon Wyman510acaa2020-11-05 18:32:04 -0600150
151 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str());
152
B. J. Wyman681b2a32021-04-20 22:31:22 +0000153 if (nullptr != preslineptr)
154 {
155 presline = *preslineptr;
156 }
157
Brandon Wymanecbecbc2021-08-31 22:53:21 +0000158 auto invMatch =
159 std::find_if(psus.begin(), psus.end(), [&invpath](auto& psu) {
160 return psu->getInventoryPath() == invpath;
161 });
162 if (invMatch != psus.end())
163 {
164 // This power supply has the same inventory path as the one with
165 // information just added to D-Bus.
166 // Changes to GPIO line name unlikely, so skip checking.
167 // Changes to the I2C bus and address unlikely, as that would
168 // require corresponding device tree updates.
169 // Return out to avoid duplicate object creation.
170 return;
171 }
172
B. J. Wyman681b2a32021-04-20 22:31:22 +0000173 log<level::DEBUG>(
174 fmt::format("make PowerSupply bus: {} addr: {} presline: {}",
175 *i2cbus, *i2caddr, presline)
176 .c_str());
177 auto psu = std::make_unique<PowerSupply>(bus, invpath, *i2cbus,
178 *i2caddr, presline);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600179 psus.emplace_back(std::move(psu));
180 }
181
182 if (psus.empty())
183 {
184 log<level::INFO>(fmt::format("No power supplies to monitor").c_str());
185 }
186}
187
Adriana Kobylake1074d82021-03-16 20:46:44 +0000188void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties)
189{
190 try
191 {
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000192 auto propIt = properties.find("SupportedType");
193 if (propIt == properties.end())
194 {
195 return;
196 }
197 const std::string* type = std::get_if<std::string>(&(propIt->second));
198 if ((type == nullptr) || (*type != "PowerSupply"))
199 {
200 return;
201 }
202
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000203 propIt = properties.find("SupportedModel");
204 if (propIt == properties.end())
205 {
206 return;
207 }
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000208 const std::string* model = std::get_if<std::string>(&(propIt->second));
209 if (model == nullptr)
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000210 {
211 return;
212 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000213
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000214 sys_properties sys;
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000215 propIt = properties.find("RedundantCount");
Adriana Kobylake1074d82021-03-16 20:46:44 +0000216 if (propIt != properties.end())
217 {
218 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second));
219 if (count != nullptr)
220 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000221 sys.powerSupplyCount = *count;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000222 }
223 }
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000224 propIt = properties.find("InputVoltage");
225 if (propIt != properties.end())
226 {
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000227 const std::vector<uint64_t>* voltage =
228 std::get_if<std::vector<uint64_t>>(&(propIt->second));
Adriana Kobylak9ea66a62021-03-24 17:54:14 +0000229 if (voltage != nullptr)
230 {
231 sys.inputVoltage = *voltage;
232 }
233 }
234
Adriana Kobylak886574c2021-11-01 18:22:28 +0000235 // The PowerConfigFullLoad is an optional property, default it to false
236 // since that's the default value of the power-config-full-load GPIO.
237 sys.powerConfigFullLoad = false;
238 propIt = properties.find("PowerConfigFullLoad");
239 if (propIt != properties.end())
240 {
241 const bool* fullLoad = std::get_if<bool>(&(propIt->second));
242 if (fullLoad != nullptr)
243 {
244 sys.powerConfigFullLoad = *fullLoad;
245 }
246 }
247
Adriana Kobylakd3a70d92021-06-04 16:24:45 +0000248 supportedConfigs.emplace(*model, sys);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000249 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500250 catch (const std::exception& e)
Adriana Kobylak0c9a33d2021-09-13 18:05:09 +0000251 {}
Adriana Kobylake1074d82021-03-16 20:46:44 +0000252}
253
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600254void PSUManager::getSystemProperties()
255{
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600256
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600257 try
258 {
259 util::DbusSubtree subtree =
260 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
Adriana Kobylake1074d82021-03-16 20:46:44 +0000261 if (subtree.empty())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600262 {
263 throw std::runtime_error("Supported Configuration Not Found");
264 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600265
Adriana Kobylake1074d82021-03-16 20:46:44 +0000266 for (const auto& [objPath, services] : subtree)
267 {
268 std::string service = services.begin()->first;
269 if (objPath.empty() || service.empty())
270 {
271 continue;
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600272 }
Adriana Kobylake1074d82021-03-16 20:46:44 +0000273 auto properties = util::getAllProperties(
274 bus, objPath, supportedConfIntf, service);
275 populateSysProperties(properties);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600276 }
277 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500278 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600279 {
280 // Interface or property not found. Let the Interfaces Added callback
281 // process the information once the interfaces are added to D-Bus.
282 }
283}
284
Brandon Wyman3e429132021-03-18 18:03:14 -0500285void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600286{
287 try
288 {
289 sdbusplus::message::object_path objPath;
Adriana Kobylake1074d82021-03-16 20:46:44 +0000290 std::map<std::string, std::map<std::string, util::DbusVariant>>
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600291 interfaces;
292 msg.read(objPath, interfaces);
293
294 auto itIntf = interfaces.find(supportedConfIntf);
Brandon Wyman510acaa2020-11-05 18:32:04 -0600295 if (itIntf != interfaces.cend())
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600296 {
Brandon Wyman510acaa2020-11-05 18:32:04 -0600297 populateSysProperties(itIntf->second);
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600298 }
299
Brandon Wyman510acaa2020-11-05 18:32:04 -0600300 itIntf = interfaces.find(IBMCFFPSInterface);
301 if (itIntf != interfaces.cend())
302 {
303 log<level::INFO>(
304 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface)
305 .c_str());
306 getPSUProperties(itIntf->second);
307 }
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000308
309 // Call to validate the psu configuration if the power is on and both
310 // the IBMCFFPSConnector and SupportedConfiguration interfaces have been
311 // processed
312 if (powerOn && !psus.empty() && !supportedConfigs.empty())
313 {
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000314 validationTimer->restartOnce(validationTimeout);
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000315 }
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600316 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500317 catch (const std::exception& e)
Adriana Kobylak9bab9e12021-02-24 15:32:03 -0600318 {
319 // Ignore, the property may be of a different type than expected.
320 }
321}
322
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500323void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
324{
325 int32_t state = 0;
326 std::string msgSensor;
Patrick Williamsabe49412020-05-13 17:59:47 -0500327 std::map<std::string, std::variant<int32_t>> msgData;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500328 msg.read(msgSensor, msgData);
329
330 // Check if it was the Present property that changed.
331 auto valPropMap = msgData.find("state");
332 if (valPropMap != msgData.end())
333 {
334 state = std::get<int32_t>(valPropMap->second);
335
336 // Power is on when state=1. Clear faults.
337 if (state)
338 {
339 powerOn = true;
Adriana Kobylaka4d38fa2021-10-05 19:57:47 +0000340 validationTimer->restartOnce(validationTimeout);
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500341 clearFaults();
Adriana Kobylakc0a07582021-10-13 15:52:25 +0000342 setPowerConfigGPIO();
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500343 }
344 else
345 {
346 powerOn = false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000347 runValidateConfig = true;
Brandon Wymana0f33ce2019-10-17 18:32:29 -0500348 }
349 }
350}
351
Brandon Wyman8b662882021-10-08 17:31:51 +0000352void PSUManager::createError(const std::string& faultName,
353 std::map<std::string, std::string>& additionalData)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500354{
355 using namespace sdbusplus::xyz::openbmc_project;
356 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
357 constexpr auto loggingCreateInterface =
358 "xyz.openbmc_project.Logging.Create";
359
360 try
361 {
Brandon Wyman8b662882021-10-08 17:31:51 +0000362 additionalData["_PID"] = std::to_string(getpid());
363
Brandon Wymanb76ab242020-09-16 18:06:06 -0500364 auto service =
365 util::getService(loggingObjectPath, loggingCreateInterface, bus);
366
367 if (service.empty())
368 {
369 log<level::ERR>("Unable to get logging manager service");
370 return;
371 }
372
373 auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
374 loggingCreateInterface, "Create");
375
376 auto level = Logging::server::Entry::Level::Error;
377 method.append(faultName, level, additionalData);
378
379 auto reply = bus.call(method);
380 }
Patrick Williamsc1d4de52021-10-06 12:45:57 -0500381 catch (const std::exception& e)
Brandon Wymanb76ab242020-09-16 18:06:06 -0500382 {
383 log<level::ERR>(
384 fmt::format(
385 "Failed creating event log for fault {} due to error {}",
386 faultName, e.what())
387 .c_str());
388 }
389}
390
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500391void PSUManager::analyze()
392{
393 for (auto& psu : psus)
394 {
395 psu->analyze();
396 }
397
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600398 if (powerOn)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500399 {
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000400 std::map<std::string, std::string> additionalData;
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000401
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600402 for (auto& psu : psus)
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500403 {
Brandon Wymanec0b8dc2021-10-08 21:49:43 +0000404 additionalData.clear();
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600405 // TODO: Fault priorities #918
406 if (!psu->isFaultLogged() && !psu->isPresent())
407 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000408 std::map<std::string, std::string> requiredPSUsData;
409 auto requiredPSUsPresent = hasRequiredPSUs(requiredPSUsData);
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000410 if (!requiredPSUsPresent)
411 {
Brandon Wymanda369c72021-10-08 18:43:30 +0000412 additionalData.merge(requiredPSUsData);
Adriana Kobylakf2ba1462021-06-24 15:16:17 +0000413 // Create error for power supply missing.
414 additionalData["CALLOUT_INVENTORY_PATH"] =
415 psu->getInventoryPath();
416 additionalData["CALLOUT_PRIORITY"] = "H";
417 createError(
418 "xyz.openbmc_project.Power.PowerSupply.Error.Missing",
419 additionalData);
420 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600421 psu->setFaultLogged();
422 }
423 else if (!psu->isFaultLogged() && psu->isFaulted())
424 {
Brandon Wyman786b6f42021-10-12 20:21:41 +0000425 // Add STATUS_WORD and STATUS_MFR last response, in padded
426 // hexadecimal format.
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600427 additionalData["STATUS_WORD"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000428 fmt::format("{:#04x}", psu->getStatusWord());
Jay Meyer10d94052020-11-30 14:41:21 -0600429 additionalData["STATUS_MFR"] =
Brandon Wyman786b6f42021-10-12 20:21:41 +0000430 fmt::format("{:#02x}", psu->getMFRFault());
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600431 // If there are faults being reported, they possibly could be
432 // related to a bug in the firmware version running on the power
433 // supply. Capture that data into the error as well.
434 additionalData["FW_VERSION"] = psu->getFWVersion();
435
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000436 if (psu->hasCommFault())
437 {
Brandon Wyman85c7bf42021-10-19 22:28:48 +0000438 additionalData["STATUS_CML"] =
439 fmt::format("{:#02x}", psu->getStatusCML());
Brandon Wymanb85b9dd2021-10-19 21:25:17 +0000440 /* Attempts to communicate with the power supply have
441 * reached there limit. Create an error. */
442 additionalData["CALLOUT_DEVICE_PATH"] =
443 psu->getDevicePath();
444
445 createError(
446 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault",
447 additionalData);
448
449 psu->setFaultLogged();
450 }
451 else if ((psu->hasInputFault() || psu->hasVINUVFault()))
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600452 {
Brandon Wymanf07bc792021-10-12 19:00:35 +0000453 // Include STATUS_INPUT for input faults.
454 additionalData["STATUS_INPUT"] =
455 fmt::format("{:#02x}", psu->getStatusInput());
456
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600457 /* The power supply location might be needed if the input
458 * fault is due to a problem with the power supply itself.
459 * Include the inventory path with a call out priority of
460 * low.
461 */
462 additionalData["CALLOUT_INVENTORY_PATH"] =
463 psu->getInventoryPath();
464 additionalData["CALLOUT_PRIORITY"] = "L";
465 createError("xyz.openbmc_project.Power.PowerSupply.Error."
466 "InputFault",
467 additionalData);
468 psu->setFaultLogged();
469 }
Brandon Wyman6710ba22021-10-27 17:39:31 +0000470 else if (psu->hasVoutOVFault())
471 {
472 // Include STATUS_VOUT for Vout faults.
473 additionalData["STATUS_VOUT"] =
474 fmt::format("{:#02x}", psu->getStatusVout());
475
476 additionalData["CALLOUT_INVENTORY_PATH"] =
477 psu->getInventoryPath();
478
479 createError(
480 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
481 additionalData);
482
483 psu->setFaultLogged();
484 }
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600485 else if (psu->hasMFRFault())
486 {
487 /* This can represent a variety of faults that result in
488 * calling out the power supply for replacement: Output
489 * OverCurrent, Output Under Voltage, and potentially other
490 * faults.
491 *
492 * Also plan on putting specific fault in AdditionalData,
493 * along with register names and register values
494 * (STATUS_WORD, STATUS_MFR, etc.).*/
495
496 additionalData["CALLOUT_INVENTORY_PATH"] =
497 psu->getInventoryPath();
498
499 createError(
500 "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
Brandon Wyman52e54e82020-10-08 14:44:58 -0500501 additionalData);
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500502
Brandon Wyman3180f4d2020-12-08 17:53:46 -0600503 psu->setFaultLogged();
504 }
Brandon Wyman4176d6b2020-10-07 17:41:06 -0500505 }
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500506 }
507 }
508}
509
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000510void PSUManager::validateConfig()
511{
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000512 if (!runValidateConfig || supportedConfigs.empty())
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000513 {
514 return;
515 }
516
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000517 std::map<std::string, std::string> additionalData;
518 auto supported = hasRequiredPSUs(additionalData);
519 if (supported)
520 {
521 runValidateConfig = false;
522 return;
523 }
524
525 // Validation failed, create an error log.
526 // Return without setting the runValidateConfig flag to false because
527 // it may be that an additional supported configuration interface is
528 // added and we need to validate it to see if it matches this system.
529 createError("xyz.openbmc_project.Power.PowerSupply.Error.NotSupported",
530 additionalData);
531}
532
533bool PSUManager::hasRequiredPSUs(
534 std::map<std::string, std::string>& additionalData)
535{
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000536 std::string model{};
Adriana Kobylak523704d2021-09-21 15:55:41 +0000537 if (!validateModelName(model, additionalData))
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000538 {
Adriana Kobylak523704d2021-09-21 15:55:41 +0000539 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000540 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000541
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000542 auto presentCount =
543 std::count_if(psus.begin(), psus.end(),
544 [](const auto& psu) { return psu->isPresent(); });
545
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000546 // Validate the supported configurations. A system may support more than one
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000547 // power supply model configuration. Since all configurations need to be
548 // checked, the additional data would contain only the information of the
549 // last configuration that did not match.
550 std::map<std::string, std::string> tmpAdditionalData;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000551 for (const auto& config : supportedConfigs)
552 {
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000553 if (config.first != model)
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000554 {
555 continue;
556 }
557 if (presentCount != config.second.powerSupplyCount)
558 {
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000559 tmpAdditionalData.clear();
560 tmpAdditionalData["EXPECTED_COUNT"] =
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000561 std::to_string(config.second.powerSupplyCount);
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000562 tmpAdditionalData["ACTUAL_COUNT"] = std::to_string(presentCount);
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000563 continue;
564 }
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000565
566 bool voltageValidated = true;
567 for (const auto& psu : psus)
568 {
569 if (!psu->isPresent())
570 {
571 // Only present PSUs report a valid input voltage
572 continue;
573 }
574
575 double actualInputVoltage;
576 int inputVoltage;
577 psu->getInputVoltage(actualInputVoltage, inputVoltage);
578
579 if (std::find(config.second.inputVoltage.begin(),
580 config.second.inputVoltage.end(),
581 inputVoltage) == config.second.inputVoltage.end())
582 {
583 tmpAdditionalData.clear();
584 tmpAdditionalData["ACTUAL_VOLTAGE"] =
585 std::to_string(actualInputVoltage);
586 for (const auto& voltage : config.second.inputVoltage)
587 {
588 tmpAdditionalData["EXPECTED_VOLTAGE"] +=
589 std::to_string(voltage) + " ";
590 }
591 tmpAdditionalData["CALLOUT_INVENTORY_PATH"] =
592 psu->getInventoryPath();
593
594 voltageValidated = false;
595 break;
596 }
597 }
598 if (!voltageValidated)
599 {
600 continue;
601 }
602
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000603 return true;
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000604 }
Adriana Kobylak70e7f932021-06-10 18:53:56 +0000605
Adriana Kobylak4175ffb2021-08-02 14:51:05 +0000606 additionalData.insert(tmpAdditionalData.begin(), tmpAdditionalData.end());
Adriana Kobylak4d9aaf92021-06-30 15:27:42 +0000607 return false;
Adriana Kobylak8f16fb52021-03-31 15:50:15 +0000608}
609
Adriana Kobylak523704d2021-09-21 15:55:41 +0000610bool PSUManager::validateModelName(
611 std::string& model, std::map<std::string, std::string>& additionalData)
612{
613 // Check that all PSUs have the same model name. Initialize the model
614 // variable with the first PSU name found, then use it as a base to compare
615 // against the rest of the PSUs.
616 model.clear();
617 for (const auto& psu : psus)
618 {
619 auto psuModel = psu->getModelName();
620 if (psuModel.empty())
621 {
622 continue;
623 }
624 if (model.empty())
625 {
626 model = psuModel;
627 continue;
628 }
629 if (psuModel != model)
630 {
631 additionalData["EXPECTED_MODEL"] = model;
632 additionalData["ACTUAL_MODEL"] = psuModel;
633 additionalData["CALLOUT_INVENTORY_PATH"] = psu->getInventoryPath();
634 model.clear();
635 return false;
636 }
637 }
638 return true;
639}
640
Adriana Kobylakc0a07582021-10-13 15:52:25 +0000641void PSUManager::setPowerConfigGPIO()
642{
643 if (!powerConfigGPIO)
644 {
645 return;
646 }
647
648 std::string model{};
649 std::map<std::string, std::string> additionalData;
650 if (!validateModelName(model, additionalData))
651 {
652 return;
653 }
654
655 auto config = supportedConfigs.find(model);
656 if (config != supportedConfigs.end())
657 {
658 // The power-config-full-load is an open drain GPIO. Set it to low (0)
659 // if the supported configuration indicates that this system model
660 // expects the maximum number of power supplies (full load set to true).
661 // Else, set it to high (1), this is the default.
662 auto powerConfigValue =
663 (config->second.powerConfigFullLoad == true ? 0 : 1);
664 auto flags = gpiod::line_request::FLAG_OPEN_DRAIN;
665 powerConfigGPIO->write(powerConfigValue, flags);
666 }
667}
668
Brandon Wyman63ea78b2020-09-24 16:49:09 -0500669} // namespace phosphor::power::manager