rp i2c: clean up tx_abrt handling

Make sure we always wait for the stop bit if there's a reason to -
either because we sent one, or because there was a hardware tx abort.
This commit is contained in:
Jeremy Fitzhardinge 2022-10-03 01:00:03 -07:00
parent e8bb8faa23
commit cae8499179

View File

@ -135,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let mut remaining = buffer.len(); let mut remaining = buffer.len();
let mut remaining_queue = buffer.len(); let mut remaining_queue = buffer.len();
let mut abort_reason = None; let mut abort_reason = Ok(());
while remaining > 0 { while remaining > 0 {
// Waggle SCK - basically the same as write // Waggle SCK - basically the same as write
@ -190,8 +190,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
match res { match res {
Err(reason) => { Err(reason) => {
abort_reason = Some(reason); abort_reason = Err(reason);
// XXX keep going anyway?
break; break;
} }
Ok(rxfifo) => { Ok(rxfifo) => {
@ -207,29 +206,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
}; };
} }
// wait for stop condition to be emitted. self.wait_stop_det(abort_reason, send_stop).await
self.wait_on(
|_me| unsafe {
if !p.ic_raw_intr_stat().read().stop_det() && send_stop {
Poll::Pending
} else {
Poll::Ready(())
}
},
|_me| unsafe {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
unsafe { p.ic_clr_stop_det().read() };
if let Some(abort_reason) = abort_reason {
return Err(abort_reason);
}
Ok(())
} }
async fn write_async_internal( async fn write_async_internal(
@ -241,7 +218,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let mut bytes = bytes.into_iter().peekable(); let mut bytes = bytes.into_iter().peekable();
'xmit: loop { let res = 'xmit: loop {
let tx_fifo_space = Self::tx_fifo_capacity(); let tx_fifo_space = Self::tx_fifo_capacity();
for _ in 0..tx_fifo_space { for _ in 0..tx_fifo_space {
@ -256,49 +233,76 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
}); });
} }
} else { } else {
break 'xmit; break 'xmit Ok(());
} }
} }
self.wait_on( let res = self
|me| { .wait_on(
if let Err(abort_reason) = me.read_and_clear_abort_reason() { |me| {
Poll::Ready(Err(abort_reason)) if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {
} else if !Self::tx_fifo_full() { Poll::Ready(abort_reason)
Poll::Ready(Ok(())) } else if !Self::tx_fifo_full() {
} else { Poll::Ready(Ok(()))
Poll::Pending } else {
} Poll::Pending
}, }
|_me| unsafe { },
p.ic_intr_mask().modify(|w| { |_me| unsafe {
w.set_m_tx_empty(true); p.ic_intr_mask().modify(|w| {
w.set_m_tx_abrt(true); w.set_m_tx_empty(true);
}) w.set_m_tx_abrt(true);
}, })
) },
.await?; )
.await;
if res.is_err() {
break res;
}
};
self.wait_stop_det(res, send_stop).await
}
/// Helper to wait for a stop bit, for both tx and rx. If we had an abort,
/// then we'll get a hardware-generated stop, otherwise wait for a stop if
/// we're expecting it.
///
/// Also handles an abort which arises while processing the tx fifo.
async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {
if had_abort.is_err() || do_stop {
let p = T::regs();
let had_abort2 = self
.wait_on(
|me| unsafe {
// We could see an abort while processing fifo backlog,
// so handle it here.
let abort = me.read_and_clear_abort_reason();
if had_abort.is_ok() && abort.is_err() {
Poll::Ready(abort)
} else if p.ic_raw_intr_stat().read().stop_det() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
},
|_me| unsafe {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
unsafe {
p.ic_clr_stop_det().read();
}
had_abort.and(had_abort2)
} else {
had_abort
} }
// wait for fifo to drain
self.wait_on(
|_me| unsafe {
if p.ic_raw_intr_stat().read().tx_empty() {
Poll::Ready(())
} else {
Poll::Pending
}
},
|_me| unsafe {
p.ic_intr_mask().modify(|w| {
w.set_m_tx_empty(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
Ok(())
} }
pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {