psu-ng: Detect CML fault

If the STATUS_WORD has the CML (Communication, Memory, Logic) fault bit
on, bit 1 in lower byte of STATUS_WORD, then read STATUS_CML, and treat
the fault as another variety of a communication fault/error.

Change-Id: Iba368683734777874ba54ec845cbc94b00010b68
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 7411c34..ef87a15 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -141,6 +141,7 @@
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
+    EXPECT_EQ(psu.hasCommFault(), false);
 
     PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
     // In order to get the various faults tested, the power supply needs to
@@ -151,11 +152,11 @@
 
     EXPECT_EQ(psu2.isPresent(), false);
 
-    // STATUS_WORD 0x0000 is powered on, no faults (0x0000).
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
     // Presence change from missing to present will trigger in1_input read in
     // an attempt to get CLEAR_FAULTS called.
     EXPECT_CALL(mockPMBus, read(READ_VIN, _)).Times(1).WillOnce(Return(206000));
+    // STATUS_WORD 0x0000 is powered on, no faults.
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(0x0000));
@@ -165,26 +166,30 @@
     EXPECT_EQ(psu2.hasInputFault(), false);
     EXPECT_EQ(psu2.hasMFRFault(), false);
     EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), false);
 
     // STATUS_WORD input fault/warn
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
     // Due to the fault bit on in STATUS_WORD, there will also be a read of
-    // STATUS_INPUT and STATUS_MFR, so there should be 3 reads total expected.
+    // STATUS_INPUT, STATUS_MFR, and STATUS_CML, so there should be 4 reads
+    // total expected.
     // STATUS_INPUT fault bits ... on.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x38));
     // STATUS_MFR don't care
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
-
+    // STATUS_CML don't care
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
     EXPECT_EQ(psu2.isFaulted(), true);
     EXPECT_EQ(psu2.hasInputFault(), true);
     EXPECT_EQ(psu2.hasMFRFault(), false);
     EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), false);
 
     // STATUS_WORD INPUT/UV fault.
     // First need it to return good status, then the fault
@@ -199,7 +204,8 @@
         .WillOnce(Return(0x38));
     // STATUS_MFR don't care
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
-
+    // STATUS_CML don't care
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
     psu2.analyze();
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
@@ -207,6 +213,7 @@
     EXPECT_EQ(psu2.hasInputFault(), true);
     EXPECT_EQ(psu2.hasMFRFault(), false);
     EXPECT_EQ(psu2.hasVINUVFault(), true);
+    EXPECT_EQ(psu2.hasCommFault(), false);
 
     // STATUS_WORD MFR fault.
     // First need it to return good status, then the fault
@@ -220,7 +227,8 @@
         .WillOnce(Return(0x00));
     // STATUS_MFR bits on.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
-
+    // STATUS_CML don't care
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
     psu2.analyze();
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
@@ -228,6 +236,7 @@
     EXPECT_EQ(psu2.hasInputFault(), false);
     EXPECT_EQ(psu2.hasMFRFault(), true);
     EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), false);
 
     // Ignore Temperature fault.
     // First STATUS_WORD with no bits set, then with temperature fault.
@@ -235,14 +244,16 @@
         .Times(2)
         .WillOnce(Return(0x0000))
         .WillOnce(Return(status_word::TEMPERATURE_FAULT_WARN));
-    // If STATUS_WORD bits set, should read STATUS_MFR_SPECIFIC and STATUS_INPUT
+    // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
+    // STATUS_CML will also be read.
     // STATUS_INPUT fault bits ... don't care.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x00));
     // STATUS_MFR don't care
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
-
+    // STATUS_CML don't care
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
     psu2.analyze();
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
@@ -250,6 +261,31 @@
     EXPECT_EQ(psu2.hasInputFault(), false);
     EXPECT_EQ(psu2.hasMFRFault(), false);
     EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), false);
+
+    // CML fault
+    // First STATUS_WORD wit no bits set, then with CML fault.
+    // STATUS_WORD with CML fault bit on.
+    EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
+        .Times(2)
+        .WillOnce(Return(0x0000))
+        .WillOnce(Return(status_word::CML_FAULT));
+    psu2.analyze();
+    // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
+    // STATUS_CML will also be read.
+    EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
+        .Times(1)
+        .WillOnce(Return(0x00));
+    EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
+    // Turn on STATUS_CML fault bit(s)
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
+    psu2.analyze();
+    EXPECT_EQ(psu2.isPresent(), true);
+    EXPECT_EQ(psu2.isFaulted(), true);
+    EXPECT_EQ(psu2.hasInputFault(), false);
+    EXPECT_EQ(psu2.hasMFRFault(), false);
+    EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), true);
 
     // Ignore fan fault
     // First STATUS_WORD with no bits set, then with fan fault.
@@ -257,13 +293,14 @@
         .Times(2)
         .WillOnce(Return(0x0000))
         .WillOnce(Return(status_word::FAN_FAULT));
