psu-ng: Power supply class updates for input history

Update the meson files to include the record_manager with the
phosphor-psu-monitor application.

Since we do not want to blindly enable input history for all power
supplies, base the enablement of the feature off of the driver name.
Change the PowerSupply class to require the driver name be passed in,
and pass that down via the PSUManager during the configuration
determination.

Add a server manager to the PSUManager to handle the INPUT HISTORY data
that will be under /org/open_power/sensors.

The INPUT_HISTORY command is handled via a sysfs file in binary format,
so add in a readBinary() base function to allow for mock testing.

Change-Id: Iea163892d5482e6f2dacacfbfa746f605af52ed5
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/phosphor-power-supply/meson.build b/phosphor-power-supply/meson.build
index bb90aea..a222ac8 100644
--- a/phosphor-power-supply/meson.build
+++ b/phosphor-power-supply/meson.build
@@ -7,6 +7,7 @@
     'main.cpp',
     'psu_manager.cpp',
     'power_supply.cpp',
+    'record_manager.cpp',
     'util.cpp',
     dependencies: [
         sdbusplus,
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index a7e3579..254c9c7 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -21,14 +21,21 @@
 // missing to present before running the bind command(s).
 constexpr auto bindDelay = 1000;
 
+// The number of INPUT_HISTORY records to keep on D-Bus.
+// Each record covers a 30-second span. That means two records are needed to
+// cover a minute of time. If we want one (1) hour of data, that would be 120
+// records.
+constexpr auto INPUT_HISTORY_MAX_RECORDS = 120;
+
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
 
 PowerSupply::PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
                          std::uint8_t i2cbus, std::uint16_t i2caddr,
+                         const std::string& driver,
                          const std::string& gpioLineName) :
     bus(bus),
-    inventoryPath(invpath), bindPath("/sys/bus/i2c/drivers/ibm-cffps")
+    inventoryPath(invpath), bindPath("/sys/bus/i2c/drivers/" + driver)
 {
     if (inventoryPath.empty())
     {
@@ -84,6 +91,7 @@
 
         updatePresence();
         updateInventory();
+        setupInputHistory();
     }
 }
 
@@ -186,6 +194,7 @@
         }
 
         setPresence(bus, invpath, present, shortName);
+        setupInputHistory();
         updateInventory();
 
         // Need Functional to already be correct before calling this.
@@ -635,6 +644,11 @@
             }
 
             checkAvailability();
+
+            if (inputHistorySupported)
+            {
+                updateHistory();
+            }
         }
         catch (const ReadFailure& e)
         {
@@ -954,6 +968,86 @@
     }
 }
 
