mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-23 20:51:31 +11:00
Implements USB enumeration workaround (RP2040-E5). (#120)
* Implement rp2040-E5 workaround for usb enumeration. * Expand documentation and add to pico_usb_serial & pico_usb_twitchy_mouse * Fix errata-5 documentation around the bus-keep state. * Update CHANGELOG.md
This commit is contained in:
parent
9792408902
commit
7bfab4ffd2
2
.github/workflows/build_and_test.yml
vendored
2
.github/workflows/build_and_test.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
features: ["", "--features eh1_0_alpha", "--features chrono"]
|
features: ["", "--features eh1_0_alpha", "--features chrono", "--features rp2040-e5"]
|
||||||
mode: ["", "--release"]
|
mode: ["", "--release"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- None
|
- `rp2040-e5` feature enabling the workaround for errata 5 on the USB device peripheral.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -44,3 +44,4 @@ defmt-rtt = "0.3.0"
|
||||||
default = ["boot2", "rt"]
|
default = ["boot2", "rt"]
|
||||||
boot2 = ["rp2040-boot2"]
|
boot2 = ["rp2040-boot2"]
|
||||||
rt = ["cortex-m-rt","rp2040-hal/rt"]
|
rt = ["cortex-m-rt","rp2040-hal/rt"]
|
||||||
|
rp2040-e5 = ["rp2040-hal/rp2040-e5"]
|
||||||
|
|
|
@ -63,6 +63,17 @@ fn main() -> ! {
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
{
|
||||||
|
let sio = hal::Sio::new(pac.SIO);
|
||||||
|
let _pins = rp_pico::Pins::new(
|
||||||
|
pac.IO_BANK0,
|
||||||
|
pac.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the USB driver
|
// Set up the USB driver
|
||||||
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
||||||
pac.USBCTRL_REGS,
|
pac.USBCTRL_REGS,
|
||||||
|
|
|
@ -83,6 +83,17 @@ fn main() -> ! {
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
{
|
||||||
|
let sio = hal::Sio::new(pac.SIO);
|
||||||
|
let _pins = rp_pico::Pins::new(
|
||||||
|
pac.IO_BANK0,
|
||||||
|
pac.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the USB driver
|
// Set up the USB driver
|
||||||
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
|
||||||
pac.USBCTRL_REGS,
|
pac.USBCTRL_REGS,
|
||||||
|
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `rp2040-e5` feature enabling the workaround for errata 5 on the USB device peripheral.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Update embedded-hal alpha support to version 1.0.0-alpha.8
|
- Update embedded-hal alpha support to version 1.0.0-alpha.8
|
||||||
|
|
|
@ -45,6 +45,7 @@ rt = ["rp2040-pac/rt"]
|
||||||
rom-func-cache = []
|
rom-func-cache = []
|
||||||
disable-intrinsics = []
|
disable-intrinsics = []
|
||||||
rom-v2-intrinsics = []
|
rom-v2-intrinsics = []
|
||||||
|
rp2040-e5 = [] # USB errata 5: USB device fails to exit RESET state on busy USB bus.
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
# irq example uses cortex-m-rt::interrupt, need rt feature for that
|
# irq example uses cortex-m-rt::interrupt, need rt feature for that
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
//! const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||||
//!
|
//!
|
||||||
//! let mut pac = pac::Peripherals::take().unwrap();
|
//! let mut pac = pac::Peripherals::take().unwrap();
|
||||||
//! let sio = Sio::new(pac.SIO);
|
|
||||||
//! let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
//! let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||||
//! let mut clocks = init_clocks_and_plls(
|
//! let mut clocks = init_clocks_and_plls(
|
||||||
//! XOSC_CRYSTAL_FREQ,
|
//! XOSC_CRYSTAL_FREQ,
|
||||||
|
@ -39,7 +38,7 @@
|
||||||
//!
|
//!
|
||||||
//! During enumeration Windows hosts send a `StatusOut` after the `DataIn` packet of the first
|
//! During enumeration Windows hosts send a `StatusOut` after the `DataIn` packet of the first
|
||||||
//! `Get Descriptor` resquest even if the `DataIn` isn't completed (typically when the `max_packet_size_ep0`
|
//! `Get Descriptor` resquest even if the `DataIn` isn't completed (typically when the `max_packet_size_ep0`
|
||||||
//! is less than 18bytes). The next request request is a `Set Address` that expect a `StatusIn`.
|
//! is less than 18bytes). The next request is a `Set Address` that expect a `StatusIn`.
|
||||||
//!
|
//!
|
||||||
//! The issue is that by the time the previous `DataIn` packet is acknoledged and the `StatusOut`
|
//! The issue is that by the time the previous `DataIn` packet is acknoledged and the `StatusOut`
|
||||||
//! followed by `Setup` are received, the usb stack may have already prepared the next `DataIn` payload
|
//! followed by `Setup` are received, the usb stack may have already prepared the next `DataIn` payload
|
||||||
|
@ -54,6 +53,57 @@
|
||||||
//!
|
//!
|
||||||
//! If the required timing cannot be met, using an maximum packet size of the endpoint 0 above 18bytes
|
//! If the required timing cannot be met, using an maximum packet size of the endpoint 0 above 18bytes
|
||||||
//! (e.g. `.max_packet_size_ep0(64)`) should avoid that issue.
|
//! (e.g. `.max_packet_size_ep0(64)`) should avoid that issue.
|
||||||
|
//!
|
||||||
|
//! ## Issue on RP2040B0 and RP2040B1: USB device fails to exit RESET state on busy USB bus.
|
||||||
|
//!
|
||||||
|
//! The feature `rp2040-e5`implements the workaround described by [RP2040-E5](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#%5B%7B%22num%22%3A630%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C115%2C158.848%2Cnull%5D).
|
||||||
|
//!
|
||||||
|
//! The workaround requires the GPIO block to be released from its reset and has for side effect
|
||||||
|
//! that GPIO15 will be stolen for a few hundred microseconds each time a Reset is detected on the
|
||||||
|
//! USB bus.
|
||||||
|
//!
|
||||||
|
//! The pin will be temporarily put in "bus keep" mode, weakly pulling the output towards its current
|
||||||
|
//! logic level. In absence of external loads, the current logic level will be maintained.
|
||||||
|
//! A user will lose control of the pin's output and reading from it may not reflect the actual state
|
||||||
|
//! of the external pin.
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! # use rp2040_hal::{clocks::init_clocks_and_plls, pac, usb::UsbBus, watchdog::Watchdog};
|
||||||
|
//! # use usb_device::class_prelude::UsbBusAllocator;
|
||||||
|
//! use rp2040_hal::{gpio::Pins, Sio};
|
||||||
|
//!
|
||||||
|
//! # const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // Typically found in BSP crates
|
||||||
|
//! #
|
||||||
|
//! # let mut pac = pac::Peripherals::take().unwrap();
|
||||||
|
//! # let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||||
|
//! # let mut clocks = init_clocks_and_plls(
|
||||||
|
//! # XOSC_CRYSTAL_FREQ,
|
||||||
|
//! # pac.XOSC,
|
||||||
|
//! # pac.CLOCKS,
|
||||||
|
//! # pac.PLL_SYS,
|
||||||
|
//! # pac.PLL_USB,
|
||||||
|
//! # &mut pac.RESETS,
|
||||||
|
//! # &mut watchdog
|
||||||
|
//! # ).ok().unwrap();
|
||||||
|
//! #
|
||||||
|
//! // required for the errata 5's workaround to function properly.
|
||||||
|
//! let sio = Sio::new(pac.SIO);
|
||||||
|
//! let _pins = Pins::new(
|
||||||
|
//! pac.IO_BANK0,
|
||||||
|
//! pac.PADS_BANK0,
|
||||||
|
//! sio.gpio_bank0,
|
||||||
|
//! &mut pac.RESETS,
|
||||||
|
//! );
|
||||||
|
//! #
|
||||||
|
//! # let usb_bus = UsbBusAllocator::new(UsbBus::new(
|
||||||
|
//! # pac.USBCTRL_REGS,
|
||||||
|
//! # pac.USBCTRL_DPRAM,
|
||||||
|
//! # clocks.usb_clock,
|
||||||
|
//! # true,
|
||||||
|
//! # &mut pac.RESETS,
|
||||||
|
//! # ));
|
||||||
|
//! # // Use the usb_bus as usual.
|
||||||
|
//! ```
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
@ -71,6 +121,9 @@ use usb_device::{
|
||||||
Result as UsbResult, UsbDirection, UsbError,
|
Result as UsbResult, UsbDirection, UsbError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
mod errata5;
|
||||||
|
|
||||||
fn ep_addr_to_ep_buf_ctrl_idx(ep_addr: EndpointAddress) -> usize {
|
fn ep_addr_to_ep_buf_ctrl_idx(ep_addr: EndpointAddress) -> usize {
|
||||||
ep_addr.index() * 2 + (if ep_addr.is_in() { 0 } else { 1 })
|
ep_addr.index() * 2 + (if ep_addr.is_in() { 0 } else { 1 })
|
||||||
}
|
}
|
||||||
|
@ -114,6 +167,8 @@ struct Inner {
|
||||||
out_endpoints: [Option<Endpoint>; 16],
|
out_endpoints: [Option<Endpoint>; 16],
|
||||||
next_offset: u16,
|
next_offset: u16,
|
||||||
read_setup: bool,
|
read_setup: bool,
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
errata5_state: Option<errata5::Errata5State>,
|
||||||
}
|
}
|
||||||
impl Inner {
|
impl Inner {
|
||||||
fn new(ctrl_reg: USBCTRL_REGS, ctrl_dpram: USBCTRL_DPRAM) -> Self {
|
fn new(ctrl_reg: USBCTRL_REGS, ctrl_dpram: USBCTRL_DPRAM) -> Self {
|
||||||
|
@ -124,6 +179,8 @@ impl Inner {
|
||||||
out_endpoints: Default::default(),
|
out_endpoints: Default::default(),
|
||||||
next_offset: 0,
|
next_offset: 0,
|
||||||
read_setup: false,
|
read_setup: false,
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
errata5_state: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,22 +601,46 @@ impl UsbBusTrait for UsbBus {
|
||||||
interrupt::free(|cs| {
|
interrupt::free(|cs| {
|
||||||
let mut inner = self.inner.borrow(cs).borrow_mut();
|
let mut inner = self.inner.borrow(cs).borrow_mut();
|
||||||
|
|
||||||
// check for bus reset
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
if let Some(state) = inner.errata5_state.take() {
|
||||||
|
unsafe {
|
||||||
|
inner.errata5_state = state.update();
|
||||||
|
}
|
||||||
|
return if inner.errata5_state.is_some() {
|
||||||
|
PollResult::None
|
||||||
|
} else {
|
||||||
|
PollResult::Reset
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for bus reset and/or suspended states.
|
||||||
let sie_status = inner.ctrl_reg.sie_status.read();
|
let sie_status = inner.ctrl_reg.sie_status.read();
|
||||||
|
let mut buff_status = inner.ctrl_reg.buff_status.read().bits();
|
||||||
|
|
||||||
if sie_status.bus_reset().bit_is_set() {
|
if sie_status.bus_reset().bit_is_set() {
|
||||||
|
#[cfg(feature = "rp2040-e5")]
|
||||||
|
if sie_status.connected().bit_is_clear() {
|
||||||
|
inner.errata5_state = Some(errata5::Errata5State::start());
|
||||||
|
return PollResult::None;
|
||||||
|
} else {
|
||||||
return PollResult::Reset;
|
return PollResult::Reset;
|
||||||
} else if sie_status.suspended().bit_is_set() {
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rp2040-e5"))]
|
||||||
|
return PollResult::Reset;
|
||||||
|
} else if buff_status == 0 && sie_status.setup_rec().bit_is_clear() {
|
||||||
|
if sie_status.suspended().bit_is_set() {
|
||||||
inner.ctrl_reg.sie_status.write(|w| w.suspended().set_bit());
|
inner.ctrl_reg.sie_status.write(|w| w.suspended().set_bit());
|
||||||
return PollResult::Suspend;
|
return PollResult::Suspend;
|
||||||
} else if sie_status.resume().bit_is_set() {
|
} else if sie_status.resume().bit_is_set() {
|
||||||
inner.ctrl_reg.sie_status.write(|w| w.resume().set_bit());
|
inner.ctrl_reg.sie_status.write(|w| w.resume().set_bit());
|
||||||
return PollResult::Resume;
|
return PollResult::Resume;
|
||||||
}
|
}
|
||||||
|
return PollResult::None;
|
||||||
|
}
|
||||||
|
|
||||||
let (mut ep_out, mut ep_in_complete, mut ep_setup): (u16, u16, u16) = (0, 0, 0);
|
let (mut ep_out, mut ep_in_complete, mut ep_setup): (u16, u16, u16) = (0, 0, 0);
|
||||||
|
|
||||||
let buff_status = inner.ctrl_reg.buff_status.read().bits();
|
|
||||||
if buff_status != 0 {
|
|
||||||
// IN Complete shall only be reported once.
|
// IN Complete shall only be reported once.
|
||||||
inner
|
inner
|
||||||
.ctrl_reg
|
.ctrl_reg
|
||||||
|
@ -567,8 +648,9 @@ impl UsbBusTrait for UsbBus {
|
||||||
.write(|w| unsafe { w.bits(0x5555_5555) });
|
.write(|w| unsafe { w.bits(0x5555_5555) });
|
||||||
|
|
||||||
for i in 0..32u32 {
|
for i in 0..32u32 {
|
||||||
let mask = 1 << i;
|
if buff_status == 0 {
|
||||||
if (buff_status & mask) == mask {
|
break;
|
||||||
|
} else if (buff_status & 1) == 1 {
|
||||||
let is_in = (i & 1) == 0;
|
let is_in = (i & 1) == 0;
|
||||||
let ep_idx = i / 2;
|
let ep_idx = i / 2;
|
||||||
if is_in {
|
if is_in {
|
||||||
|
@ -577,10 +659,10 @@ impl UsbBusTrait for UsbBus {
|
||||||
ep_out |= 1 << ep_idx;
|
ep_out |= 1 << ep_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
buff_status >>= 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// check for setup request
|
// check for setup request
|
||||||
// Only report setup if OUT has been cleared.
|
|
||||||
if sie_status.setup_rec().bit_is_set() {
|
if sie_status.setup_rec().bit_is_set() {
|
||||||
// Small max_packet_size_ep0 Work-Around
|
// Small max_packet_size_ep0 Work-Around
|
||||||
inner.ctrl_dpram.ep_buffer_control[0].modify(|_, w| w.available_0().clear_bit());
|
inner.ctrl_dpram.ep_buffer_control[0].modify(|_, w| w.available_0().clear_bit());
|
||||||
|
@ -589,9 +671,6 @@ impl UsbBusTrait for UsbBus {
|
||||||
inner.read_setup = true;
|
inner.read_setup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (0, 0, 0) = (ep_out, ep_in_complete, ep_setup) {
|
|
||||||
return PollResult::None;
|
|
||||||
}
|
|
||||||
PollResult::Data {
|
PollResult::Data {
|
||||||
ep_out,
|
ep_out,
|
||||||
ep_in_complete,
|
ep_in_complete,
|
||||||
|
|
150
rp2040-hal/src/usb/errata5.rs
Normal file
150
rp2040-hal/src/usb/errata5.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
//! After coming out of reset, the hardware expects 800us of LS_J (linestate J) time
|
||||||
|
//! before it will move to the connected state. However on a hub that broadcasts packets
|
||||||
|
//! for other devices this isn't the case. The plan here is to wait for the end of the bus
|
||||||
|
//! reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately
|
||||||
|
//! this requires us to use GPIO15 as there is no other way to force the input path.
|
||||||
|
//! We only need to force DP as DM can be left at zero. It will be gated off by GPIO
|
||||||
|
//! logic if it isn't func selected.
|
||||||
|
|
||||||
|
use rp2040_pac::Peripherals;
|
||||||
|
|
||||||
|
pub struct ForceLineStateJ {
|
||||||
|
prev_pads: u32,
|
||||||
|
prev_io_ctrls: u32,
|
||||||
|
}
|
||||||
|
pub enum Errata5State {
|
||||||
|
WaitEndOfReset,
|
||||||
|
ForceLineStateJ(ForceLineStateJ),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Errata5State {
|
||||||
|
pub fn start() -> Self {
|
||||||
|
Self::WaitEndOfReset
|
||||||
|
}
|
||||||
|
/// SAFETY: This method steals the peripherals.
|
||||||
|
/// It makes read only use of TIMER and read/write access to USBCTRL_REGS.
|
||||||
|
/// Both peripherals must be initialized & running.
|
||||||
|
pub unsafe fn update(self) -> Option<Self> {
|
||||||
|
let pac = crate::pac::Peripherals::steal();
|
||||||
|
match self {
|
||||||
|
Self::WaitEndOfReset => {
|
||||||
|
if pac.USBCTRL_REGS.sie_status.read().line_state().is_se0() {
|
||||||
|
Some(self)
|
||||||
|
} else {
|
||||||
|
let reset_state = pac.RESETS.reset.read();
|
||||||
|
assert!(
|
||||||
|
reset_state.io_bank0().bit_is_clear()
|
||||||
|
&& reset_state.pads_bank0().bit_is_clear(),
|
||||||
|
"IO Bank 0 must be out of reset for this work around to function properly."
|
||||||
|
);
|
||||||
|
Some(Self::ForceLineStateJ(start_force_j(&pac)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::ForceLineStateJ(ref state) => {
|
||||||
|
if pac
|
||||||
|
.USBCTRL_REGS
|
||||||
|
.sie_status
|
||||||
|
.read()
|
||||||
|
.connected()
|
||||||
|
.bit_is_clear()
|
||||||
|
{
|
||||||
|
Some(self)
|
||||||
|
} else {
|
||||||
|
finish(&pac, state.prev_pads, state.prev_io_ctrls);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_force_j(pac: &Peripherals) -> ForceLineStateJ {
|
||||||
|
let pads = &pac.PADS_BANK0.gpio[15];
|
||||||
|
let io = &pac.IO_BANK0.gpio[15];
|
||||||
|
let usb_ctrl = &pac.USBCTRL_REGS;
|
||||||
|
|
||||||
|
assert!(!usb_ctrl.sie_status.read().line_state().is_se0());
|
||||||
|
assert!(
|
||||||
|
pac.IO_BANK0.gpio[16].gpio_ctrl.read().funcsel().bits() != 8,
|
||||||
|
"Not expecting DM to be function 8"
|
||||||
|
);
|
||||||
|
|
||||||
|
// backup io ctrl & pad ctrl
|
||||||
|
let prev_pads = pads.read().bits();
|
||||||
|
let prev_io_ctrls = io.gpio_ctrl.read().bits();
|
||||||
|
|
||||||
|
// Enable bus keep and force pin to tristate, so USB DP muxing doesn't affect
|
||||||
|
// pin state
|
||||||
|
pads.modify(|_, w| w.pue().set_bit().pde().set_bit());
|
||||||
|
io.gpio_ctrl.modify(|_, w| w.oeover().disable());
|
||||||
|
|
||||||
|
// Select function 8 (USB debug muxing) without disturbing other controls
|
||||||
|
io.gpio_ctrl.modify(|_, w| unsafe { w.funcsel().bits(8) });
|
||||||
|
|
||||||
|
// J state is a differential 1 for a full speed device so
|
||||||
|
// DP = 1 and DM = 0. Don't actually need to set DM low as it
|
||||||
|
// is already gated assuming it isn't funcseld.
|
||||||
|
io.gpio_ctrl.modify(|_, w| w.inover().high());
|
||||||
|
|
||||||
|
// Force PHY pull up to stay before switching away from the phy
|
||||||
|
//usb_ctrl
|
||||||
|
// .usbphy_direct
|
||||||
|
// .modify(|_, w| w.dp_pullup_en().set_bit());
|
||||||
|
//usb_ctrl
|
||||||
|
// .usbphy_direct_override
|
||||||
|
// .modify(|_, w| w.dp_pullup_en_override_en().set_bit());
|
||||||
|
// Use the "SET" alias region to only write rather than "read/modify/write"
|
||||||
|
unsafe {
|
||||||
|
let ctrl_regs_set_alias: &pac::usbctrl_regs::RegisterBlock =
|
||||||
|
&*(((pac::USBCTRL_REGS::ptr() as usize) | (2 << 12)) as *const _);
|
||||||
|
ctrl_regs_set_alias
|
||||||
|
.usbphy_direct
|
||||||
|
.write_with_zero(|w| w.dp_pullup_en().set_bit());
|
||||||
|
ctrl_regs_set_alias
|
||||||
|
.usbphy_direct_override
|
||||||
|
.write_with_zero(|w| w.dp_pullup_en_override_en().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to GPIO phy with LS_J forced
|
||||||
|
unsafe {
|
||||||
|
usb_ctrl
|
||||||
|
.usb_muxing
|
||||||
|
.write_with_zero(|w| w.to_digital_pad().set_bit().softcon().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// LS_J is now forced, wait until the signal propagates through the usb logic.
|
||||||
|
loop {
|
||||||
|
let status = usb_ctrl.sie_status.read();
|
||||||
|
if status.line_state().is_j() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForceLineStateJ {
|
||||||
|
prev_pads,
|
||||||
|
prev_io_ctrls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn finish(pac: &Peripherals, prev_pads: u32, prev_io_ctrls: u32) {
|
||||||
|
let pads = &pac.PADS_BANK0.gpio[15];
|
||||||
|
let io = &pac.IO_BANK0.gpio[15];
|
||||||
|
|
||||||
|
// Switch back to USB phy
|
||||||
|
pac.USBCTRL_REGS
|
||||||
|
.usb_muxing
|
||||||
|
.write(|w| w.to_phy().set_bit().softcon().set_bit());
|
||||||
|
|
||||||
|
// Get rid of DP pullup override
|
||||||
|
unsafe {
|
||||||
|
let ctrl_regs_clear_alias: &pac::usbctrl_regs::RegisterBlock =
|
||||||
|
&*(((pac::USBCTRL_REGS::ptr() as usize) | (3 << 12)) as *const _);
|
||||||
|
ctrl_regs_clear_alias
|
||||||
|
.usbphy_direct_override
|
||||||
|
.write_with_zero(|w| w.dp_pullup_en_override_en().set_bit());
|
||||||
|
|
||||||
|
// Finally, restore the gpio ctrl value back to GPIO15
|
||||||
|
io.gpio_ctrl.write(|w| w.bits(prev_io_ctrls));
|
||||||
|
// Restore the pad ctrl value
|
||||||
|
pads.write(|w| w.bits(prev_pads));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue