chassis-psu: New Functions for MultiChassis App

Implemented several functions for monitoring power supplies in
multi-chassis systems. Added a new main function, made minor
modification to the PowerSupply class, and added several functions to
the Chassis and ChassisManager classes.

The following is a summary of each object class new addition or modified
functions:

ChassisManager:
  - initChassisPowerMonitoring(): Loops through all the chassis in the
    system and initializes power monitoring process for each chassis's
    PSUs.

Chassis:
 - initPowerMonitoring(): Subscribe to D-Bus power change and initialize
   power monitoring.
 - supportedConfigurationInterfaceAdded(): Handle addition of supported
   configuration and update missing PSUs.
 - psuInterfaceAdded(): Handle addition of PSUs on D-Bus.
 - validatePsuConfigAndInterfacesProcessed(): Validate the PSU
   configuration and reset validation timer if power is on, supported
   configs and have PSUs.
 - analyzeBrownout(): Analyze PSUs for a brownout failure and log error.
 - syncHistory(): Toggles the GPIO to sync power supply input history
   readings.
 - setInputVoltageRating(): Inform each PSUs to set its PSU input.
 - createError(): Create OpenBMC error.
 - hasRequiredPSUs(): TODO
 - updateMissingPSUs(): Update PSU inventory.
 - getSupportedConfiguration(): Retrieve supported configuration from
   D-BUS and matches chassis ID with the  current chassis id to update
   chassis configuration.
 - saveChassisName(): Save chassis short name in the class.
 - powerStateChanged(): Handle for power state property changes.
 - attemptToCreatePowerConfigGPIO(): Attempt to create GPIO

 PowerSupply:
  - PowerSupply(): Added additional class constructors. The constructors
    have the same parameters as the original, except that the new
    constructor include an extra parameter chassis short name.
 - The PowerSupply class functions implementation remains the same as
   the original, except for minor change in
   PowerSupply::setupInputPowerPeakSensor(), where the sensorPath was
   modified to include chassis name.

Test in simulation:
  - Verified supported configuration added, where it polpulates
    supported properties and updates PSUs changes.
  - Validated some of the brownout functionality using fault injection.
  - Validated the PowerSupply class sets the appropriate input voltage
    for the target chassis PSUs.
  - Verified that the chassis name is included in the PSU power input
    peak sensors on D-bus.