+void PowerSupply::setupInputHistory()
+{
+    if (bindPath.string().find("ibm-cffps") != std::string::npos)
+    {
+        // Do not enable input history for power supplies that are missing
+        if (present)
+        {
+            inputHistorySupported = true;
+            log<level::INFO>(
+                fmt::format("{} INPUT_HISTORY enabled", shortName).c_str());
+
+            std::string name{fmt::format("{}_input_power", shortName)};
+
+            historyObjectPath =
+                std::string{INPUT_HISTORY_SENSOR_ROOT} + '/' + name;
+
+            // If the power supply was present, we created the recordManager.
+            // If it then went missing, the recordManager is still there.
+            // If it then is reinserted, we should be able to use the
+            // recordManager that was allocated when it was initially present.
+            if (!recordManager)
+            {
+                recordManager = std::make_unique<history::RecordManager>(
+                    INPUT_HISTORY_MAX_RECORDS);
+            }
+
+            if (!average)
+            {
+                auto avgPath = historyObjectPath + '/' + history::Average::name;
+                average = std::make_unique<history::Average>(bus, avgPath);
+                log<level::DEBUG>(
+                    fmt::format("{} avgPath: {}", shortName, avgPath).c_str());
+            }
+            if (!maximum)
+            {
+                auto maxPath = historyObjectPath + '/' + history::Maximum::name;
+                maximum = std::make_unique<history::Maximum>(bus, maxPath);
+                log<level::DEBUG>(
+                    fmt::format("{} maxPath: {}", shortName, maxPath).c_str());
+            }
+
+            log<level::DEBUG>(fmt::format("{} historyObjectPath: {}", shortName,
+                                          historyObjectPath)
+                                  .c_str());
+        }
+    }
+    else
+    {
+        inputHistorySupported = false;
+    }
+}
+
+void PowerSupply::updateHistory()
+{
+    if (!recordManager)
+    {
+        // Not enabled
+        return;
+    }
+
+    if (!present)
+    {
+        // Cannot read when not present
+        return;
+    }
+
+    // Read just the most recent average/max record
+    auto data =
+        pmbusIntf->readBinary(INPUT_HISTORY, pmbus::Type::HwmonDeviceDebug,
+                              history::RecordManager::RAW_RECORD_SIZE);
+
+    // Update D-Bus only if something changed (a new record ID, or cleared out)
+    auto changed = recordManager->add(data);
+    if (changed)
+    {
+        average->values(std::move(recordManager->getAverageRecords()));
+        maximum->values(std::move(recordManager->getMaximumRecords()));
+    }
+}
+
 void PowerSupply::getInputVoltage(double& actualInputVoltage,
                                   int& inputVoltage) const
 {
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index 6f1044c..b81599b 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -1,6 +1,9 @@
 #pragma once
 
+#include "average.hpp"
+#include "maximum.hpp"
 #include "pmbus.hpp"
+#include "record_manager.hpp"
 #include "types.hpp"
 #include "util.hpp"
 #include "utility.hpp"
@@ -55,12 +58,13 @@
      * @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
      */
     PowerSupply(sdbusplus::bus::bus& bus, const std::string& invpath,
                 std::uint8_t i2cbus, const std::uint16_t i2caddr,
-                const std::string& gpioLineName);
+                const std::string& driver, const std::string& gpioLineName);
 
     phosphor::pmbus::PMBusBase& getPMBus()
     {
@@ -448,6 +452,42 @@
      */
     void checkAvailability();
 
+    /**
+     * @brief Setup for power supply input history.
+     *
+     * This will setup the variables and interfaces needed to get the power
+     * supply input history data over to D-Bus. The only known support for this
+     * at this time is the INPUT_HISTORY command implemented by the IBM Common
+     * Form Factor Power Suppplies (ibm-cffps). The INPUT_HISTORY command for
+     * ibm-cffps is implemented via a manufacturing specific PMBus command.
+     */
+    void setupInputHistory();
+
+    /**
+     * @brief Returns true if this power supply has input history (supported).
+     */
+    bool hasInputHistory() const
+    {
+        return inputHistorySupported;
+    }
+
+    /**
+     * @brief Returns the number of input history records
+     *
+     * PowerSupply wrapper to getNumRecords() from RecordManager.
+     */
+    size_t getNumInputHistoryRecords() const
+    {
+        if (recordManager)
+        {
+            return recordManager->getNumRecords();
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
   private:
     /** @brief systemd bus member */
     sdbusplus::bus::bus& bus;
@@ -794,6 +834,49 @@
      * @param[in]  msg - Data associated with Present add signal
      **/
     void inventoryAdded(sdbusplus::message::message& msg);
+
+    /**
+     * @brief Reads the most recent input history record from the power supply
+     * and updates the average and maximum properties in D-Bus if there is a new
+     * reading available.
+     *
+     * This will still run every time analyze() is called so code can post new
+     * data as soon as possible and the timestamp will more accurately reflect
+     * the correct time.
+     *
+     * D-Bus is only updated if there is a change and the oldest record will be
+     * pruned if the property already contains the max number of records.
+     */
+    void updateHistory();
+
+    /**
+     * @brief Set to true if INPUT_HISTORY command supported.
+     *
+     * Not all power supplies will support the INPUT_HISTORY command. The IBM
+     * Common Form Factor power supplies do support this command.
+     */
+    bool inputHistorySupported{false};
+
+    /**
+     * @brief Class that manages the input power history records.
+     **/
+    std::unique_ptr<history::RecordManager> recordManager;
+
+    /**
+     * @brief The D-Bus object for the average input power history
+     **/
+    std::unique_ptr<history::Average> average;
+
+    /**
+     * @brief The D-Bus object for the maximum input power history
+     **/
+    std::unique_ptr<history::Maximum> maximum;
+
+    /**
+     * @brief The base D-Bus object path to use for the average and maximum
+     * objects.
+     **/
+    std::string historyObjectPath;
 };
 
 } // namespace phosphor::power::psu
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index a8d0d55..acc3c2b 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -30,7 +30,8 @@
 
 PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e) :
     bus(bus), powerSystemInputs(bus, powerSystemsInputsObjPath),
