pseq: Check STATUS_VOUT first to find pgood fault
Enhance the support for finding the correct voltage rail when a pgood
fault occurs.
First check the PMBus STATUS_VOUT register for all rails. Check the
rails in power-on-sequence order.
Checking STATUS_VOUT is usually the most accurate method. For example,
if a pgood fault occurs, the power sequencer device may automatically
shut off related rails. Ideally the device will only set fault bits in
STATUS_VOUT for the rail with the pgood fault. However, all the related
rails will likely appear to be faulted by the other methods.
If no fault is found by checking STATUS_VOUT, then check the GPIOs and
output voltage for all rails. Check the rails in power-on-sequence
order.
Tested:
* Verified all automated test cases run successfully.
Change-Id: Ida8732db573013f1b72edac8ed54e3cfc38da146
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/rail.cpp b/phosphor-power-sequencer/src/rail.cpp
index 18866c5..ec97108 100644
--- a/phosphor-power-sequencer/src/rail.cpp
+++ b/phosphor-power-sequencer/src/rail.cpp
@@ -121,46 +121,9 @@
const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData)
{
- // If rail is not present, return false and don't check anything else
- if (!isPresent(services))
- {
- services.logInfoMsg(std::format("Rail {} is not present", name));
- return false;
- }
-
- // Check if STATUS_VOUT indicates a pgood fault occurred
- bool hasFault = hasPgoodFaultStatusVout(device, services, additionalData);
-
- // Check if a GPIO value indicates a pgood fault occurred
- if (!hasFault)
- {
- hasFault = hasPgoodFaultGPIO(services, gpioValues, additionalData);
- }
-
- // Check if output voltage is below UV limit indicating pgood fault occurred
- if (!hasFault)
- {
- hasFault = hasPgoodFaultOutputVoltage(device, services, additionalData);
- }
-
- // If fault detected, store debug data in additional data map
- if (hasFault)
- {
- services.logErrorMsg(
- std::format("Pgood fault detected in rail {}", name));
- storePgoodFaultDebugData(device, services, additionalData);
- }
-
- return hasFault;
-}
-
-void Rail::verifyHasPage()
-{
- if (!page)
- {
- throw std::runtime_error{
- std::format("No PAGE number defined for rail {}", name)};
- }
+ return (hasPgoodFaultStatusVout(device, services, additionalData) ||
+ hasPgoodFaultGPIO(device, services, gpioValues, additionalData) ||
+ hasPgoodFaultOutputVoltage(device, services, additionalData));
}
bool Rail::hasPgoodFaultStatusVout(
@@ -169,8 +132,8 @@
{
bool hasFault{false};
- // If we are checking the value of STATUS_VOUT for the rail
- if (checkStatusVout)
+ // If rail is present and we are checking the value of STATUS_VOUT
+ if (isPresent(services) && checkStatusVout)
{
// Read STATUS_VOUT value from device
uint8_t statusVout = getStatusVout(device);
@@ -184,6 +147,7 @@
statusVout));
additionalData.emplace("STATUS_VOUT",
std::format("{:#04x}", statusVout));
+ storePgoodFaultDebugData(device, services, additionalData);
}
else if (statusVout != 0)
{
@@ -196,14 +160,14 @@
return hasFault;
}
-bool Rail::hasPgoodFaultGPIO(Services& services,
+bool Rail::hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services,
const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData)
{
bool hasFault{false};
- // If a GPIO is defined for checking pgood status
- if (gpio)
+ // If rail is present and a GPIO is defined for checking pgood status
+ if (isPresent(services) && gpio)
{
// Get GPIO value
unsigned int line = gpio->line;
@@ -225,6 +189,7 @@
line, value));
additionalData.emplace("GPIO_LINE", std::format("{}", line));
additionalData.emplace("GPIO_VALUE", std::format("{}", value));
+ storePgoodFaultDebugData(device, services, additionalData);
}
}
@@ -237,8 +202,8 @@
{
bool hasFault{false};
- // If we are comparing output voltage to UV limit to check pgood status
- if (compareVoltageToLimit)
+ // If rail is present and we are comparing output voltage to UV limit
+ if (isPresent(services) && compareVoltageToLimit)
{
// Read output voltage and UV fault limit values from device
double vout = getReadVout(device);
@@ -254,16 +219,27 @@
additionalData.emplace("READ_VOUT", std::format("{}", vout));
additionalData.emplace("VOUT_UV_FAULT_LIMIT",
std::format("{}", uvLimit));
+ storePgoodFaultDebugData(device, services, additionalData);
}
}
return hasFault;
}
+void Rail::verifyHasPage()
+{
+ if (!page)
+ {
+ throw std::runtime_error{
+ std::format("No PAGE number defined for rail {}", name)};
+ }
+}
+
void Rail::storePgoodFaultDebugData(
PowerSequencerDevice& device, Services& services,
std::map<std::string, std::string>& additionalData)
{
+ services.logErrorMsg(std::format("Pgood fault detected in rail {}", name));
additionalData.emplace("RAIL_NAME", name);
if (page)
{
diff --git a/phosphor-power-sequencer/src/rail.hpp b/phosphor-power-sequencer/src/rail.hpp
index b4b6f95..ee331ca 100644
--- a/phosphor-power-sequencer/src/rail.hpp
+++ b/phosphor-power-sequencer/src/rail.hpp
@@ -257,14 +257,6 @@
const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData);
- private:
- /**
- * Verifies that a PMBus PAGE number is defined for the rail.
- *
- * Throws an exception if a PAGE number is not defined.
- */
- void verifyHasPage();
-
/**
* Returns whether the PMBus STATUS_VOUT command indicates a pgood fault
* has occurred on the rail.
@@ -290,12 +282,13 @@
* status.
*
* @param device Power sequencer device that enables and monitors the rail
+ * @param services System services like hardware presence and the journal
* @param gpioValues GPIO values obtained from the device (if any)
* @param additionalData Additional data to include in an error log if this
* method returns true
* @return true if a pgood fault was found on the rail, false otherwise
*/
- bool hasPgoodFaultGPIO(Services& services,
+ bool hasPgoodFaultGPIO(PowerSequencerDevice& device, Services& services,
const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData);
@@ -316,6 +309,14 @@
PowerSequencerDevice& device, Services& services,
std::map<std::string, std::string>& additionalData);
+ private:
+ /**
+ * Verifies that a PMBus PAGE number is defined for the rail.
+ *
+ * Throws an exception if a PAGE number is not defined.
+ */
+ void verifyHasPage();
+
/**
* Store pgood fault debug data in the specified additional data map.
*
diff --git a/phosphor-power-sequencer/src/standard_device.cpp b/phosphor-power-sequencer/src/standard_device.cpp
index a11a9cc..5bfbfb3 100644
--- a/phosphor-power-sequencer/src/standard_device.cpp
+++ b/phosphor-power-sequencer/src/standard_device.cpp
@@ -39,32 +39,28 @@
// obtain, so obtain them once and then pass values to each Rail object.
std::vector<int> gpioValues = getGPIOValuesIfPossible(services);
- // Loop through all the rails checking if any detected a pgood fault.
- // The rails are in power-on-sequence order.
- for (std::unique_ptr<Rail>& rail : rails)
+ // Try to find a voltage rail where a pgood fault occurred
+ Rail* rail = findRailWithPgoodFault(services, gpioValues,
+ additionalData);
+ if (rail != nullptr)
{
- if (rail->hasPgoodFault(*this, services, gpioValues,
- additionalData))
+ services.logErrorMsg(std::format(
+ "Pgood fault found in rail monitored by device {}", name));
+
+ // If this is a PSU rail and a PSU error was previously detected
+ if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
{
- services.logErrorMsg(std::format(
- "Pgood fault found in rail monitored by device {}", name));
-
- // If this is a PSU rail and a PSU error was previously detected
- if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
- {
- // Return power supply error as root cause
- error = powerSupplyError;
- }
- else
- {
- // Return pgood fault as root cause
- error =
- "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
- }
-
- storePgoodFaultDebugData(services, gpioValues, additionalData);
- break;
+ // Return power supply error as root cause
+ error = powerSupplyError;
}
+ else
+ {
+ // Return pgood fault as root cause
+ error =
+ "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
+ }
+
+ storePgoodFaultDebugData(services, gpioValues, additionalData);
}
}
catch (const std::exception& e)
@@ -88,6 +84,43 @@
return values;
}
+Rail* StandardDevice::findRailWithPgoodFault(
+ Services& services, const std::vector<int>& gpioValues,
+ std::map<std::string, std::string>& additionalData)
+{
+ // Look for the first rail in the power on sequence with a pgood fault based
+ // on STATUS_VOUT. This is usually the most accurate method. For example,
+ // if a pgood fault occurs, the power sequencer device may automatically
+ // shut off related rails. Ideally the device will only set fault bits in
+ // STATUS_VOUT for the rail with the pgood fault. However, all the related
+ // rails will likely appear to be faulted by the other methods.
+ for (std::unique_ptr<Rail>& rail : rails)
+ {
+ if (rail->hasPgoodFaultStatusVout(*this, services, additionalData))
+ {
+ return rail.get();
+ }
+ }
+
+ // Look for the first rail in the power on sequence with a pgood fault based
+ // on either a GPIO or the output voltage. Both methods check if the rail
+ // is powered off. If a pgood fault occurs during the power on sequence,
+ // the power sequencer device may stop powering on rails. As a result, all
+ // rails after the faulted one in the sequence may also be powered off.
+ for (std::unique_ptr<Rail>& rail : rails)
+ {
+ if (rail->hasPgoodFaultGPIO(*this, services, gpioValues,
+ additionalData) ||
+ rail->hasPgoodFaultOutputVoltage(*this, services, additionalData))
+ {
+ return rail.get();
+ }
+ }
+
+ // No rail with pgood fault found
+ return nullptr;
+}
+
void StandardDevice::storePgoodFaultDebugData(
Services& services, const std::vector<int>& gpioValues,
std::map<std::string, std::string>& additionalData)
diff --git a/phosphor-power-sequencer/src/standard_device.hpp b/phosphor-power-sequencer/src/standard_device.hpp
index 210494f..2c1cf3b 100644
--- a/phosphor-power-sequencer/src/standard_device.hpp
+++ b/phosphor-power-sequencer/src/standard_device.hpp
@@ -110,6 +110,27 @@
virtual std::vector<int> getGPIOValuesIfPossible(Services& services);
/**
+ * Checks whether a pgood fault has occurred on one of the rails being
+ * monitored by this device.
+ *
+ * If a pgood fault was found in a rail, a pointer to the Rail object is
+ * returned.
+ *
+ * Throws an exception if an error occurs while trying to obtain the status
+ * of the rails.
+ *
+ * @param services System services like hardware presence and the journal
+ * @param gpioValues GPIO values obtained from the device (if any)
+ * @param additionalData Additional data to include in the error log if
+ * a pgood fault was found
+ * @return pointer to Rail object where fault was found, or nullptr if no
+ * Rail found
+ */
+ virtual Rail* findRailWithPgoodFault(
+ Services& services, const std::vector<int>& gpioValues,
+ std::map<std::string, std::string>& additionalData);
+
+ /**
* Store pgood fault debug data in the specified additional data map.
*
* The default implementation stores the device name and then calls
diff --git a/phosphor-power-sequencer/test/rail_tests.cpp b/phosphor-power-sequencer/test/rail_tests.cpp
index 479767b..1351f93 100644
--- a/phosphor-power-sequencer/test/rail_tests.cpp
+++ b/phosphor-power-sequencer/test/rail_tests.cpp
@@ -773,16 +773,348 @@
TEST(RailTests, HasPgoodFault)
{
std::string name{"VDD2"};
+ std::optional<std::string> presence{};
+ std::optional<uint8_t> page{2};
bool isPowerSupplyRail{false};
+ bool checkStatusVout{true};
+ bool compareVoltageToLimit{true};
+ bool activeLow{true};
+ std::optional<GPIO> gpio{GPIO(3, activeLow)};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ // No fault detected
+ {
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
+ EXPECT_CALL(device, getVoutUVFaultLimit(2))
+ .Times(1)
+ .WillOnce(Return(1.0));
+
+ MockServices services{};
+
+ std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Fault detected via STATUS_VOUT
+ {
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10));
+ EXPECT_CALL(device, getReadVout(2)).Times(0);
+ EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
+ .Times(1);
+
+ std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_TRUE(
+ rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_EQ(additionalData.size(), 3);
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
+ EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
+ // Fault detected via GPIO
+ {
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(2)).Times(0);
+ EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Rail VDD2 pgood GPIO line offset 3 has inactive value 1"))
+ .Times(1);
+
+ std::vector<int> gpioValues{0, 0, 0, 1, 0, 0};
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_TRUE(
+ rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_EQ(additionalData.size(), 4);
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
+ EXPECT_EQ(additionalData["GPIO_LINE"], "3");
+ EXPECT_EQ(additionalData["GPIO_VALUE"], "1");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
+ // Fault detected via output voltage
+ {
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
+ EXPECT_CALL(device, getVoutUVFaultLimit(2))
+ .Times(1)
+ .WillOnce(Return(1.1));
+ EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Rail VDD2 output voltage 1.1V is <= UV fault limit 1.1V"))
+ .Times(1);
+
+ std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_TRUE(
+ rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_EQ(additionalData.size(), 4);
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
+ EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
+ EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.1");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+}
+
+TEST(RailTests, HasPgoodFaultStatusVout)
+{
+ std::string name{"VDD2"};
+ std::optional<uint8_t> page{3};
+ bool isPowerSupplyRail{false};
+ bool compareVoltageToLimit{false};
+ std::optional<GPIO> gpio{};
+
+ // Test where presence check defined: Rail is not present
+ {
+ std::optional<std::string> presence{
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(0);
+
+ MockServices services{};
+ EXPECT_CALL(services, isPresent(*presence))
+ .Times(1)
+ .WillOnce(Return(false));
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where presence check defined: Rail is present: No fault detected
+ {
+ std::optional<std::string> presence{
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
+
+ MockServices services{};
+ EXPECT_CALL(services, isPresent(*presence))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where STATUS_VOUT check is not defined
+ {
+ std::optional<std::string> presence{};
+ bool checkStatusVout{false};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(0);
+
+ MockServices services{};
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where no fault detected: No warning bits set
+ {
+ std::optional<std::string> presence{};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg).Times(0);
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where no fault detected: Warning bits set
+ {
+ std::optional<std::string> presence{};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x6a));
+
+ MockServices services{};
+ EXPECT_CALL(
+ services,
+ logInfoMsg("Rail VDD2 has warning bits set in STATUS_VOUT: 0x6a"))
+ .Times(1);
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where fault detected
+ // STATUS_WORD captured in additional data
+ {
+ std::optional<std::string> presence{};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x10));
+ EXPECT_CALL(device, getStatusWord(3)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
+ .Times(1);
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_TRUE(
+ rail.hasPgoodFaultStatusVout(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 3);
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
+ EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
+ // Test where exception thrown
+ {
+ std::optional<std::string> presence{};
+ bool checkStatusVout{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusVout(3))
+ .Times(1)
+ .WillOnce(Throw(std::runtime_error{"File does not exist"}));
+
+ MockServices services{};
+
+ std::map<std::string, std::string> additionalData{};
+ try
+ {
+ rail.hasPgoodFaultStatusVout(device, services, additionalData);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::exception& e)
+ {
+ EXPECT_STREQ(e.what(),
+ "Unable to read STATUS_VOUT value for rail VDD2: "
+ "File does not exist");
+ }
+ }
+}
+
+TEST(RailTests, HasPgoodFaultGPIO)
+{
+ std::string name{"VDD2"};
+ bool isPowerSupplyRail{false};
+ bool checkStatusVout{false};
+ bool compareVoltageToLimit{false};
// Test where presence check defined: Rail is not present
{
std::optional<std::string> presence{
"/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
std::optional<uint8_t> page{3};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
+ bool activeLow{false};
+ std::optional<GPIO> gpio{GPIO(3, activeLow)};
Rail rail{name,
presence,
page,
@@ -797,23 +1129,21 @@
EXPECT_CALL(services, isPresent(*presence))
.Times(1)
.WillOnce(Return(false));
- EXPECT_CALL(services, logInfoMsg("Rail VDD2 is not present")).Times(1);
- std::vector<int> gpioValues{};
+ std::vector<int> gpioValues{1, 1, 1, 0, 1, 1};
std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
EXPECT_EQ(additionalData.size(), 0);
}
- // Test where presence check defined: Rail is present
+ // Test where presence check defined: Rail is present: No fault detected
{
std::optional<std::string> presence{
"/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
- std::optional<uint8_t> page{};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
+ std::optional<uint8_t> page{3};
+ bool activeLow{false};
+ std::optional<GPIO> gpio{GPIO(3, activeLow)};
Rail rail{name,
presence,
page,
@@ -828,21 +1158,18 @@
EXPECT_CALL(services, isPresent(*presence))
.Times(1)
.WillOnce(Return(true));
- EXPECT_CALL(services, logInfoMsg).Times(0);
- std::vector<int> gpioValues{};
+ std::vector<int> gpioValues{1, 1, 1, 1, 1, 1};
std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
EXPECT_EQ(additionalData.size(), 0);
}
- // Test where no checks are specified
+ // Test where GPIO check not defined
{
std::optional<std::string> presence{};
- std::optional<uint8_t> page{};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{false};
+ std::optional<uint8_t> page{3};
std::optional<GPIO> gpio{};
Rail rail{name,
presence,
@@ -856,156 +1183,18 @@
MockServices services{};
- std::vector<int> gpioValues{};
+ std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
EXPECT_EQ(additionalData.size(), 0);
}
- // Test where 1 check defined: STATUS_VOUT: No fault detected
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
-
- MockServices services{};
-
- std::vector<int> gpioValues{};
- std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 0);
- }
-
- // Test where 1 check defined: STATUS_VOUT: No fault detected, but warning
- // bits set
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x6a));
-
- MockServices services{};
- EXPECT_CALL(
- services,
- logInfoMsg("Rail VDD2 has warning bits set in STATUS_VOUT: 0x6a"))
- .Times(1);
-
- std::vector<int> gpioValues{};
- std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 0);
- }
-
- // Test where 1 check defined: STATUS_VOUT: Fault detected
- // STATUS_WORD captured in additional data
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10));
- EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
-
- MockServices services{};
- EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
- .Times(1);
- EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
- .Times(1);
- EXPECT_CALL(
- services,
- logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
- .Times(1);
-
- std::vector<int> gpioValues{};
- std::map<std::string, std::string> additionalData{};
- EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 3);
- EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
- EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
- EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
- }
-
- // Test where 1 check defined: STATUS_VOUT: Exception thrown
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{false};
- std::optional<GPIO> gpio{};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2))
- .Times(1)
- .WillOnce(Throw(std::runtime_error{"File does not exist"}));
-
- MockServices services{};
-
- std::vector<int> gpioValues{};
- std::map<std::string, std::string> additionalData{};
- try
- {
- rail.hasPgoodFault(device, services, gpioValues, additionalData);
- ADD_FAILURE() << "Should not have reached this line.";
- }
- catch (const std::exception& e)
- {
- EXPECT_STREQ(e.what(),
- "Unable to read STATUS_VOUT value for rail VDD2: "
- "File does not exist");
- }
- }
-
- // Test where 1 check defined: GPIO: No fault detected
+ // Test where no fault detected
// GPIO value is 1 and GPIO is active high
{
std::optional<std::string> presence{};
std::optional<uint8_t> page{};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{false};
bool activeLow{false};
std::optional<GPIO> gpio{GPIO(3, activeLow)};
Rail rail{name,
@@ -1022,19 +1211,17 @@
std::vector<int> gpioValues{1, 1, 1, 1, 1, 1};
std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_FALSE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
EXPECT_EQ(additionalData.size(), 0);
}
- // Test where 1 check defined: GPIO: Fault detected
+ // Test where fault detected
// GPIO value is 0 and GPIO is active high
// STATUS_WORD not captured since no PMBus page defined
{
std::optional<std::string> presence{};
std::optional<uint8_t> page{};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{false};
bool activeLow{false};
std::optional<GPIO> gpio{GPIO(3, activeLow)};
Rail rail{name,
@@ -1058,20 +1245,58 @@
std::vector<int> gpioValues{1, 1, 1, 0, 1, 1};
std::map<std::string, std::string> additionalData{};
- EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ EXPECT_TRUE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
EXPECT_EQ(additionalData.size(), 3);
EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
EXPECT_EQ(additionalData["GPIO_LINE"], "3");
EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
}
- // Test where 1 check defined: GPIO: Exception thrown: Invalid line offset
+ // Test where fault detected
+ // GPIO value is 1 and GPIO is active low
+ {
+ std::optional<std::string> presence{};
+ std::optional<uint8_t> page{2};
+ bool activeLow{true};
+ std::optional<GPIO> gpio{GPIO(3, activeLow)};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Rail VDD2 pgood GPIO line offset 3 has inactive value 1"))
+ .Times(1);
+
+ std::vector<int> gpioValues{0, 0, 0, 1, 0, 0};
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_TRUE(rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData));
+ EXPECT_EQ(additionalData.size(), 4);
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
+ EXPECT_EQ(additionalData["GPIO_LINE"], "3");
+ EXPECT_EQ(additionalData["GPIO_VALUE"], "1");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
+ // Test where exception thrown
{
std::optional<std::string> presence{};
std::optional<uint8_t> page{};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{false};
bool activeLow{false};
std::optional<GPIO> gpio{GPIO(6, activeLow)};
Rail rail{name,
@@ -1090,7 +1315,8 @@
std::map<std::string, std::string> additionalData{};
try
{
- rail.hasPgoodFault(device, services, gpioValues, additionalData);
+ rail.hasPgoodFaultGPIO(device, services, gpioValues,
+ additionalData);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::exception& e)
@@ -1099,15 +1325,102 @@
"Device only has 6 GPIO values");
}
}
+}
- // Test where 1 check defined: READ_VOUT: No fault detected
- // Output voltage > UV limit
+TEST(RailTests, HasPgoodFaultOutputVoltage)
+{
+ std::string name{"VDD2"};
+ std::optional<uint8_t> page{2};
+ bool isPowerSupplyRail{false};
+ bool checkStatusVout{false};
+ std::optional<GPIO> gpio{};
+
+ // Test where presence check defined: Rail is not present
+ {
+ std::optional<std::string> presence{
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
+ bool compareVoltageToLimit{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getReadVout(2)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(2)).Times(0);
+
+ MockServices services{};
+ EXPECT_CALL(services, isPresent(*presence))
+ .Times(1)
+ .WillOnce(Return(false));
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where presence check defined: Rail is present: No fault detected
+ {
+ std::optional<std::string> presence{
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu2"};
+ bool compareVoltageToLimit{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
+ EXPECT_CALL(device, getVoutUVFaultLimit(2))
+ .Times(1)
+ .WillOnce(Return(1.0));
+
+ MockServices services{};
+ EXPECT_CALL(services, isPresent(*presence))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where voltage output check not specified
{
std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{false};
+ bool compareVoltageToLimit{false};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getReadVout(2)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(2)).Times(0);
+
+ MockServices services{};
+
+ std::map<std::string, std::string> additionalData{};
+ EXPECT_FALSE(
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
+ EXPECT_EQ(additionalData.size(), 0);
+ }
+
+ // Test where no fault detected: Output voltage > UV limit
+ {
+ std::optional<std::string> presence{};
bool compareVoltageToLimit{true};
- std::optional<GPIO> gpio{};
Rail rail{name,
presence,
page,
@@ -1124,21 +1437,16 @@
MockServices services{};
- std::vector<int> gpioValues{};
std::map<std::string, std::string> additionalData{};
EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
EXPECT_EQ(additionalData.size(), 0);
}
- // Test where 1 check defined: READ_VOUT: Fault detected
- // Output voltage < UV limit
+ // Test where fault detected: Output voltage < UV limit
{
std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{false};
bool compareVoltageToLimit{true};
- std::optional<GPIO> gpio{};
Rail rail{name,
presence,
page,
@@ -1165,10 +1473,9 @@
"Rail VDD2 output voltage 1.1V is <= UV fault limit 1.2V"))
.Times(1);
- std::vector<int> gpioValues{};
std::map<std::string, std::string> additionalData{};
EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
EXPECT_EQ(additionalData.size(), 4);
EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
@@ -1176,169 +1483,11 @@
EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
}
- // Test where 1 check defined: READ_VOUT: Exception thrown
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{false};
- bool compareVoltageToLimit{true};
- std::optional<GPIO> gpio{};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getReadVout(2))
- .Times(1)
- .WillOnce(Throw(std::runtime_error{"File does not exist"}));
-
- MockServices services{};
-
- std::vector<int> gpioValues{};
- std::map<std::string, std::string> additionalData{};
- try
- {
- rail.hasPgoodFault(device, services, gpioValues, additionalData);
- ADD_FAILURE() << "Should not have reached this line.";
- }
- catch (const std::exception& e)
- {
- EXPECT_STREQ(e.what(),
- "Unable to read READ_VOUT value for rail VDD2: "
- "File does not exist");
- }
- }
-
- // Test where 3 checks defined: No fault detected
- // GPIO value is 0 and GPIO is active low
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{true};
- bool activeLow{true};
- std::optional<GPIO> gpio{GPIO(3, activeLow)};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
- EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
- EXPECT_CALL(device, getVoutUVFaultLimit(2))
- .Times(1)
- .WillOnce(Return(0.9));
-
- MockServices services{};
-
- std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
- std::map<std::string, std::string> additionalData{};
- EXPECT_FALSE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 0);
- }
-
- // Test where 3 checks defined: Fault detected via STATUS_VOUT
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{true};
- bool activeLow{true};
- std::optional<GPIO> gpio{GPIO(3, activeLow)};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x10));
- EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
-
- MockServices services{};
- EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
- .Times(1);
- EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
- .Times(1);
- EXPECT_CALL(
- services,
- logErrorMsg("Rail VDD2 has fault bits set in STATUS_VOUT: 0x10"))
- .Times(1);
-
- std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
- std::map<std::string, std::string> additionalData{};
- EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 3);
- EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
- EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
- EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
- }
-
- // Test where 3 checks defined: Fault detected via GPIO
- // GPIO value is 1 and GPIO is active low
- {
- std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
- bool compareVoltageToLimit{true};
- bool activeLow{true};
- std::optional<GPIO> gpio{GPIO(3, activeLow)};
- Rail rail{name,
- presence,
- page,
- isPowerSupplyRail,
- checkStatusVout,
- compareVoltageToLimit,
- gpio};
-
- MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
- EXPECT_CALL(device, getStatusWord(2)).Times(1).WillOnce(Return(0xbeef));
-
- MockServices services{};
- EXPECT_CALL(services, logInfoMsg("Rail VDD2 STATUS_WORD: 0xbeef"))
- .Times(1);
- EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD2"))
- .Times(1);
- EXPECT_CALL(
- services,
- logErrorMsg(
- "Rail VDD2 pgood GPIO line offset 3 has inactive value 1"))
- .Times(1);
-
- std::vector<int> gpioValues{0, 0, 0, 1, 0, 0};
- std::map<std::string, std::string> additionalData{};
- EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
- EXPECT_EQ(additionalData.size(), 4);
- EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
- EXPECT_EQ(additionalData["GPIO_LINE"], "3");
- EXPECT_EQ(additionalData["GPIO_VALUE"], "1");
- EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
- }
-
- // Test where 3 checks defined: Fault detected via READ_VOUT
- // Output voltage == UV limit
+ // Test where fault detected: Output voltage == UV limit
// STATUS_WORD not captured because reading it caused an exception
{
std::optional<std::string> presence{};
- std::optional<uint8_t> page{2};
- bool checkStatusVout{true};
bool compareVoltageToLimit{true};
- bool activeLow{true};
- std::optional<GPIO> gpio{GPIO(3, activeLow)};
Rail rail{name,
presence,
page,
@@ -1348,7 +1497,6 @@
gpio};
MockDevice device{};
- EXPECT_CALL(device, getStatusVout(2)).Times(1).WillOnce(Return(0x00));
EXPECT_CALL(device, getReadVout(2)).Times(1).WillOnce(Return(1.1));
EXPECT_CALL(device, getVoutUVFaultLimit(2))
.Times(1)
@@ -1366,13 +1514,45 @@
"Rail VDD2 output voltage 1.1V is <= UV fault limit 1.1V"))
.Times(1);
- std::vector<int> gpioValues{0, 0, 0, 0, 0, 0};
std::map<std::string, std::string> additionalData{};
EXPECT_TRUE(
- rail.hasPgoodFault(device, services, gpioValues, additionalData));
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData));
EXPECT_EQ(additionalData.size(), 3);
EXPECT_EQ(additionalData["RAIL_NAME"], "VDD2");
EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.1");
}
+
+ // Test where exception thrown
+ {
+ std::optional<std::string> presence{};
+ bool compareVoltageToLimit{true};
+ Rail rail{name,
+ presence,
+ page,
+ isPowerSupplyRail,
+ checkStatusVout,
+ compareVoltageToLimit,
+ gpio};
+
+ MockDevice device{};
+ EXPECT_CALL(device, getReadVout(2))
+ .Times(1)
+ .WillOnce(Throw(std::runtime_error{"File does not exist"}));
+
+ MockServices services{};
+
+ std::map<std::string, std::string> additionalData{};
+ try
+ {
+ rail.hasPgoodFaultOutputVoltage(device, services, additionalData);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::exception& e)
+ {
+ EXPECT_STREQ(e.what(),
+ "Unable to read READ_VOUT value for rail VDD2: "
+ "File does not exist");
+ }
+ }
}
diff --git a/phosphor-power-sequencer/test/standard_device_tests.cpp b/phosphor-power-sequencer/test/standard_device_tests.cpp
index 2eb3a42..1afb3bd 100644
--- a/phosphor-power-sequencer/test/standard_device_tests.cpp
+++ b/phosphor-power-sequencer/test/standard_device_tests.cpp
@@ -72,6 +72,10 @@
MOCK_METHOD(uint8_t, getStatusVout, (uint8_t page), (override));
MOCK_METHOD(double, getReadVout, (uint8_t page), (override));
MOCK_METHOD(double, getVoutUVFaultLimit, (uint8_t page), (override));
+
+ // Override empty implementation with mock so we can verify it is called
+ MOCK_METHOD(void, prepareForPgoodFaultDetection, (Services & services),
+ (override));
};
/**
@@ -119,6 +123,28 @@
checkStatusVout, compareVoltageToLimit, gpio);
}
+/**
+ * Creates a Rail object that checks for a pgood fault using output voltage.
+ *
+ * @param name Unique name for the rail
+ * @param isPowerSupplyRail Specifies whether the rail is produced by a
+ power supply
+ * @param pageNum PMBus PAGE number of the rail
+ * @return Rail object
+ */
+std::unique_ptr<Rail> createRailOutputVoltage(const std::string& name,
+ bool isPowerSupplyRail,
+ uint8_t pageNum)
+{
+ std::optional<std::string> presence{};
+ std::optional<uint8_t> page{pageNum};
+ bool checkStatusVout{false};
+ bool compareVoltageToLimit{true};
+ std::optional<GPIO> gpio{};
+ return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
+ checkStatusVout, compareVoltageToLimit, gpio);
+}
+
TEST(StandardDeviceTests, Constructor)
{
// Empty vector of rails
@@ -134,7 +160,7 @@
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 3));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
@@ -168,7 +194,7 @@
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 3));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
@@ -185,15 +211,19 @@
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 2));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{1, 1, 1};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
- EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.2));
+ EXPECT_CALL(device, getVoutUVFaultLimit(5))
+ .Times(1)
+ .WillOnce(Return(1.1));
EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
MockServices services{};
@@ -206,20 +236,23 @@
EXPECT_EQ(additionalData.size(), 0);
}
- // First rail has a pgood fault
+ // First rail has a pgood fault detected via GPIO
// Is a PSU rail: No PSU error specified
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 2));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{1, 1, 0};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
- EXPECT_CALL(device, getStatusVout).Times(0);
+ EXPECT_CALL(device, getReadVout(5)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
+ EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
MockServices services{};
EXPECT_CALL(services,
@@ -252,20 +285,23 @@
EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
}
- // First rail has a pgood fault
+ // First rail has a pgood fault detected via GPIO
// Is a PSU rail: PSU error specified
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 2));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{1, 1, 0};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
- EXPECT_CALL(device, getStatusVout).Times(0);
+ EXPECT_CALL(device, getReadVout(5)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
+ EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
MockServices services{};
EXPECT_CALL(services,
@@ -297,21 +333,25 @@
EXPECT_EQ(additionalData["GPIO_VALUE"], "0");
}
- // Middle rail has a pgood fault
+ // Second rail has a pgood fault detected via output voltage
// Not a PSU rail: PSU error specified
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 2));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{1, 1, 1};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
- EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x10));
- EXPECT_CALL(device, getStatusVout(7)).Times(0);
+ EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
+ EXPECT_CALL(device, getVoutUVFaultLimit(5))
+ .Times(1)
+ .WillOnce(Return(1.2));
+ EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
MockServices services{};
@@ -329,7 +369,8 @@
.Times(1);
EXPECT_CALL(
services,
- logErrorMsg("Rail VDD has fault bits set in STATUS_VOUT: 0x10"))
+ logErrorMsg(
+ "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
.Times(1);
std::string powerSupplyError{"Undervoltage fault: PSU1"};
@@ -338,30 +379,33 @@
additionalData);
EXPECT_EQ(error,
"xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
- EXPECT_EQ(additionalData.size(), 5);
+ EXPECT_EQ(additionalData.size(), 6);
EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 1]");
EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
- EXPECT_EQ(additionalData["STATUS_VOUT"], "0x10");
+ EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
+ EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
}
- // Last rail has a pgood fault
+ // Third rail has a pgood fault detected via STATUS_VOUT
// Device returns 0 GPIO values
// Does not halt pgood fault detection because GPIO values not used by rails
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailStatusVout("PSU", true, 3));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
- EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(5)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
@@ -393,21 +437,23 @@
EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
}
- // Last rail has a pgood fault
+ // Third rail has a pgood fault detected via STATUS_VOUT
// Exception occurs trying to obtain GPIO values from device
// Does not halt pgood fault detection because GPIO values not used by rails
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailStatusVout("PSU", true, 3));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Throw(std::runtime_error{"Unable to acquire GPIO line"}));
EXPECT_CALL(device, getStatusVout(3)).Times(1).WillOnce(Return(0x00));
- EXPECT_CALL(device, getStatusVout(5)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(5)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
@@ -439,22 +485,130 @@
EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
}
+ // All three rails appear to have a pgood fault. Verify third rail is
+ // selected, even though it is last in the power on sequence, because it is
+ // checked using STATUS_VOUT. That check happens before the other checks.
+ {
+ std::vector<std::unique_ptr<Rail>> rails{};
+ rails.emplace_back(createRailGPIO("PSU", true, 2));
+ rails.emplace_back(createRailGPIO("VDD", false, 1));
+ rails.emplace_back(createRailStatusVout("VIO", false, 7));
+ StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
+ std::vector<int> gpioValues{0, 0, 0};
+ EXPECT_CALL(device, getGPIOValues)
+ .Times(1)
+ .WillOnce(Return(gpioValues));
+ EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x11));
+ EXPECT_CALL(device, getStatusWord(7)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services,
+ logInfoMsg("Device abc_pseq GPIO values: [0, 0, 0]"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Pgood fault found in rail monitored by device abc_pseq"))
+ .Times(1);
+ EXPECT_CALL(services, logInfoMsg("Rail VIO STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VIO"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg("Rail VIO has fault bits set in STATUS_VOUT: 0x11"))
+ .Times(1);
+
+ std::string powerSupplyError{};
+ std::map<std::string, std::string> additionalData{};
+ std::string error = device.findPgoodFault(services, powerSupplyError,
+ additionalData);
+ EXPECT_EQ(error,
+ "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
+ EXPECT_EQ(additionalData.size(), 5);
+ EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
+ EXPECT_EQ(additionalData["GPIO_VALUES"], "[0, 0, 0]");
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VIO");
+ EXPECT_EQ(additionalData["STATUS_VOUT"], "0x11");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
+ // Two rails appear to have a pgood fault. One is found via output voltage
+ // and one is found via a GPIO. Verify the first rail in the sequence with
+ // a fault is selected.
+ {
+ std::vector<std::unique_ptr<Rail>> rails{};
+ rails.emplace_back(createRailStatusVout("VIO", false, 7));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
+ rails.emplace_back(createRailGPIO("PSU", true, 2));
+ StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
+ std::vector<int> gpioValues{1, 1, 0};
+ EXPECT_CALL(device, getGPIOValues)
+ .Times(1)
+ .WillOnce(Return(gpioValues));
+ EXPECT_CALL(device, getStatusVout(7)).Times(1).WillOnce(Return(0x00));
+ EXPECT_CALL(device, getReadVout(5)).Times(1).WillOnce(Return(1.1));
+ EXPECT_CALL(device, getVoutUVFaultLimit(5))
+ .Times(1)
+ .WillOnce(Return(1.2));
+ EXPECT_CALL(device, getStatusWord(5)).Times(1).WillOnce(Return(0xbeef));
+
+ MockServices services{};
+ EXPECT_CALL(services,
+ logInfoMsg("Device abc_pseq GPIO values: [1, 1, 0]"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Pgood fault found in rail monitored by device abc_pseq"))
+ .Times(1);
+ EXPECT_CALL(services, logInfoMsg("Rail VDD STATUS_WORD: 0xbeef"))
+ .Times(1);
+ EXPECT_CALL(services, logErrorMsg("Pgood fault detected in rail VDD"))
+ .Times(1);
+ EXPECT_CALL(
+ services,
+ logErrorMsg(
+ "Rail VDD output voltage 1.1V is <= UV fault limit 1.2V"))
+ .Times(1);
+
+ std::string powerSupplyError{};
+ std::map<std::string, std::string> additionalData{};
+ std::string error = device.findPgoodFault(services, powerSupplyError,
+ additionalData);
+ EXPECT_EQ(error,
+ "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault");
+ EXPECT_EQ(additionalData.size(), 6);
+ EXPECT_EQ(additionalData["DEVICE_NAME"], "abc_pseq");
+ EXPECT_EQ(additionalData["GPIO_VALUES"], "[1, 1, 0]");
+ EXPECT_EQ(additionalData["RAIL_NAME"], "VDD");
+ EXPECT_EQ(additionalData["READ_VOUT"], "1.1");
+ EXPECT_EQ(additionalData["VOUT_UV_FAULT_LIMIT"], "1.2");
+ EXPECT_EQ(additionalData["STATUS_WORD"], "0xbeef");
+ }
+
// Exception is thrown during pgood fault detection
{
std::vector<std::unique_ptr<Rail>> rails{};
rails.emplace_back(createRailGPIO("PSU", true, 2));
- rails.emplace_back(createRailStatusVout("VDD", false, 5));
+ rails.emplace_back(createRailOutputVoltage("VDD", false, 5));
rails.emplace_back(createRailStatusVout("VIO", false, 7));
StandardDeviceImpl device{"abc_pseq", std::move(rails)};
+ EXPECT_CALL(device, prepareForPgoodFaultDetection).Times(1);
std::vector<int> gpioValues{1, 1, 1};
EXPECT_CALL(device, getGPIOValues)
.Times(1)
.WillOnce(Return(gpioValues));
- EXPECT_CALL(device, getStatusVout(5))
+ EXPECT_CALL(device, getReadVout(5)).Times(0);
+ EXPECT_CALL(device, getVoutUVFaultLimit(5)).Times(0);
+ EXPECT_CALL(device, getStatusVout(7))
.Times(1)
.WillOnce(Throw(std::runtime_error{"File does not exist"}));
- EXPECT_CALL(device, getStatusVout(7)).Times(0);
MockServices services{};
@@ -470,7 +624,7 @@
EXPECT_STREQ(
e.what(),
"Unable to determine if a pgood fault occurred in device abc_pseq: "
- "Unable to read STATUS_VOUT value for rail VDD: "
+ "Unable to read STATUS_VOUT value for rail VIO: "
"File does not exist");
}
}