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:
parent
e8bb8faa23
commit
cae8499179
@ -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> {
|
||||||
|
Loading…
Reference in New Issue
Block a user