Skip to content

Commit 262b6dc

Browse files
Merge pull request #165 from FrameworkComputer/pd-control
Add debug commands to disable/enable and reset PD controller
2 parents 4416d38 + 3f02cdc commit 262b6dc

File tree

9 files changed

+286
-45
lines changed

9 files changed

+286
-45
lines changed

EXAMPLES.md

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -544,43 +544,4 @@ It's not controlled by the EC, use https://keyboard.frame.work.
544544

545545
Mostly for debugging firmware.
546546

547-
### Check EFI Resource Table
548-
549-
On Framework Desktop:
550-
551-
```
552-
> sudo framework_tool --esrt
553-
ESRT Table
554-
ResourceCount: 1
555-
ResourceCountMax: 1
556-
ResourceVersion: 1
557-
ESRT Entry 0
558-
GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856
559-
GUID: DesktopAmdAi300Bios
560-
Type: SystemFirmware
561-
Version: 0x204 (516)
562-
Min FW Version: 0x100 (256)
563-
Capsule Flags: 0x0
564-
Last Attempt Version: 0x108 (264)
565-
Last Attempt Status: Success
566-
```
567-
568-
## Flashing EC firmware
569-
570-
**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render
571-
your hardware unbootable. Please update your firmware using the official BIOS
572-
update methods (Windows .exe, LVFS/FWUPD, EFI updater)!
573-
574-
This command has not been thoroughly tested on all Framework Computer systems
575-
576-
```
577-
# Simulate flashing RW (to see which blocks are updated)
578-
> framework_tool --flash-rw-ec ec.bin --dry-run
579-
580-
# Actually flash RW
581-
> framework_tool --flash-rw-ec ec.bin
582-
583-
# Boot into EC RW firmware (will crash your OS and reboot immediately)
584-
# EC will boot back into RO if the system turned off for 30s
585-
> framework_tool --reboot-ec jump-rw
586-
```
547+
See [EXAMPLES_ADVANCED.md](EXAMPLES_ADVANCED.md)

EXAMPLES_ADVANCED.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Advanced debugging
2+
3+
## PD
4+
5+
### Check PD state
6+
7+
Example on Framework 13 AMD Ryzen AI 300
8+
9+
```
10+
> sudo framework_tool.exe --pd-info
11+
Left / Ports 01
12+
Silicon ID: 0x3580
13+
Mode: MainFw
14+
Flash Row Size: 256 B
15+
Ports Enabled: 0, 1
16+
Bootloader Version: Base: 3.6.0.009, App: 0.0.01
17+
FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B
18+
FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B
19+
Right / Ports 23
20+
Silicon ID: 0x3580
21+
Mode: MainFw
22+
Flash Row Size: 256 B
23+
Ports Enabled: 0, 1
24+
Bootloader Version: Base: 3.6.0.009, App: 0.0.01
25+
FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B
26+
FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B
27+
```
28+
29+
### Disable/enable/reset PD
30+
31+
```
32+
# Disable all ports on PD 0
33+
> sudo framework_tool --pd-disable 0
34+
35+
# Reset PD 0 (enables all ports again)
36+
> sudo framework_tool --pd-reset 0
37+
38+
# Or enable all ports on PD 0 without resetting it
39+
> sudo framework_tool --pd-enable 0
40+
```
41+
42+
### Check EFI Resource Table
43+
44+
On Framework Desktop:
45+
46+
```
47+
> sudo framework_tool --esrt
48+
ESRT Table
49+
ResourceCount: 1
50+
ResourceCountMax: 1
51+
ResourceVersion: 1
52+
ESRT Entry 0
53+
GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856
54+
GUID: DesktopAmdAi300Bios
55+
Type: SystemFirmware
56+
Version: 0x204 (516)
57+
Min FW Version: 0x100 (256)
58+
Capsule Flags: 0x0
59+
Last Attempt Version: 0x108 (264)
60+
Last Attempt Status: Success
61+
```
62+
63+
## Flashing EC firmware
64+
65+
**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render
66+
your hardware unbootable. Please update your firmware using the official BIOS
67+
update methods (Windows .exe, LVFS/FWUPD, EFI updater)!
68+
69+
This command has not been thoroughly tested on all Framework Computer systems
70+
71+
```
72+
# Simulate flashing RW (to see which blocks are updated)
73+
> framework_tool --flash-rw-ec ec.bin --dry-run
74+
75+
# Actually flash RW
76+
> framework_tool --flash-rw-ec ec.bin
77+
78+
# Boot into EC RW firmware (will crash your OS and reboot immediately)
79+
# EC will boot back into RO if the system turned off for 30s
80+
> framework_tool --reboot-ec jump-rw
81+
```

