psu-ng: Add code to detect temperature fault

If the low byte of STATUS_WORD has bit 2 on/1, there is a temperature
fault or warning.

Tested:
    Verify no error logged for temperature fault during normal operation
    on real hardware.
    Verify temperature fault error logged when simulated
    over-temperature fault.

Change-Id: Ib5b0fe849699e72e517ea4d59a69bbb6de1e5283
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index c131621..7fb7d03 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -29,6 +29,7 @@
     uint8_t statusMFRValue{0x00};
     uint8_t statusCMLValue{0x00};
     uint8_t statusVOUTValue{0x00};
+    uint8_t statusTempValue{0x00};
 };
 
 // Helper function to setup expectations for various STATUS_* commands
@@ -42,7 +43,8 @@
     if (expectations.statusWordValue != 0)
     {
         // If fault bits are on in STATUS_WORD, there will also be a read of
-        // STATUS_INPUT, STATUS_MFR, and STATUS_CML.
+        // STATUS_INPUT, STATUS_MFR, STATUS_CML, STATUS_VOUT (page 0), and
+        // STATUS_TEMPERATURE.
         EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
             .Times(1)
             .WillOnce(Return(expectations.statusInputValue));
@@ -59,6 +61,9 @@
         EXPECT_CALL(mockPMBus, read("status0_vout", _))
             .Times(1)
             .WillOnce(Return(expectations.statusVOUTValue));
+        EXPECT_CALL(mockPMBus, read(STATUS_TEMPERATURE, _))
+            .Times(1)
+            .WillOnce(Return(expectations.statusTempValue));
     }
 }
 
@@ -141,6 +146,7 @@
         EXPECT_EQ(psu->hasMFRFault(), false);
         EXPECT_EQ(psu->hasVINUVFault(), false);
         EXPECT_EQ(psu->hasVoutOVFault(), false);
+        EXPECT_EQ(psu->hasTempFault(), false);
     }
     catch (...)
     {
@@ -186,6 +192,7 @@
         EXPECT_EQ(psu.hasVINUVFault(), false);
         EXPECT_EQ(psu.hasCommFault(), false);
         EXPECT_EQ(psu.hasVoutOVFault(), false);
+        EXPECT_EQ(psu.hasTempFault(), false);
     }
 
     PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
@@ -218,8 +225,10 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
 
         // Update expectations for STATUS_WORD input fault/warn
+        // STATUS_INPUT fault bits ... on.
         expectations.statusWordValue = (status_word::INPUT_FAULT_WARN);
         expectations.statusInputValue = 0x38;
         setPMBusExpectations(mockPMBus, expectations);
@@ -231,6 +240,7 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
 
     // STATUS_WORD INPUT/UV fault.
@@ -253,6 +263,7 @@
         EXPECT_EQ(psu2.hasVINUVFault(), true);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
 
     // STATUS_WORD MFR fault.
@@ -274,9 +285,10 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
 
-    // Ignore Temperature fault.
+    // Temperature fault.
     {
         // First STATUS_WORD with no bits set, then with temperature fault.
         PMBusExpectations expectations;
@@ -284,17 +296,18 @@
         psu2.analyze();
         // STATUS_WORD with temperature fault bit on.
         expectations.statusWordValue = (status_word::TEMPERATURE_FAULT_WARN);
-        // STATUS_INPUT, STATUS_MFR, STATUS_CML, and STATUS_VOUT fault bits ...
-        // don't care (defaults).
+        // STATUS_TEMPERATURE with fault bit(s) on.
+        expectations.statusTempValue = 0x10;
         setPMBusExpectations(mockPMBus, expectations);
         psu2.analyze();
         EXPECT_EQ(psu2.isPresent(), true);
-        EXPECT_EQ(psu2.isFaulted(), false);
+        EXPECT_EQ(psu2.isFaulted(), true);
         EXPECT_EQ(psu2.hasInputFault(), false);
         EXPECT_EQ(psu2.hasMFRFault(), false);
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), true);
     }
 
     // CML fault
@@ -316,6 +329,7 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), true);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
 
     // VOUT_OV_FAULT fault
@@ -329,6 +343,7 @@
             ((status_word::VOUT_FAULT) | (status_word::VOUT_OV_FAULT));
         // Turn on STATUS_VOUT fault bit(s)
         expectations.statusVOUTValue = 0xA0;
