aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimo Kröger <[email protected]>2024-03-08 20:36:37 +0100
committerTimo Kröger <[email protected]>2024-03-12 08:14:42 +0100
commit89504f51629d0ab81070db91c3eb5b96b1e41fcb (patch)
tree0656fb3800905b20f85320306569b965f72ad6cc
parent99854ff840fdd12d7562923f6a9ef2d39e1c9f17 (diff)
[UCPD] Split into CC and PD phy
PD3.0 spec requires concurrent control of CC resistors for collision avoidance. Needed to introduce some "ref counting" (its just a bool) for drop code.
-rw-r--r--embassy-stm32/src/ucpd.rs211
-rw-r--r--examples/stm32g4/src/bin/usb_c_pd.rs17
2 files changed, 144 insertions, 84 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs
index d1b793e26..2120716ef 100644
--- a/embassy-stm32/src/ucpd.rs
+++ b/embassy-stm32/src/ucpd.rs
@@ -16,11 +16,11 @@
16 16
17use core::future::poll_fn; 17use core::future::poll_fn;
18use core::marker::PhantomData; 18use core::marker::PhantomData;
19use core::sync::atomic::Ordering;
19use core::task::Poll; 20use core::task::Poll;
20 21
21use embassy_hal_internal::drop::OnDrop; 22use embassy_hal_internal::drop::OnDrop;
22use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; 23use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
23use 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;
@@ -53,22 +53,15 @@ pub enum CcPull {
53 53
54/// UCPD driver. 54/// UCPD driver.
55pub struct Ucpd<'d, T: Instance> { 55pub struct Ucpd<'d, T: Instance> {
56 _peri: PeripheralRef<'d, T>, 56 cc_phy: CcPhy<'d, T>,
57}
58
59impl<'d, T: Instance> Drop for Ucpd<'d, T> {
60 fn drop(&mut self) {
61 T::REGS.cfgr1().write(|w| w.set_ucpden(false));
62 }
63} 57}
64 58
65impl<'d, T: Instance> Ucpd<'d, T> { 59impl<'d, T: Instance> Ucpd<'d, T> {
66 /// Creates a new UCPD driver instance. 60 /// Creates a new UCPD driver instance.
67 pub fn new( 61 pub fn new(
68 peri: impl Peripheral<P = T> + 'd, 62 _peri: impl Peripheral<P = T> + 'd,
69 _cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd, 63 _cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd,
70 _cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd, 64 _cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd,
71 cc_pull: CcPull,
72 ) -> Self { 65 ) -> Self {
73 T::enable_and_reset(); 66 T::enable_and_reset();
74 67
@@ -99,7 +92,94 @@ impl<'d, T: Instance> Ucpd<'d, T> {
99 w.set_ucpden(true); 92 w.set_ucpden(true);
100 }); 93 });
101 94
102 r.cr().write(|w| { 95 Self {
96 cc_phy: CcPhy { _lifetime: PhantomData },
97 }
98 }
99
100 /// Returns the TypeC CC PHY.
101 pub fn cc_phy(&mut self) -> &mut CcPhy<'d, T> {
102 &mut self.cc_phy
103 }
104
105 /// Splits the UCPD driver into a TypeC PHY to control and monitor CC voltage
106 /// and a Power Delivery (PD) PHY with receiver and transmitter.
107 pub fn split_pd_phy(
108 self,
109 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
110 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
111 cc_sel: CcSel,
112 ) -> (CcPhy<'d, T>, PdPhy<'d, T>) {
113 let r = T::REGS;
114
115 // TODO: Currently only SOP messages are supported.
116 r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
117
118 r.cfgr1().modify(|w| {
119 // TODO: Currently only hard reset and SOP messages can be received.
120 w.set_rxordseten(0b1001);
121
122 // Enable DMA
123 w.set_txdmaen(true);
124 w.set_rxdmaen(true);
125 });
126
127 // Enable the receiver on one of the two CC lines.
128 r.cr().modify(|w| {
129 w.set_phyccsel(cc_sel);
130 w.set_phyrxen(true);
131 });
132
133 // Enable hard reset receive interrupt.
134 r.imr().modify(|w| w.set_rxhrstdetie(true));
135
136 // Both parts must be dropped before the peripheral can be disabled.
137 T::state().drop_not_ready.store(true, Ordering::Relaxed);
138
139 into_ref!(rx_dma, tx_dma);
140 let rx_dma_req = rx_dma.request();
141 let tx_dma_req = tx_dma.request();
142 (
143 self.cc_phy,
144 PdPhy {
145 _lifetime: PhantomData,
146 rx_dma_ch: rx_dma.map_into(),
147 rx_dma_req,
148 tx_dma_ch: tx_dma.map_into(),
149 tx_dma_req,
150 },
151 )
152 }
153}
154
155/// Control and monitoring of TypeC CC pin functionailty.
156pub struct CcPhy<'d, T: Instance> {
157 _lifetime: PhantomData<&'d mut T>,
158}
159
160impl<'d, T: Instance> Drop for CcPhy<'d, T> {
161 fn drop(&mut self) {
162 let r = T::REGS;
163 r.cr().modify(|w| {
164 w.set_cc1tcdis(true);
165 w.set_cc2tcdis(true);
166 w.set_ccenable(Ccenable::DISABLED);
167 });
168
169 // Check if the PdPhy part was dropped already.
170 let drop_not_ready = &T::state().drop_not_ready;
171 if drop_not_ready.load(Ordering::Relaxed) {
172 drop_not_ready.store(true, Ordering::Relaxed);
173 } else {
174 r.cfgr1().write(|w| w.set_ucpden(false));
175 }
176 }
177}
178
179impl<'d, T: Instance> CcPhy<'d, T> {
180 /// Sets the pull-up/pull-down resistor values exposed on the CC pins.
181 pub fn set_pull(&mut self, cc_pull: CcPull) {
182 T::REGS.cr().write(|w| {
103 w.set_anamode(if cc_pull == CcPull::Sink { 183 w.set_anamode(if cc_pull == CcPull::Sink {
104 Anamode::SINK 184 Anamode::SINK
105 } else { 185 } else {
@@ -116,10 +196,6 @@ impl<'d, T: Instance> Ucpd<'d, T> {
116 } else { 196 } else {
117 Ccenable::DISABLED 197 Ccenable::DISABLED
118 }); 198 });
119
120 // Make sure detector is enabled on both pins.
121 w.set_cc1tcdis(false);
122 w.set_cc2tcdis(false);
123 }); 199 });
124 200
125 // Disable dead-battery pull-down resistors which are enabled by default on boot. 201 // Disable dead-battery pull-down resistors which are enabled by default on boot.
@@ -130,30 +206,27 @@ impl<'d, T: Instance> Ucpd<'d, T> {
130 .cr3() 206 .cr3()
131 .modify(|w| w.set_ucpd1_dbdis(cc_pull != CcPull::SinkDeadBattery)); 207 .modify(|w| w.set_ucpd1_dbdis(cc_pull != CcPull::SinkDeadBattery));
132 }); 208 });
133
134 into_ref!(peri);
135 Self { _peri: peri }
136 } 209 }
137 210
138 /// Returns the current voltage level of CC1 and CC2 pin as tuple. 211 /// Returns the current voltage level of CC1 and CC2 pin as tuple.
139 /// 212 ///
140 /// Interpretation of the voltage levels depends on the configured CC line 213 /// Interpretation of the voltage levels depends on the configured CC line
141 /// pull-up/pull-down resistance. 214 /// pull-up/pull-down resistance.
142 pub fn cc_vstate(&self) -> (CcVState, CcVState) { 215 pub fn vstate(&self) -> (CcVState, CcVState) {
143 let sr = T::REGS.sr().read(); 216 let sr = T::REGS.sr().read();
144 (sr.typec_vstate_cc1(), sr.typec_vstate_cc2()) 217 (sr.typec_vstate_cc1(), sr.typec_vstate_cc2())
145 } 218 }
146 219
147 /// Waits for a change in voltage state on either CC line. 220 /// Waits for a change in voltage state on either CC line.
148 pub async fn wait_for_cc_vstate_change(&self) -> (CcVState, CcVState) { 221 pub async fn wait_for_vstate_change(&self) -> (CcVState, CcVState) {
149 let _on_drop = OnDrop::new(|| self.enable_cc_interrupts(false)); 222 let _on_drop = OnDrop::new(|| self.enable_cc_interrupts(false));
150 let prev_vstate = self.cc_vstate(); 223 let prev_vstate = self.vstate();
151 poll_fn(|cx| { 224 poll_fn(|cx| {
152 let vstate = self.cc_vstate(); 225 let vstate = self.vstate();
153 if vstate != prev_vstate { 226 if vstate != prev_vstate {
154 Poll::Ready(vstate) 227 Poll::Ready(vstate)
155 } else { 228 } else {
156 T::waker().register(cx.waker()); 229 T::state().waker.register(cx.waker());
157 self.enable_cc_interrupts(true); 230 self.enable_cc_interrupts(true);
158 Poll::Pending 231 Poll::Pending
159 } 232 }
@@ -167,48 +240,6 @@ impl<'d, T: Instance> Ucpd<'d, T> {
167 w.set_typecevt2ie(enable); 240 w.set_typecevt2ie(enable);
168 }); 241 });
169 } 242 }
170
171 /// Returns PD receiver and transmitter.
172 pub fn pd_phy(
173 &mut self,
174 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
175 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
176 cc_sel: CcSel,
177 ) -> PdPhy<'_, T> {
178 let r = T::REGS;
179
180 // TODO: Currently only SOP messages are supported.
181 r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
182
183 r.cfgr1().modify(|w| {
184 // TODO: Currently only hard reset and SOP messages can be received.
185 w.set_rxordseten(0b1001);
186
187 // Enable DMA
188 w.set_txdmaen(true);
189 w.set_rxdmaen(true);
190 });
191
192 // Enable the receiver on one of the two CC lines.
193 r.cr().modify(|w| {
194 w.set_phyccsel(cc_sel);
195 w.set_phyrxen(true);
196 });
197
198 // Enable hard reset receive interrupt.
199 r.imr().modify(|w| w.set_rxhrstdetie(true));
200
201 into_ref!(rx_dma, tx_dma);
202 let rx_dma_req = rx_dma.request();
203 let tx_dma_req = tx_dma.request();
204 PdPhy {
205 _ucpd: self,
206 rx_dma_ch: rx_dma.map_into(),
207 rx_dma_req,
208 tx_dma_ch: tx_dma.map_into(),
209 tx_dma_req,
210 }
211 }
212} 243}
213 244
214/// Receive Error. 245/// Receive Error.
@@ -238,7 +269,7 @@ pub enum TxError {
238 269
239/// Power Delivery (PD) PHY. 270/// Power Delivery (PD) PHY.
240pub struct PdPhy<'d, T: Instance> { 271pub struct PdPhy<'d, T: Instance> {
241 _ucpd: &'d Ucpd<'d, T>, 272 _lifetime: PhantomData<&'d mut T>,
242 rx_dma_ch: PeripheralRef<'d, AnyChannel>, 273 rx_dma_ch: PeripheralRef<'d, AnyChannel>,
243 rx_dma_req: Request, 274 rx_dma_req: Request,
244 tx_dma_ch: PeripheralRef<'d, AnyChannel>, 275 tx_dma_ch: PeripheralRef<'d, AnyChannel>,
@@ -247,7 +278,16 @@ pub struct PdPhy<'d, T: Instance> {
247 278
248impl<'d, T: Instance> Drop for PdPhy<'d, T> { 279impl<'d, T: Instance> Drop for PdPhy<'d, T> {
249 fn drop(&mut self) { 280 fn drop(&mut self) {
250 T::REGS.cr().modify(|w| w.set_phyrxen(false)); 281 let r = T::REGS;
282 r.cr().modify(|w| w.set_phyrxen(false));
283
284 // Check if the Type-C part was dropped already.
285 let drop_not_ready = &T::state().drop_not_ready;
286 if drop_not_ready.load(Ordering::Relaxed) {
287 drop_not_ready.store(true, Ordering::Relaxed);
288 } else {
289 r.cfgr1().write(|w| w.set_ucpden(false));
290 }
251 } 291 }
252} 292}
253 293
@@ -316,7 +356,7 @@ impl<'d, T: Instance> PdPhy<'d, T> {
316 }); 356 });
317 Poll::Ready(ret) 357 Poll::Ready(ret)
318 } else { 358 } else {
319 T::waker().register(cx.waker()); 359 T::state().waker.register(cx.waker());
320 self.enable_rx_interrupt(true); 360 self.enable_rx_interrupt(true);
321 Poll::Pending 361 Poll::Pending
322 } 362 }
@@ -383,7 +423,7 @@ impl<'d, T: Instance> PdPhy<'d, T> {
383 } else if sr.txmsgsent() { 423 } else if sr.txmsgsent() {
384 Poll::Ready(Ok(())) 424 Poll::Ready(Ok(()))
385 } else { 425 } else {
386 T::waker().register(cx.waker()); 426 T::state().waker.register(cx.waker());
387 self.enable_tx_interrupts(true); 427 self.enable_tx_interrupts(true);
388 Poll::Pending 428 Poll::Pending
389 } 429 }
@@ -427,7 +467,7 @@ impl<'d, T: Instance> PdPhy<'d, T> {
427 } else if sr.hrstsent() { 467 } else if sr.hrstsent() {
428 Poll::Ready(Ok(())) 468 Poll::Ready(Ok(()))
429 } else { 469 } else {
430 T::waker().register(cx.waker()); 470 T::state().waker.register(cx.waker());
431 self.enable_hardreset_interrupts(true); 471 self.enable_hardreset_interrupts(true);
432 Poll::Pending 472 Poll::Pending
433 } 473 }
@@ -483,18 +523,37 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
483 } 523 }
484 524
485 // Wake the task to clear and re-enabled interrupts. 525 // Wake the task to clear and re-enabled interrupts.
486 T::waker().wake(); 526 T::state().waker.wake();
487 } 527 }
488} 528}
489 529
490/// UCPD instance trait. 530/// UCPD instance trait.
491pub trait Instance: sealed::Instance + RccPeripheral {} 531pub trait Instance: sealed::Instance + RccPeripheral {}
492 532
493pub(crate) mod sealed { 533mod sealed {
534 use core::sync::atomic::AtomicBool;
535
536 use embassy_sync::waitqueue::AtomicWaker;
537
538 pub struct State {
539 pub waker: AtomicWaker,
540 // Inverted logic for a default state of 0 so that the data goes into the .bss section.
541 pub drop_not_ready: AtomicBool,
542 }
543
544 impl State {
545 pub const fn new() -> Self {
546 Self {
547 waker: AtomicWaker::new(),
548 drop_not_ready: AtomicBool::new(false),
549 }
550 }
551 }
552
494 pub trait Instance { 553 pub trait Instance {
495 type Interrupt: crate::interrupt::typelevel::Interrupt; 554 type Interrupt: crate::interrupt::typelevel::Interrupt;
496 const REGS: crate::pac::ucpd::Ucpd; 555 const REGS: crate::pac::ucpd::Ucpd;
497 fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; 556 fn state() -> &'static crate::ucpd::sealed::State;
498 } 557 }
499} 558}
500 559
@@ -505,9 +564,9 @@ foreach_interrupt!(
505 564
506 const REGS: crate::pac::ucpd::Ucpd = crate::pac::$inst; 565 const REGS: crate::pac::ucpd::Ucpd = crate::pac::$inst;
507 566
508 fn waker() -> &'static AtomicWaker { 567 fn state() -> &'static crate::ucpd::sealed::State {
509 static WAKER: AtomicWaker = AtomicWaker::new(); 568 static STATE: crate::ucpd::sealed::State = crate::ucpd::sealed::State::new();
510 &WAKER 569 &STATE
511 } 570 }
512 } 571 }
513 572
diff --git a/examples/stm32g4/src/bin/usb_c_pd.rs b/examples/stm32g4/src/bin/usb_c_pd.rs
index 14abd542f..0e80840df 100644
--- a/examples/stm32g4/src/bin/usb_c_pd.rs
+++ b/examples/stm32g4/src/bin/usb_c_pd.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::{error, info, Format}; 4use defmt::{error, info, Format};
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::ucpd::{self, CcPull, CcSel, CcVState, Ucpd}; 6use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd};
7use embassy_stm32::Config; 7use embassy_stm32::Config;
8use embassy_time::{with_timeout, Duration}; 8use embassy_time::{with_timeout, Duration};
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
@@ -16,17 +16,17 @@ enum CableOrientation {
16} 16}
17 17
18// Returns true when the cable 18// Returns true when the cable
19async fn wait_attached<T: ucpd::Instance>(ucpd: &mut Ucpd<'_, T>) -> CableOrientation { 19async fn wait_attached<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>) -> CableOrientation {
20 loop { 20 loop {
21 let (cc1, cc2) = ucpd.cc_vstate(); 21 let (cc1, cc2) = cc_phy.vstate();
22 if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST { 22 if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST {
23 // Detached, wait until attached by monitoring the CC lines. 23 // Detached, wait until attached by monitoring the CC lines.
24 ucpd.wait_for_cc_vstate_change().await; 24 cc_phy.wait_for_vstate_change().await;
25 continue; 25 continue;
26 } 26 }
27 27
28 // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms). 28 // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms).
29 if with_timeout(Duration::from_millis(100), ucpd.wait_for_cc_vstate_change()) 29 if with_timeout(Duration::from_millis(100), cc_phy.wait_for_vstate_change())
30 .await 30 .await
31 .is_ok() 31 .is_ok()
32 { 32 {
@@ -50,10 +50,11 @@ async fn main(_spawner: Spawner) {
50 50
51 info!("Hello World!"); 51 info!("Hello World!");
52 52
53 let mut ucpd = Ucpd::new(p.UCPD1, p.PB6, p.PB4, CcPull::Sink); 53 let mut ucpd = Ucpd::new(p.UCPD1, p.PB6, p.PB4);
54 ucpd.cc_phy().set_pull(CcPull::Sink);
54 55
55 info!("Waiting for USB connection..."); 56 info!("Waiting for USB connection...");
56 let cable_orientation = wait_attached(&mut ucpd).await; 57 let cable_orientation = wait_attached(ucpd.cc_phy()).await;
57 info!("USB cable connected, orientation: {}", cable_orientation); 58 info!("USB cable connected, orientation: {}", cable_orientation);
58 59
59 let cc_sel = match cable_orientation { 60 let cc_sel = match cable_orientation {
@@ -67,7 +68,7 @@ async fn main(_spawner: Spawner) {
67 } 68 }
68 CableOrientation::DebugAccessoryMode => panic!("No PD communication in DAM"), 69 CableOrientation::DebugAccessoryMode => panic!("No PD communication in DAM"),
69 }; 70 };
70 let mut pd_phy = ucpd.pd_phy(p.DMA1_CH1, p.DMA1_CH2, cc_sel); 71 let (_cc_phy, mut pd_phy) = ucpd.split_pd_phy(p.DMA1_CH1, p.DMA1_CH2, cc_sel);
71 72
72 loop { 73 loop {
73 // Enough space for the longest non-extended data message. 74 // Enough space for the longest non-extended data message.