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/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index 73998f3..2cef368 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -213,6 +213,8 @@
statusCML = pmbusIntf->read(STATUS_CML, Type::Debug);
auto status0Vout = pmbusIntf->insertPageNum(STATUS_VOUT, 0);
statusVout = pmbusIntf->read(status0Vout, Type::Debug);
+ statusTemperature =
+ pmbusIntf->read(STATUS_TEMPERATURE, Type::Debug);
if (statusWord & status_word::CML_FAULT)
{
if (!cmlFault)
@@ -257,6 +259,23 @@
voutOVFault = true;
}
+ if (statusWord & status_word::TEMPERATURE_FAULT_WARN)
+ {
+ if (!tempFault)
+ {
+ log<level::ERR>(
+ fmt::format("TEMPERATURE fault/warning: "
+ "STATUS_WORD = {:#04x}, "
+ "STATUS_MFR_SPECIFIC = {:#02x}, "
+ "STATUS_TEMPERATURE = {:#02x}",
+ statusWord, statusMFR,
+ statusTemperature)
+ .c_str());
+ }
+
+ tempFault = true;
+ }
+
if (statusWord & status_word::MFR_SPECIFIC_FAULT)
{
if (!mfrFault)
@@ -294,6 +313,7 @@
mfrFault = false;
vinUVFault = false;
voutOVFault = false;
+ tempFault = false;
}
}
catch (const ReadFailure& e)
@@ -345,6 +365,7 @@
vinUVFault = false;
cmlFault = false;
voutOVFault = false;
+ tempFault = false;
readFail = 0;
try
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 8d072c7..c1f7e59 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -177,12 +177,20 @@
}
/**
+ * @brief Returns the last value read from STATUS_TEMPERATURE.
+ */
+ uint64_t getStatusTemperature() const
+ {
+ return statusTemperature;
+ }
+
+ /**
* @brief Returns true if a fault was found.
*/
bool isFaulted() const
{
return (hasCommFault() || vinUVFault || inputFault || voutOVFault ||
- mfrFault);
+ tempFault || mfrFault);
}
/**
@@ -234,6 +242,14 @@
}
/**
+ * @brief Returns true if TEMPERATURE fault occurred.
+ */
+ bool hasTempFault() const
+ {
+ return tempFault;
+ }
+
+ /**
* @brief Returns the device path
*
* This can be used for error call outs.
@@ -308,10 +324,14 @@
/** @brief Will be updated to the latest/last value read from STATUS_VOUT.*/
uint64_t statusVout = 0;
+ /** @brief Will be updated to the latest/last value read from
+ * STATUS_TEMPERATURE.*/
+ uint64_t statusTemperature = 0;
+
/** @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. */
+ /** @brief True if bit 1 of STATUS_WORD low byte is on. */
bool cmlFault = false;
/** @brief True if bit 5 of STATUS_WORD high byte is on. */
@@ -326,6 +346,9 @@
/** @brief True if bit 5 of STATUS_WORD low byte is on. */
bool voutOVFault = false;
+ /** @brief True if bit 2 of STATUS_WORD low byte is on. */
+ bool tempFault = false;
+
/** @brief Count of the number of read failures. */
size_t readFail = 0;
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 257fdfb..b55d019 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -482,6 +482,21 @@
psu->setFaultLogged();
}
+ else if (psu->hasTempFault())
+ {
+ // Include STATUS_TEMPERATURE for temperature faults.
+ additionalData["STATUS_TEMPERATURE"] =
+ fmt::format("{:#02x}", psu->getStatusTemperature());
+
+ additionalData["CALLOUT_INVENTORY_PATH"] =
+ psu->getInventoryPath();
+
+ createError(
+ "xyz.openbmc_project.Power.PowerSupply.Error.Fault",
+ additionalData);
+
+ psu->setFaultLogged();
+ }
else if (psu->hasMFRFault())
{
/* This can represent a variety of faults that result in
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);
+}