1 | use core::marker::PhantomData; |
2 | |
3 | use embassy_hal_internal::{into_ref, Peripheral}; |
4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; |
5 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; |
6 | use embassy_usb_synopsys_otg::otg_v1::Otg; |
7 | pub use embassy_usb_synopsys_otg::Config; |
8 | use embassy_usb_synopsys_otg::{ |
9 | on_interrupt as on_interrupt_impl, Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, |
10 | PhyType, State, |
11 | }; |
12 | |
13 | use crate::gpio::{AfType, OutputType, Speed}; |
14 | use crate::interrupt; |
15 | use crate::interrupt::typelevel::Interrupt; |
16 | use crate::rcc::{self, RccPeripheral}; |
17 | |
18 | const MAX_EP_COUNT: usize = 9; |
19 | |
20 | /// Interrupt handler. |
21 | pub struct InterruptHandler<T: Instance> { |
22 | _phantom: PhantomData<T>, |
23 | } |
24 | |
25 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
26 | unsafe fn on_interrupt() { |
27 | let r: Otg = T::regs(); |
28 | let state: &'static State<_> = T::state(); |
29 | on_interrupt_impl(r, state, T::ENDPOINT_COUNT); |
30 | } |
31 | } |
32 | |
33 | macro_rules! config_ulpi_pins { |
34 | ($($pin:ident),*) => { |
35 | into_ref!($($pin),*); |
36 | critical_section::with(|_| { |
37 | $( |
38 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
39 | )* |
40 | }) |
41 | }; |
42 | } |
43 | |
44 | // From `synopsys-usb-otg` crate: |
45 | // This calculation doesn't correspond to one in a Reference Manual. |
46 | // In fact, the required number of words is higher than indicated in RM. |
47 | // The following numbers are pessimistic and were figured out empirically. |
48 | const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30; |
49 | |
50 | /// USB driver. |
51 | pub struct Driver<'d, T: Instance> { |
52 | phantom: PhantomData<&'d mut T>, |
53 | inner: OtgDriver<'d, MAX_EP_COUNT>, |
54 | } |
55 | |
56 | impl<'d, T: Instance> Driver<'d, T> { |
57 | /// Initializes USB OTG peripheral with internal Full-Speed PHY. |
58 | /// |
59 | /// # Arguments |
60 | /// |
61 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. |
62 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
63 | /// Endpoint allocation will fail if it is too small. |
64 | pub fn new_fs( |
65 | _peri: impl Peripheral<P = T> + 'd, |
66 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
67 | dp: impl Peripheral<P = impl DpPin<T>> + 'd, |
68 | dm: impl Peripheral<P = impl DmPin<T>> + 'd, |
69 | ep_out_buffer: &'d mut [u8], |
70 | config: Config, |
71 | ) -> Self { |
72 | into_ref!(dp, dm); |
73 | |
74 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
75 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
76 | |
77 | let regs = T::regs(); |
78 | |
79 | let instance = OtgInstance { |
80 | regs, |
81 | state: T::state(), |
82 | fifo_depth_words: T::FIFO_DEPTH_WORDS, |
83 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
84 | endpoint_count: T::ENDPOINT_COUNT, |
85 | phy_type: PhyType::InternalFullSpeed, |
86 | calculate_trdt_fn: calculate_trdt::<T>, |
87 | }; |
88 | |
89 | Self { |
90 | inner: OtgDriver::new(ep_out_buffer, instance, config), |
91 | phantom: PhantomData, |
92 | } |
93 | } |
94 | |
95 | /// Initializes USB OTG peripheral with internal High-Speed PHY. |
96 | /// |
97 | /// # Arguments |
98 | /// |
99 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. |
100 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
101 | /// Endpoint allocation will fail if it is too small. |
102 | pub fn new_hs( |
103 | _peri: impl Peripheral<P = T> + 'd, |
104 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
105 | _dp: impl Peripheral<P = impl DpPin<T>> + 'd, |
106 | _dm: impl Peripheral<P = impl DmPin<T>> + 'd, |
107 | ep_out_buffer: &'d mut [u8], |
108 | config: Config, |
109 | ) -> Self { |
110 | // For STM32U5 High speed pins need to be left in analog mode |
111 | #[cfg (not(all(stm32u5, peri_usb_otg_hs)))] |
112 | { |
113 | into_ref!(_dp, _dm); |
114 | _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
115 | _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
116 | } |
117 | |
118 | let instance = OtgInstance { |
119 | regs: T::regs(), |
120 | state: T::state(), |
121 | fifo_depth_words: T::FIFO_DEPTH_WORDS, |
122 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
123 | endpoint_count: T::ENDPOINT_COUNT, |
124 | phy_type: PhyType::InternalHighSpeed, |
125 | calculate_trdt_fn: calculate_trdt::<T>, |
126 | }; |
127 | |
128 | Self { |
129 | inner: OtgDriver::new(ep_out_buffer, instance, config), |
130 | phantom: PhantomData, |
131 | } |
132 | } |
133 | |
134 | /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode). |
135 | /// |
136 | /// # Arguments |
137 | /// |
138 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. |
139 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
140 | /// Endpoint allocation will fail if it is too small. |
141 | pub fn new_fs_ulpi( |
142 | _peri: impl Peripheral<P = T> + 'd, |
143 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
144 | ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd, |
145 | ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd, |
146 | ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd, |
147 | ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd, |
148 | ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd, |
149 | ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd, |
150 | ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd, |
151 | ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd, |
152 | ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd, |
153 | ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd, |
154 | ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd, |
155 | ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd, |
156 | ep_out_buffer: &'d mut [u8], |
157 | config: Config, |
158 | ) -> Self { |
159 | config_ulpi_pins!( |
160 | ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, |
161 | ulpi_d7 |
162 | ); |
163 | |
164 | let instance = OtgInstance { |
165 | regs: T::regs(), |
166 | state: T::state(), |
167 | fifo_depth_words: T::FIFO_DEPTH_WORDS, |
168 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
169 | endpoint_count: T::ENDPOINT_COUNT, |
170 | phy_type: PhyType::ExternalFullSpeed, |
171 | calculate_trdt_fn: calculate_trdt::<T>, |
172 | }; |
173 | |
174 | Self { |
175 | inner: OtgDriver::new(ep_out_buffer, instance, config), |
176 | phantom: PhantomData, |
177 | } |
178 | } |
179 | |
180 | /// Initializes USB OTG peripheral with external High-Speed PHY. |
181 | /// |
182 | /// # Arguments |
183 | /// |
184 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. |
185 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
186 | /// Endpoint allocation will fail if it is too small. |
187 | pub fn new_hs_ulpi( |
188 | _peri: impl Peripheral<P = T> + 'd, |
189 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
190 | ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd, |
191 | ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd, |
192 | ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd, |
193 | ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd, |
194 | ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd, |
195 | ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd, |
196 | ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd, |
197 | ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd, |
198 | ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd, |
199 | ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd, |
200 | ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd, |
201 | ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd, |
202 | ep_out_buffer: &'d mut [u8], |
203 | config: Config, |
204 | ) -> Self { |
205 | assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB" ); |
206 | |
207 | config_ulpi_pins!( |
208 | ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, |
209 | ulpi_d7 |
210 | ); |
211 | |
212 | let instance = OtgInstance { |
213 | regs: T::regs(), |
214 | state: T::state(), |
215 | fifo_depth_words: T::FIFO_DEPTH_WORDS, |
216 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
217 | endpoint_count: T::ENDPOINT_COUNT, |
218 | phy_type: PhyType::ExternalHighSpeed, |
219 | calculate_trdt_fn: calculate_trdt::<T>, |
220 | }; |
221 | |
222 | Self { |
223 | inner: OtgDriver::new(ep_out_buffer, instance, config), |
224 | phantom: PhantomData, |
225 | } |
226 | } |
227 | } |
228 | |
229 | impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { |
230 | type EndpointOut = Endpoint<'d, Out>; |
231 | type EndpointIn = Endpoint<'d, In>; |
232 | type ControlPipe = ControlPipe<'d>; |
233 | type Bus = Bus<'d, T>; |
234 | |
235 | fn alloc_endpoint_in( |
236 | &mut self, |
237 | ep_type: EndpointType, |
238 | max_packet_size: u16, |
239 | interval_ms: u8, |
240 | ) -> Result<Self::EndpointIn, EndpointAllocError> { |
241 | self.inner.alloc_endpoint_in(ep_type, max_packet_size, interval_ms) |
242 | } |
243 | |
244 | fn alloc_endpoint_out( |
245 | &mut self, |
246 | ep_type: EndpointType, |
247 | max_packet_size: u16, |
248 | interval_ms: u8, |
249 | ) -> Result<Self::EndpointOut, EndpointAllocError> { |
250 | self.inner.alloc_endpoint_out(ep_type, max_packet_size, interval_ms) |
251 | } |
252 | |
253 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { |
254 | let (bus, cp) = self.inner.start(control_max_packet_size); |
255 | |
256 | ( |
257 | Bus { |
258 | phantom: PhantomData, |
259 | inner: bus, |
260 | inited: false, |
261 | }, |
262 | cp, |
263 | ) |
264 | } |
265 | } |
266 | |
267 | /// USB bus. |
268 | pub struct Bus<'d, T: Instance> { |
269 | phantom: PhantomData<&'d mut T>, |
270 | inner: OtgBus<'d, MAX_EP_COUNT>, |
271 | inited: bool, |
272 | } |
273 | |
274 | impl<'d, T: Instance> Bus<'d, T> { |
275 | fn init(&mut self) { |
276 | super::common_init::<T>(); |
277 | |
278 | // Enable ULPI clock if external PHY is used |
279 | let phy_type = self.inner.phy_type(); |
280 | let _ulpien = !phy_type.internal(); |
281 | |
282 | #[cfg (any(stm32f2, stm32f4, stm32f7))] |
283 | if T::HIGH_SPEED { |
284 | critical_section::with(|_| { |
285 | let rcc = crate::pac::RCC; |
286 | rcc.ahb1enr().modify(|w| w.set_usb_otg_hsulpien(_ulpien)); |
287 | rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hsulpilpen(_ulpien)); |
288 | }); |
289 | } |
290 | |
291 | #[cfg (stm32h7)] |
292 | critical_section::with(|_| { |
293 | let rcc = crate::pac::RCC; |
294 | if T::HIGH_SPEED { |
295 | rcc.ahb1enr().modify(|w| w.set_usb_otg_hs_ulpien(_ulpien)); |
296 | rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hs_ulpilpen(_ulpien)); |
297 | } else { |
298 | rcc.ahb1enr().modify(|w| w.set_usb_otg_fs_ulpien(_ulpien)); |
299 | rcc.ahb1lpenr().modify(|w| w.set_usb_otg_fs_ulpilpen(_ulpien)); |
300 | } |
301 | }); |
302 | |
303 | #[cfg (stm32h7rs)] |
304 | critical_section::with(|_| { |
305 | let rcc = crate::pac::RCC; |
306 | rcc.ahb1enr().modify(|w| { |
307 | w.set_usbphycen(true); |
308 | w.set_usb_otg_hsen(true); |
309 | }); |
310 | rcc.ahb1lpenr().modify(|w| { |
311 | w.set_usbphyclpen(true); |
312 | w.set_usb_otg_hslpen(true); |
313 | }); |
314 | }); |
315 | |
316 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
317 | { |
318 | crate::pac::SYSCFG.otghsphycr().modify(|w| { |
319 | w.set_en(true); |
320 | }); |
321 | |
322 | critical_section::with(|_| { |
323 | crate::pac::RCC.ahb2enr1().modify(|w| { |
324 | w.set_usb_otg_hsen(true); |
325 | w.set_usb_otg_hs_phyen(true); |
326 | }); |
327 | }); |
328 | } |
329 | |
330 | let r = T::regs(); |
331 | let core_id = r.cid().read().0; |
332 | trace!("Core id {:08x}" , core_id); |
333 | |
334 | // Wait for AHB ready. |
335 | while !r.grstctl().read().ahbidl() {} |
336 | |
337 | // Configure as device. |
338 | self.inner.configure_as_device(); |
339 | |
340 | // Configuring Vbus sense and SOF output |
341 | match core_id { |
342 | 0x0000_1200 | 0x0000_1100 => self.inner.config_v1(), |
343 | 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), |
344 | 0x0000_5000 => self.inner.config_v5(), |
345 | _ => unimplemented!("Unknown USB core id {:X}" , core_id), |
346 | } |
347 | } |
348 | |
349 | fn disable(&mut self) { |
350 | T::Interrupt::disable(); |
351 | |
352 | rcc::disable::<T>(); |
353 | self.inited = false; |
354 | |
355 | #[cfg (stm32l4)] |
356 | crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); |
357 | // Cannot disable PWR, because other peripherals might be using it |
358 | } |
359 | } |
360 | |
361 | impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { |
362 | async fn poll(&mut self) -> Event { |
363 | if !self.inited { |
364 | self.init(); |
365 | self.inited = true; |
366 | } |
367 | |
368 | self.inner.poll().await |
369 | } |
370 | |
371 | fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { |
372 | self.inner.endpoint_set_stalled(ep_addr, stalled) |
373 | } |
374 | |
375 | fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { |
376 | self.inner.endpoint_is_stalled(ep_addr) |
377 | } |
378 | |
379 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { |
380 | self.inner.endpoint_set_enabled(ep_addr, enabled) |
381 | } |
382 | |
383 | async fn enable(&mut self) { |
384 | self.inner.enable().await |
385 | } |
386 | |
387 | async fn disable(&mut self) { |
388 | // NOTE: inner call is a no-op |
389 | self.inner.disable().await |
390 | } |
391 | |
392 | async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { |
393 | self.inner.remote_wakeup().await |
394 | } |
395 | } |
396 | |
397 | impl<'d, T: Instance> Drop for Bus<'d, T> { |
398 | fn drop(&mut self) { |
399 | Bus::disable(self); |
400 | } |
401 | } |
402 | |
403 | trait SealedInstance { |
404 | const HIGH_SPEED: bool; |
405 | const FIFO_DEPTH_WORDS: u16; |
406 | const ENDPOINT_COUNT: usize; |
407 | |
408 | fn regs() -> Otg; |
409 | fn state() -> &'static State<{ MAX_EP_COUNT }>; |
410 | } |
411 | |
412 | /// USB instance trait. |
413 | #[allow (private_bounds)] |
414 | pub trait Instance: SealedInstance + RccPeripheral + 'static { |
415 | /// Interrupt for this USB instance. |
416 | type Interrupt: interrupt::typelevel::Interrupt; |
417 | } |
418 | |
419 | // Internal PHY pins |
420 | pin_trait!(DpPin, Instance); |
421 | pin_trait!(DmPin, Instance); |
422 | |
423 | // External PHY pins |
424 | pin_trait!(UlpiClkPin, Instance); |
425 | pin_trait!(UlpiDirPin, Instance); |
426 | pin_trait!(UlpiNxtPin, Instance); |
427 | pin_trait!(UlpiStpPin, Instance); |
428 | pin_trait!(UlpiD0Pin, Instance); |
429 | pin_trait!(UlpiD1Pin, Instance); |
430 | pin_trait!(UlpiD2Pin, Instance); |
431 | pin_trait!(UlpiD3Pin, Instance); |
432 | pin_trait!(UlpiD4Pin, Instance); |
433 | pin_trait!(UlpiD5Pin, Instance); |
434 | pin_trait!(UlpiD6Pin, Instance); |
435 | pin_trait!(UlpiD7Pin, Instance); |
436 | |
437 | foreach_interrupt!( |
438 | (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => { |
439 | impl SealedInstance for crate::peripherals::USB_OTG_FS { |
440 | const HIGH_SPEED: bool = false; |
441 | |
442 | cfg_if::cfg_if! { |
443 | if #[cfg(stm32f1)] { |
444 | const FIFO_DEPTH_WORDS: u16 = 128; |
445 | const ENDPOINT_COUNT: usize = 8; |
446 | } else if #[cfg(any( |
447 | stm32f2, |
448 | stm32f401, |
449 | stm32f405, |
450 | stm32f407, |
451 | stm32f411, |
452 | stm32f415, |
453 | stm32f417, |
454 | stm32f427, |
455 | stm32f429, |
456 | stm32f437, |
457 | stm32f439, |
458 | ))] { |
459 | const FIFO_DEPTH_WORDS: u16 = 320; |
460 | const ENDPOINT_COUNT: usize = 4; |
461 | } else if #[cfg(any( |
462 | stm32f412, |
463 | stm32f413, |
464 | stm32f423, |
465 | stm32f446, |
466 | stm32f469, |
467 | stm32f479, |
468 | stm32f7, |
469 | stm32l4, |
470 | stm32u5, |
471 | ))] { |
472 | const FIFO_DEPTH_WORDS: u16 = 320; |
473 | const ENDPOINT_COUNT: usize = 6; |
474 | } else if #[cfg(stm32g0x1)] { |
475 | const FIFO_DEPTH_WORDS: u16 = 512; |
476 | const ENDPOINT_COUNT: usize = 8; |
477 | } else if #[cfg(any(stm32h7, stm32h7rs))] { |
478 | const FIFO_DEPTH_WORDS: u16 = 1024; |
479 | const ENDPOINT_COUNT: usize = 9; |
480 | } else if #[cfg(stm32u5)] { |
481 | const FIFO_DEPTH_WORDS: u16 = 320; |
482 | const ENDPOINT_COUNT: usize = 6; |
483 | } else { |
484 | compile_error!("USB_OTG_FS peripheral is not supported by this chip." ); |
485 | } |
486 | } |
487 | |
488 | fn regs() -> Otg { |
489 | unsafe { Otg::from_ptr(crate::pac::USB_OTG_FS.as_ptr()) } |
490 | } |
491 | |
492 | fn state() -> &'static State<MAX_EP_COUNT> { |
493 | static STATE: State<MAX_EP_COUNT> = State::new(); |
494 | &STATE |
495 | } |
496 | } |
497 | |
498 | impl Instance for crate::peripherals::USB_OTG_FS { |
499 | type Interrupt = crate::interrupt::typelevel::$irq; |
500 | } |
501 | }; |
502 | |
503 | (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => { |
504 | impl SealedInstance for crate::peripherals::USB_OTG_HS { |
505 | const HIGH_SPEED: bool = true; |
506 | |
507 | cfg_if::cfg_if! { |
508 | if #[cfg(any( |
509 | stm32f2, |
510 | stm32f405, |
511 | stm32f407, |
512 | stm32f415, |
513 | stm32f417, |
514 | stm32f427, |
515 | stm32f429, |
516 | stm32f437, |
517 | stm32f439, |
518 | ))] { |
519 | const FIFO_DEPTH_WORDS: u16 = 1024; |
520 | const ENDPOINT_COUNT: usize = 6; |
521 | } else if #[cfg(any( |
522 | stm32f446, |
523 | stm32f469, |
524 | stm32f479, |
525 | stm32f7, |
526 | stm32h7, stm32h7rs, |
527 | ))] { |
528 | const FIFO_DEPTH_WORDS: u16 = 1024; |
529 | const ENDPOINT_COUNT: usize = 9; |
530 | } else if #[cfg(stm32u5)] { |
531 | const FIFO_DEPTH_WORDS: u16 = 1024; |
532 | const ENDPOINT_COUNT: usize = 9; |
533 | } else { |
534 | compile_error!("USB_OTG_HS peripheral is not supported by this chip." ); |
535 | } |
536 | } |
537 | |
538 | fn regs() -> Otg { |
539 | // OTG HS registers are a superset of FS registers |
540 | unsafe { Otg::from_ptr(crate::pac::USB_OTG_HS.as_ptr()) } |
541 | } |
542 | |
543 | fn state() -> &'static State<MAX_EP_COUNT> { |
544 | static STATE: State<MAX_EP_COUNT> = State::new(); |
545 | &STATE |
546 | } |
547 | } |
548 | |
549 | impl Instance for crate::peripherals::USB_OTG_HS { |
550 | type Interrupt = crate::interrupt::typelevel::$irq; |
551 | } |
552 | }; |
553 | ); |
554 | |
555 | fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { |
556 | let ahb_freq = T::frequency().0; |
557 | match speed { |
558 | Dspd::HIGH_SPEED => { |
559 | // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) |
560 | if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) { |
561 | 0x9 |
562 | } else { |
563 | panic!("AHB frequency is too low" ) |
564 | } |
565 | } |
566 | Dspd::FULL_SPEED_EXTERNAL | Dspd::FULL_SPEED_INTERNAL => { |
567 | // From RM0431 (F72xx), RM0090 (F429) |
568 | match ahb_freq { |
569 | 0..=14_199_999 => panic!("AHB frequency is too low" ), |
570 | 14_200_000..=14_999_999 => 0xF, |
571 | 15_000_000..=15_999_999 => 0xE, |
572 | 16_000_000..=17_199_999 => 0xD, |
573 | 17_200_000..=18_499_999 => 0xC, |
574 | 18_500_000..=19_999_999 => 0xB, |
575 | 20_000_000..=21_799_999 => 0xA, |
576 | 21_800_000..=23_999_999 => 0x9, |
577 | 24_000_000..=27_499_999 => 0x8, |
578 | 27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE |
579 | 32_000_000..=u32::MAX => 0x6, |
580 | } |
581 | } |
582 | _ => unimplemented!(), |
583 | } |
584 | } |
585 | |