psu-ng: Add in handling of specific MFR faults
Add in a function to determine what the various bits in statusMFR may be
indicating for a fault, based on the type of power supply (device driver
bound).
Add in PS_Kill, 12Vcs, and 12V CS faults for IBM power supply types.
Add in creating error logs for PS_Kill, 12Vcs, and 12V CS faults. The
12Vcs and 12V CS faults can essentially be treated the same as VOUT_UV
faults (same error type, same call out).
Tested:
Verified no PS_Kill, 12Vcs, or 12V CS fault on normal Rainier 2S4U
Simulated PS_Kill fault:
MFR fault: STATUS_WORD = 0x1840 STATUS_MFR_SPECIFIC = 0x10
Simulated 12Vcs fault:
PGOOD fault: STATUS_WORD = 0x1840, STATUS_MFR_SPECIFIC = 0x40
MFR fault: STATUS_WORD = 0x1840 STATUS_MFR_SPECIFIC = 0x40
Simulated 12V CS fault/warning:
MFR fault: STATUS_WORD = 0x1000 STATUS_MFR_SPECIFIC = 0x80
Change-Id: Ie89a58836ecec86dfa2e124eb6ab03e9dccce929
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 908f04f..a48441d 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -189,6 +189,28 @@
}
}
+void PowerSupply::determineMFRFault()
+{
+ if (bindPath.string().find("ibm-cffps") != std::string::npos)
+ {
+ // IBM MFR_SPECIFIC[4] is PS_Kill fault
+ if (statusMFR & 0x10)
+ {
+ psKillFault = true;
+ }
+ // IBM MFR_SPECIFIC[6] is 12Vcs fault.
+ if (statusMFR & 0x40)
+ {
+ ps12VcsFault = true;
+ }
+ // IBM MFR_SPECIFIC[7] is 12V Current-Share fault.
+ if (statusMFR & 0x80)
+ {
+ psCS12VFault = true;
+ }
+ }
+}
+
void PowerSupply::analyze()
{
using namespace phosphor::pmbus;
@@ -360,6 +382,7 @@
}
mfrFault = true;
+ determineMFRFault();
}
if (statusWord & status_word::VIN_UV_FAULT)
@@ -395,6 +418,9 @@
.c_str());
pgoodFault = 0;
}
+ psKillFault = false;
+ ps12VcsFault = false;
+ psCS12VFault = false;
}
}
catch (const ReadFailure& e)
@@ -451,6 +477,9 @@
fanFault = false;
tempFault = false;
pgoodFault = 0;
+ psKillFault = false;
+ ps12VcsFault = false;
+ psCS12VFault = false;
readFail = 0;
try
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index ec523ae..c995376 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -302,6 +302,30 @@
}
/**
+ * @brief Return true if there is a PS_Kill fault.
+ */
+ bool hasPSKillFault() const
+ {
+ return psKillFault;
+ }
+
+ /**
+ * @brief Returns true if there is a 12Vcs (standy power) fault.
+ */
+ bool hasPS12VcsFault() const
+ {
+ return ps12VcsFault;
+ }
+
+ /**
+ * @brief Returns true if there is a 12V current-share fault.
+ */
+ bool hasPSCS12VFault() const
+ {
+ return psCS12VFault;
+ }
+
+ /**
* @brief Returns the device path
*
* This can be used for error call outs.
@@ -426,10 +450,35 @@
*/
int pgoodFault = 0;
+ /**
+ * @brief Power Supply Kill fault.
+ */
+ bool psKillFault = false;
+
+ /**
+ * @brief Power Supply 12Vcs fault (standby power).
+ */
+ bool ps12VcsFault = false;
+
+ /**
+ * @brief Power Supply Current-Share fault in 12V domain.
+ */
+ bool psCS12VFault = false;
+
/** @brief Count of the number of read failures. */
size_t readFail = 0;
/**
+ * @brief Determine possible manufacturer-specific faults from bits in
+ * STATUS_MFR.
+ *
+ * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer
+ * Defined" meanings. Determine which faults, if any, are present based on
+ * the power supply (device driver) type.
+ */
+ void determineMFRFault();
+
+ /**
* @brief D-Bus path to use for this power supply's inventory status.
**/
std::string inventoryPath;
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 6fa824a..ac0901b 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -429,7 +429,7 @@
for (auto& psu : psus)
{
additionalData.clear();
- // TODO: Fault priorities #918
+
if (!psu->isFaultLogged() && !psu->isPresent())
{
std::map<std::string, std::string> requiredPSUsData;
@@ -494,6 +494,13 @@
additionalData);
psu->setFaultLogged();
}
+ else if (psu->hasPSKillFault())
+ {
+ createError(
+ "xyz.openbmc_project.Power.PowerSupply.Error.PSKillFault",
+ additionalData);
+ psu->setFaultLogged();
+ }
else if (psu->hasVoutOVFault())
{
// Include STATUS_VOUT for Vout faults.
@@ -521,7 +528,8 @@
psu->setFaultLogged();
}
- else if (psu->hasVoutUVFault())
+ else if (psu->hasVoutUVFault() || psu->hasPS12VcsFault() ||
+ psu->hasPSCS12VFault())
{
// Include STATUS_VOUT for Vout faults.
additionalData["STATUS_VOUT"] =
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 222d9c3..a762ecb 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -159,6 +159,9 @@
EXPECT_EQ(psu->hasFanFault(), false);
EXPECT_EQ(psu->hasTempFault(), false);
EXPECT_EQ(psu->hasPgoodFault(), false);
+ EXPECT_EQ(psu->hasPSKillFault(), false);
+ EXPECT_EQ(psu->hasPS12VcsFault(), false);
+ EXPECT_EQ(psu->hasPSCS12VFault(), false);
}
catch (...)
{
@@ -209,6 +212,9 @@
EXPECT_EQ(psu.hasFanFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
}
PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
@@ -219,7 +225,6 @@
// Always return 1 to indicate present.
// Each analyze() call will trigger a read of the presence GPIO.
EXPECT_CALL(*mockPresenceGPIO2, read()).WillRepeatedly(Return(1));
- // Not present until analyze() does the GPIO read.
EXPECT_EQ(psu2.isPresent(), false);
MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
@@ -255,6 +260,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
// Update expectations for STATUS_WORD input fault/warn
// STATUS_INPUT fault bits ... on.
@@ -274,6 +282,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// STATUS_WORD INPUT/UV fault.
@@ -301,6 +312,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// STATUS_WORD MFR fault.
@@ -327,6 +341,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), true);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), true);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), true);
}
// Temperature fault.
@@ -353,6 +370,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), true);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// CML fault
@@ -379,6 +399,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// VOUT_OV_FAULT fault
@@ -407,6 +430,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// IOUT_OC_FAULT fault
@@ -433,6 +459,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// VOUT_UV_FAULT
@@ -459,6 +488,9 @@
EXPECT_EQ(psu2.hasFanFault(), false);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// Fan fault
@@ -484,6 +516,9 @@
EXPECT_EQ(psu2.hasFanFault(), true);
EXPECT_EQ(psu2.hasTempFault(), false);
EXPECT_EQ(psu2.hasPgoodFault(), false);
+ EXPECT_EQ(psu2.hasPSKillFault(), false);
+ EXPECT_EQ(psu2.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu2.hasPSCS12VFault(), false);
}
// PGOOD/OFF fault. Deglitched, needs to reach DEGLITCH_LIMIT.
@@ -614,6 +649,9 @@
EXPECT_EQ(psu.hasFanFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
// STATUS_WORD with fault bits galore!
expectations.statusWordValue = 0xFFFF;
@@ -655,6 +693,9 @@
psu.analyze();
// DEGLITCH_LIMIT reached for pgoodFault
EXPECT_EQ(psu.hasPgoodFault(), true);
+ EXPECT_EQ(psu.hasPSKillFault(), true);
+ EXPECT_EQ(psu.hasPS12VcsFault(), true);
+ EXPECT_EQ(psu.hasPSCS12VFault(), true);
EXPECT_CALL(mockPMBus, read("in1_input", _))
.Times(1)
@@ -672,6 +713,9 @@
EXPECT_EQ(psu.hasFanFault(), false);
EXPECT_EQ(psu.hasTempFault(), false);
EXPECT_EQ(psu.hasPgoodFault(), false);
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
// TODO: Faults clear on missing/present?
}
@@ -1083,3 +1127,129 @@
psu.analyze();
EXPECT_EQ(psu.hasPgoodFault(), false);
}
+
+TEST_F(PowerSupplyTests, HasPSKillFault)
+{
+ auto bus = sdbusplus::bus::new_default();
+ PowerSupply psu{bus, PSUInventoryPath, 4, 0x6d, PSUGPIOLineName};
+ MockedGPIOInterface* mockPresenceGPIO =
+ static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+ // Always return 1 to indicate present.
+ EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+ // STATUS_WORD 0x0000 is powered on, no faults.
+ PMBusExpectations expectations;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit(s) on.
+ expectations.statusMFRValue = 0xFF;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit 4 on.
+ expectations.statusMFRValue = 0x10;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSKillFault(), false);
+}
+
+TEST_F(PowerSupplyTests, HasPS12VcsFault)
+{
+ auto bus = sdbusplus::bus::new_default();
+ PowerSupply psu{bus, PSUInventoryPath, 5, 0x6e, PSUGPIOLineName};
+ MockedGPIOInterface* mockPresenceGPIO =
+ static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+ // Always return 1 to indicate present.
+ EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+ // STATUS_WORD 0x0000 is powered on, no faults.
+ PMBusExpectations expectations;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit(s) on.
+ expectations.statusMFRValue = 0xFF;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit 6 on.
+ expectations.statusMFRValue = 0x40;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPS12VcsFault(), false);
+}
+
+TEST_F(PowerSupplyTests, HasPSCS12VFault)
+{
+ auto bus = sdbusplus::bus::new_default();
+ PowerSupply psu{bus, PSUInventoryPath, 6, 0x6f, PSUGPIOLineName};
+ MockedGPIOInterface* mockPresenceGPIO =
+ static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+ // Always return 1 to indicate present.
+ EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
+ MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+ // STATUS_WORD 0x0000 is powered on, no faults.
+ PMBusExpectations expectations;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit(s) on.
+ expectations.statusMFRValue = 0xFF;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
+ // Next return STATUS_WORD with MFR fault bit on.
+ expectations.statusWordValue = (status_word::MFR_SPECIFIC_FAULT);
+ // STATUS_MFR_SPEFIC with bit 7 on.
+ expectations.statusMFRValue = 0x80;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), true);
+ // Back to no bits on in STATUS_WORD
+ expectations.statusWordValue = 0;
+ setPMBusExpectations(mockPMBus, expectations);
+ psu.analyze();
+ EXPECT_EQ(psu.hasPSCS12VFault(), false);
+}