Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions framework_lib/src/chromium_ec/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub enum EcCommands {
ChassisOpenCheck = 0x3E0F,
/// Get information about historical chassis open/close (intrusion) information
ChassisIntrusion = 0x3E09,
/// Control and check retimer modes (firmware update and compliance)
RetimerControl = 0x3E0A,

/// Not used by this library
AcpiNotify = 0xE10,
Expand Down
33 changes: 33 additions & 0 deletions framework_lib/src/chromium_ec/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,39 @@ impl EcRequest<EcResponseChassisIntrusionControl> for EcRequestChassisIntrusionC
}
}

#[repr(u8)]
pub enum RetimerControlMode {
/// AMD and Intel
EntryFwUpdateMode = 0x01,
/// AMD and Intel
ExitFwUpdateMode = 0x02,
/// Intel only
EnableComplianceMode = 0x04,
/// Intel only
DisableComplianceMode = 0x08,
/// Check if in FwUpdateMode
CheckStatus = 0x80,
}

#[repr(C, packed)]
pub struct EcRequestRetimerControl {
/// 0 (right) or 1 (left)
pub controller: u8,
/// See RetimerControlMode
pub mode: u8,
}

#[repr(C, packed)]
pub struct EcResponseRetimerControlStatus {
pub status: u8,
}

impl EcRequest<EcResponseRetimerControlStatus> for EcRequestRetimerControl {
fn command_id() -> EcCommands {
EcCommands::RetimerControl
}
}

#[repr(C, packed)]
pub struct EcRequestReadPdVersionV0 {}

Expand Down
61 changes: 61 additions & 0 deletions framework_lib/src/chromium_ec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,67 @@ impl CrosEc {
})
}

/// Check if the retimer is in firmware update mode
///
/// This is normally false. Update mode is used to enable the retimer power even when no device
/// is attached.
pub fn retimer_in_fwupd_mode(&self, retimer: u8) -> EcResult<bool> {
let status = EcRequestRetimerControl {
controller: retimer,
mode: RetimerControlMode::CheckStatus as u8,
}
.send_command(self)?;

Ok(status.status == 0x01)
}

/// Enable or disable retimer update mode
///
/// Check for success and returns Err if not successful
pub fn retimer_enable_fwupd(&self, retimer: u8, enable: bool) -> EcResult<()> {
if self.retimer_in_fwupd_mode(retimer)? == enable {
info!("Retimer update mode already: {:?}", enable);
return Ok(());
}
EcRequestRetimerControl {
controller: retimer,
mode: if enable {
RetimerControlMode::EntryFwUpdateMode as u8
} else {
RetimerControlMode::ExitFwUpdateMode as u8
},
}
.send_command(self)?;

// Wait half a second to let it enter the new mode
os_specific::sleep(500_000);

if self.retimer_in_fwupd_mode(retimer).unwrap() != enable {
error!("Failed to set retimer update mode to: {}", enable);
return Err(EcError::DeviceError(format!(
"Failed to set retimer update mode to: {}",
enable
)));
}
Ok(())
}

/// Enable or disable retimer Thunderbolt compliance mode (Intel only)
///
/// Cannot check for success
pub fn retimer_enable_compliance(&self, retimer: u8, enable: bool) -> EcResult<()> {
EcRequestRetimerControl {
controller: retimer,
mode: if enable {
RetimerControlMode::EnableComplianceMode as u8
} else {
RetimerControlMode::DisableComplianceMode as u8
},
}
.send_command(self)?;
Ok(())
}

