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
8 changes: 4 additions & 4 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ Board IDs
F75303_CPU: 44 C
F75303_DDR: 39 C
APU: 62 C
Fan Speed: 0 RPM
API Fan: 0 RPM
```

## Check sensors
Expand Down Expand Up @@ -517,7 +517,7 @@ Accelerometers:
F75303_CPU: 41 C
F75303_DDR: 37 C
APU: 42 C
Fan Speed: 7281 RPM
APU Fan: 7281 RPM

# Set a target RPM (all or just fan ID=0)
> sudo framework_tool --fansetrpm 3141
Expand All @@ -527,7 +527,7 @@ Accelerometers:
F75303_CPU: 42 C
F75303_DDR: 37 C
APU: 44 C
Fan Speed: 3171 RPM
APU Fan: 3171 RPM

# And back to normal
> sudo framework_tool --autofanctrl
Expand All @@ -536,7 +536,7 @@ Accelerometers:
F75303_CPU: 40 C
F75303_DDR: 38 C
APU: 42 C
Fan Speed: 0 RPM
APU Fan: 0 RPM

# Or just for a specific fan (e.g. on Framework Desktop)
> sudo framework_tool --autofanctrl 0
Expand Down
43 changes: 29 additions & 14 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,7 @@ fn selftest(ec: &CrosEc) -> Option<()> {
println!("Specify custom platform parameters with --pd-ports --pd-addrs");
return None;
};
let family = smbios::get_platform().and_then(Platform::which_family);

println!(" Dump EC memory region");
if let Some(mem) = ec.dump_mem_region() {
Expand Down Expand Up @@ -2091,10 +2092,12 @@ fn selftest(ec: &CrosEc) -> Option<()> {
println!(" - OK");

println!(" Getting AC info from EC");
// All our laptops have at least 4 PD ports so far
if power::get_pd_info(ec, 4).iter().any(|x| x.is_err()) {
println!(" Failed to get PD Info from EC");
return None;
if family != Some(PlatformFamily::FrameworkDesktop) {
// All our laptops have at least 4 PD ports so far
if power::get_pd_info(ec, 4).iter().any(|x| x.is_err()) {
println!(" Failed to get PD Info from EC");
return None;
}
}

print!("Reading PD Version from EC");
Expand All @@ -2112,16 +2115,28 @@ fn selftest(ec: &CrosEc) -> Option<()> {

let pd_01 = PdController::new(PdPort::Right01, ec.clone());
let pd_23 = PdController::new(PdPort::Left23, ec.clone());
print!(" Getting PD01 info through I2C tunnel");
print_err(pd_01.get_silicon_id())?;
print_err(pd_01.get_device_info())?;
print_err(pd_01.get_fw_versions())?;
println!(" - OK");
print!(" Getting PD23 info through I2C tunnel");
print_err(pd_23.get_silicon_id())?;
print_err(pd_23.get_device_info())?;
print_err(pd_23.get_fw_versions())?;
println!(" - OK");
let pd_back = PdController::new(PdPort::Back, ec.clone());
if family != Some(PlatformFamily::FrameworkDesktop) {
print!(" Getting PD01 info through I2C tunnel");
print_err(pd_01.get_silicon_id())?;
print_err(pd_01.get_device_info())?;
print_err(pd_01.get_fw_versions())?;
println!(" - OK");
print!(" Getting PD23 info through I2C tunnel");
print_err(pd_23.get_silicon_id())?;
print_err(pd_23.get_device_info())?;
print_err(pd_23.get_fw_versions())?;
println!(" - OK");
} else if matches!(
family,
Some(PlatformFamily::FrameworkDesktop) | Some(PlatformFamily::Framework16)
) {
print!(" Getting Back PD info through I2C tunnel");
print_err(pd_back.get_silicon_id())?;
print_err(pd_back.get_device_info())?;
print_err(pd_back.get_fw_versions())?;
println!(" - OK");
}

Some(())
}
Expand Down
217 changes: 118 additions & 99 deletions framework_lib/src/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,8 @@ pub fn print_thermal(ec: &CrosEc) {
println!(" F75303_DDR: {:>4}", TempSensor::from(temps[1]));
println!(" F75303_AMB: {:>4}", TempSensor::from(temps[2]));
println!(" APU: {:>4}", TempSensor::from(temps[3]));
4
println!(" Virtual: {:>4}", TempSensor::from(temps[4]));
3
}

_ => {
Expand All @@ -444,13 +445,25 @@ pub fn print_thermal(ec: &CrosEc) {
}

for i in 0..EC_FAN_SPEED_ENTRIES {
let name = match (i, family) {
(0, Some(PlatformFamily::Framework12)) => "APU Fan".to_string(),
(0, Some(PlatformFamily::Framework13)) => "APU Fan".to_string(),
(0, Some(PlatformFamily::Framework16)) => "Left Fan".to_string(),
(1, Some(PlatformFamily::Framework16)) => "Right Fan".to_string(),
(0, Some(PlatformFamily::FrameworkDesktop)) => "APU Fan".to_string(),
(1, Some(PlatformFamily::FrameworkDesktop)) => "Front Fan".to_string(),
(2, Some(PlatformFamily::FrameworkDesktop)) => "Third Fan".to_string(),
_ => format!("Fan {i}"),
};
let name = format!("{name}:");

let fan = u16::from_le_bytes([fans[i * 2], fans[1 + i * 2]]);
if fan == EC_FAN_SPEED_STALLED_DEPRECATED {
println!(" Fan Speed: {:>4} RPM (Stalled)", fan);
println!(" {name:<11} {:>4} RPM (Stalled)", fan);
} else if fan == EC_FAN_SPEED_NOT_PRESENT {
info!(" Fan Speed: Not present");
info!(" {name:<11} Not present");
} else {
println!(" Fan Speed: {:>4} RPM", fan);
println!(" {name:<11} {:>4} RPM", fan);
}
}
}
Expand Down Expand Up @@ -782,110 +795,116 @@ impl From<u8> for CypdPdDataRole {
}

pub fn get_and_print_cypd_pd_info(ec: &CrosEc) {
// All of our systems have a maximum of 4 PD enabled ports
let ports = 4u8;

for port in 0..ports {
println!("USB-C Port {}:", port);

let result = EcRequestGetPdPortState { port }.send_command(ec);
match result {
Ok(info) => {
let c_state = CypdTypeCState::from(info.c_state);
let connected = !matches!(c_state, CypdTypeCState::Nothing);
let power_role = CypdPdPowerRole::from(info.power_role);
let data_role = CypdPdDataRole::from(info.data_role);
let voltage = { info.voltage };
let current = { info.current };
let watts_mw = voltage as u32 * current as u32 / 1000;
let has_pd_contract = info.pd_state != 0;

println!(
" PD Contract: {}",
if info.pd_state != 0 { "Yes" } else { "No" }
);
println!(" Power Role: {:?}", power_role);
println!(" Data Role: {:?}", data_role);
if connected {
println!(
" VCONN: {}",
if info.vconn != 0 { "On" } else { "Off" }
);
println!(
" Negotiated: {}.{:03} V, {} mA, {}.{} W",
voltage / 1000,
voltage % 1000,
current,
watts_mw / 1000,
watts_mw % 1000,
);
println!(
" CC Polarity: {}",
match info.cc_polarity {
0 => "CC1",
1 => "CC2",
2 => "CC1 (Debug)",
3 => "CC2 (Debug)",
_ => "Unknown",
}
);
}
if has_pd_contract {
println!(" Port Partner: {:?}", c_state);
println!(
" EPR: {}{}",
if info.epr_active != 0 {
"Active"
} else {
"Inactive"
},
if info.epr_support != 0 {
" (Supported)"
} else {
""
}
);
if power_role == CypdPdPowerRole::Sink {
println!(
" Sink Active: {}",
if info.active_port != 0 { "Yes" } else { "No" }
);
}
}
let alt = info.pd_alt_mode_status;
// Bits 0-1 indicate DP alt mode is active (bit 0 = DFP_D/TBT,
// bit 1 = UFP_D). Only show when actually in DP alt mode.
if connected && (alt & 0x03) != 0 {
let mut modes = vec![];
if alt & 0x01 != 0 {
modes.push("DFP_D Connected");
}
if alt & 0x02 != 0 {
modes.push("UFP_D Connected");
}
if alt & 0x04 != 0 {
modes.push("Power Low");
}
if alt & 0x08 != 0 {
modes.push("Enabled");
}
if alt & 0x10 != 0 {
modes.push("Multi-Function");
}
if alt & 0x20 != 0 {
modes.push("USB Config");
}
if alt & 0x40 != 0 {
modes.push("Exit Request");
}
if alt & 0x80 != 0 {
modes.push("HPD High");
}
println!(" DP Alt Mode: {} (0x{:02X})", modes.join(", "), alt);
}
let info = match result {
Ok(info) => info,
Err(EcError::Response(EcResponseStatus::InvalidParameter)) => {
debug!("Port {port} does not exist");
continue;
}
Err(e) => {
print_err::<()>(Err(e));
continue;
}
};

println!("USB-C Port {}:", port);
let c_state = CypdTypeCState::from(info.c_state);
let connected = !matches!(c_state, CypdTypeCState::Nothing);
let power_role = CypdPdPowerRole::from(info.power_role);
let data_role = CypdPdDataRole::from(info.data_role);
let voltage = { info.voltage };
let current = { info.current };
let watts_mw = voltage as u32 * current as u32 / 1000;
let has_pd_contract = info.pd_state != 0;

println!(
" PD Contract: {}",
if info.pd_state != 0 { "Yes" } else { "No" }
);
println!(" Power Role: {:?}", power_role);
println!(" Data Role: {:?}", data_role);
if connected {
println!(
" VCONN: {}",
if info.vconn != 0 { "On" } else { "Off" }
);
println!(
" Negotiated: {}.{:03} V, {} mA, {}.{} W",
voltage / 1000,
voltage % 1000,
current,
watts_mw / 1000,
watts_mw % 1000,
);
println!(
" CC Polarity: {}",
match info.cc_polarity {
0 => "CC1",
1 => "CC2",
2 => "CC1 (Debug)",
3 => "CC2 (Debug)",
_ => "Unknown",
}
);
}
if has_pd_contract {
println!(" Port Partner: {:?}", c_state);
println!(
" EPR: {}{}",
if info.epr_active != 0 {
"Active"
} else {
"Inactive"
},
if info.epr_support != 0 {
" (Supported)"
} else {
""
}
);
if power_role == CypdPdPowerRole::Sink {
println!(
" Sink Active: {}",
if info.active_port != 0 { "Yes" } else { "No" }
);
}
}
let alt = info.pd_alt_mode_status;
// Bits 0-1 indicate DP alt mode is active (bit 0 = DFP_D/TBT,
// bit 1 = UFP_D). Only show when actually in DP alt mode.
if connected && (alt & 0x03) != 0 {
let mut modes = vec![];
if alt & 0x01 != 0 {
modes.push("DFP_D Connected");
}
if alt & 0x02 != 0 {
modes.push("UFP_D Connected");
}
if alt & 0x04 != 0 {
modes.push("Power Low");
}
if alt & 0x08 != 0 {
modes.push("Enabled");
}
if alt & 0x10 != 0 {
modes.push("Multi-Function");
}
if alt & 0x20 != 0 {
modes.push("USB Config");
}
if alt & 0x40 != 0 {
modes.push("Exit Request");
}
if alt & 0x80 != 0 {
modes.push("HPD High");
}
println!(" DP Alt Mode: {} (0x{:02X})", modes.join(", "), alt);
}
}
}
Expand Down
Loading