use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal::i2c::{ErrorKind, ErrorType, NoAcknowledgeSource, Operation, SevenBitAddress};
use pac::peripherals::lpi2c::{LPI2C, LPI2C_MTDR};
use crate::hal::timeout::Timeout;
use crate::pac;
mod cmd {
pub const TRANSMIT: u8 = 0b000;
pub const RECEIVE: u8 = 0b001;
pub const STOP: u8 = 0b010;
pub const START: u8 = 0b100;
}
const MAX_RX_RUN: usize = 256;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Nack,
ArbitrationLoss,
Fifo,
Timeout,
ZeroLengthTransfer,
}
impl embedded_hal::i2c::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::Nack => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
Error::ArbitrationLoss => ErrorKind::ArbitrationLoss,
Error::Fifo => ErrorKind::Overrun,
Error::Timeout | Error::ZeroLengthTransfer => ErrorKind::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ClockConfig {
prescale: u8,
clkhi: u8,
clklo: u8,
}
const fn clock_config(clock_hz: u32, frequency: u32) -> ClockConfig {
assert!(frequency > 0);
let mut prescale: u8 = 0;
loop {
let presc_clock = clock_hz >> prescale;
let period = presc_clock / frequency;
let half = period / 2;
if half >= 1 && half <= 64 {
let count = (half - 1) as u8;
return ClockConfig {
prescale,
clkhi: count,
clklo: count,
};
}
if prescale >= 7 {
return ClockConfig {
prescale: 7,
clkhi: 63,
clklo: 63,
};
}
prescale += 1;
}
}
pub struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
fn on_interrupt(&self, r: LPI2C) {
let msr = r.MSR().read();
let mier = r.MIER().read();
let fire = (mier.TDIE() && msr.TDF())
|| (mier.RDIE() && msr.RDF())
|| (mier.NDIE() && msr.NDF())
|| (mier.EPIE() && msr.EPF())
|| (mier.SDIE() && msr.SDF())
|| (mier.ALIE() && msr.ALF());
if fire {
r.MIER().write(|w| {
w.set_TDIE(false);
w.set_RDIE(false);
w.set_NDIE(false);
w.set_EPIE(false);
w.set_SDIE(false);
w.set_ALIE(false);
});
self.waker.wake();
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> LPI2C;
fn state() -> &'static State;
const LPCG: Option<u32>;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
}
macro_rules! impl_i2c {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::i2c::SealedInstance for crate::peripherals::$instance {
const LPCG: ::core::option::Option<u32> = crate::clock::lpcg(stringify!($instance));
#[inline]
fn regs() -> crate::pac::peripherals::lpi2c::LPI2C {
unsafe { crate::pac::peripherals::lpi2c::LPI2C::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::i2c::State {
static STATE: crate::i2c::State = crate::i2c::State::new();
&STATE
}
}
impl crate::i2c::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
}
};
}
pub(crate) use impl_i2c;
pub fn on_interrupt<T: Instance>() {
T::state().on_interrupt(T::regs());
}
pub struct I2c {
regs: LPI2C,
state: &'static State,
timeout: Timeout,
}
impl I2c {
pub fn new<T: Instance>(peri: Peri<'static, T>, clock_hz: u32, frequency: u32) -> Self {
let _ = peri;
crate::clock::ungate(T::LPCG);
let r = T::regs();
r.MCR().write(|w| w.set_RST(true));
r.MCR().write(|w| w.set_RST(false));
let cfg = clock_config(clock_hz, frequency);
r.MCFGR1().modify(|w| w.set_PRESCALE(cfg.prescale));
r.MCCR0().write(|w| {
w.set_CLKHI(cfg.clkhi);
w.set_CLKLO(cfg.clklo);
w.set_DATAVD(cfg.clkhi / 2);
w.set_SETHOLD(cfg.clkhi);
});
r.MFCR().write(|w| {
w.set_TXWATER(0);
w.set_RXWATER(0);
});
r.MCR().write(|w| w.set_MEN(true));
Self {
regs: r,
state: T::state(),
timeout: Timeout::DEFAULT,
}
}
pub fn set_timeout(&mut self, timeout: Timeout) {
self.timeout = timeout;
}
fn clear_status(&self) {
self.regs.MSR().write(|w| {
w.set_NDF(true);
w.set_ALF(true);
w.set_FEF(true);
w.set_EPF(true);
w.set_SDF(true);
});
}
fn check_error(&self) -> Result<(), Error> {
let msr = self.regs.MSR().read();
if msr.NDF() {
self.regs.MSR().write(|w| w.set_NDF(true));
return Err(Error::Nack);
}
if msr.ALF() {
self.regs.MSR().write(|w| w.set_ALF(true));
return Err(Error::ArbitrationLoss);
}
if msr.FEF() {
self.regs.MSR().write(|w| w.set_FEF(true));
return Err(Error::Fifo);
}
if msr.PLTF() {
self.regs.MSR().write(|w| w.set_PLTF(true));
return Err(Error::Timeout);
}
Ok(())
}
#[inline]
fn push(&self, command: u8, data: u8) {
self.regs
.MTDR()
.write_value(LPI2C_MTDR((u32::from(command) << 8) | u32::from(data)));
}
fn wait_tx_ready(&self) -> Result<(), Error> {
let mut deadline = self.timeout.start();
loop {
self.check_error()?;
if self.regs.MSR().read().TDF() {
return Ok(());
}
deadline.tick().map_err(|_| Error::Timeout)?;
}
}
fn wait_rx(&self) -> Result<u8, Error> {
let mut deadline = self.timeout.start();
loop {
self.check_error()?;
let rdr = self.regs.MRDR().read();
if !rdr.RXEMPTY() {
return Ok(rdr.DATA());
}
deadline.tick().map_err(|_| Error::Timeout)?;
}
}
fn read_bytes(&self, buffer: &mut [u8]) -> Result<(), Error> {
for chunk in buffer.chunks_mut(MAX_RX_RUN) {
self.wait_tx_ready()?;
self.push(cmd::RECEIVE, (chunk.len() - 1) as u8);
for slot in chunk.iter_mut() {
*slot = self.wait_rx()?;
}
}
Ok(())
}
fn stop(&self) -> Result<(), Error> {
self.wait_tx_ready()?;
self.push(cmd::STOP, 0);
let mut deadline = self.timeout.start();
loop {
self.check_error()?;
if self.regs.MSR().read().SDF() {
self.regs.MSR().write(|w| w.set_SDF(true));
return Ok(());
}
deadline.tick().map_err(|_| Error::Timeout)?;
}
}
fn transaction_blocking(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
if operations.is_empty() {
return Ok(());
}
check_operations(operations)?;
let guard = RecoverGuard::new(self.regs);
self.clear_status();
let mut start = 0;
while start < operations.len() {
let end = group_end(operations, start);
let read = is_read(&operations[start]);
self.wait_tx_ready()?;
self.push(cmd::START, (address << 1) | u8::from(read));
for op in &mut operations[start..end] {
match op {
Operation::Write(bytes) => {
for &b in bytes.iter() {
self.wait_tx_ready()?;
self.push(cmd::TRANSMIT, b);
}
}
Operation::Read(buffer) => self.read_bytes(buffer)?,
}
}
start = end;
}
self.stop()?;
guard.defuse();
Ok(())
}
async fn wait_for<F>(&self, arm: fn(&LPI2C), mut ready: F) -> Result<(), Error>
where
F: FnMut(&Self) -> Option<Result<(), Error>>,
{
poll_fn(|cx| {
if let Some(res) = ready(self) {
return Poll::Ready(res);
}
self.state.waker.register(cx.waker());
critical_section::with(|_| arm(&self.regs));
if let Some(res) = ready(self) {
return Poll::Ready(res);
}
Poll::Pending
})
.await
}
async fn wait_tx_ready_async(&self) -> Result<(), Error> {
self.wait_for(
|r| {
r.MIER().modify(|w| {
w.set_TDIE(true);
w.set_NDIE(true);
w.set_ALIE(true);
});
},
|s| match s.check_error() {
Err(e) => Some(Err(e)),
Ok(()) if s.regs.MSR().read().TDF() => Some(Ok(())),
Ok(()) => None,
},
)
.await
}
async fn wait_rx_async(&self) -> Result<u8, Error> {
let mut byte = 0u8;
self.wait_for(
|r| {
r.MIER().modify(|w| {
w.set_RDIE(true);
w.set_NDIE(true);
w.set_ALIE(true);
});
},
|s| match s.check_error() {
Err(e) => Some(Err(e)),
Ok(()) => {
let rdr = s.regs.MRDR().read();
if rdr.RXEMPTY() {
None
} else {
byte = rdr.DATA();
Some(Ok(()))
}
}
},
)
.await?;
Ok(byte)
}
async fn stop_async(&self) -> Result<(), Error> {
self.wait_tx_ready_async().await?;
self.push(cmd::STOP, 0);
self.wait_for(
|r| {
r.MIER().modify(|w| {
w.set_SDIE(true);
w.set_NDIE(true);
w.set_ALIE(true);
});
},
|s| match s.check_error() {
Err(e) => Some(Err(e)),
Ok(()) if s.regs.MSR().read().SDF() => {
s.regs.MSR().write(|w| w.set_SDF(true));
Some(Ok(()))
}
Ok(()) => None,
},
)
.await
}
async fn transaction_async(
&mut self,
address: u8,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
if operations.is_empty() {
return Ok(());
}
check_operations(operations)?;
let guard = RecoverGuard::new(self.regs);
self.clear_status();
let mut start = 0;
while start < operations.len() {
let end = group_end(operations, start);
let read = is_read(&operations[start]);
self.wait_tx_ready_async().await?;
self.push(cmd::START, (address << 1) | u8::from(read));
for op in &mut operations[start..end] {
match op {
Operation::Write(bytes) => {
for &b in bytes.iter() {
self.wait_tx_ready_async().await?;
self.push(cmd::TRANSMIT, b);
}
}
Operation::Read(buffer) => {
for chunk in buffer.chunks_mut(MAX_RX_RUN) {
self.wait_tx_ready_async().await?;
self.push(cmd::RECEIVE, (chunk.len() - 1) as u8);
for slot in chunk.iter_mut() {
*slot = self.wait_rx_async().await?;
}
}
}
}
}
start = end;
}
self.stop_async().await?;
guard.defuse();
Ok(())
}
}
fn check_operations(operations: &[Operation<'_>]) -> Result<(), Error> {
if operations
.iter()
.any(|op| matches!(op, Operation::Read(buffer) if buffer.is_empty()))
{
return Err(Error::ZeroLengthTransfer);
}
Ok(())
}
fn is_read(op: &Operation<'_>) -> bool {
matches!(op, Operation::Read(_))
}
fn group_end(operations: &[Operation<'_>], start: usize) -> usize {
let read = is_read(&operations[start]);
let mut end = start + 1;
while end < operations.len() && is_read(&operations[end]) == read {
end += 1;
}
end
}
const RECOVERY_SPINS: u32 = 200_000;
fn recover(r: LPI2C) {
critical_section::with(|_| {
r.MIER().write(|w| {
w.set_TDIE(false);
w.set_RDIE(false);
w.set_NDIE(false);
w.set_EPIE(false);
w.set_SDIE(false);
w.set_ALIE(false);
})
});
let reset_fifos = || {
r.MCR().modify(|w| {
w.set_RTF(true);
w.set_RRF(true);
})
};
let clear_flags = || {
r.MSR().write(|w| {
w.set_EPF(true);
w.set_SDF(true);
w.set_NDF(true);
w.set_ALF(true);
w.set_FEF(true);
w.set_PLTF(true);
w.set_DMF(true);
})
};
reset_fifos();
clear_flags();
if r.MSR().read().MBF() {
r.MTDR().write_value(LPI2C_MTDR(u32::from(cmd::STOP) << 8));
let mut spins = RECOVERY_SPINS;
while spins > 0 {
let msr = r.MSR().read();
if msr.SDF() || !msr.MBF() {
break;
}
spins -= 1;
}
}
reset_fifos();
clear_flags();
}
struct RecoverGuard {
regs: LPI2C,
armed: bool,
}
impl RecoverGuard {
fn new(regs: LPI2C) -> Self {
Self { regs, armed: true }
}
fn defuse(mut self) {
self.armed = false;
}
}
impl Drop for RecoverGuard {
fn drop(&mut self) {
if self.armed {
recover(self.regs);
}
}
}
impl ErrorType for I2c {
type Error = Error;
}
impl embedded_hal::i2c::I2c<SevenBitAddress> for I2c {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
self.transaction_blocking(address, operations)
}
}
impl embedded_hal_async::i2c::I2c<SevenBitAddress> for I2c {
async fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Error> {
self.transaction_async(address, operations).await
}
}
#[cfg(test)]
mod tests {
use embedded_hal::i2c::Operation;
use super::{Error, check_operations, clock_config, group_end};
#[test]
fn clock_config_standard_mode() {
let cfg = clock_config(24_000_000, 100_000);
assert_eq!(cfg.prescale, 1);
assert_eq!(cfg.clkhi, cfg.clklo);
assert!(cfg.clkhi <= 63);
}
#[test]
fn clock_config_fast_mode_fits() {
let cfg = clock_config(80_000_000, 400_000);
assert!(cfg.clkhi <= 63 && cfg.clklo <= 63);
}
#[test]
fn clock_config_clamps_slow_clock() {
let cfg = clock_config(1_000_000, 10);
assert_eq!(cfg.prescale, 7);
}
#[test]
fn same_direction_operations_form_one_segment() {
let (a, b) = ([1u8], [2u8]);
let (mut r1, mut r2) = ([0u8; 1], [0u8; 1]);
let ops = [
Operation::Read(&mut r1),
Operation::Read(&mut r2),
Operation::Write(&a),
Operation::Write(&b),
];
assert_eq!(group_end(&ops, 0), 2);
assert_eq!(group_end(&ops, 2), 4);
}
#[test]
fn zero_length_read_is_rejected() {
let mut empty = [0u8; 0];
let mut one = [0u8; 1];
assert_eq!(
check_operations(&[Operation::Write(&[1]), Operation::Read(&mut empty)]),
Err(Error::ZeroLengthTransfer)
);
assert_eq!(
check_operations(&[Operation::Write(&[]), Operation::Read(&mut one)]),
Ok(())
);
}
}