pub fn get_input_deck_status(&self) -> EcResult<InputDeckStatus> {
let status = EcRequestDeckState {
mode: DeckStateMode::ReadOnly,
Expand Down
5 changes: 5 additions & 0 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ struct ClapCli {
#[arg(long, short)]
test: bool,

/// Run self-test to check if interaction with retimers is possible
#[arg(long)]
test_retimer: bool,

/// Force execution of an unsafe command - may render your hardware unbootable!
#[arg(long, short)]
force: bool,
Expand Down Expand Up @@ -455,6 +459,7 @@ pub fn parse(args: &[String]) -> Cli {
pd_addrs,
pd_ports,
test: args.test,
test_retimer: args.test_retimer,
dry_run: args.dry_run,
force: args.force,
// TODO: Set help. Not very important because Clap handles this by itself
Expand Down
68 changes: 68 additions & 0 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ pub struct Cli {
pub flash_rw_ec: Option<String>,
pub driver: Option<CrosEcDriverType>,
pub test: bool,
pub test_retimer: bool,
pub dry_run: bool,
pub force: bool,
pub intrusion: bool,
Expand Down Expand Up @@ -271,6 +272,7 @@ pub fn parse(args: &[String]) -> Cli {
// flash_rw_ec
driver: cli.driver,
test: cli.test,
test_retimer: cli.test_retimer,
dry_run: cli.dry_run,
// force
intrusion: cli.intrusion,
Expand Down Expand Up @@ -1174,6 +1176,11 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
println!("FAILED!!");
return 1;
}
} else if args.test_retimer {
println!("Retimer Self-Test");
if let Err(err) = selftest_retimer(&ec) {
println!(" Failed: {:?}", err);
}
} else if args.power {
return power::get_and_print_power_info(&ec);
} else if args.thermal {
Expand Down Expand Up @@ -1606,6 +1613,67 @@ fn selftest(ec: &CrosEc) -> Option<()> {
Some(())
}

// Platforms that have Retimers
// Retimer I2C is always connected to the CPU, except for the Framework 16 dGPU retimer.
//
// - Framework 12
// - No Retimer, only retimer for both left ports (no firmware)
// - Framework 13 Intel
// - One Intel retimer for each port (with firmware)
// - Framework 13 AMD 7040
// - Kandou Retimer on top two ports (no firmware)
// - Analogix Retimer on bottom two ports (no firmware)
// - Framework 13 AMD AI 300
// - Parade Retimer on top two ports (with firmware)
// - Analogix Retimer on bottom two ports (no firmware)
// - Framework 16 AMD 7040
// - Kandou Retimer on top two ports (no firmware)
// - Analogix Retimer on lower and middle left ports (no firmware)
// - Framework 16 AMD AI 300
// - Parade Retimer on top two ports (with firmware)
// - Framework 16 AMD dGPU
// - None
// - Framework 16 NVIDIA dGPU
// - Parade Retimer
// - Framework Desktop
// - Parade Retimer on both back ports (with firmware)
fn selftest_retimer(ec: &CrosEc) -> EcResult<()> {
// TODO: Make sure that it can work for the NVIDIA dGPU retimer and increase to 3
for i in 0..2 {
let update_mode = ec.retimer_in_fwupd_mode(i);
if update_mode == Err(EcError::Response(EcResponseStatus::InvalidParameter)) {
println!(" Retimer status not supported on this platform. Cannot test");
return Ok(());
}
println!(" Retimers on PD Controller {}", i);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);
if update_mode? {
println!(" Disabling it to start test.");
ec.retimer_enable_fwupd(i, false)?;
}

println!(" Enabling update mode");
let success = ec.retimer_enable_fwupd(i, true);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);

println!(" Disabling update mode");
ec.retimer_enable_fwupd(i, false)?;
os_specific::sleep(100);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);
success?;
}
Ok(())
}

fn smbios_info() {
println!("Summary");
println!(" Is Framework: {}", is_framework());
Expand Down
4 changes: 4 additions & 0 deletions framework_lib/src/commandline/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pub fn parse(args: &[String]) -> Cli {
pd_addrs: None,
pd_ports: None,
test: false,
test_retimer: false,
dry_run: false,
force: false,
help: false,
Expand Down Expand Up @@ -495,6 +496,9 @@ pub fn parse(args: &[String]) -> Cli {
} else if arg == "-t" || arg == "--test" {
cli.test = true;
found_an_option = true;
} else if arg == "-t" || arg == "--test-retimer" {
cli.test_retimer = true;
found_an_option = true;
} else if arg == "-f" || arg == "--force" {
cli.force = true;
found_an_option = true;
Expand Down
Loading