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 Nov 22, 2024
1 parent 01a66be commit 5062c65
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 16 deletions.
13 changes: 13 additions & 0 deletions examples/rt685s-evk/src/bin/gpio-blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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,
Expand All @@ -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();
Expand Down
30 changes: 27 additions & 3 deletions examples/rt685s-evk/src/bin/gpio-flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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::<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 @@ -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());
Expand All @@ -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;
}
Expand Down
98 changes: 85 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,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::<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>();

disable::<peripherals::HSGPIO0>();
disable::<peripherals::HSGPIO1>();
disable::<peripherals::HSGPIO2>();
disable::<peripherals::HSGPIO3>();
disable::<peripherals::HSGPIO4>();
disable::<peripherals::HSGPIO5>();
disable::<peripherals::HSGPIO6>();
disable::<peripherals::HSGPIO7>();
// Enable INTA
interrupt::GPIO_INTA.unpend();
interrupt::GPIO_INTA.enable();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -227,12 +249,52 @@ impl<S: Sense> 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::<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 usecount = unsafe { PORT_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>(),
_ => (),
}
}
}
}

impl<S: Sense> Drop for Flex<'_, S> {
fn drop(&mut self) {
self.pin.reset();
// TODO: Disable clock for pin's port (assuming ref counted)?
Flex::<S>::decrement_port_usecount(self.pin.port());
}
}

Expand All @@ -245,6 +307,8 @@ impl<'d> Flex<'d, SenseEnabled> {
.disable_analog_multiplex()
.enable_input_buffer();

Flex::<SenseEnabled>::increment_port_usecount(pin.port());

Self {
pin: pin.map_into(),
_sense_mode: PhantomData::<SenseEnabled>,
Expand Down Expand Up @@ -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
Expand All @@ -343,13 +410,18 @@ impl<'d> Flex<'d, SenseDisabled> {
.disable_analog_multiplex()
.disable_input_buffer();

Flex::<SenseDisabled>::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 Down

0 comments on commit 5062c65

Please sign in to comment.