psu-ng: Disable INPUT_HISTORY for 1400W IBM PSU

If the device driver is ibm-cpffps, read the MFR_POUT_MAX value
(max_power_out). Only enable INPUT_HISTORY data collection if it is not
the 1400W IBM power supply (MSB/LSB results in 30725 for 1400). The
1400W IBM power supply appears to cause problems on the bus when
an INPUT_HISTORY PMBus command read occurs.

Tested:
   Simulated Rainier 2S4U
   Fake 2nd and 3rd PSUs to return 1400W value
   Verify 1st and 4th collecting INPUT_HISTORY
   Verify 2nd and 3rd PSUs not getting INPUT_HISTORY collected
   -----
   Verify real Rainier 2S4U with 1600W working as expected.

Change-Id: Ia37cea9b0273ac5926e4bc581a2ea8a4079afa23
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/phosphor-power-supply/power_supply.cpp b/phosphor-power-supply/power_supply.cpp
index b364b36..9aa2719 100644
--- a/phosphor-power-supply/power_supply.cpp
+++ b/phosphor-power-supply/power_supply.cpp
@@ -638,7 +638,8 @@
                 // re-check faults on next call.
                 clearVinUVFault();
             }
-            else if (std::abs(actualInputVoltageOld - actualInputVoltage) > 1.0)
+            else if (std::abs(actualInputVoltageOld - actualInputVoltage) >
+                     10.0)
             {
                 log<level::INFO>(
                     fmt::format(
@@ -972,51 +973,98 @@
     }
 }
 
+auto PowerSupply::getMaxPowerOut() const
+{
+    using namespace phosphor::pmbus;
+
+    auto maxPowerOut = 0;
+
+    if (present)
+    {
+        try
+        {
+            // Read max_power_out, should be direct format
+            auto maxPowerOutStr =
+                pmbusIntf->readString(MFR_POUT_MAX, Type::HwmonDeviceDebug);
+            log<level::INFO>(fmt::format("{} MFR_POUT_MAX read {}", shortName,
+                                         maxPowerOutStr)
+                                 .c_str());
+            maxPowerOut = std::stod(maxPowerOutStr);
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>(fmt::format("{} MFR_POUT_MAX read error: {}",
+                                        shortName, e.what())
+                                .c_str());
+        }
+    }
+
+    return maxPowerOut;
+}
+
 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)
+        auto maxPowerOut = getMaxPowerOut();
+
+        if (maxPowerOut != phosphor::pmbus::IBM_CFFPS_1400W)
         {
-            inputHistorySupported = true;
+            // 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
+        {
             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());
+                fmt::format("{} INPUT_HISTORY DISABLED. max_power_out: {}",
+                            shortName, maxPowerOut)
+                    .c_str());
+            inputHistorySupported = false;
         }
     }
     else
@@ -1044,7 +1092,8 @@
         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)
