Add Mshroom machine implementation
[temp2RGB.git] / src / piix4_i2c.rs
1 // Partial implementation for PCI IDE ISA Xcelerator.
2 // https://www.kernel.org/doc/html/latest/i2c/summary.html
3
4 use std::time::Duration;
5
6 use crate::{timer, wrapper_winring0};
7
8 pub const I2C_BLOCK_MAX: usize = 32;
9
10 #[repr(u16)]
11 #[derive(Clone, Copy, Debug)]
12 enum TransactionType {
13 I2cSmbusQuick = 0,
14 I2cSmbusByte = 1,
15 I2cSmbusByteData = 2,
16 I2cSmbusWordData = 3,
17 I2cSmbusProcCall = 4,
18 I2cSmbusBlockData = 5,
19 I2cSmbusI2cBlockBroken = 6,
20 I2cSmbusBlockProcCall = 7, /* SMBus 2.0 */
21 I2cSmbusI2cBlockData = 8,
22 }
23
24 #[repr(u16)]
25 #[derive(Clone, Copy, Debug)]
26 enum Piix4TransactionType {
27 Piix4Quick = 0x00,
28 Piix4Byte = 0x04,
29 Piix4ByteData = 0x08,
30 Piix4WordData = 0x0C,
31 Piix4BlockData = 0x14,
32 }
33
34 // PIIX4 SMBus address offsets
35
36 #[repr(u16)]
37 #[derive(Clone, Copy, Debug)]
38 enum SMBusAddressOffsets {
39 Smbhststs = 0,
40 Smbhslvsts = 1,
41 Smbhstcnt = 2,
42 Smbhstcmd = 3,
43 Smbhstadd = 4,
44 Smbhstdat0 = 5,
45 Smbhstdat1 = 6,
46 Smbblkdat = 7,
47 Smbslvcnt = 8,
48 Smbshdwcmd = 9,
49 Smbslvevt = 0xA,
50 Smbslvdat = 0xC,
51 }
52
53 #[repr(u8)]
54 #[derive(Clone, Copy)]
55 enum AccessType {
56 Write = 0,
57 Read = 1,
58 }
59
60 pub struct I2c {
61 base_address: u16,
62 }
63
64 enum XferResult {
65 Ok,
66 BlockData(Vec<u8>),
67 }
68
69 #[derive(Debug)]
70 enum Error {
71 Busy,
72 Timeout,
73 IO,
74 Data,
75 }
76
77 impl I2c {
78 pub fn new(base_address: u16) -> Self {
79 I2c { base_address }
80 }
81
82 pub fn write_block_data(&self, addr: u8, command: u8, data: &[u8]) {
83 let l = data.len();
84 assert!(
85 l <= I2C_BLOCK_MAX,
86 "Data length must not exceed {}",
87 I2C_BLOCK_MAX
88 );
89 let mut data_block = [0u8; I2C_BLOCK_MAX + 2];
90 data_block[0] = l as u8;
91 data_block[1..l + 1].copy_from_slice(data);
92
93 unsafe {
94 if let Err(error) = self.i2c_smbus_xfer(
95 addr,
96 AccessType::Write,
97 command,
98 TransactionType::I2cSmbusBlockData,
99 Some(&data_block),
100 ) {
101 println!("Error when writing block (I2c): {error:?}");
102 }
103 }
104 }
105
106 pub fn i2c_smbus_write_quick(&self, addr: u8, value: u8) {
107 unsafe {
108 let _ = self.i2c_smbus_xfer(
109 addr,
110 AccessType::Write,
111 value,
112 TransactionType::I2cSmbusQuick,
113 None,
114 );
115 }
116 }
117
118 unsafe fn i2c_smbus_xfer(
119 &self,
120 addr: u8,
121 access_type: AccessType,
122 command: u8,
123 transaction_type: TransactionType, // Called 'size' in 'i2c_smbus\i2c_smbus_piix4.cpp'.
124 data: Option<&[u8]>,
125 ) -> Result<XferResult, Error> {
126 let piix4_transaction_type = match transaction_type {
127 TransactionType::I2cSmbusQuick => {
128 self.write_io_port_byte(
129 SMBusAddressOffsets::Smbhstadd,
130 (addr << 1) | access_type as u8,
131 );
132 Piix4TransactionType::Piix4Quick
133 }
134 TransactionType::I2cSmbusByte => todo!(),
135 TransactionType::I2cSmbusByteData => todo!(), // Here 'data' should be a byte, maybe using a enum?.
136 TransactionType::I2cSmbusWordData => todo!(), // Here 'data' should be a u16, maybe using a enum?.
137 TransactionType::I2cSmbusBlockData => {
138 self.write_io_port_byte(
139 SMBusAddressOffsets::Smbhstadd,
140 (addr << 1) | access_type as u8,
141 );
142 self.write_io_port_byte(SMBusAddressOffsets::Smbhstcmd, command);
143 if let AccessType::Write = access_type {
144 let len = data.unwrap()[0];
145 if len == 0 || len > I2C_BLOCK_MAX as u8 {
146 panic!("Invalid len value: {}", len);
147 }
148
149 self.write_io_port_byte(SMBusAddressOffsets::Smbhstdat0, len);
150 self.read_io_port_byte(SMBusAddressOffsets::Smbhstcnt); // TODO: do something of the result!?
151 for i in 1..=len {
152 self.write_io_port_byte(
153 SMBusAddressOffsets::Smbblkdat,
154 data.unwrap()[i as usize],
155 );
156 }
157 }
158 Piix4TransactionType::Piix4BlockData
159 }
160 _ => panic!("Not supported: {:?}", transaction_type),
161 };
162
163 self.write_io_port_byte(
164 SMBusAddressOffsets::Smbhstcnt,
165 piix4_transaction_type as u8 & 0x1C,
166 );
167
168 self.piix4_transaction()?;
169
170 // if let (AccessType::Write, Piix4TransactionType::Piix4Quick) = (access_type, piix4_transaction_type) {
171 // return Ok(())
172 // }
173
174 match piix4_transaction_type {
175 Piix4TransactionType::Piix4Quick => Ok(XferResult::Ok),
176 Piix4TransactionType::Piix4Byte => todo!(),
177 Piix4TransactionType::Piix4ByteData => todo!(),
178 Piix4TransactionType::Piix4WordData => todo!(),
179 Piix4TransactionType::Piix4BlockData => {
180 let l = self.read_io_port_byte(SMBusAddressOffsets::Smbhstdat0) as usize;
181 if l == 0 || l > I2C_BLOCK_MAX {
182 return Err(Error::Data);
183 }
184 self.read_io_port_byte(SMBusAddressOffsets::Smbhstcnt);
185 let mut data = vec![0; l + 1];
186 for i in 1..=l {
187 data[i] = self.read_io_port_byte(SMBusAddressOffsets::Smbblkdat);
188 }
189 Ok(XferResult::BlockData(data))
190 }
191 }
192 }
193
194 unsafe fn piix4_transaction(&self) -> Result<(), Error> {
195 let timer = timer::Sleep::new();
196
197 // Make sure the SMBus is ready to start transmitting.
198 let mut res = self.read_io_port_byte(SMBusAddressOffsets::Smbhststs);
199 if res != 0x00 {
200 self.write_io_port_byte(SMBusAddressOffsets::Smbhststs, res);
201 res = self.read_io_port_byte(SMBusAddressOffsets::Smbhststs);
202 if res != 0x00 {
203 return Err(Error::Busy);
204 }
205 }
206
207 // Start the transaction by setting bit 6.
208 res = self.read_io_port_byte(SMBusAddressOffsets::Smbhstcnt);
209 self.write_io_port_byte(SMBusAddressOffsets::Smbhstcnt, res | 0x40);
210
211 // let duration: i64 = -2_500; // 250 us.
212 let mut n = 0;
213 loop {
214 timer.wait(Duration::from_micros(250));
215
216 res = self.read_io_port_byte(SMBusAddressOffsets::Smbhststs);
217 // println!("Res: {}", res);
218 if res > 0x01 {
219 break;
220 }
221
222 if n >= 100 {
223 return Err(Error::Timeout);
224 }
225 n += 1;
226 }
227 // println!("-----");
228
229 if res & 0x10 != 0x00 || res & 0x08 != 0x0 || res & 0x04 != 0x0 {
230 return Err(Error::IO);
231 }
232
233 res = self.read_io_port_byte(SMBusAddressOffsets::Smbhststs);
234 if res != 0x00 {
235 self.write_io_port_byte(SMBusAddressOffsets::Smbhststs, res);
236 }
237
238 Ok(())
239 }
240
241 unsafe fn write_io_port_byte(&self, op: SMBusAddressOffsets, value: u8) {
242 wrapper_winring0::WriteIoPortByte(self.base_address + op as u16, value);
243 }
244
245 unsafe fn read_io_port_byte(&self, op: SMBusAddressOffsets) -> u8 {
246 wrapper_winring0::ReadIoPortByte(self.base_address + op as u16)
247 }
248 }
249
250 /*
251 type ADL_MAIN_MALLOC_CALLBACK = unsafe fn(c_int) -> *mut c_void;
252 type ADL_CONTEXT_HANDLE = *mut c_void;
253
254 type ADL2_MAIN_CONTROL_CREATE = unsafe extern "C" fn(ADL_MAIN_MALLOC_CALLBACK, c_int, *mut ADL_CONTEXT_HANDLE) -> c_int;
255 type ADL2_MAIN_CONTROL_DESTROY = unsafe extern "C" fn(ADL_CONTEXT_HANDLE) -> c_int;
256 type ADL2_ADAPTER_NUMBEROFADAPTERS_GET = unsafe extern "C" fn(ADL_CONTEXT_HANDLE, *mut c_int) -> c_int;
257
258 pub fn test() {
259 unsafe {
260 let hDLL = LoadLibraryW(w!("atiadlxx.dll")).unwrap();
261 println!("{:?}", hDLL);
262
263 let ADL2_Main_Control_Create: ADL2_MAIN_CONTROL_CREATE = transmute(&GetProcAddress(hDLL, s!("ADL2_Main_Control_Create")).unwrap());
264 let ADL2_Main_Control_Destroy: ADL2_MAIN_CONTROL_DESTROY = transmute(&GetProcAddress(hDLL, s!("ADL2_Main_Control_Destroy")).unwrap());
265 let ADL2_Adapter_NumberOfAdapters_Get: ADL2_ADAPTER_NUMBEROFADAPTERS_GET = transmute(&GetProcAddress(hDLL, s!("ADL2_Adapter_NumberOfAdapters_Get")).unwrap());
266
267
268 let m: *mut c_void = libc::malloc(4);
269
270
271 }
272 }
273 */