-    objectManager(bus, objectManagerObjPath)
+    objectManager(bus, objectManagerObjPath),
+    historyManager(bus, "/org/open_power/sensors")
 {
     // Subscribe to InterfacesAdded before doing a property read, otherwise
     // the interface could be created after the read attempt but before the
@@ -180,12 +181,14 @@
             return;
         }
 
+        constexpr auto driver = "ibm-cffps";
         log<level::DEBUG>(
-            fmt::format("make PowerSupply bus: {} addr: {} presline: {}",
-                        *i2cbus, *i2caddr, presline)
+            fmt::format(
+                "make PowerSupply bus: {} addr: {} driver: {} presline: {}",
+                *i2cbus, *i2caddr, driver, presline)
                 .c_str());
         auto psu = std::make_unique<PowerSupply>(bus, invpath, *i2cbus,
-                                                 *i2caddr, presline);
+                                                 *i2caddr, driver, presline);
         psus.emplace_back(std::move(psu));
 
         // Subscribe to power supply presence changes
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 02fff3f..8725ce8 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -361,9 +361,22 @@
     PowerSystemInputs powerSystemInputs;
 
     /**
-     * @brief Implement the org.freedesktop.DBus.ObjectManager interface
+     * @brief Implement the ObjectManager for PowerSystemInputs object.
+     *
+     * Implements the org.freedesktop.DBus.ObjectManager interface used to
+     * communicate updates to the PowerSystemInputs object on the
+     * /xyz/openbmc_project/power/power_supplies root D-Bus path.
      */
     sdbusplus::server::manager_t objectManager;
+
+    /**
+     * @brief Implement the ObjectManager for power supply input history.
+     *
+     * Implements the org.freedesktop.DBus.ObjectManager interface used to
+     * communicate updates to the Average and Maximum interface properties on
+     * the /org/open_power/sensors root D-Bus path.
+     */
+    sdbusplus::server::manager_t historyManager;
 };
 
 } // namespace phosphor::power::manager
diff --git a/phosphor-power-supply/test/meson.build b/phosphor-power-supply/test/meson.build
index 0350799..ce429d5 100644
--- a/phosphor-power-supply/test/meson.build
+++ b/phosphor-power-supply/test/meson.build
@@ -1,6 +1,7 @@
 test('phosphor-power-supply-tests',
      executable('phosphor-power-supply-tests',
                 'power_supply_tests.cpp',
+                '../record_manager.cpp',
                 'mock.cpp',
                 dependencies: [
                     gmock,
diff --git a/phosphor-power-supply/test/mock.hpp b/phosphor-power-supply/test/mock.hpp
index edfe7a8..1235c01 100644
--- a/phosphor-power-supply/test/mock.hpp
+++ b/phosphor-power-supply/test/mock.hpp
@@ -24,6 +24,9 @@
                 (override));
     MOCK_METHOD(std::string, readString, (const std::string& name, Type type),
                 (override));
+    MOCK_METHOD(std::vector<uint8_t>, readBinary,
+                (const std::string& name, Type type, size_t length),
+                (override));
     MOCK_METHOD(void, writeBinary,
                 (const std::string& name, std::vector<uint8_t> data, Type type),
                 (override));
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 1ce91f9..88caee8 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -1,4 +1,5 @@
 #include "../power_supply.hpp"
+#include "../record_manager.hpp"
 #include "mock.hpp"
 
 #include <xyz/openbmc_project/Common/Device/error.hpp>
@@ -132,8 +133,8 @@
     // Try where inventory path is empty, constructor should fail.
     try
     {
-        auto psu =
-            std::make_unique<PowerSupply>(bus, "", 3, 0x68, PSUGPIOLineName);
+        auto psu = std::make_unique<PowerSupply>(bus, "", 3, 0x68, "ibm-cffps",
+                                                 PSUGPIOLineName);
         ADD_FAILURE() << "Should not have reached this line.";
     }
     catch (const std::invalid_argument& e)
@@ -150,8 +151,8 @@
     // Try where gpioLineName is empty.
     try
     {
-        auto psu =
-            std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, "");
+        auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68,
+                                                 "ibm-cffps", "");
         ADD_FAILURE()
             << "Should not have reached this line. Invalid gpioLineName.";
     }
@@ -169,7 +170,7 @@
     try
     {
         auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68,
-                                                 PSUGPIOLineName);
+                                                 "ibm-cffps", PSUGPIOLineName);
 
         EXPECT_EQ(psu->isPresent(), false);
         EXPECT_EQ(psu->isFaulted(), false);
