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/platform.cpp b/platform.cpp
index 7a947cf..015a1db 100644
--- a/platform.cpp
+++ b/platform.cpp
@@ -1,6 +1,10 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-FileCopyrightText: 2025 NVIDIA
 
+#include "gpio.hpp"
+#include "i2c.hpp"
+#include "utilities.hpp"
+
 #include <fcntl.h>
 #include <systemd/sd-daemon.h>
 
@@ -15,299 +19,35 @@
 #include <format>
 #include <fstream>
 #include <iostream>
-#include <thread>
-#include <unordered_map>
 
 using namespace std::chrono_literals;
 
-constexpr static const char* app_name = "platform_init";
-
-// Map of GPIO name to line.  Holds lines open for the duration of the program
-static std::unordered_map<std::string, gpiod::line> io;
-
-void sleep_milliseconds(std::chrono::milliseconds milliseconds)
-{
-    std::cerr << std::format("Sleeping for {} milliseconds\n",
-                             milliseconds.count());
-    std::this_thread::sleep_for(milliseconds);
-}
-
-void set_gpio(const char* line_name, int value,
-              std::chrono::milliseconds find_timeout = 0ms)
-{
-    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_gpio(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;
-}
-
-enum class GpioEventResult
-{
-    Error,
-    Asserted,
-    Timeout
-};
-
-struct GpioEvent
-{
-    GpioEvent(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);
-        }
-    }
-    GpioEventResult wait()
-    {
-        if (!line)
-        {
-            std::cerr << std::format("Line {} wasn't initialized\n", line_name);
-            return GpioEventResult::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 GpioEventResult::Timeout;
-        }
-
-        std::cerr << std::format("{} Asserted\n", line_name);
-
-        return GpioEventResult::Asserted;
-    }
-
-    gpiod::line line;
-    std::string line_name;
-    int value;
-};
-
-void rebind_i2c(std::string number)
-{
-    std::string bindpath =
-        std::format("/sys/bus/platform/drivers/aspeed-i2c-bus/unbind", number);
-    std::ofstream bindofs(bindpath);
-    if (!bindofs)
-    {
-        std::cerr << std::format("{} unable to open\n", bindpath);
-        return;
-    }
-    try
-    {
-        bindofs << std::format("{}.i2c\n", number);
-    }
-    catch (const std::system_error& e)
-    {
-        std::cerr << std::format("{} unable to write\n", bindpath);
-        return;
-    }
-    bindofs.close();
-    std::cerr << std::format("{} unbound\n", number);
-
-    std::string unbindpath =
-        std::format("/sys/bus/platform/drivers/aspeed-i2c-bus/bind", number);
-    std::ofstream unbindofs(unbindpath);
-    if (!unbindofs)
-    {
-        std::cerr << std::format("{} unable to open\n", unbindpath);
-        return;
-    }
-    try
-    {
-        unbindofs << std::format("{}.i2c\n", number);
-    }
-    catch (const std::system_error& e)
-    {
-        std::cerr << std::format("{} unable to write\n", unbindpath);
-        return;
-    }
-    std::cerr << std::format("{} bound\n", number);
-}
-
-void set_gpio_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());
-    }
-}
-
-void new_device(unsigned int bus, unsigned int address,
-                std::string_view device_type)
-{
-    std::string path =
-        std::format("/sys/bus/i2c/devices/i2c-{}/new_device", bus);
-    std::cerr << std::format("attempting to open {}", path);
-    std::ofstream new_device(path);
-    if (!new_device)
-    {
-        std::cerr << "Error: Unable to create I2C device\n";
-        return;
-    }
-    new_device << std::format("{} 0x{:02x}", device_type, address);
-    new_device.close();
-
-    std::cerr << std::format("{} device created at bus {}", device_type, bus);
-}
-
-void wait_for_path_to_exist(std::string_view path,
-                            std::chrono::milliseconds timeout)
-{
-    while (true)
-    {
-        std::error_code ec;
-        bool exists = std::filesystem::exists(path, ec);
-        if (exists)
-        {
-            return;
-        }
-        sleep_milliseconds(1ms);
-        timeout -= 1ms;
-    }
-    std::cerr << std::format("Failed to wait for {} to exist", path);
-}
-
 void init_p2020_gpu_card()
 {
     std::cerr << "Initializing GPU card...\n";
 
     // Init the P2020 gpio expander
-    new_device(14, 0x20, "pca6408");
+    i2c::new_device(14, 0x20, "pca6408");
 
     // Wait for device to be created
     const auto* device_path = "/sys/bus/i2c/devices/14-0020";
     wait_for_path_to_exist(device_path, 1000ms);
 
     // Find the GPIO chip number
-    std::string gpio_chip;
-    for (const auto& entry : std::filesystem::directory_iterator(device_path))
+    int gpiochipint = gpio::find_chip_idx_from_dir(device_path);
+    if (gpiochipint < 0)
     {
-        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;
     }
-
-    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;
-    }
-
     // Set MCU in recovery
-    set_gpio_raw(gpiochipint, 3, 1);
+    gpio::set_raw(gpiochipint, 3, 1);
 
     // Reset MCU
-    set_gpio_raw(gpiochipint, 4, 0);
-    set_gpio_raw(gpiochipint, 4, 1);
+    gpio::set_raw(gpiochipint, 4, 0);
+    gpio::set_raw(gpiochipint, 4, 1);
 
     // Switch MUX to MCU
-    set_gpio_raw(gpiochipint, 5, 1);
+    gpio::set_raw(gpiochipint, 5, 1);
 }
 
 bool hmc_is_present()
@@ -332,55 +72,55 @@
 int init_nvidia_gb200(bool has_p2020)
 {
     // Reset USB hubs
-    set_gpio("USB_HUB_RESET_L-O", 0, 10000ms);
+    gpio::set("USB_HUB_RESET_L-O", 0, 10000ms);
     bool hmc_present = hmc_is_present();
     if (!hmc_present)
     {
-        set_gpio("SEC_USB2_HUB_RST_L-O", 0, 10000ms);
+        gpio::set("SEC_USB2_HUB_RST_L-O", 0, 10000ms);
     }
 
     sleep_milliseconds(100ms);
     if (!hmc_present)
     {
-        set_gpio("SEC_USB2_HUB_RST_L-O", 1);
+        gpio::set("SEC_USB2_HUB_RST_L-O", 1);
     }
     //  Write SGPIO_BMC_EN-O=1 to correctly set mux to send SGPIO signals to
     //  FPGA
-    set_gpio("SGPIO_BMC_EN-O", 1);
+    gpio::set("SGPIO_BMC_EN-O", 1);
 
     // Write the bit for BMC without HMC
-    set_gpio("HMC_BMC_DETECT-O", static_cast<int>(!hmc_present), 30000ms);
+    gpio::set("HMC_BMC_DETECT-O", static_cast<int>(!hmc_present), 30000ms);
 
     // Set BMC_EROT_FPGA_SPI_MUX_SEL-O = 1 to enable FPGA to access its EROT
-    set_gpio("BMC_EROT_FPGA_SPI_MUX_SEL-O", 1);
+    gpio::set("BMC_EROT_FPGA_SPI_MUX_SEL-O", 1);
 
     // Enable 12V
-    set_gpio("BMC_12V_CTRL-O", 1, 10000ms);
+    gpio::set("BMC_12V_CTRL-O", 1, 10000ms);
 
-    set_gpio("PWR_BRAKE_L-O", 1);
-    set_gpio("SHDN_REQ_L-O", 1);
-    set_gpio("SHDN_FORCE_L-O", 1);
+    gpio::set("PWR_BRAKE_L-O", 1);
+    gpio::set("SHDN_REQ_L-O", 1);
+    gpio::set("SHDN_FORCE_L-O", 1);
     // Hold in reset (asserted) after standby power enabled
-    set_gpio("SYS_RST_IN_L-O", 0);
+    gpio::set("SYS_RST_IN_L-O", 0);
 
-    GpioEvent fpga_ready_wait = GpioEvent("FPGA_READY_BMC-I", 1);
-    GpioEvent sec_erot_fpga_rst = GpioEvent("SEC_FPGA_READY_BMC-I", 1);
+    gpio::Event fpga_ready_wait = gpio::Event("FPGA_READY_BMC-I", 1);
+    gpio::Event sec_erot_fpga_rst = gpio::Event("SEC_FPGA_READY_BMC-I", 1);
 
     // Release FPGA EROT from reset
-    set_gpio("EROT_FPGA_RST_L-O", 1);
-    set_gpio("SEC_EROT_FPGA_RST_L-O", 1);
+    gpio::set("EROT_FPGA_RST_L-O", 1);
+    gpio::set("SEC_EROT_FPGA_RST_L-O", 1);
 
     sleep_milliseconds(100ms);
 
-    set_gpio("FPGA_RST_L-O", 1);
+    gpio::set("FPGA_RST_L-O", 1);
 
-    if (fpga_ready_wait.wait() != GpioEventResult::Asserted)
+    if (fpga_ready_wait.wait() != gpio::EventResult::Asserted)
     {
         std::cerr << "FPGA_READY_BMC-I failed to assert\n";
         // return EXIT_FAILURE;
     }
 
-    if (sec_erot_fpga_rst.wait() != GpioEventResult::Asserted)
+    if (sec_erot_fpga_rst.wait() != gpio::EventResult::Asserted)
     {
         std::cerr << "SEC_FPGA_READY_BMC-I failed to assert\n";
         // return EXIT_FAILURE;
@@ -388,26 +128,26 @@
 
     // ReInitialize the FPGA connected I2C buses to unstick them and let
     // FruDevice know it can scan for FRUs I2c bus 1
-    rebind_i2c("1e78a100");
+    i2c::rebind_controller("1e78a100");
     // I2c bus 2
-    rebind_i2c("1e78a180");
+    i2c::rebind_controller("1e78a180");
 
     // Set sgpio signals
-    set_gpio("RUN_POWER_EN-O", 1);
-    set_gpio("SYS_RST_IN_L-O", 1);
-    set_gpio("GLOBAL_WP_BMC-O", 0);
+    gpio::set("RUN_POWER_EN-O", 1);
+    gpio::set("SYS_RST_IN_L-O", 1);
+    gpio::set("GLOBAL_WP_BMC-O", 0);
 
-    set_gpio("BMC_READY-O", 1);
+    gpio::set("BMC_READY-O", 1);
 
     if (has_p2020)
     {
         init_p2020_gpu_card();
     }
 
-    set_gpio("USB_HUB_RESET_L-O", 1);
+    gpio::set("USB_HUB_RESET_L-O", 1);
     if (!hmc_present)
     {
-        set_gpio("SEC_USB2_HUB_RST_L-O", 1);
+        gpio::set("SEC_USB2_HUB_RST_L-O", 1);
     }
 
     sd_notify(0, "READY=1");