Puts in the machinery to handle power detected/removed

This commit is contained in:
huntc
2022-06-16 16:08:58 +10:00
parent c46e9b6cfc
commit 4a8f117f25
6 changed files with 144 additions and 145 deletions

View File

@ -2,7 +2,7 @@
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
use core::task::Poll;
use cortex_m::peripheral::NVIC;
@ -25,6 +25,7 @@ static EP0_WAKER: AtomicWaker = NEW_AW;
static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
static POWER_AVAILABLE: AtomicBool = AtomicBool::new(false);
pub struct Driver<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
@ -39,6 +40,11 @@ impl<'d, T: Instance> Driver<'d, T> {
irq.unpend();
irq.enable();
// Initialize the bus so that it signals that power is available.
// Not required when using with_power_management as we then rely on the irq.
POWER_AVAILABLE.store(true, Ordering::Relaxed);
BUS_WAKER.wake();
Self {
phantom: PhantomData,
alloc_in: Allocator::new(),
@ -46,6 +52,25 @@ impl<'d, T: Instance> Driver<'d, T> {
}
}
/// Establish a new device that then uses the POWER peripheral to
/// detect USB power detected/removed events are handled.
#[cfg(not(feature = "_nrf5340-app"))]
pub fn with_power_management(
_usb: impl Unborrow<Target = T> + 'd,
irq: impl Unborrow<Target = T::Interrupt> + 'd,
power_irq: impl Interrupt,
) -> Self {
let regs = unsafe { &*pac::POWER::ptr() };
power_irq.set_handler(Self::on_power_interrupt);
power_irq.unpend();
power_irq.enable();
regs.intenset.write(|w| w.usbdetected().set().usbremoved().set());
Self::new(_usb, irq)
}
fn on_interrupt(_: *mut ()) {
let regs = T::regs();
@ -76,7 +101,6 @@ impl<'d, T: Instance> Driver<'d, T> {
// doesn't cause an infinite irq loop.
if regs.events_usbevent.read().bits() != 0 {
regs.events_usbevent.reset();
//regs.intenclr.write(|w| w.usbevent().clear());
BUS_WAKER.wake();
}
@ -96,6 +120,31 @@ impl<'d, T: Instance> Driver<'d, T> {
}
}
}
#[cfg(not(feature = "_nrf5340-app"))]
fn on_power_interrupt(_: *mut ()) {
let regs = unsafe { &*pac::POWER::ptr() };
let mut power_changed = false;
let mut power_available = false;
if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
power_changed = true;
power_available = true;
}
if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
power_changed = true;
power_available = false;
}
if power_changed {
POWER_AVAILABLE.store(power_available, Ordering::Relaxed);
BUS_WAKER.wake();
}
}
}
impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
@ -138,7 +187,10 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
(
Bus { phantom: PhantomData },
Bus {
phantom: PhantomData,
power_available: false,
},
ControlPipe {
_phantom: PhantomData,
max_packet_size: control_max_packet_size,
@ -149,6 +201,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
power_available: bool,
}
impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
@ -246,6 +299,17 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
trace!("USB event: ready");
}
if POWER_AVAILABLE.load(Ordering::Relaxed) != self.power_available {
self.power_available = !self.power_available;
if self.power_available {
trace!("Power event: available");
return Poll::Ready(Event::PowerDetected);
} else {
trace!("Power event: removed");
return Poll::Ready(Event::PowerRemoved);
}
}
Poll::Pending
})
}