1 #include "psu_manager.hpp" 2 3 #include "utility.hpp" 4 5 #include <fmt/format.h> 6 #include <sys/types.h> 7 #include <unistd.h> 8 9 #include <regex> 10 11 using namespace phosphor::logging; 12 13 namespace phosphor::power::manager 14 { 15 16 constexpr auto IBMCFFPSInterface = 17 "xyz.openbmc_project.Configuration.IBMCFFPSConnector"; 18 constexpr auto i2cBusProp = "I2CBus"; 19 constexpr auto i2cAddressProp = "I2CAddress"; 20 constexpr auto psuNameProp = "Name"; 21 constexpr auto presLineName = "NamedPresenceGpio"; 22 23 constexpr auto supportedConfIntf = 24 "xyz.openbmc_project.Configuration.SupportedConfiguration"; 25 26 PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) : 27 bus(bus) 28 { 29 // 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 47 validationTimer = std::make_unique<utility::Timer<ClockId::Monotonic>>( 48 e, std::bind(&PSUManager::validateConfig, this)); 49 50 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 60 // 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 71 void 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 107 void 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; 117 std::string* preslineptr = nullptr; 118 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 } 135 else if (property.first == presLineName) 136 { 137 preslineptr = 138 std::get_if<std::string>(&properties[presLineName]); 139 } 140 } 141 catch (const std::exception& e) 142 {} 143 } 144 145 if ((i2cbus) && (i2caddr) && (psuname) && (!psuname->empty())) 146 { 147 std::string invpath = basePSUInvPath; 148 invpath.push_back(psuname->back()); 149 std::string presline = ""; 150 151 log<level::DEBUG>(fmt::format("Inventory Path: {}", invpath).c_str()); 152 153 if (nullptr != preslineptr) 154 { 155 presline = *preslineptr; 156 } 157 158 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 173 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); 179 psus.emplace_back(std::move(psu)); 180 181 // Subscribe to power supply presence changes 182 auto presenceMatch = std::make_unique<sdbusplus::bus::match_t>( 183 bus, 184 sdbusplus::bus::match::rules::propertiesChanged(invpath, 185 INVENTORY_IFACE), 186 [this](auto& msg) { this->presenceChanged(msg); }); 187 presenceMatches.emplace_back(std::move(presenceMatch)); 188 } 189 190 if (psus.empty()) 191 { 192 log<level::INFO>(fmt::format("No power supplies to monitor").c_str()); 193 } 194 } 195 196 void PSUManager::populateSysProperties(const util::DbusPropertyMap& properties) 197 { 198 try 199 { 200 auto propIt = properties.find("SupportedType"); 201 if (propIt == properties.end()) 202 { 203 return; 204 } 205 const std::string* type = std::get_if<std::string>(&(propIt->second)); 206 if ((type == nullptr) || (*type != "PowerSupply")) 207 { 208 return; 209 } 210 211 propIt = properties.find("SupportedModel"); 212 if (propIt == properties.end()) 213 { 214 return; 215 } 216 const std::string* model = std::get_if<std::string>(&(propIt->second)); 217 if (model == nullptr) 218 { 219 return; 220 } 221 222 sys_properties sys; 223 propIt = properties.find("RedundantCount"); 224 if (propIt != properties.end()) 225 { 226 const uint64_t* count = std::get_if<uint64_t>(&(propIt->second)); 227 if (count != nullptr) 228 { 229 sys.powerSupplyCount = *count; 230 } 231 } 232 propIt = properties.find("InputVoltage"); 233 if (propIt != properties.end()) 234 { 235 const std::vector<uint64_t>* voltage = 236 std::get_if<std::vector<uint64_t>>(&(propIt->second)); 237 if (voltage != nullptr) 238 { 239 sys.inputVoltage = *voltage; 240 } 241 } 242 243 // The PowerConfigFullLoad is an optional property, default it to false 244 // since that's the default value of the power-config-full-load GPIO. 245 sys.powerConfigFullLoad = false; 246 propIt = properties.find("PowerConfigFullLoad"); 247 if (propIt != properties.end()) 248 { 249 const bool* fullLoad = std::get_if<bool>(&(propIt->second)); 250 if (fullLoad != nullptr) 251 { 252 sys.powerConfigFullLoad = *fullLoad; 253 } 254 } 255 256 supportedConfigs.emplace(*model, sys); 257 } 258 catch (const std::exception& e) 259 {} 260 } 261 262 void PSUManager::getSystemProperties() 263 { 264 265 try 266 { 267 util::DbusSubtree subtree = 268 util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0); 269 if (subtree.empty()) 270 { 271 throw std::runtime_error("Supported Configuration Not Found"); 272 } 273 274 for (const auto& [objPath, services] : subtree) 275 { 276 std::string service = services.begin()->first; 277 if (objPath.empty() || service.empty()) 278 { 279 continue; 280 } 281 auto properties = util::getAllProperties( 282 bus, objPath, supportedConfIntf, service); 283 populateSysProperties(properties); 284 } 285 } 286 catch (const std::exception& e) 287 { 288 // Interface or property not found. Let the Interfaces Added callback 289 // process the information once the interfaces are added to D-Bus. 290 } 291 } 292 293 void PSUManager::entityManagerIfaceAdded(sdbusplus::message::message& msg) 294 { 295 try 296 { 297 sdbusplus::message::object_path objPath; 298 std::map<std::string, std::map<std::string, util::DbusVariant>> 299 interfaces; 300 msg.read(objPath, interfaces); 301 302 auto itIntf = interfaces.find(supportedConfIntf); 303 if (itIntf != interfaces.cend()) 304 { 305 populateSysProperties(itIntf->second); 306 } 307 308 itIntf = interfaces.find(IBMCFFPSInterface); 309 if (itIntf != interfaces.cend()) 310 { 311 log<level::INFO>( 312 fmt::format("InterfacesAdded for: {}", IBMCFFPSInterface) 313 .c_str()); 314 getPSUProperties(itIntf->second); 315 } 316 317 // Call to validate the psu configuration if the power is on and both 318 // the IBMCFFPSConnector and SupportedConfiguration interfaces have been 319 // processed 320 if (powerOn && !psus.empty() && !supportedConfigs.empty()) 321 { 322 validationTimer->restartOnce(validationTimeout); 323 } 324 } 325 catch (const std::exception& e) 326 { 327 // Ignore, the property may be of a different type than expected. 328 } 329 } 330 331 void PSUManager::powerStateChanged(sdbusplus::message::message& msg) 332 { 333 int32_t state = 0; 334 std::string msgSensor; 335 std::map<std::string, std::variant<int32_t>> msgData; 336 msg.read(msgSensor, msgData); 337 338 // Check if it was the Present property that changed. 339 auto valPropMap = msgData.find("state"); 340 if (valPropMap != msgData.end()) 341 { 342 state = std::get<int32_t>(valPropMap->second); 343 344 // Power is on when state=1. Clear faults. 345 if (state) 346 { 347 powerOn = true; 348 validationTimer->restartOnce(validationTimeout); 349 clearFaults(); 350 setPowerConfigGPIO(); 351 } 352 else 353 { 354 powerOn = false; 355 runValidateConfig = true; 356 brownoutLogged = false; 357 } 358 } 359 } 360 361 void PSUManager::presenceChanged(sdbusplus::message::message& msg) 362 { 363 std::string msgSensor; 364 std::map<std::string, std::variant<uint32_t, bool>> msgData; 365 msg.read(msgSensor, msgData); 366 367 // Check if it was the Present property that changed. 368 auto valPropMap = msgData.find(PRESENT_PROP); 369 if (valPropMap != msgData.end()) 370 { 371 if (std::get<bool>(valPropMap->second)) 372 { 373 // A PSU became present, force the PSU validation to run. 374 runValidateConfig = true; 375 validationTimer->restartOnce(validationTimeout); 376 } 377 } 378 } 379 380 void PSUManager::createError(const std::string& faultName, 381 std::map<std::string, std::string>& additionalData) 382 { 383 using namespace sdbusplus::xyz::openbmc_project; 384 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging"; 385 constexpr auto loggingCreateInterface = 386 "xyz.openbmc_project.Logging.Create"; 387 388 try 389 { 390 additionalData["_PID"] = std::to_string(getpid()); 391 392 auto service = 393 util::getService(loggingObjectPath, loggingCreateInterface, bus); 394 395 if (service.empty()) 396 { 397 log<level::ERR>("Unable to get logging manager service"); 398 return; 399 } 400 401 auto method = bus.new_method_call(service.c_str(), loggingObjectPath, 402 loggingCreateInterface, "Create"); 403 404 auto level = Logging::server::Entry::Level::Error; 405 method.append(faultName, level, additionalData); 406 407 auto reply = bus.call(method); 408 } 409 catch (const std::exception& e) 410 { 411 log<level::ERR>( 412 fmt::format( 413 "Failed creating event log for fault {} due to error {}", 414 faultName, e.what()) 415 .c_str()); 416 } 417 } 418 419 void PSUManager::analyze() 420 { 421 for (auto& psu : psus) 422 { 423 psu->analyze(); 424 } 425 426 if (powerOn) 427 { 428 std::map<std::string, std::string> additionalData; 429 auto hasVINUVFaultCount = decltype(psus.size())(0); 430 431 for (auto& psu : psus) 432 { 433 additionalData.clear(); 434 435 // Check for brownout condition: PSU reports AC loss VIN fault or is 436 // not present. 437 if (!psu->isPresent() || psu->hasVINUVFault()) 438 { 439 hasVINUVFaultCount++; 440 } 441 442 if (!psu->isFaultLogged() && !psu->isPresent()) 443 { 444 std::map<std::string, std::string> requiredPSUsData; 445 auto requiredPSUsPresent = hasRequiredPSUs(requiredPSUsData); 446 if (!requiredPSUsPresent) 447 { 448 additionalData.merge(requiredPSUsData); 449 // Create error for power supply missing. 450 additionalData["CALLOUT_INVENTORY_PATH"] = 451 psu->getInventoryPath(); 452 additionalData["CALLOUT_PRIORITY"] = "H"; 453 createError( 454 "xyz.openbmc_project.Power.PowerSupply.Error.Missing", 455 additionalData); 456 } 457 psu->setFaultLogged(); 458 } 459 else if (!psu->isFaultLogged() && psu->isFaulted()) 460 { 461 // Add STATUS_WORD and STATUS_MFR last response, in padded 462 // hexadecimal format. 463 additionalData["STATUS_WORD"] = 464 fmt::format("{:#04x}", psu->getStatusWord()); 465 additionalData["STATUS_MFR"] = 466 fmt::format("{:#02x}", psu->getMFRFault()); 467 // If there are faults being reported, they possibly could be 468 // related to a bug in the firmware version running on the power 469 // supply. Capture that data into the error as well. 470 additionalData["FW_VERSION"] = psu->getFWVersion(); 471 472 if (psu->hasCommFault()) 473 { 474 additionalData["STATUS_CML"] = 475 fmt::format("{:#02x}", psu->getStatusCML()); 476 /* Attempts to communicate with the power supply have 477 * reached there limit. Create an error. */ 478 additionalData["CALLOUT_DEVICE_PATH"] = 479 psu->getDevicePath(); 480 481 createError( 482 "xyz.openbmc_project.Power.PowerSupply.Error.CommFault", 483 additionalData); 484 485 psu->setFaultLogged(); 486 } 487 else if ((psu->hasInputFault() || psu->hasVINUVFault())) 488 { 489 // Include STATUS_INPUT for input faults. 490 additionalData["STATUS_INPUT"] = 491 fmt::format("{:#02x}", psu->getStatusInput()); 492 493 /* The power supply location might be needed if the input 494 * fault is due to a problem with the power supply itself. 495 * Include the inventory path with a call out priority of 496 * low. 497 */ 498 additionalData["CALLOUT_INVENTORY_PATH"] = 499 psu->getInventoryPath(); 500 additionalData["CALLOUT_PRIORITY"] = "L"; 501 createError("xyz.openbmc_project.Power.PowerSupply.Error." 502 "InputFault", 503 additionalData); 504 psu->setFaultLogged(); 505 } 506 else if (psu->hasPSKillFault()) 507 { 508 createError( 509 "xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault", 510 additionalData); 511 psu->setFaultLogged(); 512 } 513 else if (psu->hasVoutOVFault()) 514 { 515 // Include STATUS_VOUT for Vout faults. 516 additionalData["STATUS_VOUT"] = 517 fmt::format("{:#02x}", psu->getStatusVout()); 518 519 additionalData["CALLOUT_INVENTORY_PATH"] = 520 psu->getInventoryPath(); 521 522 createError( 523 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 524 additionalData); 525 526 psu->setFaultLogged(); 527 } 528 else if (psu->hasIoutOCFault()) 529 { 530 // Include STATUS_IOUT for Iout faults. 531 additionalData["STATUS_IOUT"] = 532 fmt::format("{:#02x}", psu->getStatusIout()); 533 534 createError( 535 "xyz.openbmc_project.Power.PowerSupply.Error.IoutOCFault", 536 additionalData); 537 538 psu->setFaultLogged(); 539 } 540 else if (psu->hasVoutUVFault() || psu->hasPS12VcsFault() || 541 psu->hasPSCS12VFault()) 542 { 543 // Include STATUS_VOUT for Vout faults. 544 additionalData["STATUS_VOUT"] = 545 fmt::format("{:#02x}", psu->getStatusVout()); 546 547 additionalData["CALLOUT_INVENTORY_PATH"] = 548 psu->getInventoryPath(); 549 550 createError( 551 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 552 additionalData); 553 554 psu->setFaultLogged(); 555 } 556 // A fan fault should have priority over a temperature fault, 557 // since a failed fan may lead to a temperature problem. 558 else if (psu->hasFanFault()) 559 { 560 // Include STATUS_TEMPERATURE and STATUS_FANS_1_2 561 additionalData["STATUS_TEMPERATURE"] = 562 fmt::format("{:#02x}", psu->getStatusTemperature()); 563 additionalData["STATUS_FANS_1_2"] = 564 fmt::format("{:#02x}", psu->getStatusFans12()); 565 566 additionalData["CALLOUT_INVENTORY_PATH"] = 567 psu->getInventoryPath(); 568 569 createError( 570 "xyz.openbmc_project.Power.PowerSupply.Error.FanFault", 571 additionalData); 572 573 psu->setFaultLogged(); 574 } 575 else if (psu->hasTempFault()) 576 { 577 // Include STATUS_TEMPERATURE for temperature faults. 578 additionalData["STATUS_TEMPERATURE"] = 579 fmt::format("{:#02x}", psu->getStatusTemperature()); 580 581 additionalData["CALLOUT_INVENTORY_PATH"] = 582 psu->getInventoryPath(); 583 584 createError( 585 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 586 additionalData); 587 588 psu->setFaultLogged(); 589 } 590 else if (psu->hasMFRFault()) 591 { 592 /* This can represent a variety of faults that result in 593 * calling out the power supply for replacement: Output 594 * OverCurrent, Output Under Voltage, and potentially other 595 * faults. 596 * 597 * Also plan on putting specific fault in AdditionalData, 598 * along with register names and register values 599 * (STATUS_WORD, STATUS_MFR, etc.).*/ 600 601 additionalData["CALLOUT_INVENTORY_PATH"] = 602 psu->getInventoryPath(); 603 604 createError( 605 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 606 additionalData); 607 608 psu->setFaultLogged(); 609 } 610 else if (psu->hasPgoodFault()) 611 { 612 /* POWER_GOOD# is not low, or OFF is on */ 613 additionalData["CALLOUT_INVENTORY_PATH"] = 614 psu->getInventoryPath(); 615 616 createError( 617 "xyz.openbmc_project.Power.PowerSupply.Error.Fault", 618 additionalData); 619 620 psu->setFaultLogged(); 621 } 622 } 623 } 624 625 if (hasVINUVFaultCount == psus.size()) 626 { 627 // Brownout: All PSUs report AC loss VIN fault or are not present 628 if (!brownoutLogged) 629 { 630 createError( 631 "xyz.openbmc_project.State.Shutdown.Power.Error.Blackout", 632 additionalData); 633 brownoutLogged = true; 634 } 635 } 636 else 637 { 638 // Brownout condition is not present or has been cleared 639 brownoutLogged = false; 640 } 641 } 642 } 643 644 void PSUManager::validateConfig() 645 { 646 if (!runValidateConfig || supportedConfigs.empty()) 647 { 648 return; 649 } 650 651 std::map<std::string, std::string> additionalData; 652 auto supported = hasRequiredPSUs(additionalData); 653 if (supported) 654 { 655 runValidateConfig = false; 656 return; 657 } 658 659 // Validation failed, create an error log. 660 // Return without setting the runValidateConfig flag to false because 661 // it may be that an additional supported configuration interface is 662 // added and we need to validate it to see if it matches this system. 663 createError("xyz.openbmc_project.Power.PowerSupply.Error.NotSupported", 664 additionalData); 665 } 666 667 bool PSUManager::hasRequiredPSUs( 668 std::map<std::string, std::string>& additionalData) 669 { 670 std::string model{}; 671 if (!validateModelName(model, additionalData)) 672 { 673 return false; 674 } 675 676 auto presentCount = 677 std::count_if(psus.begin(), psus.end(), 678 [](const auto& psu) { return psu->isPresent(); }); 679 680 // Validate the supported configurations. A system may support more than one 681 // power supply model configuration. Since all configurations need to be 682 // checked, the additional data would contain only the information of the 683 // last configuration that did not match. 684 std::map<std::string, std::string> tmpAdditionalData; 685 for (const auto& config : supportedConfigs) 686 { 687 if (config.first != model) 688 { 689 continue; 690 } 691 if (presentCount != config.second.powerSupplyCount) 692 { 693 tmpAdditionalData.clear(); 694 tmpAdditionalData["EXPECTED_COUNT"] = 695 std::to_string(config.second.powerSupplyCount); 696 tmpAdditionalData["ACTUAL_COUNT"] = std::to_string(presentCount); 697 continue; 698 } 699 700 bool voltageValidated = true; 701 for (const auto& psu : psus) 702 { 703 if (!psu->isPresent()) 704 { 705 // Only present PSUs report a valid input voltage 706 continue; 707 } 708 709 double actualInputVoltage; 710 int inputVoltage; 711 psu->getInputVoltage(actualInputVoltage, inputVoltage); 712 713 if (std::find(config.second.inputVoltage.begin(), 714 config.second.inputVoltage.end(), 715 inputVoltage) == config.second.inputVoltage.end()) 716 { 717 tmpAdditionalData.clear(); 718 tmpAdditionalData["ACTUAL_VOLTAGE"] = 719 std::to_string(actualInputVoltage); 720 for (const auto& voltage : config.second.inputVoltage) 721 { 722 tmpAdditionalData["EXPECTED_VOLTAGE"] += 723 std::to_string(voltage) + " "; 724 } 725 tmpAdditionalData["CALLOUT_INVENTORY_PATH"] = 726 psu->getInventoryPath(); 727 728 voltageValidated = false; 729 break; 730 } 731 } 732 if (!voltageValidated) 733 { 734 continue; 735 } 736 737 return true; 738 } 739 740 additionalData.insert(tmpAdditionalData.begin(), tmpAdditionalData.end()); 741 return false; 742 } 743 744 bool PSUManager::validateModelName( 745 std::string& model, std::map<std::string, std::string>& additionalData) 746 { 747 // Check that all PSUs have the same model name. Initialize the model 748 // variable with the first PSU name found, then use it as a base to compare 749 // against the rest of the PSUs and get its inventory path to use as callout 750 // if needed. 751 model.clear(); 752 std::string modelInventoryPath{}; 753 for (const auto& psu : psus) 754 { 755 auto psuModel = psu->getModelName(); 756 if (psuModel.empty()) 757 { 758 continue; 759 } 760 if (model.empty()) 761 { 762 model = psuModel; 763 modelInventoryPath = psu->getInventoryPath(); 764 continue; 765 } 766 if (psuModel != model) 767 { 768 if (supportedConfigs.find(model) != supportedConfigs.end()) 769 { 770 // The base model is supported, callout the mismatched PSU. The 771 // mismatched PSU may or may not be supported. 772 additionalData["EXPECTED_MODEL"] = model; 773 additionalData["ACTUAL_MODEL"] = psuModel; 774 additionalData["CALLOUT_INVENTORY_PATH"] = 775 psu->getInventoryPath(); 776 } 777 else if (supportedConfigs.find(psuModel) != supportedConfigs.end()) 778 { 779 // The base model is not supported, but the mismatched PSU is, 780 // callout the base PSU. 781 additionalData["EXPECTED_MODEL"] = psuModel; 782 additionalData["ACTUAL_MODEL"] = model; 783 additionalData["CALLOUT_INVENTORY_PATH"] = modelInventoryPath; 784 } 785 else 786 { 787 // The base model and the mismatched PSU are not supported or 788 // could not be found in the supported configuration, callout 789 // the mismatched PSU. 790 additionalData["EXPECTED_MODEL"] = model; 791 additionalData["ACTUAL_MODEL"] = psuModel; 792 additionalData["CALLOUT_INVENTORY_PATH"] = 793 psu->getInventoryPath(); 794 } 795 model.clear(); 796 return false; 797 } 798 } 799 return true; 800 } 801 802 void PSUManager::setPowerConfigGPIO() 803 { 804 if (!powerConfigGPIO) 805 { 806 return; 807 } 808 809 std::string model{}; 810 std::map<std::string, std::string> additionalData; 811 if (!validateModelName(model, additionalData)) 812 { 813 return; 814 } 815 816 auto config = supportedConfigs.find(model); 817 if (config != supportedConfigs.end()) 818 { 819 // The power-config-full-load is an open drain GPIO. Set it to low (0) 820 // if the supported configuration indicates that this system model 821 // expects the maximum number of power supplies (full load set to true). 822 // Else, set it to high (1), this is the default. 823 auto powerConfigValue = 824 (config->second.powerConfigFullLoad == true ? 0 : 1); 825 auto flags = gpiod::line_request::FLAG_OPEN_DRAIN; 826 powerConfigGPIO->write(powerConfigValue, flags); 827 } 828 } 829 830 } // namespace phosphor::power::manager 831