blob: 0dde0d8553e11c40b8da10599c26d021ca733a8d [file] [log] [blame]
#include "gpio.hpp"
#include "i2c.hpp"
#include "utilities.hpp"
#include <systemd/sd-daemon.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
namespace nvidia
{
using steady_clock = std::chrono::steady_clock;
using namespace std::chrono_literals;
void logged_system(std::string_view cmd)
{
std::cerr << std::format("calling {} \n", cmd);
int rc = std::system(cmd.data());
(void)rc;
}
void setup_devmem()
{
logged_system("mknod /dev/mem c 1 1");
}
void handle_passthrough_registers(bool enable)
{
static constexpr uint32_t reg = 0x1e6e24bc;
std::string command;
if (enable)
{
command = std::format("devmem 0x{:x} 32 0x3f000000", reg);
}
else
{
command = std::format("devmem 0x{:x} 32 0", reg);
}
logged_system(command);
}
void wait_for_i2c_ready()
{
// hpm cpld is at bus 4, address 0x17
i2c::RawDevice cpld{4, 0x17};
auto now = steady_clock::now();
auto end = now + 20min;
while (steady_clock::now() < end)
{
static constexpr uint8_t i2c_ready = 0xf2;
uint8_t result;
int rc = cpld.read_byte(i2c_ready, result);
if (rc)
{
std::string err =
std::format("Unable to communicate with cpld. rc: {}\n", rc);
std::cerr << err;
throw std::runtime_error(err);
}
if (result == 1)
{
return;
}
std::this_thread::sleep_for(std::chrono::seconds{10});
}
throw std::runtime_error("Waiting for host timed out!\n");
}
void probe_dev(size_t bus, uint8_t address, std::string_view dev_type)
{
std::filesystem::path path =
std::format("/sys/bus/i2c/devices/i2c-{}/new_device", bus);
std::ofstream f{path};
if (!f.good())
{
std::cerr << std::format("Unable to open {}\n", path.c_str());
std::exit(EXIT_FAILURE);
}
f << std::format("{} 0x{:02x}", dev_type, address);
f.close();
std::string created_path =
std::format("/sys/bus/i2c/devices/{}-{:04x}", bus, address);
wait_for_path_to_exist(created_path, 10ms);
}
void create_i2c_mux(size_t bus, uint8_t address, std::string_view dev_type)
{
probe_dev(bus, address, dev_type);
std::filesystem::path idle =
std::format("/sys/bus/i2c/devices/{}-{:04x}/idle_state", bus, address);
std::ofstream idle_f{idle};
if (!idle_f.good())
{
std::string err = std::format("Unable to open {}\n", idle.c_str());
std::cerr << err;
throw std::runtime_error(err);
}
// -2 is idle-mux-disconnect
idle_f << -2;
idle_f.close();
}
size_t get_bus_from_channel(size_t parent_bus, uint8_t address, size_t channel)
{
std::filesystem::path path =
std::format("/sys/bus/i2c/devices/{}-{:04x}/channel-{}/i2c-dev/",
parent_bus, address, channel);
int bus = -1;
std::error_code ec{};
for (const auto& f : std::filesystem::directory_iterator(path, ec))
{
// we expect to see i2c-<bus>, trim and parse everything after the dash
const std::string& p = f.path().filename().string();
std::cerr << "Reading from " << p << "\n";
auto [_, err] = std::from_chars(p.data() + 4, p.data() + p.size(), bus);
if (err != std::errc{})
{
std::string err_s = std::format("Failed to parse {}\n", p);
std::cerr << err_s;
throw std::runtime_error(err_s);
}
}
if (bus == -1 || ec)
{
std::string err_s =
std::format("Failed to find a channel at {}\n", path.string());
std::cerr << err_s;
throw std::runtime_error(err_s);
}
return bus;
}
void bringup_cx8_mcu(size_t bus)
{
probe_dev(bus, 0x26, "pca9555");
std::string gpio_p =
std::format("/sys/bus/i2c/devices/{}-{:04x}/", bus, 0x26);
int chip_num = gpio::find_chip_idx_from_dir(gpio_p);
if (chip_num < 0)
{
std::cerr << std::format("Failed to find cx8 gpio at {}\n", gpio_p);
std::exit(EXIT_FAILURE);
}
// 14 is the reset pin on the MCU
// reset pin is active low
gpio::set_raw(chip_num, 14, 1);
}
void gringup_gpu_sma(size_t bus, size_t channel)
{
size_t gpu_bus = get_bus_from_channel(bus, 0x72, channel);
probe_dev(gpu_bus, 0x20, "pca6408");
std::string gpio_p =
std::format("/sys/bus/i2c/devices/{}-{:04x}/", gpu_bus, 0x20);
int chip_num = gpio::find_chip_idx_from_dir(gpio_p);
if (chip_num < 0)
{
std::cerr << std::format("Failed to find gpu gpio {}\n", gpio_p);
std::exit(EXIT_FAILURE);
}
// pin 4 is the reset pin, active low
// pin 5 engages the telemetry path from the SMA
gpio::set_raw(chip_num, 5, 1);
gpio::set_raw(chip_num, 4, 1);
}
void bringup_gpus_on_mcio(size_t bus)
{
create_i2c_mux(bus, 0x72, "pca9546");
gringup_gpu_sma(bus, 2);
gringup_gpu_sma(bus, 3);
}
void bringup_cx8_mcio(size_t mux_addr, size_t channel, bool has_cx8)
{
size_t bus = get_bus_from_channel(5, mux_addr, channel);
if (has_cx8)
{
bringup_cx8_mcu(bus);
}
bringup_gpus_on_mcio(bus);
}
void enumerate_mctp(int dev_num)
{
// TODO: Make this a proper dbus client
std::string preamble = "busctl call au.com.codeconstruct.MCTP1";
std::string postamble =
"au.com.codeconstruct.MCTP1.BusOwner1 AssignEndpoint ay 0";
std::string cmd =
std::format("{} /au/com/codeconstruct/mctp1/interfaces/mctpusb{} {}",
preamble, dev_num, postamble);
logged_system(cmd);
}
void wait_for_usb_to_probe()
{
std::this_thread::sleep_for(std::chrono::seconds{20});
}
int init_nvl32()
{
setup_devmem();
handle_passthrough_registers(false);
sd_notify(0, "READY=1");
wait_for_i2c_ready();
create_i2c_mux(5, 0x70, "pca9548");
create_i2c_mux(5, 0x71, "pca9548");
create_i2c_mux(5, 0x73, "pca9548");
create_i2c_mux(5, 0x75, "pca9548");
bringup_cx8_mcio(0x70, 1, true);
bringup_cx8_mcio(0x70, 5, false);
bringup_cx8_mcio(0x73, 3, true);
bringup_cx8_mcio(0x73, 7, false);
wait_for_usb_to_probe();
for (int ctr = 0; ctr < 10; ++ctr)
{
enumerate_mctp(ctr);
}
std::cerr << "platform init complete\n";
pause();
std::cerr << "Releasing platform\n";
return EXIT_SUCCESS;
}
} // namespace nvidia