pseq: Add support for UCD90160 device
Refactor power sequencer class hierarchy to add support for the UCD90160
device.
Tested:
* Verify UCD information is obtained successfully from EntityManager
* Verify JSON configuration file is found and parsed successfully
* Test where no pgood error occurs
* During power on
* Verify power on continues
* Verify no error is logged
* After chassis is powered on
* Verify chassis remains powered on
* Verify no error is logged
* Test where pgood error occurs
* During power on
* Verify power on stops and chassis is powered off
* Verify correct error is logged
* Verify callouts and additional data in error log are correct
* Detected via rail
* Detected via pin
* After chassis is powered on
* Verify chassis is powered off
* Verify correct error is logged
* Verify callouts and additional data in error log are correct
* Detected via rail
* Detected via pin
Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ib00bc1ea34b504c245a4f0cb3979a86e51507f3c
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/README.md b/phosphor-power-sequencer/README.md
index 4783cc0..0666f41 100644
--- a/phosphor-power-sequencer/README.md
+++ b/phosphor-power-sequencer/README.md
@@ -6,7 +6,7 @@
The currently implemented application is named `phosphor-power-control` and
supports GPIO based power control and power sequencer monitoring with specific
-support for the UCD90320 device available.
+support for the UCD90320 and UCD90160 devices available.
## JSON Configuration Files
diff --git a/phosphor-power-sequencer/config_files/README.md b/phosphor-power-sequencer/config_files/README.md
index c90e733..c2c9374 100644
--- a/phosphor-power-sequencer/config_files/README.md
+++ b/phosphor-power-sequencer/config_files/README.md
@@ -1,13 +1,14 @@
## Overview
-The `UCD90320Monitor` class determines its device configuration from a JSON
-configuration file. The configuration file describes the power sequencer rails
-and power good (pgood) pins defined in the system.
+The `UCD90xMonitor` class and its subclasses determine the device configuration
+from a JSON configuration file. The configuration file describes the power
+sequencer rails and power good (pgood) pins defined in the system.
## File Name
-The expected filename format is `UCD90320Monitor_<systemType>.json`, where
-`<systemType>` is a `Compatible System` name from a `Chassis` Entity Manager
+The expected filename format is `<device>Monitor_<systemType>.json`, where
+`<device>` is the specific power sequencer device type (UCD90320 or UCD90160),
+and `<systemType>` is a `Compatible System` name from a `Chassis` Entity Manager
configuration file.
Example file name:
diff --git a/phosphor-power-sequencer/src/README.md b/phosphor-power-sequencer/src/README.md
index 70213a1..91a1580 100644
--- a/phosphor-power-sequencer/src/README.md
+++ b/phosphor-power-sequencer/src/README.md
@@ -17,9 +17,10 @@
interface.
Determines the type and I2C information of the power sequencer device with the
-Entity Manager `xyz.openbmc_project.Configuration.UCD90320` interface. If a
-specific device interface is not provided or not desired, a generic base class
-implementation (see the `PowerSequencerMonitor` class below) will be used.
+Entity Manager `xyz.openbmc_project.Configuration.UCD90320` or
+`xyz.openbmc_project.Configuration.UCD90160` interface. If a specific device
+interface is not provided or not desired, a generic base class implementation
+(see the `PowerSequencerMonitor` class below) will be used.
### PowerInterface
@@ -39,9 +40,18 @@
any device specific analysis and does not need Entity Manager or JSON file
configuration.
+### UCD90xMonitor
+
+Defines a base class for monitoring the UCD90\* family of power sequencer
+devices and implements the common behavior. Uses the Entity Manager
+`IBMCompatibleSystem` interface to locate its JSON based configuration. The
+loaded configuration allows pgood failure analysis to determine the specific
+voltage rail or pgood pin which has failed.
+
### UCD90320Monitor
-Implements a specific UCD90320 power sequencer device monitoring class. Uses the
-Entity Manager `IBMCompatibleSystem` interface to locate its JSON based
-configuration. The loaded configuration allows pgood failure analysis to
-determine the specific voltage rail or pgood pin which has failed.
+Implements a specific UCD90320 power sequencer device monitoring class.
+
+### UCD90160Monitor
+
+Implements a specific UCD90160 power sequencer device monitoring class.
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index 3d1c8d0..de691c9 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -9,6 +9,8 @@
'power_control.cpp',
'power_interface.cpp',
'power_sequencer_monitor.cpp',
+ 'ucd90x_monitor.cpp',
+ 'ucd90160_monitor.cpp',
'ucd90320_monitor.cpp',
dependencies: [
libgpiodcxx,
diff --git a/phosphor-power-sequencer/src/power_sequencer_monitor.cpp b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
index 603dae1..3c8f999 100644
--- a/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
+++ b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
@@ -22,7 +22,6 @@
#include <phosphor-logging/log.hpp>
#include <exception>
-#include <map>
namespace phosphor::power::sequencer
{
@@ -93,13 +92,12 @@
else if (timeout)
{
// Default to timeout error
- logError("xyz.openbmc_project.Power.Error.PowerOnTimeout",
- additionalData);
+ logError(powerOnTimeoutError, additionalData);
}
else
{
// Default to generic pgood error
- logError("xyz.openbmc_project.Power.Error.Shutdown", additionalData);
+ logError(shutdownError, additionalData);
}
if (!timeout)
{
diff --git a/phosphor-power-sequencer/src/power_sequencer_monitor.hpp b/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
index ed095bb..740b0cd 100644
--- a/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
+++ b/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
@@ -8,6 +8,10 @@
namespace phosphor::power::sequencer
{
+constexpr auto powerOnTimeoutError =
+ "xyz.openbmc_project.Power.Error.PowerOnTimeout";
+constexpr auto shutdownError = "xyz.openbmc_project.Power.Error.Shutdown";
+
/**
* @class PowerSequencerMonitor
* Define a base class for monitoring a power sequencer device.
@@ -52,7 +56,7 @@
*/
sdbusplus::bus_t& bus;
- /*
+ /**
* Create a BMC Dump
*/
void createBmcDump();
diff --git a/phosphor-power-sequencer/src/ucd90160_monitor.cpp b/phosphor-power-sequencer/src/ucd90160_monitor.cpp
new file mode 100644
index 0000000..f933230
--- /dev/null
+++ b/phosphor-power-sequencer/src/ucd90160_monitor.cpp
@@ -0,0 +1,27 @@
+/**
+ * Copyright © 2022 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ucd90160_monitor.hpp"
+
+namespace phosphor::power::sequencer
+{
+
+UCD90160Monitor::UCD90160Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
+ std::uint16_t i2cAddress) :
+ UCD90xMonitor(bus, i2cBus, i2cAddress, "UCD90160", 16)
+{}
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90160_monitor.hpp b/phosphor-power-sequencer/src/ucd90160_monitor.hpp
new file mode 100644
index 0000000..31d4567
--- /dev/null
+++ b/phosphor-power-sequencer/src/ucd90160_monitor.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "ucd90x_monitor.hpp"
+
+#include <sdbusplus/bus.hpp>
+
+#include <cstdint>
+
+namespace phosphor::power::sequencer
+{
+
+/**
+ * @class UCD90160Monitor
+ * This class implements fault analysis for the UCD90160
+ * power sequencer device.
+ */
+class UCD90160Monitor : public UCD90xMonitor
+{
+ public:
+ UCD90160Monitor() = delete;
+ UCD90160Monitor(const UCD90160Monitor&) = delete;
+ UCD90160Monitor& operator=(const UCD90160Monitor&) = delete;
+ UCD90160Monitor(UCD90160Monitor&&) = delete;
+ UCD90160Monitor& operator=(UCD90160Monitor&&) = delete;
+ virtual ~UCD90160Monitor() = default;
+
+ /**
+ * Create a device object for UCD90160 monitoring.
+ * @param bus D-Bus bus object
+ * @param i2cBus The bus number of the power sequencer device
+ * @param i2cAddress The I2C address of the power sequencer device
+ */
+ UCD90160Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
+ std::uint16_t i2cAddress);
+};
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.cpp b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
index c6adcc0..818977b 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
@@ -16,378 +16,28 @@
#include "ucd90320_monitor.hpp"
-#include "types.hpp"
-#include "utility.hpp"
-
#include <fmt/format.h>
#include <fmt/ranges.h>
-#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
-#include <sdbusplus/bus.hpp>
-#include <xyz/openbmc_project/Common/Device/error.hpp>
-#include <chrono>
-#include <fstream>
-#include <map>
#include <span>
-#include <string>
namespace phosphor::power::sequencer
{
-using json = nlohmann::json;
-using namespace pmbus;
using namespace phosphor::logging;
-using namespace phosphor::power;
-
-const std::string compatibleInterface =
- "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
-const std::string compatibleNamesProperty = "Names";
-
-namespace device_error = sdbusplus::xyz::openbmc_project::Common::Device::Error;
UCD90320Monitor::UCD90320Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
std::uint16_t i2cAddress) :
- PowerSequencerMonitor(bus),
- match{bus,
- sdbusplus::bus::match::rules::interfacesAdded() +
- sdbusplus::bus::match::rules::sender(
- "xyz.openbmc_project.EntityManager"),
- std::bind(&UCD90320Monitor::interfacesAddedHandler, this,
- std::placeholders::_1)},
- pmbusInterface{
- fmt::format("/sys/bus/i2c/devices/{}-{:04x}", i2cBus, i2cAddress)
- .c_str(),
- "ucd9000", 0}
+ UCD90xMonitor(bus, i2cBus, i2cAddress, "UCD90320", 32)
+{}
+void UCD90320Monitor::formatGpioValues(
+ const std::vector<int>& values, unsigned int numberLines,
+ std::map<std::string, std::string>& additionalData) const
{
- // Use the compatible system types information, if already available, to
- // load the configuration file
- findCompatibleSystemTypes();
-}
-
-void UCD90320Monitor::findCompatibleSystemTypes()
-{
- try
- {
- auto subTree = util::getSubTree(bus, "/xyz/openbmc_project/inventory",
- compatibleInterface, 0);
-
- auto objectIt = subTree.cbegin();
- if (objectIt != subTree.cend())
- {
- const auto& objPath = objectIt->first;
-
- // Get the first service name
- auto serviceIt = objectIt->second.cbegin();
- if (serviceIt != objectIt->second.cend())
- {
- std::string service = serviceIt->first;
- if (!service.empty())
- {
- std::vector<std::string> compatibleSystemTypes;
-
- // Get compatible system types property value
- util::getProperty(compatibleInterface,
- compatibleNamesProperty, objPath, service,
- bus, compatibleSystemTypes);
-
- log<level::DEBUG>(
- fmt::format("Found compatible systems: {}",
- compatibleSystemTypes)
- .c_str());
- // Use compatible systems information to find config file
- findConfigFile(compatibleSystemTypes);
- }
- }
- }
- }
- catch (const std::exception&)
- {
- // Compatible system types information is not available.
- }
-}
-
-void UCD90320Monitor::findConfigFile(
- const std::vector<std::string>& compatibleSystemTypes)
-{
- // Expected config file path name:
- // /usr/share/phosphor-power-sequencer/UCD90320Monitor_<systemType>.json
-
- // Add possible file names based on compatible system types (if any)
- for (const std::string& systemType : compatibleSystemTypes)
- {
- // Check if file exists
- std::filesystem::path pathName{
- "/usr/share/phosphor-power-sequencer/UCD90320Monitor_" +
- systemType + ".json"};
- if (std::filesystem::exists(pathName))
- {
- log<level::INFO>(
- fmt::format("Config file path: {}", pathName.string()).c_str());
- parseConfigFile(pathName);
- break;
- }
- }
-}
-
-void UCD90320Monitor::interfacesAddedHandler(sdbusplus::message_t& msg)
-{
- // Only continue if message is valid and rails / pins have not already been
- // found
- if (!msg || !rails.empty())
- {
- return;
- }
-
- try
- {
- // Read the dbus message
- sdbusplus::message::object_path objPath;
- std::map<std::string,
- std::map<std::string, std::variant<std::vector<std::string>>>>
- interfaces;
- msg.read(objPath, interfaces);
-
- // Find the compatible interface, if present
- auto itIntf = interfaces.find(compatibleInterface);
- if (itIntf != interfaces.cend())
- {
- // Find the Names property of the compatible interface, if present
- auto itProp = itIntf->second.find(compatibleNamesProperty);
- if (itProp != itIntf->second.cend())
- {
- // Get value of Names property
- const auto& propValue = std::get<0>(itProp->second);
- if (!propValue.empty())
- {
- log<level::INFO>(
- fmt::format(
- "InterfacesAdded for compatible systems: {}",
- propValue)
- .c_str());
-
- // Use compatible systems information to find config file
- findConfigFile(propValue);
- }
- }
- }
- }
- catch (const std::exception&)
- {
- // Error trying to read interfacesAdded message.
- }
-}
-
-bool UCD90320Monitor::isPresent(const std::string& inventoryPath)
-{
- // Empty path indicates no presence check is needed
- if (inventoryPath.empty())
- {
- return true;
- }
-
- // Get presence from D-Bus interface/property
- try
- {
- bool present{true};
- util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
- INVENTORY_MGR_IFACE, bus, present);
- log<level::INFO>(
- fmt::format("Presence, path: {}, value: {}", inventoryPath, present)
- .c_str());
- return present;
- }
- catch (const std::exception& e)
- {
- log<level::INFO>(
- fmt::format("Error getting presence property, path: {}, error: {}",
- inventoryPath, e.what())
- .c_str());
- return false;
- }
-}
-
-void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
-{
- try
- {
- std::ifstream file{pathName};
- json rootElement = json::parse(file);
-
- // Parse rail information from config file
- auto railsIterator = rootElement.find("rails");
- if (railsIterator != rootElement.end())
- {
- for (const auto& railElement : *railsIterator)
- {
- auto nameIterator = railElement.find("name");
-
- if (nameIterator != railElement.end())
- {
- Rail rail;
- rail.name = (*nameIterator).get<std::string>();
-
- // Presence element is optional
- auto presenceIterator = railElement.find("presence");
- if (presenceIterator != railElement.end())
- {
- rail.presence = (*presenceIterator).get<std::string>();
- }
-
- log<level::DEBUG>(
- fmt::format("Adding rail, name: {}, presence: {}",
- rail.name, rail.presence)
- .c_str());
- rails.emplace_back(std::move(rail));
- }
- else
- {
- log<level::ERR>(
- fmt::format(
- "No name found within rail in configuration file: {}",
- pathName.string())
- .c_str());
- }
- }
- }
- else
- {
- log<level::ERR>(
- fmt::format("No rails found in configuration file: {}",
- pathName.string())
- .c_str());
- }
- log<level::DEBUG>(
- fmt::format("Found number of rails: {}", rails.size()).c_str());
-
- // Parse pin information from config file
- auto pinsIterator = rootElement.find("pins");
- if (pinsIterator != rootElement.end())
- {
- for (const auto& pinElement : *pinsIterator)
- {
- auto nameIterator = pinElement.find("name");
- auto lineIterator = pinElement.find("line");
-
- if (nameIterator != pinElement.end() &&
- lineIterator != pinElement.end())
- {
- Pin pin;
- pin.name = (*nameIterator).get<std::string>();
- pin.line = (*lineIterator).get<unsigned int>();
-
- // Presence element is optional
- auto presenceIterator = pinElement.find("presence");
- if (presenceIterator != pinElement.end())
- {
- pin.presence = (*presenceIterator).get<std::string>();
- }
-
- log<level::DEBUG>(
- fmt::format(
- "Adding pin, name: {}, line: {}, presence: {}",
- pin.name, pin.line, pin.presence)
- .c_str());
- pins.emplace_back(std::move(pin));
- }
- else
- {
- log<level::ERR>(
- fmt::format(
- "No name or line found within pin in configuration file: {}",
- pathName.string())
- .c_str());
- }
- }
- }
- else
- {
- log<level::ERR>(
- fmt::format("No pins found in configuration file: {}",
- pathName.string())
- .c_str());
- }
- log<level::DEBUG>(
- fmt::format("Found number of pins: {}", pins.size()).c_str());
- }
- catch (const std::exception& e)
- {
- log<level::ERR>(
- fmt::format("Error parsing configuration file, error: {}", e.what())
- .c_str());
- }
-}
-
-void UCD90320Monitor::onFailure(bool timeout,
- const std::string& powerSupplyError)
-{
- std::string message;
- std::map<std::string, std::string> additionalData{};
-
- try
- {
- onFailureCheckRails(message, additionalData, powerSupplyError);
- onFailureCheckPins(message, additionalData);
- }
- catch (const std::exception& e)
- {
- log<level::ERR>(
- fmt::format("Error when collecting metadata, error: {}", e.what())
- .c_str());
- additionalData.emplace("ERROR", e.what());
- }
-
- if (message.empty())
- {
- // Could not isolate, but we know something failed, so issue a timeout
- // or generic power good error
- message = timeout ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
- : "xyz.openbmc_project.Power.Error.Shutdown";
- }
- logError(message, additionalData);
- if (!timeout)
- {
- createBmcDump();
- }
-}
-
-void UCD90320Monitor::onFailureCheckPins(
- std::string& message, std::map<std::string, std::string>& additionalData)
-{
- // Setup a list of all the GPIOs on the chip
- gpiod::chip chip{"ucd90320", gpiod::chip::OPEN_BY_LABEL};
- log<level::INFO>(fmt::format("GPIO chip name: {}", chip.name()).c_str());
- log<level::INFO>(fmt::format("GPIO chip label: {}", chip.label()).c_str());
- unsigned int numberLines = chip.num_lines();
- log<level::INFO>(
- fmt::format("GPIO chip number of lines: {}", numberLines).c_str());
-
- // Workaround libgpiod bulk line maximum by getting values from individual
- // lines
- std::vector<int> values;
- try
- {
- for (unsigned int offset = 0; offset < numberLines; ++offset)
- {
- gpiod::line line = chip.get_line(offset);
- line.request({"phosphor-power-control",
- gpiod::line_request::DIRECTION_INPUT, 0});
- values.push_back(line.get_value());
- line.release();
- }
- }
- catch (const std::exception& e)
- {
- log<level::ERR>(
- fmt::format("Error reading device GPIOs, error: {}", e.what())
- .c_str());
- additionalData.emplace("GPIO_ERROR", e.what());
- }
-
- // Add GPIO values to additional data, device has 84 GPIO pins so that value
- // is expected
+ // Device has 84 GPIO pins so that value is expected
if (numberLines == 84 && values.size() >= 84)
{
log<level::INFO>(fmt::format("MAR01-24 GPIO values: {}",
@@ -426,114 +76,6 @@
log<level::INFO>(fmt::format("GPIO values: {}", values).c_str());
additionalData.emplace("GPIO_VALUES", fmt::format("{}", values));
}
-
- // Only check GPIOs if no rail fail was found
- if (message.empty())
- {
- for (size_t pin = 0; pin < pins.size(); ++pin)
- {
- unsigned int line = pins[pin].line;
- if (line < values.size())
- {
- int value = values[line];
-
- if ((value == 0) && isPresent(pins[pin].presence))
- {
- additionalData.emplace("INPUT_NUM",
- fmt::format("{}", line));
- additionalData.emplace("INPUT_NAME", pins[pin].name);
- message =
- "xyz.openbmc_project.Power.Error.PowerSequencerPGOODFault";
- return;
- }
- }
- }
- }
-}
-
-void UCD90320Monitor::onFailureCheckRails(
- std::string& message, std::map<std::string, std::string>& additionalData,
- const std::string& powerSupplyError)
-{
- auto statusWord = readStatusWord();
- additionalData.emplace("STATUS_WORD", fmt::format("{:#06x}", statusWord));
- try
- {
- additionalData.emplace("MFR_STATUS",
- fmt::format("{:#014x}", readMFRStatus()));
- }
- catch (const std::exception& e)
- {
- log<level::ERR>(
- fmt::format("Error when collecting MFR_STATUS, error: {}", e.what())
- .c_str());
- additionalData.emplace("ERROR", e.what());
- }
-
- // The status_word register has a summary bit to tell us if each page even
- // needs to be checked
- if (statusWord & status_word::VOUT_FAULT)
- {
- constexpr size_t numberPages = 32;
- for (size_t page = 0; page < numberPages; page++)
- {
- auto statusVout = pmbusInterface.insertPageNum(STATUS_VOUT, page);
- if (pmbusInterface.exists(statusVout, Type::Debug))
- {
- uint8_t vout = pmbusInterface.read(statusVout, Type::Debug);
-
- if (vout)
- {
- // If any bits are on log them, though some are just
- // warnings so they won't cause errors
- log<level::INFO>(
- fmt::format("{}, value: {:#04x}", statusVout, vout)
- .c_str());
-
- // Log errors if any non-warning bits on
- if (vout & ~status_vout::WARNING_MASK)
- {
- additionalData.emplace(
- fmt::format("STATUS{}_VOUT", page),
- fmt::format("{:#04x}", vout));
-
- // Base the callouts on the first present vout failure
- // found
- if (message.empty() && (page < rails.size()) &&
- isPresent(rails[page].presence))
- {
- additionalData.emplace("RAIL_NAME",
- rails[page].name);
-
- // Use power supply error if set and 12v rail has
- // failed, else use voltage error
- message =
- ((page == 0) && !powerSupplyError.empty())
- ? powerSupplyError
- : "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
- }
- }
- }
- }
- }
- }
- // If no vout failure found, but power supply error is set, use power supply
- // error
- if (message.empty())
- {
- message = powerSupplyError;
- }
-}
-
-uint16_t UCD90320Monitor::readStatusWord()
-{
- return pmbusInterface.read(STATUS_WORD, Type::Debug);
-}
-
-uint64_t UCD90320Monitor::readMFRStatus()
-{
- const std::string mfrStatus = "mfr_status";
- return pmbusInterface.read(mfrStatus, Type::HwmonDeviceDebug);
}
} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.hpp b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
index 9bcbf77..a40456d 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.hpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
@@ -1,37 +1,23 @@
#pragma once
-#include "pmbus.hpp"
-#include "power_sequencer_monitor.hpp"
+#include "ucd90x_monitor.hpp"
-#include <gpiod.hpp>
#include <sdbusplus/bus.hpp>
-#include <sdbusplus/bus/match.hpp>
-#include <filesystem>
+#include <cstdint>
+#include <map>
+#include <string>
#include <vector>
namespace phosphor::power::sequencer
{
-struct Pin
-{
- std::string name;
- unsigned int line;
- std::string presence;
-};
-
-struct Rail
-{
- std::string name;
- std::string presence;
-};
-
/**
* @class UCD90320Monitor
* This class implements fault analysis for the UCD90320
* power sequencer device.
*/
-class UCD90320Monitor : public PowerSequencerMonitor
+class UCD90320Monitor : public UCD90xMonitor
{
public:
UCD90320Monitor() = delete;
@@ -50,103 +36,11 @@
UCD90320Monitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
std::uint16_t i2cAddress);
- /**
- * Callback function to handle interfacesAdded D-Bus signals
- * @param msg Expanded sdbusplus message data
- */
- void interfacesAddedHandler(sdbusplus::message_t& msg);
-
- /** @copydoc PowerSequencerMonitor::onFailure() */
- void onFailure(bool timeout, const std::string& powerSupplyError) override;
-
- private:
- /**
- * The match to Entity Manager interfaces added.
- */
- sdbusplus::bus::match_t match;
-
- /**
- * List of pins
- */
- std::vector<Pin> pins;
-
- /**
- * The read/write interface to this hardware
- */
- pmbus::PMBus pmbusInterface;
-
- /**
- * List of rails
- */
- std::vector<Rail> rails;
-
- /**
- * Finds the list of compatible system types using D-Bus methods.
- * This list is used to find the correct JSON configuration file for the
- * current system.
- */
- void findCompatibleSystemTypes();
-
- /**
- * Finds the JSON configuration file.
- * Looks for a configuration file based on the list of compatible system
- * types.
- * Throws an exception if an operating system error occurs while checking
- * for the existance of a file.
- * @param compatibleSystemTypes List of compatible system types
- */
- void findConfigFile(const std::vector<std::string>& compatibleSystemTypes);
-
- /**
- * Returns whether the hardware with the specified inventory path is
- * present.
- * If an error occurs while obtaining the presence value, presence is
- * assumed to be false. An empty string path indicates no presence check is
- * needed.
- * @param inventoryPath D-Bus inventory path of the hardware
- * @return true if hardware is present, false otherwise
- */
- bool isPresent(const std::string& inventoryPath);
-
- /**
- * Analyzes the device pins for errors when the device is known to be in an
- * error state.
- * @param message Message property of the error log entry
- * @param additionalData AdditionalData property of the error log entry
- */
- void onFailureCheckPins(std::string& message,
- std::map<std::string, std::string>& additionalData);
-
- /**
- * Analyzes the device rails for errors when the device is known to be in an
- * error state.
- * @param message Message property of the error log entry
- * @param additionalData AdditionalData property of the error log entry
- * @param powerSupplyError The power supply error to log. A default
- * std:string, i.e. empty string (""), is passed when there is no power
- * supply error to log.
- */
- void onFailureCheckRails(std::string& message,
- std::map<std::string, std::string>& additionalData,
- const std::string& powerSupplyError);
-
- /**
- * Parse the JSON configuration file.
- * @param pathName the path name
- */
- void parseConfigFile(const std::filesystem::path& pathName);
-
- /**
- * Reads the mfr_status register
- * @return the register contents
- */
- uint64_t readMFRStatus();
-
- /**
- * Reads the status_word register
- * @return the register contents
- */
- uint16_t readStatusWord();
+ protected:
+ /** @copydoc UCD90xMonitor::formatGpioValues() */
+ void formatGpioValues(
+ const std::vector<int>& values, unsigned int numberLines,
+ std::map<std::string, std::string>& additionalData) const override;
};
} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90x_monitor.cpp b/phosphor-power-sequencer/src/ucd90x_monitor.cpp
new file mode 100644
index 0000000..2dc4675
--- /dev/null
+++ b/phosphor-power-sequencer/src/ucd90x_monitor.cpp
@@ -0,0 +1,564 @@
+/**
+ * Copyright © 2022 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ucd90x_monitor.hpp"
+
+#include "types.hpp"
+#include "utility.hpp"
+
+#include <fmt/format.h>
+#include <fmt/ranges.h>
+
+#include <gpiod.hpp>
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <algorithm>
+#include <chrono>
+#include <exception>
+#include <fstream>
+
+namespace phosphor::power::sequencer
+{
+
+using json = nlohmann::json;
+using namespace pmbus;
+using namespace phosphor::logging;
+
+const std::string compatibleInterface =
+ "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
+const std::string compatibleNamesProperty = "Names";
+
+UCD90xMonitor::UCD90xMonitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
+ std::uint16_t i2cAddress,
+ const std::string& deviceName,
+ size_t numberPages) :
+ PowerSequencerMonitor(bus),
+ deviceName{deviceName},
+ match{bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::sender(
+ "xyz.openbmc_project.EntityManager"),
+ std::bind(&UCD90xMonitor::interfacesAddedHandler, this,
+ std::placeholders::_1)},
+ numberPages{numberPages},
+ pmbusInterface{
+ fmt::format("/sys/bus/i2c/devices/{}-{:04x}", i2cBus, i2cAddress)
+ .c_str(),
+ "ucd9000", 0}
+{
+ log<level::DEBUG>(
+ fmt::format("Device path: {}", pmbusInterface.path().string()).c_str());
+ log<level::DEBUG>(fmt::format("Hwmon path: {}",
+ pmbusInterface.getPath(Type::Hwmon).string())
+ .c_str());
+ log<level::DEBUG>(fmt::format("Debug path: {}",
+ pmbusInterface.getPath(Type::Debug).string())
+ .c_str());
+ log<level::DEBUG>(
+ fmt::format("Device debug path: {}",
+ pmbusInterface.getPath(Type::DeviceDebug).string())
+ .c_str());
+ log<level::DEBUG>(
+ fmt::format("Hwmon device debug path: {}",
+ pmbusInterface.getPath(Type::HwmonDeviceDebug).string())
+ .c_str());
+
+ // Use the compatible system types information, if already available, to
+ // load the configuration file
+ findCompatibleSystemTypes();
+}
+
+void UCD90xMonitor::findCompatibleSystemTypes()
+{
+ try
+ {
+ auto subTree = util::getSubTree(bus, "/xyz/openbmc_project/inventory",
+ compatibleInterface, 0);
+
+ auto objectIt = subTree.cbegin();
+ if (objectIt != subTree.cend())
+ {
+ const auto& objPath = objectIt->first;
+
+ // Get the first service name
+ auto serviceIt = objectIt->second.cbegin();
+ if (serviceIt != objectIt->second.cend())
+ {
+ std::string service = serviceIt->first;
+ if (!service.empty())
+ {
+ std::vector<std::string> compatibleSystemTypes;
+
+ // Get compatible system types property value
+ util::getProperty(compatibleInterface,
+ compatibleNamesProperty, objPath, service,
+ bus, compatibleSystemTypes);
+
+ log<level::DEBUG>(
+ fmt::format("Found compatible systems: {}",
+ compatibleSystemTypes)
+ .c_str());
+ // Use compatible systems information to find config file
+ findConfigFile(compatibleSystemTypes);
+ }
+ }
+ }
+ }
+ catch (const std::exception&)
+ {
+ // Compatible system types information is not available.
+ }
+}
+
+void UCD90xMonitor::findConfigFile(
+ const std::vector<std::string>& compatibleSystemTypes)
+{
+ // Expected config file path name:
+ // /usr/share/phosphor-power-sequencer/<deviceName>Monitor_<systemType>.json
+
+ // Add possible file names based on compatible system types (if any)
+ for (const std::string& systemType : compatibleSystemTypes)
+ {
+ // Check if file exists
+ std::filesystem::path pathName{"/usr/share/phosphor-power-sequencer/" +
+ deviceName + "Monitor_" + systemType +
+ ".json"};
+ log<level::DEBUG>(
+ fmt::format("Attempting config file path: {}", pathName.string())
+ .c_str());
+ if (std::filesystem::exists(pathName))
+ {
+ log<level::INFO>(
+ fmt::format("Config file path: {}", pathName.string()).c_str());
+ parseConfigFile(pathName);
+ break;
+ }
+ }
+}
+
+void UCD90xMonitor::interfacesAddedHandler(sdbusplus::message_t& msg)
+{
+ // Only continue if message is valid and rails / pins have not already been
+ // found
+ if (!msg || !rails.empty())
+ {
+ return;
+ }
+
+ try
+ {
+ // Read the dbus message
+ sdbusplus::message::object_path objPath;
+ std::map<std::string,
+ std::map<std::string, std::variant<std::vector<std::string>>>>
+ interfaces;
+ msg.read(objPath, interfaces);
+
+ // Find the compatible interface, if present
+ auto itIntf = interfaces.find(compatibleInterface);
+ if (itIntf != interfaces.cend())
+ {
+ // Find the Names property of the compatible interface, if present
+ auto itProp = itIntf->second.find(compatibleNamesProperty);
+ if (itProp != itIntf->second.cend())
+ {
+ // Get value of Names property
+ const auto& propValue = std::get<0>(itProp->second);
+ if (!propValue.empty())
+ {
+ log<level::INFO>(
+ fmt::format(
+ "InterfacesAdded for compatible systems: {}",
+ propValue)
+ .c_str());
+
+ // Use compatible systems information to find config file
+ findConfigFile(propValue);
+ }
+ }
+ }
+ }
+ catch (const std::exception&)
+ {
+ // Error trying to read interfacesAdded message.
+ }
+}
+
+bool UCD90xMonitor::isPresent(const std::string& inventoryPath)
+{
+ // Empty path indicates no presence check is needed
+ if (inventoryPath.empty())
+ {
+ return true;
+ }
+
+ // Get presence from D-Bus interface/property
+ try
+ {
+ bool present{true};
+ util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
+ INVENTORY_MGR_IFACE, bus, present);
+ log<level::INFO>(
+ fmt::format("Presence, path: {}, value: {}", inventoryPath, present)
+ .c_str());
+ return present;
+ }
+ catch (const std::exception& e)
+ {
+ log<level::INFO>(
+ fmt::format("Error getting presence property, path: {}, error: {}",
+ inventoryPath, e.what())
+ .c_str());
+ return false;
+ }
+}
+
+void UCD90xMonitor::formatGpioValues(
+ const std::vector<int>& values, unsigned int /*numberLines*/,
+ std::map<std::string, std::string>& additionalData) const
+{
+ log<level::INFO>(fmt::format("GPIO values: {}", values).c_str());
+ additionalData.emplace("GPIO_VALUES", fmt::format("{}", values));
+}
+
+void UCD90xMonitor::onFailure(bool timeout, const std::string& powerSupplyError)
+{
+ std::string message;
+ std::map<std::string, std::string> additionalData{};
+
+ try
+ {
+ onFailureCheckRails(message, additionalData, powerSupplyError);
+ log<level::DEBUG>(
+ fmt::format("After onFailureCheckRails, message: {}", message)
+ .c_str());
+ onFailureCheckPins(message, additionalData);
+ log<level::DEBUG>(
+ fmt::format("After onFailureCheckPins, message: {}", message)
+ .c_str());
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Error when collecting metadata, error: {}", e.what())
+ .c_str());
+ additionalData.emplace("ERROR", e.what());
+ }
+
+ if (message.empty())
+ {
+ // Could not isolate, but we know something failed, so issue a timeout
+ // or generic power good error
+ message = timeout ? powerOnTimeoutError : shutdownError;
+ }
+ logError(message, additionalData);
+ if (!timeout)
+ {
+ createBmcDump();
+ }
+}
+
+void UCD90xMonitor::onFailureCheckPins(
+ std::string& message, std::map<std::string, std::string>& additionalData)
+{
+ // Create a lower case version of device name to use as label in libgpiod
+ std::string label{deviceName};
+ std::transform(label.begin(), label.end(), label.begin(), ::tolower);
+
+ // Setup a list of all the GPIOs on the chip
+ gpiod::chip chip{label, gpiod::chip::OPEN_BY_LABEL};
+ log<level::INFO>(fmt::format("GPIO chip name: {}", chip.name()).c_str());
+ log<level::INFO>(fmt::format("GPIO chip label: {}", chip.label()).c_str());
+ unsigned int numberLines = chip.num_lines();
+ log<level::INFO>(
+ fmt::format("GPIO chip number of lines: {}", numberLines).c_str());
+
+ // Workaround libgpiod bulk line maximum by getting values from individual
+ // lines
+ std::vector<int> values;
+ try
+ {
+ for (unsigned int offset = 0; offset < numberLines; ++offset)
+ {
+ gpiod::line line = chip.get_line(offset);
+ line.request({"phosphor-power-control",
+ gpiod::line_request::DIRECTION_INPUT, 0});
+ values.push_back(line.get_value());
+ line.release();
+ }
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Error reading device GPIOs, error: {}", e.what())
+ .c_str());
+ additionalData.emplace("GPIO_ERROR", e.what());
+ }
+
+ formatGpioValues(values, numberLines, additionalData);
+
+ // Only check GPIOs if no rail fail was found
+ if (message.empty())
+ {
+ for (size_t pin = 0; pin < pins.size(); ++pin)
+ {
+ unsigned int line = pins[pin].line;
+ if (line < values.size())
+ {
+ int value = values[line];
+
+ if ((value == 0) && isPresent(pins[pin].presence))
+ {
+ additionalData.emplace("INPUT_NUM",
+ fmt::format("{}", line));
+ additionalData.emplace("INPUT_NAME", pins[pin].name);
+ message =
+ "xyz.openbmc_project.Power.Error.PowerSequencerPGOODFault";
+ return;
+ }
+ }
+ }
+ }
+}
+
+void UCD90xMonitor::onFailureCheckRails(
+ std::string& message, std::map<std::string, std::string>& additionalData,
+ const std::string& powerSupplyError)
+{
+ auto statusWord = readStatusWord();
+ additionalData.emplace("STATUS_WORD", fmt::format("{:#06x}", statusWord));
+ try
+ {
+ additionalData.emplace("MFR_STATUS",
+ fmt::format("{:#014x}", readMFRStatus()));
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Error when collecting MFR_STATUS, error: {}", e.what())
+ .c_str());
+ additionalData.emplace("ERROR", e.what());
+ }
+
+ // The status_word register has a summary bit to tell us if each page even
+ // needs to be checked
+ if (statusWord & status_word::VOUT_FAULT)
+ {
+ for (size_t page = 0; page < numberPages; page++)
+ {
+ auto statusVout = pmbusInterface.insertPageNum(STATUS_VOUT, page);
+ if (pmbusInterface.exists(statusVout, Type::Debug))
+ {
+ uint8_t vout = pmbusInterface.read(statusVout, Type::Debug);
+
+ if (vout)
+ {
+ // If any bits are on log them, though some are just
+ // warnings so they won't cause errors
+ log<level::INFO>(
+ fmt::format("{}, value: {:#04x}", statusVout, vout)
+ .c_str());
+
+ // Log errors if any non-warning bits on
+ if (vout & ~status_vout::WARNING_MASK)
+ {
+ additionalData.emplace(
+ fmt::format("STATUS{}_VOUT", page),
+ fmt::format("{:#04x}", vout));
+
+ // Base the callouts on the first present vout failure
+ // found
+ if (message.empty() && (page < rails.size()) &&
+ isPresent(rails[page].presence))
+ {
+ additionalData.emplace("RAIL_NAME",
+ rails[page].name);
+
+ // Use power supply error if set and 12v rail has
+ // failed, else use voltage error
+ message =
+ ((page == 0) && !powerSupplyError.empty())
+ ? powerSupplyError
+ : "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
+ }
+ }
+ }
+ }
+ }
+ }
+ // If no vout failure found, but power supply error is set, use power supply
+ // error
+ if (message.empty())
+ {
+ message = powerSupplyError;
+ }
+}
+
+void UCD90xMonitor::parseConfigFile(const std::filesystem::path& pathName)
+{
+ try
+ {
+ log<level::DEBUG>(
+ std::string("Loading configuration file " + pathName.string())
+ .c_str());
+
+ std::ifstream file{pathName};
+ json rootElement = json::parse(file);
+ log<level::DEBUG>(fmt::format("Parsed, root element is_object: {}",
+ rootElement.is_object())
+ .c_str());
+
+ // Parse rail information from config file
+ auto railsIterator = rootElement.find("rails");
+ if (railsIterator != rootElement.end())
+ {
+ for (const auto& railElement : *railsIterator)
+ {
+ log<level::DEBUG>(fmt::format("Rail element is_object: {}",
+ railElement.is_object())
+ .c_str());
+
+ auto nameIterator = railElement.find("name");
+ if (nameIterator != railElement.end())
+ {
+ log<level::DEBUG>(fmt::format("Name element is_string: {}",
+ (*nameIterator).is_string())
+ .c_str());
+ Rail rail;
+ rail.name = (*nameIterator).get<std::string>();
+
+ // Presence element is optional
+ auto presenceIterator = railElement.find("presence");
+ if (presenceIterator != railElement.end())
+ {
+ log<level::DEBUG>(
+ fmt::format("Presence element is_string: {}",
+ (*presenceIterator).is_string())
+ .c_str());
+
+ rail.presence = (*presenceIterator).get<std::string>();
+ }
+
+ log<level::DEBUG>(
+ fmt::format("Adding rail, name: {}, presence: {}",
+ rail.name, rail.presence)
+ .c_str());
+ rails.emplace_back(std::move(rail));
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format(
+ "No name found within rail in configuration file: {}",
+ pathName.string())
+ .c_str());
+ }
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("No rails found in configuration file: {}",
+ pathName.string())
+ .c_str());
+ }
+ log<level::DEBUG>(
+ fmt::format("Found number of rails: {}", rails.size()).c_str());
+
+ // Parse pin information from config file
+ auto pinsIterator = rootElement.find("pins");
+ if (pinsIterator != rootElement.end())
+ {
+ for (const auto& pinElement : *pinsIterator)
+ {
+ log<level::DEBUG>(fmt::format("Pin element is_object: {}",
+ pinElement.is_object())
+ .c_str());
+ auto nameIterator = pinElement.find("name");
+ auto lineIterator = pinElement.find("line");
+ if (nameIterator != pinElement.end() &&
+ lineIterator != pinElement.end())
+ {
+ log<level::DEBUG>(fmt::format("Name element is_string: {}",
+ (*nameIterator).is_string())
+ .c_str());
+ log<level::DEBUG>(
+ fmt::format("Line element is_number_integer: {}",
+ (*lineIterator).is_number_integer())
+ .c_str());
+ Pin pin;
+ pin.name = (*nameIterator).get<std::string>();
+ pin.line = (*lineIterator).get<unsigned int>();
+
+ // Presence element is optional
+ auto presenceIterator = pinElement.find("presence");
+ if (presenceIterator != pinElement.end())
+ {
+ log<level::DEBUG>(
+ fmt::format("Presence element is_string: {}",
+ (*presenceIterator).is_string())
+ .c_str());
+ pin.presence = (*presenceIterator).get<std::string>();
+ }
+
+ log<level::DEBUG>(
+ fmt::format(
+ "Adding pin, name: {}, line: {}, presence: {}",
+ pin.name, pin.line, pin.presence)
+ .c_str());
+ pins.emplace_back(std::move(pin));
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format(
+ "No name or line found within pin in configuration file: {}",
+ pathName.string())
+ .c_str());
+ }
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ fmt::format("No pins found in configuration file: {}",
+ pathName.string())
+ .c_str());
+ }
+ log<level::DEBUG>(
+ fmt::format("Found number of pins: {}", pins.size()).c_str());
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(
+ fmt::format("Error parsing configuration file, error: {}", e.what())
+ .c_str());
+ }
+}
+
+uint16_t UCD90xMonitor::readStatusWord()
+{
+ return pmbusInterface.read(STATUS_WORD, Type::Debug);
+}
+
+uint64_t UCD90xMonitor::readMFRStatus()
+{
+ const std::string mfrStatus = "mfr_status";
+ return pmbusInterface.read(mfrStatus, Type::HwmonDeviceDebug);
+}
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90x_monitor.hpp b/phosphor-power-sequencer/src/ucd90x_monitor.hpp
new file mode 100644
index 0000000..f52046c
--- /dev/null
+++ b/phosphor-power-sequencer/src/ucd90x_monitor.hpp
@@ -0,0 +1,178 @@
+#pragma once
+
+#include "pmbus.hpp"
+#include "power_sequencer_monitor.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <cstdint>
+#include <filesystem>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace phosphor::power::sequencer
+{
+
+struct Pin
+{
+ std::string name;
+ unsigned int line;
+ std::string presence;
+};
+
+struct Rail
+{
+ std::string name;
+ std::string presence;
+};
+
+/**
+ * @class UCD90xMonitor
+ * Define a base class for monitoring the UCD90* family of power sequencer
+ * devices.
+ */
+class UCD90xMonitor : public PowerSequencerMonitor
+{
+ public:
+ UCD90xMonitor() = delete;
+ UCD90xMonitor(const UCD90xMonitor&) = delete;
+ UCD90xMonitor& operator=(const UCD90xMonitor&) = delete;
+ UCD90xMonitor(UCD90xMonitor&&) = delete;
+ UCD90xMonitor& operator=(UCD90xMonitor&&) = delete;
+ virtual ~UCD90xMonitor() = default;
+
+ /**
+ * Create a base object for UCD90* monitoring.
+ * @param bus D-Bus bus object
+ * @param i2cBus The bus number of the power sequencer device
+ * @param i2cAddress The I2C address of the power sequencer device
+ * @param deviceName The name of the device
+ * @param numberPages The number of pages the PMBus device supports
+ */
+ UCD90xMonitor(sdbusplus::bus_t& bus, std::uint8_t i2cBus,
+ std::uint16_t i2cAddress, const std::string& deviceName,
+ size_t numberPages);
+
+ /**
+ * Callback function to handle interfacesAdded D-Bus signals
+ * @param msg Expanded sdbusplus message data
+ */
+ void interfacesAddedHandler(sdbusplus::message_t& msg);
+
+ /** @copydoc PowerSequencerMonitor::onFailure() */
+ void onFailure(bool timeout, const std::string& powerSupplyError) override;
+
+ protected:
+ /**
+ * Formats the GPIO values read from the device.
+ * @param values List of GPIO values
+ * @param numberLines Number of GPIO lines
+ * @param additionalData AdditionalData property of the error log entry
+ */
+ virtual void formatGpioValues(
+ const std::vector<int>& values, unsigned int numberLines,
+ std::map<std::string, std::string>& additionalData) const;
+
+ private:
+ /**
+ * Device name
+ */
+ std::string deviceName;
+
+ /**
+ * The match to Entity Manager interfaces added.
+ */
+ sdbusplus::bus::match_t match;
+
+ /**
+ * The number of pages the PMBus device supports
+ */
+ size_t numberPages;
+
+ /**
+ * List of pins
+ */
+ std::vector<Pin> pins;
+
+ /**
+ * The read/write interface to this hardware
+ */
+ pmbus::PMBus pmbusInterface;
+
+ /**
+ * List of rails
+ */
+ std::vector<Rail> rails;
+
+ /**
+ * Finds the list of compatible system types using D-Bus methods.
+ * This list is used to find the correct JSON configuration file for the
+ * current system.
+ */
+ void findCompatibleSystemTypes();
+
+ /**
+ * Finds the JSON configuration file.
+ * Looks for a configuration file based on the list of compatible system
+ * types.
+ * Throws an exception if an operating system error occurs while checking
+ * for the existance of a file.
+ * @param compatibleSystemTypes List of compatible system types
+ */
+ void findConfigFile(const std::vector<std::string>& compatibleSystemTypes);
+
+ /**
+ * Returns whether the hardware with the specified inventory path is
+ * present.
+ * If an error occurs while obtaining the presence value, presence is
+ * assumed to be false. An empty string path indicates no presence check is
+ * needed.
+ * @param inventoryPath D-Bus inventory path of the hardware
+ * @return true if hardware is present, false otherwise
+ */
+ bool isPresent(const std::string& inventoryPath);
+
+ /**
+ * Analyzes the device pins for errors when the device is known to be in an
+ * error state.
+ * @param message Message property of the error log entry
+ * @param additionalData AdditionalData property of the error log entry
+ */
+ void onFailureCheckPins(std::string& message,
+ std::map<std::string, std::string>& additionalData);
+
+ /**
+ * Analyzes the device rails for errors when the device is known to be in an
+ * error state.
+ * @param message Message property of the error log entry
+ * @param additionalData AdditionalData property of the error log entry
+ * @param powerSupplyError The power supply error to log. A default
+ * std:string, i.e. empty string (""), is passed when there is no power
+ * supply error to log.
+ */
+ void onFailureCheckRails(std::string& message,
+ std::map<std::string, std::string>& additionalData,
+ const std::string& powerSupplyError);
+
+ /**
+ * Parse the JSON configuration file.
+ * @param pathName the path name
+ */
+ void parseConfigFile(const std::filesystem::path& pathName);
+
+ /**
+ * Reads the mfr_status register
+ * @return the register contents
+ */
+ uint64_t readMFRStatus();
+
+ /**
+ * Reads the status_word register
+ * @return the register contents
+ */
+ uint16_t readStatusWord();
+};
+
+} // namespace phosphor::power::sequencer