lpc55_hal/peripherals/
usbhs.rs

1use core::ops::Deref;
2use embedded_time::duration::Extensions;
3
4use crate::drivers::timer;
5use crate::peripherals::{anactrl, ctimer, pmc, syscon};
6use crate::raw;
7use crate::traits::wg::timer::CountDown;
8use crate::typestates::{
9    init_state,
10    usbhs_mode,
11    // ValidUsbClockToken,
12    // Fro96MHzEnabledToken,
13    ClocksSupportUsbhsToken,
14};
15
16use crate::traits::usb::{Usb, UsbSpeed};
17
18// Main struct
19pub struct Usbhs<
20    State: init_state::InitState = init_state::Unknown,
21    Mode: usbhs_mode::UsbhsMode = usbhs_mode::Unknown,
22> {
23    pub(crate) raw_phy: raw::USBPHY,
24    pub(crate) raw_hsd: raw::USB1,
25    pub(crate) raw_hsh: raw::USBHSH,
26    _state: State,
27    _mode: Mode,
28}
29
30pub type EnabledUsbhsDevice = Usbhs<init_state::Enabled, usbhs_mode::Device>;
31pub type EnabledUsbhsHost = Usbhs<init_state::Enabled, usbhs_mode::Host>;
32
33impl Deref for EnabledUsbhsDevice {
34    type Target = raw::usb1::RegisterBlock;
35    fn deref(&self) -> &Self::Target {
36        &self.raw_hsd
37    }
38}
39
40unsafe impl Sync for EnabledUsbhsDevice {}
41
42impl Usb<init_state::Enabled> for EnabledUsbhsDevice {
43    const SPEED: UsbSpeed = UsbSpeed::HighSpeed;
44    // const NUM_ENDPOINTS: usize = 1 + 5;
45}
46
47impl Usbhs {
48    pub fn new(raw_phy: raw::USBPHY, raw_hsd: raw::USB1, raw_hsh: raw::USBHSH) -> Self {
49        Usbhs {
50            raw_phy,
51            raw_hsd,
52            raw_hsh,
53            _state: init_state::Unknown,
54            _mode: usbhs_mode::Unknown,
55        }
56    }
57}
58
59impl<State: init_state::InitState, Mode: usbhs_mode::UsbhsMode> Usbhs<State, Mode> {
60    pub fn release(self) -> (raw::USB1, raw::USBHSH) {
61        (self.raw_hsd, self.raw_hsh)
62    }
63
64    pub fn enabled_as_device(
65        mut self,
66        anactrl: &mut anactrl::Anactrl,
67        pmc: &mut pmc::Pmc,
68        syscon: &mut syscon::Syscon,
69        timer: &mut timer::Timer<impl ctimer::Ctimer<init_state::Enabled>>,
70        // lock_fro_to_sof: bool, // we always lock to SOF
71        _clocks_token: ClocksSupportUsbhsToken,
72    ) -> EnabledUsbhsDevice {
73        // Reset devices
74        syscon.reset(&mut self.raw_hsh);
75        syscon.reset(&mut self.raw_hsd);
76        syscon.reset(&mut self.raw_phy);
77
78        // Briefly turn on host controller to enable device control of USB1 port
79        syscon.enable_clock(&mut self.raw_hsh);
80
81        self.raw_hsh
82            .portmode
83            .modify(|_, w| w.dev_enable().set_bit());
84
85        syscon.disable_clock(&mut self.raw_hsh);
86
87        // Power on 32M crystal for HS PHY and connect to USB PLL
88        pmc.raw
89            .pdruncfg0
90            .modify(|_, w| w.pden_xtal32m().poweredon());
91        pmc.raw
92            .pdruncfg0
93            .modify(|_, w| w.pden_ldoxo32m().poweredon());
94        anactrl
95            .raw
96            .xo32m_ctrl
97            .modify(|_, w| w.enable_pll_usb_out().set_bit());
98
99        pmc.power_on(&mut self.raw_phy);
100
101        // Give long delay for PHY to be ready
102        timer.start(5000_u32.microseconds());
103        nb::block!(timer.wait()).ok();
104
105        syscon.enable_clock(&mut self.raw_phy);
106
107        // Initial config of PHY control registers
108        self.raw_phy.ctrl.write(|w| w.sftrst().clear_bit());
109
110        self.raw_phy.pll_sic.modify(|_, w| {
111            w.pll_div_sel()
112                .bits(6) /* 16MHz = xtal32m */
113                .pll_reg_enable()
114                .set_bit()
115        });
116
117        self.raw_phy.pll_sic_clr.write(|w| unsafe {
118            // must be done, according to SDK.
119            w.bits(1 << 16 /* mystery bit */)
120        });
121
122        // Must wait at least 15 us for pll-reg to stabilize
123        timer.start(15.microseconds());
124        nb::block!(timer.wait()).ok();
125
126        self.raw_phy
127            .pll_sic
128            .modify(|_, w| w.pll_power().set_bit().pll_en_usb_clks().set_bit());
129
130        self.raw_phy.ctrl.modify(|_, w| {
131            w.clkgate()
132                .clear_bit()
133                .enautoclr_clkgate()
134                .set_bit()
135                .enautoclr_phy_pwd()
136                .clear_bit()
137        });
138
139        // Turn on everything in PHY
140        self.raw_phy.pwd.write(|w| unsafe { w.bits(0) });
141
142        // turn on USB1 device controller access
143        syscon.enable_clock(&mut self.raw_hsd);
144
145        Usbhs {
146            raw_phy: self.raw_phy,
147            raw_hsd: self.raw_hsd,
148            raw_hsh: self.raw_hsh,
149            _state: init_state::Enabled(()),
150            _mode: usbhs_mode::Device,
151        }
152    }
153
154    pub fn borrow<F: Fn(&mut Self)>(&mut self, func: F) {
155        func(self);
156    }
157}
158
159#[derive(Debug)]
160pub struct UsbHsDevInfo {
161    pub maj_rev: u8,
162    pub min_rev: u8,
163    pub err_code: u8,
164    pub frame_nr: u16,
165}
166
167impl EnabledUsbhsDevice {
168    pub fn info(&self) -> UsbHsDevInfo {
169        // technically, e.g. maj/min rev need only the clock, and not the power enabled
170        UsbHsDevInfo {
171            maj_rev: self.raw_hsd.info.read().majrev().bits(),
172            min_rev: self.raw_hsd.info.read().minrev().bits(),
173            err_code: self.raw_hsd.info.read().err_code().bits(),
174            frame_nr: self.raw_hsd.info.read().frame_nr().bits(),
175        }
176    }
177
178    pub fn disable_high_speed(&mut self) {
179        // Note: Application Note https://www.nxp.com/docs/en/application-note/TN00071.zip
180        // states that devcmdstat.force_fs (bit 21) might also be used.
181        self.raw_phy.pwd_set.write(|w| unsafe {
182            w.bits(1 << 12) /* TXPWDV2I */
183        });
184    }
185}
186
187impl<State: init_state::InitState> Usbhs<State, usbhs_mode::Device> {
188    /// Disables the USB HS peripheral, assumed in device mode
189    pub fn disabled(
190        mut self,
191        pmc: &mut pmc::Pmc,
192        syscon: &mut syscon::Syscon,
193    ) -> Usbhs<init_state::Disabled, usbhs_mode::Device> {
194        syscon.disable_clock(&mut self.raw_hsd);
195
196        syscon.disable_clock(&mut self.raw_phy);
197
198        pmc.power_off(&mut self.raw_phy);
199
200        pmc.raw
201            .pdruncfg0
202            .modify(|_, w| w.pden_xtal32m().poweredoff());
203        pmc.raw
204            .pdruncfg0
205            .modify(|_, w| w.pden_ldoxo32m().poweredoff());
206
207        Usbhs {
208            raw_phy: self.raw_phy,
209            raw_hsd: self.raw_hsd,
210            raw_hsh: self.raw_hsh,
211            _state: init_state::Disabled,
212            _mode: usbhs_mode::Device,
213        }
214    }
215}
216
217impl From<(raw::USBPHY, raw::USB1, raw::USBHSH)> for Usbhs {
218    fn from(raw: (raw::USBPHY, raw::USB1, raw::USBHSH)) -> Self {
219        Usbhs::new(raw.0, raw.1, raw.2)
220    }
221}