Refactor common functions into files

Refactor common functions into files that can be reused by other
platforms. This is just a start, but make it so we can put different
platform init routines in other files and still access the functions
for gpio i2c and filesystem helpers.

Tested:
flash onto a gb200, log output was clean.

```
USB_HUB_RESET_L-O Request to set to 0
USB_HUB_RESET_L-O Set to 0
HMC present in platformSleeping for 100 milliseconds
SGPIO_BMC_EN-O Request to set to 1
SGPIO_BMC_EN-O Set to 1
HMC_BMC_DETECT-O Request to set to 0
HMC_BMC_DETECT-O Set to 0
BMC_EROT_FPGA_SPI_MUX_SEL-O Request to set to 1
BMC_EROT_FPGA_SPI_MUX_SEL-O Set to 1
BMC_12V_CTRL-O Request to set to 1
BMC_12V_CTRL-O Set to 1
PWR_BRAKE_L-O Request to set to 1
PWR_BRAKE_L-O Set to 1
SHDN_REQ_L-O Request to set to 1
SHDN_REQ_L-O Set to 1
SHDN_FORCE_L-O Request to set to 1
SHDN_FORCE_L-O Set to 1
SYS_RST_IN_L-O Request to set to 0
SYS_RST_IN_L-O Set to 0
FPGA_READY_BMC-I GpioEvent is already 1
SEC_FPGA_READY_BMC-I GpioEvent is already 1
EROT_FPGA_RST_L-O Request to set to 1
EROT_FPGA_RST_L-O Set to 1
SEC_EROT_FPGA_RST_L-O Request to set to 1
SEC_EROT_FPGA_RST_L-O Set to 1
Sleeping for 100 milliseconds
FPGA_RST_L-O Request to set to 1
FPGA_RST_L-O Set to 1
FPGA_READY_BMC-I  Waiting to go to assert
FPGA_READY_BMC-I Timeout
FPGA_READY_BMC-I failed to assert
SEC_FPGA_READY_BMC-I  Waiting to go to assert
SEC_FPGA_READY_BMC-I Timeout
SEC_FPGA_READY_BMC-I failed to assert
1e78a100 unbound
1e78a100 bound
1e78a180 unbound
1e78a180 bound
RUN_POWER_EN-O Request to set to 1
RUN_POWER_EN-O Set to 1
SYS_RST_IN_L-O Request to set to 1
SYS_RST_IN_L-O Settingto 1
GLOBAL_WP_BMC-O Request to set to 0
GLOBAL_WP_BMC-O Set to 0
BMC_READY-O Request to set to 1
BMC_READY-O Set to 1
USB_HUB_RESET_L-O Request to set to 1
USB_HUB_RESET_L-O Settingto 1
Platform init complete
```

