pseq: Create GPIO class hierarchy

Create GPIO class hierarchy within the phosphor-power-sequencer
application.

The GPIO abstract base class declares virtual methods for requesting,
reading, writing, and releasing a GPIO.

The BMCGPIO subclass provides a real implementation using the libgpiod
API.

The MockGPIO subclass provides a mock implementation for automated
testing.

Tested:
* Verified GPIO could be read
* Verified GPIO could be written
* Verified error paths
* Verified MockGPIO could be used in an automated test

Change-Id: Ie5808988c5d743c44735f5bc4c09cd15b397518b
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/gpio.hpp b/phosphor-power-sequencer/src/gpio.hpp
new file mode 100644
index 0000000..d7ebf83
--- /dev/null
+++ b/phosphor-power-sequencer/src/gpio.hpp
@@ -0,0 +1,150 @@
+/**
+ * Copyright © 2025 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gpiod.hpp>
+
+#include <string>
+
+namespace phosphor::power::sequencer
+{
+
+/**
+ * @class GPIO
+ *
+ * Abstract base class for a General-Purpose Input/Output pin.
+ */
+class GPIO
+{
+  public:
+    GPIO() = default;
+    GPIO(const GPIO&) = delete;
+    GPIO(GPIO&&) = delete;
+    GPIO& operator=(const GPIO&) = delete;
+    GPIO& operator=(GPIO&&) = delete;
+    virtual ~GPIO() = default;
+
+    enum class RequestType
+    {
+        Read,
+        Write
+    };
+
+    /**
+     * Request ownership of the GPIO.
+     *
+     * Throws an exception if an error occurs.
+     *
+     * This is required before getting or setting the GPIO value.
+     *
+     * @param type specifies whether requesting to read or write the GPIO
+     */
+    virtual void request(RequestType type) = 0;
+
+    /**
+     * Gets the value of the GPIO.
+     *
+     * Throws an exception if an error occurs.
+     *
+     * @return 0 or 1
+     */
+    virtual int getValue() = 0;
+
+    /**
+     * Sets the value of the GPIO.
+     *
+     * Throws an exception if an error occurs.
+     *
+     * @param value new value (0 or 1)
+     */
+    virtual void setValue(int value) = 0;
+
+    /**
+     * Release ownership of the GPIO.
+     *
+     * Throws an exception if an error occurs.
+     */
+    virtual void release() = 0;
+};
+
+/**
+ * @class BMCGPIO
+ *
+ * Implementation of the GPIO interface using the standard BMC API (libgpiod).
+ */
+class BMCGPIO : public GPIO
+{
+  public:
+    BMCGPIO() = delete;
+    BMCGPIO(const BMCGPIO&) = delete;
+    BMCGPIO(BMCGPIO&&) = delete;
+    BMCGPIO& operator=(const BMCGPIO&) = delete;
+    BMCGPIO& operator=(BMCGPIO&&) = delete;
+    virtual ~BMCGPIO() = default;
+
+    /**
+     * Constructor.
+     *
+     * Throws an exception if a GPIO with the specified name cannot be found.
+     *
+     * @param name GPIO name
+     */
+    explicit BMCGPIO(const std::string& name)
+    {
+        line = gpiod::find_line(name);
+        if (!line)
+        {
+            throw std::invalid_argument{"Invalid GPIO name: " + name};
+        }
+    }
+
+    virtual void request(RequestType type) override
+    {
+        int lineRequestType;
+        if (type == RequestType::Read)
+        {
+            lineRequestType = gpiod::line_request::DIRECTION_INPUT;
+        }
+        else
+        {
+            lineRequestType = gpiod::line_request::DIRECTION_OUTPUT;
+        }
+        line.request({"phosphor-power-control", lineRequestType, 0});
+    }
+
+    virtual int getValue() override
+    {
+        return line.get_value();
+    }
+
+    virtual void setValue(int value) override
+    {
+        line.set_value(value);
+    }
+
+    virtual void release() override
+    {
+        line.release();
+    }
+
+  private:
+    /**
+     * GPIO line to access the pin.
+     */
+    gpiod::line line;
+};
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/test/mock_gpio.hpp b/phosphor-power-sequencer/test/mock_gpio.hpp
new file mode 100644
index 0000000..e68b1de
--- /dev/null
+++ b/phosphor-power-sequencer/test/mock_gpio.hpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2025 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "gpio.hpp"
+
+#include <gmock/gmock.h>
+
+namespace phosphor::power::sequencer
+{
+
+/**
+ * @class MockGPIO
+ *
+ * Mock implementation of the GPIO interface.
+ */
+class MockGPIO : public GPIO
+{
+  public:
+    MockGPIO() = default;
+    MockGPIO(const MockGPIO&) = delete;
+    MockGPIO(MockGPIO&&) = delete;
+    MockGPIO& operator=(const MockGPIO&) = delete;
+    MockGPIO& operator=(MockGPIO&&) = delete;
+    virtual ~MockGPIO() = default;
+
+    MOCK_METHOD(void, request, (RequestType type), (override));
+    MOCK_METHOD(int, getValue, (), (override));
+    MOCK_METHOD(void, setValue, (int value), (override));
+    MOCK_METHOD(void, release, (), (override));
+};
+
+} // namespace phosphor::power::sequencer