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..77c70e74 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,22 +108,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 +151,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 @@ -227,12 +249,52 @@ impl Flex<'_, S> { // There is not currently a "safe" method for setting a single-bit. unsafe { w.notp().bits(1 << self.pin.pin()) }); } + + /// 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 usecount = unsafe { PORT_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 usecount = unsafe { PORT_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::(), + _ => (), + } + } + } } impl Drop for Flex<'_, S> { fn drop(&mut self) { self.pin.reset(); - // TODO: Disable clock for pin's port (assuming ref counted)? + Flex::::decrement_port_usecount(self.pin.port()); } } @@ -245,6 +307,8 @@ impl<'d> Flex<'d, SenseEnabled> { .disable_analog_multiplex() .enable_input_buffer(); + Flex::::increment_port_usecount(pin.port()); + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -325,6 +389,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 +410,8 @@ impl<'d> Flex<'d, SenseDisabled> { .disable_analog_multiplex() .disable_input_buffer(); + Flex::::increment_port_usecount(pin.port()); + Self { pin: pin.map_into(), _sense_mode: PhantomData::, @@ -350,6 +419,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