1 #pragma once 2 3 #include "pmbus.hpp" 4 #include "types.hpp" 5 #include "util.hpp" 6 #include "utility.hpp" 7 8 #include <gpiod.hpp> 9 #include <sdbusplus/bus/match.hpp> 10 11 #include <filesystem> 12 #include <stdexcept> 13 14 namespace phosphor::power::psu 15 { 16 17 #if IBM_VPD 18 // PMBus device driver "file name" to read for CCIN value. 19 constexpr auto CCIN = "ccin"; 20 constexpr auto PART_NUMBER = "part_number"; 21 constexpr auto FRU_NUMBER = "fru"; 22 constexpr auto SERIAL_HEADER = "header"; 23 constexpr auto SERIAL_NUMBER = "serial_number"; 24 constexpr auto FW_VERSION = "fw_version"; 25 26 // The D-Bus property name to update with the CCIN value. 27 constexpr auto MODEL_PROP = "Model"; 28 constexpr auto PN_PROP = "PartNumber"; 29 constexpr auto SPARE_PN_PROP = "SparePartNumber"; 30 constexpr auto SN_PROP = "SerialNumber"; 31 constexpr auto VERSION_PROP = "Version"; 32 33 // ipzVPD Keyword sizes 34 static constexpr auto FL_KW_SIZE = 20; 35 #endif 36 37 constexpr auto LOG_LIMIT = 3; 38 constexpr auto DEGLITCH_LIMIT = 3; 39 40 /** 41 * @class PowerSupply 42 * Represents a PMBus power supply device. 43 */ 44 class PowerSupply 45 { 46 public: 47 PowerSupply() = delete; 48 PowerSupply(const PowerSupply&) = delete; 49 PowerSupply(PowerSupply&&) = delete; 50 PowerSupply& operator=(const PowerSupply&) = delete; 51 PowerSupply& operator=(PowerSupply&&) = delete; 52 ~PowerSupply() = default; 53 54 /** 55 * @param[in] invpath - String for inventory path to use 56 * @param[in] i2cbus - The bus number this power supply is on 57 * @param[in] i2caddr - The 16-bit I2C address of the power supply 58 * @param[in] gpioLineName - The gpio-line-name to read for presence. See 59 * https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md 60 */ 61 PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath, 62 std::uint8_t i2cbus, const std::uint16_t i2caddr, 63 const std::string& gpioLineName); 64 65 phosphor::pmbus::PMBusBase& getPMBus() 66 { 67 return *pmbusIntf; 68 } 69 70 GPIOInterfaceBase* getPresenceGPIO() 71 { 72 return presenceGPIO.get(); 73 } 74 75 std::string getPresenceGPIOName() const 76 { 77 if (presenceGPIO != nullptr) 78 { 79 return presenceGPIO->getName(); 80 } 81 else 82 { 83 return std::string(); 84 } 85 } 86 87 /** 88 * Power supply specific function to analyze for faults/errors. 89 * 90 * Various PMBus status bits will be checked for fault conditions. 91 * If a certain fault bits are on, the appropriate error will be 92 * committed. 93 */ 94 void analyze(); 95 96 /** 97 * Write PMBus ON_OFF_CONFIG 98 * 99 * This function will be called to cause the PMBus device driver to send the 100 * ON_OFF_CONFIG command. Takes one byte of data. 101 * 102 * @param[in] data - The ON_OFF_CONFIG data byte mask. 103 */ 104 void onOffConfig(uint8_t data); 105 106 /** 107 * Clears all the member variables that indicate if a fault bit was seen as 108 * on in the STATUS_WORD or STATUS_MFR_SPECIFIC response. 109 */ 110 void clearFaultFlags() 111 { 112 inputFault = 0; 113 mfrFault = 0; 114 statusMFR = 0; 115 vinUVFault = 0; 116 cmlFault = 0; 117 voutOVFault = 0; 118 ioutOCFault = 0; 119 voutUVFault = 0; 120 fanFault = 0; 121 tempFault = 0; 122 pgoodFault = 0; 123 psKillFault = 0; 124 ps12VcsFault = 0; 125 psCS12VFault = 0; 126 } 127 128 /** 129 * Write PMBus CLEAR_FAULTS 130 * 131 * This function will be called in various situations in order to clear 132 * any fault status bits that may have been set, in order to start over 133 * with a clean state. Presence changes and power state changes will 134 * want to clear any faults logged. 135 */ 136 void clearFaults(); 137 138 /** 139 * @brief Adds properties to the inventory. 140 * 141 * Reads the values from the device and writes them to the 142 * associated power supply D-Bus inventory object. 143 * 144 * This needs to be done on startup, and each time the presence 145 * state changes. 146 * 147 * Properties added: 148 * - Serial Number 149 * - Part Number 150 * - CCIN (Customer Card Identification Number) - added as the Model 151 * - Firmware version 152 */ 153 void updateInventory(); 154 155 /** 156 * @brief Accessor function to indicate present status 157 */ 158 bool isPresent() const 159 { 160 return present; 161 } 162 163 /** 164 * @brief Returns the last read value from STATUS_WORD. 165 */ 166 uint64_t getStatusWord() const 167 { 168 return statusWord; 169 } 170 171 /** 172 * @brief Returns the last read value from STATUS_INPUT. 173 */ 174 uint64_t getStatusInput() const 175 { 176 return statusInput; 177 } 178 179 /** 180 * @brief Returns the last read value from STATUS_MFR. 181 */ 182 uint64_t getMFRFault() const 183 { 184 return statusMFR; 185 } 186 187 /** 188 * @brief Returns the last read value from STATUS_CML. 189 */ 190 uint64_t getStatusCML() const 191 { 192 return statusCML; 193 } 194 195 /** 196 * @brief Returns the last read value from STATUS_VOUT. 197 */ 198 uint64_t getStatusVout() const 199 { 200 return statusVout; 201 } 202 203 /** 204 * @brief Returns the last value read from STATUS_IOUT. 205 */ 206 uint64_t getStatusIout() const 207 { 208 return statusIout; 209 } 210 211 /** 212 * @brief Returns the last value read from STATUS_FANS_1_2. 213 */ 214 uint64_t getStatusFans12() const 215 { 216 return statusFans12; 217 } 218 219 /** 220 * @brief Returns the last value read from STATUS_TEMPERATURE. 221 */ 222 uint64_t getStatusTemperature() const 223 { 224 return statusTemperature; 225 } 226 227 /** 228 * @brief Returns true if a fault was found. 229 */ 230 bool isFaulted() const 231 { 232 return (hasCommFault() || (vinUVFault >= DEGLITCH_LIMIT) || 233 (inputFault >= DEGLITCH_LIMIT) || 234 (voutOVFault >= DEGLITCH_LIMIT) || 235 (ioutOCFault >= DEGLITCH_LIMIT) || 236 (voutUVFault >= DEGLITCH_LIMIT) || 237 (fanFault >= DEGLITCH_LIMIT) || (tempFault >= DEGLITCH_LIMIT) || 238 (pgoodFault >= DEGLITCH_LIMIT) || (mfrFault >= DEGLITCH_LIMIT)); 239 } 240 241 /** 242 * @brief Return whether a fault has been logged for this power supply 243 */ 244 bool isFaultLogged() const 245 { 246 return faultLogged; 247 } 248 249 /** 250 * @brief Called when a fault for this power supply has been logged. 251 */ 252 void setFaultLogged() 253 { 254 faultLogged = true; 255 } 256 257 /** 258 * @brief Returns true if INPUT fault occurred. 259 */ 260 bool hasInputFault() const 261 { 262 return (inputFault >= DEGLITCH_LIMIT); 263 } 264 265 /** 266 * @brief Returns true if MFRSPECIFIC occurred. 267 */ 268 bool hasMFRFault() const 269 { 270 return (mfrFault >= DEGLITCH_LIMIT); 271 } 272 273 /** 274 * @brief Returns true if VIN_UV_FAULT occurred. 275 */ 276 bool hasVINUVFault() const 277 { 278 return (vinUVFault >= DEGLITCH_LIMIT); 279 } 280 281 /** 282 * @brief Returns true if VOUT_OV_FAULT occurred. 283 */ 284 bool hasVoutOVFault() const 285 { 286 return (voutOVFault >= DEGLITCH_LIMIT); 287 } 288 289 /** 290 * @brief Returns true if IOUT_OC fault occurred (bit 4 STATUS_BYTE). 291 */ 292 bool hasIoutOCFault() const 293 { 294 return (ioutOCFault >= DEGLITCH_LIMIT); 295 } 296 297 /** 298 * @brief Returns true if VOUT_UV_FAULT occurred. 299 */ 300 bool hasVoutUVFault() const 301 { 302 return (voutUVFault >= DEGLITCH_LIMIT); 303 } 304 305 /** 306 *@brief Returns true if fan fault occurred. 307 */ 308 bool hasFanFault() const 309 { 310 return (fanFault >= DEGLITCH_LIMIT); 311 } 312 313 /** 314 * @brief Returns true if TEMPERATURE fault occurred. 315 */ 316 bool hasTempFault() const 317 { 318 return (tempFault >= DEGLITCH_LIMIT); 319 } 320 321 /** 322 * @brief Returns true if there is a PGood fault (PGOOD# inactive, or OFF 323 * bit on). 324 */ 325 bool hasPgoodFault() const 326 { 327 return (pgoodFault >= DEGLITCH_LIMIT); 328 } 329 330 /** 331 * @brief Return true if there is a PS_Kill fault. 332 */ 333 bool hasPSKillFault() const 334 { 335 return (psKillFault >= DEGLITCH_LIMIT); 336 } 337 338 /** 339 * @brief Returns true if there is a 12Vcs (standy power) fault. 340 */ 341 bool hasPS12VcsFault() const 342 { 343 return (ps12VcsFault >= DEGLITCH_LIMIT); 344 } 345 346 /** 347 * @brief Returns true if there is a 12V current-share fault. 348 */ 349 bool hasPSCS12VFault() const 350 { 351 return (psCS12VFault >= DEGLITCH_LIMIT); 352 } 353 354 /** 355 * @brief Returns the device path 356 * 357 * This can be used for error call outs. 358 * Example: /sys/bus/i2c/devices/3-0068 359 */ 360 const std::string getDevicePath() const 361 { 362 return pmbusIntf->path(); 363 } 364 365 /** 366 * @brief Returns this power supply's inventory path. 367 * 368 * This can be used for error call outs. 369 * Example: 370 * /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1 371 */ 372 const std::string& getInventoryPath() const 373 { 374 return inventoryPath; 375 } 376 377 /** 378 * @brief Returns the short name (last part of inventoryPath). 379 */ 380 const std::string& getShortName() const 381 { 382 return shortName; 383 } 384 385 /** 386 * @brief Returns the firmware revision version read from the power supply 387 */ 388 const std::string& getFWVersion() const 389 { 390 return fwVersion; 391 } 392 393 /** 394 * @brief Returns the model name of the power supply 395 */ 396 const std::string& getModelName() const 397 { 398 return modelName; 399 } 400 401 /** @brief Returns true if the number of failed reads exceeds limit 402 * TODO: or CML bit on. 403 */ 404 bool hasCommFault() const 405 { 406 return ((readFail >= LOG_LIMIT) || (cmlFault >= DEGLITCH_LIMIT)); 407 } 408 409 /** 410 * @brief Reads the pmbus input voltage and returns that actual voltage 411 * reading and the calculated input voltage based on thresholds. 412 * @param[out] actualInputVoltage - The actual voltage reading, in Volts. 413 * @param[out] inputVoltage - A rounded up/down value of the actual input 414 * voltage based on thresholds, in Volts. 415 */ 416 void getInputVoltage(double& actualInputVoltage, int& inputVoltage) const; 417 418 /** 419 * @brief Check if the PS is considered to be available or not 420 * 421 * It is unavailable if any of: 422 * - not present 423 * - input fault active 424 * - Vin UV fault active 425 * - PS KILL fault active 426 * - Iout OC fault active 427 * 428 * Other faults will, through creating error logs with callouts, already 429 * be setting the Functional property to false. 430 * 431 * On changes, the Available property is updated in the inventory. 432 */ 433 void checkAvailability(); 434 435 private: 436 /** @brief systemd bus member */ 437 sdbusplus::bus::bus& bus; 438 439 /** @brief Will be updated to the latest/lastvalue read from STATUS_WORD.*/ 440 uint64_t statusWord = 0; 441 442 /** @brief Will be set to the last read value of STATUS_WORD. */ 443 uint64_t statusWordOld = 0; 444 445 /** @brief Will be updated to the latest/lastvalue read from STATUS_INPUT.*/ 446 uint64_t statusInput = 0; 447 448 /** @brief Will be updated to the latest/lastvalue read from STATUS_MFR.*/ 449 uint64_t statusMFR = 0; 450 451 /** @brief Will be updated to the latest/last value read from STATUS_CML.*/ 452 uint64_t statusCML = 0; 453 454 /** @brief Will be updated to the latest/last value read from STATUS_VOUT.*/ 455 uint64_t statusVout = 0; 456 457 /** @brief Will be updated to the latest/last value read from STATUS_IOUT.*/ 458 uint64_t statusIout = 0; 459 460 /** @brief Will be updated to the latest/last value read from 461 * STATUS_FANS_1_2. */ 462 uint64_t statusFans12 = 0; 463 464 /** @brief Will be updated to the latest/last value read from 465 * STATUS_TEMPERATURE.*/ 466 uint64_t statusTemperature = 0; 467 468 /** @brief Will be updated with latest converted value read from READ_VIN */ 469 int inputVoltage = phosphor::pmbus::in_input::VIN_VOLTAGE_0; 470 471 /** @brief True if an error for a fault has already been logged. */ 472 bool faultLogged = false; 473 474 /** @brief Incremented if bit 1 of STATUS_WORD low byte is on. 475 * 476 * Considered faulted if reaches DEGLITCH_LIMIT. 477 */ 478 size_t cmlFault = 0; 479 480 /** @brief Incremented if bit 5 of STATUS_WORD high byte is on. 481 * 482 * Considered faulted if reaches DEGLITCH_LIMIT. 483 */ 484 size_t inputFault = 0; 485 486 /** @brief Incremented if bit 4 of STATUS_WORD high byte is on. 487 * 488 * Considered faulted if reaches DEGLITCH_LIMIT. 489 */ 490 size_t mfrFault = 0; 491 492 /** @brief Incremented if bit 3 of STATUS_WORD low byte is on. 493 * 494 * Considered faulted if reaches DEGLITCH_LIMIT. 495 */ 496 size_t vinUVFault = 0; 497 498 /** @brief Incremented if bit 5 of STATUS_WORD low byte is on. 499 * 500 * Considered faulted if reaches DEGLITCH_LIMIT. 501 */ 502 size_t voutOVFault = 0; 503 504 /** @brief Incremented if bit 4 of STATUS_WORD low byte is on. 505 * 506 * Considered faulted if reaches DEGLITCH_LIMIT. 507 */ 508 size_t ioutOCFault = 0; 509 510 /** @brief Incremented if bit 7 of STATUS_WORD high byte is on and bit 5 511 * (VOUT_OV) of low byte is off. 512 * 513 * Considered faulted if reaches DEGLITCH_LIMIT. 514 */ 515 size_t voutUVFault = 0; 516 517 /** @brief Incremented if FANS fault/warn bit on in STATUS_WORD. 518 * 519 * Considered faulted if reaches DEGLITCH_LIMIT. 520 */ 521 size_t fanFault = 0; 522 523 /** @brief Incremented if bit 2 of STATUS_WORD low byte is on. 524 * 525 * Considered faulted if reaches DEGLITCH_LIMIT. */ 526 size_t tempFault = 0; 527 528 /** 529 * @brief Incremented if bit 11 or 6 of STATUS_WORD is on. PGOOD# is 530 * inactive, or the unit is off. 531 * 532 * Considered faulted if reaches DEGLITCH_LIMIT. 533 */ 534 size_t pgoodFault = 0; 535 536 /** 537 * @brief Power Supply Kill fault. 538 * 539 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 540 * bit 4 to indicate this fault. Considered faulted if it reaches 541 * DEGLITCH_LIMIT. 542 */ 543 size_t psKillFault = 0; 544 545 /** 546 * @brief Power Supply 12Vcs fault (standby power). 547 * 548 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 549 * bit 6 to indicate this fault. Considered faulted if it reaches 550 * DEGLITCH_LIMIT. 551 */ 552 size_t ps12VcsFault = 0; 553 554 /** 555 * @brief Power Supply Current-Share fault in 12V domain. 556 * 557 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 558 * bit 7 to indicate this fault. Considered faulted if it reaches 559 * DEGLITCH_LIMIT. 560 */ 561 size_t psCS12VFault = 0; 562 563 /** @brief Count of the number of read failures. */ 564 size_t readFail = 0; 565 566 /** 567 * @brief Examine STATUS_WORD for CML (communication, memory, logic fault). 568 */ 569 void analyzeCMLFault(); 570 571 /** 572 * @brief Examine STATUS_WORD for INPUT bit on. 573 * 574 * "An input voltage, input current, or input power fault or warning has 575 * occurred." 576 */ 577 void analyzeInputFault(); 578 579 /** 580 * @brief Examine STATUS_WORD for VOUT being set. 581 * 582 * If VOUT is on, "An output voltage fault or warning has occurred.", and 583 * VOUT_OV_FAULT is on, there is an output over-voltage fault. 584 */ 585 void analyzeVoutOVFault(); 586 587 /* 588 * @brief Examine STATUS_WORD value read for IOUT_OC_FAULT. 589 * 590 * "An output overcurrent fault has occurred." If it is on, and fault not 591 * set, trace STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_IOUT values. 592 */ 593 void analyzeIoutOCFault(); 594 595 /** 596 * @brief Examines STATUS_WORD value read to see if there is a UV fault. 597 * 598 * Checks if the VOUT bit is on, indicating "An output voltage fault or 599 * warning has occurred", if it is on, but VOUT_OV_FAULT is off, it is 600 * determined to be an indication of an output under-voltage fault. 601 */ 602 void analyzeVoutUVFault(); 603 604 /** 605 * @brief Examine STATUS_WORD for the fan fault/warning bit. 606 * 607 * If fanFault is not on, trace that the bit now came on, include 608 * STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_FANS_1_2 values as well, to 609 * help with understanding what may have caused it to be set. 610 */ 611 void analyzeFanFault(); 612 613 /** 614 * @brief Examine STATUS_WORD for temperature fault. 615 */ 616 void analyzeTemperatureFault(); 617 618 /** 619 * @brief Examine STATUS_WORD for pgood or unit off faults. 620 */ 621 void analyzePgoodFault(); 622 623 /** 624 * @brief Determine possible manufacturer-specific faults from bits in 625 * STATUS_MFR. 626 * 627 * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer 628 * Defined" meanings. Determine which faults, if any, are present based on 629 * the power supply (device driver) type. 630 */ 631 void determineMFRFault(); 632 633 /** 634 * @brief Examine STATUS_WORD value read for MFRSPECIFIC bit on. 635 * 636 * "A manufacturer specific fault or warning has occurred." 637 * 638 * If it is on, call the determineMFRFault() helper function to examine the 639 * value read from STATUS_MFR_SPECIFIC. 640 */ 641 void analyzeMFRFault(); 642 643 /** 644 * @brief Analyzes the STATUS_WORD for a VIN_UV_FAULT indicator. 645 */ 646 void analyzeVinUVFault(); 647 648 /** 649 * @brief D-Bus path to use for this power supply's inventory status. 650 **/ 651 std::string inventoryPath; 652 653 /** 654 * @brief Store the short name to avoid string processing. 655 * 656 * The short name will be something like powersupply1, the last part of the 657 * inventoryPath. 658 */ 659 std::string shortName; 660 661 /** 662 * @brief Given a full inventory path, returns the last node of the path as 663 * the "short name" 664 */ 665 std::string findShortName(const std::string& invPath) 666 { 667 auto const lastSlashPos = invPath.find_last_of('/'); 668 669 if ((lastSlashPos == std::string::npos) || 670 ((lastSlashPos + 1) == invPath.size())) 671 { 672 return invPath; 673 } 674 else 675 { 676 return invPath.substr(lastSlashPos + 1); 677 } 678 } 679 680 /** 681 * @brief The libgpiod object for monitoring PSU presence 682 */ 683 std::unique_ptr<GPIOInterfaceBase> presenceGPIO = nullptr; 684 685 /** @brief True if the power supply is present. */ 686 bool present = false; 687 688 /** @brief Power supply model name. */ 689 std::string modelName; 690 691 /** @brief D-Bus match variable used to subscribe to Present property 692 * changes. 693 **/ 694 std::unique_ptr<sdbusplus::bus::match_t> presentMatch; 695 696 /** @brief D-Bus match variable used to subscribe for Present property 697 * interface added. 698 */ 699 std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch; 700 701 /** 702 * @brief Pointer to the PMBus interface 703 * 704 * Used to read or write to/from PMBus power supply devices. 705 */ 706 std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr; 707 708 /** @brief Stored copy of the firmware version/revision string */ 709 std::string fwVersion; 710 711 /** 712 * @brief The file system path used for binding the device driver. 713 */ 714 const std::filesystem::path bindPath; 715 716 /* @brief The string to pass in for binding the device driver. */ 717 std::string bindDevice; 718 719 /** 720 * @brief The result of the most recent availability check 721 * 722 * Saved on the object so changes can be detected. 723 */ 724 bool available = false; 725 726 /** 727 * @brief Binds or unbinds the power supply device driver 728 * 729 * Called when a presence change is detected to either bind the device 730 * driver for the power supply when it is installed, or unbind the device 731 * driver when the power supply is removed. 732 * 733 * Writes <device> to <path>/bind (or unbind) 734 * 735 * @param present - when true, will bind the device driver 736 * when false, will unbind the device driver 737 */ 738 void bindOrUnbindDriver(bool present); 739 740 /** 741 * @brief Updates the presence status by querying D-Bus 742 * 743 * The D-Bus inventory properties for this power supply will be read to 744 * determine if the power supply is present or not and update this 745 * object's present member variable to reflect current status. 746 **/ 747 void updatePresence(); 748 749 /** 750 * @brief Updates the power supply presence by reading the GPIO line. 751 */ 752 void updatePresenceGPIO(); 753 754 /** 755 * @brief Callback for inventory property changes 756 * 757 * Process change of Present property for power supply. 758 * 759 * This is used if we are watching the D-Bus properties instead of reading 760 * the GPIO presence line ourselves. 761 * 762 * @param[in] msg - Data associated with Present change signal 763 **/ 764 void inventoryChanged(sdbusplus::message::message& msg); 765 766 /** 767 * @brief Callback for inventory property added. 768 * 769 * Process add of the interface with the Present property for power supply. 770 * 771 * This is used if we are watching the D-Bus properties instead of reading 772 * the GPIO presence line ourselves. 773 * 774 * @param[in] msg - Data associated with Present add signal 775 **/ 776 void inventoryAdded(sdbusplus::message::message& msg); 777 }; 778 779 } // namespace phosphor::power::psu 780