Change-Id: I75a1ab1dd004767f072e35f3ce2c83ff281eb1ca
Signed-off-by: Faisal Awada <faisal@us.ibm.com>
diff --git a/phosphor-power-supply/new_power_supply.hpp b/phosphor-power-supply/new_power_supply.hpp
new file mode 100644
index 0000000..162a4f8
--- /dev/null
+++ b/phosphor-power-supply/new_power_supply.hpp
@@ -0,0 +1,1099 @@
+#pragma once
+
+#include "pmbus.hpp"
+#include "types.hpp"
+#include "util.hpp"
+#include "utility.hpp"
+
+#include <gpiod.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
+#include <xyz/openbmc_project/Sensor/Value/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/Availability/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/OperationalStatus/server.hpp>
+
+#include <filesystem>
+#include <stdexcept>
+
+namespace phosphor::power::psu
+{
+
+#if IBM_VPD
+// PMBus device driver "file name" to read for CCIN value.
+constexpr auto CCIN = "ccin";
+constexpr auto PART_NUMBER = "mfr_revision";
+constexpr auto FRU_NUMBER = "mfr_model";
+constexpr auto SERIAL_HEADER = "mfr_location";
+constexpr auto SERIAL_NUMBER = "mfr_serial";
+constexpr auto FW_VERSION = "fw_version";
+
+// The D-Bus property name to update with the CCIN value.
+constexpr auto MODEL_PROP = "Model";
+constexpr auto PN_PROP = "PartNumber";
+constexpr auto SPARE_PN_PROP = "SparePartNumber";
+constexpr auto SN_PROP = "SerialNumber";
+constexpr auto VERSION_PROP = "Version";
+
+// ipzVPD Keyword sizes
+static constexpr auto FL_KW_SIZE = 20;
+static constexpr auto FN_KW_SIZE = 7;
+static constexpr auto PN_KW_SIZE = 7;
+// For IBM power supplies, the SN is 6-byte header + 6-byte serial.
+static constexpr auto SN_KW_SIZE = 12;
+static constexpr auto CC_KW_SIZE = 4;
+#endif
+
+constexpr auto LOG_LIMIT = 3;
+constexpr auto DEGLITCH_LIMIT = 3;
+constexpr auto PGOOD_DEGLITCH_LIMIT = 5;
+// Number of polls to remember that an AC fault occured. Should remain greater
+// than PGOOD_DEGLITCH_LIMIT.
+constexpr auto AC_FAULT_LIMIT = 6;
+
+constexpr auto IBMCFFPS_DD_NAME = "ibm-cffps";
+constexpr auto ACBEL_FSG032_DD_NAME = "acbel-fsg032";
+
+using AvailabilityInterface =
+    sdbusplus::xyz::openbmc_project::State::Decorator::server::Availability;
+using OperationalStatusInterface = sdbusplus::xyz::openbmc_project::State::
+    Decorator::server::OperationalStatus;
+using AssocDefInterface =
+    sdbusplus::xyz::openbmc_project::Association::server::Definitions;
+using SensorInterface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
+using SensorObject = sdbusplus::server::object_t<SensorInterface>;
+using PowerSensorObject =
+    sdbusplus::server::object_t<SensorInterface, OperationalStatusInterface,
+                                AvailabilityInterface, AssocDefInterface>;
+
+using AssociationTuple = std::tuple<std::string, std::string, std::string>;
+
+/**
+ * @class PowerSupply
+ * Represents a PMBus power supply device.
+ */
+class PowerSupply
+{
+  public:
+    PowerSupply() = delete;
+    PowerSupply(const PowerSupply&) = delete;
+    PowerSupply(PowerSupply&&) = delete;
+    PowerSupply& operator=(const PowerSupply&) = delete;
+    PowerSupply& operator=(PowerSupply&&) = delete;
+    ~PowerSupply() = default;
+
+    /**
+     * @param[in] invpath - String for inventory path to use
+     * @param[in] i2cbus - The bus number this power supply is on
+     * @param[in] i2caddr - The 16-bit I2C address of the power supply
+     * @param[in] driver - i2c driver name for power supply
+     * @param[in] gpioLineName - The gpio-line-name to read for presence. See
+     * https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md
+     * @param[in] callback - Get the power on status of the psu manager class
+     */
+    PowerSupply(sdbusplus::bus_t& bus, const std::string& invpath,
+                std::uint8_t i2cbus, const std::uint16_t i2caddr,
+                const std::string& driver, const std::string& gpioLineName,
+                std::function<bool()>&& callback);
+
+    /**
+     * @param[in] invpath - String for inventory path to use
+     * @param[in] i2cbus - The bus number this power supply is on
+     * @param[in] i2caddr - The 16-bit I2C address of the power supply
+     * @param[in] driver - i2c driver name for power supply
+     * @param[in] gpioLineName - The gpio-line-name to read for presence. See
+     * https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md
+     * @param[in] callback - Get the power on status of the psu manager class
+     * @param[in] chassisShortName - Chassis name
+     */
+    PowerSupply(sdbusplus::bus_t& bus, const std::string& invpath,
+                std::uint8_t i2cbus, const std::uint16_t i2caddr,
+                const std::string& driver, const std::string& gpioLineName,
+                std::function<bool()>&& callback,
+                const std::string& chassisShortName);
+
+    phosphor::pmbus::PMBusBase& getPMBus()
+    {
+        return *pmbusIntf;
+    }
+
+    GPIOInterfaceBase* getPresenceGPIO()
+    {
+        return presenceGPIO.get();
+    }
+
+    std::string getPresenceGPIOName() const
+    {
+        if (presenceGPIO != nullptr)
+        {
+            return presenceGPIO->getName();
+        }
+        else
+        {
+            return std::string();
+        }
+    }
+
+    /**
+     * Power supply specific function to analyze for faults/errors.
+     *
+     * Various PMBus status bits will be checked for fault conditions.
+     * If a certain fault bits are on, the appropriate error will be
+     * committed.
+     */
+    void analyze();
+
+    /**
+     * Write PMBus ON_OFF_CONFIG
+     *
+     * This function will be called to cause the PMBus device driver to send the
+     * ON_OFF_CONFIG command. Takes one byte of data.
+     *
+     * @param[in] data - The ON_OFF_CONFIG data byte mask.
+     */
+    void onOffConfig(uint8_t data);
+
+    /**
+     * Clears all the member variables that indicate if a fault bit was seen as
+     * on in the STATUS_WORD or STATUS_MFR_SPECIFIC response.
+     */
+    void clearFaultFlags()
+    {
+        inputFault = 0;
+        mfrFault = 0;
+        statusMFR = 0;
+        vinUVFault = 0;
+        cmlFault = 0;
+        voutOVFault = 0;
+        ioutOCFault = 0;
+        voutUVFault = 0;
+        fanFault = 0;
+        tempFault = 0;
+        pgoodFault = 0;
+        psKillFault = 0;
+        ps12VcsFault = 0;
+        psCS12VFault = 0;
+        faultLogged = false;
+    }
+
+    /**
+     * @brief Function to specifically clear VIN_UV/OFF fault(s).
+     *
+     * The PMBus HWMON device driver has various alarm "files" to read out of
+     * sysfs. Reading those files will indicate if various alarms are active or
+     * not, and then specifically clear those faults that go with that alarm.
+     *
+     * The VIN_UV fault, indicated in STATUS_INPUT, goes with in1_lcrit_alarm.
+     * When a VIN_UV fault occurs, the "Unit Off For Insufficient Input Voltage"
+     * may also be active. Reading in1_lcrit_alarm should clear both fault bits,
+     * resulting in the corresponding fault bits in STATUS_WORD also clearing.
+     *
+     * See: https://www.kernel.org/doc/html/latest/hwmon/pmbus.html
+     */
+    void clearVinUVFault();
+
+    /**
+     * Write PMBus CLEAR_FAULTS
+     *
+     * This function will be called in various situations in order to clear
+     * any fault status bits that may have been set, in order to start over
+     * with a clean state. Presence changes and power state changes will
+     * want to clear any faults logged.
+     */
+    void clearFaults();
+
+    /**
+     * @brief Adds properties to the inventory.
+     *
+     * Reads the values from the device and writes them to the
+     * associated power supply D-Bus inventory object.
+     *
+     * This needs to be done on startup, and each time the presence
+     * state changes.
+     *
+     * Properties added:
+     * - Serial Number
+     * - Part Number
+     * - CCIN (Customer Card Identification Number) - added as the Model
+     * - Firmware version
+     */
+    void updateInventory();
+
+    /**
+     * @brief Accessor function to indicate present status
+     */
+    bool isPresent() const
+    {
+        return present;
+    }
+
+    /**
+     * @brief Returns the last read value from STATUS_WORD.
+     */
+    uint64_t getStatusWord() const
+    {
+        return statusWord;
+    }
+
+    /**
+     * @brief Returns the last read value from STATUS_INPUT.
+     */
+    uint64_t getStatusInput() const
+    {
+        return statusInput;
+    }
+
+    /**
+     * @brief Returns the last read value from STATUS_MFR.
+     */
+    uint64_t getMFRFault() const
+    {
+        return statusMFR;
+    }
+
+    /**
+     * @brief Returns the last read value from STATUS_CML.
+     */
+    uint64_t getStatusCML() const
+    {
+        return statusCML;
+    }
+
+    /**
+     * @brief Returns the last read value from STATUS_VOUT.
+     */
+    uint64_t getStatusVout() const
+    {
+        return statusVout;
+    }
+
+    /**
+     * @brief Returns the last value read from STATUS_IOUT.
+     */
+    uint64_t getStatusIout() const
+    {
+        return statusIout;
+    }
+
+    /**
+     * @brief Returns the last value read from STATUS_FANS_1_2.
+     */
+    uint64_t getStatusFans12() const
+    {
+        return statusFans12;
+    }
+
+    /**
+     * @brief Returns the last value read from STATUS_TEMPERATURE.
+     */
+    uint64_t getStatusTemperature() const
+    {
+        return statusTemperature;
+    }
+
+    /**
+     * @brief Returns true if a fault was found.
+     */
+    bool isFaulted() const
+    {
+        return (hasCommFault() || (vinUVFault >= DEGLITCH_LIMIT) ||
+                (inputFault >= DEGLITCH_LIMIT) ||
+                (voutOVFault >= DEGLITCH_LIMIT) ||
+                (ioutOCFault >= DEGLITCH_LIMIT) ||
+                (voutUVFault >= DEGLITCH_LIMIT) ||
+                (fanFault >= DEGLITCH_LIMIT) || (tempFault >= DEGLITCH_LIMIT) ||
+                (pgoodFault >= PGOOD_DEGLITCH_LIMIT) ||
+                (mfrFault >= DEGLITCH_LIMIT));
+    }
+
+    /**
+     * @brief Return whether a fault has been logged for this power supply
+     */
+    bool isFaultLogged() const
+    {
+        return faultLogged;
+    }
+
+    /**
+     * @brief Called when a fault for this power supply has been logged.
+     */
+    void setFaultLogged()
+    {
+        faultLogged = true;
+    }
+
+    /**
+     * @brief Returns true if INPUT fault occurred.
+     */
+    bool hasInputFault() const
+    {
+        return (inputFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if MFRSPECIFIC occurred.
+     */
+    bool hasMFRFault() const
+    {
+        return (mfrFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if VIN_UV_FAULT occurred.
+     */
+    bool hasVINUVFault() const
+    {
+        return (vinUVFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if VOUT_OV_FAULT occurred.
+     */
+    bool hasVoutOVFault() const
+    {
+        return (voutOVFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if IOUT_OC fault occurred (bit 4 STATUS_BYTE).
+     */
+    bool hasIoutOCFault() const
+    {
+        return (ioutOCFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if VOUT_UV_FAULT occurred.
+     */
+    bool hasVoutUVFault() const
+    {
+        return (voutUVFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     *@brief Returns true if fan fault occurred.
+     */
+    bool hasFanFault() const
+    {
+        return (fanFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if TEMPERATURE fault occurred.
+     */
+    bool hasTempFault() const
+    {
+        return (tempFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if there is a PGood fault (PGOOD# inactive, or OFF
+     * bit on).
+     */
+    bool hasPgoodFault() const
+    {
+        return (pgoodFault >= PGOOD_DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Return true if there is a PS_Kill fault.
+     */
+    bool hasPSKillFault() const
+    {
+        return (psKillFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if there is a 12Vcs (standy power) fault.
+     */
+    bool hasPS12VcsFault() const
+    {
+        return (ps12VcsFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if there is a 12V current-share fault.
+     */
+    bool hasPSCS12VFault() const
+    {
+        return (psCS12VFault >= DEGLITCH_LIMIT);
+    }
+
+    /**
+     * @brief Returns true if an AC fault has occurred in the window of
+     * interest.
+     */
+    bool hasACFault() const
+    {
+        return acFault != 0;
+    }
+
+    /**
+     * @brief Returns the device path
+     *
+     * This can be used for error call outs.
+     * Example: /sys/bus/i2c/devices/3-0068
+     */
+    const std::string getDevicePath() const
+    {
+        return pmbusIntf->path();
+    }
+
+    /**
+     * @brief Returns this power supply's inventory path.
+     *
+     * This can be used for error call outs.
+     * Example:
+     * /xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1
+     */
+    const std::string& getInventoryPath() const
+    {
+        return inventoryPath;
+    }
+
+    /**
+     * @brief Returns the short name (last part of inventoryPath).
+     */
+    const std::string& getShortName() const
+    {
+        return shortName;
+    }
+
+    /**
+     * @brief Returns the firmware revision version read from the power supply
+     */
+    const std::string& getFWVersion() const
+    {
+        return fwVersion;
+    }
+
+    /**
+     * @brief Returns the model name of the power supply
+     */
+    const std::string& getModelName() const
+    {
+        return modelName;
+    }
+
+    /**
+     * @brief Returns true if the number of failed reads exceeds limit
+     */
+    bool hasCommFault() const
+    {
+        return (readFail >= LOG_LIMIT);
+    }
+
+    /**
+     * @brief Reads the pmbus input voltage and returns that actual voltage
+     *        reading and the calculated input voltage based on thresholds.
+     * @param[out] actualInputVoltage - The actual voltage reading, in Volts.
+     * @param[out] inputVoltage - A rounded up/down value of the actual input
+     *             voltage based on thresholds, in Volts.
+     */
+    void getInputVoltage(double& actualInputVoltage, int& inputVoltage) const;
+
+    /**
+     * @brief Check if the PS is considered to be available or not
+     *
+     * It is unavailable if any of:
+     * - not present
+     * - input fault active
+     * - Vin UV fault active
+     * - PS KILL fault active
+     * - Iout OC fault active
+     *
+     * Other faults will, through creating error logs with callouts, already
+     * be setting the Functional property to false.
+     *
+     * On changes, the Available property is updated in the inventory.
+     */
+    void checkAvailability();
+
+    /**
+     * @brief Returns true when INPUT_HISTORY sync is required.
+     */
+    bool isSyncHistoryRequired() const
+    {
+        return syncHistoryRequired;
+    }
+
+    /**
+     * @brief Clears the indicator that sync required for INPUT_HISTORY.
+     *
+     * Sets variable to false to indicate that the sync is no longer required.
+     * This can be used after the PSUManager has reacted to the need for the
+     * INPUT_HISTORY data to be synchronized.
+     */
+    void clearSyncHistoryRequired()
+    {
+        syncHistoryRequired = false;
+    }
+
+    /**
+     * @brief Puts the input voltage rating on D-Bus.
+     *
+     * The rating is like 0, 110, 220.
+     */
+    void setInputVoltageRating();
+
+    /**
+     * @brief Returns the peak input power value if there is one,
+     *        otherwise std::nullopt.
+     */
+    std::optional<double> getPeakInputPower() const
+    {
+        std::optional<double> value;
+        if (peakInputPowerSensor)
+        {
+            value = peakInputPowerSensor->value();
+        }
+        return value;
+    }
+
+    /**
+     * @brief Converts a Linear Format power number to an integer
+     *
+     * The PMBus spec describes a 2 byte Linear Format
+     * number that is composed of an exponent and mantissa
+     * in two's complement notation.
+     *
+     * Value = Mantissa * 2**Exponent
+     *
+     * @return double - The converted value
+     */
+    static double linearToInteger(uint16_t data);
+
+    /**
+     * @brief Retrieve device driver name
+     */
+    const std::string& getDriverName() const
+    {
+        return driverName;
+    }
+
+    /**
+     * @brief Set device driver name
+     * @param[in] newDriver - device driver name.
+     */
+    void setDriverName(const std::string& newDriver)
+    {
+        driverName = newDriver;
+    }
+
+  private:
+    /**
+     * @brief Examine STATUS_WORD for CML (communication, memory, logic fault).
+     */
+    void analyzeCMLFault();
+
+    /**
+     * @brief Examine STATUS_WORD for INPUT bit on.
+     *
+     * "An input voltage, input current, or input power fault or warning has
+     * occurred."
+     */
+    void analyzeInputFault();
+
+    /**
+     * @brief Examine STATUS_WORD for VOUT being set.
+     *
+     * If VOUT is on, "An output voltage fault or warning has occurred.", and
+     * VOUT_OV_FAULT is on, there is an output over-voltage fault.
+     */
+    void analyzeVoutOVFault();
+
+    /**
+     * @brief Examine STATUS_WORD value read for IOUT_OC_FAULT.
+     *
+     * "An output overcurrent fault has occurred." If it is on, and fault not
+     * set, trace STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_IOUT values.
+     */
+    void analyzeIoutOCFault();
+
+    /**
+     * @brief Examines STATUS_WORD value read to see if there is a UV fault.
+     *
+     * Checks if the VOUT bit is on, indicating "An output voltage fault or
+     * warning has occurred", if it is on, but VOUT_OV_FAULT is off, it is
+     * determined to be an indication of an output under-voltage fault.
+     */
+    void analyzeVoutUVFault();
+
+    /**
+     * @brief Examine STATUS_WORD for the fan fault/warning bit.
+     *
+     * If fanFault is not on, trace that the bit now came on, include
+     * STATUS_WORD, STATUS_MFR_SPECIFIC, and STATUS_FANS_1_2 values as well, to
+     * help with understanding what may have caused it to be set.
+     */
+    void analyzeFanFault();
+
+    /**
+     * @brief Examine STATUS_WORD for temperature fault.
+     */
+    void analyzeTemperatureFault();
+
+    /**
+     * @brief Examine STATUS_WORD for pgood or unit off faults.
+     */
+    void analyzePgoodFault();
+
+    /**
+     * @brief Determine possible manufacturer-specific faults from bits in
+     * STATUS_MFR.
+     *
+     * The bits in the STATUS_MFR_SPECIFIC command response have "Manufacturer
+     * Defined" meanings. Determine which faults, if any, are present based on
+     * the power supply (device driver) type.
+     */
+    void determineMFRFault();
+
+    /**
+     * @brief Examine STATUS_WORD value read for MFRSPECIFIC bit on.
+     *
+     * "A manufacturer specific fault or warning has occurred."
+     *
+     * If it is on, call the determineMFRFault() helper function to examine the
+     * value read from STATUS_MFR_SPECIFIC.
+     */
+    void analyzeMFRFault();
+
+    /**
+     * @brief Analyzes the STATUS_WORD for a VIN_UV_FAULT indicator.
+     */
+    void analyzeVinUVFault();
+
+    /**
+     * @brief Given a full inventory path, returns the last node of the path as
+     * the "short name"
+     */
+    std::string findShortName(const std::string& invPath)
+    {
+        const auto lastSlashPos = invPath.find_last_of('/');
+
+        if ((lastSlashPos == std::string::npos) ||
+            ((lastSlashPos + 1) == invPath.size()))
+        {
+            return invPath;
+        }
+        else
+        {
+            return invPath.substr(lastSlashPos + 1);
+        }
+    }
+
+    /**
+     * @brief Binds or unbinds the power supply device driver
+     *
+     * Called when a presence change is detected to either bind the device
+     * driver for the power supply when it is installed, or unbind the device
+     * driver when the power supply is removed.
+     *
+     * Note:
+     *    Bind device when device present and i2cbus-i2caddr does not exist
+     *    UnBind device when device not present and i2cbus-i2caddr  exist
+
+     * Writes <device> to <path>/bind (or unbind)
+     *
+     * @param present - when true, will bind the device driver
+     *                  when false, will unbind the device driver
+     */
+    void bindOrUnbindDriver(bool present);
+
+    /**
+     *  @brief Updates the presence status by querying D-Bus
+     *
+     * The D-Bus inventory properties for this power supply will be read to
+     * determine if the power supply is present or not and update this
+     * object's present member variable to reflect current status.
+     **/
+    void updatePresence();
+
+    /**
+     * @brief Updates the power supply presence by reading the GPIO line.
+     */
+    void updatePresenceGPIO();
+
+    /**
+     * @brief Callback for inventory property changes
+     *
+     * Process change of Present property for power supply.
+     *
+     * This is used if we are watching the D-Bus properties instead of reading
+     * the GPIO presence line ourselves.
+     *
+     * @param[in]  msg - Data associated with Present change signal
+     **/
+    void inventoryChanged(sdbusplus::message_t& msg);
+
+    /**
+     * @brief Callback for inventory property added.
+     *
+     * Process add of the interface with the Present property for power supply.
+     *
+     * This is used if we are watching the D-Bus properties instead of reading
+     * the GPIO presence line ourselves.
+     *
+     * @param[in]  msg - Data associated with Present add signal
+     **/
+    void inventoryAdded(sdbusplus::message_t& msg);
+
+    /**
+     * @brief Reads the pmbus MFR_POUT_MAX value.
+     *
+     * "The MFR_POUT_MAX command sets or retrieves the maximum rated output
+     * power, in watts, that the unit is rated to supply."
+     *
+     * @return max_power_out value converted from string.
+     */
+    auto getMaxPowerOut() const;
+
+    /**
+     * @brief Reads a VPD value from PMBus, correct size, and contents.
+     *
+     * If the VPD data read is not the passed in size, resize and fill with
+     * spaces. If the data contains a non-alphanumeric value, replace any of
+     * those values with spaces.
+     *
+     * @param[in] vpdName - The name of the sysfs "file" to read data from.
+     * @param[in] type - The HWMON file type to read from.
+     * @param[in] vpdSize - The expacted size of the data for this VPD/property
+     *
+     * @return A string containing the VPD data read, resized if necessary
+     */
+    auto readVPDValue(const std::string& vpdName,
+                      const phosphor::pmbus::Type& type,
+                      const std::size_t& vpdSize);
+
+    /**
+     * @brief Retrieve PSU VPD keyword from D-Bus
+     *
+     * It retrieves PSU VPD keyword from D-Bus and assign the associated
+     * string to vpdStr.
+     * @param[in] keyword - The VPD search keyword
+     * @param[out] vpdStr - The VPD string associated with the keyword.
+     */
+    void getPsuVpdFromDbus(const std::string& keyword, std::string& vpdStr);
+
+    /**
+     * @brief Creates the appropriate sensor D-Bus objects.
+     */
+    void setupSensors();
+
+    /**
+     * @brief Monitors sensor values and updates D-Bus.
+     *        Called from analyze().
+     */
+    void monitorSensors();
+
+    /**
+     * @brief Creates the peak input power sensor D-Bus object
+     *        if the PS supports it.
+     */
+    void setupInputPowerPeakSensor();
+
+    /**
+     * @brief Monitors the peak input power sensor
+     */
+    void monitorPeakInputPowerSensor();
+
+    /**
+     * @brief Sets any sensor objects to Available = false on D-Bus.
+     */
+    void setSensorsNotAvailable();
+
+    /**
+     * @brief Returns the associations to create for a sensor on this
+     *        power supply.
+     */
+    std::vector<AssociationTuple> getSensorAssociations();
+
+    /**
+     * @brief systemd bus member
+     */
+    sdbusplus::bus_t& bus;
+
+    /**
+     * @brief D-Bus path to use for this power supply's inventory status.
+     **/
+    std::string inventoryPath;
+
+    /**
+     * @brief The file system path used for binding the device driver.
+     */
+    std::filesystem::path bindPath;
+
+    /**
+     * @brief Get the power on status of the psu manager class.
+     *
+     * This is a callback method used to get the power on status of the psu
+     * manager class.
+     */
+    std::function<bool()> isPowerOn;
+
+    /**
+     * @brief Set to true when INPUT_HISTORY sync is required.
+     *
+     * A power supply will need to synchronize its INPUT_HISTORY data with the
+     * other power supplies installed in the system when it goes from missing to
+     * present.
+     */
+    bool syncHistoryRequired{false};
+
+    /**
+     * @brief Store the short name to avoid string processing.
+     *
+     * The short name will be something like powersupply1, the last part of the
+     * inventoryPath.
+     */
+    std::string shortName;
+
+    /**
+     * @brief The libgpiod object for monitoring PSU presence
+     */
+    std::unique_ptr<GPIOInterfaceBase> presenceGPIO = nullptr;
+
+    /**
+     * @brief True if the power supply is present.
+     */
+    bool present = false;
+
+    /**
+     * @brief Power supply model name.
+     */
+    std::string modelName;
+
+    /**
+     * @brief D-Bus match variable used to subscribe to Present property
+     * changes.
+     **/
+    std::unique_ptr<sdbusplus::bus::match_t> presentMatch;
+
+    /**
+     * @brief D-Bus match variable used to subscribe for Present property
+     * interface added.
+     */
+    std::unique_ptr<sdbusplus::bus::match_t> presentAddedMatch;
+
+    /**
+     * @brief Pointer to the PMBus interface
+     *
+     * Used to read or write to/from PMBus power supply devices.
+     */
+    std::unique_ptr<phosphor::pmbus::PMBusBase> pmbusIntf = nullptr;
+
+    /**
+     * @brief Stored copy of the firmware version/revision string
+     */
+    std::string fwVersion;
+
+    /**
+     * @brief The string to pass in for binding the device driver.
+     */
+    std::string bindDevice;
+
+    /**
+     * @brief The result of the most recent availability check
+     *
+     * Saved on the object so changes can be detected.
+     */
+    bool available = false;
+
+    /**
+     * @brief Will be updated to the latest/lastvalue read from STATUS_WORD.
+     */
+    uint64_t statusWord = 0;
+
+    /**
+     * @brief Will be set to the last read value of STATUS_WORD.
+     */
+    uint64_t statusWordOld = 0;
+
+    /**
+     * @brief Will be updated to the latest/lastvalue read from STATUS_INPUT.
+     */
+    uint64_t statusInput = 0;
+
+    /**
+     * @brief Will be updated to the latest/lastvalue read from STATUS_MFR.
+     */
+    uint64_t statusMFR = 0;
+
+    /**
+     * @brief Will be updated to the latest/last value read from STATUS_CML.
+     */
+    uint64_t statusCML = 0;
+
+    /**
+     * @brief Will be updated to the latest/last value read from STATUS_VOUT.
+     */
+    uint64_t statusVout = 0;
+
+    /**
+     * @brief Will be updated to the latest/last value read from STATUS_IOUT.
+     */
+    uint64_t statusIout = 0;
+
+    /**
+     * @brief Will be updated to the latest/last value read from
+     * STATUS_FANS_1_2.
+     */
+    uint64_t statusFans12 = 0;
+
+    /**
+     * @brief Will be updated to the latest/last value read from
+     * STATUS_TEMPERATURE.
+     */
+    uint64_t statusTemperature = 0;
+
+    /**
+     * @brief Will be updated with latest converted value read from READ_VIN
+     */
+    int inputVoltage = phosphor::pmbus::in_input::VIN_VOLTAGE_0;
+
+    /**
+     * @brief Will be updated with the actual voltage last read from READ_VIN
+     */
+    double actualInputVoltage = 0;
+
+    /**
+     * @brief True if an error for a fault has already been logged.
+     */
+    bool faultLogged = false;
+
+    /**
+     * @brief Incremented if bit 1 of STATUS_WORD low byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t cmlFault = 0;
+
+    /**
+     * @brief Incremented if bit 5 of STATUS_WORD high byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t inputFault = 0;
+
+    /**
+     * @brief Incremented if bit 4 of STATUS_WORD high byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t mfrFault = 0;
+
+    /**
+     * @brief Incremented if bit 3 of STATUS_WORD low byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t vinUVFault = 0;
+
+    /**
+     * @brief Incremented if bit 5 of STATUS_WORD low byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t voutOVFault = 0;
+
+    /**
+     * @brief Incremented if bit 4 of STATUS_WORD low byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t ioutOCFault = 0;
+
+    /**
+     * @brief Incremented if bit 7 of STATUS_WORD high byte is on and bit 5
+     * (VOUT_OV) of low byte is off.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t voutUVFault = 0;
+
+    /**
+     * @brief Incremented if FANS fault/warn bit on in STATUS_WORD.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t fanFault = 0;
+
+    /**
+     * @brief Incremented if bit 2 of STATUS_WORD low byte is on.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t tempFault = 0;
+
+    /**
+     * @brief Incremented if bit 11 or 6 of STATUS_WORD is on. PGOOD# is
+     * inactive, or the unit is off.
+     *
+     * Considered faulted if reaches DEGLITCH_LIMIT.
+     */
+    size_t pgoodFault = 0;
+
+    /**
+     * @brief Power Supply Kill fault.
+     *
+     * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
+     * bit 4 to indicate this fault. Considered faulted if it reaches
+     * DEGLITCH_LIMIT.
+     */
+    size_t psKillFault = 0;
+
+    /**
+     * @brief Power Supply 12Vcs fault (standby power).
+     *
+     * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
+     * bit 6 to indicate this fault. Considered faulted if it reaches
+     * DEGLITCH_LIMIT.
+     */
+    size_t ps12VcsFault = 0;
+
+    /**
+     * @brief Power Supply Current-Share fault in 12V domain.
+     *
+     * Incremented based on bits in STATUS_MFR_SPECIFIC. IBM power supplies use
+     * bit 7 to indicate this fault. Considered faulted if it reaches
+     * DEGLITCH_LIMIT.
+     */
+    size_t psCS12VFault = 0;
+
+    /**
+     * @brief Set to AC_FAULT_LIMIT when AC fault is detected, decremented when
+     * AC fault has cleared. Effectively forms a timer since last AC failure.
+     * Zero indicates being outside the window of concern.
+     */
+    size_t acFault = 0;
+
+    /**
+     * @brief Count of the number of read failures.
+     */
+    size_t readFail = 0;
+
+    /**
+     * @brief The D-Bus object for the input voltage rating
+     *
+     * It is updated at startup and power on.  If a power supply is
+     * added or removed after that, it does not need to be updated
+     * again (though that could be done as a future improvement).
+     */
+    std::unique_ptr<SensorObject> inputVoltageRatingIface;
+
+    /**
+     * @brief The D-Bus object for the peak input power sensor.
+     */
+    std::unique_ptr<PowerSensorObject> peakInputPowerSensor;
+
+    /**
+     * @brief The device driver name
+     */
+    std::string driverName;
+
+    /**
+     * @brief The chassis unique name
+     */
+    std::string chassisName;
+};
+
+} // namespace phosphor::power::psu