aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Kröger <[email protected]>2024-03-07 18:35:05 +0100
committerTimo Kröger <[email protected]>2024-03-12 08:14:42 +0100
commit36a99189210bf15c93198a4bf9a0d2ab732c8bf8 (patch)
tree1bcdf15955fc8423b397e460a9a59750479a0452
parent984d5bbc720dfe9acf22400d2462c0ffce52a7cb (diff)
[UCPD] Implement PD transmitter
-rw-r--r--embassy-stm32/src/ucpd.rs81
1 files changed, 80 insertions, 1 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs
index f3f225d0c..0a6f7df71 100644
--- a/embassy-stm32/src/ucpd.rs
+++ b/embassy-stm32/src/ucpd.rs
@@ -24,7 +24,7 @@ use embassy_sync::waitqueue::AtomicWaker;
24 24
25use crate::dma::{AnyChannel, Request, Transfer, TransferOptions}; 25use crate::dma::{AnyChannel, Request, Transfer, TransferOptions};
26use crate::interrupt; 26use crate::interrupt;
27use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk}; 27use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode};
28pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; 28pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState};
29use crate::rcc::RccPeripheral; 29use crate::rcc::RccPeripheral;
30 30
@@ -194,6 +194,9 @@ impl<'d, T: Instance> Ucpd<'d, T> {
194 w.set_phyrxen(true); 194 w.set_phyrxen(true);
195 }); 195 });
196 196
197 // TODO: Currently only SOP messages are supported.
198 r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
199
197 into_ref!(rx_dma, tx_dma); 200 into_ref!(rx_dma, tx_dma);
198 let rx_dma_req = rx_dma.request(); 201 let rx_dma_req = rx_dma.request();
199 let tx_dma_req = tx_dma.request(); 202 let tx_dma_req = tx_dma.request();
@@ -308,6 +311,13 @@ impl<'d, T: Instance> PdRx<'d, T> {
308 } 311 }
309} 312}
310 313
314/// Transmit Error.
315#[derive(Debug, Clone, Copy)]
316pub enum TxError {
317 /// Concurrent receive in progress or excessive noise on the line.
318 Discarded,
319}
320
311/// Power Delivery (PD) Transmitter. 321/// Power Delivery (PD) Transmitter.
312pub struct PdTx<'d, T: Instance> { 322pub struct PdTx<'d, T: Instance> {
313 _ucpd: &'d Ucpd<'d, T>, 323 _ucpd: &'d Ucpd<'d, T>,
@@ -315,6 +325,68 @@ pub struct PdTx<'d, T: Instance> {
315 dma_req: Request, 325 dma_req: Request,
316} 326}
317 327
328impl<'d, T: Instance> PdTx<'d, T> {
329 /// Transmits a PD message.
330 pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> {
331 let r = T::REGS;
332
333 // When a previous transmission was dropped before it had finished it
334 // might still be running because there is no way to abort an ongoing
335 // message transmission. Wait for it to finish but ignore errors.
336 if r.cr().read().txsend() {
337 let _ = self.wait_tx_done().await;
338 }
339
340 // Clear the TX interrupt flags.
341 T::REGS.icr().write(|w| {
342 w.set_txmsgdisccf(true);
343 w.set_txmsgsentcf(true);
344 });
345
346 // Start the DMA and let it do its thing in the background.
347 let _dma = unsafe {
348 Transfer::new_write(
349 &self.dma_ch,
350 self.dma_req,
351 buf,
352 r.txdr().as_ptr() as *mut u8,
353 TransferOptions::default(),
354 )
355 };
356
357 // Configure and start the transmission.
358 r.tx_payszr().write(|w| w.set_txpaysz(buf.len() as _));
359 r.cr().modify(|w| {
360 w.set_txmode(Txmode::PACKET);
361 w.set_txsend(true);
362 });
363
364 self.wait_tx_done().await
365 }
366
367 async fn wait_tx_done(&self) -> Result<(), TxError> {
368 poll_fn(|cx| {
369 let r = T::REGS;
370 if r.sr().read().txmsgdisc() {
371 Poll::Ready(Err(TxError::Discarded))
372 } else if r.sr().read().txmsgsent() {
373 Poll::Ready(Ok(()))
374 } else {
375 // Enable transmit interrupts.
376 T::waker().register(cx.waker());
377 r.imr().modify(|w| {
378 w.set_txmsgdiscie(true);
379 w.set_txmsgsentie(true);
380 });
381 Poll::Pending
382 }
383 })
384 .await
385 }
386
387 fn clear_tx_flags(&self) {}
388}
389
318/// Interrupt handler. 390/// Interrupt handler.
319pub struct InterruptHandler<T: Instance> { 391pub struct InterruptHandler<T: Instance> {
320 _phantom: PhantomData<T>, 392 _phantom: PhantomData<T>,
@@ -336,6 +408,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
336 r.imr().modify(|w| w.set_rxmsgendie(false)); 408 r.imr().modify(|w| w.set_rxmsgendie(false));
337 } 409 }
338 410
411 if sr.txmsgdisc() || sr.txmsgsent() {
412 r.imr().modify(|w| {
413 w.set_txmsgdiscie(false);
414 w.set_txmsgsentie(false);
415 });
416 }
417
339 // Wake the task to clear and re-enabled interrupts. 418 // Wake the task to clear and re-enabled interrupts.
340 T::waker().wake(); 419 T::waker().wake();
341 } 420 }