blob: 604bcd27bc7549808787670a73a58d813815715e [file]
// Licensed under the Apache-2.0 license
//! embedded-hal trait implementations for AST1060 I2C driver
//!
//! This module provides [embedded-hal](https://docs.rs/embedded-hal) trait implementations
//! for the AST1060 I2C controller, enabling compatibility with the broader Rust embedded
//! ecosystem.
//!
//! # Implementation Status
//!
//! Currently implements:
//! - [`embedded_hal::i2c::I2c`] - Master mode I2C operations
//! - [`embedded_hal::i2c::Error`] - Standard error type mapping
//!
//! This allows the AST1060 I2C driver to be used with any crate that accepts embedded-hal
//! I2C traits, such as device drivers, protocols, and middleware.
//!
//! # Hardware Features
//!
//! The AST1060 I2C controller supports:
//! - **14 independent I2C controllers**
//! - **Buffer mode with 32-byte FIFO** for efficient transfers
//! - **Multi-master capability** with arbitration
//! - **Standard (100 kHz) and Fast (400 kHz) modes**
//! - **Slave/target mode** with hardware packet mode and interrupts
//! - **`SMBus` protocol support**
//! - **Automatic bus recovery**
//!
//! # Slave Mode API
//!
//! Slave functionality is provided through hardware-specific methods:
//! - [`configure_slave()`](super::controller::Ast1060I2c::configure_slave) - Configure slave address and mode
//! - [`slave_read()`](super::controller::Ast1060I2c::slave_read) - Read data received from master
//! - [`slave_write()`](super::controller::Ast1060I2c::slave_write) - Write response data for master
//! - [`handle_slave_interrupt()`](super::controller::Ast1060I2c::handle_slave_interrupt) - Process slave events
//!
//! These methods are more ergonomic and hardware-aware than generic HAL traits,
//! exposing AST1060-specific features like packet mode and structured interrupt events.
//!
//! # Integration
//!
//! For Hubris RTOS integration, see the `drv-ast1060-i2c-refactor` crate which
//! wraps this driver to implement the `drv-i2c-types::I2cHardware` trait.
//!
//! # Example Usage
//!
//! ```no_run
//! use embedded_hal::i2c::I2c;
//! use aspeed_ddk::i2c_core::controller::Ast1060I2c;
//! use aspeed_ddk::i2c_core::types::{I2cConfig, I2cSpeed};
//!
//! // Initialize I2C controller 0 at 400 kHz
//! let config = I2cConfig::new(I2cSpeed::Fast);
//! let mut i2c = unsafe { Ast1060I2c::new(0) };
//! i2c.init_hardware(&config);
//!
//! // Use embedded-hal I2c trait methods
//! i2c.write(0x50, &[0x10, 0x20])?;
//! let mut buffer = [0u8; 4];
//! i2c.read(0x51, &mut buffer)?;
//! i2c.write_read(0x52, &[0x00], &mut buffer)?;
//! # Ok::<(), aspeed_ddk::i2c_core::error::I2cError>(())
//! ```
use super::controller::Ast1060I2c;
use super::error::I2cError;
use embedded_hal::i2c::{
Error, ErrorKind, ErrorType, I2c, NoAcknowledgeSource, Operation, SevenBitAddress,
};
// ============================================================================
// embedded-hal Error Trait Implementation
// ============================================================================
/// Implements the embedded-hal Error trait for `I2cError`.
///
/// Maps AST1060-specific I2C errors to standard embedded-hal `ErrorKind` values.
/// This enables interoperability with any code using the embedded-hal I2C traits.
///
/// # Error Mappings
///
/// - `NoAcknowledge` → `ErrorKind::NoAcknowledge` - Slave did not acknowledge
/// - `ArbitrationLoss` → `ErrorKind::ArbitrationLoss` - Lost bus arbitration
/// - `Bus`, `BusRecoveryFailed` → `ErrorKind::Bus` - Bus errors
/// - `Overrun` → `ErrorKind::Overrun` - Data overrun/underrun
/// - All others → `ErrorKind::Other` - Hardware-specific errors
impl Error for I2cError {
fn kind(&self) -> ErrorKind {
match self {
I2cError::NoAcknowledge => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
I2cError::ArbitrationLoss => ErrorKind::ArbitrationLoss,
I2cError::Bus | I2cError::BusRecoveryFailed => ErrorKind::Bus,
I2cError::Overrun => ErrorKind::Overrun,
I2cError::Timeout
| I2cError::Busy
| I2cError::Invalid
| I2cError::Abnormal
| I2cError::SlaveError
| I2cError::InvalidAddress => ErrorKind::Other,
}
}
}
/// Associates the `I2cError` type with `Ast1060I2c` for embedded-hal trait implementations.
impl<Y: FnMut(u32)> ErrorType for Ast1060I2c<'_, Y> {
type Error = I2cError;
}
// ============================================================================
// embedded-hal I2c Trait Implementation (Master Mode)
// ============================================================================
/// Implements the embedded-hal I2c trait for `Ast1060I2c`.
///
/// Provides standard I2C master mode operations compatible with the embedded-hal ecosystem.
/// This implementation uses the AST1060's buffer mode with a 32-byte FIFO for efficient
/// data transfer.
///
/// # Supported Operations
///
/// - **write**: Send data to a slave device
/// - **read**: Receive data from a slave device
/// - **`write_read`**: Write then read without releasing the bus (combined transaction)
/// - **transaction**: Execute a sequence of read/write operations
///
/// # Buffer Mode Limitations
///
/// The AST1060 hardware uses a 32-byte FIFO buffer. For transfers larger than 32 bytes,
/// the driver automatically splits them into multiple hardware transactions while
/// maintaining the logical transaction semantics.
///
/// # Example
///
/// ```no_run
/// use embedded_hal::i2c::I2c;
/// # use aspeed_ddk::i2c_core::controller::Ast1060I2c;
/// # let mut i2c = unsafe { Ast1060I2c::new(0) };
///
/// // Write data to slave at address 0x50
/// i2c.write(0x50, &[0x00, 0x01, 0x02])?;
///
/// // Read data from slave at address 0x51
/// let mut buffer = [0u8; 4];
/// i2c.read(0x51, &mut buffer)?;
///
/// // Combined write-read transaction
/// i2c.write_read(0x52, &[0xA0], &mut buffer)?;
/// # Ok::<(), aspeed_ddk::i2c_core::error::I2cError>(())
/// ```
impl<Y: FnMut(u32)> I2c<SevenBitAddress> for Ast1060I2c<'_, Y> {
/// Writes data to an I2C slave device.
///
/// # Arguments
///
/// * `address` - 7-bit I2C slave address
/// * `bytes` - Data bytes to write
///
/// # Errors
///
/// Returns an error if:
/// - The slave does not acknowledge (`NoAcknowledge`)
/// - Bus arbitration is lost (`ArbitrationLoss`)
/// - A bus error occurs (`Bus`)
/// - The operation times out (`Timeout`)
fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> {
Ast1060I2c::write(self, address, bytes)
}
/// Reads data from an I2C slave device.
///
/// # Arguments
///
/// * `address` - 7-bit I2C slave address
/// * `buffer` - Buffer to store received data
///
/// # Errors
///
/// Returns an error if:
/// - The slave does not acknowledge (`NoAcknowledge`)
/// - Bus arbitration is lost (`ArbitrationLoss`)
/// - A bus error occurs (`Bus`)
/// - The operation times out (`Timeout`)
fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> {
Ast1060I2c::read(self, address, buffer)
}
/// Performs a combined write-read transaction without releasing the bus.
///
/// This is commonly used to write a register address followed by reading
/// the register value in a single atomic transaction.
///
/// # Arguments
///
/// * `address` - 7-bit I2C slave address
/// * `bytes` - Data bytes to write first
/// * `buffer` - Buffer to store read data
///
/// # Errors
///
/// Returns an error if:
/// - The slave does not acknowledge (`NoAcknowledge`)
/// - Bus arbitration is lost (`ArbitrationLoss`)
/// - A bus error occurs (`Bus`)
/// - The operation times out (`Timeout`)
///
/// # Example
///
/// ```no_run
/// # use embedded_hal::i2c::I2c;
/// # use aspeed_ddk::i2c_core::controller::Ast1060I2c;
/// # let mut i2c = unsafe { Ast1060I2c::new(0) };
/// // Read from register 0x10 at slave address 0x50
/// let mut data = [0u8; 4];
/// i2c.write_read(0x50, &[0x10], &mut data)?;
/// # Ok::<(), aspeed_ddk::i2c_core::error::I2cError>(())
/// ```
fn write_read(
&mut self,
address: SevenBitAddress,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Self::Error> {
Ast1060I2c::write_read(self, address, bytes, buffer)
}
/// Executes a sequence of I2C operations as a single transaction.
///
/// Each operation is executed in order. The bus is held between operations
/// and only released after all operations complete or an error occurs.
///
/// # Arguments
///
/// * `address` - 7-bit I2C slave address for all operations
/// * `operations` - Slice of read/write operations to execute
///
/// # Errors
///
/// Returns an error if any operation fails. Subsequent operations are not
/// executed if an error occurs.
///
/// # Example
///
/// ```no_run
/// # use embedded_hal::i2c::{I2c, Operation};
/// # use aspeed_ddk::i2c_core::controller::Ast1060I2c;
/// # let mut i2c = unsafe { Ast1060I2c::new(0) };
/// let mut read_buf = [0u8; 4];
/// let mut ops = [
/// Operation::Write(&[0x10]), // Write register address
/// Operation::Read(&mut read_buf), // Read register value
/// ];
/// i2c.transaction(0x50, &mut ops)?;
/// # Ok::<(), aspeed_ddk::i2c_core::error::I2cError>(())
/// ```
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
// Execute each operation in sequence
for op in operations {
match op {
Operation::Read(buf) => self.read(address, buf)?,
Operation::Write(data) => self.write(address, data)?,
}
}
Ok(())
}
}