blob: 6391910e7ef6e66ce72513c2ca4198655d62b4b1 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! AST1060 I2C Slave/Target Mode Implementation
//!
//! This module provides slave (target) mode functionality for the AST1060 I2C controllers.
//! In slave mode, the controller responds to requests from an external I2C master.
use super::I2cXferMode;
use super::{constants, controller::Ast1060I2c, error::I2cError};
/// Hardware buffer size (32 bytes / 8 DWORDs)
const BUFFER_SIZE: usize = 32;
/// Maximum slave receive buffer size (hardware limitation)
pub const SLAVE_BUFFER_SIZE: usize = 256;
/// Slave RX DMA enable bit in slave command register (i2cs28 bit 9).
///
/// When set, the hardware writes received bytes into the DMA buffer pointed to
/// by i2cs38/i2cs3c instead of the 32-byte FIFO. Supports up to 4096-byte transfers.
const AST_I2CS_RX_DMA_EN: u32 = 1 << 9;
/// Slave mode configuration
#[derive(Debug, Clone, Copy)]
pub struct SlaveConfig {
/// Primary slave address (7-bit)
pub address: u8,
/// Enable packet mode for slave
pub packet_mode: bool,
/// Use buffer mode (32 bytes) vs byte mode (1 byte)
pub buffer_mode: bool,
}
impl SlaveConfig {
/// Create a new slave configuration
pub fn new(address: u8) -> Result<Self, I2cError> {
if address > 0x7F {
return Err(I2cError::InvalidAddress);
}
Ok(Self {
address,
packet_mode: true, // Recommended for performance
buffer_mode: true, // Recommended for performance
})
}
}
/// Slave mode events
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlaveEvent {
/// Master is requesting to read from us (we need to send data)
ReadRequest,
/// Master is writing to us (we're receiving data)
WriteRequest,
/// Data received from master
DataReceived { len: usize },
/// Data sent to master
DataSent { len: usize },
/// Data received from master and send data to master (combined event)
DataReceivedAndSent { rx_len: usize, tx_len: usize },
/// Stop condition received
Stop,
}
/// Slave mode data buffer for application-level buffering
pub struct SlaveBuffer {
data: [u8; SLAVE_BUFFER_SIZE],
len: usize,
}
impl Default for SlaveBuffer {
fn default() -> Self {
Self::new()
}
}
impl SlaveBuffer {
#[must_use]
pub const fn new() -> Self {
Self {
data: [0u8; SLAVE_BUFFER_SIZE],
len: 0,
}
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data[..self.len]
}
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.data[..self.len]
}
pub fn set_len(&mut self, len: usize) {
self.len = len.min(SLAVE_BUFFER_SIZE);
}
#[must_use]
pub fn len(&self) -> usize {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn clear(&mut self) {
self.len = 0;
}
pub fn write(&mut self, data: &[u8]) -> usize {
let to_copy = data.len().min(SLAVE_BUFFER_SIZE);
self.data[..to_copy].copy_from_slice(&data[..to_copy]);
self.len = to_copy;
to_copy
}
}
impl<Y: FnMut(u32)> Ast1060I2c<'_, Y> {
#[inline]
fn slave_rx_len(&self) -> usize {
if self.xfer_mode == I2cXferMode::DmaMode {
self.regs().i2cs4c().read().dmarx_actual_len_byte().bits() as usize
} else {
// Hardware includes the I2C address byte in the buffer count (packet mode,
// I2CC00 bit 20). Subtract 1 to report only the payload byte count.
self.regs()
.i2cc0c()
.read()
.actual_rxd_pool_buffer_size()
.bits()
.saturating_sub(1) as usize
}
}
/// Arm slave receive path based on transfer mode.
///
/// This mirrors the old AST1060 driver behavior where packet-slave IRQ
/// branches re-arm either RX FIFO or RX DMA depending on `xfer_mode`.
fn arm_slave_receive(&mut self, cmd: &mut u32) {
if self.xfer_mode == I2cXferMode::DmaMode {
if let Some(dma_buf) = self.dma_buf.as_deref_mut() {
let dma_addr = dma_buf.as_mut_ptr() as u32;
let dma_len = u16::try_from(dma_buf.len().min(4096) - 1).unwrap_or(u16::MAX);
unsafe {
self.regs().i2cs4c().write(|w| w.bits(0));
self.regs().i2cs38().write(|w| w.bits(dma_addr));
self.regs().i2cs3c().write(|w| w.bits(dma_addr));
self.regs().i2cs2c().write(|w| {
w.dmarx_buf_len_byte()
.bits(dma_len)
.dmarx_buf_len_wr_enbl_for_cur_cmd()
.set_bit()
});
}
*cmd |= AST_I2CS_RX_DMA_EN;
} else {
*cmd |= constants::AST_I2CS_RX_BUFF_EN;
self.regs().i2cc0c().write(|w| unsafe {
w.rx_pool_buffer_size().bits(constants::I2C_BUF_SIZE - 1)
});
}
} else if self.xfer_mode == I2cXferMode::BufferMode {
*cmd |= constants::AST_I2CS_RX_BUFF_EN;
self.regs()
.i2cc0c()
.write(|w| unsafe { w.rx_pool_buffer_size().bits(constants::I2C_BUF_SIZE - 1) });
} else {
*cmd &= !constants::AST_I2CS_PKT_MODE_EN;
}
}
/// Configure the controller for slave mode
pub fn configure_slave(&mut self, config: &SlaveConfig) -> Result<(), I2cError> {
// Ensure master mode is disabled first
self.regs()
.i2cc00()
.modify(|_, w| w.enbl_master_fn().clear_bit());
// Set slave address
self.regs().i2cs40().write(|w| unsafe {
w.slave_dev_addr1()
.bits(config.address)
.enbl_slave_dev_addr1only_for_new_reg_mode()
.bit(true)
});
// Clear slave interrupts
self.clear_slave_interrupts();
// Enable slave mode and save address byte in packet mode (I2CC00 bit 20)
// This makes the hardware include the destination address byte in the receive buffer
// which is required for MCTP-over-SMBus (DSP0237) packet format.
self.regs().i2cc00().modify(|r, w| unsafe {
w.bits(
r.bits() | constants::AST_I2CC_SLAVE_EN | constants::AST_I2CC_SLAVE_PKT_SAVE_ADDR,
)
});
// Configure slave mode
let mut cmd = 0u32;
if config.packet_mode {
cmd |= constants::AST_I2CS_PKT_MODE_EN;
cmd |= constants::AST_I2CS_ACTIVE_ALL;
}
if self.xfer_mode == I2cXferMode::BufferMode {
cmd |= constants::AST_I2CS_RX_BUFF_EN;
self.regs()
.i2cc0c()
.write(|w| unsafe { w.rx_pool_buffer_size().bits(constants::I2C_BUF_SIZE - 1) });
} else if self.xfer_mode == I2cXferMode::DmaMode {
if let Some(dma_buf) = self.dma_buf.as_deref_mut() {
// Arm slave DMA: point hardware at the non-cached buffer and enable RX_DMA.
// i2cs38/i2cs3c hold the physical DMA buffer address (same address in
// both registers — the hardware uses both for different address widths).
// i2cs2c sets the DMA receive length and enables the length register.
let dma_addr = dma_buf.as_mut_ptr() as u32;
let dma_len = u16::try_from(dma_buf.len().min(4096) - 1).unwrap_or(u16::MAX);
unsafe {
self.regs().i2cs38().write(|w| w.bits(dma_addr));
self.regs().i2cs3c().write(|w| w.bits(dma_addr));
self.regs().i2cs2c().write(|w| {
w.dmarx_buf_len_byte()
.bits(dma_len)
.dmarx_buf_len_wr_enbl_for_cur_cmd()
.set_bit()
});
}
cmd |= AST_I2CS_RX_DMA_EN;
} else {
// No DMA buffer provided — fall back to buffer mode.
cmd |= constants::AST_I2CS_RX_BUFF_EN;
self.regs().i2cc0c().write(|w| unsafe {
w.rx_pool_buffer_size().bits(constants::I2C_BUF_SIZE - 1)
});
}
} else {
cmd &= !constants::AST_I2CS_PKT_MODE_EN;
}
// Set slave command register
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
// Enable slave interrupts
self.enable_slave_interrupts();
Ok(())
}
/// Enable slave mode interrupts
fn enable_slave_interrupts(&mut self) {
let mut mask = constants::AST_I2CS_PKT_DONE | constants::AST_I2CS_INACTIVE_TO;
if self.xfer_mode == I2cXferMode::BufferMode || self.xfer_mode == I2cXferMode::DmaMode {
mask |= constants::AST_I2CM_ABNORMAL
| constants::AST_I2CM_NORMAL_STOP
| constants::AST_I2CM_RX_DONE
| constants::AST_I2CM_TX_ACK;
}
unsafe {
self.regs().i2cs20().write(|w| w.bits(mask));
}
}
/// Clear slave mode interrupts
fn clear_slave_interrupts(&mut self) {
unsafe {
self.regs().i2cs24().write(|w| w.bits(0xFFFF_FFFF));
let _ = self.regs().i2cs24().read().bits();
}
}
/// Enable slave mode (re-enable after disable)
///
/// This re-enables slave mode and interrupts without reconfiguring the address.
/// Use `configure_slave()` for initial setup, this for re-enabling after `disable_slave()`.
pub fn enable_slave(&mut self) {
// Enable slave mode
self.regs()
.i2cc00()
.modify(|_, w| w.enbl_slave_fn().set_bit());
// Enable slave interrupts
self.enable_slave_interrupts();
}
/// Disable slave mode
pub fn disable_slave(&mut self) {
// Disable interrupts
unsafe {
self.regs().i2cs20().write(|w| w.bits(0));
}
// Clear interrupts
self.clear_slave_interrupts();
// Disable slave mode
self.regs()
.i2cc00()
.modify(|_, w| w.enbl_slave_fn().clear_bit());
}
/// Check if slave has received data
#[must_use]
pub fn slave_has_data(&self) -> bool {
let status = self.regs().i2cs24().read().bits();
(status & constants::AST_I2CS_RX_DONE) != 0
}
/// Read data received in slave mode
pub fn slave_read(&mut self, buffer: &mut [u8]) -> Result<usize, I2cError> {
// Get receive length from buffer length register
if self.xfer_mode == I2cXferMode::BufferMode {
let len = self
.regs()
.i2cc0c()
.read()
.actual_rxd_pool_buffer_size()
.bits() as usize;
let to_read = len.min(buffer.len()).min(BUFFER_SIZE);
// Read from buffer
self.copy_from_buffer(&mut buffer[..to_read])?;
// Re-enable RX buffer
let mut cmd = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_PKT_MODE_EN;
cmd |= constants::AST_I2CS_RX_BUFF_EN;
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
Ok(to_read)
} else if self.xfer_mode == I2cXferMode::DmaMode {
// DMA mode: the hardware has already DMA'd into `self.dma_buf`.
// Read actual received byte count from the DMA status register.
let hw_len = self.regs().i2cs4c().read().dmarx_actual_len_byte().bits() as usize;
let to_read = hw_len.min(buffer.len());
if let Some(dma_buf) = self.dma_buf.as_deref() {
let src_len = to_read.min(dma_buf.len());
buffer[..src_len].copy_from_slice(&dma_buf[..src_len]);
}
// Re-arm slave DMA for next receive
let mut cmd = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_PKT_MODE_EN;
if let Some(dma_buf) = self.dma_buf.as_deref_mut() {
let dma_addr = dma_buf.as_mut_ptr() as u32;
let dma_len = u16::try_from(dma_buf.len().min(4096) - 1).unwrap_or(u16::MAX);
unsafe {
self.regs().i2cs4c().write(|w| w.bits(0));
self.regs().i2cs38().write(|w| w.bits(dma_addr));
self.regs().i2cs3c().write(|w| w.bits(dma_addr));
self.regs().i2cs2c().write(|w| {
w.dmarx_buf_len_byte()
.bits(dma_len)
.dmarx_buf_len_wr_enbl_for_cur_cmd()
.set_bit()
});
}
cmd |= AST_I2CS_RX_DMA_EN;
} else {
cmd |= constants::AST_I2CS_RX_BUFF_EN;
}
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
Ok(to_read)
} else {
// byte mode
buffer[0] = self.regs().i2cc08().read().rx_byte_buffer().bits();
let cmd = constants::AST_I2CS_ACTIVE_ALL;
self.regs().i2cs28().write(|w| unsafe { w.bits(cmd) });
self.clear_slave_interrupts();
Ok(1)
}
}
/// Write data to send in slave mode (in response to read request)
pub fn slave_write(&mut self, data: &[u8]) -> Result<usize, I2cError> {
if data.is_empty() {
return Ok(0);
}
if self.xfer_mode == I2cXferMode::BufferMode {
let to_write = 1;
// Copy data to buffer
self.copy_to_buffer(&data[..to_write])?;
// Set transfer length
#[allow(clippy::cast_possible_truncation)]
self.regs()
.i2cc0c()
.write(|w| unsafe { w.tx_data_byte_count().bits(to_write as u8 - 1) });
// Trigger slave transmit
let mut cmd = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_PKT_MODE_EN;
cmd |= constants::AST_I2CS_TX_BUFF_EN;
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
Ok(to_write)
} else if self.xfer_mode == I2cXferMode::DmaMode {
// In DMA mode, copy data to DMA buffer and set TX length
let dma_buf = self.dma_buf.as_deref_mut().ok_or(I2cError::Invalid)?;
// Copy data to DMA buffer starting at offset 0
let to_write = data.len().min(dma_buf.len());
unsafe {
core::ptr::copy_nonoverlapping(data.as_ptr(), dma_buf.as_mut_ptr(), to_write);
}
// Clear TX status/offset register
unsafe {
self.regs().i2cs4c().write(|w| w.bits(0));
}
// Set TX length (len - 1) and enable write
let tx_len = u16::try_from(to_write - 1).map_err(|_| I2cError::Invalid)?;
unsafe {
self.regs().i2cs2c().modify(|_, w| {
w.dmatx_buf_len_byte()
.bits(tx_len)
.dmatx_buf_len_wr_enbl_for_cur_cmd()
.set_bit()
});
}
// Trigger slave transmit with TX DMA enabled
let mut cmd = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_PKT_MODE_EN;
cmd |= constants::AST_I2CS_TX_DMA_EN;
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
Ok(to_write)
} else {
// byte mode
let cmd = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_TX_CMD;
unsafe {
self.regs()
.i2cc08()
.write(|w| w.tx_byte_buffer().bits(data[0]));
self.regs().i2cs28().write(|w| w.bits(cmd));
}
self.clear_slave_interrupts();
Ok(1)
}
}
/// Handle slave mode interrupt
#[allow(clippy::too_many_lines)]
pub fn handle_slave_interrupt(&mut self) -> Option<SlaveEvent> {
let status = self.regs().i2cs24().read().bits();
if status == 0 {
return None;
}
// Check for errors first
if (status & constants::AST_I2CS_PKT_ERROR) != 0 {
self.clear_slave_interrupts();
return None;
}
if (status & constants::AST_I2CS_PKT_DONE) != 0 {
let mut cmd: u32 = constants::AST_I2CS_ACTIVE_ALL | constants::AST_I2CS_PKT_MODE_EN;
unsafe {
self.regs()
.i2cs24()
.write(|w| w.bits(constants::AST_I2CS_PKT_DONE));
}
let sts = status & (!(constants::AST_I2CS_PKT_DONE | constants::AST_I2CS_PKT_ERROR));
if sts == constants::AST_I2CS_SLAVE_MATCH
|| sts == constants::AST_I2CS_SLAVE_MATCH | constants::AST_I2CS_RX_DONE
{
// S: Sw
return Some(SlaveEvent::WriteRequest);
} else if sts == constants::AST_I2CS_SLAVE_MATCH | constants::AST_I2CS_WAIT_RX_DMA
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
{
// S: Sw|D
self.arm_slave_receive(&mut cmd);
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
return Some(SlaveEvent::DataReceived {
len: self.slave_rx_len(),
});
} else if sts == constants::AST_I2CS_SLAVE_MATCH | constants::AST_I2CS_STOP {
// S: Sw|P
self.arm_slave_receive(&mut cmd);
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
return Some(SlaveEvent::Stop);
} else if sts == constants::AST_I2CS_RX_DONE | constants::AST_I2CS_STOP
|| sts == constants::AST_I2CS_RX_DONE | constants::AST_I2CS_WAIT_RX_DMA
|| sts
== constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
| constants::AST_I2CS_STOP
|| sts
== constants::AST_I2CS_RX_DONE_NAK
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_STOP
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_STOP
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
| constants::AST_I2CS_STOP
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE_NAK
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_STOP
{
// S: (Sw)|D|(P)
return Some(SlaveEvent::DataReceived {
len: self.slave_rx_len(),
});
} else if sts == constants::AST_I2CS_RX_DONE | constants::AST_I2CS_WAIT_TX_DMA
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_TX_DMA
{
// S: rx_done | wait_tx
return Some(SlaveEvent::DataReceivedAndSent {
rx_len: self.slave_rx_len(),
tx_len: usize::from(
self.regs().i2cc0c().read().tx_data_byte_count().bits() + 1,
),
});
} else if sts == constants::AST_I2CS_SLAVE_MATCH | constants::AST_I2CS_WAIT_TX_DMA {
// S: Sw | wait_tx
return Some(SlaveEvent::DataSent {
len: usize::from(self.regs().i2cc0c().read().tx_data_byte_count().bits() + 1),
});
} else if sts == constants::AST_I2CS_WAIT_TX_DMA {
// S: wait_tx
return Some(SlaveEvent::DataSent {
len: usize::from(self.regs().i2cc0c().read().tx_data_byte_count().bits() + 1),
});
} else if sts == constants::AST_I2CS_TX_NAK | constants::AST_I2CS_STOP
|| sts == constants::AST_I2CS_STOP
|| sts
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_TX_NAK
| constants::AST_I2CS_STOP
{
// S: (Sr) (TX_NAK)|P — master read completed with NAK then STOP
self.arm_slave_receive(&mut cmd);
unsafe {
self.regs().i2cs28().write(|w| w.bits(cmd));
}
return Some(SlaveEvent::Stop);
} else {
// TODO packet slave sts
}
} else {
//byte irq
let cmd: u32 = constants::AST_I2CS_ACTIVE_ALL;
if status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
{
// S: Sw|D
let _byte_data = self.regs().i2cc08().read().rx_byte_buffer().bits();
self.regs().i2cs28().write(|w| unsafe { w.bits(cmd) });
self.regs().i2cs24().write(|w| unsafe { w.bits(status) });
self.regs().i2cs24().read().bits();
return Some(SlaveEvent::WriteRequest);
} else if status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
| constants::AST_I2CS_STOP
| constants::AST_I2CS_TX_NAK
|| status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_RX_DMA
| constants::AST_I2CS_STOP
{
// S: Sw|D|P
let _byte_data = self.regs().i2cc08().read().rx_byte_buffer().bits();
self.regs().i2cs28().write(|w| unsafe { w.bits(cmd) });
self.regs().i2cs24().write(|w| unsafe { w.bits(status) });
return Some(SlaveEvent::WriteRequest);
} else if status == constants::AST_I2CS_RX_DONE | constants::AST_I2CS_WAIT_RX_DMA {
// S: rD
return Some(SlaveEvent::DataReceived { len: 1 });
} else if status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_RX_DONE
| constants::AST_I2CS_WAIT_TX_DMA
{
// S: Sr|D
// received one byte
let _byte_data = self.regs().i2cc08().read().rx_byte_buffer().bits();
return Some(SlaveEvent::DataSent { len: 1 });
} else if status == constants::AST_I2CS_TX_ACK | constants::AST_I2CS_WAIT_TX_DMA {
// S: tD
return Some(SlaveEvent::DataSent { len: 1 });
} else if status == constants::AST_I2CS_STOP
|| status == constants::AST_I2CS_STOP | constants::AST_I2CS_TX_NAK
|| status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_STOP
| constants::AST_I2CS_TX_NAK
|| status
== constants::AST_I2CS_SLAVE_MATCH
| constants::AST_I2CS_WAIT_RX_DMA
| constants::AST_I2CS_STOP
| constants::AST_I2CS_TX_NAK
{
// S: P
self.regs().i2cs28().write(|w| unsafe { w.bits(cmd) });
self.regs().i2cs24().write(|w| unsafe { w.bits(status) });
return Some(SlaveEvent::Stop);
}
// TODO byte slave sts
}
None
}
}