psu-ng: Add in ability to get presence via GPIO

The device tree and entity-manager (D-Bus) properties have the power
supply GPIO presence lines named, so we can use those GPIO line names to
find the GPIO device to read for determining power supply presence.

Some systems have the power supply presence lines in a gpio-keys section
(notably IBM Rainier). To facilitate continued function while running
with a device tree that has not removed those device tree entries, allow
for a fallback to the old inventory D-Bus propertiesChanged or
interfaceAdded matches for presence.

Change-Id: I5002aa62e5b460463cc26328c889a3786b467a3c
Signed-off-by: B. J. Wyman <bjwyman@gmail.com>
diff --git a/phosphor-power-supply/test/power_supply_tests.cpp b/phosphor-power-supply/test/power_supply_tests.cpp
index 74d98c1..b298d6c 100644
--- a/phosphor-power-supply/test/power_supply_tests.cpp
+++ b/phosphor-power-supply/test/power_supply_tests.cpp
@@ -20,6 +20,7 @@
 using ::testing::StrEq;
 
 static auto PSUInventoryPath = "/xyz/bmc/inv/sys/chassis/board/powersupply0";
+static auto PSUGPIOLineName = "presence-ps0";
 
 class PowerSupplyTests : public ::testing::Test
 {
@@ -44,13 +45,18 @@
      * @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] gpioLineName - The string for the gpio-line-name to read for
+     * presence.
+     * @param[in] bindDelay - Time in milliseconds to delay binding the device
+     * driver after seeing the presence line go active.
      */
     auto bus = sdbusplus::bus::new_default();
 
     // Try where inventory path is empty, constructor should fail.
     try
     {
-        auto psu = std::make_unique<PowerSupply>(bus, "", 3, 0x68);
+        auto psu =
+            std::make_unique<PowerSupply>(bus, "", 3, 0x68, PSUGPIOLineName);
         ADD_FAILURE() << "Should not have reached this line.";
     }
     catch (const std::invalid_argument& e)
@@ -62,13 +68,31 @@
         ADD_FAILURE() << "Should not have caught exception.";
     }
 
-    // Test with valid arguments
+    // TODO: Try invalid i2c address?
+
+    // Try where gpioLineName is empty.
     try
     {
-        EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-            .Times(1);
         auto psu =
-            std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68);
+            std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68, "");
+        ADD_FAILURE()
+            << "Should not have reached this line. Invalid gpioLineName.";
+    }
+    catch (const std::invalid_argument& e)
+    {
+        EXPECT_STREQ(e.what(), "Invalid empty gpioLineName");
+    }
+    catch (...)
+    {
+        ADD_FAILURE() << "Should not have caught exception.";
+    }
+
+    // Test with valid arguments
+    // NOT using D-Bus inventory path for presence.
+    try
+    {
+        auto psu = std::make_unique<PowerSupply>(bus, PSUInventoryPath, 3, 0x68,
+                                                 PSUGPIOLineName);
 
         EXPECT_EQ(psu->isPresent(), false);
         EXPECT_EQ(psu->isFaulted(), false);
@@ -80,14 +104,36 @@
     {
         ADD_FAILURE() << "Should not have caught exception.";
     }
+
+    // Test with valid arguments
+    // TODO: Using D-Bus inventory path for presence.
+    try
+    {
+        // FIXME: How do I get that presenceGPIO.read() in the startup to throw
+        // an exception?
+
+        // EXPECT_CALL(mockedUtil, getPresence(_,
+        // StrEq(PSUInventoryPath)))
+        //    .Times(1);
+    }
+    catch (...)
+    {
+        ADD_FAILURE() << "Should not have caught exception.";
+    }
 }
 
 TEST_F(PowerSupplyTests, Analyze)
 {
     auto bus = sdbusplus::bus::new_default();
 
-    EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
-    PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
+    // If I default to reading the GPIO, I will NOT expect a call to
+    // getPresence().
+
+    PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(0));
+
     psu.analyze();
     // By default, nothing should change.
     EXPECT_EQ(psu.isPresent(), false);
@@ -96,17 +142,19 @@
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
 
-    // In order to get the various faults tested, the power supply needs to be
-    // present in order to read from the PMBus device(s).
-    EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-        .Times(1)
-        .WillOnce(Return(true)); // present
-    PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a};
-    EXPECT_EQ(psu2.isPresent(), true);
+    PowerSupply psu2{bus, PSUInventoryPath, 5, 0x6a, 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).
+    MockedGPIOReader* mockPresenceGPIO2 =
+        static_cast<MockedGPIOReader*>(psu2.getPresenceGPIO());
+    ON_CALL(*mockPresenceGPIO2, read()).WillByDefault(Return(1));
+
+    EXPECT_EQ(psu2.isPresent(), false);
 
     // STATUS_WORD 0x0000 is powered on, no faults.
+    // It will read STATUS_MFR at the same time, so there are 2 reads.
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu2.getPMBus());
-    EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
+    EXPECT_CALL(mockPMBus, read(_, _)).Times(2).WillRepeatedly(Return(0x0000));
     psu2.analyze();
     EXPECT_EQ(psu2.isPresent(), true);
     EXPECT_EQ(psu2.isFaulted(), false);
