From 62841dd5b958fa2458aceb638a17d243e72d987f Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 12:52:29 +0200 Subject: [PATCH] rp/pio: revert pio pin funcsel to null on pio+sms drop once all sharing owners of pio pins have been dropped we should reset the pin for use by other hal objects. unfortunately this needs an atomic state per pio block because PioCommon and all of the state machines really do share ownership of any wrapped pins. only PioCommon can create them, but all state machines can keep them alive. since state machines can be moved to core1 we can't do reference counting in relaxed mode, but we *can* do relaxed pin accounting (since only common and the final drop can modify this). --- embassy-rp/src/pio.rs | 67 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 0616f6fc0..32e9be74d 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -4,9 +4,11 @@ use core::pin::Pin as FuturePin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; +use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use pac::io::vals::Gpio0ctrlFuncsel; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; @@ -320,6 +322,12 @@ pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, } +impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachineInstance<'d, PIO, SM> { + fn drop(&mut self) { + on_pio_drop::(); + } +} + impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; const SM: usize = SM; @@ -794,6 +802,12 @@ pub struct PioCommon<'d, PIO: PioInstance> { pio: PhantomData<&'d PIO>, } +impl<'d, PIO: PioInstance> Drop for PioCommon<'d, PIO> { + fn drop(&mut self) { + on_pio_drop::(); + } +} + pub struct PioInstanceMemory<'d, PIO: PioInstance> { used_mask: u32, pio: PhantomData<&'d PIO>, @@ -870,10 +884,15 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { unsafe { PIO::PIO.input_sync_bypass().read() } } - pub fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + /// Register a pin for PIO usage. Pins will be released from the PIO block + /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* + /// all [`PioStateMachine`]s for this block have been dropped. + pub fn make_pio_pin(&mut self, pin: impl Pin) -> PioPin { unsafe { pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } + // we can be relaxed about this because we're &mut here and nothing is cached + PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); PioPin { pin_bank: pin.pin_bank(), pio: PhantomData::default(), @@ -891,6 +910,8 @@ pub struct Pio<'d, PIO: PioInstance> { impl<'d, PIO: PioInstance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + PIO::state().users.store(5, Ordering::Release); + PIO::state().used_pins.store(0, Ordering::Release); Self { common: PioCommon { instructions_used: 0, @@ -904,13 +925,35 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { } } -pub trait PioInstance: sealed::PioInstance + Sized + Unpin { - fn pio(&self) -> u8 { - Self::PIO_NO +// we need to keep a record of which pins are assigned to each PIO. make_pio_pin +// notionally takes ownership of the pin it is given, but the wrapped pin cannot +// be treated as an owned resource since dropping it would have to deconfigure +// the pin, breaking running state machines in the process. pins are also shared +// between all state machines, which makes ownership even messier to track any +// other way. +pub struct State { + users: AtomicU8, + used_pins: AtomicU32, +} + +fn on_pio_drop() { + let state = PIO::state(); + if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { + let used_pins = state.used_pins.load(Ordering::Relaxed); + let null = Gpio0ctrlFuncsel::NULL.0; + for i in 0..32 { + if used_pins & (1 << i) != 0 { + unsafe { + pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); + } + } + } } } mod sealed { + use super::*; + pub trait PioStateMachine { type Pio: super::PioInstance; const SM: usize; @@ -925,6 +968,22 @@ mod sealed { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; + + #[inline] + fn state() -> &'static State { + static STATE: State = State { + users: AtomicU8::new(0), + used_pins: AtomicU32::new(0), + }; + + &STATE + } + } +} + +pub trait PioInstance: sealed::PioInstance + Sized + Unpin { + fn pio(&self) -> u8 { + Self::PIO_NO } }