i2c scratch 2
diff --git a/services/i2c/backend-aspeed/src/lib.rs b/services/i2c/backend-aspeed/src/lib.rs index 46885f6..0e90b81 100644 --- a/services/i2c/backend-aspeed/src/lib.rs +++ b/services/i2c/backend-aspeed/src/lib.rs
@@ -45,6 +45,20 @@ use pw_log; // --------------------------------------------------------------------------- +// I2C Slave Command Register Constants +// --------------------------------------------------------------------------- +// These are copied from aspeed_ddk::i2c_core::constants which is private. + +/// Enable slave packet mode +const AST_I2CS_PKT_MODE_EN: u32 = 1 << 16; +/// Slave active for all addresses +const AST_I2CS_ACTIVE_ALL: u32 = 0x3 << 17; +/// Enable slave RX buffer +const AST_I2CS_RX_BUFF_EN: u32 = 1 << 7; +/// Enable slave TX buffer +const AST_I2CS_TX_BUFF_EN: u32 = 1 << 6; + +// --------------------------------------------------------------------------- // Error mapping // --------------------------------------------------------------------------- @@ -350,13 +364,17 @@ self.slave_tx_bufs[bus as usize][..len].copy_from_slice(&data[..len]); self.slave_tx_lens[bus as usize] = len; - // Pre-load the hardware TX buffer and length register so it's ready for the next - // master read. We do NOT set AST_I2CS_TX_BUFF_EN here because that would put the - // slave into TX mode and prevent it from receiving writes. The TX_BUFF_EN flag - // will be set later when we detect a ReadRequest event. + // Pre-load the hardware TX buffer and enable it so the slave can respond to reads. + // CRITICAL: We must set TX_BUFF_EN here, not in the ReadRequest handler, because + // by the time we detect ReadRequest the master has already started clocking and + // it's too late to respond (resulting in 0xFF on the bus). + // + // Setting both RX_BUFF_EN and TX_BUFF_EN allows the slave to simultaneously: + // - Accept writes from the master (RX mode) + // - Respond immediately to reads with pre-loaded data (TX mode) let (regs, buffs) = self.controller_regs(bus)?; - // Manually pre-load TX buffer without triggering transmission. + // Manually pre-load TX buffer and arm it for transmission. // Only copy 1 byte to match the DDK's current limitation. let to_write = 1.min(len); if to_write > 0 { @@ -368,9 +386,30 @@ // Set transfer length register (tx_data_byte_count = len - 1) regs.i2cc0c() .modify(|_, w| w.tx_data_byte_count().bits((to_write - 1) as u8)); + + // ARM TX BUFFER: Set both RX_BUFF_EN and TX_BUFF_EN to enable simultaneous + // receive (for writes) and transmit (for reads). This is required for the + // slave to respond to reads without delay. + let mut cmd = AST_I2CS_ACTIVE_ALL | AST_I2CS_PKT_MODE_EN; + cmd |= AST_I2CS_RX_BUFF_EN; // Keep RX enabled for writes + cmd |= AST_I2CS_TX_BUFF_EN; // Arm TX for immediate read response + regs.i2cs28().write(|w| w.bits(cmd)); } - // Note: We do NOT set AST_I2CS_TX_BUFF_EN here - that happens in slave_wait_event - // when ReadRequest is detected, to avoid blocking RX operations. + } + + Ok(()) + } + + /// Re-enable RX after a read transaction completes. + /// + /// Called from slave_wait_event() after a DataSent event to restore RX mode. + fn slave_rearm_rx(&mut self, bus: u8) -> Result<(), ResponseCode> { + let (regs, _) = self.controller_regs(bus)?; + + unsafe { + let mut cmd = AST_I2CS_ACTIVE_ALL | AST_I2CS_PKT_MODE_EN; + cmd |= AST_I2CS_RX_BUFF_EN; + regs.i2cs28().write(|w| w.bits(cmd)); } Ok(()) @@ -418,14 +457,15 @@ return Ok((SlaveEventKind::DataReceived, n)); } Some(SlaveEvent::ReadRequest) => { - // Hardware should already have the TX buffer armed from slave_set_response(), - // but call slave_write() as a fallback in case the timing worked out. - let _ = i2c.slave_write(&tx_local[..tx_len]); - return Ok((SlaveEventKind::ReadRequest, 0)); + // TX buffer was pre-armed in slave_set_response(), so the hardware + // should respond automatically. We just need to wait for DataSent. + continue; } Some(SlaveEvent::DataSent { len: _ }) => { - // Read transaction completed — treat same as ReadRequest - // (data was already pre-loaded and sent by hardware). + // Read transaction completed. Re-arm RX mode for next write. + // Drop i2c to release register borrows before calling slave_rearm_rx. + drop(i2c); + let _ = self.slave_rearm_rx(bus); return Ok((SlaveEventKind::ReadRequest, 0)); } Some(SlaveEvent::Stop) => {