Change-Id: I7f2134470536d48e1face39075c5fa1d40dc0de7
Signed-off-by: Marc Olberding <molberding@nvidia.com>
diff --git a/gpio.cpp b/gpio.cpp
new file mode 100644
index 0000000..d4b2d1b
--- /dev/null
+++ b/gpio.cpp
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#include "gpio.hpp"
+
+#include "utilities.hpp"
+
+#include <cstring>
+#include <filesystem>
+#include <format>
+#include <iostream>
+#include <unordered_map>
+
+static std::unordered_map<std::string, gpiod::line> io;
+using namespace std::chrono_literals;
+
+namespace gpio
+{
+
+void set(const char* line_name, int value,
+         std::chrono::milliseconds find_timeout)
+{
+    std::cerr << std::format("{} Request to set to {}\n", line_name, value);
+    std::chrono::milliseconds polling_time = 10ms;
+    gpiod::line& line = io[line_name];
+    if (!line)
+    {
+        do
+        {
+            line = gpiod::find_line(line_name);
+            if (!line)
+            {
+                std::cerr << std::format(
+                    "{} not found yet, waiting and retrying\n", line_name);
+
+                sleep_milliseconds(polling_time);
+                find_timeout -= polling_time;
+            }
+        } while (!line && find_timeout > 0s);
+        if (!line && find_timeout <= 0s)
+        {
+            std::cerr << std::format("{} Unable to find\n", line_name);
+            return;
+        }
+        try
+        {
+            line.request({app_name, gpiod::line_request::DIRECTION_OUTPUT, 0},
+                         value);
+        }
+        catch (const std::system_error& e)
+        {
+            std::cerr << std::format(
+                "{} unable to set direction and value {}\n", line_name,
+                e.what());
+            return;
+        }
+        // No need to set if the init did it for us
+        std::cerr << std::format("{} Set to {}\n", line_name, value);
+        return;
+    }
+    std::cerr << std::format("{} Settingto {}\n", line_name, value);
+    line.set_value(value);
+}
+
+int get(const char* line_name)
+{
+    std::cerr << std::format("{} Request to get\n", line_name);
+
+    gpiod::line line = gpiod::find_line(line_name);
+    if (!line)
+    {
+        std::cerr << std::format("{} Set unable to find\n", line_name);
+        return -1;
+    }
+    try
+    {
+        line.request({app_name, gpiod::line_request::DIRECTION_INPUT, 0});
+    }
+    catch (const std::system_error& e)
+    {
+        std::cerr << std::format("{} unable to set {}\n", line_name, e.what());
+    }
+
+    int value = line.get_value();
+    std::cerr << std::format("{} was {}\n", line_name, value);
+    return value;
+}
+
+Event::Event(const char* line_name_in, int value_in) :
+    line_name(line_name_in), value(value_in)
+{
+    line = gpiod::find_line(line_name);
+    if (!line)
+    {
+        std::cerr << std::format("{} GpioEvent: Unable to find\n", line_name);
+        return;
+    }
+    int edge = (value != 0) ? ::gpiod::line_request::EVENT_RISING_EDGE
+                            : ::gpiod::line_request::EVENT_FALLING_EDGE;
+
+    line.request({app_name, edge, 0});
+
+    int val = line.get_value();
+    if (val == value)
+    {
+        std::cerr << std::format("{} GpioEvent is already {}\n", line_name,
+                                 val);
+    }
+    else
+    {
+        std::cerr << std::format("GpioEvent created for {}\n", line_name);
+    }
+}
+
+EventResult Event::wait()
+{
+    if (!line)
+    {
+        std::cerr << std::format("Line {} wasn't initialized\n", line_name);
+        return EventResult::Error;
+    }
+    std::cerr << std::format("{}  Waiting to go to {}\n", line_name,
+                             (value != 0) ? "assert" : "deassert");
+    auto events = line.event_wait(std::chrono::seconds(120));
+    if (!events)
+    {
+        std::cerr << std::format("{} Timeout\n", line_name);
+        return EventResult::Timeout;
+    }
+
+    std::cerr << std::format("{} Asserted\n", line_name);
+
+    return EventResult::Asserted;
+}
+
+void set_raw(unsigned int chip_num, unsigned int bit_num, int value)
+{
+    std::string syspath = std::format("gpiochip{}", chip_num);
+    std::cerr << std::format("Setting gpiochip{} bit {} to {}\n", chip_num,
+                             bit_num, value);
+    try
+    {
+        gpiod::chip chip(syspath);
+        gpiod::line line = chip.get_line(bit_num);
+        line.request({app_name, gpiod::line_request::DIRECTION_OUTPUT, 0},
+                     value);
+        std::cerr << std::format("gpiochip{} bit {} set to {}\n", chip_num,
+                                 bit_num, value);
+    }
+    catch (const std::system_error& e)
+    {
+        std::cerr << std::format("Error setting gpiochip{} bit {}: {}\n",
+                                 chip_num, bit_num, e.what());
+    }
+}
+
+int find_chip_idx_from_dir(std::string_view device_path)
+{
+    std::string gpio_chip;
+    for (const auto& entry : std::filesystem::directory_iterator(device_path))
+    {
+        std::string path = entry.path().string();
+        if (path.find("gpiochip") != std::string::npos)
+        {
+            gpio_chip =
+                path.substr(path.find("gpiochip") + std::strlen("gpiochip"));
+            break;
+        }
+    }
+    if (gpio_chip.empty())
+    {
+        std::cerr << "Error: Could not find GPIO chip number\n";
+        return -ENOENT;
+    }
+
+    std::cerr << "Found GPIO chip: gpiochip" << gpio_chip << "\n";
+    unsigned int gpiochipint = 0;
+    std::from_chars_result r =
+        std::from_chars(&*gpio_chip.begin(), &*gpio_chip.end(), gpiochipint);
+    if (r.ec != std::error_code() || r.ptr != &*gpio_chip.end())
+    {
+        std::cout << "Failed to convert gpiochip\n";
+        return -EINVAL;
+    }
+    return gpiochipint;
+}
+} // namespace gpio