diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7354b8a0..a46fc070 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: path: target/debug/framework_tool - name: Build Linux tool (Release) - run: cargo build -p framework_tool --release + run: cargo build -p framework_tool --release --features nvidia - name: Upload Linux App uses: actions/upload-artifact@v4 @@ -124,7 +124,7 @@ jobs: - name: Build Windows tool run: | cargo build -p framework_tool - cargo build -p framework_tool --release + cargo build -p framework_tool --release --features nvidia - name: Check if Windows tool can start run: cargo run -- --help --release diff --git a/Cargo.lock b/Cargo.lock index be5f5f4d..b1f289ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -331,6 +331,41 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.98", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.98", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -397,6 +432,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -425,6 +466,7 @@ dependencies = [ "num", "num-derive", "num-traits", + "nvml-wrapper", "plain", "redox_hwio", "regex", @@ -646,6 +688,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.3.0" @@ -733,6 +781,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.0", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -921,6 +979,29 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nvml-wrapper" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5c6c0ef9702176a570f06ad94f3198bc29c524c8b498f1b9346e1b1bdcbb3a" +dependencies = [ + "bitflags 2.6.0", + "libloading", + "nvml-wrapper-sys", + "static_assertions", + "thiserror 1.0.69", + "wrapcenum-derive", +] + +[[package]] +name = "nvml-wrapper-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd23dbe2eb8d8335d2bce0299e0a07d6a63c089243d626ca75b770a962ff49e6" +dependencies = [ + "libloading", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -1170,6 +1251,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "static_vcruntime" version = "2.0.0" @@ -1213,13 +1300,33 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -1856,7 +1963,19 @@ dependencies = [ "futures", "log", "serde", - "thiserror", + "thiserror 2.0.11", "windows 0.59.0", "windows-core", ] + +[[package]] +name = "wrapcenum-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.98", +] diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 162538f4..4213b22c 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -18,6 +18,7 @@ readonly = [ ] rusb = ["dep:rusb"] hidapi = ["dep:hidapi"] uefi = [ "lazy_static/spin_no_std" ] +nvidia = ["dep:nvml-wrapper"] [build-dependencies] built = { version = "0.5", features = ["chrono", "git2"] } @@ -52,6 +53,7 @@ clap-num = { version = "1.2.0" } clap-verbosity-flag = { version = "2.2.1" } windows-version = "0.1.4" winreg = "0.55.0" +nvml-wrapper = { version = "0.11.0", optional = true } [target.'cfg(unix)'.dependencies] libc = "0.2.155" @@ -62,6 +64,7 @@ env_logger = "0.11" clap = { version = "4.5", features = ["derive", "cargo"] } clap-num = { version = "1.2.0" } clap-verbosity-flag = { version = "2.2.1" } +nvml-wrapper = { version = "0.11.0", optional = true } [target.'cfg(windows)'.dependencies.windows] version = "0.59.0" diff --git a/framework_lib/src/chromium_ec/i2c_passthrough.rs b/framework_lib/src/chromium_ec/i2c_passthrough.rs index 6b0a0f00..019fcdee 100644 --- a/framework_lib/src/chromium_ec/i2c_passthrough.rs +++ b/framework_lib/src/chromium_ec/i2c_passthrough.rs @@ -121,6 +121,66 @@ pub fn i2c_read( }) } +/// I2C read with 16-bit addressing (for larger EEPROMs like 24C32+) +/// Always sends a 2-byte address, even for addr=0 +pub fn i2c_read_16bit_addr( + ec: &CrosEc, + i2c_port: u8, + i2c_addr: u16, + addr: u16, + len: u16, +) -> EcResult { + trace!( + "i2c_read_16bit_addr(i2c_port: 0x{:X}, i2c_addr: 0x{:X}, addr: 0x{:X}, len: 0x{:X})", + i2c_port, + i2c_addr, + addr, + len + ); + if usize::from(len) > MAX_I2C_CHUNK { + return EcResult::Err(EcError::DeviceError(format!( + "i2c_read too long. Must be <128, is: {}", + len + ))); + } + // Always use 16-bit addressing (big-endian for EEPROM) + let addr_bytes = u16::to_be_bytes(addr).to_vec(); + let messages = vec![ + EcParamsI2cPassthruMsg { + addr_and_flags: i2c_addr, + transfer_len: addr_bytes.len() as u16, + }, + EcParamsI2cPassthruMsg { + addr_and_flags: i2c_addr + I2C_READ_FLAG, + transfer_len: len, // How much to read + }, + ]; + let msgs_len = size_of::() * messages.len(); + let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) }; + + let params = EcParamsI2cPassthru { + port: i2c_port, + messages: messages.len() as u8, + msg: [], // Messages are copied right after this struct + }; + let params_len = size_of::(); + let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(¶ms) }; + + let mut buffer: Vec = vec![0; params_len + msgs_len + addr_bytes.len()]; + buffer[0..params_len].copy_from_slice(params_buffer); + buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer); + buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes); + + let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; + let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; + let res_data = &data[size_of::<_EcI2cPassthruResponse>()..]; + debug_assert!(res.messages as usize == messages.len() || res.messages == 0); + Ok(EcI2cPassthruResponse { + i2c_status: res.i2c_status, + data: res_data.to_vec(), + }) +} + /* Write address and bytes in a single I2C transfer */ pub fn i2c_write_block( ec: &CrosEc, diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index f2d980f6..f69e395e 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1295,7 +1295,8 @@ impl CrosEc { let remaining = len - data.len() as u16; let chunk_len = std::cmp::min(i2c_passthrough::MAX_I2C_CHUNK, remaining.into()); let offset = addr + data.len() as u16; - let i2c_response = i2c_passthrough::i2c_read( + // Use 16-bit addressing for GPU EEPROM (required for larger EEPROMs) + let i2c_response = i2c_passthrough::i2c_read_16bit_addr( self, eeprom_port, eeprom_addr, @@ -1311,7 +1312,7 @@ impl CrosEc { data.extend(i2c_response.data); } - Ok(data) + Ok(data[..(len.into())].to_vec()) } pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 8df96ed9..a2727903 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -288,6 +288,10 @@ struct ClapCli { /// File to dump the gpu EEPROM to #[arg(long)] dump_gpu_descriptor_file: Option, + + /// Show NVIDIA GPU information (Framework 16 only) + #[arg(long)] + nvidia: bool, } /// Parse a list of commandline arguments and return the struct @@ -484,6 +488,7 @@ pub fn parse(args: &[String]) -> Cli { dump_gpu_descriptor_file: args .dump_gpu_descriptor_file .map(|x| x.into_os_string().into_string().unwrap()), + nvidia: args.nvidia, raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index cf45bdbe..ccc37490 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -72,6 +72,9 @@ use sha2::{Digest, Sha256, Sha384, Sha512}; //use smbioslib::*; use smbioslib::{DefinedStruct, SMBiosInformation}; +#[cfg(feature = "nvidia")] +use nvml_wrapper::{enum_wrappers::device::TemperatureSensor, Nvml}; + use crate::chromium_ec::{CrosEc, CrosEcDriverType, HardwareDeviceType}; #[cfg(feature = "uefi")] @@ -221,6 +224,7 @@ pub struct Cli { pub flash_gpu_descriptor: Option<(u8, String)>, pub flash_gpu_descriptor_file: Option, pub dump_gpu_descriptor_file: Option, + pub nvidia: bool, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -306,6 +310,7 @@ pub fn parse(args: &[String]) -> Cli { info: cli.info, // flash_gpu_descriptor // flash_gpu_descriptor_file + nvidia: cli.nvidia, // allupdate paginate: cli.paginate, // raw_command @@ -767,6 +772,216 @@ fn print_versions(ec: &CrosEc) { } } } + + #[cfg(feature = "nvidia")] + print_nvidia_details(); +} + +/// Brief NVIDIA details for --version output +#[cfg(feature = "nvidia")] +fn print_nvidia_details() { + let nvml = match Nvml::init() { + Ok(nvml) => nvml, + Err(err) => { + error!("Nvidia, library init fail: {:?}", err); + return; + } + }; + let device = match nvml.device_by_index(0) { + Ok(device) => device, + Err(err) => { + error!("Nvidia, device not found: {:?}", err); + return; + } + }; + + println!("NVIDIA GPU"); + println!( + " Name: {}", + device.name().unwrap_or("Unknown".to_string()) + ); + println!( + " VBIOS Version: {}", + device.vbios_version().unwrap_or("Unknown".to_string()) + ); +} + +/// Detailed NVIDIA information for --nvidia command +#[cfg(feature = "nvidia")] +fn print_nvidia_info() { + let nvml = match Nvml::init() { + Ok(nvml) => nvml, + Err(err) => { + error!("Nvidia, library init fail: {:?}", err); + return; + } + }; + let device = match nvml.device_by_index(0) { + Ok(device) => device, + Err(err) => { + error!("Nvidia, device not found: {:?}", err); + return; + } + }; + + println!("NVIDIA GPU"); + + // Basic identification + println!("Identification"); + println!( + " Name: {}", + device.name().unwrap_or("Unknown".to_string()) + ); + if let Ok(arch) = device.architecture() { + println!(" Architecture: {:?}", arch); + } + if let Ok(serial) = device.serial() { + println!(" Serial Number: {}", serial); + } + if let Ok(part_number) = device.board_part_number() { + println!(" Part Number: {}", part_number); + } + if let Ok(board_id) = device.board_id() { + println!(" Board ID: {}", board_id); + } + + // Firmware versions + println!("Firmware"); + println!( + " VBIOS Version: {}", + device.vbios_version().unwrap_or("Unknown".to_string()) + ); + println!( + " InfoROM Version: {}", + device + .info_rom_image_version() + .unwrap_or("Unknown".to_string()) + ); + + // PCI information + println!("PCI"); + if let Ok(pci) = device.pci_info() { + println!(" Bus: {:02X}", pci.bus); + println!(" Device: {:02X}", pci.device); + println!(" Domain: {:04X}", pci.domain); + println!(" Device ID: {:04X}", pci.pci_device_id); + if let Some(sub_id) = pci.pci_sub_system_id { + println!(" Subsystem ID: {:08X}", sub_id); + } + } + + // Power information + println!("Power"); + if let Ok(state) = device.performance_state() { + println!(" Performance State: {:?}", state); + } + if let Ok(power) = device.power_usage() { + println!(" Current Usage: {:.2} W", power as f64 / 1000.0); + } + if let Ok(limit) = device.power_management_limit() { + println!(" Power Limit: {:.2} W", limit as f64 / 1000.0); + } + if let Ok(default) = device.power_management_limit_default() { + println!(" Default Limit: {:.2} W", default as f64 / 1000.0); + } + if let Ok(constraints) = device.power_management_limit_constraints() { + println!( + " Min Limit: {:.2} W", + constraints.min_limit as f64 / 1000.0 + ); + println!( + " Max Limit: {:.2} W", + constraints.max_limit as f64 / 1000.0 + ); + } + if let Ok(energy) = device.total_energy_consumption() { + println!(" Total Energy: {:.2} J", energy as f64 / 1000.0); + } + + // Thermal information + println!("Thermal"); + if let Ok(temp) = device.temperature(TemperatureSensor::Gpu) { + println!(" GPU Temperature: {}C", temp); + } + if let Ok(num_fans) = device.num_fans() { + println!(" Number of Fans: {}", num_fans); + } + + // Throttling + if let Ok(throttle) = device.current_throttle_reasons() { + println!("Throttle Reasons"); + if throttle.is_empty() { + println!(" None"); + } else { + if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::GPU_IDLE) { + println!(" GPU Idle"); + } + if throttle.contains( + nvml_wrapper::bitmasks::device::ThrottleReasons::APPLICATIONS_CLOCKS_SETTING, + ) { + println!(" Applications Clocks Setting"); + } + if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SW_POWER_CAP) { + println!(" Software Power Cap"); + } + if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_SLOWDOWN) { + println!(" Hardware Slowdown"); + } + if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SYNC_BOOST) { + println!(" Sync Boost"); + } + if throttle + .contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SW_THERMAL_SLOWDOWN) + { + println!(" Software Thermal Slowdown"); + } + if throttle + .contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_THERMAL_SLOWDOWN) + { + println!(" Hardware Thermal Slowdown"); + } + if throttle + .contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_POWER_BRAKE_SLOWDOWN) + { + println!(" Hardware Power Brake Slowdown"); + } + if throttle + .contains(nvml_wrapper::bitmasks::device::ThrottleReasons::DISPLAY_CLOCK_SETTING) + { + println!(" Display Clock Setting"); + } + } + } + + // Utilization + println!("Utilization"); + if let Ok(util) = device.utilization_rates() { + println!(" GPU: {}%", util.gpu); + println!(" Memory: {}%", util.memory); + } + + // Memory information + println!("Memory"); + if let Ok(mem) = device.memory_info() { + let total_gb = mem.total as f64 / (1024.0 * 1024.0 * 1024.0); + let used_gb = mem.used as f64 / (1024.0 * 1024.0 * 1024.0); + let free_gb = mem.free as f64 / (1024.0 * 1024.0 * 1024.0); + println!(" Total: {:.2} GB", total_gb); + println!(" Used: {:.2} GB", used_gb); + println!(" Free: {:.2} GB", free_gb); + } + + // Display status + println!("Display"); + if let Ok(active) = device.is_display_active() { + println!(" Active: {}", if active { "Yes" } else { "No" }); + } + if let Ok(connected) = device.is_display_connected() { + println!( + " Connected: {}", + if connected { "Yes" } else { "No" } + ); + } } fn print_esrt() { @@ -822,18 +1037,66 @@ fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { } fn dump_dgpu_eeprom(ec: &CrosEc, dump_path: &str) { - let flash_bin = ec.read_gpu_descriptor().unwrap(); + // Read raw bytes from EEPROM + let raw_bytes = match ec.read_ec_gpu_chunk(0x00, 256) { + Ok(data) => data, + Err(err) => { + error!("Failed to read EEPROM: {:?}", err); + return; + } + }; + + // For stdout, just print the raw bytes + if dump_path == "-" { + println!("{:02X?}", raw_bytes); + println!("Read {} bytes from EEPROM", raw_bytes.len()); + return; + } + + // Check if header is valid by examining magic bytes + let expected_magic = [0x32, 0xAC, 0x00, 0x00]; + let has_valid_header = raw_bytes.len() >= 4 && raw_bytes[0..4] == expected_magic; + + let flash_bin = if has_valid_header { + // Header looks valid, try to read the full descriptor + match ec.read_gpu_descriptor() { + Ok(data) => data, + Err(err) => { + error!("GPU descriptor read failed: {:?}", err); + println!("Falling back to raw EEPROM dump (256 bytes)"); + raw_bytes + } + } + } else { + error!( + "GPU descriptor invalid: magic {:02X?} != expected {:02X?}", + &raw_bytes[0..4], + expected_magic + ); + println!("Dumping raw EEPROM (256 bytes)"); + raw_bytes + }; #[cfg(not(feature = "uefi"))] { - let mut file = fs::File::create(dump_path).unwrap(); - file.write_all(&flash_bin).unwrap(); + match fs::File::create(dump_path) { + Ok(mut file) => { + if let Err(err) = file.write_all(&flash_bin) { + error!("Failed to write file: {:?}", err); + return; + } + } + Err(err) => { + error!("Failed to create file: {:?}", err); + return; + } + } } #[cfg(feature = "uefi")] { - let ret = crate::uefi::fs::shell_write_file(dump_path, &flash_bin); - if ret.is_err() { - println!("Failed to dump EC FW image."); + if let Err(err) = crate::uefi::fs::shell_write_file(dump_path, &flash_bin) { + error!("Failed to dump EC FW image: {:?}", err); + return; } } println!("Wrote {} bytes to {}", flash_bin.len(), dump_path); @@ -1072,6 +1335,11 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Hdr CRC: {:X}", { header.crc32 }); } } + } else if args.nvidia { + #[cfg(feature = "nvidia")] + print_nvidia_info(); + #[cfg(not(feature = "nvidia"))] + error!("Not built with nvidia feature"); } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some((limit, soc)) = args.charge_current_limit { @@ -1454,7 +1722,9 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!("Unsupported on this platform"); } } else if let Some(dump_path) = &args.dump_gpu_descriptor_file { - println!("Dumping to {}", dump_path); + if dump_path != "-" { + println!("Dumping to {}", dump_path); + } dump_dgpu_eeprom(&ec, dump_path); } @@ -1502,6 +1772,7 @@ Options: --inputdeck Show status of the input deck --inputdeck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) --expansion-bay Show status of the expansion bay (Framework 16 only) + --nvidia Show NVIDIA GPU information (Framework 16 only) --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') --charge-current-limit [] Get or set battery current charge limit (Percentage number as arg, e.g. '100') --get-gpio Get GPIO value by name or all, if no name provided diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 877b7dac..cb2b4c5d 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -95,6 +95,7 @@ pub fn parse(args: &[String]) -> Cli { dump_gpu_descriptor_file: None, allupdate: false, info: false, + nvidia: false, raw_command: vec![], }; @@ -250,6 +251,9 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--expansion-bay" { cli.expansion_bay = true; found_an_option = true; + } else if arg == "--nvidia" { + cli.nvidia = true; + found_an_option = true; } else if arg == "--charge-limit" { cli.charge_limit = if args.len() > i + 1 { if let Ok(percent) = args[i + 1].parse::() { diff --git a/framework_lib/src/parade_retimer.rs b/framework_lib/src/parade_retimer.rs index f5d3b1f7..6f0cf8cd 100644 --- a/framework_lib/src/parade_retimer.rs +++ b/framework_lib/src/parade_retimer.rs @@ -10,10 +10,12 @@ use crate::os_specific; pub fn get_version(ec: &CrosEc) -> EcResult>> { let res = EcRequestGetGpuPcie {}.send_command(ec)?; let vendor: Option = FromPrimitive::from_u8(res.gpu_vendor); + info!("GPU vendor: {:?}", vendor); if vendor != Some(GpuVendor::NvidiaGn22) { - debug!("No compatible retimer present"); + info!("No compatible retimer present (vendor mismatch)"); return Ok(None); }; + info!("NVIDIA GPU detected, checking retimer..."); // I2C Port on the EC let i2c_port = 5; @@ -22,8 +24,27 @@ pub fn get_version(ec: &CrosEc) -> EcResult>> { let i2c_addr = 0x10; // Check safe mode + info!( + "Reading retimer at I2C port {} addr 0x{:02X}", + i2c_port, + i2c_addr >> 1 + ); let i2c_response = i2c_read(ec, i2c_port, i2c_addr >> 1, 0x00, 0x01)?; - if i2c_response.data[0] == 0 { + info!( + "I2C response: status=0x{:02X}, data_len={}", + i2c_response.i2c_status, + i2c_response.data.len() + ); + if i2c_response.i2c_status == 0x01 { + // NAK + warn!("Unable to communicate with dGPU Retimer. Try to force it on by plugging a cable into the dGPU"); + } + let Some(&safe_mode) = i2c_response.data.first() else { + info!("Failed to read retimer safe mode status (empty response)"); + return Ok(None); + }; + info!("Retimer safe mode status: {}", safe_mode); + if safe_mode == 0 { // Safe mode not enabled, enable it i2c_write(ec, i2c_port, i2c_addr >> 1, 0x00, &[0x01])?; } @@ -31,10 +52,11 @@ pub fn get_version(ec: &CrosEc) -> EcResult>> { // Wake up from low power mode for _ in 0..3 { let i2c_response = i2c_read(ec, i2c_port, (i2c_addr + 2) >> 1, 0x70, 0x01)?; - if i2c_response.data[0] != 0 { - i2c_write(ec, i2c_port, (i2c_addr + 2) >> 1, 0x70, &[0x00])?; - os_specific::sleep(50_000); + if i2c_response.data.first() == Some(&0) { + continue; } + i2c_write(ec, i2c_port, (i2c_addr + 2) >> 1, 0x70, &[0x00])?; + os_specific::sleep(50_000); } // Read version diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 6dceeabb..27c3c034 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -15,6 +15,7 @@ path = "src/main.rs" [features] default = [ ] readonly = [ "framework_lib/readonly" ] +nvidia = [ "framework_lib/nvidia" ] [dependencies.framework_lib] path = "../framework_lib"