@@ -190,11 +238,14 @@
     // Test where PSU is NOT present
     try
     {
-        EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-            .Times(1)
-            .WillOnce(Return(false));
-        PowerSupply psu{bus, PSUInventoryPath, 4, 0x69};
+        // Assume GPIO presence, not inventory presence?
+        PowerSupply psu{bus, PSUInventoryPath, 4, 0x69, PSUGPIOLineName};
+
+        MockedGPIOReader* mockPresenceGPIO =
+            static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+        ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(0));
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+        // Constructor should set initial presence, default read returns 0.
         // If it is not present, I should not be trying to write to it.
         EXPECT_CALL(mockPMBus, writeBinary(_, _, _)).Times(0);
         psu.onOffConfig(data);
@@ -206,11 +257,15 @@
     // Test where PSU is present
     try
     {
-        EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-            .Times(1)
-            .WillOnce(Return(true)); // present
-        PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a};
+        // Assume GPIO presence, not inventory presence?
+        PowerSupply psu{bus, PSUInventoryPath, 5, 0x6a, PSUGPIOLineName};
+        MockedGPIOReader* mockPresenceGPIO =
+            static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+        ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+        // TODO: expect setPresence call?
+        // updatePresence() private function reads gpio, called by analyze().
+        psu.analyze();
         // TODO: ???should I check the filename?
         EXPECT_CALL(mockPMBus,
                     writeBinary(_, ElementsAre(0x15), Type::HwmonDeviceDebug))
@@ -225,16 +280,19 @@
 TEST_F(PowerSupplyTests, ClearFaults)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-        .Times(1)
-        .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 13, 0x68};
+    PowerSupply psu{bus, PSUInventoryPath, 13, 0x68, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    // GPIO read return 1 to indicate present.
+    ON_CALL(*mockPresenceGPIO, read()).WillByDefault(Return(1));
+    MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
+    ON_CALL(mockPMBus, read(_, _)).WillByDefault(Return(0));
+    psu.analyze();
     EXPECT_EQ(psu.isPresent(), true);
     EXPECT_EQ(psu.isFaulted(), false);
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
-    MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_CALL(mockPMBus, read(_, _))
         .Times(2)
         .WillOnce(Return(0xFFFF))
@@ -254,6 +312,8 @@
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_EQ(psu.hasVINUVFault(), false);
+
+    // TODO: Faults clear on missing/present?
 }
 
 TEST_F(PowerSupplyTests, UpdateInventory)
@@ -262,10 +322,7 @@
 
     try
     {
-        EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-            .Times(1)
-            .WillOnce(Return(false)); // missing
-        PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
+        PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, 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);
@@ -278,10 +335,12 @@
 
     try
     {
-        EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath)))
-            .Times(1)
-            .WillOnce(Return(true)); // present
-        PowerSupply psu{bus, PSUInventoryPath, 13, 0x69};
+        PowerSupply psu{bus, PSUInventoryPath, 13, 0x69, PSUGPIOLineName};
+        MockedGPIOReader* mockPresenceGPIO =
+            static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+        // GPIO read return 1 to indicate present.
+        EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
+        psu.analyze();
         MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
         EXPECT_CALL(mockPMBus, readString(_, _)).WillRepeatedly(Return(""));
         psu.updateInventory();
@@ -307,22 +366,28 @@
 TEST_F(PowerSupplyTests, IsPresent)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, StrEq(PSUInventoryPath))).Times(1);
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
+
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
     EXPECT_EQ(psu.isPresent(), false);
 
-    EXPECT_CALL(mockedUtil, getPresence(_, _))
-        .WillOnce(Return(true)); // present
-    PowerSupply psu2{bus, PSUInventoryPath, 10, 0x6b};
-    EXPECT_EQ(psu2.isPresent(), true);
+    // Change GPIO read to return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).Times(1).WillOnce(Return(1));
+    psu.analyze();
+    EXPECT_EQ(psu.isPresent(), true);
 }
 
 TEST_F(PowerSupplyTests, IsFaulted)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, _))
-        .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f};
+
+    PowerSupply psu{bus, PSUInventoryPath, 11, 0x6f, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    psu.analyze();
     EXPECT_EQ(psu.isFaulted(), false);
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_CALL(mockPMBus, read(_, _))
@@ -336,9 +401,13 @@
 TEST_F(PowerSupplyTests, HasInputFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, _))
-        .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
+
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    psu.analyze();
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasInputFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -358,9 +427,13 @@
 TEST_F(PowerSupplyTests, HasMFRFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, _))
-        .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
+
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    psu.analyze();
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasMFRFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));
@@ -380,9 +453,13 @@
 TEST_F(PowerSupplyTests, HasVINUVFault)
 {
     auto bus = sdbusplus::bus::new_default();
-    EXPECT_CALL(mockedUtil, getPresence(_, _))
-        .WillOnce(Return(true)); // present
-    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68};
+
+    PowerSupply psu{bus, PSUInventoryPath, 3, 0x68, PSUGPIOLineName};
+    MockedGPIOReader* mockPresenceGPIO =
+        static_cast<MockedGPIOReader*>(psu.getPresenceGPIO());
+    // Always return 1 to indicate present.
+    EXPECT_CALL(*mockPresenceGPIO, read()).WillRepeatedly(Return(1));
+    psu.analyze();
     MockedPMBus& mockPMBus = static_cast<MockedPMBus&>(psu.getPMBus());
     EXPECT_EQ(psu.hasVINUVFault(), false);
     EXPECT_CALL(mockPMBus, read(_, _)).Times(1).WillOnce(Return(0x0000));