psu-ng: Add code to detect VOUT_UV_FAULT
If the output voltage hits an undervoltage condition, the VOUT
fault/warn bit in STATUS_WORD will be on, but the VOUT_OV_FAULT bit will
be off.
Check for VOUT_UV_FAULT after check for VOUT_OV_FAULT, create error,
call out the power supply indicating that fault.
Tested:
Verify no fault detected or error logged in normal conditions on
real hardware.
Simulate VOUT_UV fault:
VOUT_UV_FAULT fault: STATUS_WORD = 0x8000, STATUS_MFR_SPECIFIC = 0x0, STATUS_VOUT = 0x0
and
VOUT_UV_FAULT fault: STATUS_WORD = 0x8000, STATUS_MFR_SPECIFIC = 0x0, STATUS_VOUT = 0x30
Change-Id: I2605a197634616c4c2ad49f0275eaccaef7cc5f0
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 dcf6177..7c884ff 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -250,10 +250,11 @@
if (!voutOVFault)
{
log<level::ERR>(
- fmt::format("INPUT fault: STATUS_WORD = {:#04x}, "
- "STATUS_MFR_SPECIFIC = {:#02x}, "
- "STATUS_VOUT = {:#02x}",
- statusWord, statusMFR, statusVout)
+ fmt::format(
+ "VOUT_OV_FAULT fault: STATUS_WORD = {:#04x}, "
+ "STATUS_MFR_SPECIFIC = {:#02x}, "
+ "STATUS_VOUT = {:#02x}",
+ statusWord, statusMFR, statusVout)
.c_str());
}
@@ -275,6 +276,23 @@
ioutOCFault = true;
}
+ if ((statusWord & status_word::VOUT_FAULT) &&
+ !(statusWord & status_word::VOUT_OV_FAULT))
+ {
+ if (!voutUVFault)
+ {
+ log<level::ERR>(
+ fmt::format(
+ "VOUT_UV_FAULT fault: STATUS_WORD = {:#04x}, "
+ "STATUS_MFR_SPECIFIC = {:#02x}, "
+ "STATUS_VOUT = {:#02x}",
+ statusWord, statusMFR, statusVout)
+ .c_str());
+ }
+
+ voutUVFault = true;
+ }
+
if (statusWord & status_word::TEMPERATURE_FAULT_WARN)
{
if (!tempFault)
@@ -346,6 +364,7 @@
vinUVFault = false;
voutOVFault = false;
ioutOCFault = false;
+ voutUVFault = false;
tempFault = false;
pgoodFault = false;
}
@@ -400,6 +419,7 @@
cmlFault = false;
voutOVFault = false;
ioutOCFault = false;
+ voutUVFault = false;
tempFault = false;
pgoodFault = false;
readFail = 0;
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 8bd2a30..149d81f 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -198,7 +198,8 @@
bool isFaulted() const
{
return (hasCommFault() || vinUVFault || inputFault || voutOVFault ||
- ioutOCFault || tempFault || pgoodFault || mfrFault);
+ ioutOCFault || voutUVFault || tempFault || pgoodFault ||
+ mfrFault);
}
/**
@@ -258,6 +259,14 @@
}
/**
+ * @brief Returns true if VOUT_UV_FAULT occurred.
+ */
+ bool hasVoutUVFault() const
+ {
+ return voutUVFault;
+ }
+
+ /**
* @brief Returns true if TEMPERATURE fault occurred.
*/
bool hasTempFault() const
@@ -377,10 +386,15 @@
/** @brief True if bit 4 of STATUS_WORD low byte is on. */
bool ioutOCFault = false;
+ /** @brief True if bit 7 of STATUS_WORD high byte is on and bit 5 (VOUT_OV)
+ * of low byte is off. */
+ bool voutUVFault = false;
+
/** @brief True if bit 2 of STATUS_WORD low byte is on. */
bool tempFault = false;
- /** @brief True if bit 11 or 6 of STATUS_WORD is on. PGOOD# is inactive, or
+ /**
+ * @brief True if bit 11 or 6 of STATUS_WORD is on. PGOOD# is inactive, or
* the unit is off.
*/
bool pgoodFault = false;
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 282412c..1c9378b 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -494,6 +494,21 @@
psu->setFaultLogged();
}
+ else if (psu->hasVoutUVFault())
+ {
+ // Include STATUS_VOUT for Vout faults.
+ additionalData["STATUS_VOUT"] =
+ fmt::format("{:#02x}", psu->getStatusVout());
+
+ additionalData["CALLOUT_INVENTORY_PATH"] =
+ psu->getInventoryPath();
+
+ createError(
+ "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
+ additionalData);
+
+ psu->setFaultLogged();
+ }
else if (psu->hasTempFault())
{
// Include STATUS_TEMPERATURE for temperature faults.
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 309e6df..b340449 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -151,6 +151,7 @@
EXPECT_EQ(psu->hasVINUVFault(), false);
EXPECT_EQ(psu->hasVoutOVFault(), false);
EXPECT_EQ(psu->hasIoutOCFault(), false);
+ EXPECT_EQ(psu->hasVoutUVFault(), false);
EXPECT_EQ(psu->hasTempFault(), false);
EXPECT_EQ(psu->hasPgoodFault(), false);
}
@@ -199,6 +200,7 @@
EXPECT_EQ(psu.hasCommFault(), false);
EXPECT_EQ(psu.hasVoutOVFault(), false);
EXPECT_EQ(psu.hasIoutOCFault(), false);
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
}
@@ -234,6 +236,7 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
@@ -251,6 +254,7 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -276,6 +280,7 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -300,6 +305,7 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -324,6 +330,7 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), true);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -348,6 +355,7 @@
EXPECT_EQ(psu2.hasCommFault(), true);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -373,6 +381,7 @@
EXPECT_EQ(psu2.hasVINUVFault(), false);
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), true);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
@@ -398,6 +407,32 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), true);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
+ EXPECT_EQ(psu2.hasTempFault(), false);
+ EXPECT_EQ(psu2.hasPgoodFault(), false);
+ }
+
+ // VOUT_UV_FAULT
+ {
+ // First STATUS_WORD with no bits set, then with VOUT fault.
+ PMBusExpectations expectations;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu2.analyze();
+ // Change STATUS_WORD to indicate VOUT fault.
+ expectations.statusWordValue = (status_word::VOUT_FAULT);
+ // Turn on STATUS_VOUT fault bit(s)
+ expectations.statusVOUTValue = 0x30;
+ setPMBusExpectations(mockPMBus, expectations);
+ 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(), false);
+ EXPECT_EQ(psu2.hasVoutOVFault(), false);
+ EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), true);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
@@ -419,12 +454,13 @@
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
}
+ // PGOOD/OFF fault.
{
- // PGOOD/OFF fault.
// First STATUS_WORD with no bits set.
PMBusExpectations expectations;
setPMBusExpectations(mockPMBus, expectations);
@@ -444,6 +480,7 @@
EXPECT_EQ(psu2.hasVINUVFault(), false);
EXPECT_EQ(psu2.hasCommFault(), false);
EXPECT_EQ(psu2.hasVoutOVFault(), false);
+ EXPECT_EQ(psu2.hasVoutUVFault(), false);
EXPECT_EQ(psu2.hasIoutOCFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), true);
@@ -521,6 +558,7 @@
EXPECT_EQ(psu.hasCommFault(), false);
EXPECT_EQ(psu.hasVoutOVFault(), false);
EXPECT_EQ(psu.hasIoutOCFault(), false);
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
@@ -548,8 +586,12 @@
EXPECT_EQ(psu.hasCommFault(), true);
EXPECT_EQ(psu.hasVoutOVFault(), true);
EXPECT_EQ(psu.hasIoutOCFault(), true);
+ // Cannot have VOUT_OV_FAULT and VOUT_UV_FAULT.
+ // Rely on HasVoutUVFault() to verify this sets and clears.
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
EXPECT_EQ(psu.hasTempFault(), true);
EXPECT_EQ(psu.hasPgoodFault(), true);
+
EXPECT_CALL(mockPMBus, read("in1_input", _))
.Times(1)
.WillOnce(Return(209000));
@@ -562,6 +604,7 @@
EXPECT_EQ(psu.hasCommFault(), false);
EXPECT_EQ(psu.hasVoutOVFault(), false);
EXPECT_EQ(psu.hasIoutOCFault(), false);
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
@@ -820,6 +863,36 @@
EXPECT_EQ(psu.hasIoutOCFault(), false);
}
+TEST_F(PowerSupplyTests, HasVoutUVFault)
+{
+ 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.hasVoutUVFault(), false);
+ PMBusExpectations expectations;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
+ // Turn fault on.
+ expectations.statusWordValue = (status_word::VOUT_FAULT);
+ // STATUS_VOUT fault bit(s)
+ expectations.statusVOUTValue = 0x30;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasVoutUVFault(), true);
+ // Back to no fault bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasVoutUVFault(), false);
+}
+
TEST_F(PowerSupplyTests, HasTempFault)
{
auto bus = sdbusplus::bus::new_default();