1//! USB Type-C/USB Power Delivery Interface (UCPD)
2
3// Implementation Notes
4//
5// As of Feb. 2024 the UCPD peripheral is availalbe on: G0, G4, H5, L5, U5
6//
7// Cube HAL LL Driver (g0):
8// https://github.com/STMicroelectronics/stm32g0xx_hal_driver/blob/v1.4.6/Inc/stm32g0xx_ll_ucpd.h
9// https://github.com/STMicroelectronics/stm32g0xx_hal_driver/blob/v1.4.6/Src/stm32g0xx_ll_ucpd.c
10// Except for a the `LL_UCPD_RxAnalogFilterEnable/Disable()` functions the Cube HAL implementation of
11// all families is the same.
12//
13// Dead battery pull-down resistors functionality is enabled by default on startup and must
14// be disabled by setting a bit in PWR/SYSCFG registers. The exact name and location for that
15// bit is different for each familily.
16
17use core::future::poll_fn;
18use core::marker::PhantomData;
19use core::sync::atomic::{AtomicBool, Ordering};
20use core::task::Poll;
21
22use embassy_hal_internal::drop::OnDrop;
23use embassy_hal_internal::{into_ref, Peripheral};
24use embassy_sync::waitqueue::AtomicWaker;
25
26use crate::dma::{ChannelAndRequest, TransferOptions};
27use crate::interrupt;
28use crate::interrupt::typelevel::Interrupt;
29use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode};
30pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState};
31use crate::rcc::{self, RccPeripheral};
32
33pub(crate) fn init(
34 _cs: critical_section::CriticalSection,
35 #[cfg(peri_ucpd1)] ucpd1_db_enable: bool,
36 #[cfg(peri_ucpd2)] ucpd2_db_enable: bool,
37) {
38 #[cfg(stm32g0x1)]
39 {
40 // according to RM0444 (STM32G0x1) section 8.1.1:
41 // when UCPD is disabled setting the strobe will disable dead battery
42 // (which is enabled after reset) but if UCPD is enabled, setting the
43 // strobe will apply the CC pin configuration from the control register
44 // (which is why we need to be careful about when we call this)
45 crate::pac::SYSCFG.cfgr1().modify(|w| {
46 w.set_ucpd1_strobe(!ucpd1_db_enable);
47 w.set_ucpd2_strobe(!ucpd2_db_enable);
48 });
49 }
50
51 #[cfg(any(stm32g4, stm32l5))]
52 {
53 crate::pac::PWR.cr3().modify(|w| {
54 #[cfg(stm32g4)]
55 w.set_ucpd1_dbdis(!ucpd1_db_enable);
56 #[cfg(stm32l5)]
57 w.set_ucpd_dbdis(!ucpd1_db_enable);
58 })
59 }
60
61 #[cfg(any(stm32h5, stm32u5, stm32h7rs))]
62 {
63 crate::pac::PWR.ucpdr().modify(|w| {
64 w.set_ucpd_dbdis(!ucpd1_db_enable);
65 })
66 }
67}
68
69/// Pull-up or Pull-down resistor state of both CC lines.
70#[derive(Debug, Clone, Copy, PartialEq)]
71#[cfg_attr(feature = "defmt", derive(defmt::Format))]
72pub enum CcPull {
73 /// Analog PHY for CC pin disabled.
74 Disabled,
75
76 /// Rd=5.1k pull-down resistor.
77 Sink,
78
79 /// Rp=56k pull-up resistor to indicate default USB power.
80 SourceDefaultUsb,
81
82 /// Rp=22k pull-up resistor to indicate support for up to 1.5A.
83 Source1_5A,
84
85 /// Rp=10k pull-up resistor to indicate support for up to 3.0A.
86 Source3_0A,
87}
88
89/// UCPD configuration
90#[non_exhaustive]
91#[derive(Copy, Clone, Debug)]
92pub struct Config {
93 /// Receive SOP packets
94 pub sop: bool,
95 /// Receive SOP' packets
96 pub sop_prime: bool,
97 /// Receive SOP'' packets
98 pub sop_double_prime: bool,
99 /// Receive SOP'_Debug packets
100 pub sop_prime_debug: bool,
101 /// Receive SOP''_Debug packets
102 pub sop_double_prime_debug: bool,
103}
104
105impl Default for Config {
106 fn default() -> Self {
107 Self {
108 sop: true,
109 sop_prime: false,
110 sop_double_prime: false,
111 sop_prime_debug: false,
112 sop_double_prime_debug: false,
113 }
114 }
115}
116
117/// UCPD driver.
118pub struct Ucpd<'d, T: Instance> {
119 cc_phy: CcPhy<'d, T>,
120}
121
122impl<'d, T: Instance> Ucpd<'d, T> {
123 /// Creates a new UCPD driver instance.
124 pub fn new(
125 _peri: impl Peripheral<P = T> + 'd,
126 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
127 cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd,
128 cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd,
129 config: Config,
130 ) -> Self {
131 into_ref!(cc1, cc2);
132 cc1.set_as_analog();
133 cc2.set_as_analog();
134
135 rcc::enable_and_reset::<T>();
136 T::Interrupt::unpend();
137 unsafe { T::Interrupt::enable() };
138
139 let r = T::REGS;
140 r.cfgr1().write(|w| {
141 // "The receiver is designed to work in the clock frequency range from 6 to 18 MHz.
142 // However, the optimum performance is ensured in the range from 6 to 12 MHz"
143 // UCPD is driven by HSI16 (16MHz internal oscillator), which we need to divide by 2.
144 w.set_psc_usbpdclk(PscUsbpdclk::DIV2);
145
146 // Prescaler to produce a target half-bit frequency of 600kHz which is required
147 // to produce transmit with a nominal nominal bit rate of 300Kbps+-10% using
148 // biphase mark coding (BMC, aka differential manchester coding).
149 // A divider of 13 gives the target frequency closest to spec (~615kHz, 1.625us).
150 w.set_hbitclkdiv(13 - 1);
151
152 // Time window for detecting non-idle (12-20us).
153 // 1.75us * 8 = 14us.
154 w.set_transwin(8 - 1);
155
156 // Time from the end of last bit of a Frame until the start of the first bit of the
157 // next Preamble (min 25us).
158 // 1.75us * 17 = ~30us
159 w.set_ifrgap(17 - 1);
160
161 // UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing).
162 let rxordset = (config.sop as u16) << 0
163 | (config.sop_prime as u16) << 1
164 | (config.sop_double_prime as u16) << 2
165 // Hard reset
166 | 0x1 << 3
167 | (config.sop_prime_debug as u16) << 4
168 | (config.sop_double_prime_debug as u16) << 5;
169 w.set_rxordseten(rxordset);
170
171 // Enable DMA
172 w.set_txdmaen(true);
173 w.set_rxdmaen(true);
174
175 w.set_ucpden(true);
176 });
177
178 #[cfg(stm32h5)]
179 r.cfgr2().write(|w| {
180 w.set_rxafilten(true);
181 });
182
183 // Software trim according to RM0481, p. 2650/2668
184 #[cfg(stm32h5)]
185 {
186 let trim_rd_cc1 = unsafe { *(0x4002_242C as *const u32) & 0xF };
187 let trim_rd_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 8) & 0xF };
188
189 r.cfgr3().write(|w| {
190 w.set_trim_cc1_rd(trim_rd_cc1 as u8);
191 w.set_trim_cc2_rd(trim_rd_cc2 as u8);
192 });
193 }
194
195 Self {
196 cc_phy: CcPhy { _lifetime: PhantomData },
197 }
198 }
199
200 /// Returns the TypeC CC PHY.
201 pub fn cc_phy(&mut self) -> &mut CcPhy<'d, T> {
202 &mut self.cc_phy
203 }
204
205 /// Splits the UCPD driver into a TypeC PHY to control and monitor CC voltage
206 /// and a Power Delivery (PD) PHY with receiver and transmitter.
207 pub fn split_pd_phy(
208 self,
209 rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
210 tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
211 cc_sel: CcSel,
212 ) -> (CcPhy<'d, T>, PdPhy<'d, T>) {
213 let r = T::REGS;
214
215 // TODO: Currently only SOP messages are supported.
216 r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));
217
218 // Enable the receiver on one of the two CC lines.
219 r.cr().modify(|w| w.set_phyccsel(cc_sel));
220
221 // Enable hard reset receive interrupt.
222 r.imr().modify(|w| w.set_rxhrstdetie(true));
223
224 // Enable PD packet reception
225 r.cr().modify(|w| w.set_phyrxen(true));
226
227 // Both parts must be dropped before the peripheral can be disabled.
228 T::state().drop_not_ready.store(true, Ordering::Relaxed);
229
230 into_ref!(rx_dma, tx_dma);
231 let rx_dma_req = rx_dma.request();
232 let tx_dma_req = tx_dma.request();
233 (
234 self.cc_phy,
235 PdPhy {
236 _lifetime: PhantomData,
237 rx_dma: ChannelAndRequest {
238 channel: rx_dma.map_into(),
239 request: rx_dma_req,
240 },
241 tx_dma: ChannelAndRequest {
242 channel: tx_dma.map_into(),
243 request: tx_dma_req,
244 },
245 },
246 )
247 }
248}
249
250/// Control and monitoring of TypeC CC pin functionailty.
251pub struct CcPhy<'d, T: Instance> {
252 _lifetime: PhantomData<&'d mut T>,
253}
254
255impl<'d, T: Instance> Drop for CcPhy<'d, T> {
256 fn drop(&mut self) {
257 let r: Ucpd = T::REGS;
258 r.cr().modify(|w: &mut Cr| {
259 w.set_cc1tcdis(val:true);
260 w.set_cc2tcdis(val:true);
261 w.set_ccenable(val:Ccenable::DISABLED);
262 });
263
264 // Check if the PdPhy part was dropped already.
265 let drop_not_ready: &AtomicBool = &T::state().drop_not_ready;
266 if drop_not_ready.load(order:Ordering::Relaxed) {
267 drop_not_ready.store(val:true, order:Ordering::Relaxed);
268 } else {
269 r.cfgr1().write(|w: &mut Cfgr1| w.set_ucpden(val:false));
270 rcc::disable::<T>();
271 T::Interrupt::disable();
272 }
273 }
274}
275
276impl<'d, T: Instance> CcPhy<'d, T> {
277 /// Sets the pull-up/pull-down resistor values exposed on the CC pins.
278 pub fn set_pull(&mut self, cc_pull: CcPull) {
279 T::REGS.cr().modify(|w| {
280 w.set_anamode(if cc_pull == CcPull::Sink {
281 Anamode::SINK
282 } else {
283 Anamode::SOURCE
284 });
285 w.set_anasubmode(match cc_pull {
286 CcPull::SourceDefaultUsb => 1,
287 CcPull::Source1_5A => 2,
288 CcPull::Source3_0A => 3,
289 _ => 0,
290 });
291 w.set_ccenable(if cc_pull == CcPull::Disabled {
292 Ccenable::DISABLED
293 } else {
294 Ccenable::BOTH
295 });
296 });
297
298 // Software trim according to RM0481, p. 2650/2668
299 #[cfg(stm32h5)]
300 T::REGS.cfgr3().modify(|w| match cc_pull {
301 CcPull::Source1_5A => {
302 let trim_1a5_cc1 = unsafe { *(0x08FF_F844 as *const u32) & 0xF };
303 let trim_1a5_cc2 = unsafe { ((*(0x08FF_F844 as *const u32)) >> 16) & 0xF };
304
305 w.set_trim_cc1_rp(trim_1a5_cc1 as u8);
306 w.set_trim_cc2_rp(trim_1a5_cc2 as u8);
307 }
308 _ => {
309 let trim_3a0_cc1 = unsafe { (*(0x4002_242C as *const u32) >> 4) & 0xF };
310 let trim_3a0_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 12) & 0xF };
311
312 w.set_trim_cc1_rp(trim_3a0_cc1 as u8);
313 w.set_trim_cc2_rp(trim_3a0_cc2 as u8);
314 }
315 });
316
317 // Disable dead-battery pull-down resistors which are enabled by default on boot.
318 critical_section::with(|cs| {
319 init(
320 cs,
321 false,
322 #[cfg(peri_ucpd2)]
323 false,
324 );
325 });
326 }
327
328 /// Returns the current voltage level of CC1 and CC2 pin as tuple.
329 ///
330 /// Interpretation of the voltage levels depends on the configured CC line
331 /// pull-up/pull-down resistance.
332 pub fn vstate(&self) -> (CcVState, CcVState) {
333 let sr = T::REGS.sr().read();
334 (sr.typec_vstate_cc1(), sr.typec_vstate_cc2())
335 }
336
337 /// Waits for a change in voltage state on either CC line.
338 pub async fn wait_for_vstate_change(&self) -> (CcVState, CcVState) {
339 let _on_drop = OnDrop::new(|| self.enable_cc_interrupts(false));
340 let prev_vstate = self.vstate();
341 poll_fn(|cx| {
342 let vstate = self.vstate();
343 if vstate != prev_vstate {
344 Poll::Ready(vstate)
345 } else {
346 T::state().waker.register(cx.waker());
347 self.enable_cc_interrupts(true);
348 Poll::Pending
349 }
350 })
351 .await
352 }
353
354 fn enable_cc_interrupts(&self, enable: bool) {
355 T::REGS.imr().modify(|w| {
356 w.set_typecevt1ie(enable);
357 w.set_typecevt2ie(enable);
358 });
359 }
360}
361
362/// Receive SOP.
363#[derive(Debug, Clone, Copy)]
364#[cfg_attr(feature = "defmt", derive(defmt::Format))]
365pub enum Sop {
366 /// SOP
367 Sop,
368 /// SOP'
369 SopPrime,
370 /// SOP''
371 SopDoublePrime,
372 /// SOP'_Debug
373 SopPrimeDebug,
374 /// SOP''_Debug
375 SopDoublePrimeDebug,
376}
377
378/// Receive Error.
379#[derive(Debug, Clone, Copy)]
380#[cfg_attr(feature = "defmt", derive(defmt::Format))]
381pub enum RxError {
382 /// Incorrect CRC or truncated message (a line becoming static before EOP is met).
383 Crc,
384
385 /// Provided buffer was too small for the received message.
386 Overrun,
387
388 /// Hard Reset received before or during reception.
389 HardReset,
390}
391
392/// Transmit Error.
393#[derive(Debug, Clone, Copy)]
394#[cfg_attr(feature = "defmt", derive(defmt::Format))]
395pub enum TxError {
396 /// Concurrent receive in progress or excessive noise on the line.
397 Discarded,
398
399 /// Hard Reset received before or during transmission.
400 HardReset,
401}
402
403/// Power Delivery (PD) PHY.
404pub struct PdPhy<'d, T: Instance> {
405 _lifetime: PhantomData<&'d mut T>,
406 rx_dma: ChannelAndRequest<'d>,
407 tx_dma: ChannelAndRequest<'d>,
408}
409
410impl<'d, T: Instance> Drop for PdPhy<'d, T> {
411 fn drop(&mut self) {
412 T::REGS.cr().modify(|w: &mut Cr| w.set_phyrxen(val:false));
413 // Check if the Type-C part was dropped already.
414 let drop_not_ready: &AtomicBool = &T::state().drop_not_ready;
415 if drop_not_ready.load(order:Ordering::Relaxed) {
416 drop_not_ready.store(val:true, order:Ordering::Relaxed);
417 } else {
418 T::REGS.cfgr1().write(|w: &mut Cfgr1| w.set_ucpden(val:false));
419 rcc::disable::<T>();
420 T::Interrupt::disable();
421 }
422 }
423}
424
425impl<'d, T: Instance> PdPhy<'d, T> {
426 /// Receives a PD message into the provided buffer.
427 ///
428 /// Returns the number of received bytes or an error.
429 pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> {
430 self.receive_with_sop(buf).await.map(|(_sop, size)| size)
431 }
432
433 /// Receives SOP and a PD message into the provided buffer.
434 ///
435 /// Returns the start of packet type and number of received bytes or an error.
436 pub async fn receive_with_sop(&mut self, buf: &mut [u8]) -> Result<(Sop, usize), RxError> {
437 let r = T::REGS;
438
439 let dma = unsafe {
440 self.rx_dma
441 .read(r.rxdr().as_ptr() as *mut u8, buf, TransferOptions::default())
442 };
443
444 let _on_drop = OnDrop::new(|| {
445 Self::enable_rx_interrupt(false);
446 // Clear interrupt flags
447 r.icr().write(|w| {
448 w.set_rxorddetcf(true);
449 w.set_rxovrcf(true);
450 w.set_rxmsgendcf(true);
451 });
452 });
453
454 poll_fn(|cx| {
455 let sr = r.sr().read();
456 if sr.rxhrstdet() {
457 // Clean and re-enable hard reset receive interrupt.
458 r.icr().write(|w| w.set_rxhrstdetcf(true));
459 r.imr().modify(|w| w.set_rxhrstdetie(true));
460 Poll::Ready(Err(RxError::HardReset))
461 } else if sr.rxmsgend() {
462 let ret = if sr.rxovr() {
463 Err(RxError::Overrun)
464 } else if sr.rxerr() {
465 Err(RxError::Crc)
466 } else {
467 Ok(())
468 };
469 Poll::Ready(ret)
470 } else {
471 T::state().waker.register(cx.waker());
472 Self::enable_rx_interrupt(true);
473 Poll::Pending
474 }
475 })
476 .await?;
477
478 // Make sure that the last byte was fetched by DMA.
479 while r.sr().read().rxne() {
480 if dma.get_remaining_transfers() == 0 {
481 return Err(RxError::Overrun);
482 }
483 }
484
485 let sop = match r.rx_ordsetr().read().rxordset() {
486 Rxordset::SOP => Sop::Sop,
487 Rxordset::SOP_PRIME => Sop::SopPrime,
488 Rxordset::SOP_DOUBLE_PRIME => Sop::SopDoublePrime,
489 Rxordset::SOP_PRIME_DEBUG => Sop::SopPrimeDebug,
490 Rxordset::SOP_DOUBLE_PRIME_DEBUG => Sop::SopDoublePrimeDebug,
491 Rxordset::CABLE_RESET => return Err(RxError::HardReset),
492 // Extension headers are not supported
493 _ => unreachable!(),
494 };
495
496 Ok((sop, r.rx_payszr().read().rxpaysz().into()))
497 }
498
499 fn enable_rx_interrupt(enable: bool) {
500 T::REGS.imr().modify(|w| w.set_rxmsgendie(enable));
501 }
502
503 /// Transmits a PD message.
504 pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> {
505 let r = T::REGS;
506
507 // When a previous transmission was dropped before it had finished it
508 // might still be running because there is no way to abort an ongoing
509 // message transmission. Wait for it to finish but ignore errors.
510 if r.cr().read().txsend() {
511 if let Err(TxError::HardReset) = Self::wait_tx_done().await {
512 return Err(TxError::HardReset);
513 }
514 }
515
516 // Clear the TX interrupt flags.
517 T::REGS.icr().write(|w| {
518 w.set_txmsgdisccf(true);
519 w.set_txmsgsentcf(true);
520 });
521
522 // Start the DMA and let it do its thing in the background.
523 let _dma = unsafe {
524 self.tx_dma
525 .write(buf, r.txdr().as_ptr() as *mut u8, TransferOptions::default())
526 };
527
528 // Configure and start the transmission.
529 r.tx_payszr().write(|w| w.set_txpaysz(buf.len() as _));
530 r.cr().modify(|w| {
531 w.set_txmode(Txmode::PACKET);
532 w.set_txsend(true);
533 });
534
535 Self::wait_tx_done().await
536 }
537
538 async fn wait_tx_done() -> Result<(), TxError> {
539 let _on_drop = OnDrop::new(|| Self::enable_tx_interrupts(false));
540 poll_fn(|cx| {
541 let r = T::REGS;
542 let sr = r.sr().read();
543 if sr.rxhrstdet() {
544 // Clean and re-enable hard reset receive interrupt.
545 r.icr().write(|w| w.set_rxhrstdetcf(true));
546 r.imr().modify(|w| w.set_rxhrstdetie(true));
547 Poll::Ready(Err(TxError::HardReset))
548 } else if sr.txmsgdisc() {
549 Poll::Ready(Err(TxError::Discarded))
550 } else if sr.txmsgsent() {
551 Poll::Ready(Ok(()))
552 } else {
553 T::state().waker.register(cx.waker());
554 Self::enable_tx_interrupts(true);
555 Poll::Pending
556 }
557 })
558 .await
559 }
560
561 fn enable_tx_interrupts(enable: bool) {
562 T::REGS.imr().modify(|w| {
563 w.set_txmsgdiscie(enable);
564 w.set_txmsgsentie(enable);
565 });
566 }
567
568 /// Transmit a hard reset.
569 pub async fn transmit_hardreset(&mut self) -> Result<(), TxError> {
570 let r = T::REGS;
571
572 // Clear the hardreset interrupt flags.
573 T::REGS.icr().write(|w| {
574 w.set_hrstdisccf(true);
575 w.set_hrstsentcf(true);
576 });
577
578 // Trigger hard reset transmission.
579 r.cr().modify(|w| {
580 w.set_txhrst(true);
581 });
582
583 let _on_drop = OnDrop::new(|| self.enable_hardreset_interrupts(false));
584 poll_fn(|cx| {
585 let r = T::REGS;
586 let sr = r.sr().read();
587 if sr.rxhrstdet() {
588 // Clean and re-enable hard reset receive interrupt.
589 r.icr().write(|w| w.set_rxhrstdetcf(true));
590 r.imr().modify(|w| w.set_rxhrstdetie(true));
591 Poll::Ready(Err(TxError::HardReset))
592 } else if sr.hrstdisc() {
593 Poll::Ready(Err(TxError::Discarded))
594 } else if sr.hrstsent() {
595 Poll::Ready(Ok(()))
596 } else {
597 T::state().waker.register(cx.waker());
598 self.enable_hardreset_interrupts(true);
599 Poll::Pending
600 }
601 })
602 .await
603 }
604
605 fn enable_hardreset_interrupts(&self, enable: bool) {
606 T::REGS.imr().modify(|w| {
607 w.set_hrstdiscie(enable);
608 w.set_hrstsentie(enable);
609 });
610 }
611}
612
613/// Interrupt handler.
614pub struct InterruptHandler<T: Instance> {
615 _phantom: PhantomData<T>,
616}
617
618impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
619 unsafe fn on_interrupt() {
620 let r = T::REGS;
621 let sr = r.sr().read();
622
623 if sr.typecevt1() || sr.typecevt2() {
624 r.icr().write(|w| {
625 w.set_typecevt1cf(true);
626 w.set_typecevt2cf(true);
627 });
628 }
629
630 if sr.rxhrstdet() {
631 r.imr().modify(|w| w.set_rxhrstdetie(false));
632 }
633
634 if sr.rxmsgend() {
635 r.imr().modify(|w| w.set_rxmsgendie(false));
636 }
637
638 if sr.txmsgdisc() || sr.txmsgsent() {
639 r.imr().modify(|w| {
640 w.set_txmsgdiscie(false);
641 w.set_txmsgsentie(false);
642 });
643 }
644
645 if sr.hrstdisc() || sr.hrstsent() {
646 r.imr().modify(|w| {
647 w.set_hrstdiscie(false);
648 w.set_hrstsentie(false);
649 });
650 }
651
652 // Wake the task to clear and re-enabled interrupts.
653 T::state().waker.wake();
654 }
655}
656
657struct State {
658 waker: AtomicWaker,
659 // Inverted logic for a default state of 0 so that the data goes into the .bss section.
660 drop_not_ready: AtomicBool,
661}
662
663impl State {
664 pub const fn new() -> Self {
665 Self {
666 waker: AtomicWaker::new(),
667 drop_not_ready: AtomicBool::new(false),
668 }
669 }
670}
671
672trait SealedInstance {
673 const REGS: crate::pac::ucpd::Ucpd;
674 fn state() -> &'static State;
675}
676
677/// UCPD instance trait.
678#[allow(private_bounds)]
679pub trait Instance: SealedInstance + RccPeripheral {
680 /// Interrupt for this instance.
681 type Interrupt: crate::interrupt::typelevel::Interrupt;
682}
683
684foreach_interrupt!(
685 ($inst:ident, ucpd, UCPD, GLOBAL, $irq:ident) => {
686 impl SealedInstance for crate::peripherals::$inst {
687 const REGS: crate::pac::ucpd::Ucpd = crate::pac::$inst;
688
689 fn state() -> &'static State {
690 static STATE: State = State::new();
691 &STATE
692 }
693 }
694
695 impl Instance for crate::peripherals::$inst {
696 type Interrupt = crate::interrupt::typelevel::$irq;
697 }
698 };
699);
700
701pin_trait!(Cc1Pin, Instance);
702pin_trait!(Cc2Pin, Instance);
703
704dma_trait!(TxDma, Instance);
705dma_trait!(RxDma, Instance);
706