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
diff --git a/gpio.hpp b/gpio.hpp
new file mode 100644
index 0000000..09d479e
--- /dev/null
+++ b/gpio.hpp
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#pragma once
+
+#include <gpiod.hpp>
+
+#include <chrono>
+#include <string>
+
+namespace gpio
+{
+void set(const char* line_name, int value,
+         std::chrono::milliseconds find_timeout = std::chrono::milliseconds{1});
+
+void set_raw(unsigned int chip_num, unsigned bit_num, int value);
+
+int get(const char* line_name);
+
+enum class EventResult
+{
+    Error,
+    Asserted,
+    Timeout
+};
+
+struct Event
+{
+    Event(const char* line_name_in, int value_in);
+    EventResult wait();
+
+    gpiod::line line;
+    std::string line_name;
+    int value;
+};
+
+int find_chip_idx_from_dir(std::string_view device_path);
+
+} // namespace gpio
diff --git a/i2c.cpp b/i2c.cpp
new file mode 100644
index 0000000..e6d60e1
--- /dev/null
+++ b/i2c.cpp
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#include "i2c.hpp"
+
+#include <format>
+#include <fstream>
+#include <iostream>
+namespace i2c
+{
+
+void rebind_controller(std::string_view 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 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);
+}
+} // namespace i2c
diff --git a/i2c.hpp b/i2c.hpp
new file mode 100644
index 0000000..6685162
--- /dev/null
+++ b/i2c.hpp
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#pragma once
+
+#include <string>
+
+namespace i2c
+{
+
+void rebind_controller(const std::string_view number);
+void new_device(unsigned int bus, unsigned int address,
+                std::string_view device_type);
+} // namespace i2c
diff --git a/meson.build b/meson.build
index 26dbac2..837e289 100644
--- a/meson.build
+++ b/meson.build
@@ -14,7 +14,7 @@
 
 exe = executable(
     'platform',
-    'platform.cpp',
+    ['platform.cpp', 'i2c.cpp', 'gpio.cpp', 'utilities.cpp'],
     dependencies: [gpiodcxx_dep, libsystemd_dep, cli11_dep],
     include_directories: ['.'],
     install: true,
@@ -31,3 +31,5 @@
         {'PLATFORM_NAME': get_option('platform-name')},
     ),
 )
+
+
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");
diff --git a/utilities.cpp b/utilities.cpp
new file mode 100644
index 0000000..e27ceb3
--- /dev/null
+++ b/utilities.cpp
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#include "utilities.hpp"
+
+#include <chrono>
+#include <filesystem>
+#include <format>
+#include <iostream>
+#include <thread>
+
+using namespace std::chrono_literals;
+
+void sleep_milliseconds(std::chrono::milliseconds milliseconds)
+{
+    std::cerr << std::format("Sleeping for {} milliseconds\n",
+                             milliseconds.count());
+    std::this_thread::sleep_for(milliseconds);
+}
+
+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);
+}
diff --git a/utilities.hpp b/utilities.hpp
new file mode 100644
index 0000000..6c581ce
--- /dev/null
+++ b/utilities.hpp
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: 2025 NVIDIA
+
+#pragma once
+
+#include <chrono>
+#include <string_view>
+
+constexpr static const char* app_name = "platform_init";
+
+void sleep_milliseconds(std::chrono::milliseconds milliseconds);
+
+void wait_for_path_to_exist(std::string_view path,
+                            std::chrono::milliseconds timeout);