1 #include "../power_supply.hpp" 2 #include "mock.hpp" 3 4 #include <xyz/openbmc_project/Common/Device/error.hpp> 5 #include <xyz/openbmc_project/Common/error.hpp> 6 7 #include <gmock/gmock.h> 8 #include <gtest/gtest.h> 9 10 using namespace phosphor::power::psu; 11 using namespace phosphor::pmbus; 12 13 using ::testing::_; 14 using ::testing::Args; 15 using ::testing::Assign; 16 using ::testing::DoAll; 17 using ::testing::ElementsAre; 18 using ::testing::NotNull; 19 using ::testing::Return; 20 using ::testing::StrEq; 21 22 static auto PSUInventoryPath = "/xyz/bmc/inv/sys/chassis/board/powersupply0"; 23 static auto PSUGPIOLineName = "presence-ps0"; 24 25 struct PMBusExpectations 26 { 27 uint16_t statusWordValue{0x0000}; 28 uint8_t statusInputValue{0x00}; 29 uint8_t statusMFRValue{0x00}; 30 uint8_t statusCMLValue{0x00}; 31 uint8_t statusVOUTValue{0x00}; 32 uint8_t statusTempValue{0x00}; 33 }; 34 35 // Helper function to setup expectations for various STATUS_* commands 36 void setPMBusExpectations(MockedPMBus& mockPMBus, 37 const PMBusExpectations& expectations) 38 { 39 EXPECT_CALL(mockPMBus, read(STATUS_WORD, _)) 40 .Times(1) 41 .WillOnce(Return(expectations.statusWordValue)); 42 43 if (expectations.statusWordValue != 0) 44 { 45 // If fault bits are on in STATUS_WORD, there will also be a read of 46 // STATUS_INPUT, STATUS_MFR, STATUS_CML, STATUS_VOUT (page 0), and 47 // STATUS_TEMPERATURE. 48 EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _)) 49 .Times(1) 50 .WillOnce(Return(expectations.statusInputValue)); 51 EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)) 52 .Times(1) 53 .WillOnce(Return(expectations.statusMFRValue)); 54 EXPECT_CALL(mockPMBus, read(STATUS_CML, _)) 55 .Times(1) 56 .WillOnce(Return(expectations.statusCMLValue)); 57 // Page will need to be set to 0 to read STATUS_VOUT. 58 EXPECT_CALL(mockPMBus, insertPageNum(STATUS_VOUT, 0)) 59 .Times(1) 60 .WillOnce(Return("status0_vout")); 61 EXPECT_CALL(mockPMBus, read("status0_vout", _)) 62 .Times(1) 63 .WillOnce(Return(expectations.statusVOUTValue)); 64 EXPECT_CALL(mockPMBus, read(STATUS_TEMPERATURE, _)) 65 .Times(1) 66 .WillOnce(Return(expectations.statusTempValue)); 67 } 68 } 69 70 class PowerSupplyTests : public ::testing::Test 71 { 72 public: 73 PowerSupplyTests() : 74 mockedUtil(reinterpret_cast<const MockedUtil&>(getUtils())) 75 { 76 ON_CALL(mockedUtil, getPresence(_, _)).WillByDefault(Return(false)); 77 } 78 79 ~PowerSupplyTests() override 80 { 81 freeUtils(); 82 } 83 84 const MockedUtil& mockedUtil; 85 }; 86 87 TEST_F(PowerSupplyTests, Constructor) 88 { 89 /** 90 * @param[in] invpath - String for inventory path to use 91 * @param[in] i2cbus - The bus number this power supply is on 92 * @param[in] i2caddr - The 16-bit I2C address of the power supply 93 * @param[in] gpioLineName - The string for the gpio-line-name to read for 94 * presence. 95 * @param[in] bindDelay - Time in milliseconds to delay binding the device 96 * driver after seeing the presence line go active. 97 */ 98 auto bus = sdbusplus::bus::new_default(); 99 100 // Try where inventory path is empty, constructor should fail. 101 try 102 { 103 auto psu = 104 std::make_unique<PowerSupply>(bus, "", 3, 0x68, PSUGPIOLineName); 105 ADD_FAILURE() << "Should not have reached this line."; 106 } 107 catch (const std::invalid_argument& e) 108 { 109 EXPECT_STREQ(e.what(), "Invalid empty inventoryPath"); 110 } 111 catch (...) 112 { 113 ADD_FAILURE() << "Should not have caught exception."; 114 } 115 116 // TODO: Try invalid i2c address? 117 118 // Try where gpioLineName is empty. 119 try 120 { 121 auto psu = 122 std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, ""); 123 ADD_FAILURE() 124 << "Should not have reached this line. Invalid gpioLineName."; 125 } 126 catch (const std::invalid_argument& e) 127 { 128 EXPECT_STREQ(e.what(), "Invalid empty gpioLineName"); 129 } 130 catch (...) 131 { 132 ADD_FAILURE() << "Should not have caught exception."; 133 } 134 135 // Test with valid arguments 136 // NOT using D-Bus inventory path for presence. 137 try 138 { 139 auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, 140 PSUGPIOLineName); 141 142 EXPECT_EQ(psu->isPresent(), false); 143 EXPECT_EQ(psu->isFaulted(), false); 144 EXPECT_EQ(psu->hasCommFault(), false); 145 EXPECT_EQ(psu->hasInputFault(), false); 146 EXPECT_EQ(psu->hasMFRFault(), false); 147 EXPECT_EQ(psu->hasVINUVFault(), false); 148 EXPECT_EQ(psu->hasVoutOVFault(), false); 149 EXPECT_EQ(psu->hasTempFault(), false); 150 } 151 catch (...) 152 { 153 ADD_FAILURE() << "Should not have caught exception."; 154 } 155 156 // Test with valid arguments 157 // TODO: Using D-Bus inventory path for presence. 158 try 159 { 160 // FIXME: How do I get that presenceGPIO.read() in the startup to throw 161 // an exception? 162 163 // EXPECT_CALL(mockedUtil, getPresence(_, 164 // StrEq(PSUInventoryPath))) 165 // .Times(1); 166 } 167 catch (...) 168 { 169 ADD_FAILURE() << "Should not have caught exception."; 170 } 171 } 172 173 TEST_F(PowerSupplyTests, Analyze) 174 { 175 auto bus = sdbusplus::bus::new_default(); 176 177 { 178 // If I default to reading the GPIO, I will NOT expect a call to 179 // getPresence(). 180 181 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 182 MockedGPIOInterface* mockPresenceGPIO = 183 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 184 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(0)); 185 186 psu.analyze(); 187 // By default, nothing should change. 188 EXPECT_EQ(psu.isPresent(), false); 189 EXPECT_EQ(psu.isFaulted(), false); 190 EXPECT_EQ(psu.hasInputFault(), false); 191 EXPECT_EQ(psu.hasMFRFault(), false); 192 EXPECT_EQ(psu.hasVINUVFault(), false); 193 EXPECT_EQ(psu.hasCommFault(), false); 194 EXPECT_EQ(psu.hasVoutOVFault(), false); 195 EXPECT_EQ(psu.hasTempFault(), false); 196 } 197 198 PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 199 // In order to get the various faults tested, the power supply needs to 200 // be present in order to read from the PMBus device(s). 201 MockedGPIOInterface* mockPresenceGPIO2 = 202 static_cast<MockedGPIOInterface*>(psu2.getPresenceGPIO()); 203 ON_CALL(*mockPresenceGPIO2, read()).WillByDefault(Return(1)); 204 EXPECT_EQ(psu2.isPresent(), false); 205 206 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus()); 207 // Presence change from missing to present will trigger write to 208 // ON_OFF_CONFIG. 209 EXPECT_CALL(mockPMBus, writeBinary(ON_OFF_CONFIG, _, _)); 210 // Presence change from missing to present will trigger in1_input read 211 // in an attempt to get CLEAR_FAULTS called. 212 EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000)); 213 214 // STATUS_WORD INPUT fault. 215 { 216 // Start with STATUS_WORD 0x0000. Powered on, no faults. 217 // Set expectations for a no fault 218 PMBusExpectations expectations; 219 setPMBusExpectations(mockPMBus, expectations); 220 psu2.analyze(); 221 EXPECT_EQ(psu2.isPresent(), true); 222 EXPECT_EQ(psu2.isFaulted(), false); 223 EXPECT_EQ(psu2.hasInputFault(), false); 224 EXPECT_EQ(psu2.hasMFRFault(), false); 225 EXPECT_EQ(psu2.hasVINUVFault(), false); 226 EXPECT_EQ(psu2.hasCommFault(), false); 227 EXPECT_EQ(psu2.hasVoutOVFault(), false); 228 EXPECT_EQ(psu2.hasTempFault(), false); 229 230 // Update expectations for STATUS_WORD input fault/warn 231 // STATUS_INPUT fault bits ... on. 232 expectations.statusWordValue = (status_word::INPUT_FAULT_WARN); 233 expectations.statusInputValue = 0x38; 234 setPMBusExpectations(mockPMBus, expectations); 235 psu2.analyze(); 236 EXPECT_EQ(psu2.isPresent(), true); 237 EXPECT_EQ(psu2.isFaulted(), true); 238 EXPECT_EQ(psu2.hasInputFault(), true); 239 EXPECT_EQ(psu2.hasMFRFault(), false); 240 EXPECT_EQ(psu2.hasVINUVFault(), false); 241 EXPECT_EQ(psu2.hasCommFault(), false); 242 EXPECT_EQ(psu2.hasVoutOVFault(), false); 243 EXPECT_EQ(psu2.hasTempFault(), false); 244 } 245 246 // STATUS_WORD INPUT/UV fault. 247 { 248 // First need it to return good status, then the fault 249 PMBusExpectations expectations; 250 setPMBusExpectations(mockPMBus, expectations); 251 psu2.analyze(); 252 // Now set fault bits in STATUS_WORD 253 expectations.statusWordValue = 254 (status_word::INPUT_FAULT_WARN | status_word::VIN_UV_FAULT); 255 // STATUS_INPUT fault bits ... on. 256 expectations.statusInputValue = 0x38; 257 setPMBusExpectations(mockPMBus, expectations); 258 psu2.analyze(); 259 EXPECT_EQ(psu2.isPresent(), true); 260 EXPECT_EQ(psu2.isFaulted(), true); 261 EXPECT_EQ(psu2.hasInputFault(), true); 262 EXPECT_EQ(psu2.hasMFRFault(), false); 263 EXPECT_EQ(psu2.hasVINUVFault(), true); 264 EXPECT_EQ(psu2.hasCommFault(), false); 265 EXPECT_EQ(psu2.hasVoutOVFault(), false); 266 EXPECT_EQ(psu2.hasTempFault(), false); 267 } 268 269 // STATUS_WORD MFR fault. 270 { 271 // First need it to return good status, then the fault 272 PMBusExpectations expectations; 273 setPMBusExpectations(mockPMBus, expectations); 274 psu2.analyze(); 275 // Now STATUS_WORD with MFR fault bit on. 276 expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT); 277 // STATUS_MFR bits on. 278 expectations.statusMFRValue = 0xFF; 279 setPMBusExpectations(mockPMBus, expectations); 280 psu2.analyze(); 281 EXPECT_EQ(psu2.isPresent(), true); 282 EXPECT_EQ(psu2.isFaulted(), true); 283 EXPECT_EQ(psu2.hasInputFault(), false); 284 EXPECT_EQ(psu2.hasMFRFault(), true); 285 EXPECT_EQ(psu2.hasVINUVFault(), false); 286 EXPECT_EQ(psu2.hasCommFault(), false); 287 EXPECT_EQ(psu2.hasVoutOVFault(), false); 288 EXPECT_EQ(psu2.hasTempFault(), false); 289 } 290 291 // Temperature fault. 292 { 293 // First STATUS_WORD with no bits set, then with temperature fault. 294 PMBusExpectations expectations; 295 setPMBusExpectations(mockPMBus, expectations); 296 psu2.analyze(); 297 // STATUS_WORD with temperature fault bit on. 298 expectations.statusWordValue = (status_word::TEMPERATURE_FAULT_WARN); 299 // STATUS_TEMPERATURE with fault bit(s) on. 300 expectations.statusTempValue = 0x10; 301 setPMBusExpectations(mockPMBus, expectations); 302 psu2.analyze(); 303 EXPECT_EQ(psu2.isPresent(), true); 304 EXPECT_EQ(psu2.isFaulted(), true); 305 EXPECT_EQ(psu2.hasInputFault(), false); 306 EXPECT_EQ(psu2.hasMFRFault(), false); 307 EXPECT_EQ(psu2.hasVINUVFault(), false); 308 EXPECT_EQ(psu2.hasCommFault(), false); 309 EXPECT_EQ(psu2.hasVoutOVFault(), false); 310 EXPECT_EQ(psu2.hasTempFault(), true); 311 } 312 313 // CML fault 314 { 315 // First STATUS_WORD wit no bits set, then with CML fault. 316 PMBusExpectations expectations; 317 setPMBusExpectations(mockPMBus, expectations); 318 psu2.analyze(); 319 // STATUS_WORD with CML fault bit on. 320 expectations.statusWordValue = (status_word::CML_FAULT); 321 // Turn on STATUS_CML fault bit(s) 322 expectations.statusCMLValue = 0xFF; 323 setPMBusExpectations(mockPMBus, expectations); 324 psu2.analyze(); 325 EXPECT_EQ(psu2.isPresent(), true); 326 EXPECT_EQ(psu2.isFaulted(), true); 327 EXPECT_EQ(psu2.hasInputFault(), false); 328 EXPECT_EQ(psu2.hasMFRFault(), false); 329 EXPECT_EQ(psu2.hasVINUVFault(), false); 330 EXPECT_EQ(psu2.hasCommFault(), true); 331 EXPECT_EQ(psu2.hasVoutOVFault(), false); 332 EXPECT_EQ(psu2.hasTempFault(), false); 333 } 334 335 // VOUT_OV_FAULT fault 336 { 337 // First STATUS_WORD with no bits set, then with VOUT/VOUT_OV fault. 338 PMBusExpectations expectations; 339 setPMBusExpectations(mockPMBus, expectations); 340 psu2.analyze(); 341 // STATUS_WORD with VOUT/VOUT_OV fault. 342 expectations.statusWordValue = 343 ((status_word::VOUT_FAULT) | (status_word::VOUT_OV_FAULT)); 344 // Turn on STATUS_VOUT fault bit(s) 345 expectations.statusVOUTValue = 0xA0; 346 // STATUS_TEMPERATURE don't care (default) 347 setPMBusExpectations(mockPMBus, expectations); 348 psu2.analyze(); 349 EXPECT_EQ(psu2.isPresent(), true); 350 EXPECT_EQ(psu2.isFaulted(), true); 351 EXPECT_EQ(psu2.hasInputFault(), false); 352 EXPECT_EQ(psu2.hasMFRFault(), false); 353 EXPECT_EQ(psu2.hasVINUVFault(), false); 354 EXPECT_EQ(psu2.hasCommFault(), false); 355 EXPECT_EQ(psu2.hasVoutOVFault(), true); 356 EXPECT_EQ(psu2.hasTempFault(), false); 357 } 358 359 // Ignore fan fault 360 { 361 // First STATUS_WORD with no bits set, then with fan fault. 362 PMBusExpectations expectations; 363 setPMBusExpectations(mockPMBus, expectations); 364 psu2.analyze(); 365 expectations.statusWordValue = (status_word::FAN_FAULT); 366 setPMBusExpectations(mockPMBus, expectations); 367 psu2.analyze(); 368 EXPECT_EQ(psu2.isPresent(), true); 369 EXPECT_EQ(psu2.isFaulted(), false); 370 EXPECT_EQ(psu2.hasInputFault(), false); 371 EXPECT_EQ(psu2.hasMFRFault(), false); 372 EXPECT_EQ(psu2.hasVINUVFault(), false); 373 EXPECT_EQ(psu2.hasCommFault(), false); 374 EXPECT_EQ(psu2.hasVoutOVFault(), false); 375 EXPECT_EQ(psu2.hasTempFault(), false); 376 } 377 // TODO: ReadFailure 378 } 379 380 TEST_F(PowerSupplyTests, OnOffConfig) 381 { 382 auto bus = sdbusplus::bus::new_default(); 383 uint8_t data = 0x15; 384 385 // Test where PSU is NOT present 386 try 387 { 388 // Assume GPIO presence, not inventory presence? 389 PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName}; 390 391 MockedGPIOInterface* mockPresenceGPIO = 392 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 393 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0)); 394 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 395 // Constructor should set initial presence, default read returns 0. 396 // If it is not present, I should not be trying to write to it. 397 EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0); 398 psu.onOffConfig(data); 399 } 400 catch (...) 401 {} 402 403 // Test where PSU is present 404 try 405 { 406 // Assume GPIO presence, not inventory presence? 407 PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName}; 408 MockedGPIOInterface* mockPresenceGPIO = 409 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 410 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 411 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 412 // TODO: expect setPresence call? 413 // updatePresence() private function reads gpio, called by analyze(). 414 psu.analyze(); 415 // TODO: ???should I check the filename? 416 EXPECT_CALL(mockPMBus, 417 writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug)) 418 .Times(1); 419 psu.onOffConfig(data); 420 } 421 catch (...) 422 {} 423 } 424 425 TEST_F(PowerSupplyTests, ClearFaults) 426 { 427 auto bus = sdbusplus::bus::new_default(); 428 PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName}; 429 MockedGPIOInterface* mockPresenceGPIO = 430 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 431 // GPIO read return 1 to indicate present. 432 ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1)); 433 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 434 // Presence change from missing to present will trigger in1_input read in 435 // an attempt to get CLEAR_FAULTS called. 436 EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000)); 437 // STATUS_WORD 0x0000 is powered on, no faults. 438 PMBusExpectations expectations; 439 setPMBusExpectations(mockPMBus, expectations); 440 psu.analyze(); 441 EXPECT_EQ(psu.isPresent(), true); 442 EXPECT_EQ(psu.isFaulted(), false); 443 EXPECT_EQ(psu.hasInputFault(), false); 444 EXPECT_EQ(psu.hasMFRFault(), false); 445 EXPECT_EQ(psu.hasVINUVFault(), false); 446 EXPECT_EQ(psu.hasCommFault(), false); 447 EXPECT_EQ(psu.hasVoutOVFault(), false); 448 EXPECT_EQ(psu.hasTempFault(), false); 449 450 // STATUS_WORD with fault bits galore! 451 expectations.statusWordValue = 0xFFFF; 452 // STATUS_INPUT with fault bits on. 453 expectations.statusInputValue = 0xFF; 454 // STATUS_MFR_SPEFIC with bits on. 455 expectations.statusMFRValue = 0xFF; 456 // STATUS_CML with bits on. 457 expectations.statusCMLValue = 0xFF; 458 // STATUS_VOUT with bits on. 459 expectations.statusVOUTValue = 0xFF; 460 // STATUS_TEMPERATURE with bits on. 461 expectations.statusTempValue = 0xFF; 462 setPMBusExpectations(mockPMBus, expectations); 463 psu.analyze(); 464 EXPECT_EQ(psu.isPresent(), true); 465 EXPECT_EQ(psu.isFaulted(), true); 466 EXPECT_EQ(psu.hasInputFault(), true); 467 EXPECT_EQ(psu.hasMFRFault(), true); 468 EXPECT_EQ(psu.hasVINUVFault(), true); 469 EXPECT_EQ(psu.hasCommFault(), true); 470 EXPECT_EQ(psu.hasVoutOVFault(), true); 471 EXPECT_EQ(psu.hasTempFault(), true); 472 EXPECT_CALL(mockPMBus, read("in1_input", _)) 473 .Times(1) 474 .WillOnce(Return(209000)); 475 psu.clearFaults(); 476 EXPECT_EQ(psu.isPresent(), true); 477 EXPECT_EQ(psu.isFaulted(), false); 478 EXPECT_EQ(psu.hasInputFault(), false); 479 EXPECT_EQ(psu.hasMFRFault(), false); 480 EXPECT_EQ(psu.hasVINUVFault(), false); 481 EXPECT_EQ(psu.hasCommFault(), false); 482 EXPECT_EQ(psu.hasVoutOVFault(), false); 483 EXPECT_EQ(psu.hasTempFault(), false); 484 485 // TODO: Faults clear on missing/present? 486 } 487 488 TEST_F(PowerSupplyTests, UpdateInventory) 489 { 490 auto bus = sdbusplus::bus::new_default(); 491 492 try 493 { 494 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 495 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 496 // If it is not present, I should not be trying to read a string 497 EXPECT_CALL(mockPMBus, readString(_, _)).Times(0); 498 psu.updateInventory(); 499 } 500 catch (...) 501 { 502 ADD_FAILURE() << "Should not have caught exception."; 503 } 504 505 try 506 { 507 PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName}; 508 MockedGPIOInterface* mockPresenceGPIO = 509 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 510 // GPIO read return 1 to indicate present. 511 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 512 psu.analyze(); 513 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 514 EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return("")); 515 psu.updateInventory(); 516 517 #if IBM_VPD 518 EXPECT_CALL(mockPMBus, readString(_, _)) 519 .WillOnce(Return("CCIN")) 520 .WillOnce(Return("PN3456")) 521 .WillOnce(Return("FN3456")) 522 .WillOnce(Return("HEADER")) 523 .WillOnce(Return("SN3456")) 524 .WillOnce(Return("FW3456")); 525 #endif 526 psu.updateInventory(); 527 // TODO: D-Bus mocking to verify values stored on D-Bus (???) 528 } 529 catch (...) 530 { 531 ADD_FAILURE() << "Should not have caught exception."; 532 } 533 } 534 535 TEST_F(PowerSupplyTests, IsPresent) 536 { 537 auto bus = sdbusplus::bus::new_default(); 538 539 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 540 MockedGPIOInterface* mockPresenceGPIO = 541 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 542 EXPECT_EQ(psu.isPresent(), false); 543 544 // Change GPIO read to return 1 to indicate present. 545 EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1)); 546 psu.analyze(); 547 EXPECT_EQ(psu.isPresent(), true); 548 } 549 550 TEST_F(PowerSupplyTests, IsFaulted) 551 { 552 auto bus = sdbusplus::bus::new_default(); 553 554 PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName}; 555 MockedGPIOInterface* mockPresenceGPIO = 556 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 557 // Always return 1 to indicate present. 558 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 559 psu.analyze(); 560 EXPECT_EQ(psu.isFaulted(), false); 561 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 562 PMBusExpectations expectations; 563 // STATUS_WORD with fault bits on. 564 expectations.statusWordValue = 0xFFFF; 565 // STATUS_INPUT with fault bits on. 566 expectations.statusInputValue = 0xFF; 567 // STATUS_MFR_SPECIFIC with faults bits on. 568 expectations.statusMFRValue = 0xFF; 569 // STATUS_CML with faults bits on. 570 expectations.statusCMLValue = 0xFF; 571 // STATUS_VOUT with fault bits on. 572 expectations.statusVOUTValue = 0xFF; 573 // STATUS_TEMPERATURE with fault bits on. 574 expectations.statusTempValue = 0xFF; 575 setPMBusExpectations(mockPMBus, expectations); 576 psu.analyze(); 577 EXPECT_EQ(psu.isFaulted(), true); 578 } 579 580 TEST_F(PowerSupplyTests, HasInputFault) 581 { 582 auto bus = sdbusplus::bus::new_default(); 583 584 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 585 MockedGPIOInterface* mockPresenceGPIO = 586 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 587 // Always return 1 to indicate present. 588 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 589 psu.analyze(); 590 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 591 EXPECT_EQ(psu.hasInputFault(), false); 592 // STATUS_WORD 0x0000 is powered on, no faults. 593 PMBusExpectations expectations; 594 setPMBusExpectations(mockPMBus, expectations); 595 psu.analyze(); 596 EXPECT_EQ(psu.hasInputFault(), false); 597 // STATUS_WORD with input fault/warn on. 598 expectations.statusWordValue = (status_word::INPUT_FAULT_WARN); 599 // STATUS_INPUT with an input fault bit on. 600 expectations.statusInputValue = 0x80; 601 setPMBusExpectations(mockPMBus, expectations); 602 psu.analyze(); 603 EXPECT_EQ(psu.hasInputFault(), true); 604 // STATUS_WORD with no bits on. 605 expectations.statusWordValue = 0; 606 setPMBusExpectations(mockPMBus, expectations); 607 psu.analyze(); 608 EXPECT_EQ(psu.hasInputFault(), false); 609 } 610 611 TEST_F(PowerSupplyTests, HasMFRFault) 612 { 613 auto bus = sdbusplus::bus::new_default(); 614 615 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 616 MockedGPIOInterface* mockPresenceGPIO = 617 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 618 // Always return 1 to indicate present. 619 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 620 psu.analyze(); 621 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 622 EXPECT_EQ(psu.hasMFRFault(), false); 623 // First return STATUS_WORD with no bits on. 624 // STATUS_WORD 0x0000 is powered on, no faults. 625 PMBusExpectations expectations; 626 setPMBusExpectations(mockPMBus, expectations); 627 psu.analyze(); 628 EXPECT_EQ(psu.hasMFRFault(), false); 629 // Next return STATUS_WORD with MFR fault bit on. 630 expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT); 631 // STATUS_MFR_SPEFIC with bit(s) on. 632 expectations.statusMFRValue = 0xFF; 633 setPMBusExpectations(mockPMBus, expectations); 634 psu.analyze(); 635 EXPECT_EQ(psu.hasMFRFault(), true); 636 // Back to no bits on in STATUS_WORD 637 expectations.statusWordValue = 0; 638 setPMBusExpectations(mockPMBus, expectations); 639 psu.analyze(); 640 EXPECT_EQ(psu.hasMFRFault(), false); 641 } 642 643 TEST_F(PowerSupplyTests, HasVINUVFault) 644 { 645 auto bus = sdbusplus::bus::new_default(); 646 647 PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName}; 648 MockedGPIOInterface* mockPresenceGPIO = 649 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 650 // Always return 1 to indicate present. 651 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 652 psu.analyze(); 653 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 654 EXPECT_EQ(psu.hasVINUVFault(), false); 655 // STATUS_WORD 0x0000 is powered on, no faults. 656 PMBusExpectations expectations; 657 setPMBusExpectations(mockPMBus, expectations); 658 psu.analyze(); 659 EXPECT_EQ(psu.hasVINUVFault(), false); 660 // Turn fault on. 661 expectations.statusWordValue = (status_word::VIN_UV_FAULT); 662 // Curious disagreement between PMBus Spec. Part II Figure 16 and 33. Go by 663 // Figure 16, and assume bits on in STATUS_INPUT. 664 expectations.statusInputValue = 0x18; 665 setPMBusExpectations(mockPMBus, expectations); 666 psu.analyze(); 667 EXPECT_EQ(psu.hasVINUVFault(), true); 668 // Back to no fault bits on in STATUS_WORD 669 expectations.statusWordValue = 0; 670 setPMBusExpectations(mockPMBus, expectations); 671 psu.analyze(); 672 EXPECT_EQ(psu.hasVINUVFault(), false); 673 } 674 675 TEST_F(PowerSupplyTests, HasVoutOVFault) 676 { 677 auto bus = sdbusplus::bus::new_default(); 678 679 PowerSupply psu{bus, PSUInventoryPath, 3, 0x69, PSUGPIOLineName}; 680 MockedGPIOInterface* mockPresenceGPIO = 681 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 682 // Always return 1 to indicate present. 683 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 684 psu.analyze(); 685 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 686 EXPECT_EQ(psu.hasVoutOVFault(), false); 687 // STATUS_WORD 0x0000 is powered on, no faults. 688 PMBusExpectations expectations; 689 setPMBusExpectations(mockPMBus, expectations); 690 psu.analyze(); 691 EXPECT_EQ(psu.hasVoutOVFault(), false); 692 // Turn fault on. 693 expectations.statusWordValue = (status_word::VOUT_OV_FAULT); 694 // STATUS_VOUT fault bit(s) 695 expectations.statusVOUTValue = 0x80; 696 // STATUS_TEMPERATURE default. 697 setPMBusExpectations(mockPMBus, expectations); 698 psu.analyze(); 699 EXPECT_EQ(psu.hasVoutOVFault(), true); 700 // Back to no fault bits on in STATUS_WORD 701 expectations.statusWordValue = 0; 702 setPMBusExpectations(mockPMBus, expectations); 703 psu.analyze(); 704 EXPECT_EQ(psu.hasVoutOVFault(), false); 705 } 706 707 TEST_F(PowerSupplyTests, HasTempFault) 708 { 709 auto bus = sdbusplus::bus::new_default(); 710 711 PowerSupply psu{bus, PSUInventoryPath, 3, 0x6a, PSUGPIOLineName}; 712 MockedGPIOInterface* mockPresenceGPIO = 713 static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO()); 714 // Always return 1 to indicate present. 715 EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1)); 716 psu.analyze(); 717 MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus()); 718 EXPECT_EQ(psu.hasTempFault(), false); 719 // STATUS_WORD 0x0000 is powered on, no faults. 720 PMBusExpectations expectations; 721 setPMBusExpectations(mockPMBus, expectations); 722 psu.analyze(); 723 EXPECT_EQ(psu.hasTempFault(), false); 724 // Turn fault on. 725 expectations.statusWordValue = (status_word::TEMPERATURE_FAULT_WARN); 726 // STATUS_TEMPERATURE fault bit on (OT Fault) 727 expectations.statusTempValue = 0x80; 728 setPMBusExpectations(mockPMBus, expectations); 729 psu.analyze(); 730 EXPECT_EQ(psu.hasTempFault(), true); 731 // Back to no fault bits on in STATUS_WORD 732 expectations.statusWordValue = 0; 733 setPMBusExpectations(mockPMBus, expectations); 734 psu.analyze(); 735 EXPECT_EQ(psu.hasTempFault(), false); 736 } 737