framework_lib/src/ccgx/binary.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn read_metadata(
106106
let buffer = read_256_bytes(file_buffer, metadata_offset, flash_row_size)?;
107107
match ccgx {
108108
SiliconId::Ccg3 => parse_metadata_ccg3(&buffer),
109-
SiliconId::Ccg5 | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer),
109+
SiliconId::Ccg5 | SiliconId::Ccg6Adl | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer),
110110
SiliconId::Ccg8 => parse_metadata_cyacd2(&buffer)
111111
.map(|(fw_row_start, fw_size)| (fw_row_start / (flash_row_size as u32), fw_size)),
112112
}
@@ -172,6 +172,7 @@ pub fn read_versions(file_buffer: &[u8], ccgx: SiliconId) -> Option<PdFirmwareFi
172172
let (flash_row_size, f1_metadata_row, fw2_metadata_row) = match ccgx {
173173
SiliconId::Ccg3 => (SMALL_ROW, 0x03FF, 0x03FE),
174174
SiliconId::Ccg5 => (LARGE_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG5),
175+
SiliconId::Ccg6Adl => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6),
175176
SiliconId::Ccg6 => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6),
176177
SiliconId::Ccg8 => (LARGE_ROW, FW1_METADATA_ROW_CCG8, FW2_METADATA_ROW_CCG8),
177178
};

framework_lib/src/ccgx/device.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,34 @@ use crate::util::{assert_win_len, Config, Platform};
1414

1515
use super::*;
1616

17+
const _HPI_FLASH_ENTER_SIGNATURE: char = 'P';
18+
const _HPI_JUMP_TO_ALT_SIGNATURE: char = 'A';
19+
const _HPI_JUMP_TO_BOOT_SIGNATURE: char = 'J';
20+
const HPI_RESET_SIGNATURE: char = 'R';
21+
const _HPI_FLASH_RW_SIGNATURE: char = 'F';
22+
const HPI_RESET_DEV_CMD: u8 = 1;
23+
const _HPI_FLASH_READ_CMD: u8 = 0;
24+
const _HPI_FLASH_WRITE_CMD: u8 = 1;
25+
26+
#[derive(Debug, Copy, Clone)]
1727
enum ControlRegisters {
1828
DeviceMode = 0,
1929
SiliconId = 2, // Two bytes long, First LSB, then MSB
30+
_InterruptStatus = 0x06,
31+
_JumpToBoot = 0x07,
32+
ResetRequest = 0x08,
33+
_FlashmodeEnter = 0x0A,
34+
_ValidateFw = 0x0B,
35+
_FlashSignature = 0x0C,
2036
BootLoaderVersion = 0x10,
2137
Firmware1Version = 0x18,
2238
Firmware2Version = 0x20,
39+
PdPortsEnable = 0x2C,
40+
_ResponseType = 0x7E,
41+
_FlashRwMem = 0x0200,
2342
}
2443

