blob: 0dde0d8553e11c40b8da10599c26d021ca733a8d [file] [log] [blame]
Marc Olberding5d50e522025-09-03 18:23:32 -07001#include "gpio.hpp"
2#include "i2c.hpp"
3#include "utilities.hpp"
4
5#include <systemd/sd-daemon.h>
6
7#include <chrono>
8#include <filesystem>
9#include <fstream>
10#include <iostream>
11#include <thread>
12
13namespace nvidia
14{
15
16using steady_clock = std::chrono::steady_clock;
17using namespace std::chrono_literals;
18
19void logged_system(std::string_view cmd)
20{
21 std::cerr << std::format("calling {} \n", cmd);
22 int rc = std::system(cmd.data());
23 (void)rc;
24}
25
26void setup_devmem()
27{
28 logged_system("mknod /dev/mem c 1 1");
29}
30
31void handle_passthrough_registers(bool enable)
32{
33 static constexpr uint32_t reg = 0x1e6e24bc;
34 std::string command;
35 if (enable)
36 {
37 command = std::format("devmem 0x{:x} 32 0x3f000000", reg);
38 }
39 else
40 {
41 command = std::format("devmem 0x{:x} 32 0", reg);
42 }
43 logged_system(command);
44}
45
46void wait_for_i2c_ready()
47{
48 // hpm cpld is at bus 4, address 0x17
49 i2c::RawDevice cpld{4, 0x17};
50 auto now = steady_clock::now();
51 auto end = now + 20min;
52 while (steady_clock::now() < end)
53 {
54 static constexpr uint8_t i2c_ready = 0xf2;
55 uint8_t result;
56 int rc = cpld.read_byte(i2c_ready, result);
57 if (rc)
58 {
59 std::string err =
60 std::format("Unable to communicate with cpld. rc: {}\n", rc);
61 std::cerr << err;
62 throw std::runtime_error(err);
63 }
64
65 if (result == 1)
66 {
67 return;
68 }
69
70 std::this_thread::sleep_for(std::chrono::seconds{10});
71 }
72
73 throw std::runtime_error("Waiting for host timed out!\n");
74}
75
76void probe_dev(size_t bus, uint8_t address, std::string_view dev_type)
77{
78 std::filesystem::path path =
79 std::format("/sys/bus/i2c/devices/i2c-{}/new_device", bus);
80
81 std::ofstream f{path};
82 if (!f.good())
83 {
84 std::cerr << std::format("Unable to open {}\n", path.c_str());
85 std::exit(EXIT_FAILURE);
86 }
87
88 f << std::format("{} 0x{:02x}", dev_type, address);
89 f.close();
90
91 std::string created_path =
92 std::format("/sys/bus/i2c/devices/{}-{:04x}", bus, address);
93 wait_for_path_to_exist(created_path, 10ms);
94}
95
96void create_i2c_mux(size_t bus, uint8_t address, std::string_view dev_type)
97{
98 probe_dev(bus, address, dev_type);
99
100 std::filesystem::path idle =
101 std::format("/sys/bus/i2c/devices/{}-{:04x}/idle_state", bus, address);
102 std::ofstream idle_f{idle};
103 if (!idle_f.good())
104 {
105 std::string err = std::format("Unable to open {}\n", idle.c_str());
106 std::cerr << err;
107 throw std::runtime_error(err);
108 }
109
110 // -2 is idle-mux-disconnect
111 idle_f << -2;
112 idle_f.close();
113}
114
115size_t get_bus_from_channel(size_t parent_bus, uint8_t address, size_t channel)
116{
117 std::filesystem::path path =
118 std::format("/sys/bus/i2c/devices/{}-{:04x}/channel-{}/i2c-dev/",
119 parent_bus, address, channel);
120 int bus = -1;
121 std::error_code ec{};
122 for (const auto& f : std::filesystem::directory_iterator(path, ec))
123 {
124 // we expect to see i2c-<bus>, trim and parse everything after the dash
125 const std::string& p = f.path().filename().string();
126 std::cerr << "Reading from " << p << "\n";
127 auto [_, err] = std::from_chars(p.data() + 4, p.data() + p.size(), bus);
128 if (err != std::errc{})
129 {
130 std::string err_s = std::format("Failed to parse {}\n", p);
131 std::cerr << err_s;
132 throw std::runtime_error(err_s);
133 }
134 }
135 if (bus == -1 || ec)
136 {
137 std::string err_s =
138 std::format("Failed to find a channel at {}\n", path.string());
139 std::cerr << err_s;
140 throw std::runtime_error(err_s);
141 }
142 return bus;
143}
144
145void bringup_cx8_mcu(size_t bus)
146{
147 probe_dev(bus, 0x26, "pca9555");
148 std::string gpio_p =
149 std::format("/sys/bus/i2c/devices/{}-{:04x}/", bus, 0x26);
150 int chip_num = gpio::find_chip_idx_from_dir(gpio_p);
151 if (chip_num < 0)
152 {
153 std::cerr << std::format("Failed to find cx8 gpio at {}\n", gpio_p);
154 std::exit(EXIT_FAILURE);
155 }
156
157 // 14 is the reset pin on the MCU
158 // reset pin is active low
159 gpio::set_raw(chip_num, 14, 1);
160}
161
162void gringup_gpu_sma(size_t bus, size_t channel)
163{
164 size_t gpu_bus = get_bus_from_channel(bus, 0x72, channel);
165 probe_dev(gpu_bus, 0x20, "pca6408");
166 std::string gpio_p =
167 std::format("/sys/bus/i2c/devices/{}-{:04x}/", gpu_bus, 0x20);
168 int chip_num = gpio::find_chip_idx_from_dir(gpio_p);
169 if (chip_num < 0)
170 {
171 std::cerr << std::format("Failed to find gpu gpio {}\n", gpio_p);
172 std::exit(EXIT_FAILURE);
173 }
174
175 // pin 4 is the reset pin, active low
176 // pin 5 engages the telemetry path from the SMA
177 gpio::set_raw(chip_num, 5, 1);
178 gpio::set_raw(chip_num, 4, 1);
179}
180
181void bringup_gpus_on_mcio(size_t bus)
182{
183 create_i2c_mux(bus, 0x72, "pca9546");
184
185 gringup_gpu_sma(bus, 2);
186 gringup_gpu_sma(bus, 3);
187}
188
189void bringup_cx8_mcio(size_t mux_addr, size_t channel, bool has_cx8)
190{
191 size_t bus = get_bus_from_channel(5, mux_addr, channel);
192 if (has_cx8)
193 {
194 bringup_cx8_mcu(bus);
195 }
196 bringup_gpus_on_mcio(bus);
197}
198
199void enumerate_mctp(int dev_num)
200{
201 // TODO: Make this a proper dbus client
202 std::string preamble = "busctl call au.com.codeconstruct.MCTP1";
203 std::string postamble =
204 "au.com.codeconstruct.MCTP1.BusOwner1 AssignEndpoint ay 0";
205
206 std::string cmd =
207 std::format("{} /au/com/codeconstruct/mctp1/interfaces/mctpusb{} {}",
208 preamble, dev_num, postamble);
209 logged_system(cmd);
210}
211
212void wait_for_usb_to_probe()
213{
214 std::this_thread::sleep_for(std::chrono::seconds{20});
215}
216
217int init_nvl32()
218{
219 setup_devmem();
220 handle_passthrough_registers(false);
221 sd_notify(0, "READY=1");
222
223 wait_for_i2c_ready();
224
225 create_i2c_mux(5, 0x70, "pca9548");
226 create_i2c_mux(5, 0x71, "pca9548");
227 create_i2c_mux(5, 0x73, "pca9548");
228 create_i2c_mux(5, 0x75, "pca9548");
229
230 bringup_cx8_mcio(0x70, 1, true);
231 bringup_cx8_mcio(0x70, 5, false);
232 bringup_cx8_mcio(0x73, 3, true);
233 bringup_cx8_mcio(0x73, 7, false);
234
235 wait_for_usb_to_probe();
236 for (int ctr = 0; ctr < 10; ++ctr)
237 {
238 enumerate_mctp(ctr);
239 }
240 std::cerr << "platform init complete\n";
241 pause();
242 std::cerr << "Releasing platform\n";
243
244 return EXIT_SUCCESS;
245}
246
247} // namespace nvidia