Skip to content

Commit

Permalink
Refactor polling for ready during i2c master start and implementation…
Browse files Browse the repository at this point in the history
… of OnDrop for 10 bit addressing errors
  • Loading branch information
akshat2112 committed Feb 27, 2025
1 parent ebec4ff commit 345a703
Showing 1 changed file with 59 additions and 117 deletions.
176 changes: 59 additions & 117 deletions src/i2c/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,45 +397,7 @@ impl<'a> I2cMaster<'a, Async> {

i2cregs.mstctl().write(|w| w.mststart().set_bit());

let res = self
.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
if is_read && stat.mststate().is_receive_ready()
|| !is_read && stat.mststate().is_transmit_ready()
{
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mststate().is_nack_address() {
Poll::Ready(Err(TransferError::AddressNack.into()))
} else if is_read && !stat.mststate().is_receive_ready() {
Poll::Ready(Err(TransferError::ReadFail.into()))
} else if !is_read && !stat.mststate().is_transmit_ready() {
Poll::Ready(Err(TransferError::WriteFail.into()))
} else {
Poll::<Result<()>>::Pending
}
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::<Result<()>>::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await;
let res = self.poll_for_ready(is_read).await;

// Defuse the sentinel if future is not dropped
on_drop.defuse();
Expand Down Expand Up @@ -477,105 +439,44 @@ impl<'a> I2cMaster<'a, Async> {
)
.await?;

// Sentinel to perform corrective action if future is dropped
let on_drop = OnDrop::new(|| {
// Disable and re-enable master mode to clear out stalled HW state
// if we failed to complete sending of the address
// In practice, this seems to be only way to recover. Engaging with
// NXP to see if there is better way to handle this.
i2cregs.cfg().write(|w| w.msten().disabled());
i2cregs.cfg().write(|w| w.msten().enabled());
});

// The first byte of a 10-bit address is 11110XXX,
// where XXX are the 2 most significant bits of the 10-bit address
// followed by the read/write bit
let addr_high = TEN_BIT_PREFIX | (((address >> 8) as u8) << 1);
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_high) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await?;
// 10-bit address mode requires the two address bytes to be sent as a write operation
self.poll_for_ready(false).await?;

// Send the second part of the 10-bit address
let addr_low = (address & 0xFF) as u8;
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_low) });
i2cregs.mstctl().write(|w| w.mstcontinue().set_bit());

self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await?;
// 10-bit address mode requires the two address bytes to be sent as a write operation
self.poll_for_ready(false).await?;

// If this is a read operation, send a repeated start with the read bit set
if is_read {
let addr_high_read = addr_high | 0b1;
i2cregs.mstdat().write(|w| unsafe { w.data().bits(addr_high_read) });
i2cregs.mstctl().write(|w| w.mststart().set_bit());

self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await?;
self.poll_for_ready(is_read).await?;
}
// Defuse the sentinel if future is not dropped
on_drop.defuse();
Ok(())
}

Expand Down Expand Up @@ -809,6 +710,47 @@ impl<'a> I2cMaster<'a, Async> {
})
.await
}

/// During i2c start, poll for ready state and check for errors
async fn poll_for_ready(&mut self, is_read: bool) -> Result<()> {
self.wait_on(
|me| {
let stat = me.info.regs.stat().read();

if stat.mstpending().is_pending() {
if is_read && stat.mststate().is_receive_ready() || !is_read && stat.mststate().is_transmit_ready()
{
Poll::Ready(Ok::<(), Error>(()))
} else if stat.mststate().is_nack_address() {
Poll::Ready(Err(TransferError::AddressNack.into()))
} else if is_read && !stat.mststate().is_receive_ready() {
Poll::Ready(Err(TransferError::ReadFail.into()))
} else if !is_read && !stat.mststate().is_transmit_ready() {
Poll::Ready(Err(TransferError::WriteFail.into()))
} else {
Poll::<Result<()>>::Pending
}
} else if stat.mstarbloss().is_arbitration_loss() {
Poll::Ready(Err(TransferError::ArbitrationLoss.into()))
} else if stat.mstststperr().is_error() {
Poll::Ready(Err(TransferError::StartStopError.into()))
} else {
Poll::<Result<()>>::Pending
}
},
|me| {
me.info.regs.intenset().write(|w| {
w.mstpendingen()
.set_bit()
.mstarblossen()
.set_bit()
.mstststperren()
.set_bit()
});
},
)
.await
}
}

/// Error Types for I2C communication
Expand Down

0 comments on commit 345a703

Please sign in to comment.