-    // STATUS_WORD bits set causes read STATUS_MFR_SPECIFIC and STATUS_INPUT.
+    // If the STATUS_WORD has bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT, and
+    // STATUS_CML will also be read.
     // Don't care if bits set or not.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x00));
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0));
-
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0));
     psu2.analyze();
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
@@ -271,6 +308,7 @@
     EXPECT_EQ(psu2.hasInputFault(), false);
     EXPECT_EQ(psu2.hasMFRFault(), false);
     EXPECT_EQ(psu2.hasVINUVFault(), false);
+    EXPECT_EQ(psu2.hasCommFault(), false);
 
     // TODO: ReadFailure
 }
@@ -336,24 +374,28 @@
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
+    EXPECT_EQ(psu.hasCommFault(), false);
     // STATUS_WORD with fault bits galore!
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(0xFFFF));
-    // If STATUS_WORD has any fault bits on, STATUS_MFR_SPECIFIC and
-    // STATUS_INPUT will be read.
+    // If STATUS_WORD has any fault bits on, STATUS_MFR_SPECIFIC, STATUS_INPUT
+    // and STATUS_CML will be read.
     // STATUS_INPUT with fault bits on.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0xFF));
     // STATUS_MFR_SPEFIC with bits on.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
+    // STATUS_CML with bits on.
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
     psu.analyze();
     EXPECT_EQ(psu.isPresent(), true);
     EXPECT_EQ(psu.isFaulted(), true);
     EXPECT_EQ(psu.hasInputFault(), true);
     EXPECT_EQ(psu.hasMFRFault(), true);
     EXPECT_EQ(psu.hasVINUVFault(), true);
+    EXPECT_EQ(psu.hasCommFault(), true);
     EXPECT_CALL(mockPMBus, read("in1_input", _))
         .Times(1)
         .WillOnce(Return(209000));
@@ -363,6 +405,7 @@
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
+    EXPECT_EQ(psu.hasCommFault(), false);
 
     // TODO: Faults clear on missing/present?
 }
@@ -445,13 +488,16 @@
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(0xFFFF));
-    // If STATUS_WORD has bit(s) on, STATUS_MFR_SPECIFIC and STATUS_INPUT read.
+    // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
+    // STATUS_INPUT, and STATUS_CML.
     // STATUS_INPUT with fault bits on.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0xFF));
     // STATUS_MFR_SPECIFIC with faults bits on.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
+    // STATUS_CML with faults bits on.
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0xFF));
     psu.analyze();
     EXPECT_EQ(psu.isFaulted(), true);
 }
@@ -475,13 +521,16 @@
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(status_word::INPUT_FAULT_WARN));
-    // If STATUS_WORD has bit(s) on, STATUS_MFR_SPECIFIC and STATUS_INPUT read.
+    // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
+    // STATUS_INPUT, and STATUS_CML.
     // STATUS_INPUT with an input fault bit on.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x80));
     // STATUS_MFR don't care.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
+    // STATUS_CML don't care.
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
     psu.analyze();
     EXPECT_EQ(psu.hasInputFault(), true);
     // STATUS_WORD with no bits on.
@@ -514,13 +563,16 @@
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(status_word::MFR_SPECIFIC_FAULT));
-    // If STATUS_WORD has bits on, STATUS_MFR_SPECIFIC and STATUS_INPUT read.
+    // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
+    // STATUS_INPUT, and STATUS_CML.
     // STATUS_INPUT don't care
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x00));
     // STATUS_MFR_SPEFIC with bit(s) on.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0xFF));
+    // STATUS_CML don't care.
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
     psu.analyze();
     EXPECT_EQ(psu.hasMFRFault(), true);
     // Back to no bits on in STATUS_WORD
@@ -552,15 +604,17 @@
     EXPECT_CALL(mockPMBus, read(STATUS_WORD, _))
         .Times(1)
         .WillOnce(Return(status_word::VIN_UV_FAULT));
-    // Fault bits on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC and
-    // STATUS_INPUT.
-    // Curious disagreement between PMBus Spec. Part II Figure 16 and 33.
-    // Go by Figure 16, and assume bits on in STATUS_INPUT.
+    // Fault bit(s) on in STATUS_WORD causes read of STATUS_MFR_SPECIFIC,
+    // STATUS_INPUT, and STATUS_CML.
+    // Curious disagreement between PMBus Spec. Part II Figure 16 and 33. Go by
+    // Figure 16, and assume bits on in STATUS_INPUT.
     EXPECT_CALL(mockPMBus, read(STATUS_INPUT, _))
         .Times(1)
         .WillOnce(Return(0x18));
     // STATUS_MFR don't care.
     EXPECT_CALL(mockPMBus, read(STATUS_MFR, _)).Times(1).WillOnce(Return(0x00));
+    // STATUS_CML don't care.
+    EXPECT_CALL(mockPMBus, read(STATUS_CML, _)).Times(1).WillOnce(Return(0x00));
 
     psu.analyze();
     EXPECT_EQ(psu.hasVINUVFault(), true);