Skip to content

Commit

Permalink
add reference counting to flex pins to init/deinit GPIO port clocks
Browse files Browse the repository at this point in the history
  • Loading branch information
tullom committed Dec 11, 2024
1 parent 1f0b910 commit 0fa0e82
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 18 deletions.
14 changes: 13 additions & 1 deletion examples/rt685s-evk/src/bin/gpio-blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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,
Expand All @@ -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();
Expand Down
31 changes: 27 additions & 4 deletions examples/rt685s-evk/src/bin/gpio-flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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::<SenseDisabled>::new(p.PIO1_0);

assert!(cc1.pscctl1().read().hsgpio1_clk().is_enable_clock());

// enable level sensing
let mut flex = flex.enable_sensing();

Expand Down Expand Up @@ -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());
Expand All @@ -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;
}
Expand Down
82 changes: 69 additions & 13 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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::<peripherals::HSGPIO0>();
enable_and_reset::<peripherals::HSGPIO1>();
enable_and_reset::<peripherals::HSGPIO2>();
enable_and_reset::<peripherals::HSGPIO3>();
enable_and_reset::<peripherals::HSGPIO4>();
enable_and_reset::<peripherals::HSGPIO5>();
enable_and_reset::<peripherals::HSGPIO6>();
enable_and_reset::<peripherals::HSGPIO7>();

// Enable INTA
interrupt::GPIO_INTA.unpend();

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -234,7 +238,7 @@ impl<S: Sense> Flex<'_, S> {
impl<S: Sense> 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());
}
}

Expand All @@ -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::<SenseEnabled>,
Expand Down Expand Up @@ -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
Expand All @@ -345,13 +354,18 @@ impl<'d> Flex<'d, SenseDisabled> {
.disable_analog_multiplex()
.disable_input_buffer();

increment_port_usecount(pin.port());

Self {
pin: pin.map_into(),
_sense_mode: PhantomData::<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
Expand All @@ -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::<peripherals::HSGPIO0>(),
1 => enable_and_reset::<peripherals::HSGPIO1>(),
2 => enable_and_reset::<peripherals::HSGPIO2>(),
3 => enable_and_reset::<peripherals::HSGPIO3>(),
4 => enable_and_reset::<peripherals::HSGPIO4>(),
5 => enable_and_reset::<peripherals::HSGPIO5>(),
6 => enable_and_reset::<peripherals::HSGPIO6>(),
7 => enable_and_reset::<peripherals::HSGPIO7>(),
_ => (),
}
}
}

/// 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::<peripherals::HSGPIO0>(),
1 => disable::<peripherals::HSGPIO1>(),
2 => disable::<peripherals::HSGPIO2>(),
3 => disable::<peripherals::HSGPIO3>(),
4 => disable::<peripherals::HSGPIO4>(),
5 => disable::<peripherals::HSGPIO5>(),
6 => disable::<peripherals::HSGPIO6>(),
7 => disable::<peripherals::HSGPIO7>(),
_ => (),
}
}
}

/// Input pin
pub struct Input<'d> {
pin: Flex<'d, SenseEnabled>,
Expand Down

0 comments on commit 0fa0e82

Please sign in to comment.