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 supplies 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 firmware revision version read from the power supply 379 */ 380 const std::string& getFWVersion() const 381 { 382 return fwVersion; 383 } 384 385 /** 386 * @brief Returns the model name of the power supply 387 */ 388 const std::string& getModelName() const 389 { 390 return modelName; 391 } 392 393 /** @brief Returns true if the number of failed reads exceeds limit 394 * TODO: or CML bit on. 395 */ 396 bool hasCommFault() const 397 { 398 return ((readFail >= LOG_LIMIT) || (cmlFault >= DEGLITCH_LIMIT)); 399 } 400 401 /** 402 * @brief Reads the pmbus input voltage and returns that actual voltage 403 * reading and the calculated input voltage based on thresholds. 404 * @param[out] actualInputVoltage - The actual voltage reading, in Volts. 405 * @param[out] inputVoltage - A rounded up/down value of the actual input 406 * voltage based on thresholds, in Volts. 407 */ 408 void getInputVoltage(double& actualInputVoltage, int& inputVoltage) const; 409 410 private: 411 /** @brief systemd bus member */ 412 sdbusplus::bus::bus& bus; 413 414 /** @brief Will be updated to the latest/lastvalue read from STATUS_WORD.*/ 415 uint64_t statusWord = 0; 416 417 /** @brief Will be updated to the latest/lastvalue read from STATUS_INPUT.*/ 418 uint64_t statusInput = 0; 419 420 /** @brief Will be updated to the latest/lastvalue read from STATUS_MFR.*/ 421 uint64_t statusMFR = 0; 422 423 /** @brief Will be updated to the latest/last value read from STATUS_CML.*/ 424 uint64_t statusCML = 0; 425 426 /** @brief Will be updated to the latest/last value read from STATUS_VOUT.*/ 427 uint64_t statusVout = 0; 428 429 /** @brief Will be updated to the latest/last value read from STATUS_IOUT.*/ 430 uint64_t statusIout = 0; 431 432 /** @brief Will be updated to the latest/last value read from 433 * STATUS_FANS_1_2. */ 434 uint64_t statusFans12 = 0; 435 436 /** @brief Will be updated to the latest/last value read from 437 * STATUS_TEMPERATURE.*/ 438 uint64_t statusTemperature = 0; 439 440 /** @brief Will be updated with latest converted value read from READ_VIN */ 441 int inputVoltage = phosphor::pmbus::in_input::VIN_VOLTAGE_0; 442 443 /** @brief True if an error for a fault has already been logged. */ 444 bool faultLogged = false; 445 446 /** @brief Incremented if bit 1 of STATUS_WORD low byte is on. 447 * 448 * Considered faulted if reaches DEGLITCH_LIMIT. 449 */ 450 size_t cmlFault = 0; 451 452 /** @brief Incremented if bit 5 of STATUS_WORD high byte is on. 453 * 454 * Considered faulted if reaches DEGLITCH_LIMIT. 455 */ 456 size_t inputFault = 0; 457 458 /** @brief Incremented if bit 4 of STATUS_WORD high byte is on. 459 * 460 * Considered faulted if reaches DEGLITCH_LIMIT. 461 */ 462 size_t mfrFault = 0; 463 464 /** @brief Incremented if bit 3 of STATUS_WORD low byte is on. 465 * 466 * Considered faulted if reaches DEGLITCH_LIMIT. 467 */ 468 size_t vinUVFault = 0; 469 470 /** @brief Incremented if bit 5 of STATUS_WORD low byte is on. 471 * 472 * Considered faulted if reaches DEGLITCH_LIMIT. 473 */ 474 size_t voutOVFault = 0; 475 476 /** @brief Incremented if bit 4 of STATUS_WORD low byte is on. 477 * 478 * Considered faulted if reaches DEGLITCH_LIMIT. 479 */ 480 size_t ioutOCFault = 0; 481 482 /** @brief Incremented if bit 7 of STATUS_WORD high byte is on and bit 5 483 * (VOUT_OV) of low byte is off. 484 * 485 * Considered faulted if reaches DEGLITCH_LIMIT. 486 */ 487 size_t voutUVFault = 0; 488 489 /** @brief Incremented if FANS fault/warn bit on in STATUS_WORD. 490 * 491 * Considered faulted if reaches DEGLITCH_LIMIT. 492 */ 493 size_t fanFault = 0; 494 495 /** @brief Incremented if bit 2 of STATUS_WORD low byte is on. 496 * 497 * Considered faulted if reaches DEGLITCH_LIMIT. */ 498 size_t tempFault = 0; 499 500 /** 501 * @brief Incremented if bit 11 or 6 of STATUS_WORD is on. PGOOD# is 502 * inactive, or the unit is off. 503 * 504 * Considered faulted if reaches DEGLITCH_LIMIT. 505 */ 506 size_t pgoodFault = 0; 507 508 /** 509 * @brief Power Supply Kill fault. 510 * 511 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 512 * bit 4 to indicate this fault. Considered faulted if it reaches 513 * DEGLITCH_LIMIT. 514 */ 515 size_t psKillFault = 0; 516 517 /** 518 * @brief Power Supply 12Vcs fault (standby power). 519 * 520 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 521 * bit 6 to indicate this fault. Considered faulted if it reaches 522 * DEGLITCH_LIMIT. 523 */ 524 size_t ps12VcsFault = 0; 525 526 /** 527 * @brief Power Supply Current-Share fault in 12V domain. 528 * 529 * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use 530 * bit 7 to indicate this fault. Considered faulted if it reaches 531 * DEGLITCH_LIMIT. 532 */ 533 size_t psCS12VFault = 0; 534 535 /** @brief Count of the number of read failures. */ 536 size_t readFail = 0; 537 538 /** 539 * @brief Examine STATUS_WORD for CML (communication, memory, logic fault). 540 */ 541 void analyzeCMLFault(); 542 543 /** 544 * @brief Examine STATUS_WORD for INPUT bit on. 545 * 546 * "An input voltage, input current, or input power fault or warning has 547 * occurred." 548 */ 549 void analyzeInputFault(); 550 551 /** 552 * @brief Examine STATUS_WORD for VOUT being set. 553 * 554 * If VOUT is on, "An output voltage fault or warning has occurred.", and 555 * VOUT_OV_FAULT is on, there is an output over-voltage fault. 556 */ 557 void analyzeVoutOVFault(); 558 559 /* 560 * @brief Examine STATUS_WORD value read for IOUT_OC_FAULT. 561 * 562 * "An output overcurrent fault has occurred." If it is on, and fault not 563 * set, trace STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_IOUT values. 564 */ 565 void analyzeIoutOCFault(); 566 567 /** 568 * @brief Examines STATUS_WORD value read to see if there is a UV fault. 569 * 570 * Checks if the VOUT bit is on, indicating "An output voltage fault or 571 * warning has occurred", if it is on, but VOUT_OV_FAULT is off, it is 572 * determined to be an indication of an output under-voltage fault. 573 */ 574 void analyzeVoutUVFault(); 575 576 /** 577 * @brief Examine STATUS_WORD for the fan fault/warning bit. 578 * 579 * If fanFault is not on, trace that the bit now came on, include 580 * STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_FANS_1_2 values as well, to 581 * help with understanding what may have caused it to be set. 582 */ 583 void analyzeFanFault(); 584 585 /** 586 * @brief Examine STATUS_WORD for temperature fault. 587 */ 588 void analyzeTemperatureFault(); 589 590 /** 591 * @brief Examine STATUS_WORD for pgood or unit off faults. 592 */ 593 void analyzePgoodFault(); 594 595 /** 596 * @brief Determine possible manufacturer-specific faults from bits in 597 * STATUS_MFR. 598 * 599 * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer 600 * Defined" meanings. Determine which faults, if any, are present based on 601 * the power supply (device driver) type. 602 */ 603 void determineMFRFault(); 604 605 /** 606 * @brief Examine STATUS_WORD value read for MFRSPECIFIC bit on. 607 * 608 * "A manufacturer specific fault or warning has occurred." 609 * 610 * If it is on, call the determineMFRFault() helper function to examine the 611 * value read from STATUS_MFR_SPECIFIC. 612 */ 613 void analyzeMFRFault(); 614 615 /** 616 * @brief Analyzes the STATUS_WORD for a VIN_UV_FAULT indicator. 617 */ 618 void analyzeVinUVFault(); 619 620 /** 621 * @brief D-Bus path to use for this power supply's inventory status. 622 **/ 623 std::string inventoryPath; 624 625 /** 626 * @brief The libgpiod object for monitoring PSU presence 627 */ 628 std::unique_ptr<GPIOInterfaceBase> presenceGPIO = nullptr; 629 630 /** @brief True if the power supply is present. */ 631 bool present = false; 632 633 /** @brief Power supply model name. */ 634 std::string modelName; 635 636 /** @brief D-Bus match variable used to subscribe to Present property 637 * changes. 638 **/ 639 std::unique_ptr<sdbusplus::bus::match_t> presentMatch; 640 641 /** @brief D-Bus match variable used to subscribe for Present property 642 * interface added. 643 */ 644 std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch; 645 646 /** 647 * @brief Pointer to the PMBus interface 648 * 649 * Used to read or write to/from PMBus power supply devices. 650 */ 651 std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr; 652 653 /** @brief Stored copy of the firmware version/revision string */ 654 std::string fwVersion; 655 656 /** 657 * @brief The file system path used for binding the device driver. 658 */ 659 const std::filesystem::path bindPath; 660 661 /* @brief The string to pass in for binding the device driver. */ 662 std::string bindDevice; 663 664 /** 665 * @brief Binds or unbinds the power supply device driver 666 * 667 * Called when a presence change is detected to either bind the device 668 * driver for the power supply when it is installed, or unbind the device 669 * driver when the power supply is removed. 670 * 671 * Writes <device> to <path>/bind (or unbind) 672 * 673 * @param present - when true, will bind the device driver 674 * when false, will unbind the device driver 675 */ 676 void bindOrUnbindDriver(bool present); 677 678 /** 679 * @brief Updates the presence status by querying D-Bus 680 * 681 * The D-Bus inventory properties for this power supply will be read to 682 * determine if the power supply is present or not and update this 683 * object's present member variable to reflect current status. 684 **/ 685 void updatePresence(); 686 687 /** 688 * @brief Updates the power supply presence by reading the GPIO line. 689 */ 690 void updatePresenceGPIO(); 691 692 /** 693 * @brief Callback for inventory property changes 694 * 695 * Process change of Present property for power supply. 696 * 697 * This is used if we are watching the D-Bus properties instead of reading 698 * the GPIO presence line ourselves. 699 * 700 * @param[in] msg - Data associated with Present change signal 701 **/ 702 void inventoryChanged(sdbusplus::message::message& msg); 703 704 /** 705 * @brief Callback for inventory property added. 706 * 707 * Process add of the interface with the Present property for power supply. 708 * 709 * This is used if we are watching the D-Bus properties instead of reading 710 * the GPIO presence line ourselves. 711 * 712 * @param[in] msg - Data associated with Present add signal 713 **/ 714 void inventoryAdded(sdbusplus::message::message& msg); 715 }; 716 717 } // namespace phosphor::power::psu 718