25-
#[derive(Debug)]
44+
#[derive(Debug, PartialEq, Clone, Copy)]
2645
pub enum PdPort {
2746
Left01,
2847
Right23,
@@ -130,7 +149,7 @@ pub struct PdController {
130149
ec: CrosEc,
131150
}
132151

133-
#[derive(Debug, PartialEq)]
152+
#[derive(Debug, PartialEq, Clone, Copy)]
134153
pub enum FwMode {
135154
BootLoader = 0,
136155
/// Backup CCGX firmware (No 1)
@@ -182,6 +201,21 @@ impl PdController {
182201
)
183202
}
184203

204+
pub fn i2c_write(&self, addr: u16, data: &[u8]) -> EcResult<EcI2cPassthruResponse> {
205+
trace!(
206+
"I2C passthrough from I2C Port {} to I2C Addr {}",
207+
self.port.i2c_port()?,
208+
self.port.i2c_address()?
209+
);
210+
i2c_write(
211+
&self.ec,
212+
self.port.i2c_port()?,
213+
self.port.i2c_address()?,
214+
addr,
215+
data,
216+
)
217+
}
218+
185219
fn ccgx_read(&self, reg: ControlRegisters, len: u16) -> EcResult<Vec<u8>> {
186220
let mut data: Vec<u8> = Vec::with_capacity(len.into());
187221

@@ -204,6 +238,35 @@ impl PdController {
204238
Ok(data)
205239
}
206240

241+
fn ccgx_write(&self, reg: ControlRegisters, data: &[u8]) -> EcResult<()> {
242+
let addr = reg as u16;
243+
trace!(
244+
"ccgx_write(reg: {:?}, addr: {}, data.len(): {}",
245+
reg,
246+
addr,
247+
data.len()
248+
);
249+
let mut data_written = 0;
250+
251+
while data_written < data.len() {
252+
let chunk_len = std::cmp::min(MAX_I2C_CHUNK, data.len());
253+
let buffer = &data[data_written..data_written + chunk_len];
254+
let offset = addr + data_written as u16;
255+
256+
let i2c_response = self.i2c_write(offset, buffer)?;
257+
if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() {
258+
return Err(EcError::DeviceError(format!(
259+
"I2C write was not successful: {:?}",
260+
err
261+
)));
262+
}
263+
264+
data_written += chunk_len;
265+
}
266+
267+
Ok(())
268+
}
269+
207270
pub fn get_silicon_id(&self) -> EcResult<u16> {
208271
let data = self.ccgx_read(ControlRegisters::SiliconId, 2)?;
209272
assert_win_len(data.len(), 2);
@@ -295,4 +358,24 @@ impl PdController {
295358
base_ver, app_ver
296359
);
297360
}
361+
362+
pub fn reset_device(&self) -> EcResult<()> {
363+
self.ccgx_write(
364+
ControlRegisters::ResetRequest,
365+
&[HPI_RESET_SIGNATURE as u8, HPI_RESET_DEV_CMD],
366+
)?;
367+
Ok(())
368+
}
369+
370+
pub fn enable_ports(&self, enable: bool) -> EcResult<()> {
371+
let mask = if enable { 0b11 } else { 0b00 };
372+
self.ccgx_write(ControlRegisters::PdPortsEnable, &[mask])?;
373+
Ok(())
374+
}
375+
376+
pub fn get_port_status(&self) -> EcResult<u8> {
377+
let data = self.ccgx_read(ControlRegisters::PdPortsEnable, 1)?;
378+
assert_win_len(data.len(), 1);
379+
Ok(data[0])
380+
}
298381
}

framework_lib/src/ccgx/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ struct CyAcd2Metadata {
105105
pub enum SiliconId {
106106
Ccg3 = 0x1D00,
107107
Ccg5 = 0x2100,
108-
Ccg6 = 0x3000,
108+
Ccg6Adl = 0x3000,
109+
Ccg6 = 0x30A0,
109110
Ccg8 = 0x3580,
110111
}
111112

framework_lib/src/chromium_ec/i2c_passthrough.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ pub fn i2c_write(
155155

156156
let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?;
157157
let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) };
158-
assert_eq!(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header
158+
util::assert_win_len(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header
159159
debug_assert_eq!(res.messages as usize, messages.len());
160160
Ok(EcI2cPassthruResponse {
161161
i2c_status: res.i2c_status,

framework_lib/src/commandline/clap_std.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ struct ClapCli {
8383
#[arg(long)]
8484
pd_info: bool,
8585

86+
/// Reset a specific PD controller (for debugging only)
87+
#[arg(long)]
88+
pd_reset: Option<u8>,
89+
90+
/// Disable all ports on a specific PD controller (for debugging only)
91+
#[arg(long)]
92+
pd_disable: Option<u8>,
93+
94+
/// Enable all ports on a specific PD controller (for debugging only)
95+
#[arg(long)]
96+
pd_enable: Option<u8>,
97+
8698
/// Show details about connected DP or HDMI Expansion Cards
8799
#[arg(long)]
88100
dp_hdmi_info: bool,
@@ -376,6 +388,9 @@ pub fn parse(args: &[String]) -> Cli {
376388
autofanctrl: args.autofanctrl,
377389
pdports: args.pdports,
378390
pd_info: args.pd_info,
391+
pd_reset: args.pd_reset,
392+
pd_disable: args.pd_disable,
393+
pd_enable: args.pd_enable,
379394
dp_hdmi_info: args.dp_hdmi_info,
380395
dp_hdmi_update: args
381396
.dp_hdmi_update

framework_lib/src/commandline/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ pub struct Cli {
156156
pub pdports: bool,
157157
pub privacy: bool,
158158
pub pd_info: bool,
159+
pub pd_reset: Option<u8>,
160+
pub pd_disable: Option<u8>,
161+
pub pd_enable: Option<u8>,
159162
pub dp_hdmi_info: bool,
160163
pub dp_hdmi_update: Option<String>,
161164
pub audio_card_info: bool,
@@ -217,13 +220,25 @@ fn print_single_pd_details(pd: &PdController) {
217220
println!(" Silicon ID: 0x{:X}", si);
218221
} else {
219222
println!(" Failed to read Silicon ID/Family");
223+
return;
220224
}
221225
if let Ok((mode, frs)) = pd.get_device_info() {
222226
println!(" Mode: {:?}", mode);
223227
println!(" Flash Row Size: {} B", frs);
224228
} else {
225229
println!(" Failed to device info");
226230
}
231+
if let Ok(port_mask) = pd.get_port_status() {
232+
let ports = match port_mask {
233+
1 => "0",
234+
2 => "1",
235+
3 => "0, 1",
236+
_ => "None",
237+
};
238+
println!(" Ports Enabled: {}", ports);
239+
} else {
240+
println!(" Ports Enabled: Unknown");
241+
}
227242
pd.print_fw_info();
228243
}
229244

@@ -999,6 +1014,39 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
9991014
smbios_info();
10001015
} else if args.pd_info {
10011016
print_pd_details(&ec);
1017+
} else if let Some(pd) = args.pd_reset {
1018+
println!("Resetting PD {}...", pd);
1019+
print_err(match pd {
1020+
0 => PdController::new(PdPort::Left01, ec.clone()).reset_device(),
1021+
1 => PdController::new(PdPort::Right23, ec.clone()).reset_device(),
1022+
2 => PdController::new(PdPort::Back, ec.clone()).reset_device(),
1023+
_ => {
1024+
error!("PD {} does not exist", pd);
1025+
Ok(())
1026+
}
1027+
});
1028+
} else if let Some(pd) = args.pd_disable {
1029+
println!("Disabling PD {}...", pd);
1030+
print_err(match pd {
1031+
0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(false),
1032+
1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(false),
1033+
2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(false),
1034+
_ => {
1035+
error!("PD {} does not exist", pd);
1036+
Ok(())
1037+
}
1038+
});
1039+
} else if let Some(pd) = args.pd_enable {
1040+
println!("Enabling PD {}...", pd);
1041+
print_err(match pd {
1042+
0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(true),
1043+
1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(true),
1044+
2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(true),
1045+
_ => {
1046+
error!("PD {} does not exist", pd);
1047+
Ok(())
1048+
}
1049+
});
10021050
} else if args.dp_hdmi_info {
10031051
#[cfg(feature = "hidapi")]
10041052
print_dp_hdmi_details(true);

0 commit comments

Comments
 (0)