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/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 64e0f26..ca9c084 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -210,6 +210,21 @@
{
statusInput = pmbusIntf->read(STATUS_INPUT, Type::Debug);
statusMFR = pmbusIntf->read(STATUS_MFR, Type::Debug);
+ statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
+ if (statusWord & status_word::CML_FAULT)
+ {
+ if (!cmlFault)
+ {
+ log<level::INFO>(
+ fmt::format("CML fault: STATUS_WORD = {:#04x}, "
+ "STATUS_CML = {:#02x}",
+ statusWord, statusCML)
+ .c_str());
+ }
+ faultFound = true;
+ cmlFault = true;
+ }
+
if (statusWord & status_word::INPUT_FAULT_WARN)
{
if (!inputFault)
@@ -260,6 +275,7 @@
else
{
faultFound = false;
+ cmlFault = false;
inputFault = false;
mfrFault = false;
vinUVFault = false;
@@ -313,6 +329,7 @@
mfrFault = false;
statusMFR = 0;
vinUVFault = false;
+ cmlFault = false;
readFail = 0;
try
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 86ce57c..d322043 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -161,6 +161,14 @@
}
/**
+ * @brief Returns the last read value from STATUS_CML.
+ */
+ uint64_t getStatusCML() const
+ {
+ return statusCML;
+ }
+
+ /**
* @brief Returns true if a fault was found.
*/
bool isFaulted() const
@@ -252,7 +260,7 @@
*/
bool hasCommFault() const
{
- return readFail >= LOG_LIMIT;
+ return ((readFail >= LOG_LIMIT) || (cmlFault));
}
/**
@@ -277,12 +285,18 @@
/** @brief Will be updated to the latest/lastvalue read from STATUS_MFR.*/
uint64_t statusMFR = 0;
+ /** @brief Will be updated to the latest/last value read from STATUS_CML.*/
+ uint64_t statusCML = 0;
+
/** @brief True if a fault has already been found and not cleared */
bool faultFound = false;
/** @brief True if an error for a fault has already been logged. */
bool faultLogged = false;
+ /** @brief True if bit 2 of STATUS_WORD low byte is on. */
+ bool cmlFault = false;
+
/** @brief True if bit 5 of STATUS_WORD high byte is on. */
bool inputFault = false;
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 4cfa801..c63207f 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -411,6 +411,8 @@
if (psu->hasCommFault())
{
+ additionalData["STATUS_CML"] =
+ fmt::format("{:#02x}", psu->getStatusCML());
/* Attempts to communicate with the power supply have
* reached there limit. Create an error. */
additionalData["CALLOUT_DEVICE_PATH"] =
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);
diff --git a/pmbus.hpp b/pmbus.hpp
index 4bdc23f..43d7dd8 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -60,6 +60,9 @@
// overtemperature warning, undertemperature warning, undertemperature fault.
constexpr auto STATUS_TEMPERATURE = "status0_temp";
+// Reports on the communication, memory, logic fault(s).
+constexpr auto STATUS_CML = "status0_cml";
+
namespace status_word
{
constexpr auto VOUT_FAULT = 0x8000;
@@ -101,6 +104,9 @@
// STATUS_WORD. Bit 2 of the low byte (STATUS_BYTE).
constexpr auto TEMPERATURE_FAULT_WARN = 0x0004;
+// The bit mask representing the CML (Communication, Memory, and/or Logic) fault
+// bit of the STATUS_WORD. Bit 1 of the low byte (STATUS_BYTE).
+constexpr auto CML_FAULT = 0x0002;
} // namespace status_word
namespace status_vout