diff --git a/examples/rt685s-evk/src/bin/gpio-blinky.rs b/examples/rt685s-evk/src/bin/gpio-blinky.rs index 903813ce..3e1a5183 100644 --- a/examples/rt685s-evk/src/bin/gpio-blinky.rs +++ b/examples/rt685s-evk/src/bin/gpio-blinky.rs @@ -5,7 +5,7 @@ extern crate embassy_imxrt_examples; use defmt::info; use embassy_executor::Spawner; -use embassy_imxrt::gpio; +use embassy_imxrt::{gpio, pac}; use embassy_time::Timer; #[embassy_executor::main] @@ -14,6 +14,13 @@ async fn main(_spawner: Spawner) { info!("Initializing GPIO"); + 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, @@ -22,6 +29,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 f1d5bbe2..542f95f8 100644 --- a/examples/rt685s-evk/src/bin/gpio-flex.rs +++ b/examples/rt685s-evk/src/bin/gpio-flex.rs @@ -5,8 +5,8 @@ extern crate embassy_imxrt_examples; use defmt::{assert, info}; use embassy_executor::Spawner; -use embassy_imxrt::gpio; use embassy_imxrt::gpio::SenseDisabled; +use embassy_imxrt::{gpio, pac}; use embassy_time::Timer; #[embassy_executor::main] @@ -15,9 +15,14 @@ async fn main(_spawner: Spawner) { info!("Initializing GPIO"); + 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(); @@ -67,9 +72,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()); @@ -80,16 +97,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 34430186..8df47f60 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -4,13 +4,15 @@ 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::sync::atomic::Ordering::Relaxed; 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,18 +108,9 @@ fn irq_handler(port_wakers: &[Option<&PortWaker>]) { } /// Initialization Logic -/// Note: GPIO port clocks are initialized in the clocks module. +/// Note: A GPIO port's clock is initialized whenever the first pin from a port is constructed +/// and deinitialized when the last pin from a port is dropped pub(crate) 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::(); - // Enable INTA interrupt::GPIO_INTA.unpend(); @@ -154,6 +147,17 @@ pub struct SenseDisabled; impl Sealed for SenseDisabled {} impl Sense for SenseDisabled {} +static PORT_USECOUNT: [AtomicU32; PORT_COUNT] = [ + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), + AtomicU32::new(0), +]; + /// Flex pin. /// /// This pin can be either an input or output pin. The output level register bit will @@ -234,7 +238,7 @@ 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)? + decrement_port_usecount(self.pin.port()); } } @@ -247,6 +251,8 @@ impl<'d> Flex<'d, SenseEnabled> { .disable_analog_multiplex() .enable_input_buffer(); + increment_port_usecount(pin.port()); + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -327,6 +333,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 @@ -345,6 +354,8 @@ impl<'d> Flex<'d, SenseDisabled> { .disable_analog_multiplex() .disable_input_buffer(); + increment_port_usecount(pin.port()); + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -352,6 +363,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 @@ -361,6 +375,48 @@ impl<'d> Flex<'d, SenseDisabled> { } } +/// Increment port usecount counter +/// +/// If the calling pin is the first pin in that port, then this enables the port's clock +fn increment_port_usecount(port: usize) { + let port_usecount = &PORT_USECOUNT[port]; + let usecount = port_usecount.fetch_add(1, Relaxed); + if usecount == 0 { + 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::(), + _ => (), + } + } +} + +/// Decrement port usecount counter +/// +/// If the calling pin is the last pin in that port, then this disables the port's clock +fn decrement_port_usecount(port: usize) { + let port_usecount = &PORT_USECOUNT[port]; + let usecount = port_usecount.fetch_sub(1, Relaxed); + if usecount == 1 { + match port { + 0 => disable::(), + 1 => disable::(), + 2 => disable::(), + 3 => disable::(), + 4 => disable::(), + 5 => disable::(), + 6 => disable::(), + 7 => disable::(), + _ => (), + } + } +} + /// Input pin pub struct Input<'d> { pin: Flex<'d, SenseEnabled>,