aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/ucpd.rs
diff options
context:
space:
mode:
authorTimo Kröger <[email protected]>2024-03-07 19:47:13 +0100
committerTimo Kröger <[email protected]>2024-03-12 08:14:42 +0100
commit5e271ff31b55b339d4321af4b2c8a096bf153d4b (patch)
tree1319404e86b0af66eef9810bd3d0b4b752f40799 /embassy-stm32/src/ucpd.rs
parent36a99189210bf15c93198a4bf9a0d2ab732c8bf8 (diff)
[UCPD] Combine RX and TX
`select(rx.receive(), tx.transmit()` had subtle interrupt enable race conditions. Combine receiver and transmitter into one new `PdPhy` struct to disallow the problematic pattern. Scanning through the USB PD 2.0 specification there is no need to have RX and TX running concurrently (after all the USB PD communication is half-duplex).
Diffstat (limited to 'embassy-stm32/src/ucpd.rs')
-rw-r--r--embassy-stm32/src/ucpd.rs125
1 files changed, 58 insertions, 67 deletions
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs
index 0a6f7df71..02c81c2b6 100644
--- a/embassy-stm32/src/ucpd.rs
+++ b/embassy-stm32/src/ucpd.rs
@@ -57,11 +57,7 @@ pub struct Ucpd<'d, T: Instance> {
57 57
58impl<'d, T: Instance> Drop for Ucpd<'d, T> { 58impl<'d, T: Instance> Drop for Ucpd<'d, T> {
59 fn drop(&mut self) { 59 fn drop(&mut self) {
60 T::REGS.cr().modify(|w| { 60 T::REGS.cfgr1().write(|w| w.set_ucpden(false));
61 w.set_ccenable(Ccenable::DISABLED);
62 w.set_cc1tcdis(true);
63 w.set_cc2tcdis(true);
64 });
65 } 61 }
66} 62}
67 63
@@ -99,12 +95,6 @@ impl<'d, T: Instance> Ucpd<'d, T> {
99 // 1.75us * 17 = ~30us 95 // 1.75us * 17 = ~30us
100 w.set_ifrgap(17 - 1); 96 w.set_ifrgap(17 - 1);
101 97
102 // TODO: Currently only SOP messages are supported.
103 w.set_rxordseten(0x1);
104
105 // Enable DMA and the peripheral
106 w.set_txdmaen(true);
107 w.set_rxdmaen(true);
108 w.set_ucpden(true); 98 w.set_ucpden(true);
109 }); 99 });
110 100
@@ -155,7 +145,7 @@ impl<'d, T: Instance> Ucpd<'d, T> {
155 145
156 /// Waits for a change in voltage state on either CC line. 146 /// Waits for a change in voltage state on either CC line.
157 pub async fn wait_for_cc_vstate_change(&self) -> (CcVState, CcVState) { 147 pub async fn wait_for_cc_vstate_change(&self) -> (CcVState, CcVState) {
158 let _on_drop = OnDrop::new(|| critical_section::with(|_| self.enable_cc_interrupts(false))); 148 let _on_drop = OnDrop::new(|| self.enable_cc_interrupts(false));
159 let prev_vstate = self.cc_vstate(); 149 let prev_vstate = self.cc_vstate();
160 poll_fn(|cx| { 150 poll_fn(|cx| {
161 let vstate = self.cc_vstate(); 151 let vstate = self.cc_vstate();
@@ -171,47 +161,49 @@ impl<'d, T: Instance> Ucpd<'d, T> {
171 } 161 }
172 162
173 fn enable_cc_interrupts(&self, enable: bool) { 163 fn enable_cc_interrupts(&self, enable: bool) {
174 critical_section::with(|_| { 164 T::REGS.imr().modify(|w| {
175 T::REGS.imr().modify(|w| { 165 w.set_typecevt1ie(enable);
176 w.set_typecevt1ie(enable); 166 w.set_typecevt2ie(enable);
177 w.set_typecevt2ie(enable);
178 })
179 }); 167 });
180 } 168 }
181 169
182 /// Returns PD receiver and transmitter. 170 /// Returns PD receiver and transmitter.
183 pub fn pd( 171 pub fn pd_phy(
184 &mut self, 172 &mut self,
185 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, 173 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
186 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, 174 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
187 cc_sel: CcSel, 175 cc_sel: CcSel,
188 ) -> (PdRx<'_, T>, PdTx<'_, T>) { 176 ) -> PdPhy<'_, T> {
189 let r = T::REGS; 177 let r = T::REGS;
190 178
179 // TODO: Currently only SOP messages are supported.
180 r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
181
182 r.cfgr1().modify(|w| {
183 // TODO: Currently only SOP messages are supported.
184 w.set_rxordseten(0x1);
185
186 // Enable DMA
187 w.set_txdmaen(true);
188 w.set_rxdmaen(true);
189 });
190
191 // Enable the receiver on one of the two CC lines. 191 // Enable the receiver on one of the two CC lines.
192 r.cr().modify(|w| { 192 r.cr().modify(|w| {
193 w.set_phyccsel(cc_sel); 193 w.set_phyccsel(cc_sel);
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
200 into_ref!(rx_dma, tx_dma); 197 into_ref!(rx_dma, tx_dma);
201 let rx_dma_req = rx_dma.request(); 198 let rx_dma_req = rx_dma.request();
202 let tx_dma_req = tx_dma.request(); 199 let tx_dma_req = tx_dma.request();
203 ( 200 PdPhy {
204 PdRx { 201 _ucpd: self,
205 _ucpd: self, 202 rx_dma_ch: rx_dma.map_into(),
206 dma_ch: rx_dma.map_into(), 203 rx_dma_req,
207 dma_req: rx_dma_req, 204 tx_dma_ch: tx_dma.map_into(),
208 }, 205 tx_dma_req,
209 PdTx { 206 }
210 _ucpd: self,
211 dma_ch: tx_dma.map_into(),
212 dma_req: tx_dma_req,
213 },
214 )
215 } 207 }
216} 208}
217 209
@@ -225,20 +217,29 @@ pub enum RxError {
225 Overrun, 217 Overrun,
226} 218}
227 219
228/// Power Delivery (PD) Receiver. 220/// Transmit Error.
229pub struct PdRx<'d, T: Instance> { 221#[derive(Debug, Clone, Copy)]
222pub enum TxError {
223 /// Concurrent receive in progress or excessive noise on the line.
224 Discarded,
225}
226
227/// Power Delivery (PD) PHY.
228pub struct PdPhy<'d, T: Instance> {
230 _ucpd: &'d Ucpd<'d, T>, 229 _ucpd: &'d Ucpd<'d, T>,
231 dma_ch: PeripheralRef<'d, AnyChannel>, 230 rx_dma_ch: PeripheralRef<'d, AnyChannel>,
232 dma_req: Request, 231 rx_dma_req: Request,
232 tx_dma_ch: PeripheralRef<'d, AnyChannel>,
233 tx_dma_req: Request,
233} 234}
234 235
235impl<'d, T: Instance> Drop for PdRx<'d, T> { 236impl<'d, T: Instance> Drop for PdPhy<'d, T> {
236 fn drop(&mut self) { 237 fn drop(&mut self) {
237 T::REGS.cr().modify(|w| w.set_phyrxen(false)); 238 T::REGS.cr().modify(|w| w.set_phyrxen(false));
238 } 239 }
239} 240}
240 241
241impl<'d, T: Instance> PdRx<'d, T> { 242impl<'d, T: Instance> PdPhy<'d, T> {
242 /// Receives a PD message into the provided buffer. 243 /// Receives a PD message into the provided buffer.
243 /// 244 ///
244 /// Returns the number of received bytes or an error. 245 /// Returns the number of received bytes or an error.
@@ -261,8 +262,8 @@ impl<'d, T: Instance> PdRx<'d, T> {
261 transfer_options.complete_transfer_ir = false; 262 transfer_options.complete_transfer_ir = false;
262 263
263 Transfer::new_read( 264 Transfer::new_read(
264 &self.dma_ch, 265 &self.rx_dma_ch,
265 self.dma_req, 266 self.rx_dma_req,
266 r.rxdr().as_ptr() as *mut u8, 267 r.rxdr().as_ptr() as *mut u8,
267 buf, 268 buf,
268 transfer_options, 269 transfer_options,
@@ -282,6 +283,7 @@ impl<'d, T: Instance> PdRx<'d, T> {
282 } 283 }
283 284
284 async fn wait_rx_done(&self) -> Result<(), RxError> { 285 async fn wait_rx_done(&self) -> Result<(), RxError> {
286 let _on_drop = OnDrop::new(|| self.enable_rx_interrupt(false));
285 poll_fn(|cx| { 287 poll_fn(|cx| {
286 let r = T::REGS; 288 let r = T::REGS;
287 let sr = r.sr().read(); 289 let sr = r.sr().read();
@@ -301,31 +303,18 @@ impl<'d, T: Instance> PdRx<'d, T> {
301 }); 303 });
302 Poll::Ready(ret) 304 Poll::Ready(ret)
303 } else { 305 } else {
304 // Enable receiver interrupt.
305 T::waker().register(cx.waker()); 306 T::waker().register(cx.waker());
306 r.imr().modify(|w| w.set_rxmsgendie(true)); 307 self.enable_rx_interrupt(true);
307 Poll::Pending 308 Poll::Pending
308 } 309 }
309 }) 310 })
310 .await 311 .await
311 } 312 }
312}
313 313
314/// Transmit Error. 314 fn enable_rx_interrupt(&self, enable: bool) {
315#[derive(Debug, Clone, Copy)] 315 T::REGS.imr().modify(|w| w.set_rxmsgendie(enable));
316pub enum TxError { 316 }
317 /// Concurrent receive in progress or excessive noise on the line.
318 Discarded,
319}
320
321/// Power Delivery (PD) Transmitter.
322pub struct PdTx<'d, T: Instance> {
323 _ucpd: &'d Ucpd<'d, T>,
324 dma_ch: PeripheralRef<'d, AnyChannel>,
325 dma_req: Request,
326}
327 317
328impl<'d, T: Instance> PdTx<'d, T> {
329 /// Transmits a PD message. 318 /// Transmits a PD message.
330 pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> { 319 pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> {
331 let r = T::REGS; 320 let r = T::REGS;
@@ -346,8 +335,8 @@ impl<'d, T: Instance> PdTx<'d, T> {
346 // Start the DMA and let it do its thing in the background. 335 // Start the DMA and let it do its thing in the background.
347 let _dma = unsafe { 336 let _dma = unsafe {
348 Transfer::new_write( 337 Transfer::new_write(
349 &self.dma_ch, 338 &self.tx_dma_ch,
350 self.dma_req, 339 self.tx_dma_req,
351 buf, 340 buf,
352 r.txdr().as_ptr() as *mut u8, 341 r.txdr().as_ptr() as *mut u8,
353 TransferOptions::default(), 342 TransferOptions::default(),
@@ -365,6 +354,7 @@ impl<'d, T: Instance> PdTx<'d, T> {
365 } 354 }
366 355
367 async fn wait_tx_done(&self) -> Result<(), TxError> { 356 async fn wait_tx_done(&self) -> Result<(), TxError> {
357 let _on_drop = OnDrop::new(|| self.enable_tx_interrupts(false));
368 poll_fn(|cx| { 358 poll_fn(|cx| {
369 let r = T::REGS; 359 let r = T::REGS;
370 if r.sr().read().txmsgdisc() { 360 if r.sr().read().txmsgdisc() {
@@ -372,19 +362,20 @@ impl<'d, T: Instance> PdTx<'d, T> {
372 } else if r.sr().read().txmsgsent() { 362 } else if r.sr().read().txmsgsent() {
373 Poll::Ready(Ok(())) 363 Poll::Ready(Ok(()))
374 } else { 364 } else {
375 // Enable transmit interrupts.
376 T::waker().register(cx.waker()); 365 T::waker().register(cx.waker());
377 r.imr().modify(|w| { 366 self.enable_tx_interrupts(true);
378 w.set_txmsgdiscie(true);
379 w.set_txmsgsentie(true);
380 });
381 Poll::Pending 367 Poll::Pending
382 } 368 }
383 }) 369 })
384 .await 370 .await
385 } 371 }
386 372
387 fn clear_tx_flags(&self) {} 373 fn enable_tx_interrupts(&self, enable: bool) {
374 T::REGS.imr().modify(|w| {
375 w.set_txmsgdiscie(enable);
376 w.set_txmsgsentie(enable);
377 });
378 }
388} 379}
389 380
390/// Interrupt handler. 381/// Interrupt handler.