+        // STATUS_TEMPERATURE don't care (default)
         setPMBusExpectations(mockPMBus, expectations);
         psu2.analyze();
         EXPECT_EQ(psu2.isPresent(), true);
@@ -338,6 +353,7 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), true);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
 
     // Ignore fan fault
@@ -356,6 +372,7 @@
         EXPECT_EQ(psu2.hasVINUVFault(), false);
         EXPECT_EQ(psu2.hasCommFault(), false);
         EXPECT_EQ(psu2.hasVoutOVFault(), false);
+        EXPECT_EQ(psu2.hasTempFault(), false);
     }
     // TODO: ReadFailure
 }
@@ -428,6 +445,7 @@
     EXPECT_EQ(psu.hasVINUVFault(), false);
     EXPECT_EQ(psu.hasCommFault(), false);
     EXPECT_EQ(psu.hasVoutOVFault(), false);
+    EXPECT_EQ(psu.hasTempFault(), false);
 
     // STATUS_WORD with fault bits galore!
     expectations.statusWordValue = 0xFFFF;
@@ -439,6 +457,8 @@
     expectations.statusCMLValue = 0xFF;
     // STATUS_VOUT with bits on.
     expectations.statusVOUTValue = 0xFF;
+    // STATUS_TEMPERATURE with bits on.
+    expectations.statusTempValue = 0xFF;
     setPMBusExpectations(mockPMBus, expectations);
     psu.analyze();
     EXPECT_EQ(psu.isPresent(), true);
@@ -448,6 +468,7 @@
     EXPECT_EQ(psu.hasVINUVFault(), true);
     EXPECT_EQ(psu.hasCommFault(), true);
     EXPECT_EQ(psu.hasVoutOVFault(), true);
+    EXPECT_EQ(psu.hasTempFault(), true);
     EXPECT_CALL(mockPMBus, read("in1_input", _))
         .Times(1)
         .WillOnce(Return(209000));
@@ -459,6 +480,7 @@
     EXPECT_EQ(psu.hasVINUVFault(), false);
     EXPECT_EQ(psu.hasCommFault(), false);
     EXPECT_EQ(psu.hasVoutOVFault(), false);
+    EXPECT_EQ(psu.hasTempFault(), false);
 
     // TODO: Faults clear on missing/present?
 }
@@ -548,6 +570,8 @@
     expectations.statusCMLValue = 0xFF;
     // STATUS_VOUT with fault bits on.
     expectations.statusVOUTValue = 0xFF;
+    // STATUS_TEMPERATURE with fault bits on.
+    expectations.statusTempValue = 0xFF;
     setPMBusExpectations(mockPMBus, expectations);
     psu.analyze();
     EXPECT_EQ(psu.isFaulted(), true);
@@ -669,6 +693,7 @@
     expectations.statusWordValue = (status_word::VOUT_OV_FAULT);
     // STATUS_VOUT fault bit(s)
     expectations.statusVOUTValue = 0x80;
+    // STATUS_TEMPERATURE default.
     setPMBusExpectations(mockPMBus, expectations);
     psu.analyze();
     EXPECT_EQ(psu.hasVoutOVFault(), true);
@@ -678,3 +703,34 @@
     psu.analyze();
     EXPECT_EQ(psu.hasVoutOVFault(), false);
 }
+
+TEST_F(PowerSupplyTests, HasTempFault)
+{
+    auto bus = sdbusplus::bus::new_default();
+
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6a, PSUGPIOLineName};
+    MockedGPIOInterface* mockPresenceGPIO =
+        static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    psu.analyze();
+    MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+    EXPECT_EQ(psu.hasTempFault(), false);
+    // STATUS_WORD 0x0000 is powered on, no faults.
+    PMBusExpectations expectations;
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasTempFault(), false);
+    // Turn fault on.
+    expectations.statusWordValue = (status_word::TEMPERATURE_FAULT_WARN);
+    // STATUS_TEMPERATURE fault bit on (OT Fault)
+    expectations.statusTempValue = 0x80;
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasTempFault(), true);
+    // Back to no fault bits on in STATUS_WORD
+    expectations.statusWordValue = 0;
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasTempFault(), false);
+}