diff options
| author | Timo Kröger <[email protected]> | 2024-03-07 19:47:13 +0100 |
|---|---|---|
| committer | Timo Kröger <[email protected]> | 2024-03-12 08:14:42 +0100 |
| commit | 5e271ff31b55b339d4321af4b2c8a096bf153d4b (patch) | |
| tree | 1319404e86b0af66eef9810bd3d0b4b752f40799 /embassy-stm32/src/ucpd.rs | |
| parent | 36a99189210bf15c93198a4bf9a0d2ab732c8bf8 (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.rs | 125 |
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 | ||
| 58 | impl<'d, T: Instance> Drop for Ucpd<'d, T> { | 58 | impl<'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. |
| 229 | pub struct PdRx<'d, T: Instance> { | 221 | #[derive(Debug, Clone, Copy)] |
| 222 | pub enum TxError { | ||
| 223 | /// Concurrent receive in progress or excessive noise on the line. | ||
| 224 | Discarded, | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Power Delivery (PD) PHY. | ||
| 228 | pub 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 | ||
| 235 | impl<'d, T: Instance> Drop for PdRx<'d, T> { | 236 | impl<'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 | ||
| 241 | impl<'d, T: Instance> PdRx<'d, T> { | 242 | impl<'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)); |
| 316 | pub enum TxError { | 316 | } |
| 317 | /// Concurrent receive in progress or excessive noise on the line. | ||
| 318 | Discarded, | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Power Delivery (PD) Transmitter. | ||
| 322 | pub struct PdTx<'d, T: Instance> { | ||
| 323 | _ucpd: &'d Ucpd<'d, T>, | ||
| 324 | dma_ch: PeripheralRef<'d, AnyChannel>, | ||
| 325 | dma_req: Request, | ||
| 326 | } | ||
| 327 | 317 | ||
| 328 | impl<'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. |