@@ -217,7 +218,8 @@
         // If I default to reading the GPIO, I will NOT expect a call to
         // getPresence().
 
-        PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
+        PowerSupply psu{bus,  PSUInventoryPath, 4,
+                        0x69, "ibm-cffps",      PSUGPIOLineName};
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
         EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(0));
@@ -241,7 +243,8 @@
         EXPECT_EQ(psu.hasPSCS12VFault(), false);
     }
 
-    PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
+    PowerSupply psu2{bus,  PSUInventoryPath, 5,
+                     0x6a, "ibm-cffps",      PSUGPIOLineName};
     // In order to get the various faults tested, the power supply needs to
     // be present in order to read from the PMBus device(s).
     MockedGPIOInterface* mockPresenceGPIO2 =
@@ -724,7 +727,8 @@
     {
         // Assume GPIO presence, not inventory presence?
         EXPECT_CALL(mockedUtil, setAvailable(_, _, _)).Times(0);
-        PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
+        PowerSupply psu{bus,  PSUInventoryPath, 4,
+                        0x69, "ibm-cffps",      PSUGPIOLineName};
 
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
@@ -743,7 +747,8 @@
     {
         // Assume GPIO presence, not inventory presence?
         EXPECT_CALL(mockedUtil, setAvailable(_, _, true));
-        PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
+        PowerSupply psu{bus,  PSUInventoryPath, 5,
+                        0x6a, "ibm-cffps",      PSUGPIOLineName};
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
         // There will potentially be multiple calls, we want it to continue
@@ -772,7 +777,8 @@
 TEST_F(PowerSupplyTests, ClearFaults)
 {
     auto bus = sdbusplus::bus::new_default();
-    PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 13,
+                    0x68, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -992,7 +998,8 @@
 
     try
     {
-        PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+        PowerSupply psu{bus,  PSUInventoryPath, 3,
+                        0x68, "ibm-cffps",      PSUGPIOLineName};
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // If it is not present, I should not be trying to read a string
         EXPECT_CALL(mockPMBus, readString(_, _)).Times(0);
@@ -1005,7 +1012,8 @@
 
     try
     {
-        PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName};
+        PowerSupply psu{bus,  PSUInventoryPath, 13,
+                        0x69, "ibm-cffps",      PSUGPIOLineName};
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
         // GPIO read return 1 to indicate present.
@@ -1046,7 +1054,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x68, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     EXPECT_EQ(psu.isPresent(), false);
@@ -1074,7 +1083,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 11,
+                    0x6f, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1127,7 +1137,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x68, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1177,7 +1188,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x68, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1221,7 +1233,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x68, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1285,7 +1298,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x69, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x69, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1329,7 +1343,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6d, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x6d, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1378,7 +1393,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6a, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x6a, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1425,7 +1441,8 @@
     EXPECT_CALL(mockedUtil, setAvailable(_, _, true)).Times(1);
     EXPECT_CALL(mockedUtil, setAvailable(_, _, false)).Times(0);
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6d, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x6d, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1474,7 +1491,8 @@
     EXPECT_CALL(mockedUtil, setAvailable(_, _, true)).Times(1);
     EXPECT_CALL(mockedUtil, setAvailable(_, _, false)).Times(0);
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6a, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x6a, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1520,7 +1538,8 @@
 {
     auto bus = sdbusplus::bus::new_default();
 
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x6b, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 3,
+                    0x6b, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1622,7 +1641,8 @@
 TEST_F(PowerSupplyTests, HasPSKillFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    PowerSupply psu{bus, PSUInventoryPath, 4, 0x6d, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 4,
+                    0x6d, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1705,7 +1725,8 @@
 TEST_F(PowerSupplyTests, HasPS12VcsFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    PowerSupply psu{bus, PSUInventoryPath, 5, 0x6e, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 5,
+                    0x6e, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1772,7 +1793,8 @@
 TEST_F(PowerSupplyTests, HasPSCS12VFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    PowerSupply psu{bus, PSUInventoryPath, 6, 0x6f, PSUGPIOLineName};
+    PowerSupply psu{bus,  PSUInventoryPath, 6,
+                    0x6f, "ibm-cffps",      PSUGPIOLineName};
     MockedGPIOInterface* mockPresenceGPIO =
         static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
     // Always return 1 to indicate present.
@@ -1834,3 +1856,115 @@
     psu.analyze();
     EXPECT_EQ(psu.hasPSCS12VFault(), false);
 }
+
+TEST_F(PowerSupplyTests, SetupInputHistory)
+{
+    auto bus = sdbusplus::bus::new_default();
+    {
+        PowerSupply psu{bus,  PSUInventoryPath, 6,
+                        0x6f, "ibm-cffps",      PSUGPIOLineName};
+        // Defaults to not present due to constructor and mock ordering.
+        psu.setupInputHistory();
+        EXPECT_EQ(psu.hasInputHistory(), false);
+        MockedGPIOInterface* mockPresenceGPIO =
+            static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+        // Always return 1 to indicate present.
+        EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+        psu.analyze();
+        psu.setupInputHistory();
+        EXPECT_EQ(psu.hasInputHistory(), true);
+    }
+    {
+        PowerSupply psu{bus,  PSUInventoryPath, 11,
+                        0x58, "inspur-ipsps",   PSUGPIOLineName};
+        // Defaults to not present due to constructor and mock ordering.
+        psu.setupInputHistory();
+        EXPECT_EQ(psu.hasInputHistory(), false);
+        MockedGPIOInterface* mockPresenceGPIO =
+            static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+        // Always return 1 to indicate present.
+        EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+        psu.analyze();
+        psu.setupInputHistory();
+        // After updating to present, and retrying setup, expect inspur-ipsps to
+        // still not support INPUT_HISTORY.
+        EXPECT_EQ(psu.hasInputHistory(), false);
+    }
+}
+
+TEST_F(PowerSupplyTests, UpdateHistory)
+{
+    auto bus = sdbusplus::bus::new_default();
+    PowerSupply psu{bus,  PSUInventoryPath, 7,
+                    0x6e, "ibm-cffps",      PSUGPIOLineName};
+    EXPECT_EQ(psu.hasInputHistory(), false);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 0);
+    MockedGPIOInterface* mockPresenceGPIO =
+        static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+    setMissingToPresentExpects(mockPMBus, mockedUtil);
+    PMBusExpectations expectations;
+    setPMBusExpectations(mockPMBus, expectations);
+    EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
+        .Times(6)
+        .WillRepeatedly(Return("205000"));
+    EXPECT_CALL(mockedUtil, setAvailable(_, _, true));
+    // First read after missing/present will have no data.
+    std::vector<uint8_t> emptyHistory{};
+    // Second read, after about 30 seconds, should have a record. 5-bytes.
+    // Sequence Number: 0x00, Average: 0x50 0xf3 (212), Maximum: 0x54 0xf3 (213)
+    std::vector<uint8_t> firstHistory{0x00, 0x50, 0xf3, 0x54, 0xf3};
+    // Third read, after about 60 seconds, should have two records, 10-bytes,
+    // but only reading 5 bytes, so make sure new/next sequence number
+    std::vector<uint8_t> secondHistory{0x01, 0x54, 0xf3, 0x58, 0xf3};
+    // Fourth read, 3rd sequence number (0x02).
+    std::vector<uint8_t> thirdHistory{0x02, 0x54, 0xf3, 0x58, 0xf3};
+    // Fifth read, out of sequence, clear and insert this one?
+    std::vector<uint8_t> outseqHistory{0xff, 0x5c, 0xf3, 0x60, 0xf3};
+    EXPECT_CALL(
+        mockPMBus,
+        readBinary(INPUT_HISTORY, Type::HwmonDeviceDebug,
+                   phosphor::power::history::RecordManager::RAW_RECORD_SIZE))
+        .Times(6)
+        .WillOnce(Return(emptyHistory))
+        .WillOnce(Return(firstHistory))
+        .WillOnce(Return(secondHistory))
+        .WillOnce(Return(thirdHistory))
+        .WillOnce(Return(outseqHistory))
+        .WillOnce(Return(emptyHistory));
+    // Calling analyze will update the presence, which will setup the input
+    // history if the power supply went from missing to present.
+    psu.analyze();
+    // The ibm-cffps power supply should support input history
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    // Usually should have empty buffer right after missing to present.
+    // Faked that out above with mocked readBinary with emptyHistory data.
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 0);
+    // Second run through...
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 1);
+    // Third run through
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 2);
+    // Fourth run through. Up to 3 records now?
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 3);
+    // Out of sequencer, reset, insert new one.
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 1);
+    // Empty one after last one good. Reset/clear.
+    setPMBusExpectations(mockPMBus, expectations);
+    psu.analyze();
+    EXPECT_EQ(psu.hasInputHistory(), true);
+    EXPECT_EQ(psu.getNumInputHistoryRecords(), 0);
+}