use crate::gpio::Pull;
use crate::mailbox::Mailbox;
use crate::pac::{BSC0, BSC1, GPIO};
use crate::timer::Timer;
use embedded_hal::i2c::Operation;
const TIMEOUT_BASE_US: u64 = 5_000;
const TIMEOUT_PER_BYTE_US: u64 = 1_000;
const CONTROL_TIMEOUT_US: u64 = 10_000;
const IC_CLK_HZ: u64 = 200_000_000;
const MAX_FREQUENCY_HZ: u32 = 400_000;
const IC_CON: usize = 0x00;
const IC_TAR: usize = 0x04;
const IC_DATA_CMD: usize = 0x10;
const IC_SS_SCL_HCNT: usize = 0x14;
const IC_SS_SCL_LCNT: usize = 0x18;
const IC_FS_SCL_HCNT: usize = 0x1c;
const IC_FS_SCL_LCNT: usize = 0x20;
const IC_INTR_MASK: usize = 0x30;
const IC_RAW_INTR_STAT: usize = 0x34;
const IC_CLR_INTR: usize = 0x40;
const IC_CLR_TX_ABRT: usize = 0x54;
const IC_CLR_STOP_DET: usize = 0x60;
const IC_ENABLE: usize = 0x6c;
const IC_STATUS: usize = 0x70;
const IC_TXFLR: usize = 0x74;
const IC_TX_ABRT_SOURCE: usize = 0x80;
const IC_ENABLE_STATUS: usize = 0x9c;
const IC_COMP_PARAM_1: usize = 0xf4;
const CON_MASTER: u32 = 1 << 0;
const CON_SPEED_STD: u32 = 1 << 1;
const CON_SPEED_FAST: u32 = 2 << 1;
const CON_RESTART_EN: u32 = 1 << 5;
const CON_SLAVE_DISABLE: u32 = 1 << 6;
const CMD_READ: u32 = 1 << 8;
const CMD_STOP: u32 = 1 << 9;
const CMD_RESTART: u32 = 1 << 10;
const ENABLE: u32 = 1 << 0;
const ENABLE_ABORT: u32 = 1 << 1;
const STATUS_TFE: u32 = 1 << 2;
const STATUS_RFNE: u32 = 1 << 3;
const STATUS_MASTER_ACTIVITY: u32 = 1 << 5;
const INTR_TX_ABRT: u32 = 1 << 6;
const INTR_STOP_DET: u32 = 1 << 9;
const ABRT_NOACK: u32 = 0b1_1111;
const FUNCSEL_I2C: u32 = 3;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
NoAcknowledge,
ZeroLengthUnsupported,
Timeout,
Incomplete {
received: usize,
requested: usize,
},
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
match self {
Self::NoAcknowledge => embedded_hal::i2c::ErrorKind::NoAcknowledge(
embedded_hal::i2c::NoAcknowledgeSource::Unknown,
),
Self::ZeroLengthUnsupported | Self::Timeout | Self::Incomplete { .. } => {
embedded_hal::i2c::ErrorKind::Other
}
}
}
}
pub struct I2c<'a, I = BSC1> {
_bsc: I,
base: usize,
timer: &'a Timer,
tx_depth: u32,
rx_depth: u32,
}
impl<'a> I2c<'a, BSC1> {
pub fn init_with_frequency(
gpio: &GPIO,
bsc1: BSC1,
frequency_hz: u32,
mailbox: &mut Mailbox,
timer: &'a Timer,
) -> Result<Self, crate::mailbox::Error> {
let _ = (gpio, mailbox);
route_pins([2, 3]);
Ok(Self::configure(bsc1, BSC1::ADDR, frequency_hz, timer))
}
}
impl<'a> I2c<'a, BSC0> {
pub fn init_id_with_frequency(
gpio: &GPIO,
bsc0: BSC0,
frequency_hz: u32,
mailbox: &mut Mailbox,
timer: &'a Timer,
) -> Result<Self, crate::mailbox::Error> {
let _ = (gpio, mailbox);
route_pins([0, 1]);
Ok(Self::configure(bsc0, BSC0::ADDR, frequency_hz, timer))
}
}
fn route_pins(pins: [u8; 2]) {
for pin in pins {
crate::gpio::set_pull(pin, Pull::Up);
crate::gpio::set_drive_12ma(pin);
crate::gpio::select_function(pin, FUNCSEL_I2C);
}
}
fn scl_counts(frequency_hz: u32) -> (u32, u32, u32) {
let frequency_hz = u64::from(frequency_hz.clamp(1, MAX_FREQUENCY_HZ));
let (speed, high_ns, low_ns) = if frequency_hz > 100_000 {
(CON_SPEED_FAST, 600, 1300)
} else {
(CON_SPEED_STD, 4000, 4700)
};
let period = IC_CLK_HZ.div_ceil(frequency_hz);
let min_high = (IC_CLK_HZ * high_ns).div_ceil(1_000_000_000);
let min_low = (IC_CLK_HZ * low_ns).div_ceil(1_000_000_000);
let high = (period * high_ns / (high_ns + low_ns)).max(min_high);
let low = period.saturating_sub(high).max(min_low);
let hcnt = (high.saturating_sub(8)).clamp(6, 0xffff) as u32;
let lcnt = (low.saturating_sub(1)).clamp(8, 0xffff) as u32;
(speed, hcnt, lcnt)
}
impl<'a, I> I2c<'a, I> {
fn configure(bsc: I, base: usize, frequency_hz: u32, timer: &'a Timer) -> Self {
let mut i2c = Self {
_bsc: bsc,
base,
timer,
tx_depth: 1,
rx_depth: 1,
};
i2c.disable();
let (speed, hcnt, lcnt) = scl_counts(frequency_hz);
let (hcnt_reg, lcnt_reg) = if speed == CON_SPEED_FAST {
(IC_FS_SCL_HCNT, IC_FS_SCL_LCNT)
} else {
(IC_SS_SCL_HCNT, IC_SS_SCL_LCNT)
};
i2c.write(
IC_CON,
CON_MASTER | speed | CON_RESTART_EN | CON_SLAVE_DISABLE,
);
i2c.write(hcnt_reg, hcnt);
i2c.write(lcnt_reg, lcnt);
i2c.write(IC_INTR_MASK, 0);
let params = i2c.read(IC_COMP_PARAM_1);
i2c.tx_depth = ((params >> 16) & 0xff) + 1;
i2c.rx_depth = ((params >> 8) & 0xff) + 1;
i2c
}
fn read(&self, offset: usize) -> u32 {
unsafe { ((self.base + offset) as *const u32).read_volatile() }
}
fn write(&self, offset: usize, value: u32) {
unsafe { ((self.base + offset) as *mut u32).write_volatile(value) };
}
fn disable(&mut self) -> bool {
self.write(IC_ENABLE, 0);
let deadline = self.timer.now_micros() + CONTROL_TIMEOUT_US;
while self.read(IC_ENABLE_STATUS) & 1 != 0 {
if self.timer.now_micros() > deadline {
return false;
}
}
true
}
fn abandon(&mut self) {
self.write(IC_ENABLE, ENABLE | ENABLE_ABORT);
let deadline = self.timer.now_micros() + CONTROL_TIMEOUT_US;
while self.read(IC_ENABLE) & ENABLE_ABORT != 0 && self.timer.now_micros() <= deadline {}
let _ = self.read(IC_CLR_INTR);
self.disable();
}
fn take_abort(&mut self) -> Option<Error> {
if self.read(IC_RAW_INTR_STAT) & INTR_TX_ABRT == 0 {
return None;
}
let source = self.read(IC_TX_ABRT_SOURCE);
let _ = self.read(IC_CLR_TX_ABRT);
self.disable();
Some(if source & ABRT_NOACK != 0 {
Error::NoAcknowledge
} else {
Error::Timeout
})
}
fn run(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
let mut total = 0;
for operation in operations.iter() {
let len = match operation {
Operation::Read(buffer) => buffer.len(),
Operation::Write(bytes) => bytes.len(),
};
if len == 0 {
return Err(Error::ZeroLengthUnsupported);
}
total += len;
}
let count = operations.len();
if count == 0 {
return Ok(());
}
if !self.disable() {
return Err(Error::Timeout);
}
self.write(IC_TAR, u32::from(address & 0x7f));
let _ = self.read(IC_CLR_INTR);
self.write(IC_ENABLE, ENABLE);
let deadline =
self.timer.now_micros() + TIMEOUT_BASE_US + TIMEOUT_PER_BYTE_US * total as u64;
let (mut push_op, mut push_byte) = (0, 0);
let (mut drain_op, mut drain_byte) = (0, 0);
let mut outstanding = 0;
loop {
if let Some(error) = self.take_abort() {
return Err(error);
}
loop {
while drain_op < count {
match &operations[drain_op] {
Operation::Read(buffer) if drain_byte < buffer.len() => break,
_ => {
drain_op += 1;
drain_byte = 0;
}
}
}
if drain_op == count || self.read(IC_STATUS) & STATUS_RFNE == 0 {
break;
}
let byte = self.read(IC_DATA_CMD) as u8;
if let Operation::Read(buffer) = &mut operations[drain_op] {
buffer[drain_byte] = byte;
}
drain_byte += 1;
outstanding -= 1;
}
if push_op < count && self.read(IC_TXFLR) < self.tx_depth {
let (len, is_read) = match &operations[push_op] {
Operation::Read(buffer) => (buffer.len(), true),
Operation::Write(bytes) => (bytes.len(), false),
};
if !is_read || outstanding < self.rx_depth {
let mut command = 0;
if push_op > 0 && push_byte == 0 {
command |= CMD_RESTART;
}
if push_op == count - 1 && push_byte == len - 1 {
command |= CMD_STOP;
}
match &operations[push_op] {
Operation::Read(_) => {
self.write(IC_DATA_CMD, command | CMD_READ);
outstanding += 1;
}
Operation::Write(bytes) => {
self.write(IC_DATA_CMD, command | u32::from(bytes[push_byte]));
}
}
push_byte += 1;
if push_byte == len {
push_op += 1;
push_byte = 0;
}
}
}
if push_op == count && drain_op == count {
let status = self.read(IC_STATUS);
let idle = status & STATUS_TFE != 0 && status & STATUS_MASTER_ACTIVITY == 0;
if idle && self.read(IC_RAW_INTR_STAT) & INTR_STOP_DET != 0 {
let _ = self.read(IC_CLR_STOP_DET);
if let Some(error) = self.take_abort() {
return Err(error);
}
return Ok(());
}
}
if self.timer.now_micros() > deadline {
let idle = self.read(IC_STATUS) & STATUS_MASTER_ACTIVITY == 0;
self.abandon();
return Err(match &operations.get(drain_op) {
Some(Operation::Read(buffer)) if idle => Error::Incomplete {
received: drain_byte,
requested: buffer.len(),
},
_ => Error::Timeout,
});
}
}
}
}
impl<I> embedded_hal::i2c::ErrorType for I2c<'_, I> {
type Error = Error;
}
impl<I> embedded_hal::i2c::I2c for I2c<'_, I> {
fn transaction(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
self.run(address, operations)
}
}