#![allow(dead_code)]
mod consts;
mod instances;
pub use consts::{
I2cAddressMode, I2cError, I2cRegisters, I2cSpeed, I2C_DEFAULT_TIMEOUT, I2C_RX_FIFO_DEPTH,
I2C_TX_FIFO_DEPTH,
};
pub use instances::{
I2cClockConfig, I2cInstance, I2C0_BASE, I2C1_BASE, I2C2_BASE, I2C3_BASE, I2C4_BASE,
RTCSYS_I2C_BASE,
};
use consts::*;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
pub struct I2c {
regs: &'static I2cRegisters,
instance: I2cInstance,
speed: I2cSpeed,
clock_config: I2cClockConfig,
}
impl I2c {
pub fn new(instance: I2cInstance) -> Self {
let base = instance.base_address();
Self {
regs: unsafe { &*(base as *const I2cRegisters) },
instance,
speed: I2cSpeed::Fast,
clock_config: I2cClockConfig::CLK_100MHZ,
}
}
pub unsafe fn from_base_address(base: usize, instance: I2cInstance) -> Self {
Self {
regs: unsafe { &*(base as *const I2cRegisters) },
instance,
speed: I2cSpeed::Fast,
clock_config: I2cClockConfig::CLK_100MHZ,
}
}
pub fn set_clock_config(&mut self, config: I2cClockConfig) {
self.clock_config = config;
}
pub fn instance(&self) -> I2cInstance {
self.instance
}
pub fn speed(&self) -> I2cSpeed {
self.speed
}
fn disable(&self) {
self.regs.ic_enable.write(IC_ENABLE::ENABLE::CLEAR);
let mut timeout = I2C_DEFAULT_TIMEOUT;
while self.regs.ic_enable_status.is_set(IC_ENABLE_STATUS::IC_EN) {
timeout -= 1;
if timeout == 0 {
log::warn!("I2C disable timeout");
break;
}
}
}
fn enable(&self) {
self.regs.ic_enable.write(IC_ENABLE::ENABLE::SET);
}
pub fn is_enabled(&self) -> bool {
self.regs.ic_enable_status.is_set(IC_ENABLE_STATUS::IC_EN)
}
pub fn init(&mut self, speed: I2cSpeed) {
self.speed = speed;
self.disable();
self.regs.ic_con.write(
IC_CON::MASTER_MODE::SET
+ IC_CON::SPEED.val(speed as u32)
+ IC_CON::IC_10BITADDR_SLAVE::CLEAR
+ IC_CON::IC_10BITADDR_MASTER::CLEAR
+ IC_CON::IC_RESTART_EN::SET
+ IC_CON::IC_SLAVE_DISABLE::SET,
);
self.configure_scl_timing();
self.regs
.ic_sda_hold
.write(IC_SDA_HOLD::IC_SDA_HOLD.val(self.clock_config.sda_hold as u32));
self.regs
.ic_sda_setup
.write(IC_SDA_SETUP::SDA_SETUP.val(self.clock_config.sda_setup as u32));
self.regs
.ic_fs_spklen
.write(IC_FS_SPKLEN::IC_FS_SPKLEN.val(self.clock_config.fs_spklen as u32));
self.regs.ic_rx_tl.write(IC_RX_TL::RX_TL.val(0));
self.regs.ic_tx_tl.write(IC_TX_TL::TX_TL.val(0));
self.regs.ic_intr_mask.set(0);
let _ = self.regs.ic_clr_intr.get();
log::debug!(
"I2C{} initialized with {:?} mode",
self.instance.index(),
speed
);
}
fn configure_scl_timing(&self) {
self.regs
.ic_ss_scl_hcnt
.write(IC_SS_SCL_HCNT::IC_SS_SCL_HCNT.val(self.clock_config.ss_scl_hcnt as u32));
self.regs
.ic_ss_scl_lcnt
.write(IC_SS_SCL_LCNT::IC_SS_SCL_LCNT.val(self.clock_config.ss_scl_lcnt as u32));
self.regs
.ic_fs_scl_hcnt
.write(IC_FS_SCL_HCNT::IC_FS_SCL_HCNT.val(self.clock_config.fs_scl_hcnt as u32));
self.regs
.ic_fs_scl_lcnt
.write(IC_FS_SCL_LCNT::IC_FS_SCL_LCNT.val(self.clock_config.fs_scl_lcnt as u32));
}
fn set_target_address(&self, addr: u16, mode: I2cAddressMode) {
let was_enabled = self.is_enabled();
if was_enabled {
self.disable();
}
match mode {
I2cAddressMode::SevenBit => {
self.regs.ic_con.modify(IC_CON::IC_10BITADDR_MASTER::CLEAR);
}
I2cAddressMode::TenBit => {
self.regs.ic_con.modify(IC_CON::IC_10BITADDR_MASTER::SET);
}
}
self.regs.ic_tar.write(
IC_TAR::IC_TAR.val(addr as u32)
+ IC_TAR::SPECIAL::CLEAR
+ IC_TAR::GC_OR_START::GeneralCall,
);
if was_enabled {
self.enable();
}
}
fn wait_tx_fifo_not_full(&self) -> Result<(), I2cError> {
let mut timeout = I2C_DEFAULT_TIMEOUT;
while !self.regs.ic_status.is_set(IC_STATUS::ST_TFNF) {
timeout -= 1;
if timeout == 0 {
return Err(I2cError::Timeout);
}
}
Ok(())
}
fn wait_rx_fifo_not_empty(&self) -> Result<(), I2cError> {
let mut timeout = I2C_DEFAULT_TIMEOUT;
while !self.regs.ic_status.is_set(IC_STATUS::ST_RFNE) {
if self.regs.ic_raw_intr_stat.is_set(IC_RAW_INTR_STAT::IST_TX_ABRT) {
let _ = self.regs.ic_clr_tx_abrt.get();
return Err(I2cError::TxAbort);
}
timeout -= 1;
if timeout == 0 {
return Err(I2cError::Timeout);
}
}
Ok(())
}
fn wait_transfer_complete(&self) -> Result<(), I2cError> {
let mut timeout = I2C_DEFAULT_TIMEOUT;
while self.regs.ic_status.is_set(IC_STATUS::ST_ACTIVITY)
|| !self.regs.ic_status.is_set(IC_STATUS::ST_TFE)
{
if self.regs.ic_raw_intr_stat.is_set(IC_RAW_INTR_STAT::IST_TX_ABRT) {
let _ = self.regs.ic_clr_tx_abrt.get();
return Err(I2cError::TxAbort);
}
timeout -= 1;
if timeout == 0 {
return Err(I2cError::Timeout);
}
}
Ok(())
}
fn check_errors(&self) -> Result<(), I2cError> {
let raw_stat = self.regs.ic_raw_intr_stat.get();
if raw_stat & (1 << 6) != 0 {
let _ = self.regs.ic_clr_tx_abrt.get();
return Err(I2cError::TxAbort);
}
if raw_stat & (1 << 1) != 0 {
let _ = self.regs.ic_clr_rx_over.get();
return Err(I2cError::RxOverflow);
}
if raw_stat & (1 << 3) != 0 {
let _ = self.regs.ic_clr_tx_over.get();
return Err(I2cError::TxOverflow);
}
Ok(())
}
pub fn write(&self, addr: u8, data: &[u8]) -> Result<(), I2cError> {
self.write_with_mode(addr as u16, I2cAddressMode::SevenBit, data)
}
pub fn write_with_mode(
&self,
addr: u16,
mode: I2cAddressMode,
data: &[u8],
) -> Result<(), I2cError> {
if data.is_empty() {
return Ok(());
}
self.set_target_address(addr, mode);
self.enable();
let len = data.len();
for (i, &byte) in data.iter().enumerate() {
self.wait_tx_fifo_not_full()?;
let mut cmd = IC_DATA_CMD::DAT.val(byte as u32) + IC_DATA_CMD::CMD::Write;
if i == len - 1 {
cmd += IC_DATA_CMD::STOP::SET;
}
self.regs.ic_data_cmd.write(cmd);
}
self.wait_transfer_complete()?;
self.check_errors()
}
pub fn read(&self, addr: u8, buffer: &mut [u8]) -> Result<(), I2cError> {
self.read_with_mode(addr as u16, I2cAddressMode::SevenBit, buffer)
}
pub fn read_with_mode(
&self,
addr: u16,
mode: I2cAddressMode,
buffer: &mut [u8],
) -> Result<(), I2cError> {
if buffer.is_empty() {
return Ok(());
}
self.set_target_address(addr, mode);
self.enable();
let len = buffer.len();
for (i, byte) in buffer.iter_mut().enumerate() {
self.wait_tx_fifo_not_full()?;
let mut cmd = IC_DATA_CMD::CMD::Read;
if i == len - 1 {
cmd += IC_DATA_CMD::STOP::SET;
}
self.regs.ic_data_cmd.write(cmd);
self.wait_rx_fifo_not_empty()?;
*byte = self.regs.ic_data_cmd.read(IC_DATA_CMD::DAT) as u8;
}
self.check_errors()
}
pub fn write_read(
&self,
addr: u8,
write_data: &[u8],
read_buffer: &mut [u8],
) -> Result<(), I2cError> {
self.write_read_with_mode(
addr as u16,
I2cAddressMode::SevenBit,
write_data,
read_buffer,
)
}
pub fn write_read_with_mode(
&self,
addr: u16,
mode: I2cAddressMode,
write_data: &[u8],
read_buffer: &mut [u8],
) -> Result<(), I2cError> {
if write_data.is_empty() && read_buffer.is_empty() {
return Ok(());
}
self.set_target_address(addr, mode);
self.enable();
for &byte in write_data {
self.wait_tx_fifo_not_full()?;
self.regs
.ic_data_cmd
.write(IC_DATA_CMD::DAT.val(byte as u32) + IC_DATA_CMD::CMD::Write);
}
let read_len = read_buffer.len();
for (i, byte) in read_buffer.iter_mut().enumerate() {
self.wait_tx_fifo_not_full()?;
let mut cmd = IC_DATA_CMD::CMD::Read;
if i == 0 && !write_data.is_empty() {
cmd += IC_DATA_CMD::RESTART::SET;
}
if i == read_len - 1 {
cmd += IC_DATA_CMD::STOP::SET;
}
log::info!("read command: {i}");
self.regs.ic_data_cmd.write(cmd);
self.wait_rx_fifo_not_empty()?;
*byte = self.regs.ic_data_cmd.read(IC_DATA_CMD::DAT) as u8;
log::info!("byte: {:#x} next: {:#x}", *byte, self.regs.ic_data_cmd.read(IC_DATA_CMD::DAT));
}
self.check_errors()
}
pub fn tx_fifo_level(&self) -> u8 {
self.regs.ic_txflr.read(IC_TXFLR::TXFLR) as u8
}
pub fn rx_fifo_level(&self) -> u8 {
self.regs.ic_rxflr.read(IC_RXFLR::RXFLR) as u8
}
pub fn raw_interrupt_status(&self) -> u32 {
self.regs.ic_raw_intr_stat.get()
}
pub fn clear_all_interrupts(&self) {
let _ = self.regs.ic_clr_intr.get();
}
pub fn enable_tx_dma(&self) {
self.regs.ic_dma_cr.modify(IC_DMA_CR::TDMAE::SET);
}
pub fn enable_rx_dma(&self) {
self.regs.ic_dma_cr.modify(IC_DMA_CR::RDMAE::SET);
}
pub fn disable_dma(&self) {
self.regs
.ic_dma_cr
.modify(IC_DMA_CR::TDMAE::CLEAR + IC_DMA_CR::RDMAE::CLEAR);
}
pub fn set_dma_tx_level(&self, level: u8) {
self.regs
.ic_dma_tdlr
.write(IC_DMA_TDLR::DMATDL.val(level as u32));
}
pub fn set_dma_rx_level(&self, level: u8) {
self.regs
.ic_dma_rdlr
.write(IC_DMA_RDLR::DMARDL.val(level as u32));
}
}