Skip to content

Commit 3e819a2

Browse files
Merge pull request #112 from FrameworkComputer/i2c-refactor
Split I2C tunneling out of ccgx module
2 parents df851bb + 426090e commit 3e819a2

File tree

3 files changed

+175
-111
lines changed

3 files changed

+175
-111
lines changed

framework_lib/src/ccgx/device.rs

Lines changed: 10 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,17 @@
33
//! The current implementation talks to them by tunneling I2C through EC host commands.
44
55
use alloc::format;
6-
use alloc::string::ToString;
7-
use alloc::vec;
86
use alloc::vec::Vec;
97
#[cfg(feature = "uefi")]
108
use core::prelude::rust_2021::derive;
119

1210
use crate::ccgx::{AppVersion, BaseVersion, ControllerVersion};
13-
use crate::chromium_ec::command::EcCommands;
14-
use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResult};
15-
use crate::util::{self, assert_win_len, Config, Platform};
16-
use std::mem::size_of;
11+
use crate::chromium_ec::i2c_passthrough::*;
12+
use crate::chromium_ec::{CrosEc, EcError, EcResult};
13+
use crate::util::{assert_win_len, Config, Platform};
1714

1815
use super::*;
1916

20-
/// Maximum transfer size for one I2C transaction supported by the chip
21-
const MAX_I2C_CHUNK: usize = 128;
22-
2317
enum ControlRegisters {
2418
DeviceMode = 0,
2519
SiliconId = 2, // Two bytes long, First LSB, then MSB
@@ -106,58 +100,6 @@ pub struct PdController {
106100
ec: CrosEc,
107101
}
108102

109-
fn passthrough_offset(dev_index: u16) -> u16 {
110-
dev_index * 0x4000
111-
}
112-
113-
#[repr(C, packed)]
114-
struct EcParamsI2cPassthruMsg {
115-
/// Slave address and flags
116-
addr_and_flags: u16,
117-
transfer_len: u16,
118-
}
119-
120-
#[repr(C, packed)]
121-
struct EcParamsI2cPassthru {
122-
port: u8,
123-
/// How many messages
124-
messages: u8,
125-
msg: [EcParamsI2cPassthruMsg; 0],
126-
}
127-
128-
#[repr(C, packed)]
129-
struct _EcI2cPassthruResponse {
130-
i2c_status: u8,
131-
/// How many messages
132-
messages: u8,
133-
data: [u8; 0],
134-
}
135-
136-
struct EcI2cPassthruResponse {
137-
i2c_status: u8, // TODO: Can probably use enum
138-
data: Vec<u8>,
139-
}
140-
141-
impl EcI2cPassthruResponse {
142-
fn is_successful(&self) -> EcResult<()> {
143-
if self.i2c_status & 1 > 0 {
144-
return Err(EcError::DeviceError(
145-
"I2C Transfer not acknowledged".to_string(),
146-
));
147-
}
148-
if self.i2c_status & (1 << 1) > 0 {
149-
return Err(EcError::DeviceError("I2C Transfer timeout".to_string()));
150-
}
151-
// I'm not aware of any other errors, but there might be.
152-
// But I don't think multiple errors can be indicated at the same time
153-
assert_eq!(self.i2c_status, 0);
154-
Ok(())
155-
}
156-
}
157-
158-
/// Indicate that it's a read, not a write
159-
const I2C_READ_FLAG: u16 = 1 << 15;
160-
161103
#[derive(Debug, PartialEq)]
162104
pub enum FwMode {
163105
BootLoader = 0,
@@ -194,63 +136,20 @@ impl PdController {
194136
pub fn new(port: PdPort, ec: CrosEc) -> Self {
195137
PdController { port, ec }
196138
}
197-
/// Wrapped with support for dev id
198-
/// TODO: Should move into chromium_ec module
199-
/// TODO: Must not call CrosEc::new() otherwise the driver isn't configurable!
200-
fn send_ec_command(&self, code: u16, dev_index: u16, data: &[u8]) -> EcResult<Vec<u8>> {
201-
let command_id = code + passthrough_offset(dev_index);
202-
self.ec.send_command(command_id, 0, data)
203-
}
204139

205140
fn i2c_read(&self, addr: u16, len: u16) -> EcResult<EcI2cPassthruResponse> {
206141
trace!(
207142
"I2C passthrough from I2C Port {} to I2C Addr {}",
208143
self.port.i2c_port().unwrap(),
209144
self.port.i2c_address()
210145
);
211-
trace!("i2c_read(addr: {}, len: {})", addr, len);
212-
if usize::from(len) > MAX_I2C_CHUNK {
213-
return EcResult::Err(EcError::DeviceError(format!(
214-
"i2c_read too long. Must be <128, is: {}",
215-
len
216-
)));
217-
}
218-
let addr_bytes = u16::to_le_bytes(addr);
219-
let messages = vec![
220-
EcParamsI2cPassthruMsg {
221-
addr_and_flags: self.port.i2c_address(),
222-
transfer_len: addr_bytes.len() as u16,
223-
},
224-
EcParamsI2cPassthruMsg {
225-
addr_and_flags: self.port.i2c_address() + I2C_READ_FLAG,
226-
transfer_len: len, // How much to read
227-
},
228-
];
229-
let msgs_len = size_of::<EcParamsI2cPassthruMsg>() * messages.len();
230-
let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) };
231-
232-
let params = EcParamsI2cPassthru {
233-
port: self.port.i2c_port()?,
234-
messages: messages.len() as u8,
235-
msg: [], // Messages are copied right after this struct
236-
};
237-
let params_len = size_of::<EcParamsI2cPassthru>();
238-
let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(&params) };
239-
240-
let mut buffer: Vec<u8> = vec![0; params_len + msgs_len + addr_bytes.len()];
241-
buffer[0..params_len].copy_from_slice(params_buffer);
242-
buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer);
243-
buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes);
244-
245-
let data = self.send_ec_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?;
246-
let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) };
247-
let res_data = &data[size_of::<_EcI2cPassthruResponse>()..];
248-
// TODO: Seems to be either one, non-deterministically
249-
debug_assert!(res.messages as usize == messages.len() || res.messages == 0);
250-
Ok(EcI2cPassthruResponse {
251-
i2c_status: res.i2c_status,
252-
data: res_data.to_vec(),
253-
})
146+
i2c_read(
147+
&self.ec,
148+
self.port.i2c_port().unwrap(),
149+
self.port.i2c_address(),
150+
addr,
151+
len,
152+
)
254153
}
255154

