nvl32: Add nvl32 target

Also adds an i2c device class to allow us to send raw i2c

Tested:
ran on the nvl32-obmc model and i2c devices were enumerated correctly.
as well as mctp devices

Change-Id: I073156de2bbe06b06017379de35e076166df3875
Signed-off-by: Marc Olberding <molberding@nvidia.com>
diff --git a/i2c.cpp b/i2c.cpp
index e6d60e1..cc8be13 100644
--- a/i2c.cpp
+++ b/i2c.cpp
@@ -3,9 +3,22 @@
 
 #include "i2c.hpp"
 
+#include <fcntl.h>
+#include <linux/i2c-dev.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <filesystem>
 #include <format>
 #include <fstream>
 #include <iostream>
+
+extern "C"
+{
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+}
+
 namespace i2c
 {
 
@@ -68,4 +81,44 @@
 
     std::cerr << std::format("{} device created at bus {}", device_type, bus);
 }
+
+RawDevice::RawDevice(size_t bus, uint8_t address)
+{
+    std::string bus_path = std::format("/dev/i2c-{}", bus);
+    std::filesystem::path dev_path = bus_path;
+    fd = open(dev_path.c_str(), O_RDWR);
+    if (fd < 0)
+    {
+        std::cerr << std::format("failed to open {}\n", dev_path.native());
+        throw std::runtime_error(
+            std::format("Failed to open {}", dev_path.native()));
+    }
+
+    if (ioctl(fd, I2C_SLAVE, address) < 0)
+    {
+        // dtor won't be called since we never finished constructing it, clean
+        // up our fd
+        close(fd);
+        throw std::runtime_error(
+            std::format("Failed to specify address {}", address));
+    }
+}
+
+RawDevice::~RawDevice()
+{
+    close(fd);
+}
+
+int RawDevice::read_byte(uint8_t reg, uint8_t& val)
+{
+    int result = i2c_smbus_read_byte_data(fd, reg);
+    if (result < 0)
+    {
+        return -result;
+    }
+
+    val = result;
+    return 0;
+}
+
 } // namespace i2c