Skip to main content

ra_hal/
usb.rs

1//! USB Full-Speed 2.0 peripheral.
2
3use core::{
4    marker::PhantomData,
5    sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering},
6};
7
8use embassy_hal_internal::{Peri, PeripheralType, interrupt::InterruptExt};
9use embassy_sync::waitqueue::AtomicWaker;
10use embassy_usb_driver::{
11    Direction, Driver, EndpointAddress, EndpointAllocError, EndpointInfo, EndpointType,
12};
13
14use crate::{
15    event_link::{IcuInterrupt, InterruptEvent},
16    gpio::{Pin, PortFunction},
17    interrupt::typelevel::{Binding, Handler as InterruptHandler, Interrupt as InterruptType},
18    module_stop::ModuleStop,
19    pac::{
20        self,
21        usbfs::vals::{Dvsq, Pipesel, Type},
22    },
23    peripherals::USBFS,
24    usb::{
25        bus::{Bus, BusEvent},
26        control_pipe::ControlPipe,
27        endpoint::{EndpointIn, EndpointOut},
28    },
29};
30
31pub mod bus;
32pub mod control_pipe;
33pub mod endpoint;
34
35#[derive(Clone, Copy)]
36struct PipeConfig {
37    ep_addr: EndpointAddress,
38    ep_type: EndpointType,
39    max_packet: u16,
40    interval_ms: u8,
41}
42struct State {
43    /// The bus waker
44    bus_waker: AtomicWaker,
45    /// The various endpoint wakers, indexed by pipe number (0 = control, 1-9 = data pipes)
46    ep_wakers: [AtomicWaker; 10],
47    /// Bus-level events set by ISR, consumed by Bus::poll.
48    pending_bus: AtomicU32,
49    /// BRDY flags set by ISR per pipe (bit N = pipe N has data ready).
50    pipe_brdy: AtomicU32,
51    /// BEMP flags set by ISR per pipe (bit N = pipe N transmit buffer empty).
52    pipe_bemp: AtomicU32,
53    /// Whether a valid SETUP packet is waiting to be read.
54    setup_ready: AtomicBool,
55    /// Cached setup packet set by ISR (8 bytes stored as two u32s, LE).
56    setup_lo: AtomicU32,
57    setup_hi: AtomicU32,
58    /// Bit mask of pipes that have been enabled via endpoint_set_enabled.
59    pipe_enabled: AtomicU32,
60}
61
62/// Interrupt handler for `USBFS` interrupts.
63pub struct UsbInterruptHandler<I: Instance> {
64    _phantom: PhantomData<I>,
65}
66
67/// `embassy-usb-driver` implementation for the USBFS peripheral.
68pub struct Usbfs<'a, I: Instance> {
69    _phantom: PhantomData<&'a I>,
70    pipes: [Option<PipeConfig>; 9],
71    next_ep_in: u8,
72    next_ep_out: u8,
73}
74
75impl State {
76    const fn new() -> Self {
77        Self {
78            bus_waker: AtomicWaker::new(),
79            ep_wakers: [const { AtomicWaker::new() }; 10],
80            pending_bus: AtomicU32::new(0),
81            pipe_brdy: AtomicU32::new(0),
82            pipe_bemp: AtomicU32::new(0),
83            setup_ready: AtomicBool::new(false),
84            setup_lo: AtomicU32::new(0),
85            setup_hi: AtomicU32::new(0),
86            pipe_enabled: AtomicU32::new(0),
87        }
88    }
89}
90
91static STATE: State = State::new();
92
93/// Per-pipe endpoint address table populated during `start()`.
94/// Index 0 = pipe 1, index 8 = pipe 9. 0xFF means unallocated.
95static EP_ADDR: [AtomicU8; 9] = [const { AtomicU8::new(0xFF) }; 9];
96
97/// USB device instance
98#[allow(private_bounds)]
99pub trait Instance: SealedInstance + ModuleStop + PeripheralType + 'static + Send {}
100
101pub(crate) trait SealedInstance {
102    fn regs() -> pac::usbfs::Usbfs;
103}
104
105pub(crate) trait SealedDpPin<I: SealedInstance>: Pin + PeripheralType {
106    // const PERIPHERAL_FUNC: PortFunction;
107
108    #[inline(always)]
109    fn set_pfunc(&self) {
110        /*
111         * Technically, we should probably set the peripheral function for the pin; however,
112         * Table 19.17 shows that the pins used for DP and DM don't care about the `PSEL` settings.
113         * Furthermore, if we set a value for pins P914 and P915, the readback fails as they read
114         * as HiZ (0x00).
115         *
116         * Leaving this as a vestigial remnant for reference in other chips.
117         */
118        //self.set_as_pf(Self::PERIPHERAL_FUNC);
119    }
120}
121
122/// A pin that can be used for the differential negative input.
123#[allow(private_bounds)]
124pub trait DpPin<I: Instance>: SealedDpPin<I> {}
125
126#[allow(unused)]
127macro_rules! dp_pin {
128    ($instance:ident, $pin:ident, $pfunc:ident) => {
129        impl crate::usb::DpPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
130        impl crate::usb::SealedDpPin<crate::peripherals::$instance> for crate::peripherals::$pin {
131            // const PERIPHERAL_FUNC: crate::gpio::PortFunction = crate::gpio::PortFunction::$pfunc;
132        }
133    };
134}
135
136#[cfg(not(ra6m5))]
137pub(crate) use dp_pin;
138
139pub(crate) trait SealedDmPin<I: SealedInstance>: Pin + PeripheralType {
140    // const PERIPHERAL_FUNC: PortFunction;
141
142    #[inline(always)]
143    fn set_pfunc(&self) {
144        /*
145         * Technically, we should probably set the peripheral function for the pin; however,
146         * Table 19.17 shows that the pins used for DP and DM don't care about the `PSEL` settings.
147         * Furthermore, if we set a value for pins P914 and P915, the readback fails as they read
148         * as HiZ (0x00).
149         *
150         * Leaving this as a vestigial remnant for reference in other chips.
151         */
152        //self.set_as_pf(Self::PERIPHERAL_FUNC);
153    }
154}
155
156/// A pin that can be used for the differential positive input.
157#[allow(private_bounds)]
158pub trait DmPin<I: Instance>: SealedDmPin<I> {}
159
160#[allow(unused)]
161macro_rules! dm_pin {
162    ($instance:ident, $pin:ident, $pfunc:ident) => {
163        impl crate::usb::DmPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
164        impl crate::usb::SealedDmPin<crate::peripherals::$instance> for crate::peripherals::$pin {
165            // const PERIPHERAL_FUNC: crate::gpio::PortFunction = crate::gpio::PortFunction::$pfunc;
166        }
167    };
168}
169
170#[cfg(not(ra6m5))]
171pub(crate) use dm_pin;
172
173pub(crate) trait SealedVbusPin<I: SealedInstance>: Pin + PeripheralType {
174    const PERIPHERAL_FUNC: PortFunction;
175
176    #[inline(always)]
177    fn set_pfunc(&self) {
178        self.set_as_pf(Self::PERIPHERAL_FUNC);
179    }
180}
181
182/// A pin that can be used for VBUS detection.
183#[allow(private_bounds)]
184pub trait VbusPin<I: Instance>: SealedVbusPin<I> {}
185
186macro_rules! vbus_pin {
187    ($instance:ident, $pin:ident, $pfunc:ident) => {
188        impl crate::usb::VbusPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
189        impl crate::usb::SealedVbusPin<crate::peripherals::$instance> for crate::peripherals::$pin {
190            const PERIPHERAL_FUNC: crate::gpio::PortFunction = crate::gpio::PortFunction::$pfunc;
191        }
192    };
193}
194
195pub(crate) use vbus_pin;
196
197impl SealedInstance for USBFS {
198    fn regs() -> pac::usbfs::Usbfs {
199        pac::USBFS
200    }
201}
202
203impl Instance for USBFS {}
204
205impl<I: Instance, Int: InterruptType> InterruptHandler<Int> for UsbInterruptHandler<I> {
206    unsafe fn on_interrupt() {
207        let r = I::regs();
208        Int::IRQ.icu_unpend();
209
210        let sts = r.intsts0().read();
211        trace!(
212            "USBFS IRQ: vbint={} dvst={} resm={} ctrt={} valid={} brdy={} bemp={}",
213            sts.vbint(),
214            sts.dvst(),
215            sts.resm(),
216            sts.ctrt(),
217            sts.valid(),
218            sts.brdy(),
219            sts.bemp(),
220        );
221
222        // VBUS change
223        if sts.vbint() {
224            r.intsts0().modify(|r| r.set_vbint(false));
225            if sts.vbsts() {
226                debug!("USBFS IRQ: VBUS detected");
227                STATE
228                    .pending_bus
229                    .fetch_or(BusEvent::PowerDetected as _, Ordering::Release);
230            } else {
231                debug!("USBFS IRQ: VBUS removed");
232                STATE
233                    .pending_bus
234                    .fetch_or(BusEvent::PowerRemoved as _, Ordering::Release);
235            }
236            STATE.bus_waker.wake();
237        }
238
239        // Device state transition (reset / suspend / configured)
240        if sts.dvst() {
241            r.intsts0().modify(|r| r.set_dvst(false));
242            match sts.dvsq() {
243                Dvsq::Default => {
244                    debug!("USBFS IRQ: bus reset (DVSQ=Default)");
245                    STATE
246                        .pending_bus
247                        .fetch_or(BusEvent::Reset as _, Ordering::Release);
248                    STATE.bus_waker.wake();
249                }
250                Dvsq::Suspend4 | Dvsq::Suspend5 | Dvsq::Suspend6 | Dvsq::Suspend7 => {
251                    debug!("USBFS IRQ: suspend (DVSQ={:04b})", sts.dvsq().to_bits());
252                    STATE
253                        .pending_bus
254                        .fetch_or(BusEvent::Suspend as _, Ordering::Release);
255                    STATE.bus_waker.wake();
256                }
257                other => {
258                    trace!("USBFS IRQ: DVST dvsq={:04b}", other.to_bits());
259                }
260            }
261        }
262
263        // Resume
264        if sts.resm() {
265            r.intsts0().modify(|r| r.set_resm(false));
266            debug!("USBFS IRQ: resume");
267            STATE
268                .pending_bus
269                .fetch_or(BusEvent::Resume as _, Ordering::Release);
270            STATE.bus_waker.wake();
271        }
272
273        // Control transfer stage transition
274        if sts.ctrt() {
275            r.intsts0().modify(|r| r.set_ctrt(false));
276            if sts.valid() {
277                // Latch setup packet before clearing VALID
278                let req = r.usbreq().read();
279                let val = r.usbval().read();
280                let idx = r.usbindx().read();
281                let len = r.usbleng().read();
282
283                let lo = u32::from(req.bmrequesttype())
284                    | (u32::from(req.brequest()) << 8)
285                    | ((val.wvalue() as u32) << 16);
286                let hi = idx.windex() as u32 | ((len.wlentuh() as u32) << 16);
287
288                debug!(
289                    "USBFS IRQ: SETUP bmrt={:08b} req={:#04x} val={:#06x} idx={:#06x} len={}",
290                    req.bmrequesttype(),
291                    req.brequest(),
292                    val.wvalue(),
293                    idx.windex(),
294                    len.wlentuh(),
295                );
296
297                STATE.setup_lo.store(lo, Ordering::Relaxed);
298                STATE.setup_hi.store(hi, Ordering::Relaxed);
299                STATE.setup_ready.store(true, Ordering::Release);
300                // Clear VALID so hardware can accept the next SETUP
301                r.intsts0().modify(|r| r.set_valid(false));
302                STATE.ep_wakers[0].wake();
303            } else {
304                trace!("USBFS IRQ: CTRT without VALID (status stage)");
305            }
306        }
307
308        // Buffer ready (data received into pipe FIFO)
309        if sts.brdy() {
310            let brdy = r.brdysts().read();
311            let mut mask = 0u32;
312            for i in 0..10usize {
313                if brdy.brdy(i) {
314                    // Clear hardware flag to stop interrupt from re-firing
315                    r.brdysts().modify(|r| r.set_brdy(i, false));
316                    mask |= 1 << i;
317                }
318            }
319            trace!("USBFS IRQ: BRDY pipes={:010b}", mask);
320            STATE.pipe_brdy.fetch_or(mask, Ordering::Release);
321            for i in 0..10usize {
322                if mask & (1 << i) != 0 {
323                    STATE.ep_wakers[i].wake();
324                }
325            }
326        }
327
328        // Buffer empty (pipe transmit FIFO drained / data sent)
329        if sts.bemp() {
330            let bemp = r.bempsts().read();
331            let mut mask = 0u32;
332            for i in 0..10usize {
333                if bemp.bemp(i) {
334                    r.bempsts().modify(|r| r.set_bemp(i, false));
335                    mask |= 1 << i;
336                }
337            }
338            trace!("USBFS IRQ: BEMP pipes={:010b}", mask);
339            STATE.pipe_bemp.fetch_or(mask, Ordering::Release);
340            for i in 0..10usize {
341                if mask & (1 << i) != 0 {
342                    STATE.ep_wakers[i].wake();
343                }
344            }
345        }
346    }
347}
348
349// TODO: Come up with a better way to gate the dedicated dp/dm pins.
350impl<'a, I: Instance> Usbfs<'a, I> {
351    /// Creates a new USB driver.
352    pub fn new<
353        Int: InterruptType,
354        #[cfg(not(ra6m5))] P: DpPin<I>,
355        #[cfg(not(ra6m5))] M: DmPin<I>,
356        V: VbusPin<I>,
357    >(
358        _usb: Peri<'a, I>,
359        #[cfg(not(ra6m5))] dp: Peri<'a, P>,
360        #[cfg(not(ra6m5))] dm: Peri<'a, M>,
361        vbus: Peri<'a, V>,
362        irqs: impl Binding<Int, UsbInterruptHandler<I>> + 'a,
363    ) -> Self {
364        let _ = irqs;
365
366        // Manual section 27.4.1: Releasing the module-stop state enables access to the registers.
367        I::start_module();
368
369        #[cfg(not(ra6m5))]
370        {
371            dp.set_pfunc();
372            dm.set_pfunc();
373        }
374        vbus.set_pfunc();
375
376        // Safety: interrupt handler is bound above.
377        unsafe {
378            Int::IRQ.enable();
379            Int::IRQ.icu_enable(InterruptEvent::UsbfsInt);
380        }
381
382        info!("USBFS: driver created, interrupt enabled");
383
384        Self {
385            _phantom: PhantomData,
386            pipes: [None; 9],
387            next_ep_in: 1,
388            next_ep_out: 1,
389        }
390    }
391}
392
393impl<'a, I: Instance> Driver<'a> for Usbfs<'a, I> {
394    type EndpointOut = EndpointOut<'a, I>;
395    type EndpointIn = EndpointIn<'a, I>;
396    type ControlPipe = ControlPipe<'a, I>;
397    type Bus = Bus<'a, I>;
398
399    fn alloc_endpoint_out(
400        &mut self,
401        ep_type: EndpointType,
402        ep_addr: Option<EndpointAddress>,
403        max_packet_size: u16,
404        interval_ms: u8,
405    ) -> Result<Self::EndpointOut, EndpointAllocError> {
406        let pipe = find_free_pipe(&self.pipes, ep_type).ok_or(EndpointAllocError)?;
407
408        let ep_num = if let Some(addr) = ep_addr {
409            if addr.is_in() {
410                return Err(EndpointAllocError);
411            }
412            addr.index() as u8
413        } else {
414            let n = self.next_ep_out;
415            self.next_ep_out += 1;
416            n
417        };
418
419        let addr = EndpointAddress::from_parts(ep_num as usize, Direction::Out);
420        self.pipes[pipe - 1] = Some(PipeConfig {
421            ep_addr: addr,
422            ep_type,
423            max_packet: max_packet_size,
424            interval_ms,
425        });
426
427        Ok(EndpointOut {
428            _phantom: PhantomData,
429            info: EndpointInfo {
430                addr,
431                ep_type,
432                max_packet_size,
433                interval_ms,
434            },
435            pipe: pipe as u8,
436        })
437    }
438
439    fn alloc_endpoint_in(
440        &mut self,
441        ep_type: EndpointType,
442        ep_addr: Option<EndpointAddress>,
443        max_packet_size: u16,
444        interval_ms: u8,
445    ) -> Result<Self::EndpointIn, EndpointAllocError> {
446        let pipe = find_free_pipe(&self.pipes, ep_type).ok_or(EndpointAllocError)?;
447
448        let ep_num = if let Some(addr) = ep_addr {
449            if addr.is_out() {
450                return Err(EndpointAllocError);
451            }
452            addr.index() as u8
453        } else {
454            let n = self.next_ep_in;
455            self.next_ep_in += 1;
456            n
457        };
458
459        let addr = EndpointAddress::from_parts(ep_num as usize, Direction::In);
460        self.pipes[pipe - 1] = Some(PipeConfig {
461            ep_addr: addr,
462            ep_type,
463            max_packet: max_packet_size,
464            interval_ms,
465        });
466
467        Ok(EndpointIn {
468            _phantom: PhantomData,
469            info: EndpointInfo {
470                addr,
471                ep_type,
472                max_packet_size,
473                interval_ms,
474            },
475            pipe: pipe as u8,
476        })
477    }
478
479    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
480        let r = I::regs();
481
482        info!("USBFS: start (ctrl max_packet={})", control_max_packet_size);
483        /*
484         * Section 27.3.1.1: Enable the USB clock gate first (SCKE=1). When SCKE=0, only SYSCFG
485         * may be written (Section 27.2.1, `SCKE` bit).
486         */
487        r.syscfg().modify(|r| r.set_scke(true));
488
489        // Brief spin to let the clock stabilise before touching other USB regs.
490        for _ in 0..200 {
491            cortex_m::asm::nop();
492        }
493
494        // Section 27.2.1 Note 2: Read and confirm SCKE=1
495        if !r.syscfg().read().scke() {
496            panic!("USBFS: SCKE=0 after SCKE=1 write");
497        }
498
499        /*
500         * Section 27.2.34: Enable the internal USB LDO regulator (VDCEN=1) before starting the
501         * transceiver. Without this, the USB transceiver has no power supply (Section 27.3.1.4,
502         * Figure 27.4), and DPRPU has no physical effect (DP stays at SE0 (LNST=00) regardless of
503         * SYSCFG settings).
504         *
505         * Note: This depends on the hardware design (see Figure 27.2). `VCC_USB` and `VCC_USB_LDO`
506         * can be connected together to `VCC` when `VCC` is between 3.0V and 3.6V.
507         */
508        #[cfg(usbfs_4m1)]
509        {
510            r.usbmc().modify(|r| {
511                r.set_vdcen(true);
512            });
513            debug!("USBFS: USBMC written (VDCEN=1)");
514        }
515
516        // Errata note: the PAC exposes `USBFS.UCKSEL`. This register doesn't appear in the manual.
517
518        // Allow the LDO output to stabilise before enabling the transceiver.
519        for _ in 0..1000 {
520            cortex_m::asm::nop();
521        }
522
523        r.syscfg().modify(|r| r.set_usbe(true));
524        // Device mode, not host
525        r.syscfg().modify(|r| r.set_dcfm(false));
526        debug!("USBFS: SYSCFG after init={:?}", r.syscfg().read());
527
528        // Default control pipe max packet size
529        r.dcpmaxp()
530            .modify(|r| r.set_mxps(control_max_packet_size as u8));
531
532        // Configure allocated data pipes
533        for (idx, slot) in self.pipes.iter().enumerate() {
534            let Some(cfg) = slot else { continue };
535            let pipe = (idx + 1) as u8;
536
537            info!(
538                "USBFS: pipe {} epnum={} dir={} type={:02b} mxps={}",
539                pipe,
540                cfg.ep_addr.index(),
541                cfg.ep_addr.is_in(),
542                pipe_type(cfg.ep_type).to_bits(),
543                cfg.max_packet,
544            );
545
546            // Select the pipe to configure via the windowed registers
547            r.pipesel().modify(|r| {
548                r.set_pipesel(Pipesel::from_bits(pipe));
549            });
550
551            let is_in = cfg.ep_addr.is_in();
552            let is_bulk = cfg.ep_type == EndpointType::Bulk;
553            let is_double_buffered = is_bulk && pipe <= 5; // double-buffer for bulk pipes
554
555            r.pipecfg().modify(|r| {
556                r.set_epnum(cfg.ep_addr.index() as u8);
557                r.set_dir(is_in); // true = IN (device -> host)
558                r.set_type_(pipe_type(cfg.ep_type));
559                r.set_dblb(is_double_buffered);
560                // SHTNAK: auto-NAK after transfer for OUT bulk pipes
561                r.set_shtnak(!is_in && is_bulk);
562            });
563
564            r.pipemaxp().modify(|r| r.set_mxps(cfg.max_packet));
565
566            if cfg.ep_type == EndpointType::Interrupt {
567                // interval_ms is a power-of-two exponent for the RA4M1 interval field
568                let iitv = cfg.interval_ms.trailing_zeros().min(7) as u8;
569                r.pipeperi().modify(|r| r.set_iitv(iitv));
570            }
571
572            // Reset sequence toggle and clear FIFO
573            r.pipectr(idx).modify(|r| {
574                r.set_sqclr(true);
575                r.set_aclrm(true);
576            });
577            r.pipectr(idx).modify(|r| r.set_aclrm(false));
578
579            // Store ep_addr to pipe mapping for Bus methods
580            EP_ADDR[idx].store(u8::from(cfg.ep_addr), Ordering::Release);
581        }
582
583        // Enable global interrupt sources
584        r.intenb0().modify(|r| {
585            r.set_brdye(true);
586            r.set_bempe(true);
587            r.set_ctre(true);
588            r.set_dvse(true);
589            r.set_rsme(true);
590            r.set_vbse(true);
591        });
592
593        // Enable BEMP for pipe 0 (DCP) so the control pipe write path works
594        r.bempenb().modify(|r| r.set_bemp(0, true));
595        // Enable BRDY for pipe 0 so the control pipe read path works
596        r.brdyenb().modify(|r| r.set_brdy(0, true));
597
598        /*
599         * Section 27.4.3: clear INTSTS0 and INTSTS1 after port setup to avoid spurious interrupts
600         * from pin state changes during initialisation.
601         */
602        r.intsts0().write_value(Default::default());
603        r.intsts1().write_value(Default::default());
604
605        /*
606         * Section 27.3.3.7 If the USB peripheral is already powered, the VBUS signal will be present.
607         * Pretend that a PowerDetected event has occurred since VBINT only fires on transitions.
608         * Read VBSTS three times and confirm a consistent value.
609         */
610        let vbsts = {
611            let a = r.intsts0().read().vbsts();
612            let b = r.intsts0().read().vbsts();
613            let c = r.intsts0().read().vbsts();
614            // Use majority vote; if two of the three agree, we have a stable reading.
615            (c || a) && b || (a && c)
616        };
617        debug!("USBFS: VBSTS={}", vbsts);
618        if vbsts {
619            info!("USBFS: VBUS already present, synthesizing PowerDetected");
620            STATE
621                .pending_bus
622                .fetch_or(BusEvent::PowerDetected as _, Ordering::Release);
623        }
624
625        let max_packet_size = control_max_packet_size;
626        (
627            Bus {
628                _phantom: PhantomData,
629            },
630            ControlPipe {
631                _phantom: PhantomData,
632                max_packet_size,
633            },
634        )
635    }
636}
637
638/// Return the pipe number (1-9) suitable for the requested transfer type, or
639/// `None` if no compatible free pipe is available.
640fn find_free_pipe(pipes: &[Option<PipeConfig>; 9], ep_type: EndpointType) -> Option<usize> {
641    for (i, cfg) in pipes.iter().enumerate() {
642        if cfg.is_some() {
643            continue;
644        }
645        let pipe = i + 1; // pipe numbers 1-9
646        let ok = match ep_type {
647            EndpointType::Bulk => pipe <= 5,
648            EndpointType::Interrupt => pipe >= 6,
649            EndpointType::Isochronous => pipe <= 2,
650            EndpointType::Control => false,
651        };
652        if ok {
653            return Some(pipe);
654        }
655    }
656    None
657}
658
659// Because of orphan rules
660fn pipe_type(ep_type: EndpointType) -> Type {
661    match ep_type {
662        EndpointType::Bulk => Type::_01,
663        EndpointType::Interrupt => Type::_10,
664        EndpointType::Isochronous => Type::_11,
665        EndpointType::Control => Type::_00,
666    }
667}