256155
fn ccgx_read(&self, reg: ControlRegisters, len: u16) -> EcResult<Vec<u8>> {
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use crate::chromium_ec::command::EcCommands;
2+
use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResult};
3+
use crate::util;
4+
use alloc::format;
5+
use alloc::string::ToString;
6+
use alloc::vec;
7+
use alloc::vec::Vec;
8+
use std::mem::size_of;
9+
10+
/// Maximum transfer size for one I2C transaction supported by the chip
11+
pub const MAX_I2C_CHUNK: usize = 128;
12+
13+
#[repr(C, packed)]
14+
pub struct EcParamsI2cPassthruMsg {
15+
/// Slave address and flags
16+
addr_and_flags: u16,
17+
transfer_len: u16,
18+
}
19+
20+
#[repr(C, packed)]
21+
pub struct EcParamsI2cPassthru {
22+
port: u8,
23+
/// How many messages
24+
messages: u8,
25+
msg: [EcParamsI2cPassthruMsg; 0],
26+
}
27+
28+
#[repr(C, packed)]
29+
struct _EcI2cPassthruResponse {
30+
i2c_status: u8,
31+
/// How many messages
32+
messages: u8,
33+
data: [u8; 0],
34+
}
35+
36+
#[derive(Debug)]
37+
pub struct EcI2cPassthruResponse {
38+
pub i2c_status: u8, // TODO: Can probably use enum
39+
pub data: Vec<u8>,
40+
}
41+
42+
impl EcI2cPassthruResponse {
43+
pub fn is_successful(&self) -> EcResult<()> {
44+
if self.i2c_status & 1 > 0 {
45+
return Err(EcError::DeviceError(
46+
"I2C Transfer not acknowledged".to_string(),
47+
));
48+
}
49+
if self.i2c_status & (1 << 1) > 0 {
50+
return Err(EcError::DeviceError("I2C Transfer timeout".to_string()));
51+
}
52+
// I'm not aware of any other errors, but there might be.
53+
// But I don't think multiple errors can be indicated at the same time
54+
assert_eq!(self.i2c_status, 0);
55+
Ok(())
56+
}
57+
}
58+
59+
/// Indicate that it's a read, not a write
60+
const I2C_READ_FLAG: u16 = 1 << 15;
61+
62+
pub fn i2c_read(
63+
ec: &CrosEc,
64+
i2c_port: u8,
65+
i2c_addr: u16,
66+
addr: u16,
67+
len: u16,
68+
) -> EcResult<EcI2cPassthruResponse> {
69+
trace!(
70+
"i2c_read(i2c_port: 0x{:X}, i2c_addr: 0x{:X}, addr: 0x{:X}, len: 0x{:X})",
71+
i2c_port,
72+
i2c_addr,
73+
addr,
74+
len
75+
);
76+
if usize::from(len) > MAX_I2C_CHUNK {
77+
return EcResult::Err(EcError::DeviceError(format!(
78+
"i2c_read too long. Must be <128, is: {}",
79+
len
80+
)));
81+
}
82+
let addr_bytes = u16::to_le_bytes(addr);
83+
let messages = vec![
84+
EcParamsI2cPassthruMsg {
85+
addr_and_flags: i2c_addr,
86+
transfer_len: addr_bytes.len() as u16,
87+
},
88+
EcParamsI2cPassthruMsg {
89+
addr_and_flags: i2c_addr + I2C_READ_FLAG,
90+
transfer_len: len, // How much to read
91+
},
92+
];
93+
let msgs_len = size_of::<EcParamsI2cPassthruMsg>() * messages.len();
94+
let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) };
95+
96+
let params = EcParamsI2cPassthru {
97+
port: i2c_port,
98+
messages: messages.len() as u8,
99+
msg: [], // Messages are copied right after this struct
100+
};
101+
let params_len = size_of::<EcParamsI2cPassthru>();
102+
let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(&params) };
103+
104+
let mut buffer: Vec<u8> = vec![0; params_len + msgs_len + addr_bytes.len()];
105+
buffer[0..params_len].copy_from_slice(params_buffer);
106+
buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer);
107+
buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes);
108+
109+
let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?;
110+
let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) };
111+
let res_data = &data[size_of::<_EcI2cPassthruResponse>()..];
112+
// TODO: Seems to be either one, non-deterministically
113+
debug_assert!(res.messages as usize == messages.len() || res.messages == 0);
114+
Ok(EcI2cPassthruResponse {
115+
i2c_status: res.i2c_status,
116+
data: res_data.to_vec(),
117+
})
118+
}
119+
120+
pub fn i2c_write(
121+
ec: &CrosEc,
122+
i2c_port: u8,
123+
i2c_addr: u16,
124+
addr: u16,
125+
data: &[u8],
126+
) -> EcResult<EcI2cPassthruResponse> {
127+
trace!(
128+
" i2c_write(addr: {}, len: {}, data: {:?})",
129+
addr,
130+
data.len(),
131+
data
132+
);
133+
let addr_bytes = [addr as u8, (addr >> 8) as u8];
134+
let messages = vec![EcParamsI2cPassthruMsg {
135+
addr_and_flags: i2c_addr,
136+
transfer_len: (addr_bytes.len() + data.len()) as u16,
137+
}];
138+
let msgs_len = size_of::<EcParamsI2cPassthruMsg>() * messages.len();
139+
let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) };
140+
141+
let params = EcParamsI2cPassthru {
142+
port: i2c_port,
143+
messages: messages.len() as u8,
144+
msg: [], // Messages are copied right after this struct
145+
};
146+
let params_len = size_of::<EcParamsI2cPassthru>();
147+
let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(&params) };
148+
149+
let mut buffer: Vec<u8> = vec![0; params_len + msgs_len + addr_bytes.len() + data.len()];
150+
buffer[0..params_len].copy_from_slice(params_buffer);
151+
buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer);
152+
buffer[params_len + msgs_len..params_len + msgs_len + addr_bytes.len()]
153+
.copy_from_slice(&addr_bytes);
154+
buffer[params_len + msgs_len + addr_bytes.len()..].copy_from_slice(data);
155+
156+
let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?;
157+
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
159+
debug_assert_eq!(res.messages as usize, messages.len());
160+
Ok(EcI2cPassthruResponse {
161+
i2c_status: res.i2c_status,
162+
data: vec![], // Writing doesn't return any data
163+
})
164+
}

framework_lib/src/chromium_ec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub mod command;
2323
pub mod commands;
2424
#[cfg(feature = "cros_ec_driver")]
2525
mod cros_ec;
26+
pub mod i2c_passthrough;
2627
pub mod input_deck;
2728
mod portio;
2829
mod portio_mec;

0 commit comments

Comments
 (0)