From 63ea072e2d51b44b492714b0cd69e9f428a709e1 Mon Sep 17 00:00:00 2001 From: matteotullo Date: Thu, 31 Oct 2024 19:13:35 -0700 Subject: [PATCH] add reference counting to flex pins to init/deinit GPIO port clocks --- examples/rt685s-evk/src/bin/gpio-blinky.rs | 13 ++ examples/rt685s-evk/src/bin/gpio-flex.rs | 30 ++++- src/gpio.rs | 131 +++++++++++++++++++-- 3 files changed, 158 insertions(+), 16 deletions(-) diff --git a/examples/rt685s-evk/src/bin/gpio-blinky.rs b/examples/rt685s-evk/src/bin/gpio-blinky.rs index 0f6a939c..ee98849a 100644 --- a/examples/rt685s-evk/src/bin/gpio-blinky.rs +++ b/examples/rt685s-evk/src/bin/gpio-blinky.rs @@ -6,6 +6,7 @@ extern crate embassy_imxrt_examples; use defmt::info; use embassy_executor::Spawner; use embassy_imxrt::gpio; +use embassy_imxrt::pac; use embassy_time::Timer; #[embassy_executor::main] @@ -15,6 +16,13 @@ async fn main(_spawner: Spawner) { info!("Initializing GPIO"); unsafe { gpio::init() }; + let cc1 = unsafe { pac::Clkctl1::steal() }; + + assert!( + cc1.pscctl1().read().hsgpio0_clk().is_disable_clock(), + "GPIO port 0 clock was enabled before any GPIO pins were created!" + ); + let mut led = gpio::Output::new( p.PIO0_26, gpio::Level::Low, @@ -23,6 +31,11 @@ async fn main(_spawner: Spawner) { gpio::SlewRate::Standard, ); + assert!( + cc1.pscctl1().read().hsgpio0_clk().is_enable_clock(), + "GPIO port 0 clock is still disabled even after a GPIO pin is created!" + ); + loop { info!("Toggling LED"); led.toggle(); diff --git a/examples/rt685s-evk/src/bin/gpio-flex.rs b/examples/rt685s-evk/src/bin/gpio-flex.rs index fea8d59f..44c7074c 100644 --- a/examples/rt685s-evk/src/bin/gpio-flex.rs +++ b/examples/rt685s-evk/src/bin/gpio-flex.rs @@ -7,6 +7,7 @@ use defmt::{assert, info}; use embassy_executor::Spawner; use embassy_imxrt::gpio; use embassy_imxrt::gpio::SenseDisabled; +use embassy_imxrt::pac; use embassy_time::Timer; #[embassy_executor::main] @@ -16,9 +17,14 @@ async fn main(_spawner: Spawner) { info!("Initializing GPIO"); unsafe { gpio::init() }; + let cc1 = unsafe { pac::Clkctl1::steal() }; + assert!(cc1.pscctl1().read().hsgpio1_clk().is_disable_clock()); + // Start with a level sensing disabled, output only state let flex = gpio::Flex::::new(p.PIO1_0); + assert!(cc1.pscctl1().read().hsgpio1_clk().is_enable_clock()); + // enable level sensing let mut flex = flex.enable_sensing(); @@ -68,9 +74,21 @@ async fn main(_spawner: Spawner) { // set pin level high flex.set_high(); + flex.set_as_output( + gpio::DriveMode::PushPull, + gpio::DriveStrength::Normal, + gpio::SlewRate::Standard, + ); // re-enable level sensing let mut flex = flex.enable_sensing(); + // set pin level high + flex.set_high(); + flex.set_as_output( + gpio::DriveMode::PushPull, + gpio::DriveStrength::Normal, + gpio::SlewRate::Standard, + ); // check pin level is high assert!(flex.is_high()); @@ -81,16 +99,22 @@ async fn main(_spawner: Spawner) { // check pin level is low assert!(flex.is_low()); - let mut flex = flex.disable_sensing(); - // toggle pin flex.toggle(); - let flex = flex.enable_sensing(); + flex.set_as_output( + gpio::DriveMode::PushPull, + gpio::DriveStrength::Normal, + gpio::SlewRate::Standard, + ); // check pin level is high assert!(flex.is_high()); + assert!(cc1.pscctl1().read().hsgpio1_clk().is_enable_clock()); + drop(flex); + assert!(cc1.pscctl1().read().hsgpio1_clk().is_disable_clock()); + loop { Timer::after_millis(1000).await; } diff --git a/src/gpio.rs b/src/gpio.rs index ee5abea2..ef4d7221 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -4,13 +4,14 @@ use core::convert::Infallible; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin as FuturePin; +use core::sync::atomic::AtomicU32; use core::task::{Context, Poll}; use embassy_hal_internal::interrupt::InterruptExt; use embassy_sync::waitqueue::AtomicWaker; use sealed::Sealed; -use crate::clocks::enable_and_reset; +use crate::clocks::{disable, enable_and_reset}; use crate::iopctl::IopctlPin; pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate}; use crate::{interrupt, into_ref, peripherals, Peripheral, PeripheralRef}; @@ -106,22 +107,19 @@ fn irq_handler(port_wakers: &[Option<&PortWaker>]) { } /// Initialization Logic -/// Note: GPIO port clocks are initialized in the clocks module. /// # Safety /// /// This function enables GPIO INT A interrupt. It should not be called /// until you are ready to handle the interrupt pub unsafe fn init() { - // Enable GPIO clocks - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - enable_and_reset::(); - + disable::(); + disable::(); + disable::(); + disable::(); + disable::(); + disable::(); + disable::(); + disable::(); // Enable INTA interrupt::GPIO_INTA.unpend(); interrupt::GPIO_INTA.enable(); @@ -152,6 +150,29 @@ pub struct SenseDisabled; impl Sealed for SenseDisabled {} impl Sense for SenseDisabled {} +struct PortUsecountWrapper { + usecount: AtomicU32, +} + +impl PortUsecountWrapper { + const fn new() -> Self { + Self { + usecount: AtomicU32::new(0), + } + } +} + +static mut PORT_USECOUNT: [PortUsecountWrapper; PORT_COUNT] = [ + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), + PortUsecountWrapper::new(), +]; + /// Flex pin. /// /// This pin can be either an input or output pin. The output level register bit will @@ -232,7 +253,35 @@ impl Flex<'_, S> { impl Drop for Flex<'_, S> { fn drop(&mut self) { self.pin.reset(); - // TODO: Disable clock for pin's port (assuming ref counted)? + + // Extract port number from offset + let port = self.pin.port(); + + let is_last_drop = critical_section::with(|_| { + // SAFETY: Safe since we are using an atomic load and stores. + let usecount = unsafe { PORT_USECOUNT[port].usecount.load(core::sync::atomic::Ordering::Acquire) }; + assert!(usecount >= 1); + unsafe { + PORT_USECOUNT[port] + .usecount + .store(usecount - 1, core::sync::atomic::Ordering::Release) + }; + usecount == 1 + }); + + if is_last_drop { + match port { + 0 => disable::(), + 1 => disable::(), + 2 => disable::(), + 3 => disable::(), + 4 => disable::(), + 5 => disable::(), + 6 => disable::(), + 7 => disable::(), + _ => (), + } + } } } @@ -245,6 +294,31 @@ impl<'d> Flex<'d, SenseEnabled> { .disable_analog_multiplex() .enable_input_buffer(); + let port = pin.port(); + let is_first_construct = critical_section::with(|_| { + // SAFETY: Safe since we are using an atomic load and stores. + let usecount = unsafe { PORT_USECOUNT[port].usecount.load(core::sync::atomic::Ordering::Acquire) }; + unsafe { + PORT_USECOUNT[port] + .usecount + .store(usecount + 1, core::sync::atomic::Ordering::Release) + }; + usecount == 0 + }); + if is_first_construct { + match port { + 0 => enable_and_reset::(), + 1 => enable_and_reset::(), + 2 => enable_and_reset::(), + 3 => enable_and_reset::(), + 4 => enable_and_reset::(), + 5 => enable_and_reset::(), + 6 => enable_and_reset::(), + 7 => enable_and_reset::(), + _ => (), + } + } + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -325,6 +399,9 @@ impl<'d> Flex<'d, SenseEnabled> { /// Return a new Flex pin instance with level sensing disabled. /// /// Consumes less power than a flex pin with sensing enabled. + /// + /// NOTE: A brand new flex pin is returned without any sense of previous state. + /// Be sure to set the pin level and pin direction (input or output) #[must_use] pub fn disable_sensing(self) -> Flex<'d, SenseDisabled> { // Cloning the pin is ok since we consume self immediately @@ -343,6 +420,31 @@ impl<'d> Flex<'d, SenseDisabled> { .disable_analog_multiplex() .disable_input_buffer(); + let port = pin.port(); + let is_first_construct = critical_section::with(|_| { + // SAFETY: Safe since we are using an atomic load and stores. + let usecount = unsafe { PORT_USECOUNT[port].usecount.load(core::sync::atomic::Ordering::Acquire) }; + unsafe { + PORT_USECOUNT[port] + .usecount + .store(usecount + 1, core::sync::atomic::Ordering::Release) + }; + usecount == 0 + }); + if is_first_construct { + match port { + 0 => enable_and_reset::(), + 1 => enable_and_reset::(), + 2 => enable_and_reset::(), + 3 => enable_and_reset::(), + 4 => enable_and_reset::(), + 5 => enable_and_reset::(), + 6 => enable_and_reset::(), + 7 => enable_and_reset::(), + _ => (), + } + } + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -350,6 +452,9 @@ impl<'d> Flex<'d, SenseDisabled> { } /// Return a new Flex pin instance with level sensing enabled. + /// + /// NOTE: A brand new flex pin is returned without any sense of previous state. + /// Be sure to set the pin level and pin direction (input or output) #[must_use] pub fn enable_sensing(self) -> Flex<'d, SenseEnabled> { // Cloning the pin is ok since we consume self immediately