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 Will be updated with the actual voltage last read from READ_VIN 472 */ 473 double actualInputVoltage = 0; 474 475 /** @brief True if an error for a fault has already been logged. */ 476 bool faultLogged = false; 477 478 /** @brief Incremented if bit 1 of STATUS_WORD low byte is on. 479 * 480 * Considered faulted if reaches DEGLITCH_LIMIT. 481 */ 482 size_t cmlFault = 0; 483 484 /** @brief Incremented if bit 5 of STATUS_WORD high byte is on. 485 * 486 * Considered faulted if reaches DEGLITCH_LIMIT. 487 */ 488 size_t inputFault = 0; 489 490 /** @brief Incremented if bit 4 of STATUS_WORD high byte is on. 491 * 492 * Considered faulted if reaches DEGLITCH_LIMIT. 493 */ 494 size_t mfrFault = 0; 495 496 /** @brief Incremented if bit 3 of STATUS_WORD low byte is on. 497 * 498 * Considered faulted if reaches DEGLITCH_LIMIT. 499 */ 500 size_t vinUVFault = 0; 501 502 /** @brief Incremented if bit 5 of STATUS_WORD low byte is on. 503 * 504 * Considered faulted if reaches DEGLITCH_LIMIT. 505 */ 506 size_t voutOVFault = 0; 507 508 /** @brief Incremented if bit 4 of STATUS_WORD low byte is on. 509 * 510 * Considered faulted if reaches DEGLITCH_LIMIT. 511 */ 512 size_t ioutOCFault = 0; 513 514 /** @brief Incremented if bit 7 of STATUS_WORD high byte is on and bit 5 515 * (VOUT_OV) of low byte is off. 516 * 517 * Considered faulted if reaches DEGLITCH_LIMIT. 518 */ 519 size_t voutUVFault = 0; 520 521 /** @brief Incremented if FANS fault/warn bit on in STATUS_WORD. 522 * 523 * Considered faulted if reaches DEGLITCH_LIMIT. 524 */ 525 size_t fanFault = 0; 526 527 /** @brief Incremented if bit 2 of STATUS_WORD low byte is on. 528 * 529 * Considered faulted if reaches DEGLITCH_LIMIT. */ 530 size_t tempFault = 0; 531 532 /** 533 * @brief Incremented if bit 11 or 6 of STATUS_WORD is on. PGOOD# is 534 * inactive, or the unit is off. 535 * 536 * Considered faulted if reaches DEGLITCH_LIMIT. 537 */ 538 size_t pgoodFault = 0; 539 540 /** 541 * @brief Power Supply Kill fault. 542 * 543 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 544 * bit 4 to indicate this fault. Considered faulted if it reaches 545 * DEGLITCH_LIMIT. 546 */ 547 size_t psKillFault = 0; 548 549 /** 550 * @brief Power Supply 12Vcs fault (standby power). 551 * 552 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 553 * bit 6 to indicate this fault. Considered faulted if it reaches 554 * DEGLITCH_LIMIT. 555 */ 556 size_t ps12VcsFault = 0; 557 558 /** 559 * @brief Power Supply Current-Share fault in 12V domain. 560 * 561 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 562 * bit 7 to indicate this fault. Considered faulted if it reaches 563 * DEGLITCH_LIMIT. 564 */ 565 size_t psCS12VFault = 0; 566 567 /** @brief Count of the number of read failures. */ 568 size_t readFail = 0; 569 570 /** 571 * @brief Examine STATUS_WORD for CML (communication, memory, logic fault). 572 */ 573 void analyzeCMLFault(); 574 575 /** 576 * @brief Examine STATUS_WORD for INPUT bit on. 577 * 578 * "An input voltage, input current, or input power fault or warning has 579 * occurred." 580 */ 581 void analyzeInputFault(); 582 583 /** 584 * @brief Examine STATUS_WORD for VOUT being set. 585 * 586 * If VOUT is on, "An output voltage fault or warning has occurred.", and 587 * VOUT_OV_FAULT is on, there is an output over-voltage fault. 588 */ 589 void analyzeVoutOVFault(); 590 591 /* 592 * @brief Examine STATUS_WORD value read for IOUT_OC_FAULT. 593 * 594 * "An output overcurrent fault has occurred." If it is on, and fault not 595 * set, trace STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_IOUT values. 596 */ 597 void analyzeIoutOCFault(); 598 599 /** 600 * @brief Examines STATUS_WORD value read to see if there is a UV fault. 601 * 602 * Checks if the VOUT bit is on, indicating "An output voltage fault or 603 * warning has occurred", if it is on, but VOUT_OV_FAULT is off, it is 604 * determined to be an indication of an output under-voltage fault. 605 */ 606 void analyzeVoutUVFault(); 607 608 /** 609 * @brief Examine STATUS_WORD for the fan fault/warning bit. 610 * 611 * If fanFault is not on, trace that the bit now came on, include 612 * STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_FANS_1_2 values as well, to 613 * help with understanding what may have caused it to be set. 614 */ 615 void analyzeFanFault(); 616 617 /** 618 * @brief Examine STATUS_WORD for temperature fault. 619 */ 620 void analyzeTemperatureFault(); 621 622 /** 623 * @brief Examine STATUS_WORD for pgood or unit off faults. 624 */ 625 void analyzePgoodFault(); 626 627 /** 628 * @brief Determine possible manufacturer-specific faults from bits in 629 * STATUS_MFR. 630 * 631 * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer 632 * Defined" meanings. Determine which faults, if any, are present based on 633 * the power supply (device driver) type. 634 */ 635 void determineMFRFault(); 636 637 /** 638 * @brief Examine STATUS_WORD value read for MFRSPECIFIC bit on. 639 * 640 * "A manufacturer specific fault or warning has occurred." 641 * 642 * If it is on, call the determineMFRFault() helper function to examine the 643 * value read from STATUS_MFR_SPECIFIC. 644 */ 645 void analyzeMFRFault(); 646 647 /** 648 * @brief Analyzes the STATUS_WORD for a VIN_UV_FAULT indicator. 649 */ 650 void analyzeVinUVFault(); 651 652 /** 653 * @brief D-Bus path to use for this power supply's inventory status. 654 **/ 655 std::string inventoryPath; 656 657 /** 658 * @brief Store the short name to avoid string processing. 659 * 660 * The short name will be something like powersupply1, the last part of the 661 * inventoryPath. 662 */ 663 std::string shortName; 664 665 /** 666 * @brief Given a full inventory path, returns the last node of the path as 667 * the "short name" 668 */ 669 std::string findShortName(const std::string& invPath) 670 { 671 auto const lastSlashPos = invPath.find_last_of('/'); 672 673 if ((lastSlashPos == std::string::npos) || 674 ((lastSlashPos + 1) == invPath.size())) 675 { 676 return invPath; 677 } 678 else 679 { 680 return invPath.substr(lastSlashPos + 1); 681 } 682 } 683 684 /** 685 * @brief The libgpiod object for monitoring PSU presence 686 */ 687 std::unique_ptr<GPIOInterfaceBase> presenceGPIO = nullptr; 688 689 /** @brief True if the power supply is present. */ 690 bool present = false; 691 692 /** @brief Power supply model name. */ 693 std::string modelName; 694 695 /** @brief D-Bus match variable used to subscribe to Present property 696 * changes. 697 **/ 698 std::unique_ptr<sdbusplus::bus::match_t> presentMatch; 699 700 /** @brief D-Bus match variable used to subscribe for Present property 701 * interface added. 702 */ 703 std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch; 704 705 /** 706 * @brief Pointer to the PMBus interface 707 * 708 * Used to read or write to/from PMBus power supply devices. 709 */ 710 std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr; 711 712 /** @brief Stored copy of the firmware version/revision string */ 713 std::string fwVersion; 714 715 /** 716 * @brief The file system path used for binding the device driver. 717 */ 718 const std::filesystem::path bindPath; 719 720 /* @brief The string to pass in for binding the device driver. */ 721 std::string bindDevice; 722 723 /** 724 * @brief The result of the most recent availability check 725 * 726 * Saved on the object so changes can be detected. 727 */ 728 bool available = false; 729 730 /** 731 * @brief Binds or unbinds the power supply device driver 732 * 733 * Called when a presence change is detected to either bind the device 734 * driver for the power supply when it is installed, or unbind the device 735 * driver when the power supply is removed. 736 * 737 * Writes <device> to <path>/bind (or unbind) 738 * 739 * @param present - when true, will bind the device driver 740 * when false, will unbind the device driver 741 */ 742 void bindOrUnbindDriver(bool present); 743 744 /** 745 * @brief Updates the presence status by querying D-Bus 746 * 747 * The D-Bus inventory properties for this power supply will be read to 748 * determine if the power supply is present or not and update this 749 * object's present member variable to reflect current status. 750 **/ 751 void updatePresence(); 752 753 /** 754 * @brief Updates the power supply presence by reading the GPIO line. 755 */ 756 void updatePresenceGPIO(); 757 758 /** 759 * @brief Callback for inventory property changes 760 * 761 * Process change of Present property for power supply. 762 * 763 * This is used if we are watching the D-Bus properties instead of reading 764 * the GPIO presence line ourselves. 765 * 766 * @param[in] msg - Data associated with Present change signal 767 **/ 768 void inventoryChanged(sdbusplus::message::message& msg); 769 770 /** 771 * @brief Callback for inventory property added. 772 * 773 * Process add of the interface with the Present property for power supply. 774 * 775 * This is used if we are watching the D-Bus properties instead of reading 776 * the GPIO presence line ourselves. 777 * 778 * @param[in] msg - Data associated with Present add signal 779 **/ 780 void inventoryAdded(sdbusplus::message::message& msg); 781 }; 782 783 } // namespace phosphor::power::psu 784