+    // Update D-Bus only if something changed (a new record ID, or cleared
+    // out)
     auto changed = recordManager->add(data);
     if (changed)
     {
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
index e342d72..48c0b5d 100644
--- a/phosphor-power-supply/power_supply.hpp
+++ b/phosphor-power-supply/power_supply.hpp
@@ -857,6 +857,16 @@
     void inventoryAdded(sdbusplus::message::message& 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 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.
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index fabe535..1e47b9c 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -256,6 +256,12 @@
 
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
 
     // STATUS_WORD INPUT fault.
     {
@@ -756,6 +762,12 @@
         EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         setMissingToPresentExpects(mockPMBus, mockedUtil);
+        // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+        // for INPUT_HISTORY will check max_power_out to see if it is
+        // old/unsupported power supply. Indicate good value, supported.
+        EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+            .Times(1)
+            .WillOnce(Return("2000"));
         // If I am calling analyze(), I should probably give it good data.
         // STATUS_WORD 0x0000 is powered on, no faults.
         PMBusExpectations expectations;
@@ -786,6 +798,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1020,6 +1038,12 @@
         EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         setMissingToPresentExpects(mockPMBus, mockedUtil);
+        // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+        // for INPUT_HISTORY will check max_power_out to see if it is
+        // old/unsupported power supply. Indicate good value, supported.
+        EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+            .Times(1)
+            .WillOnce(Return("2000"));
         // STATUS_WORD 0x0000 is powered on, no faults.
         PMBusExpectations expectations;
         setPMBusExpectations(mockPMBus, expectations);
@@ -1066,6 +1090,12 @@
     // to the correct/latest HWMON directory, in case it changes.
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // Call to analyze things will trigger read of STATUS_WORD and READ_VIN.
     // Default expectations will be on, no faults.
     PMBusExpectations expectations;
@@ -1091,6 +1121,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // Call to analyze things will trigger read of STATUS_WORD and READ_VIN.
     // Default expectations will be on, no faults.
     PMBusExpectations expectations;
@@ -1145,6 +1181,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1196,6 +1238,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // First return STATUS_WORD with no bits on.
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
@@ -1241,6 +1289,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
 
     // Presence change from missing to present will trigger in1_input read in
     // an attempt to get CLEAR_FAULTS called. Return value ignored.
@@ -1306,6 +1360,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1351,6 +1411,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1401,6 +1467,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1449,6 +1521,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1499,6 +1577,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1546,6 +1630,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1649,6 +1739,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1733,6 +1829,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1801,6 +1903,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     // STATUS_WORD 0x0000 is powered on, no faults.
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
@@ -1868,13 +1976,63 @@
         EXPECT_EQ(psu.hasInputHistory(), false);
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+        MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // Always return 1 to indicate present.
         EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+        setMissingToPresentExpects(mockPMBus, mockedUtil);
+        PMBusExpectations expectations;
+        setPMBusExpectations(mockPMBus, expectations);
+        // After reading STATUS_WORD, etc., there will be a READ_VIN check.
+        EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
+            .Times(1)
+            .WillOnce(Return("206000"));
+        // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+        // for INPUT_HISTORY will check max_power_out to see if it is
+        // old/unsupported power supply. Indicate good value, supported.
+        /// Also called when I redo setupInputHistory().
+        EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+            .Times(2)
+            .WillRepeatedly(Return("2000"));
+        // Call to analyze() and above expectations to get missing/present and
+        // good status.
         psu.analyze();
         psu.setupInputHistory();
         EXPECT_EQ(psu.hasInputHistory(), true);
     }
     {
+        // Workaround - Disable INPUT_HISTORY collection if 1400W
+        PowerSupply psu{bus,  PSUInventoryPath, 3,
+                        0x68, "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());
+        MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+        // Always return 1 to indicate present.
+        EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+        setMissingToPresentExpects(mockPMBus, mockedUtil);
+        // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+        // for INPUT_HISTORY will check max_power_out to see if it is
+        // old/unsupported power supply. Indicate 1400W IBM value, unsupported.
+        EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+            .Times(2)
+            .WillRepeatedly(Return("30725"));
+        PMBusExpectations expectations;
+        setPMBusExpectations(mockPMBus, expectations);
+        // After reading STATUS_WORD, etc., there will be a READ_VIN check.
+        EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
+            .Times(1)
+            .WillOnce(Return("206000"));
+        // Call to analyze() and above expectations to get missing/present and
+        // good status.
+        psu.analyze();
+        psu.setupInputHistory();
+        // After updating to present, and retrying setup, expect ibm-cffps with
+        // 1400W to still not support INPUT_HISTORY.
+        EXPECT_EQ(psu.hasInputHistory(), false);
+    }
+    {
         PowerSupply psu{bus,  PSUInventoryPath, 11,
                         0x58, "inspur-ipsps",   PSUGPIOLineName};
         // Defaults to not present due to constructor and mock ordering.
@@ -1882,8 +2040,18 @@
         EXPECT_EQ(psu.hasInputHistory(), false);
         MockedGPIOInterface* mockPresenceGPIO =
             static_cast<MockedGPIOInterface*>(psu.getPresenceGPIO());
+        MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         // Always return 1 to indicate present.
         EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+        setMissingToPresentExpects(mockPMBus, mockedUtil);
+        PMBusExpectations expectations;
+        setPMBusExpectations(mockPMBus, expectations);
+        // After reading STATUS_WORD, etc., there will be a READ_VIN check.
+        EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
+            .Times(1)
+            .WillOnce(Return("206000"));
+        // Call to analyze() and above expectations to get missing/present and
+        // good status.
         psu.analyze();
         psu.setupInputHistory();
         // After updating to present, and retrying setup, expect inspur-ipsps to
@@ -1905,6 +2073,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
     EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
@@ -1982,6 +2156,12 @@
     EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     setMissingToPresentExpects(mockPMBus, mockedUtil);
+    // Missing/present will trigger attempt to setup INPUT_HISTORY. Setup
+    // for INPUT_HISTORY will check max_power_out to see if it is
+    // old/unsupported power supply. Indicate good value, supported.
+    EXPECT_CALL(mockPMBus, readString(MFR_POUT_MAX, _))
+        .Times(1)
+        .WillOnce(Return("2000"));
     PMBusExpectations expectations;
     setPMBusExpectations(mockPMBus, expectations);
     EXPECT_CALL(mockPMBus, readString(READ_VIN, _))
diff --git a/pmbus.hpp b/pmbus.hpp
index 7e71148..be352b6 100644
--- a/pmbus.hpp
+++ b/pmbus.hpp
@@ -14,6 +14,11 @@
 // The file name Linux uses to capture the READ_VIN from pmbus.
 constexpr auto READ_VIN = "in1_input";
 
+// The file name Linux uses to capture the MFR_POUT_MAX from pmbus.
+constexpr auto MFR_POUT_MAX = "max_power_out";
+// The max_power_out value expected to be read for 1400W IBM CFFPS type.
+constexpr auto IBM_CFFPS_1400W = 30725;
+
 namespace in_input
 {
 // VIN thresholds in Volts